From 23fa8841e312e69cfeeed74606b56066067e2935 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Tue, 29 Nov 2022 18:17:13 -0600 Subject: [PATCH 001/219] Linesearch Same as kharmaim-stable but this segfaults. Committing to share code. --- kharma/emhd/emhd_sources.hpp | 8 +-- kharma/imex_driver.cpp | 32 +++++++++-- kharma/implicit/implicit.cpp | 101 ++++++++++++++++++++++++++++------- kharma/implicit/implicit.hpp | 31 +++++------ pars/bondi_viscous.par | 9 ++-- pars/emhdmodes.par | 12 +++-- 6 files changed, 143 insertions(+), 50 deletions(-) diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index 9f6f8f37..a39e11b6 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -92,12 +92,12 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 dt_ucov[mu] = (ucov_new[mu] - ucov_old[mu]) / dt; // Compute div of ucon (only the temporal part is nonzero) - Real div_ucon = 0; + Real div_ucon = 0; DLOOP1 div_ucon += G.gcon(Loci::center, j, i, 0, mu) * dt_ucov[mu]; // dTheta/dt const Real Theta_new = m::max((gam-1) * P_new(m_p.UU) / P_new(m_p.RHO), SMALL); const Real Theta_old = m::max((gam-1) * P_old(m_p.UU) / P_old(m_p.RHO), SMALL); - const Real dt_Theta = (Theta_new - Theta_old) / dt; + const Real dt_Theta = (Theta_new - Theta_old) / dt; // TEMPORAL SOURCE TERMS const Real& rho = P(m_p.RHO); @@ -105,10 +105,10 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons const Real& dPtilde = P(m_p.DP); const Real& Theta = (gam-1) * P(m_p.UU) / P(m_p.RHO); - Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; + Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; - Real dP0 = -rho * nu_e * div_ucon; + Real dP0 = -rho * nu_e * div_ucon; DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; Real q0_tilde = q0; diff --git a/kharma/imex_driver.cpp b/kharma/imex_driver.cpp index b841fa2b..441d0c74 100644 --- a/kharma/imex_driver.cpp +++ b/kharma/imex_driver.cpp @@ -72,6 +72,7 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) // '_flux_src' refers to the mesh object corresponding to -divF + S // '_solver' refers to the fluid state passed to the Implicit solver. At the end of the solve + // '_linesearch' refers to the fluid state updated while performing a linesearch in the solver // copy P and U from solver state to sub_step_final state. TaskCollection tc; @@ -104,6 +105,8 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) // When solving, we need a temporary copy with any explicit updates, // but not overwriting the beginning- or mid-step values pmb->meshblock_data.Add("solver", base); + // Need an additional state for linesearch + pmb->meshblock_data.Add("linesearch", base); } } @@ -112,12 +115,16 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) const int num_partitions = pmesh->DefaultNumPartitions(); TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { + auto &tl = single_tasklist_per_pack_region[i]; auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(stage_name[stage - 1], i); auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); auto &md_solver = pmesh->mesh_data.GetOrAdd("solver", i); + auto &md_linesearch = pmesh->mesh_data.GetOrAdd("linesearch", i); + + const bool linesearch = pkgs.at("Implicit")->Param("linesearch"); auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_none; @@ -250,12 +257,27 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) std::vector({isImplicit}), md_sub_step_init.get(), md_sub_step_init.get(), 1.0, 0.0, md_solver.get()); - // Time-step implicit variables by root-finding the residual - // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables - // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta auto t_guess_ready = t_explicit | t_copy_guess; - auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), - md_flux_src.get(), md_solver.get(), dt_this); + + // The `solver` MeshData object now has the implicit primitives corresponding to initial/half step and + // explicit variables have been updated to match the current step. + // Copy the primitives (MetaData::Derived) to the `linesearch` MeshData object if linesearch was enabled. + auto t_copy_linesearch = t_none; + auto t_implicit = t_none; + if (linesearch) { + auto t_copy_linesearch = tl.AddTask(t_guess_ready, Update::WeightedSumData>, + std::vector({Metadata::Derived}), md_solver.get(), + md_solver.get(), 1.0, 0.0, md_linesearch.get()); + // Time-step implicit variables by root-finding the residual + // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables + // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta + auto t_implicit = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); + } + else { + auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); + } // Copy the solver state into the final state md_sub_step_final auto t_copy_result = tl.AddTask(t_implicit, Update::WeightedSumData>, diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index df0a00c9..a96610ee 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -82,8 +82,6 @@ std::shared_ptr Initialize(ParameterInput *pin) params.Add("jacobian_delta", jacobian_delta); Real rootfind_tol = pin->GetOrAddReal("implicit", "rootfind_tol", 1.e-12); params.Add("rootfind_tol", rootfind_tol); - Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); - params.Add("linesearch_lambda", linesearch_lambda); int min_nonlinear_iter = pin->GetOrAddInteger("implicit", "min_nonlinear_iter", 1); params.Add("min_nonlinear_iter", min_nonlinear_iter); int max_nonlinear_iter = pin->GetOrAddInteger("implicit", "max_nonlinear_iter", 3); @@ -91,6 +89,15 @@ std::shared_ptr Initialize(ParameterInput *pin) bool use_qr = pin->GetOrAddBoolean("implicit", "use_qr", true); params.Add("use_qr", use_qr); + bool linesearch = pin->GetOrAddBoolean("implicit", "linesearch", true); + params.Add("linesearch", linesearch); + int max_linesearch_iter = pin->GetOrAddInteger("implicit", "max_linesearch_iter", 3); + params.Add("max_linesearch_iter", max_linesearch_iter); + Real linesearch_eps = pin->GetOrAddReal("implicit", "linesearch_eps", 1.e-4); + params.Add("linesearch_eps", linesearch_eps); + Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); + params.Add("linesearch_lambda", linesearch_lambda); + int verbose = pin->GetOrAddInteger("debug", "verbose", 0); params.Add("verbose", verbose); @@ -113,36 +120,47 @@ std::shared_ptr Initialize(ParameterInput *pin) } TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, - MeshData *md_solver, const Real& dt) + MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { Flag(md_full_step_init, "Implicit Iteration start, full step"); Flag(md_sub_step_init, "Implicit Iteration start, sub step"); Flag(md_flux_src, "Implicit Iteration start, divF and sources"); + Flag(md_linesearch, "Linesearch"); auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); + auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); + auto pmb_linesearch = md_linesearch->GetBlockData(0)->GetBlockPointer(); // Parameters const auto& implicit_par = pmb_full_step_init->packages.Get("Implicit")->AllParams(); const int iter_min = implicit_par.Get("min_nonlinear_iter"); const int iter_max = implicit_par.Get("max_nonlinear_iter"); - const Real lambda = implicit_par.Get("linesearch_lambda"); const Real delta = implicit_par.Get("jacobian_delta"); const Real rootfind_tol = implicit_par.Get("rootfind_tol"); const bool use_qr = implicit_par.Get("use_qr"); - const int verbose = implicit_par.Get("verbose"); + const int verbose = implicit_par.Get("verbose"); const Real gam = pmb_full_step_init->packages.Get("GRMHD")->Param("gamma"); + + const bool linesearch = implicit_par.Get("linesearch"); + const int max_linesearch_iter = implicit_par.Get("max_linesearch_iter"); + const Real linesearch_eps = implicit_par.Get("linesearch_eps"); + const Real linesearch_lambda = implicit_par.Get("linesearch_lambda"); + // Misc other constants for inside the kernel const bool am_rank0 = MPIRank0(); const Real tiny(SMALL), alpha(1.0); // We need two sets of emhd_params because we need the relaxation scale // at the same state in the implicit source terms - EMHD_parameters emhd_params_full_step_init, emhd_params_sub_step_init; + // Need an object of `EMHD_parameters` for the `linesearch` state + EMHD_parameters emhd_params_sub_step_init, emhd_params_solver, emhd_params_linesearch; if (pmb_sub_step_init->packages.AllPackages().count("EMHD")) { - const auto& pars_full_step_init = pmb_full_step_init->packages.Get("EMHD")->AllParams(); const auto& pars_sub_step_init = pmb_sub_step_init->packages.Get("EMHD")->AllParams(); - emhd_params_full_step_init = pars_full_step_init.Get("emhd_params"); + const auto& pars_solver = pmb_solver->packages.Get("EMHD")->AllParams(); + const auto& pars_linesearch = pmb_linesearch->packages.Get("EMHD")->AllParams(); emhd_params_sub_step_init = pars_sub_step_init.Get("emhd_params"); + emhd_params_solver = pars_solver.Get("emhd_params"); + emhd_params_linesearch = pars_linesearch.Get("emhd_params"); } // I don't normally do this, but we *really* care about variable ordering here. @@ -150,10 +168,10 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // just the residual & Jacobian we care about, which makes the solve much faster. // This strategy is ugly but potentially gives us complete control, // in case Kokkos's un-pivoted LU proves problematic - MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); + MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), isPrimitive); - auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); + auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), isPrimitive); + auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -168,7 +186,8 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Flux divergence plus explicit source terms. This is what we'd be adding. auto& flux_src_all = md_flux_src->PackVariables(ordered_cons); // Guess at initial state. We update only the implicit primitive vars - auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_linesearch_all = md_linesearch->PackVariables(ordered_prims); // Sizes and scratchpads const int nblock = U_full_step_init_all.GetDim(5); @@ -245,6 +264,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i ScratchPad2D U_sub_step_init_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D flux_src_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_solver_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), nvar, n1); // Copy some file contents to scratchpads, so we can slice them PLOOP { @@ -254,9 +274,10 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i U_full_step_init_s(ip, i) = U_full_step_init_all(b)(ip, k, j, i); P_sub_step_init_s(ip, i) = P_sub_step_init_all(b)(ip, k, j, i); U_sub_step_init_s(ip, i) = U_sub_step_init_all(b)(ip, k, j, i); - flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); - P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); - dU_implicit_s(ip, i) = 0.; + flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); + P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); + P_linesearch_s(ip, i) = P_linesearch_all(b)(ip, k, j, i); + dU_implicit_s(ip, i) = 0.; } ); } @@ -283,6 +304,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); + auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); // Solver variables auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); @@ -300,10 +322,16 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i dU_implicit(m_u.Q), dU_implicit(m_u.DP)); } + // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver + // since we do a copy in imex_driver just before, but it is required for the subsequent + // iterations of the solver. + PLOOP P_linesearch(ip) = P_solver(ip); + Real lambda = linesearch_lambda; + // Jacobian calculation // Requires calculating the residual anyway, so we grab it here calc_jacobian(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, - flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_full_step_init, + flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); @@ -337,11 +365,44 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked> ::invoke(alpha, jacobian, delta_prim); - // Update the guess. For now lambda == 1, choose on the fly? + // Update the guess + if (linesearch) { + norm_all(b, k, j, i) = 0; + FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); + norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); + + Real f0 = 0.5 * norm_all(b, k, j, i); + Real fprime0 = -2. * f0; + + for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { + // Take step + FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); + + // Compute norm of the residual (loss function) + calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, + dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, + k, j, i, gam, dt, residual); + + norm_all(b, k, j, i) = 0; + FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); + norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); + Real f1 = 0.5 * norm_all(b, k, j, i); + + // Compute new step length + int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); + Real denom = (f1 - f0 - (fprime0 * lambda)) * condition + (1 - condition); + Real lambda_new = -fprime0 * lambda * lambda / denom / 2.; + lambda = lambda * (1 - condition) + (condition * lambda_new); + + // Check if new solution has converged within required tolerance + if (condition == 0) break; + } + } + FLOOP P_solver(ip) += lambda * delta_prim(ip); calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", @@ -353,9 +414,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Store for maximum/output // I would be tempted to store the whole residual, but it's of variable size - norm_all(b, k , j, i) = 0; + norm_all(b, k , j, i) = 0; FLOOP norm_all(b, k, j, i) += residual(ip)*residual(ip); - norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? + norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? } ); member.team_barrier(); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index c3a70109..40cfb307 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -61,14 +61,15 @@ std::shared_ptr Initialize(ParameterInput *pin); /** * @brief take the per-zone implicit portion of a semi-implicit scheme * - * @param mdi the fluid state at the beginning of the step - * @param md0 the initial fluid state for this substep - * @param dudt the negative flux divergence plus explicit source terms + * @param md_full_step_init the fluid state at the beginning of the step + * @param md_sub_step_init the initial fluid state for this substep + * @param md_flux_src the negative flux divergence plus explicit source terms * @param md_solver should contain initial guess on call, contains result on return + * @param md_linesearch should contain solver prims at start, updated in the linesearch * @param dt the timestep (current substep) */ -TaskStatus Step(MeshData *mdi, MeshData *md0, MeshData *dudt, - MeshData *mc_solver, const Real& dt); +TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, + MeshData *md_linesearch, MeshData *md_solver, const Real& dt); /** * Calculate the residual generated by the trial primitives P_test @@ -81,7 +82,7 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P const Local& Pi, const Local& Ui, const Local& Ps, const Local& dudt_explicit, const Local& dUi, const Local& tmp, const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params, - const EMHD_parameters& emhd_params_tau,const int& nfvar, + const EMHD_parameters& emhd_params_s,const int& nfvar, const int& k, const int& j, const int& i, const Real& gam, const double& dt, Local& residual) { @@ -95,14 +96,14 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P if (m_p.Q >= 0) { // Compute new implicit source terms and time derivative source terms Real dUq, dUdP; // Don't need full array for these - EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_tau, dUq, dUdP); // dU_new + EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_s, dUq, dUdP); // dU_new // ... - 0.5*(dU_new(ip) + dUi(ip)) ... residual(m_u.Q) -= 0.5*(dUq + dUi(m_u.Q)); residual(m_u.DP) -= 0.5*(dUdP + dUi(m_u.DP)); // if (i == 11 && j == 11) { // printf("Implicit sources: "); printf("%6.5e %6.5e", dUq - dUi(m_u.Q), dUdP - dUi(m_u.DP)); printf("\n"); // } - EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params, gam, dt, k, j, i, dUq, dUdP); // dU_time + EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params_s, gam, dt, k, j, i, dUq, dUdP); // dU_time // ... - dU_time(ip) residual(m_u.Q) -= dUq; residual(m_u.DP) -= dUdP; @@ -112,13 +113,13 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P // Normalize Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P_test, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters(G, Ps, m_p, emhd_params_s, gam, k, j, i, tau, chi_e, nu_e); residual(m_u.Q) *= tau; residual(m_u.DP) *= tau; if (emhd_params.higher_order_terms){ - Real rho = P_test(m_p.RHO); - Real u = P_test(m_p.UU); - Real Theta = (gam - 1.) * u / rho; + Real rho = Ps(m_p.RHO); + Real uu = Ps(m_p.UU); + Real Theta = (gam - 1.) * uu / rho; residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; @@ -137,7 +138,7 @@ template KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P_solver, const Local& P_full_step_init, const Local& U_full_step_init, const Local& P_sub_step_init, const Local& flux_src, const Local& dU_implicit, Local& tmp1, Local& tmp2, Local& tmp3, - const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_full_step_init, + const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_solver, const EMHD_parameters& emhd_params_sub_step_init, const int& nvar, const int& nfvar, const int& k, const int& j, const int& i, const Real& jac_delta, const Real& gam, const double& dt, @@ -145,7 +146,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P { // Calculate residual of P calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // Use one scratchpad as the incremented prims P_delta, // one as the new residual residual_delta @@ -165,7 +166,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P // Compute the residual for P_delta, residual_delta calc_residual(G, P_delta, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); // Compute forward derivatives of each residual vs the primitive col for (int row = 0; row < nfvar; row++) { diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index c6ab6825..84b59642 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -52,9 +52,12 @@ eta = 0.01 type = imex -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 mdot = 1.0 diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 81d9c94f..72f9c10c 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -61,9 +61,15 @@ disable_floors = true enable_emhd_limits = false -min_nonlinear_iter = 3 -max_nonlinear_iter = 3 -use_qr = false +min_nonlinear_iter = 3 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-20 +linesearch = false +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true + # General verbosity level: From c6dcbfd31972bdd8d09f2b954d3ae104114ae983 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Wed, 30 Nov 2022 17:17:58 -0600 Subject: [PATCH 002/219] Linesearch fixed. (i) Wasn't allocating the right amount of scratch memory for the imex solve. (ii) Viscous bondi needs 5 zones in the horizon. Was my bane for most of the day. --- kharma/emhd/emhd_sources.hpp | 8 +-- kharma/imex_driver.cpp | 32 +++++++++-- kharma/implicit/implicit.cpp | 106 +++++++++++++++++++++++++++-------- kharma/implicit/implicit.hpp | 31 +++++----- kharma/kharma.cpp | 3 + pars/bondi_viscous.par | 15 ++--- pars/emhdmodes.par | 12 +++- tests/bondi_viscous/check.py | 93 ++++++++++++++++-------------- tests/bondi_viscous/check.sh | 15 ----- tests/bondi_viscous/run.sh | 45 ++++++++------- 10 files changed, 228 insertions(+), 132 deletions(-) delete mode 100755 tests/bondi_viscous/check.sh diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index 9f6f8f37..a39e11b6 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -92,12 +92,12 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 dt_ucov[mu] = (ucov_new[mu] - ucov_old[mu]) / dt; // Compute div of ucon (only the temporal part is nonzero) - Real div_ucon = 0; + Real div_ucon = 0; DLOOP1 div_ucon += G.gcon(Loci::center, j, i, 0, mu) * dt_ucov[mu]; // dTheta/dt const Real Theta_new = m::max((gam-1) * P_new(m_p.UU) / P_new(m_p.RHO), SMALL); const Real Theta_old = m::max((gam-1) * P_old(m_p.UU) / P_old(m_p.RHO), SMALL); - const Real dt_Theta = (Theta_new - Theta_old) / dt; + const Real dt_Theta = (Theta_new - Theta_old) / dt; // TEMPORAL SOURCE TERMS const Real& rho = P(m_p.RHO); @@ -105,10 +105,10 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons const Real& dPtilde = P(m_p.DP); const Real& Theta = (gam-1) * P(m_p.UU) / P(m_p.RHO); - Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; + Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; - Real dP0 = -rho * nu_e * div_ucon; + Real dP0 = -rho * nu_e * div_ucon; DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; Real q0_tilde = q0; diff --git a/kharma/imex_driver.cpp b/kharma/imex_driver.cpp index b841fa2b..441d0c74 100644 --- a/kharma/imex_driver.cpp +++ b/kharma/imex_driver.cpp @@ -72,6 +72,7 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) // '_flux_src' refers to the mesh object corresponding to -divF + S // '_solver' refers to the fluid state passed to the Implicit solver. At the end of the solve + // '_linesearch' refers to the fluid state updated while performing a linesearch in the solver // copy P and U from solver state to sub_step_final state. TaskCollection tc; @@ -104,6 +105,8 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) // When solving, we need a temporary copy with any explicit updates, // but not overwriting the beginning- or mid-step values pmb->meshblock_data.Add("solver", base); + // Need an additional state for linesearch + pmb->meshblock_data.Add("linesearch", base); } } @@ -112,12 +115,16 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) const int num_partitions = pmesh->DefaultNumPartitions(); TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { + auto &tl = single_tasklist_per_pack_region[i]; auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(stage_name[stage - 1], i); auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); auto &md_solver = pmesh->mesh_data.GetOrAdd("solver", i); + auto &md_linesearch = pmesh->mesh_data.GetOrAdd("linesearch", i); + + const bool linesearch = pkgs.at("Implicit")->Param("linesearch"); auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_none; @@ -250,12 +257,27 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) std::vector({isImplicit}), md_sub_step_init.get(), md_sub_step_init.get(), 1.0, 0.0, md_solver.get()); - // Time-step implicit variables by root-finding the residual - // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables - // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta auto t_guess_ready = t_explicit | t_copy_guess; - auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), - md_flux_src.get(), md_solver.get(), dt_this); + + // The `solver` MeshData object now has the implicit primitives corresponding to initial/half step and + // explicit variables have been updated to match the current step. + // Copy the primitives (MetaData::Derived) to the `linesearch` MeshData object if linesearch was enabled. + auto t_copy_linesearch = t_none; + auto t_implicit = t_none; + if (linesearch) { + auto t_copy_linesearch = tl.AddTask(t_guess_ready, Update::WeightedSumData>, + std::vector({Metadata::Derived}), md_solver.get(), + md_solver.get(), 1.0, 0.0, md_linesearch.get()); + // Time-step implicit variables by root-finding the residual + // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables + // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta + auto t_implicit = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); + } + else { + auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); + } // Copy the solver state into the final state md_sub_step_final auto t_copy_result = tl.AddTask(t_implicit, Update::WeightedSumData>, diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index df0a00c9..abe59319 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -82,8 +82,6 @@ std::shared_ptr Initialize(ParameterInput *pin) params.Add("jacobian_delta", jacobian_delta); Real rootfind_tol = pin->GetOrAddReal("implicit", "rootfind_tol", 1.e-12); params.Add("rootfind_tol", rootfind_tol); - Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); - params.Add("linesearch_lambda", linesearch_lambda); int min_nonlinear_iter = pin->GetOrAddInteger("implicit", "min_nonlinear_iter", 1); params.Add("min_nonlinear_iter", min_nonlinear_iter); int max_nonlinear_iter = pin->GetOrAddInteger("implicit", "max_nonlinear_iter", 3); @@ -91,6 +89,15 @@ std::shared_ptr Initialize(ParameterInput *pin) bool use_qr = pin->GetOrAddBoolean("implicit", "use_qr", true); params.Add("use_qr", use_qr); + bool linesearch = pin->GetOrAddBoolean("implicit", "linesearch", true); + params.Add("linesearch", linesearch); + int max_linesearch_iter = pin->GetOrAddInteger("implicit", "max_linesearch_iter", 3); + params.Add("max_linesearch_iter", max_linesearch_iter); + Real linesearch_eps = pin->GetOrAddReal("implicit", "linesearch_eps", 1.e-4); + params.Add("linesearch_eps", linesearch_eps); + Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); + params.Add("linesearch_lambda", linesearch_lambda); + int verbose = pin->GetOrAddInteger("debug", "verbose", 0); params.Add("verbose", verbose); @@ -113,36 +120,47 @@ std::shared_ptr Initialize(ParameterInput *pin) } TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, - MeshData *md_solver, const Real& dt) + MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { Flag(md_full_step_init, "Implicit Iteration start, full step"); Flag(md_sub_step_init, "Implicit Iteration start, sub step"); Flag(md_flux_src, "Implicit Iteration start, divF and sources"); + Flag(md_linesearch, "Linesearch"); auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); + auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); + auto pmb_linesearch = md_linesearch->GetBlockData(0)->GetBlockPointer(); // Parameters const auto& implicit_par = pmb_full_step_init->packages.Get("Implicit")->AllParams(); const int iter_min = implicit_par.Get("min_nonlinear_iter"); const int iter_max = implicit_par.Get("max_nonlinear_iter"); - const Real lambda = implicit_par.Get("linesearch_lambda"); const Real delta = implicit_par.Get("jacobian_delta"); const Real rootfind_tol = implicit_par.Get("rootfind_tol"); const bool use_qr = implicit_par.Get("use_qr"); - const int verbose = implicit_par.Get("verbose"); + const int verbose = implicit_par.Get("verbose"); const Real gam = pmb_full_step_init->packages.Get("GRMHD")->Param("gamma"); + + const bool linesearch = implicit_par.Get("linesearch"); + const int max_linesearch_iter = implicit_par.Get("max_linesearch_iter"); + const Real linesearch_eps = implicit_par.Get("linesearch_eps"); + const Real linesearch_lambda = implicit_par.Get("linesearch_lambda"); + // Misc other constants for inside the kernel const bool am_rank0 = MPIRank0(); const Real tiny(SMALL), alpha(1.0); // We need two sets of emhd_params because we need the relaxation scale // at the same state in the implicit source terms - EMHD_parameters emhd_params_full_step_init, emhd_params_sub_step_init; + // Need an object of `EMHD_parameters` for the `linesearch` state + EMHD_parameters emhd_params_sub_step_init, emhd_params_solver, emhd_params_linesearch; if (pmb_sub_step_init->packages.AllPackages().count("EMHD")) { - const auto& pars_full_step_init = pmb_full_step_init->packages.Get("EMHD")->AllParams(); const auto& pars_sub_step_init = pmb_sub_step_init->packages.Get("EMHD")->AllParams(); - emhd_params_full_step_init = pars_full_step_init.Get("emhd_params"); + const auto& pars_solver = pmb_solver->packages.Get("EMHD")->AllParams(); + const auto& pars_linesearch = pmb_linesearch->packages.Get("EMHD")->AllParams(); emhd_params_sub_step_init = pars_sub_step_init.Get("emhd_params"); + emhd_params_solver = pars_solver.Get("emhd_params"); + emhd_params_linesearch = pars_linesearch.Get("emhd_params"); } // I don't normally do this, but we *really* care about variable ordering here. @@ -150,10 +168,10 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // just the residual & Jacobian we care about, which makes the solve much faster. // This strategy is ugly but potentially gives us complete control, // in case Kokkos's un-pivoted LU proves problematic - MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); + MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), isPrimitive); - auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); + auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), isPrimitive); + auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -168,7 +186,8 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Flux divergence plus explicit source terms. This is what we'd be adding. auto& flux_src_all = md_flux_src->PackVariables(ordered_cons); // Guess at initial state. We update only the implicit primitive vars - auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_linesearch_all = md_linesearch->PackVariables(ordered_prims); // Sizes and scratchpads const int nblock = U_full_step_init_all.GetDim(5); @@ -214,8 +233,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Allocate enough to cache: // jacobian (2D) // residual, deltaP (implicit only) - // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, divF_src, P_solver, dU_implicit, two temps (all vars) - const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (10) * var_size_in_bytes; + // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, P_solver, P_linesearch, + // dU_implicit, three temps (all vars) + const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (11) * var_size_in_bytes; // Iterate. This loop is outside the kokkos kernel in order to print max_norm // There are generally a low and similar number of iterations between @@ -245,6 +265,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i ScratchPad2D U_sub_step_init_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D flux_src_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_solver_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), nvar, n1); // Copy some file contents to scratchpads, so we can slice them PLOOP { @@ -254,9 +275,10 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i U_full_step_init_s(ip, i) = U_full_step_init_all(b)(ip, k, j, i); P_sub_step_init_s(ip, i) = P_sub_step_init_all(b)(ip, k, j, i); U_sub_step_init_s(ip, i) = U_sub_step_init_all(b)(ip, k, j, i); - flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); - P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); - dU_implicit_s(ip, i) = 0.; + flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); + P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); + P_linesearch_s(ip, i) = P_linesearch_all(b)(ip, k, j, i); + dU_implicit_s(ip, i) = 0.; } ); } @@ -283,6 +305,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); + auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); // Solver variables auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); @@ -300,10 +323,16 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i dU_implicit(m_u.Q), dU_implicit(m_u.DP)); } + // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver + // since we do a copy in imex_driver just before, but it is required for the subsequent + // iterations of the solver. + PLOOP P_linesearch(ip) = P_solver(ip); + Real lambda = linesearch_lambda; + // Jacobian calculation // Requires calculating the residual anyway, so we grab it here calc_jacobian(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, - flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_full_step_init, + flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); @@ -337,11 +366,44 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked> ::invoke(alpha, jacobian, delta_prim); - // Update the guess. For now lambda == 1, choose on the fly? + // Update the guess + if (linesearch) { + norm_all(b, k, j, i) = 0; + FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); + norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); + + Real f0 = 0.5 * norm_all(b, k, j, i); + Real fprime0 = -2. * f0; + + for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { + // Take step + FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); + + // Compute norm of the residual (loss function) + calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, + dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, + k, j, i, gam, dt, residual); + + norm_all(b, k, j, i) = 0; + FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); + norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); + Real f1 = 0.5 * norm_all(b, k, j, i); + + // Compute new step length + int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); + Real denom = (f1 - f0 - (fprime0 * lambda)) * condition + (1 - condition); + Real lambda_new = -fprime0 * lambda * lambda / denom / 2.; + lambda = lambda * (1 - condition) + (condition * lambda_new); + + // Check if new solution has converged within required tolerance + if (condition == 0) break; + } + } + FLOOP P_solver(ip) += lambda * delta_prim(ip); calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", @@ -353,9 +415,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Store for maximum/output // I would be tempted to store the whole residual, but it's of variable size - norm_all(b, k , j, i) = 0; + norm_all(b, k , j, i) = 0; FLOOP norm_all(b, k, j, i) += residual(ip)*residual(ip); - norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? + norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? } ); member.team_barrier(); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index c3a70109..40cfb307 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -61,14 +61,15 @@ std::shared_ptr Initialize(ParameterInput *pin); /** * @brief take the per-zone implicit portion of a semi-implicit scheme * - * @param mdi the fluid state at the beginning of the step - * @param md0 the initial fluid state for this substep - * @param dudt the negative flux divergence plus explicit source terms + * @param md_full_step_init the fluid state at the beginning of the step + * @param md_sub_step_init the initial fluid state for this substep + * @param md_flux_src the negative flux divergence plus explicit source terms * @param md_solver should contain initial guess on call, contains result on return + * @param md_linesearch should contain solver prims at start, updated in the linesearch * @param dt the timestep (current substep) */ -TaskStatus Step(MeshData *mdi, MeshData *md0, MeshData *dudt, - MeshData *mc_solver, const Real& dt); +TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, + MeshData *md_linesearch, MeshData *md_solver, const Real& dt); /** * Calculate the residual generated by the trial primitives P_test @@ -81,7 +82,7 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P const Local& Pi, const Local& Ui, const Local& Ps, const Local& dudt_explicit, const Local& dUi, const Local& tmp, const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params, - const EMHD_parameters& emhd_params_tau,const int& nfvar, + const EMHD_parameters& emhd_params_s,const int& nfvar, const int& k, const int& j, const int& i, const Real& gam, const double& dt, Local& residual) { @@ -95,14 +96,14 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P if (m_p.Q >= 0) { // Compute new implicit source terms and time derivative source terms Real dUq, dUdP; // Don't need full array for these - EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_tau, dUq, dUdP); // dU_new + EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_s, dUq, dUdP); // dU_new // ... - 0.5*(dU_new(ip) + dUi(ip)) ... residual(m_u.Q) -= 0.5*(dUq + dUi(m_u.Q)); residual(m_u.DP) -= 0.5*(dUdP + dUi(m_u.DP)); // if (i == 11 && j == 11) { // printf("Implicit sources: "); printf("%6.5e %6.5e", dUq - dUi(m_u.Q), dUdP - dUi(m_u.DP)); printf("\n"); // } - EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params, gam, dt, k, j, i, dUq, dUdP); // dU_time + EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params_s, gam, dt, k, j, i, dUq, dUdP); // dU_time // ... - dU_time(ip) residual(m_u.Q) -= dUq; residual(m_u.DP) -= dUdP; @@ -112,13 +113,13 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P // Normalize Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P_test, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters(G, Ps, m_p, emhd_params_s, gam, k, j, i, tau, chi_e, nu_e); residual(m_u.Q) *= tau; residual(m_u.DP) *= tau; if (emhd_params.higher_order_terms){ - Real rho = P_test(m_p.RHO); - Real u = P_test(m_p.UU); - Real Theta = (gam - 1.) * u / rho; + Real rho = Ps(m_p.RHO); + Real uu = Ps(m_p.UU); + Real Theta = (gam - 1.) * uu / rho; residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; @@ -137,7 +138,7 @@ template KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P_solver, const Local& P_full_step_init, const Local& U_full_step_init, const Local& P_sub_step_init, const Local& flux_src, const Local& dU_implicit, Local& tmp1, Local& tmp2, Local& tmp3, - const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_full_step_init, + const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_solver, const EMHD_parameters& emhd_params_sub_step_init, const int& nvar, const int& nfvar, const int& k, const int& j, const int& i, const Real& jac_delta, const Real& gam, const double& dt, @@ -145,7 +146,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P { // Calculate residual of P calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // Use one scratchpad as the incremented prims P_delta, // one as the new residual residual_delta @@ -165,7 +166,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P // Compute the residual for P_delta, residual_delta calc_residual(G, P_delta, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); // Compute forward derivatives of each residual vs the primitive col for (int row = 0; row < nfvar; row++) { diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 89e5d6a3..41ae0a21 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -146,6 +146,9 @@ void KHARMA::FixParameters(std::unique_ptr& pin) int nx1 = pin->GetInteger("parthenon/mesh", "nx1"); Real a = pin->GetReal("coordinates", "a"); GReal Rhor = 1 + sqrt(1 - a*a); + if (prob == "bondi_viscous") { + Rhor = pin->GetOrAddReal("coordinates", "Rhor", 3.0); + } GReal x1hor = log_r ? log(Rhor) : Rhor; // Set Rin such that we have 5 zones completely inside the event horizon diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index c6ab6825..99d0f8fe 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -21,8 +21,6 @@ base = ks transform = mks a = 0.0 hslope = 1.0 -# Override usual 5 zones in EH by specifying inner radius -r_in = 3.0 r_out = 20 @@ -35,7 +33,7 @@ reconstruction = weno5 implicit = true -implicit = true +implicit = false initial_cleanup = false # IMPORTANT: This block must be present and values filled in all EGRMHD simulations @@ -52,9 +50,12 @@ eta = 0.01 type = imex -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 mdot = 1.0 @@ -71,7 +72,7 @@ verbose = 1 file_type = hdf5 -dt = 20.0 +dt = 100.0 single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 81d9c94f..6c955b88 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -61,9 +61,15 @@ disable_floors = true enable_emhd_limits = false -min_nonlinear_iter = 3 -max_nonlinear_iter = 3 -use_qr = false +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-20 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true + # General verbosity level: diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index 1f084926..b224c074 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -5,59 +5,66 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import pyharm + if __name__=='__main__': - outputdir = os.getcwd() - kharmadir = '/data/bh29-home/vdhruv2/kharma' - RES = [int(r) for r in sys.argv[1].split(",")] - VISCOSITY = 1 - if VISCOSITY: - PRIMS = ['rho','u','dP'] - else: - PRIMS = ['rho','u'] - L1_norm = np.zeros([len(RES), len(PRIMS)]) + outputdir = './' + kharmadir = '../../' + + NVAR = 3 + VARS = ['rho', 'u', 'dP'] + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] + + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) for r, res in enumerate(RES): # load analytic result - if VISCOSITY: - rho_analytic, u_analytic, dP_analytic = np.loadtxt(os.path.join(kharmadir, \ - 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ - usecols=(0,1,3), unpack=True) - else: - rho_analytic, u_analytic, = np.loadtxt(os.path.join(kharmadir, \ - 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ - usecols=(0,1), unpack=True) + rho_analytic, uu_analytic, dP_analytic = np.loadtxt(os.path.join(kharmadir, \ + 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ + usecols=(0,1,3), unpack=True) # load code data - dfile = h5py.File('emhd_2d_{}_end.h5'.format(res), 'r') + dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - u = np.squeeze(dfile['prims'][Ellipsis,1][()]) - if VISCOSITY: - dP_tilde = np.squeeze(dfile['prims'][Ellipsis,9][()]) - + uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) + dP_tilde = np.squeeze(dfile['prims'][Ellipsis,9][()]) + t = dfile['t'][()] gam = dfile['header/gam'][()] higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') - # compute dP - if VISCOSITY: - if higher_order_terms=="TRUE": - tau = 30. - eta = 0.01 - P = (gam - 1.) * u - Theta = P / rho - nu_emhd = eta / rho - dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) - else: - dP = dP_tilde + # compute dP + if higher_order_terms=="TRUE": + print("Res: "+str(res)+"; higher order terms enabled") + tau = 30. + eta = 0.01 + P = (gam - 1.) * uu + Theta = P / rho + nu_emhd = eta / rho + dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) + else: + dP = dP_tilde # compute L1 norm - L1_norm[r,0] = np.mean(np.fabs(rho-rho_analytic[:,None])) - L1_norm[r,1] = np.mean(np.fabs(u-u_analytic[:,None])) - if VISCOSITY: - L1_norm[r,2] = np.mean(np.fabs(dP-dP_analytic[:,None])[1:-1]) + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(dP - dP_analytic[:,None])[1:-1]) + + # MEASURE CONVERGENCE + L1 = np.array(L1) + powerfits = [0.,]*NVAR + fail = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -2 or powerfits[k] < -2.7: + fail = 1 # plotting parameters @@ -81,14 +88,16 @@ # loop over prims tracker = 0 - for n in range(len(PRIMS)): - color = colors[tracker] - ax.loglog(RES, L1_norm[:,n], color=color, marker='o', label=PRIMS[n]) - tracker+=1 + for n in range(len(VARS)): + color = colors[tracker] + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) + tracker+=1 ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') plt.xscale('log', base=2) ax.set_xlabel('Resolution') ax.set_ylabel('L1 norm') ax.legend() - plt.savefig(os.path.join(outputdir, 'bondi_viscous_convergence.png'), dpi=300) + plt.savefig(os.path.join(outputdir, "bondi_viscous_convergence_"+SHORT+".png"), dpi=300) + + exit(fail) diff --git a/tests/bondi_viscous/check.sh b/tests/bondi_viscous/check.sh deleted file mode 100755 index 402306da..00000000 --- a/tests/bondi_viscous/check.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Run checks against analytic result for specified tests - -. /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - -RES2D="32,64,128,256" - -conda activate base - -fail=0 - -python3 check.py $RES2D "Bondi viscous" emhd2d || fail=1 - -exit $fail diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index 07e365f8..e6d04e07 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -1,32 +1,39 @@ #!/bin/bash -#set -euo pipefail +set -euo pipefail -BASE=~/kharma +BASE=../.. + +exit_code=0 # Viscous bondi inflow convergence to exercise all terms in the evolution equation of dP conv_2d() { - for res in 32 64 128 256 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ - b_field/implicit=false - if [[ -d $res ]]; then - echo -e "Resolution directory exists. Clearing existing files in there and copying new files\n" - rm -r ${res} - else - mkdir $res - fi - . /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - conda activate pyharm - pyharm-convert --double *.phdf - conda deactivate - cp -r ./bondi_viscous.out0*.h5 $res - mv bondi_viscous.out0.00000.h5 emhd_2d_${res}_start.h5 - mv bondi_viscous.out0.final.h5 emhd_2d_${res}_end.h5 - rm -r ./bondi_viscous* + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + + mv bondi_viscous.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv bondi_viscous.out0.final.phdf emhd_2d_${res}_end_${1}.phdf done + check_code=0 + pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + rm -r *.phdf + rm -r *.xdmf + rm -r *.out0* + if [[ $check_code != 0 ]]; then + echo Viscous Bondi test $3 FAIL: $check_code + exit_code=1 + else + echo Viscous Bondi test $3 success + fi } -conv_2d +ALL_RES="32,64,128,256" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Viscous Bondi in 2D, WENO5" + +exit $exit_code From 7782c3c59b17af4b1c7e79f4c9048dcd05a0b853 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Wed, 30 Nov 2022 17:40:31 -0600 Subject: [PATCH 003/219] Added viscous Bondi to gitlab-ci.yml --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f327a084..2261c4a4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,6 +78,12 @@ emhdmodes: - cd tests/emhdmodes - ./run.sh +bondi_viscous: + stage: tests + script: + - cd tests/bondi_viscous + - ./run.sh + noh: stage: tests script: From cc17ad7000b3cdb6366b3a111693efa461688530 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 1 Dec 2022 15:22:33 -0600 Subject: [PATCH 004/219] Provide with an option for the EMHD sector to feedback onto the ideal sector ``. This allows integrating viscous Bondi problem into this branch. --- kharma/emhd/emhd.cpp | 9 +++++++-- kharma/emhd/emhd.hpp | 28 ++++++++++++++++++---------- kharma/flux.hpp | 12 ++++++------ kharma/flux_functions.hpp | 4 ++-- kharma/grmhd/source.cpp | 4 ++-- kharma/reductions/reductions.hpp | 2 +- pars/bondi_viscous.par | 21 +++++++++++---------- pars/emhdmodes.par | 10 ++++++---- 8 files changed, 53 insertions(+), 37 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 7f2f8833..ce6d857c 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -64,18 +64,23 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // We share implementations in one function, controlled by these parameters // These are always necessary for performing EGRMHD. - bool higher_order_terms = pin->GetOrAddBoolean("emhd", "higher_order_terms", false); + bool higher_order_terms = pin->GetOrAddBoolean("emhd", "higher_order_terms", false); std::string closure_type = pin->GetOrAddString("emhd", "closure_type", "torus"); + // Should the EMHD sector feedback onto the ideal MHD variables? The default is 'yes'. + // So far it's just the viscous Bondi problem that doesn't require feedback + bool feedback = pin->GetOrAddBoolean("emhd", "feedback", true); + Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); - Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); + Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); Real kappa = pin->GetOrAddReal("emhd", "kappa", 1.0); Real eta = pin->GetOrAddReal("emhd", "eta", 1.0); EMHD_parameters emhd_params; emhd_params.higher_order_terms = higher_order_terms; + emhd_params.feedback = feedback; if (closure_type == "constant") { emhd_params.type = ClosureType::constant; } else if (closure_type == "sound_speed" || closure_type == "soundspeed") { diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 9743558e..96c24a1f 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -55,6 +55,7 @@ class EMHD_parameters { public: bool higher_order_terms; + bool feedback; ClosureType type; Real tau; Real conduction_alpha; @@ -297,22 +298,29 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Real& r * Entirely local! */ KOKKOS_INLINE_FUNCTION void calc_tensor(const Real& rho, const Real& u, const Real& pgas, + const EMHD::EMHD_parameters& emhd_params, const Real& q, const Real& dP, const FourVectors& D, const int& dir, Real emhd[GR_DIM]) { - const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); - const Real eta = pgas + rho + u + bsq; + const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); + const Real eta = pgas + rho + u + bsq; const Real ptot = pgas + 0.5 * bsq; - DLOOP1 { - emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] - + ptot * (dir == mu) - - D.bcon[dir] * D.bcov[mu] - + (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + - (D.bcon[dir] * D.ucov[mu])) - - dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); + if (!emhd_params.feedback) { + DLOOP1 { + emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + + ptot * (dir == mu) + - D.bcon[dir] * D.bcov[mu]; + } + } else { + DLOOP1 { + emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + + ptot * (dir == mu) + - D.bcon[dir] * D.bcov[mu] + + (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])) + - dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); + } } } diff --git a/kharma/flux.hpp b/kharma/flux.hpp index 0fe89036..bbf4c141 100644 --- a/kharma/flux.hpp +++ b/kharma/flux.hpp @@ -111,17 +111,17 @@ inline TaskStatus GetFlux(MeshData *md) Flag(md, "Recon and flux"); // Pointers auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Exit on trivial operations const int ndim = pmesh->ndim; if (ndim < 3 && dir == X3DIR) return TaskStatus::complete; if (ndim < 2 && dir == X2DIR) return TaskStatus::complete; // Options - const auto& pars = pmb0->packages.Get("GRMHD")->AllParams(); - const auto& globals = pmb0->packages.Get("Globals")->AllParams(); + const auto& pars = pmb0->packages.Get("GRMHD")->AllParams(); + const auto& globals = pmb0->packages.Get("Globals")->AllParams(); const auto& floor_pars = pmb0->packages.Get("Floors")->AllParams(); - const bool use_hlle = pars.Get("use_hlle"); + const bool use_hlle = pars.Get("use_hlle"); // Apply post-reconstruction floors. // Only enabled for WENO since it is not TVD, and only when other // floors are enabled. @@ -132,9 +132,9 @@ inline TaskStatus GetFlux(MeshData *md) // Check presence of different packages const auto& pkgs = pmb0->packages.AllPackages(); const bool use_b_flux_ct = pkgs.count("B_FluxCT"); - const bool use_b_cd = pkgs.count("B_CD"); + const bool use_b_cd = pkgs.count("B_CD"); const bool use_electrons = pkgs.count("Electrons"); - const bool use_emhd = pkgs.count("EMHD"); + const bool use_emhd = pkgs.count("EMHD"); // Pull flag indicating primitive variables const MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); diff --git a/kharma/flux_functions.hpp b/kharma/flux_functions.hpp index a1c8052a..ee90c25f 100644 --- a/kharma/flux_functions.hpp +++ b/kharma/flux_functions.hpp @@ -62,7 +62,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, EMHD::convert_prims_to_q_dP(P(m_p.Q), P(m_p.DP), P(m_p.RHO), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor - EMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), q, dP, D, dir, T); + EMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), emhd_params, q, dP, D, dir, T); } else if (m_p.B1 >= 0) { // GRMHD stress-energy tensor w/ first index up, second index down GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); @@ -193,7 +193,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor - EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), q, dP, D, dir, T); + EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), emhd_params, q, dP, D, dir, T); } else if (m_p.B1 >= 0) { // GRMHD stress-energy tensor w/ first index up, second index down GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); diff --git a/kharma/grmhd/source.cpp b/kharma/grmhd/source.cpp index a872f173..47a79d07 100644 --- a/kharma/grmhd/source.cpp +++ b/kharma/grmhd/source.cpp @@ -67,8 +67,8 @@ TaskStatus GRMHD::AddSource(MeshData *md, MeshData *mdudt) // Get stuff we don't want to recalculate every loop iteration // This is basically a manual version of GRMHD::calc_tensor but saves recalculating e.g. dot(bcon, bcov) 4 times Real pgas = (gam - 1) * P(b, m_p.UU, k, j, i); - Real bsq = dot(D.bcon, D.bcov); - Real eta = pgas + P(b, m_p.RHO, k, j, i) + P(b, m_p.UU, k, j, i) + bsq; + Real bsq = dot(D.bcon, D.bcov); + Real eta = pgas + P(b, m_p.RHO, k, j, i) + P(b, m_p.UU, k, j, i) + bsq; Real ptot = pgas + 0.5 * bsq; // Contract mhd stress tensor with connection, and multiply by metric determinant diff --git a/kharma/reductions/reductions.hpp b/kharma/reductions/reductions.hpp index 42795c72..82e041f1 100644 --- a/kharma/reductions/reductions.hpp +++ b/kharma/reductions/reductions.hpp @@ -96,7 +96,7 @@ Real DomainSum(MeshData *md, const Real& radius); GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); \ DLOOP1 Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, mu, T[mu]); \ GReal gdA = G.dx3v(k) * G.dx2v(j) * G.gdet(Loci::center, j, i); \ - GReal dA = G.dx3v(k) * G.dx2v(j); \ + GReal dA = G.dx3v(k) * G.dx2v(j); \ fn \ } \ , sum_reducer); \ diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index 99d0f8fe..8daa9de2 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -36,16 +36,6 @@ implicit = true implicit = false initial_cleanup = false -# IMPORTANT: This block must be present and values filled in all EGRMHD simulations - -on = true -higher_order_terms = true - -closure_type = kappa_eta -tau = 30. -kappa = 0.0 -eta = 0.01 - type = imex @@ -57,6 +47,17 @@ linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 +# IMPORTANT: This block must be present and values filled in all EGRMHD simulations + +on = true +higher_order_terms = true +feedback = false + +closure_type = kappa_eta +tau = 30. +kappa = 0.0 +eta = 0.01 + mdot = 1.0 rs = 8.0 diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 6c955b88..6299e56e 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -85,12 +85,14 @@ flag_verbose = 0 # This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = false -closure_type = sound_speed -tau = 1.0 +feedback = true + +closure_type = sound_speed +tau = 1.0 conduction_alpha = 1.0 -viscosity_alpha = 1.0 +viscosity_alpha = 1.0 file_type = hdf5 From 8b8df06078ef1ddf314458d0efaa19ad56b7bc03 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Mon, 5 Dec 2022 00:47:37 -0600 Subject: [PATCH 005/219] Save L2 norm of the residual at each zone and instances of when the linesearch failed, ie. when even manual backtracking was not sufficient as well. TODO: 1. Provide the option to average over neighboring good zones when a particular zone fails to converge to physically meaningful primitives. 2. Provide a runtime option to save the components of the residual. Useful for debugging. I've made some edits towards initializing an EMHD torus but it doesn't work (ctop is NaN within a few timesteps). --- kharma/emhd/emhd.cpp | 4 +- kharma/floors/floors.hpp | 28 ++-- kharma/flux.hpp | 2 +- kharma/implicit/implicit.cpp | 153 +++++++++++++++++--- kharma/prob/emhd/emhdmodes.hpp | 8 +- kharma/prob/emhd/fm_torus_emhd.cpp | 218 +++++++++++++++++++++++++++++ kharma/prob/fm_torus.cpp | 42 +++--- kharma/prob/fm_torus.hpp | 3 + kharma/prob/problem.cpp | 2 + kharma/types.hpp | 4 +- pars/bondi_viscous.par | 2 +- pars/emhdmodes.par | 4 +- pars/sane_emhd.par | 106 ++++++++++++++ 13 files changed, 509 insertions(+), 67 deletions(-) create mode 100644 kharma/prob/emhd/fm_torus_emhd.cpp create mode 100644 pars/sane_emhd.par diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index ce6d857c..0cb04b02 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -71,7 +71,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // So far it's just the viscous Bondi problem that doesn't require feedback bool feedback = pin->GetOrAddBoolean("emhd", "feedback", true); - Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); + Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); @@ -109,7 +109,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // Floors specific to EMHD calculations? Currently only need to enforce bsq>0 in one denominator MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isEMHD = Metadata::AllocateNewFlag("EMHDFlag"); + MetadataFlag isEMHD = Metadata::AllocateNewFlag("EMHDFlag"); params.Add("EMHDFlag", isEMHD); // General options for primitive and conserved scalar variables in ImEx driver diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index bff165ec..c212e75e 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -365,8 +365,8 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa } else { // Add the material in the normal observer frame, by: // Adding the floors to the primitive variables - const Real rho_add = m::max(0., rhoflr_max - rho); - const Real u_add = m::max(0., uflr_max - u); + const Real rho_add = m::max(0., rhoflr_max - rho); + const Real u_add = m::max(0., uflr_max - u); const Real uvec[NVEC] = {0}, B[NVEC] = {0}; // Calculating the corresponding conserved variables @@ -440,16 +440,16 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, co if (floors.use_r_char) { // Steeper floor from iharm3d Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); - rhoflr_geom = floors.rho_min_geom * rhoscal; - uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); } else { // Original floors from iharm2d rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d } } else { rhoflr_geom = floors.rho_min_geom; - uflr_geom = floors.u_min_geom; + uflr_geom = floors.u_min_geom; } int fflag = 0; @@ -461,7 +461,7 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, co #endif P(m.RHO) += m::max(0., rhoflr_geom - P(m.RHO)); - P(m.UU) += m::max(0., uflr_geom - P(m.UU)); + P(m.UU) += m::max(0., uflr_geom - P(m.UU)); return fflag; } @@ -481,16 +481,16 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, c if (floors.use_r_char) { // Steeper floor from iharm3d Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); - rhoflr_geom = floors.rho_min_geom * rhoscal; - uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); } else { // Original floors from iharm2d rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d } } else { rhoflr_geom = floors.rho_min_geom; - uflr_geom = floors.u_min_geom; + uflr_geom = floors.u_min_geom; } int fflag = 0; @@ -502,7 +502,7 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, c #endif P(m.RHO, k, j, i) += m::max(0., rhoflr_geom - P(m.RHO, k, j, i)); - P(m.UU, k, j, i) += m::max(0., uflr_geom - P(m.UU, k, j, i)); + P(m.UU, k, j, i) += m::max(0., uflr_geom - P(m.UU, k, j, i)); return fflag; } @@ -528,8 +528,8 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Real rho = P(m_p.RHO, k, j, i); Real uu = P(m_p.UU, k, j, i); - Real qtilde = P(m_p.Q, k, j, i); - Real dPtilde = P(m_p.DP, k, j, i); + Real qtilde = P(m_p.Q, k, j, i); + Real dPtilde = P(m_p.DP, k, j, i); Real pg = (gam - 1.) * uu; Real Theta = pg / rho; diff --git a/kharma/flux.hpp b/kharma/flux.hpp index bbf4c141..07f58102 100644 --- a/kharma/flux.hpp +++ b/kharma/flux.hpp @@ -152,7 +152,7 @@ inline TaskStatus GetFlux(MeshData *md) // Pack variables. Keep ctop separate PackIndexMap prims_map, cons_map; - const auto& ctop = md->PackVariables(std::vector{"ctop"}); + const auto& ctop = md->PackVariables(std::vector{"ctop"}); const auto& P_all = md->PackVariables(std::vector{isPrimitive}, prims_map); const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index abe59319..dbbbc2f6 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -110,6 +110,56 @@ std::shared_ptr Initialize(ParameterInput *pin) MetadataFlag isExplicit = Metadata::AllocateNewFlag("Explicit"); params.Add("ExplicitFlag", isExplicit); + // Allocate additional fields that reflect the success of the solver + // L2 norm of the residual + Metadata m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("solve_norm", m_real); + // Integer field that saves where the solver fails (rho + drho < 0 || u + du < 0) + // Metadata m_int = Metadata({Metadata::Integer, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariabl + + bool print_residual = pin->GetOrAddBoolean("implicit", "print_residual", false); + params.Add("print_residual", print_residual); + // TODO: Find a way to save residuals based on a runtime parameter. We don't want to unnecessarily allocate + // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. For now, + // we just print the value of the residual if the norm exceeds the max_norm.s + // Should the solve save the residual vector field? Useful for debugging purposes. Default is NO. + // bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); + // params.Add("save_residual", save_residual); + + // Vector field to store residual components (only for those variables that are evolved implicitly) + // if (save_residual) { + // auto driver_type = pin->GetString("driver", "type"); + // bool grmhd_implicit = (driver_type == "imex") && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); + // bool implicit_b = (driver_type == "imex") && (pin->GetOrAddBoolean("b_field", "implicit", grmhd_implicit)); + // bool emhd_enabled = pin->GetOrAddBoolean("emhd", "on", false); + // int nvars_implicit = 0; + // if (grmhd_implicit){ + // if (emhd_enabled) { + // if (implicit_b) { + // nvars_implicit = 10; + // } + // else + // nvars_implicit = 7; + // } else { + // if (implicit_b) { + // nvars_implicit = 8; + // } + // else + // nvars_implicit = 6; + // } + // } + // const int nfvar = nvars_implicit; + + // // flags_vec = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + // // auto flags_vec(flags_vec); + // // flags_vec.push_back(Metadata::Vector); + // std::vector s_vector({nfvar}); + // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); + // pkg->AddField("residual", m); + // } + + // Anything we need to run from this package on callbacks // Maybe a post-step L2 or flag count or similar // pkg->PostFillDerivedBlock = Implicit::PostFillDerivedBlock; @@ -146,6 +196,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i const Real linesearch_eps = implicit_par.Get("linesearch_eps"); const Real linesearch_lambda = implicit_par.Get("linesearch_lambda"); + const bool print_residual = implicit_par.Get("print_residual"); + // const bool save_residual = implicit_par.Get("save_residual"); + // Misc other constants for inside the kernel const bool am_rank0 = MPIRank0(); const Real tiny(SMALL), alpha(1.0); @@ -198,19 +251,28 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P_full_step_init_implicit.GetDim(4); + // Pull fields associated with the solver's performance + auto& solve_norm_all = md_solver->PackVariables(std::vector{"solve_norm"}); + auto& solve_fail_all = md_solver->PackVariables(std::vector{"solve_fail"}); + // auto& solve_fail_all = md_solver->GetBlockData(0)->Get("solve_fail").data; + + // if (save_residual) { + // auto& residual_all = md_solver->GetBlockData(0)->Get("residual").data; + // } + auto bounds = pmb_sub_step_init->cellbounds; const int n1 = bounds.ncellsi(IndexDomain::entire); const int n2 = bounds.ncellsj(IndexDomain::entire); const int n3 = bounds.ncellsk(IndexDomain::entire); // RETURN if there aren't any implicit variables to evolve - //std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; + // std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; if (nfvar == 0) return TaskStatus::complete; // The norm of the residual. We store this to avoid the main kernel // also being a 2-stage reduction, which is complex and sucks. // TODO keep this around as a field? - ParArray4D norm_all("norm_all", nblock, n3, n2, n1); + // ParArray4D norm_all("norm_all", nblock, n3, n2, n1); // EDIT // Get meshblock array bounds from Parthenon const IndexDomain domain = IndexDomain::interior; @@ -230,12 +292,17 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nvar, n1); const size_t fvar_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nfvar, n1); const size_t tensor_size_in_bytes = parthenon::ScratchPad3D::shmem_size(nfvar, nfvar, n1); + const size_t scalar_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); + const size_t int_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); // Allocate enough to cache: // jacobian (2D) - // residual, deltaP (implicit only) - // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, P_solver, P_linesearch, - // dU_implicit, three temps (all vars) - const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (11) * var_size_in_bytes; + // residual, deltaP, trans, work (implicit only) + // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, + // P_solver, P_linesearch, dU_implicit, three temps (all vars) + // solve_norm, solve_fail + const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (11) * var_size_in_bytes + \ + (2) * scalar_size_in_bytes; + // + int_size_in_bytes; // Iterate. This loop is outside the kokkos kernel in order to print max_norm // There are generally a low and similar number of iterations between @@ -266,6 +333,10 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i ScratchPad2D flux_src_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_solver_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), nvar, n1); + // Scratchpads for solver performance diagnostics + ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); + // ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); + ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); // Copy some file contents to scratchpads, so we can slice them PLOOP { @@ -279,6 +350,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); P_linesearch_s(ip, i) = P_linesearch_all(b)(ip, k, j, i); dU_implicit_s(ip, i) = 0.; + + solve_norm_s(i) = 0.; + solve_fail_s(i) = 0; } ); } @@ -310,14 +384,18 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); - auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); - auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); + auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); + auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); // Temporaries auto tmp1 = Kokkos::subview(tmp1_s, Kokkos::ALL(), i); auto tmp2 = Kokkos::subview(tmp2_s, Kokkos::ALL(), i); auto tmp3 = Kokkos::subview(tmp3_s, Kokkos::ALL(), i); // Implicit sources at starting state auto dU_implicit = Kokkos::subview(dU_implicit_s, Kokkos::ALL(), i); + // Solver performance diagnostics + auto solve_norm = Kokkos::subview(solve_norm_s, i); + auto solve_fail = Kokkos::subview(solve_fail_s, i); + if (m_p.Q >= 0) { EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, emhd_params_sub_step_init, dU_implicit(m_u.Q), dU_implicit(m_u.DP)); @@ -366,28 +444,41 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked> ::invoke(alpha, jacobian, delta_prim); - // Update the guess + // Check for positive definite values of density and internal energy. + // Break from solve if manual backtracking is not sufficient. + // The primitives will be averaged over good neighbors. + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = 1; + lambda = 0.1; + } + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = 2; + // break; // Doesn't break from the inner par_for. + // Let it continue for now, but we'll average over the zone later + } + + // Linesearch if (linesearch) { - norm_all(b, k, j, i) = 0; - FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); - norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); - Real f0 = 0.5 * norm_all(b, k, j, i); + Real f0 = 0.5 * solve_norm(); Real fprime0 = -2. * f0; for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { // Take step FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); - // Compute norm of the residual (loss function) + // Compute solve_norm of the residual (loss function) calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, k, j, i, gam, dt, residual); - norm_all(b, k, j, i) = 0; - FLOOP norm_all(b, k, j, i) += residual(ip) * residual(ip); - norm_all(b, k, j, i) = sqrt(norm_all(b, k, j, i)); - Real f1 = 0.5 * norm_all(b, k, j, i); + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); + Real f1 = 0.5 * solve_norm(); // Compute new step length int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); @@ -400,6 +491,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i } } + // Update the guess FLOOP P_solver(ip) += lambda * delta_prim(ip); calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, @@ -415,21 +507,38 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Store for maximum/output // I would be tempted to store the whole residual, but it's of variable size - norm_all(b, k , j, i) = 0; - FLOOP norm_all(b, k, j, i) += residual(ip)*residual(ip); - norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); // TODO faster to scratch cache & copy? + + if (print_residual) { + if (solve_norm_s(i) > rootfind_tol) { + FLOOP std::cout< *md_full_step_init, MeshData *md_sub_step_i Kokkos::Max norm_max(max_norm.val); pmb_sub_step_init->par_reduce("max_norm", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_MESH_3D_REDUCE { - if (norm_all(b, k, j, i) > local_result) local_result = norm_all(b, k, j, i); + if (solve_norm_all(b, 0, k, j, i) > local_result) local_result = solve_norm_all(b, 0, k, j, i); } , norm_max); // Then MPI reduce it diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 7763866c..1a8cfa31 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -51,13 +51,13 @@ TaskStatus InitializeEMHDModes(MeshBlockData *rc, ParameterInput *pin) { Flag(rc, "Initializing EMHD Modes problem"); auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; // It is well and good this problem should cry if B/EMHD are disabled. GridVector B_P = rc->Get("prims.B").data; - GridVector q = rc->Get("prims.q").data; - GridVector dP = rc->Get("prims.dP").data; + GridVector q = rc->Get("prims.q").data; + GridVector dP = rc->Get("prims.dP").data; const auto& G = pmb->coords; diff --git a/kharma/prob/emhd/fm_torus_emhd.cpp b/kharma/prob/emhd/fm_torus_emhd.cpp new file mode 100644 index 00000000..f0041de9 --- /dev/null +++ b/kharma/prob/emhd/fm_torus_emhd.cpp @@ -0,0 +1,218 @@ +/* + * File: fm_torus.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../fm_torus.hpp" + +#include "mpi.hpp" +#include "prob_common.hpp" +#include "types.hpp" + +#include +#include "Kokkos_Random.hpp" + +TaskStatus InitializeFMTorusEMHD(MeshBlockData *rc, ParameterInput *pin) +{ + Flag(rc, "Initializing torus problem"); + + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + GridVector B_P = rc->Get("prims.B").data; + + // This problem init is exclusively for the EMHD torus; get copies of q and dP + const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", true); + GridVector q = rc->Get("prims.q").data; + GridVector dP = rc->Get("prims.dP").data; + const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); + + const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); + const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); + const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); + const GReal tilt_deg = pin->GetOrAddReal("torus", "tilt", 0.0); + const GReal tilt = tilt_deg / 180. * M_PI; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + IndexDomain domain = IndexDomain::interior; + const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); + const int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); + const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); + + // Get coordinate systems + // G clearly holds a reference to an existing system G.coords.base, + // but we don't know if it's KS or BL coordinates + // Since we can't create a system and assign later, we just + // rebuild copies of both based on the BH spin "a" + const auto& G = pmb->coords; + const bool use_ks = G.coords.is_ks(); + const GReal a = G.coords.get_a(); + const SphBLCoords blcoords = SphBLCoords(a); + const SphKSCoords kscoords = SphKSCoords(a); + + // Fishbone-Moncrief parameters + Real l = lfish_calc(a, rmax); + + pmb->par_for("fm_torus_init", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA_3D { + GReal Xnative[GR_DIM], Xembed[GR_DIM], Xmidplane[GR_DIM]; + G.coord(k, j, i, Loci::center, Xnative); + G.coord_embed(k, j, i, Loci::center, Xembed); + // What are our corresponding "midplane" values for evaluating the function? + rotate_polar(Xembed, tilt, Xmidplane); + + GReal r = Xmidplane[1], th = Xmidplane[2]; + GReal sth = sin(th); + GReal cth = cos(th); + + Real lnh = lnh_calc(a, l, rin, r, th); + + // Region inside magnetized torus; u^i is calculated in + // Boyer-Lindquist coordinates, as per Fishbone & Moncrief, + // so it needs to be transformed at the end + // everything outside is left 0 to be added by the floors + if (lnh >= 0. && r >= rin) { + Real r2 = r*r; + Real a2 = a*a; + Real DD = r2 - 2. * r + a2; + Real AA = m::pow(r2 + a2, 2) - DD * a2 * sth * sth; + Real SS = r2 + a2 * cth * cth; + + // Calculate rho and u + Real hm1 = exp(lnh) - 1.; + Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), 1. / (gam - 1.)); + Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); + + // Calculate u^phi + Real expm2chi = SS * SS * DD / (AA * AA * sth * sth); + Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); + Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + + m::sqrt(SS / AA) * up1 / sth; + + const Real ucon_tilt[GR_DIM] = {0., 0., 0., up}; + Real ucon_bl[GR_DIM]; + rotate_polar_vec(Xmidplane, ucon_tilt, -tilt, Xembed, ucon_bl); + + Real gcov_bl[GR_DIM][GR_DIM]; + blcoords.gcov_embed(Xembed, gcov_bl); + set_ut(gcov_bl, ucon_bl); + + // Then transform that 4-vector to KS if necessary, + // and then to native coordinates + Real ucon_native[GR_DIM]; + if (use_ks) { + Real ucon_ks[GR_DIM]; + kscoords.vec_from_bl(Xembed, ucon_bl, ucon_ks); + G.coords.con_vec_to_native(Xnative, ucon_ks, ucon_native); + } else { + G.coords.con_vec_to_native(Xnative, ucon_bl, ucon_native); + } + + // Convert native 4-vector to primitive u-twiddle, see Gammie '04 + Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; + G.gcon(Loci::center, j, i, gcon); + fourvel_to_prim(gcon, ucon_native, u_prim); + + rho(k, j, i) = rho_l; + u(k, j, i) = u_l; + uvec(0, k, j, i) = u_prim[0]; + uvec(1, k, j, i) = u_prim[1]; + uvec(2, k, j, i) = u_prim[2]; + // EMHD variables + q(k, j, i) = 0.; + dP(k, j, i) = 0.; + } + } + ); + + // Find rho_max "analytically" by looking over the whole mesh domain for the maximum in the midplane + // Done device-side for speed (for large 2D meshes this may get bad) but may work fine in HostSpace + // Note this covers the full domain on each rank: it doesn't need a grid so it's not a memory problem, + // and an MPI synch as is done for beta_min would be a headache + GReal x1min = pmb->pmy_mesh->mesh_size.x1min; + GReal x1max = pmb->pmy_mesh->mesh_size.x1max; + // Add back 2D if torus solution may not be largest in midplane (before tilt ofc) + //GReal x2min = pmb->pmy_mesh->mesh_size.x2min; + //GReal x2max = pmb->pmy_mesh->mesh_size.x2max; + GReal dx = 0.001; + int nx1 = (x1max - x1min) / dx; + //int nx2 = (x2max - x2min) / dx; + + // If we print diagnostics, do so only from block 0 as the others do exactly the same thing + // Since this is initialization, we are guaranteed to have a block 0 + if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + std::cout << "Calculating maximum density:" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "dx = " << dx << std::endl; + std::cout << "x1min->x1max: " << x1min << " " << x1max << std::endl; + std::cout << "nx1 = " << nx1 << std::endl; + //cout << "x2min->x2max: " << x2min << " " << x2max << std::endl; + //cout << "nx2 = " << nx2 << std::endl; + } + + Real rho_max = 0; + Kokkos::Max max_reducer(rho_max); + pmb->par_reduce("fm_torus_maxrho", 0, nx1, + KOKKOS_LAMBDA_1D_REDUCE { + GReal x1 = x1min + i*dx; + //GReal x2 = x2min + j*dx; + GReal Xnative[GR_DIM] = {0,x1,0,0}; + GReal Xembed[GR_DIM]; + G.coords.coord_to_embed(Xnative, Xembed); + const GReal r = Xembed[1]; + // Regardless of native coordinate shenanigans, + // set th=pi/2 since the midplane is densest in the solution + const GReal rho = fm_torus_rho(a, rin, rmax, gam, kappa, r, M_PI/2.); + // TODO umax for printing/recording? + + // Record max + if (rho > local_result) local_result = rho; + } + , max_reducer); + + // Record and print normalization factor + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rho_norm"))) + pmb->packages.Get("GRMHD")->AllParams().Add("rho_norm", rho_max); + if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + std::cout << "Initial maximum density is " << rho_max << std::endl; + } + + pmb->par_for("fm_torus_normalize", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA_3D { + rho(k, j, i) /= rho_max; + u(k, j, i) /= rho_max; + } + ); + + return TaskStatus::complete; +} diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index 97cda914..d520512e 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -45,18 +45,21 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) { Flag(rc, "Initializing torus problem"); - auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; + GridVector B_P = rc->Get("prims.B").data; + + // Have a look at InitializeFMTorusEMHD for the EMHD torus initialization + const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", false); - const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); - const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); - const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); + const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); + const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); + const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); const GReal tilt_deg = pin->GetOrAddReal("torus", "tilt", 0.0); - const GReal tilt = tilt_deg / 180. * M_PI; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const GReal tilt = tilt_deg / 180. * M_PI; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); IndexDomain domain = IndexDomain::interior; const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); @@ -68,9 +71,9 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) // but we don't know if it's KS or BL coordinates // Since we can't create a system and assign later, we just // rebuild copies of both based on the BH spin "a" - const auto& G = pmb->coords; - const bool use_ks = G.coords.is_ks(); - const GReal a = G.coords.get_a(); + const auto& G = pmb->coords; + const bool use_ks = G.coords.is_ks(); + const GReal a = G.coords.get_a(); const SphBLCoords blcoords = SphBLCoords(a); const SphKSCoords kscoords = SphKSCoords(a); @@ -85,7 +88,7 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) // What are our corresponding "midplane" values for evaluating the function? rotate_polar(Xembed, tilt, Xmidplane); - GReal r = Xmidplane[1], th = Xmidplane[2]; + GReal r = Xmidplane[1], th = Xmidplane[2]; GReal sth = sin(th); GReal cth = cos(th); @@ -103,16 +106,15 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) Real SS = r2 + a2 * cth * cth; // Calculate rho and u - Real hm1 = exp(lnh) - 1.; - Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), - 1. / (gam - 1.)); - Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); + Real hm1 = exp(lnh) - 1.; + Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), 1. / (gam - 1.)); + Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); // Calculate u^phi Real expm2chi = SS * SS * DD / (AA * AA * sth * sth); - Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); - Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + - m::sqrt(SS / AA) * up1 / sth; + Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); + Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + + m::sqrt(SS / AA) * up1 / sth; const Real ucon_tilt[GR_DIM] = {0., 0., 0., up}; Real ucon_bl[GR_DIM]; diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index 3c1a8d02..165bd734 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -11,6 +11,9 @@ * @param rmax is the radius of maximum density of the F-M torus in r_g */ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin); +/* Need a different initialization function since we have additional fields (q, dP) + * for the EMHD problem that are declared at runtime*/ +TaskStatus InitializeFMTorusEMHD(MeshBlockData *rc, ParameterInput *pin); /** * Perturb the internal energy by a uniform random proportion per cell. * Resulting internal energies will be between u \pm u*u_jitter/2 diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 03ff1802..664cdad9 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -117,6 +117,8 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // Everything } else if (prob == "torus") { status = InitializeFMTorus(rc.get(), pin); + } else if (prob == "torus_emhd") { + status = InitializeFMTorusEMHD(rc.get(), pin); } else if (prob == "resize_restart") { status = ReadIharmRestart(rc.get(), pin); } diff --git a/kharma/types.hpp b/kharma/types.hpp index 6cd0325c..a0817ba4 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -224,12 +224,12 @@ inline void PrintZone(MeshBlockData *rc) auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - cerr << "RHO: " << rhop(0,0,100) + std::cerr << "RHO: " << rhop(0,0,100) << " UU: " << up(0,0,100) << " U: " << uvecp(0, 0,0,100) << " " << uvecp(1, 0,0,100)<< " " << uvecp(2, 0,0,100) << " B: " << Bp(0, 0,0,100) << " " << Bp(1, 0,0,100) << " " << Bp(2, 0,0,100) << " q: " << q(0,0,100) - << " dP: " << dP(0,0,100) << endl; + << " dP: " << dP(0,0,100) << std::endl; } inline void Flag(std::string label) diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index 8daa9de2..04f88ab8 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -75,7 +75,7 @@ verbose = 1 file_type = hdf5 dt = 100.0 single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail file_type = hst diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 6299e56e..b4ced3c7 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -70,6 +70,8 @@ max_linesearch_iter = 3 linesearch_eps = 1.e-4 use_qr = true +print_residual = false + # General verbosity level: @@ -100,7 +102,7 @@ file_type = hdf5 dt = 100.0 # Output in double due to low amplitude single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail file_type = hst diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par new file mode 100644 index 00000000..21a9208c --- /dev/null +++ b/pars/sane_emhd.par @@ -0,0 +1,106 @@ +# SANE model mirroring the simulation library +# Quite small to run for more than 10kM, 6M/12M F-M torus, +# Overall simulation size 1000M + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +nx1 = 32 +nx2 = 32 +nx3 = 32 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 4000.0 +nlim = -1 + + +type = imex +two_sync = true + + +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-3 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true +print_residual = false + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +implicit = false +type = sane +beta_min = 100. +initial_cleanup = true + +# This block must be present and values filled in all EGRMHD simulations + +on = true +higher_order_terms = true +feedback = true + +closure_type = torus +conduction_alpha = 1.0 +viscosity_alpha = 1.0 + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +archive_parameters = true +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +on = false +ne = 1.e-4 +Tp = 10 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, q, dP, jcon, fflag, pflag, solve_norm, solve_fail + + +file_type = rst +dt = 100.0 + + +file_type = hst +dt = 0.1 From ed1fdffc46d85ec4b4f2eb09cdd197e78b0ac460 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Mon, 5 Dec 2022 18:03:55 -0500 Subject: [PATCH 006/219] updated Bondi --- kharma/prob/bondi.hpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 53245a4d..de456c00 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -55,23 +55,36 @@ TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin); * * Used for initialization and boundary conditions */ -TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); +TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); // (Hyerin) why did you change it to interior? /** * Supporting functions for Bondi flow calculations * * Adapted from M. Chandra + * Modified by Hyerin Cho and Ramesh Narayan */ KOKKOS_INLINE_FUNCTION Real get_Tfunc(const Real T, const GReal r, const Real C1, const Real C2, const Real n) { return m::pow(1. + (1. + n) * T, 2.) * (1. - 2. / r + m::pow(C1 / m::pow(r,2) / m::pow(T, n), 2.)) - C2; } -KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n) +KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n, const Real rs) { Real rtol = 1.e-12; Real ftol = 1.e-14; - Real Tmin = 0.6 * (m::sqrt(C2) - 1.) / (n + 1); - Real Tmax = m::pow(C1 * m::sqrt(2. / m::pow(r,3)), 1. / n); + Real Tinf = (m::sqrt(C2) - 1.) / (n + 1); // temperature at infinity + Real Tnear = m::pow(C1 * m::sqrt(2. / m::pow(r,3)), 1. / n); // temperature near the BH + Real Tmin, Tmax; + + // There are two branches of solutions (see Michel et al. 1971) and the two branches cross at rs. + // These bounds are set to only select the inflowing solution only. + if (r 0) return -1; - Th = (f1 * T0 - f0 * T1) / (f1 - f0); + Th = (T0 + T1) / 2.; // a simple bisection method which is stable and fast fh = get_Tfunc(Th, r, C1, C2, n); Real epsT = rtol * (Tmin + Tmax); while (m::abs(Th - T0) > epsT && m::abs(Th - T1) > epsT && m::abs(fh) > ftol) { - if (fh * f0 < 0.) { + if (fh * f0 > 0.) { T0 = Th; f0 = fh; } else { @@ -94,7 +107,7 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c f1 = fh; } - Th = (f1 * T0 - f0 * T1) / (f1 - f0); + Th = (T0 + T1) / 2.; fh = get_Tfunc(Th, r, C1, C2, n); } @@ -128,7 +141,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const Coordin // be a little cautious about initializing the Ergosphere zones if (ks.a > 0.1 && r < 2) return; - Real T = get_T(r, C1, C2, n); + Real T = get_T(r, C1, C2, n, rs); Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real rho = m::pow(T, n); Real u = rho * T * n; From 0d34701d6dd7ebe5596ccaafb1d0532de5c4dec1 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 7 Dec 2022 08:21:42 -0500 Subject: [PATCH 007/219] fixed hdf5_utils to be more generally used --- kharma/prob/hdf5_utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index 95ef1430..84f32256 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -391,10 +391,11 @@ int hdf5_read_single_val(void *val, const char *name, hsize_t hdf5_type) int hdf5_read_array(void *data, const char *name, size_t rank, hsize_t *fdims, hsize_t *fstart, hsize_t *fcount, hsize_t *mdims, hsize_t *mstart, hsize_t hdf5_type) { - hid_t filespace = H5Screate_simple(4, fdims, NULL); + //hid_t filespace = H5Screate_simple(4, fdims, NULL); + hid_t filespace = H5Screate_simple(rank, fdims, NULL); // edited by Hyerin H5Sselect_hyperslab(filespace, H5S_SELECT_SET, fstart, NULL, fcount, NULL); - hid_t memspace = H5Screate_simple(4, mdims, NULL); + hid_t memspace = H5Screate_simple(rank, mdims, NULL); H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mstart, NULL, fcount, NULL); From bb288cad7f2dce69bc78740fd7e6998f68f02859 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 7 Dec 2022 08:24:06 -0500 Subject: [PATCH 008/219] updated hdf5_utils --- kharma/prob/hdf5_utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index 95ef1430..84f32256 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -391,10 +391,11 @@ int hdf5_read_single_val(void *val, const char *name, hsize_t hdf5_type) int hdf5_read_array(void *data, const char *name, size_t rank, hsize_t *fdims, hsize_t *fstart, hsize_t *fcount, hsize_t *mdims, hsize_t *mstart, hsize_t hdf5_type) { - hid_t filespace = H5Screate_simple(4, fdims, NULL); + //hid_t filespace = H5Screate_simple(4, fdims, NULL); + hid_t filespace = H5Screate_simple(rank, fdims, NULL); // edited by Hyerin H5Sselect_hyperslab(filespace, H5S_SELECT_SET, fstart, NULL, fcount, NULL); - hid_t memspace = H5Screate_simple(4, mdims, NULL); + hid_t memspace = H5Screate_simple(rank, mdims, NULL); H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mstart, NULL, fcount, NULL); From a2cbd02bcf3376f3b22aaf089f94409aa9e8dc25 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 7 Dec 2022 08:36:20 -0500 Subject: [PATCH 009/219] Modified bondi such that it can also do bondi_shell, added resize_restart_kharma problem, and some more updates --- kharma/boundaries.cpp | 11 + kharma/kharma.cpp | 7 + kharma/main.cpp | 3 +- kharma/prob/bondi.cpp | 10 +- kharma/prob/bondi.hpp | 12 +- kharma/prob/problem.cpp | 12 +- kharma/prob/resize_restart_kharma.cpp | 427 ++++++++++++++++++++++++++ kharma/prob/resize_restart_kharma.hpp | 157 ++++++++++ machines/cannon_ramesh.sh | 31 ++ 9 files changed, 665 insertions(+), 5 deletions(-) create mode 100644 kharma/prob/resize_restart_kharma.cpp create mode 100644 kharma/prob/resize_restart_kharma.hpp create mode 100755 machines/cannon_ramesh.sh diff --git a/kharma/boundaries.cpp b/kharma/boundaries.cpp index d91700d0..86d32a19 100644 --- a/kharma/boundaries.cpp +++ b/kharma/boundaries.cpp @@ -47,6 +47,7 @@ #include "bondi.hpp" #include "emhd/conducting_atmosphere.hpp" #include "emhd/bondi_viscous.hpp" +#include "resize_restart_kharma.hpp" // Hyerin //#include "hubble.hpp" // Going to need all modules' headers here @@ -226,10 +227,18 @@ void KBoundaries::InnerX1(std::shared_ptr> &rc, bool coarse) // TODO implement as named callback, give combo start/bound problems their own "packages" auto pmb = rc->GetBlockPointer(); std::string prob = pmb->packages.Get("GRMHD")->Param("problem"); + Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin if (prob == "hubble") { //SetHubble(rc.get(), IndexDomain::inner_x1, coarse); } else if (prob == "conducting_atmosphere"){ dirichlet_bc(rc.get(), IndexDomain::inner_x1, coarse); + } else if ((prob == "resize_restart_kharma")&& (x1min>1)){ + // Hyerin (if the inner x1 bound is far from BH, constant bc) + SetKharmaRestart(rc.get(), IndexDomain::inner_x1,coarse); + } else if ((prob == "bondi") && (x1min>1)){ // Hyerin + SetBondi(rc.get(), IndexDomain::inner_x1,coarse); + //} else if ((prob == "gizmo_shell") && (x1min>1)){ // Hyerin + // SetGizmoShell(rc.get(), IndexDomain::inner_x1,coarse); } else { OutflowX1(rc, IndexDomain::inner_x1, coarse); } @@ -250,6 +259,8 @@ void KBoundaries::OuterX1(std::shared_ptr> &rc, bool coarse) dirichlet_bc(rc.get(), IndexDomain::outer_x1, coarse); } else if (prob == "bondi_viscous") { SetBondiViscous(rc.get(), IndexDomain::outer_x1, coarse); + } else if (prob == "resize_restart_kharma") { // Hyerin, constant boundary condition + SetKharmaRestart(rc.get(),IndexDomain::outer_x1, coarse); } else { OutflowX1(rc, IndexDomain::outer_x1, coarse); } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 89e5d6a3..91aeb899 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -56,6 +56,7 @@ #include "boundaries.hpp" #include "harm_driver.hpp" #include "resize_restart.hpp" +#include "resize_restart_kharma.hpp" std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin) { @@ -109,6 +110,9 @@ void KHARMA::FixParameters(std::unique_ptr& pin) if (prob == "resize_restart") { ReadIharmRestartHeader(pin->GetString("resize_restart", "fname"), pin); } + if (prob == "resize_restart_kharma") { + ReadKharmaRestartHeader(pin->GetString("resize_restart", "fname"), pin); + } // Then handle coordinate systems and boundaries! std::string coordinate_base = pin->GetString("coordinates", "base"); @@ -142,6 +146,9 @@ void KHARMA::FixParameters(std::unique_ptr& pin) GReal Rin = pin->GetReal("coordinates", "r_in"); GReal x1min = log_r ? log(Rin) : Rin; pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); + if (Rin < 2.5){ // warn to check if there are 5 zones inside the event horizon + std::cout << "Hyerin: Rin = " << Rin << ". Check if there are 5 zones inside the EH." << std::endl; + } } else { int nx1 = pin->GetInteger("parthenon/mesh", "nx1"); Real a = pin->GetReal("coordinates", "a"); diff --git a/kharma/main.cpp b/kharma/main.cpp index e1053b8b..06751bf2 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -147,7 +147,8 @@ int main(int argc, char *argv[]) std::cout << "Running post-initialization tasks..." << std::endl; auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_restart = (prob == "resize_restart") || pman.IsRestart(); + //bool is_restart = (prob == "resize_restart") || pman.IsRestart(); + bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); // Hyerin bool is_resize = (prob == "resize_restart") && !pman.IsRestart(); KHARMA::PostInitialize(pin, pmesh, is_restart, is_resize); Flag("Post-initialization completed"); diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index e4ad747e..094a4110 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -45,12 +45,16 @@ TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin) const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); const Real rs = pin->GetOrAddReal("bondi", "rs", 8.0); + // r_shell : the radius of the shell where inside this radius is filled with vacuum. If 0, the simulation is initialized to Bondi everywhere + const Real r_shell = pin->GetOrAddReal("bondi", "r_shell", 0.); // Add these to package properties, since they continue to be needed on boundaries if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot"))) pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rs"))) pmb->packages.Get("GRMHD")->AddParam("rs", rs); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("r_shell"))) + pmb->packages.Get("GRMHD")->AddParam("r_shell", r_shell); // Set the whole domain to the analytic solution to begin SetBondi(rc); @@ -72,6 +76,7 @@ TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain, bool coarse) const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const Real r_shell = pmb->packages.Get("GRMHD")->Param("r_shell"); // Just the X1 right boundary GRCoordinates G = pmb->coords; @@ -87,6 +92,9 @@ TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain, bool coarse) if (domain == IndexDomain::outer_x1) { ibs = bounds.GetBoundsI(IndexDomain::interior).e+1; ibe = bounds.GetBoundsI(IndexDomain::entire).e; + } else if (domain == IndexDomain::inner_x1) { + ibs = bounds.GetBoundsI(IndexDomain::entire).s; + ibe = bounds.GetBoundsI(IndexDomain::interior).s-1; } else { ibs = bounds.GetBoundsI(domain).s; ibe = bounds.GetBoundsI(domain).e; @@ -95,7 +103,7 @@ TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain, bool coarse) IndexRange kb_e = bounds.GetBoundsK(IndexDomain::entire); pmb->par_for("bondi_boundary", kb_e.s, kb_e.e, jb_e.s, jb_e.e, ibs, ibe, KOKKOS_LAMBDA_3D { - get_prim_bondi(G, cs, P, m_p, gam, bl, ks, mdot, rs, k, j, i); + get_prim_bondi(G, cs, P, m_p, gam, bl, ks, mdot, rs, r_shell, k, j, i); // TODO all flux GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u); } diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index de456c00..119940c1 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -55,7 +55,7 @@ TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin); * * Used for initialization and boundary conditions */ -TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); // (Hyerin) why did you change it to interior? +TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); /** * Supporting functions for Bondi flow calculations @@ -122,7 +122,7 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c */ KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, const Real& gam, const SphBLCoords& bl, const SphKSCoords& ks, - const Real mdot, const Real rs, const int& k, const int& j, const int& i) + const Real mdot, const Real rs, const Real r_shell, const int& k, const int& j, const int& i) { // Solution constants // Ideally these could be cached but preformance isn't an issue here @@ -148,6 +148,14 @@ KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const Coordin // Set u^t to make u^r a 4-vector Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; + if (rGetReal("parthenon/mesh", "x1min"); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x1min"))) + pmb->packages.Get("GRMHD")->AddParam("x1min", x1min); auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter @@ -119,6 +127,8 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) status = InitializeFMTorus(rc.get(), pin); } else if (prob == "resize_restart") { status = ReadIharmRestart(rc.get(), pin); + } else if (prob == "resize_restart_kharma") { // Hyerin + status = ReadKharmaRestart(rc.get(), pin); } // If we didn't initialize a problem, yell @@ -127,7 +137,7 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) } // If we're not restarting, do any grooming of the initial conditions - if (prob != "resize_restart") { + if ((prob != "resize_restart") && (prob != "resize_restart_kharma")) { //Hyerin // Perturb the internal energy a bit to encourage accretion // Note this defaults to zero & is basically turned on only for torii if (pin->GetOrAddReal("perturbation", "u_jitter", 0.0) > 0.0) { diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp new file mode 100644 index 00000000..fa3ec192 --- /dev/null +++ b/kharma/prob/resize_restart_kharma.cpp @@ -0,0 +1,427 @@ +/* + * File: resize_restart_kharma.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "resize_restart_kharma.hpp" + +#include "hdf5_utils.h" +#include "types.hpp" + +#include +#include + +//using namespace Kokkos; // Hyerin: 10/07/22 comment this out, use par_for instead + + +// TODO +// Record & read: +// 1. startx/stopx/dx +// 2. coordinate name FMKS/MKS/etc +// 3. all coordinate params in play +// 4. Electron MODEL bitflag param +// 5. nprim for sanity check? +// 6. Indication of EMHD vs MHD + +// TODO this code is very specific to spherical systems/boundares or entirely periodic boxes. +// No other boundaries/geometries are really supported. +// +// Reads in KHARMA restart file but at a different simulation size + + +void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin) +{ + bool use_dt = pin->GetOrAddBoolean("resize_restart", "use_dt", true); + bool use_tf = pin->GetOrAddBoolean("resize_restart", "use_tf", false); + + // Read input from restart file + // (from external/parthenon/src/parthenon_manager.cpp) + std::unique_ptr restartReader; + restartReader = std::make_unique(fname.c_str()); + + // Load input stream + std::unique_ptr fpinput; + fpinput = std::make_unique(); + auto inputString = restartReader->GetAttr("Input", "File"); + std::istringstream is(inputString); + fpinput->LoadFromStream(is); + + int fnx1, fnx2, fnx3, fmbnx1, fmbnx2, fmbnx3; + fnx1 = fpinput->GetInteger("parthenon/mesh", "nx1"); + fnx2 = fpinput->GetInteger("parthenon/mesh", "nx2"); + fnx3 = fpinput->GetInteger("parthenon/mesh", "nx3"); + fmbnx1 = fpinput->GetInteger("parthenon/meshblock", "nx1"); + fmbnx2 = fpinput->GetInteger("parthenon/meshblock", "nx2"); + fmbnx3 = fpinput->GetInteger("parthenon/meshblock", "nx3"); + Real fx1min = fpinput->GetReal("parthenon/mesh", "x1min"); + Real fx1max = fpinput->GetReal("parthenon/mesh", "x1max"); + bool fghostzones = fpinput->GetBoolean("parthenon/output1", "ghost_zones"); + int fnghost = fpinput->GetInteger("parthenon/mesh", "nghost"); + if (pin->GetOrAddBoolean("resize_restart", "use_restart_size", false)) { + // This locks the mesh size to be zone-for-zone the same as the iharm3d dump file + pin->SetInteger("parthenon/mesh", "nx1", fnx1); + pin->SetInteger("parthenon/mesh", "nx2", fnx2); + pin->SetInteger("parthenon/mesh", "nx3", fnx3); + pin->SetInteger("parthenon/meshblock", "nx1", fmbnx1); + pin->SetInteger("parthenon/meshblock", "nx2", fmbnx2); + pin->SetInteger("parthenon/meshblock", "nx3", fmbnx3); + } + // Record the old values in any case + pin->SetInteger("parthenon/mesh", "restart_nx1", fnx1); + pin->SetInteger("parthenon/mesh", "restart_nx2", fnx2); + pin->SetInteger("parthenon/mesh", "restart_nx3", fnx3); + pin->SetInteger("parthenon/meshblock", "restart_nx1", fmbnx1); + pin->SetInteger("parthenon/meshblock", "restart_nx2", fmbnx2); + pin->SetInteger("parthenon/meshblock", "restart_nx3", fmbnx3); + pin->SetReal("parthenon/mesh", "restart_x1min", fx1min); + pin->SetReal("parthenon/mesh", "restart_x1max", fx1max); + pin->SetInteger("parthenon/mesh", "restart_nghost", fnghost); + pin->SetBoolean("parthenon/mesh", "restart_ghostzones", fghostzones); + + Real gam, tNow, dt, tf; + gam = fpinput->GetReal("GRMHD", "gamma"); + tNow = restartReader->GetAttr("Info", "Time"); + dt = restartReader->GetAttr("Info", "dt"); + tf = fpinput->GetReal("parthenon/time", "tlim"); + int ncycle = restartReader->GetAttr("Info", "NCycle"); + + pin->SetReal("GRMHD", "gamma", gam); + pin->SetReal("parthenon/time", "start_time", tNow); + if (use_dt) { + pin->SetReal("parthenon/time", "dt", dt); + } + if (use_tf) { + pin->SetReal("parthenon/time", "tlim", tf); + } + pin->SetInteger("parthenon/time", "ncycle", ncycle); + // TODO NSTEP, next tdump/tlog, etc? + + Real a, hslope;//, Rout; + a = fpinput->GetReal("coordinates", "a"); + pin->SetReal("coordinates", "a", a); + hslope = fpinput->GetReal("coordinates", "hslope"); + pin->SetReal("coordinates", "hslope", hslope); + + // close hdf5 file to prevent HDF5 hangs and corrupted files + restartReader = nullptr; +} + +TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin) +{ + Flag(rc, "Restarting from KHARMA checkpoint file"); + + auto pmb = rc->GetBlockPointer(); + + const int n1tot = pin->GetInteger("parthenon/mesh", "restart_nx1"); + const int n2tot = pin->GetInteger("parthenon/mesh", "restart_nx2"); + const int n3tot = pin->GetInteger("parthenon/mesh", "restart_nx3"); + const int n1mb = pin->GetInteger("parthenon/meshblock", "restart_nx1"); + const int n2mb = pin->GetInteger("parthenon/meshblock", "restart_nx2"); + const int n3mb = pin->GetInteger("parthenon/meshblock", "restart_nx3"); + auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess + auto fname_fill = pin->GetOrAddString("resize_restart", "fname_fill", "none"); + const bool is_spherical = pin->GetBoolean("coordinates", "spherical"); + const Real fx1min = pin->GetReal("parthenon/mesh", "restart_x1min"); + const Real fx1max = pin->GetReal("parthenon/mesh", "restart_x1max"); + const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); + const Real rs = pin->GetOrAddReal("bondi", "rs", 8.0); + const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); + const int nghost = pin->GetReal("parthenon/mesh", "restart_nghost"); + const bool ghost_zones = pin->GetBoolean("parthenon/mesh", "restart_ghostzones"); + + // Add these to package properties, since they continue to be needed on boundaries + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx1"))) + pmb->packages.Get("GRMHD")->AddParam("rnx1", n1tot); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx2"))) + pmb->packages.Get("GRMHD")->AddParam("rnx2", n2tot); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx3"))) + pmb->packages.Get("GRMHD")->AddParam("rnx3", n3tot); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx1"))) + pmb->packages.Get("GRMHD")->AddParam("rmbnx1", n1mb); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx2"))) + pmb->packages.Get("GRMHD")->AddParam("rmbnx2", n2mb); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx3"))) + pmb->packages.Get("GRMHD")->AddParam("rmbnx3", n3mb); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("fname"))) + pmb->packages.Get("GRMHD")->AddParam("fname", fname); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("fname_fill"))) + pmb->packages.Get("GRMHD")->AddParam("fname_fill", fname_fill); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("spherical"))) + pmb->packages.Get("GRMHD")->AddParam("spherical", is_spherical); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rx1min"))) + pmb->packages.Get("GRMHD")->AddParam("rx1min", fx1min); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rx1max"))) + pmb->packages.Get("GRMHD")->AddParam("rx1max", fx1max); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot"))) + pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rs"))) + pmb->packages.Get("GRMHD")->AddParam("rs", rs); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x1min"))) + pmb->packages.Get("GRMHD")->AddParam("x1min", x1min); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnghost"))) + pmb->packages.Get("GRMHD")->AddParam("rnghost", nghost); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rghostzones"))) + pmb->packages.Get("GRMHD")->AddParam("rghostzones", ghost_zones); + + // Set the whole domain + SetKharmaRestart(rc); + + return TaskStatus::complete; +} + +TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Setting KHARMA restart zones"); + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + //GridVector B_P; // refer to reductions/reductions.hpp + //if (pin->GetOrAddString("b_field", "type", "none") != "none") { + // B_P = rc->Get("prims.B").data; + //} + + auto& G = pmb->coords; + + //if (pin->GetOrAddString("b_field", "type", "none") != "none") { + // auto B_host = B_P.GetHostMirror(); + //} + + // Size/domain of the MeshBlock we're reading to + int is, ie; + if (domain == IndexDomain::outer_x1) {// copying from bondi + is = pmb->cellbounds.GetBoundsI(IndexDomain::interior).e+1; + ie = pmb->cellbounds.GetBoundsI(IndexDomain::entire).e; + } else if (domain == IndexDomain::inner_x1) { + is = pmb->cellbounds.GetBoundsI(IndexDomain::entire).s; + ie = pmb->cellbounds.GetBoundsI(IndexDomain::interior).s-1; + } else { + is = pmb->cellbounds.is(domain); + ie = pmb->cellbounds.ie(domain); + } + int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); + int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); + //IndexRange block = IndexRange{0, nb - 1}; + + const int n1tot = pmb->packages.Get("GRMHD")->Param("rnx1"); + const int n2tot = pmb->packages.Get("GRMHD")->Param("rnx2"); + const int n3tot = pmb->packages.Get("GRMHD")->Param("rnx3"); + hsize_t n1mb = pmb->packages.Get("GRMHD")->Param("rmbnx1"); + hsize_t n2mb = pmb->packages.Get("GRMHD")->Param("rmbnx2"); + hsize_t n3mb = pmb->packages.Get("GRMHD")->Param("rmbnx3"); + hsize_t nBlocks = (int) (n1tot*n2tot*n3tot)/(n1mb*n2mb*n3mb); + if ((domain != IndexDomain::outer_x1) && (domain != IndexDomain::inner_x1)) { + // read from a restart file and save it to static GridScalar + //cout << "Hyerin: reading files" << endl; + + const bool fghostzones = pmb->packages.Get("GRMHD")->Param("rghostzones"); + int fnghost = pmb->packages.Get("GRMHD")->Param("rnghost"); + + if (! fghostzones) fnghost=0; // reset to 0 + int x3factor=1; + if (n3tot <= 1) x3factor=0; // if less than 3D, do not add ghosts in x3 + hsize_t length[GR_DIM] = {nBlocks, + n1mb+2*fnghost, + n2mb+2*fnghost, + n3mb+2*fnghost*x3factor}; + const int block_sz = length[0]*length[1]*length[2]*length[3]; + //std::cout << "lengths " << length[0] << " " << length[1] <<" " << length[2]<<" " << length[3] << std::endl; + + auto fname = pmb->packages.Get("GRMHD")->Param("fname"); + auto fname_fill = pmb->packages.Get("GRMHD")->Param("fname_fill"); + + // read from file and stored in device Hyerin (10/18/2022) + GridScalar x1_f_device("x1_f_device", length[0], length[1]); + GridScalar x2_f_device("x2_f_device", length[0], length[2]); + GridScalar x3_f_device("x3_f_device", length[0], length[3]); + GridScalar rho_f_device("rho_f_device", length[0], length[3], length[2], length[1]); + GridScalar u_f_device("u_f_device", length[0], length[3], length[2], length[1]); + GridVector uvec_f_device("uvec_f_device", NVEC, length[0], length[3], length[2], length[1]); + auto x1_f_host = x1_f_device.GetHostMirror(); + auto x2_f_host = x2_f_device.GetHostMirror(); + auto x3_f_host = x3_f_device.GetHostMirror(); + auto rho_f_host = rho_f_device.GetHostMirror(); + auto u_f_host = u_f_device.GetHostMirror(); + auto uvec_f_host = uvec_f_device.GetHostMirror(); + // Hyerin (09/19/2022) : new attempt to read the file + hdf5_open(fname.c_str()); + hdf5_set_directory("/"); + Real *rho_file = new double[block_sz]; + Real *u_file = new double[block_sz]; + Real *uvec_file = new double[block_sz*3]; + Real *x1_file = new double[length[0]*length[1]]; + Real *x2_file = new double[length[0]*length[2]]; + Real *x3_file = new double[length[0]*length[3]]; + //static hsize_t fdims[] = {length[0], length[3], length[2], length[1],1}; //outdated + static hsize_t fdims[] = {length[0], 1, length[3], length[2], length[1]}; + //static hsize_t fdims_vec[] = {length[0], length[3], length[2], length[1],3}; //outdated + static hsize_t fdims_vec[] = {length[0], 3, length[3], length[2], length[1]}; + static hsize_t fdims_x1[] = {length[0], length[1]}; + static hsize_t fdims_x2[] = {length[0], length[2]}; + static hsize_t fdims_x3[] = {length[0], length[3]}; + hsize_t fstart[] = {0, 0, 0, 0, 0}; + hsize_t fstart_x[] = {0, 0}; + hdf5_read_array(rho_file, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); + hdf5_read_array(u_file, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); + hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); + hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); + hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); + hdf5_close(); + + GridScalar x1_fill_device("x1_fill_device", length[0], length[1]); + GridScalar x2_fill_device("x2_fill_device", length[0], length[2]); + GridScalar x3_fill_device("x2_fill_device", length[0], length[3]); + GridScalar rho_fill_device("rho_fill_device", length[0], length[3], length[2], length[1]); + GridScalar u_fill_device("u_fill_device", length[0], length[3], length[2], length[1]); + GridVector uvec_fill_device("uvec_fill_device", NVEC, length[0], length[3], length[2], length[1]); + auto x1_fill_host = x1_fill_device.GetHostMirror(); + auto x2_fill_host = x2_fill_device.GetHostMirror(); + auto x3_fill_host = x3_fill_device.GetHostMirror(); + auto rho_fill_host = rho_fill_device.GetHostMirror(); + auto u_fill_host = u_fill_device.GetHostMirror(); + auto uvec_fill_host = uvec_fill_device.GetHostMirror(); + Real *rho_filefill = new double[block_sz]; + Real *u_filefill = new double[block_sz]; + Real *uvec_filefill = new double[block_sz*3]; + Real *x1_filefill = new double[length[0]*length[1]]; + Real *x2_filefill = new double[length[0]*length[2]]; + Real *x3_filefill = new double[length[0]*length[3]]; + if (fname_fill != "none") { // TODO: here I'm assuming fname and fname_fill has same dimensions, which is not always the case. + hdf5_open(fname_fill.c_str()); + hdf5_set_directory("/"); + hdf5_read_array(rho_filefill, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); + hdf5_read_array(u_filefill, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); + hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); + hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); + hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); + hdf5_close(); + } + + const Real fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); + const Real fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); + + // save the grid coordinate values to host array + for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { + for (int itemp = 0; itemp < length[1]; itemp++) { + x1_f_host(iblocktemp,itemp) = x1_file[length[1]*iblocktemp+itemp]; + if (fname_fill != "none") x1_fill_host(iblocktemp,itemp) = x1_filefill[length[1]*iblocktemp+itemp]; + } for (int jtemp = 0; jtemp < length[2]; jtemp++) { + x2_f_host(iblocktemp,jtemp) = x2_file[length[2]*iblocktemp+jtemp]; + if (fname_fill != "none") x3_fill_host(iblocktemp,jtemp) = x2_filefill[length[2]*iblocktemp+jtemp]; + } for (int ktemp = 0; ktemp < length[3]; ktemp++) { + x3_f_host(iblocktemp,ktemp) = x3_file[length[3]*iblocktemp+ktemp]; + if (fname_fill != "none") x3_fill_host(iblocktemp,ktemp) = x3_filefill[length[3]*iblocktemp+ktemp]; + } + } + // re-arrange uvec such that it can be read in the VLOOP + int vector_file_index, scalar_file_index; + for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { + for (int itemp = 0; itemp < length[1]; itemp++) { + for (int jtemp = 0; jtemp < length[2]; jtemp++) { + for (int ktemp = 0; ktemp < length[3]; ktemp++) { + scalar_file_index = length[1]*(length[2]*(length[3]*iblocktemp+ktemp)+jtemp)+itemp; + + rho_f_host(iblocktemp,ktemp,jtemp,itemp) = rho_file[scalar_file_index]; + u_f_host(iblocktemp,ktemp,jtemp,itemp) = u_file[scalar_file_index]; + if (fname_fill != "none") { + rho_fill_host(iblocktemp,ktemp,jtemp,itemp) = rho_filefill[scalar_file_index]; + u_fill_host(iblocktemp,ktemp,jtemp,itemp) = u_filefill[scalar_file_index]; + } + for (int ltemp = 0; ltemp < 3; ltemp++) { + //vector_file_index = 3*(scalar_file_index)+ltemp; // outdated parthenon phdf5 saving order + vector_file_index = length[1]*(length[2]*(length[3]*(3*iblocktemp+ltemp)+ktemp)+jtemp)+itemp; + + uvec_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_file[vector_file_index]; + if (fname_fill != "none") { + uvec_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_filefill[vector_file_index]; + } + } + } + } + } + } + const bool is_spherical = pmb->packages.Get("GRMHD")->Param("spherical"); + const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); + const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); + const bool should_fill = !(fname_fill == "none"); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + //cout << "Hyerin: should fill " << should_fill <(G.coords.base); + SphBLCoords blcoord = SphBLCoords(kscoord.a); //, kscoord.ext_g); // modified (11/15/22) + CoordinateEmbedding coords = G.coords; + + PackIndexMap prims_map, cons_map; + auto P = GRMHD::PackMHDPrims(rc, prims_map); + auto U = GRMHD::PackMHDCons(rc, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + // Deep copy to device + x1_f_device.DeepCopy(x1_f_host); + x2_f_device.DeepCopy(x2_f_host); + x3_f_device.DeepCopy(x3_f_host); + rho_f_device.DeepCopy(rho_f_host); + u_f_device.DeepCopy(u_f_host); + uvec_f_device.DeepCopy(uvec_f_host); + if (fname_fill != "none") { + x1_fill_device.DeepCopy(x1_fill_host); + x2_fill_device.DeepCopy(x2_fill_host); + x3_fill_device.DeepCopy(x3_fill_host); + rho_fill_device.DeepCopy(rho_fill_host); + u_fill_device.DeepCopy(u_fill_host); + uvec_fill_device.DeepCopy(uvec_fill_host); + } + //if (pin->GetOrAddString("b_field", "type", "none") != "none") { + // B_P.DeepCopy(B_host); + //} + Kokkos::fence(); + + // Host-side interpolate & copy into the mirror array + pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA_3D { + get_prim_restart_kharma(G, coords, P, m_p, blcoord, kscoord, + fx1min, fx1max, should_fill, is_spherical, gam, rs, mdot, length, + x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, + x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, + k, j, i); + GRMHD::p_to_u(G,P,m_p,gam,k,j,i,U,m_u); //TODO: shouldn't I do this too? + //if (pin->GetOrAddString("b_field", "type", "none") != "none") { + // VLOOP B_host(v, k, j, i) = interp_scalar(G, X, startx, stopx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(B_file[v*block_sz])); + //} + } + ); + } + + return TaskStatus::complete; +} diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp new file mode 100644 index 00000000..e634f633 --- /dev/null +++ b/kharma/prob/resize_restart_kharma.hpp @@ -0,0 +1,157 @@ +// Load the grid variables up with primitives from an old KHARMA run +#pragma once + +#include "decs.hpp" + +#include "mesh/mesh.hpp" + +// added by Hyerin (10/07/22) +#include "bondi.hpp" + +/** + * Read the header of an KHARMA HDF5 restart file, and set appropriate parameters + * Call this before mesh creation! + */ +void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin); + +/** + * Read data from an KHARMA restart file. Does not support >1 meshblock in Parthenon + * + * Returns stop time tf of the original simulation, for e.g. replicating regression tests + */ +TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin); + +// newly added by Hyerin (09/06/22) +TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); + +// Hint form resize.hpp +// TODO: (Hyerin) should I do const for x1, x2, x3, var? +KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], + //Real *x1, Real *x2, Real *x3, + const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, + const hsize_t length[GR_DIM], int& iblock, + int& i, int& j, int& k, GReal del[GR_DIM]) +{ + //cout << "Hyerin: entered Xtoindex" <& P, const VarMap& m_p, + const SphBLCoords& bl, const SphKSCoords& ks, + const Real fx1min, const Real fx1max, const bool should_fill, const bool is_spherical, + const Real gam, const Real rs, const Real mdot, const hsize_t length[GR_DIM], + const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, + const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridScalar& rho_fill, const GridScalar& u_fill, const GridVector& uvec_fill, + const int& k, const int& j, const int& i) +{ + Real rho_temp, u_temp; + Real u_prim[NVEC]; + + GReal X[GR_DIM]; + G.coord(k, j, i, Loci::center, X); + GReal del[GR_DIM]; // not really needed now since I am doing nearest neighbor interpolation + int iblocktemp, itemp, jtemp, ktemp; + // Interpolate the value at this location from the global grid + if ((!should_fill) && (X[1]fx1max)||(X[1] Date: Wed, 7 Dec 2022 18:00:00 -0600 Subject: [PATCH 010/219] Code fixes and improvements: 1. Non-ideal contributions to the stress-energy tensor weren't being considered when computing source terms for u, uvec. We now have a `Flux::AddSource` that computes the said source terms, and is called in the ImEx driver, replacing `GRMHD::AddSource`. This also meant I had to define a global `Flux::calc_tensor` that can accept data over MeshblockPacks. 2. Boundary sync at end of sub-step in the ImEx driver is now carried out over the entire domain and is done after fixing UtoP failures (similar to the `dev` commit). 3. No clue as to how the conducting atmosphere problem worked previosuly without the feedback from the non-ideal sector onto the source terms, but that is fixed now. Have included a conditional statement in `EMHD::convert_prims_to_q_dP` to deal with `kappa_eta` closure type. This is necessary because `conduction_alpha` and `viscosity_alpha` are no longer constant but rather depend on `kappa` and `eta` respectively, and rho as well. The problem now converges at the expected order. 4. Included a PtoU call at the end of drift frame floors. Also, floored bsq calculation to SMALL. This is necessary because `ApplyFloors` is called prior to B-field initialization during problem initialization. 5. Removed the option to print residual during the implicit solve since it clearly wouldn't work on GPUs. 6. Included problem init and .par files for ImEx and EMHD torus. This is not to say they run. 7. Have included a comment in problem.cpp to let users know that during problem initialization, fluid frame floors are used since the fluid conserved vars outside the torus are NaNs. However, this is done in a rather underhanded manner and should be more explicit. 8. Edits made to conducting atmosphere initialization and .par file so it is up-to-date with the latest version of `kharmaim`. 9. Updated `run.sh` and `check.py` scripts for conducting atmosphere so that it is similar to the rest of the test problems. This is not to say it will pass CI. As it stands, the problem can be run ONLY ON CPUs. --- kharma/b_flux_ct/seed_B_ct.cpp | 6 +- kharma/debug.cpp | 1 + kharma/emhd/emhd.hpp | 9 +- kharma/floors/floors.cpp | 9 +- kharma/floors/floors.hpp | 8 +- kharma/flux.cpp | 72 ++++++++++++++-- kharma/flux.hpp | 7 +- kharma/flux_functions.hpp | 30 ++++++- kharma/grmhd/grmhd.cpp | 6 +- kharma/grmhd/grmhd_functions.hpp | 2 +- kharma/grmhd/source.cpp | 4 +- kharma/imex_driver.cpp | 20 ++--- kharma/implicit/implicit.cpp | 22 ++--- kharma/prob/emhd/conducting_atmosphere.cpp | 27 +++--- kharma/prob/problem.cpp | 15 +++- kharma/types.hpp | 26 ++++-- pars/conducting_atmosphere.par | 38 +++++---- pars/emhdmodes.par | 3 - pars/sane_emhd.par | 16 ++-- pars/sane_imex.par | 98 ++++++++++++++++++++++ tests/bondi_viscous/check.py | 1 + tests/conducting_atmosphere/check.py | 71 ++++++++++------ tests/conducting_atmosphere/check.sh | 19 ----- tests/conducting_atmosphere/run.sh | 46 +++++----- 24 files changed, 387 insertions(+), 169 deletions(-) create mode 100644 pars/sane_imex.par delete mode 100755 tests/conducting_atmosphere/check.sh diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index c935c5dc..49f7bafb 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -85,10 +85,10 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) if (!is_torus) throw std::invalid_argument("Magnetic field seed "+b_field_type+" supports only torus problems!"); // Torus parameters - rin = pin->GetReal("torus", "rin"); - rmax = pin->GetReal("torus", "rmax"); + rin = pin->GetReal("torus", "rin"); + rmax = pin->GetReal("torus", "rmax"); kappa = pin->GetReal("torus", "kappa"); - tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; + tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; // Other things we need only for torus evaluation gam = pmb->packages.Get("GRMHD")->Param("gamma"); rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); diff --git a/kharma/debug.cpp b/kharma/debug.cpp index 22e0e5f5..4a95e498 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -123,6 +123,7 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { if (m::isnan(ctop(b, dir-1, k, j, i))) { ++local_result; + fprintf(stderr, "ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT } } , nan_reducer); diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 96c24a1f..d5b04947 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -334,8 +334,13 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea dP = dP_tilde; if (emhd_params.higher_order_terms) { - q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); - dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); + if (emhd_params.type == ClosureType::kappa_eta) { + q *= m::sqrt(emhd_params.kappa * m::pow(Theta, 2) / emhd_params.tau); + dP *= m::sqrt(emhd_params.eta * Theta / emhd_params.tau); + } else { + q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); + dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); + } } } diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 417500db..56f414d0 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -147,7 +147,6 @@ std::shared_ptr Initialize(ParameterInput *pin) pkg->AddField("fflag", m); // Similar to fflag - will register zones where limits on q and dP are hit - // Enabled only if pkg->AddField("eflag", m); // bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); // if (do_emhd && enable_emhd_limits) { @@ -193,12 +192,12 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) GridScalar eflag = mbd->Get("eflag").data; const bool enable_emhd_limits = mbd->GetBlockPointer()->packages.Get("Floors")->Param("enable_emhd_limits"); - EMHD::EMHD_parameters emhd_params; + EMHD::EMHD_parameters emhd_params_tmp; if (enable_emhd_limits) { const auto& pars = pmb->packages.Get("EMHD")->AllParams(); - emhd_params = pars.Get("emhd_params"); - + emhd_params_tmp = pars.Get("emhd_params"); } + const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); @@ -214,7 +213,7 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) KOKKOS_LAMBDA_3D { if (((int) pflag(k, j, i)) >= InversionStatus::success) { // apply_floors can involve another U_to_P call. Hide the pflag in bottom 5 bits and retrieve both - int comboflag = apply_floors(G, P, m_p, gam, k, j, i, floors, U, m_u); + int comboflag = apply_floors(G, P, m_p, gam, emhd_params, k, j, i, floors, U, m_u); fflag(k, j, i) = (comboflag / HIT_FLOOR_GEOM_RHO) * HIT_FLOOR_GEOM_RHO; // Record the pflag as well. KHARMA did not traditionally do this, diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index c212e75e..6719875c 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -207,7 +207,8 @@ KOKKOS_INLINE_FUNCTION int apply_ceilings(const GRCoordinates& G, const Variable * LOCKSTEP: this function respects P and ignores U in order to return consistent P<->U */ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, - const Real& gam, const int& k, const int& j, const int& i, const Floors::Prescription& floors, + const Real& gam, const EMHD::EMHD_parameters& emhd_params, + const int& k, const int& j, const int& i, const Floors::Prescription& floors, const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) { int fflag = 0; @@ -314,7 +315,7 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa Bcon[2] = P(m_p.B2, k, j, i); Bcon[3] = P(m_p.B3, k, j, i); DLOOP2 Bcov[mu] += G.gcov(Loci::center, j, i, mu, nu) * Bcon[nu]; - const Real Bsq = dot(Bcon, Bcov); + const Real Bsq = m::max(dot(Bcon, Bcov), SMALL); // Normal observer fluid momentum Real Qcov[GR_DIM] = {0}; @@ -362,6 +363,9 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa P(m_p.U2, k, j, i) = Dtmp.ucon[2] + (beta[2] * gamma/lapse); P(m_p.U3, k, j, i) = Dtmp.ucon[3] + (beta[3] * gamma/lapse); + // Update the conserved variables + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + } else { // Add the material in the normal observer frame, by: // Adding the floors to the primitive variables diff --git a/kharma/flux.cpp b/kharma/flux.cpp index b3ecb51a..cd027998 100644 --- a/kharma/flux.cpp +++ b/kharma/flux.cpp @@ -48,12 +48,14 @@ TaskStatus Flux::PtoU(MeshBlockData *rc, IndexDomain domain) auto pmb = rc->GetBlockPointer(); // Options const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); - const Real gam = pars.Get("gamma"); - auto pkgs = pmb->packages.AllPackages(); - const bool flux_ct = pkgs.count("B_FluxCT"); - const bool b_cd = pkgs.count("B_CD"); + const Real gam = pars.Get("gamma"); + auto pkgs = pmb->packages.AllPackages(); + + const bool flux_ct = pkgs.count("B_FluxCT"); + const bool b_cd = pkgs.count("B_CD"); const bool use_electrons = pkgs.count("Electrons"); - const bool use_emhd = pkgs.count("EMHD"); + const bool use_emhd = pkgs.count("EMHD"); + MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); EMHD::EMHD_parameters emhd_params_tmp; @@ -117,3 +119,63 @@ TaskStatus Flux::PtoU(MeshBlockData *rc, IndexDomain domain) Flag(rc, "Got conserved variables"); return TaskStatus::complete; } + +TaskStatus Flux::AddSource(MeshData *md, MeshData *mdudt) +{ + Flag(mdudt, "Adding source terms to uu, uvec"); + // Pointers + auto pmesh = md->GetMeshPointer(); + auto& mbd = md->GetBlockData(0); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Options + const Real gam = pmb0->packages.Get("GRMHD")->Param("gamma"); + const auto use_emhd = pmb0->packages.AllPackages().count("EMHD"); + + // Pack variables + const MetadataFlag isPrimitive = pmb0->packages.Get("GRMHD")->Param("PrimitiveFlag"); + PackIndexMap prims_map, cons_map; + auto P = md->PackVariables(std::vector{isPrimitive}, prims_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + + // EMHD params + EMHD::EMHD_parameters emhd_params_tmp; + if (use_emhd) { + const auto& emhd_pars = pmb0->packages.Get("EMHD")->AllParams(); + emhd_params_tmp = emhd_pars.Get("emhd_params"); + } + const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; + + // Get sizes + IndexDomain domain = IndexDomain::interior; + auto ib = md->GetBoundsI(domain); + auto jb = md->GetBoundsJ(domain); + auto kb = md->GetBoundsK(domain); + auto block = IndexRange{0, P.GetDim(5)-1}; + + pmb0->par_for("tmunu_source", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA_MESH_3D { + const auto& G = dUdt.GetCoords(b); + FourVectors D; + GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, D); + // Call Flux::calc_tensor which will in turn call the right calc_tensor based on the number of primitives + Real T[GR_DIM] = {0}; + Real new_du[GR_DIM] = {0}; + DLOOP2 { + Flux::calc_tensor(G, P(b), m_p, D, emhd_params, gam, k, j, i, mu, T); + Real Tmunu = T[nu]; + + // Contract mhd stress tensor with connection, and multiply by metric determinant + for (int lam = 0; lam < GR_DIM; ++lam) { + new_du[lam] += Tmunu * G.gdet_conn(j, i, nu, lam, mu); + } + } + + dUdt(b, m_u.UU, k, j, i) += new_du[0]; + VLOOP dUdt(b, m_u.U1 + v, k, j, i) += new_du[1 + v]; + } + ); + + Flag(mdudt, "Added"); + return TaskStatus::complete; +} \ No newline at end of file diff --git a/kharma/flux.hpp b/kharma/flux.hpp index 07f58102..5ee1c067 100644 --- a/kharma/flux.hpp +++ b/kharma/flux.hpp @@ -68,7 +68,12 @@ TaskStatus ApplyFluxes(MeshData *md, MeshData *mdudt); * declaring UtoP vs FillDerived in GRMHD package. */ TaskStatus PtoU(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior); -inline TaskStatus PtoUTask(MeshBlockData *rc) { return PtoU(rc); } +inline TaskStatus PtoUTask(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire) { return PtoU(rc, domain); } + +/** + * Function to compute and apply the source terms to internal energy and velocity conserved vars over the entire grid. + */ +TaskStatus AddSource(MeshData *md, MeshData *mdudt); // Fluxes a.k.a. "Approximate Riemann Solvers" // More complex solvers require speed estimates not calculable completely from diff --git a/kharma/flux_functions.hpp b/kharma/flux_functions.hpp index ee90c25f..9ae62dd7 100644 --- a/kharma/flux_functions.hpp +++ b/kharma/flux_functions.hpp @@ -54,7 +54,6 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, Real T[GR_DIM]) { if (m_p.Q >= 0) { - // Apply higher-order terms conversion if necessary Real q, dP; const Real Theta = (gam - 1) * P(m_p.UU) / P(m_p.RHO); @@ -70,8 +69,31 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, // GRHD stress-energy tensor w/ first index up, second index down GRHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); } +} - // if (i == 11 && j == 11) printf("mhd: %6.5e %6.5e %6.5e %6.5e %6.5e\n", flux(m_u.RHO), T[0], T[1], T[2], T[3]); +template +KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, + const EMHD::EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, const int& dir, + Real T[GR_DIM]) +{ + if (m_p.Q >= 0) { + + // Apply higher-order terms conversion if necessary + Real q, dP; + const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); + const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); + EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); + + // Then calculate the tensor + EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), emhd_params, q, dP, D, dir, T); + } else if (m_p.B1 >= 0) { + // GRMHD stress-energy tensor w/ first index up, second index down + GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); + } else { + // GRHD stress-energy tensor w/ first index up, second index down + GRHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); + } } template @@ -295,6 +317,10 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real cs2 = gam * (gam - 1) * P(m.UU) / ef; Real cms2; if (m.Q > 0) { + // Get the EGRMHD parameters + Real tau, chi_e, nu_e; + EMHD::set_parameters(G, P, m, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + // Find fast magnetosonic speed const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); const Real ee = bsq + ef; diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 6ffd7a93..5c2d249d 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -281,8 +281,8 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // a particular package -- that is, some portion of the things that the package needs done // at each step, which must be done at specific times. // See the header files defining each of these functions for their purpose and call context. - pkg->CheckRefinementBlock = GRMHD::CheckRefinement; - pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; + pkg->CheckRefinementBlock = GRMHD::CheckRefinement; + pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; pkg->PostStepDiagnosticsMesh = GRMHD::PostStepDiagnostics; return pkg; @@ -507,7 +507,7 @@ AmrTag CheckRefinement(MeshBlockData *rc) , Kokkos::MinMax(minmax)); auto pkg = pmb->packages.Get("GRMHD"); - const auto &refine_tol = pkg->Param("refine_tol"); + const auto &refine_tol = pkg->Param("refine_tol"); const auto &derefine_tol = pkg->Param("derefine_tol"); if (minmax.max_val - minmax.min_val > refine_tol) return AmrTag::refine; diff --git a/kharma/grmhd/grmhd_functions.hpp b/kharma/grmhd/grmhd_functions.hpp index 3aef0f2f..1f34db64 100644 --- a/kharma/grmhd/grmhd_functions.hpp +++ b/kharma/grmhd/grmhd_functions.hpp @@ -176,7 +176,7 @@ KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Real uvec[N // This fn is guaranteed to have B values D.bcon[0] = 0; - VLOOP D.bcon[0] += B_P[v] * D.ucov[v+1]; + VLOOP D.bcon[0] += B_P[v] * D.ucov[v+1]; VLOOP D.bcon[v+1] = (B_P[v] + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; G.lower(D.bcon, D.bcov, k, j, i, loc); diff --git a/kharma/grmhd/source.cpp b/kharma/grmhd/source.cpp index 47a79d07..465f2789 100644 --- a/kharma/grmhd/source.cpp +++ b/kharma/grmhd/source.cpp @@ -43,13 +43,13 @@ TaskStatus GRMHD::AddSource(MeshData *md, MeshData *mdudt) Flag(mdudt, "Adding GRMHD source"); // Pointers auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options const Real gam = pmb0->packages.Get("GRMHD")->Param("gamma"); // Pack variables PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(md, prims_map); + auto P = GRMHD::PackMHDPrims(md, prims_map); auto dUdt = GRMHD::PackMHDCons(mdudt, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); // Get sizes diff --git a/kharma/imex_driver.cpp b/kharma/imex_driver.cpp index 441d0c74..70fecdd2 100644 --- a/kharma/imex_driver.cpp +++ b/kharma/imex_driver.cpp @@ -192,11 +192,11 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) // ADD EXPLICIT SOURCES TO CONSERVED VARIABLES // Source term for GRMHD, \Gamma * T // TODO take this out in Minkowski space - auto t_grmhd_source = tl.AddTask(t_flux_div, GRMHD::AddSource, md_sub_step_init.get(), md_flux_src.get()); + auto t_tmunu_source = tl.AddTask(t_flux_div, Flux::AddSource, md_sub_step_init.get(), md_flux_src.get()); // Source term for constraint-damping. Applied only to B - auto t_b_cd_source = t_grmhd_source; + auto t_b_cd_source = t_tmunu_source; if (use_b_cd) { - t_b_cd_source = tl.AddTask(t_grmhd_source, B_CD::AddSource, md_sub_step_init.get(), md_flux_src.get()); + t_b_cd_source = tl.AddTask(t_tmunu_source, B_CD::AddSource, md_sub_step_init.get(), md_flux_src.get()); } // Wind source. Applied to conserved variables similar to GR source term auto t_wind_source = t_b_cd_source; @@ -314,10 +314,10 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) TaskRegion &sync_region = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { auto &tl = sync_region[i]; - auto &mc1 = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); + auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); // MPI/MeshBlock boundary exchange. // Note that in this driver, this block syncs *primitive* variables, not conserved - KBoundaries::AddBoundarySync(t_none, tl, mc1); + KBoundaries::AddBoundarySync(t_none, tl, mbd_sub_step_final); } // Async Region: Any post-sync tasks. Fixups, timestep & AMR things. @@ -328,17 +328,17 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) auto &mbd_sub_step_init = pmb->meshblock_data.Get(stage_name[stage-1]); auto &mbd_sub_step_final = pmb->meshblock_data.Get(stage_name[stage]); - auto t_set_bc = tl.AddTask(t_none, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); - // If we're evolving even the GRMHD variables explicitly, we need to fix UtoP variable inversion failures // Syncing bounds before calling this, and then running it over the whole domain, will make // behavior for different mesh breakdowns much more similar (identical?), since bad zones in // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk - auto t_fix_derived = t_set_bc; + auto t_fix_derived = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_fix_derived = tl.AddTask(t_set_bc, GRMHD::FixUtoP, mbd_sub_step_final.get()); + t_fix_derived = tl.AddTask(t_fix_derived, GRMHD::FixUtoP, mbd_sub_step_final.get()); } + auto t_set_bc = tl.AddTask(t_fix_derived, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + // Electron heating goes where it does in HARMDriver, for the same reasons auto t_heat_electrons = t_fix_derived; if (use_electrons) { @@ -347,7 +347,7 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) } // Make sure conserved vars are synchronized at step end - auto t_ptou = tl.AddTask(t_heat_electrons, Flux::PtoUTask, mbd_sub_step_final.get()); + auto t_ptou = tl.AddTask(t_heat_electrons, Flux::PtoUTask, mbd_sub_step_final.get(), IndexDomain::entire); auto t_step_done = t_ptou; diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index dbbbc2f6..985e3f57 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -118,11 +118,9 @@ std::shared_ptr Initialize(ParameterInput *pin) // Metadata m_int = Metadata({Metadata::Integer, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariabl - bool print_residual = pin->GetOrAddBoolean("implicit", "print_residual", false); - params.Add("print_residual", print_residual); // TODO: Find a way to save residuals based on a runtime parameter. We don't want to unnecessarily allocate - // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. For now, - // we just print the value of the residual if the norm exceeds the max_norm.s + // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. + // Should the solve save the residual vector field? Useful for debugging purposes. Default is NO. // bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); // params.Add("save_residual", save_residual); @@ -196,7 +194,6 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i const Real linesearch_eps = implicit_par.Get("linesearch_eps"); const Real linesearch_lambda = implicit_par.Get("linesearch_lambda"); - const bool print_residual = implicit_par.Get("print_residual"); // const bool save_residual = implicit_par.Get("save_residual"); // Misc other constants for inside the kernel @@ -415,7 +412,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); - // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { + // if (am_rank0 && b == 0 && i == 10 && j == 10 && k == kb.s) { // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", @@ -426,9 +423,9 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // printf("Ps: "); PLOOP printf("%6.5e ", P_sub_step_init(ip)); printf("\n"); // printf("Us: "); PLOOP printf("%6.5e ", U_sub_step_init(ip)); printf("\n"); // printf("dUdt: "); PLOOP printf("%6.5e ", dU_implicit(ip)); printf("\n"); - // printf("Initial Jacobian:\n"); for (int jp=0; jp *md_full_step_init, MeshData *md_sub_step_i solve_norm() = 0; FLOOP solve_norm() += residual(ip) * residual(ip); solve_norm() = m::sqrt(solve_norm()); // TODO faster to scratch cache & copy? - - if (print_residual) { - if (solve_norm_s(i) > rootfind_tol) { - FLOOP std::cout< *rc, ParameterInput *pin) if (use_emhd) dP_host(k, j, i) = 0.; - // Note that the velocity primitives defined up there isn't quite right. + // Note that the velocity primitives defined up there aren't quite right. // For a fluid at rest wrt. the normal observer, ucon = {-1/g_tt,0,0,0}. // We need to use this info to obtain the correct values for U1, U2 and U3 @@ -238,25 +238,22 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) uvec_host(V2, k, j, i) = ucon[2] + beta[2]*gamma/alpha; uvec_host(V3, k, j, i) = ucon[3] + beta[3]*gamma/alpha; - if (use_emhd) - if (higher_order_terms){ - - const Real Theta = (gam - 1.) * u_temp / rho_temp; + if (use_emhd) { + // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde + Real q_tilde = q_host(k, j, i); + Real dP_tilde = dP_host(k, j, i); - // Set EMHD parameters + if (emhd_params.higher_order_terms) { Real tau, chi_e, nu_e; EMHD::set_parameters(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + const Real Theta = (gam - 1.) * u_temp / rho_temp; - // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde - Real q_tilde = q_host(k, j, i); - Real dP_tilde = dP_host(k, j, i); - if (emhd_params.higher_order_terms) { - q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho_temp * pow(Theta, 2.))) : 0.; - dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; - } - q_host(k, j, i) = q_tilde; - dP_host(k, j, i) = dP_tilde; + q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho_temp * pow(Theta, 2.))) : 0.; + dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; } + q_host(k, j, i) = q_tilde; + dP_host(k, j, i) = dP_tilde; + } } } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 664cdad9..ed89f94a 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -81,6 +81,8 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter + auto use_emhd = pin->GetOrAddBoolean("emhd", "on", false); // Needed to check if it's an EMHD torus + if (MPIRank0()) { std::cout << "Initializing problem: " << prob << std::endl; } @@ -115,9 +117,9 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) } else if (prob == "bondi_viscous") { status = InitializeBondiViscous(rc.get(), pin); // Everything - } else if (prob == "torus") { + } else if ((prob == "torus") && (!use_emhd)) { status = InitializeFMTorus(rc.get(), pin); - } else if (prob == "torus_emhd") { + } else if ((prob == "torus") && (use_emhd)){ status = InitializeFMTorusEMHD(rc.get(), pin); } else if (prob == "resize_restart") { status = ReadIharmRestart(rc.get(), pin); @@ -142,6 +144,15 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) } } + // Note that at this stage we have initialized the fluid primitives ONLY in the torus. + // What this means is that in the following `PtoU` call, we will get the NaNs for the conserved vars + // outside the torus since the floors are not called yet (we need conserved vars for NOF floors). + // In the subsequent `ApplyFloors` call we are able to initialize the NOF floors despite this + // because it falls back to fluid frame floors in the event the UtoP is unsuccessful. + // TODO: Maybe let the user know that despite asking for NOF floors, fluid frame floors will be applied + // the very first time during problem init. + // For now, I've opened an issue on github to address this. + // Fill the conserved variables U, // which we'll treat as the independent/fundamental state. // P is filled again from this later on diff --git a/kharma/types.hpp b/kharma/types.hpp index a0817ba4..64224e12 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -224,12 +224,26 @@ inline void PrintZone(MeshBlockData *rc) auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - std::cerr << "RHO: " << rhop(0,0,100) - << " UU: " << up(0,0,100) - << " U: " << uvecp(0, 0,0,100) << " " << uvecp(1, 0,0,100)<< " " << uvecp(2, 0,0,100) - << " B: " << Bp(0, 0,0,100) << " " << Bp(1, 0,0,100) << " " << Bp(2, 0,0,100) - << " q: " << q(0,0,100) - << " dP: " << dP(0,0,100) << std::endl; + + auto rhoU = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); + auto uU = rc->Get("cons.u").data.GetHostMirrorAndCopy(); + auto uvecU = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); + auto BU = rc->Get("cons.B").data.GetHostMirrorAndCopy(); + auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); + auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); + + std::cerr << "RHO: " << rhop(0,108,63) + << " UU: " << up(0,108,63) + << " U: " << uvecp(0,0,108,63) << " " << uvecp(1,0,108,63)<< " " << uvecp(2,0,108,63) + << " B: " << Bp(0,0,108,63) << " " << Bp(1,0,108,63) << " " << Bp(2,0,108,63) + << " q: " << q(0,108,63) + << " dP: " << dP(0,108,63) << std::endl; + std::cerr << "RHO: " << rhoU(0,108,63) + << " UU: " << uU(0,108,63) + << " U: " << uvecU(0,0,108,63) << " " << uvecU(1,0,108,63)<< " " << uvecU(2,0,108,63) + << " B: " << BU(0,0,108,63) << " " << BU(1,0,108,63) << " " << BU(2,0,108,63) + << " q: " << qU(0,108,63) + << " dP: " << dPU(0,108,63) << std::endl; } inline void Flag(std::string label) diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index d8bd396c..af49ee2f 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -26,25 +26,35 @@ base = ks transform = mks a = 0.0 hslope = 1.0 -r_in = 100. +r_in = 200. r_out = 300. check_inflow_inner = false check_inflow_outer = false - tlim = 150. + +type = imex + + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 + -cfl = 0.5 +implicit = true +cfl = 0.9 gamma = 1.333333 reconstruction = weno5 -implicit = true -implicit = true +implicit = false initial_cleanup = false @@ -52,34 +62,26 @@ initial_cleanup = false on = true higher_order_terms = true +feedback = true closure_type = kappa_eta tau = 10. kappa = 0.1 eta = 0.0 - -type = imex - - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 - input = ODE -disable_floors = false +disable_floors = true +emhd_limits = false -verbose = 1 -flag_verbose = 2 -extra_checks = 1 +verbose = 1 file_type = hdf5 -dt = 20 +dt = 10 single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index b4ced3c7..8f34e899 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -70,9 +70,6 @@ max_linesearch_iter = 3 linesearch_eps = 1.e-4 use_qr = true -print_residual = false - - # General verbosity level: # 1: general archival info diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par index 21a9208c..df41df12 100644 --- a/pars/sane_emhd.par +++ b/pars/sane_emhd.par @@ -1,4 +1,4 @@ -# SANE model mirroring the simulation library +# Extended SANE model mirroring the simulation library # Quite small to run for more than 10kM, 6M/12M F-M torus, # Overall simulation size 1000M @@ -44,9 +44,9 @@ linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 use_qr = true -print_residual = false +implicit = true cfl = 0.9 gamma = 1.666667 reconstruction = weno5 @@ -75,10 +75,12 @@ rmax = 12.0 u_jitter = 0.04 -rho_min_geom = 1e-6 -u_min_geom = 1e-8 -bsq_over_rho_max = 100 -u_over_rho_max = 2 +frame = drift +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 +enable_emhd_limits = true archive_parameters = true @@ -95,7 +97,7 @@ Tp = 10 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, q, dP, jcon, fflag, pflag, solve_norm, solve_fail +variables = prims.rho, prims.u, prims.uvec, prims.B, q, dP, jcon, fflag, pflag, solve_norm, solve_fail, eflag file_type = rst diff --git a/pars/sane_imex.par b/pars/sane_imex.par new file mode 100644 index 00000000..80af3404 --- /dev/null +++ b/pars/sane_imex.par @@ -0,0 +1,98 @@ +# SANE model mirroring the simulation library +# Quite small to run for more than 10kM, 6M/12M F-M torus, +# Overall simulation size 1000M +# Uses the IMEX solver + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +nx1 = 32 +nx2 = 32 +nx3 = 32 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 4000.0 +nlim = -1 + + +type = imex +two_sync = true + + +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-3 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true + + +implicit = true +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +implicit = false +type = sane +beta_min = 100. +initial_cleanup = true + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +frame = drift +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +archive_parameters = true +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +on = false +ne = 1.e-4 +Tp = 10 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag, solve_norm, solve_fail + + +file_type = rst +dt = 100.0 + + +file_type = hst +dt = 0.1 diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index b224c074..fef28d82 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -94,6 +94,7 @@ tracker+=1 ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + # ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') plt.xscale('log', base=2) ax.set_xlabel('Resolution') ax.set_ylabel('L1 norm') diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index 05bd5f51..c73cd5e6 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -5,53 +5,68 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import pyharm + if __name__=='__main__': - outputdir = os.getcwd() - kharmadir = '/home/vdhruv2/kharma' - RES = [int(r) for r in sys.argv[1].split(",")] - NG = 4 - CONDUCTION = 1 - if CONDUCTION: - PRIMS = ['rho','u','q'] - else: - PRIMS = ['rho','u'] - L1_norm = np.zeros([len(RES), len(PRIMS)]) + outputdir = './' + kharmadir = '../../' + + NVAR = 3 + VARS = ['rho', 'u', 'q'] + NG = 4 + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] + + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) for r, res in enumerate(RES): # load analytic result rho_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_rho.txt'))[NG:-NG] - u_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_u.txt'))[NG:-NG] - if CONDUCTION: - q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_phi.txt'))[NG:-NG] + uu_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_u.txt'))[NG:-NG] + q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_phi.txt'))[NG:-NG] # load code data - dfile = h5py.File('emhd_2d_{}_end.h5'.format(res), 'r') + dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - u = np.squeeze(dfile['prims'][Ellipsis,1][()]) - if CONDUCTION: - q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) + uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) + q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) t = dfile['t'][()] gam = dfile['header/gam'][()] + higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') # compute q - if CONDUCTION: + if higher_order_terms=="TRUE": + print("Res: "+str(res)+"; higher order terms enabled") tau = 10. kappa = 0.1 - P = (gam - 1.) * u + P = (gam - 1.) * uu Theta = P / rho - cs2 = (gam * P) / (rho + (gam * u)) chi_emhd = kappa / rho q = q_tilde * np.sqrt(chi_emhd * rho * Theta**2 / tau) + else: + q = q_tilde # compute L1 norm - L1_norm[r,0] = np.mean(np.fabs(rho-rho_analytic[:,None])) - L1_norm[r,1] = np.mean(np.fabs(u-u_analytic[:,None])) - if CONDUCTION: - L1_norm[r,2] = np.mean(np.fabs(q-q_analytic[:,None])[1:-1]) + # compute L1 norm + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(q - q_analytic[:,None])[1:-1]) + + # MEASURE CONVERGENCE + L1 = np.array(L1) + powerfits = [0.,]*NVAR + fail = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -1.6 or powerfits[k] < -2.2: + fail = 1 # plotting parameters @@ -75,9 +90,9 @@ # loop over prims tracker = 0 - for n in range(len(PRIMS)): + for n in range(len(VARS)): color = colors[tracker] - ax.loglog(RES, L1_norm[:,n], color=color, marker='o', label=PRIMS[n]) + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) tracker+=1 ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') @@ -86,4 +101,6 @@ ax.set_xlabel('Resolution') ax.set_ylabel('L1 norm') ax.legend() - plt.savefig(os.path.join(outputdir, 'conducting_atmosphere_convergence.png'), dpi=300) + plt.savefig(os.path.join(outputdir, "conducting_atmosphere_convergence_"+SHORT+".png"), dpi=300) + + exit(fail) diff --git a/tests/conducting_atmosphere/check.sh b/tests/conducting_atmosphere/check.sh deleted file mode 100755 index 00132ccf..00000000 --- a/tests/conducting_atmosphere/check.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Run checks against analytic result for specified tests - -. /home/vdhruv2/anaconda3/etc/profile.d/conda.sh -conda activate pyharm - -# Very small amplitude by default, preserve double precision -~/pyHARM/scripts/pyharm-convert --double *.phdf - -RES2D="64,128,256,512" - -conda activate base - -fail=0 - -python3 check.py $RES2D "Conducting atmosphere" emhd2d || fail=1 - -exit $fail diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 764086ae..47f30046 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -1,34 +1,40 @@ #!/bin/bash -#set -euo pipefail +# set -euo pipefail -BASE=~/kharma +BASE=../.. + +exit_code=0 # Extended MHD atmosphere test convergence to exercise geometrical terms # We'll use just 1 MPI rank to circumvent the somewhat annoying ODE initialization conv_2d() { - for res in 64 128 256 512 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do cp -r ${BASE}/kharma/prob/emhd/conducting_atmosphere_${res}_default/*txt ./ $BASE/run.sh -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 - if [[ -d $res ]]; then - echo -e "Resolution directory exists. Clearing existing files in there and copying new files\n" - rm ${res}/* - else - mkdir $res - fi - . /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - conda activate pyharm - ~/pyHARM/scripts/pyharm-convert --double *.phdf - conda deactivate - cp -r ./conducting_atmosphere.out0*.h5 $res - mv conducting_atmosphere.out0.00000.h5 emhd_2d_${res}_start.h5 - mv conducting_atmosphere.out0.final.h5 emhd_2d_${res}_end.h5 - rm -r ./conducting_atmosphere* - rm ./atmosphere*.txt + parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + + mv conducting_atmosphere.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv conducting_atmosphere.out0.final.phdf emhd_2d_${res}_end_${1}.phdf done + check_code=0 + pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + rm -r *.phdf + rm -r *.xdmf + rm -r *.out0* + rm -r ./*.txt + if [[ $check_code != 0 ]]; then + echo Conducting atmosphere test $3 FAIL: $check_code + exit_code=1 + else + echo Conducting atmosphere test $3 success + fi } -conv_2d +ALL_RES="64,128,256,512" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Conducting atmosphere in 2D, WENO5" From 9b3f3360080822d267b96f2fe0a2d42765d87b7b Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Mon, 12 Dec 2022 10:26:02 -0500 Subject: [PATCH 011/219] Added B field copying feature to resize_restart_kharma and other relevant scripts. For some reason though it seems like B fields are not copied over correctly, or being overriden by some other functions (maybe SyncAllBounds?) --- kharma/b_flux_ct/b_flux_ct.cpp | 1 + kharma/b_flux_ct/seed_B_ct.cpp | 4 +-- kharma/prob/resize_restart_kharma.cpp | 37 +++++++++++++++++++++------ kharma/prob/resize_restart_kharma.hpp | 21 +++++++++++---- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index a2693e84..c4df4494 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -87,6 +87,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack std::vector flags_prim, flags_cons; if (driver_type == "harm") { flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, + Metadata::FillGhost, Metadata::Restart, // added by Hyerin (12/09/2022) isPrimitive, isMHD, Metadata::Vector}); flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, Metadata::Restart, Metadata::Conserved, isMHD, Metadata::WithFluxes, Metadata::Vector}); diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 6183d9a5..1e57b7bb 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -102,7 +102,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) break; } - IndexDomain domain = IndexDomain::interior; + IndexDomain domain = IndexDomain::entire; //Hyerin: why interior? int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); @@ -212,7 +212,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } break; case BSeedType::vertical: - q = bz * r * m::sin(th); + q = bz * r * m::sin(th) / 2.; default: // This shouldn't be reached. Squawk here? break; diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index fa3ec192..94ae9e5e 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -86,6 +86,7 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& Real fx1max = fpinput->GetReal("parthenon/mesh", "x1max"); bool fghostzones = fpinput->GetBoolean("parthenon/output1", "ghost_zones"); int fnghost = fpinput->GetInteger("parthenon/mesh", "nghost"); + auto fBfield = fpinput->GetOrAddString("b_field", "type", "none"); if (pin->GetOrAddBoolean("resize_restart", "use_restart_size", false)) { // This locks the mesh size to be zone-for-zone the same as the iharm3d dump file pin->SetInteger("parthenon/mesh", "nx1", fnx1); @@ -106,6 +107,7 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin->SetReal("parthenon/mesh", "restart_x1max", fx1max); pin->SetInteger("parthenon/mesh", "restart_nghost", fnghost); pin->SetBoolean("parthenon/mesh", "restart_ghostzones", fghostzones); + pin->SetString("b_field", "type", fBfield); // (12/07/22) Hyerin need to test Real gam, tNow, dt, tf; gam = fpinput->GetReal("GRMHD", "gamma"); @@ -157,6 +159,7 @@ TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin) const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); const int nghost = pin->GetReal("parthenon/mesh", "restart_nghost"); const bool ghost_zones = pin->GetBoolean("parthenon/mesh", "restart_ghostzones"); + auto fBfield = pin->GetOrAddString("b_field", "type", "none"); // Add these to package properties, since they continue to be needed on boundaries if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx1"))) @@ -191,6 +194,8 @@ TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin) pmb->packages.Get("GRMHD")->AddParam("rnghost", nghost); if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rghostzones"))) pmb->packages.Get("GRMHD")->AddParam("rghostzones", ghost_zones); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("b_field_type"))) + pmb->packages.Get("GRMHD")->AddParam("b_field_type", fBfield); // Set the whole domain SetKharmaRestart(rc); @@ -205,10 +210,12 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - //GridVector B_P; // refer to reductions/reductions.hpp - //if (pin->GetOrAddString("b_field", "type", "none") != "none") { - // B_P = rc->Get("prims.B").data; - //} + auto b_field_type = pmb->packages.Get("GRMHD")->Param("b_field_type"); + const bool include_B = (b_field_type != "none"); + GridVector B_P; // refer to reductions/reductions.hpp + if (include_B) { + B_P = rc->Get("prims.B").data; + } auto& G = pmb->coords; @@ -266,18 +273,21 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co GridScalar rho_f_device("rho_f_device", length[0], length[3], length[2], length[1]); GridScalar u_f_device("u_f_device", length[0], length[3], length[2], length[1]); GridVector uvec_f_device("uvec_f_device", NVEC, length[0], length[3], length[2], length[1]); + GridVector B_f_device("B_f_device", NVEC, length[0], length[3], length[2], length[1]); auto x1_f_host = x1_f_device.GetHostMirror(); auto x2_f_host = x2_f_device.GetHostMirror(); auto x3_f_host = x3_f_device.GetHostMirror(); auto rho_f_host = rho_f_device.GetHostMirror(); auto u_f_host = u_f_device.GetHostMirror(); auto uvec_f_host = uvec_f_device.GetHostMirror(); + auto B_f_host = B_f_device.GetHostMirror(); // Hyerin (09/19/2022) : new attempt to read the file hdf5_open(fname.c_str()); hdf5_set_directory("/"); Real *rho_file = new double[block_sz]; Real *u_file = new double[block_sz]; Real *uvec_file = new double[block_sz*3]; + Real *B_file = new double[block_sz*3]; Real *x1_file = new double[length[0]*length[1]]; Real *x2_file = new double[length[0]*length[2]]; Real *x3_file = new double[length[0]*length[3]]; @@ -293,6 +303,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co hdf5_read_array(rho_file, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(u_file, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); @@ -304,15 +315,18 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co GridScalar rho_fill_device("rho_fill_device", length[0], length[3], length[2], length[1]); GridScalar u_fill_device("u_fill_device", length[0], length[3], length[2], length[1]); GridVector uvec_fill_device("uvec_fill_device", NVEC, length[0], length[3], length[2], length[1]); + GridVector B_fill_device("B_fill_device", NVEC, length[0], length[3], length[2], length[1]); auto x1_fill_host = x1_fill_device.GetHostMirror(); auto x2_fill_host = x2_fill_device.GetHostMirror(); auto x3_fill_host = x3_fill_device.GetHostMirror(); auto rho_fill_host = rho_fill_device.GetHostMirror(); auto u_fill_host = u_fill_device.GetHostMirror(); auto uvec_fill_host = uvec_fill_device.GetHostMirror(); + auto B_fill_host = B_fill_device.GetHostMirror(); Real *rho_filefill = new double[block_sz]; Real *u_filefill = new double[block_sz]; Real *uvec_filefill = new double[block_sz*3]; + Real *B_filefill = new double[block_sz*3]; Real *x1_filefill = new double[length[0]*length[1]]; Real *x2_filefill = new double[length[0]*length[2]]; Real *x3_filefill = new double[length[0]*length[3]]; @@ -322,6 +336,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co hdf5_read_array(rho_filefill, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(u_filefill, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); @@ -338,7 +353,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co if (fname_fill != "none") x1_fill_host(iblocktemp,itemp) = x1_filefill[length[1]*iblocktemp+itemp]; } for (int jtemp = 0; jtemp < length[2]; jtemp++) { x2_f_host(iblocktemp,jtemp) = x2_file[length[2]*iblocktemp+jtemp]; - if (fname_fill != "none") x3_fill_host(iblocktemp,jtemp) = x2_filefill[length[2]*iblocktemp+jtemp]; + if (fname_fill != "none") x2_fill_host(iblocktemp,jtemp) = x2_filefill[length[2]*iblocktemp+jtemp]; } for (int ktemp = 0; ktemp < length[3]; ktemp++) { x3_f_host(iblocktemp,ktemp) = x3_file[length[3]*iblocktemp+ktemp]; if (fname_fill != "none") x3_fill_host(iblocktemp,ktemp) = x3_filefill[length[3]*iblocktemp+ktemp]; @@ -363,14 +378,18 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co vector_file_index = length[1]*(length[2]*(length[3]*(3*iblocktemp+ltemp)+ktemp)+jtemp)+itemp; uvec_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_file[vector_file_index]; + if (include_B) B_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_file[vector_file_index]; if (fname_fill != "none") { uvec_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_filefill[vector_file_index]; + if (include_B) B_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_filefill[vector_file_index]; } } } } } } + std::cout << "Hyerin: first five Bs" << B_file[0] << " " << B_file[1] << " " << B_file[2] << " " << B_file[3] << " " << B_file[4] << std::endl; + //std::cout << "Hyerin: 6,7,8,9,10 B_f " << B_f_host(0,0,0,0,6) << " " << B_f_host(0,0,0,0,7) << " " << B_f_host(0,0,0,0,8) << " " << B_f_host(0,0,0,0,9) << " " << B_f_host(0,0,0,0,10) << std::endl; const bool is_spherical = pmb->packages.Get("GRMHD")->Param("spherical"); const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); @@ -394,6 +413,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co rho_f_device.DeepCopy(rho_f_host); u_f_device.DeepCopy(u_f_host); uvec_f_device.DeepCopy(uvec_f_host); + if (include_B) B_f_device.DeepCopy(B_f_host); if (fname_fill != "none") { x1_fill_device.DeepCopy(x1_fill_host); x2_fill_device.DeepCopy(x2_fill_host); @@ -401,6 +421,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co rho_fill_device.DeepCopy(rho_fill_host); u_fill_device.DeepCopy(u_fill_host); uvec_fill_device.DeepCopy(uvec_fill_host); + if (include_B) B_fill_device.DeepCopy(B_fill_host); } //if (pin->GetOrAddString("b_field", "type", "none") != "none") { // B_P.DeepCopy(B_host); @@ -411,9 +432,9 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, KOKKOS_LAMBDA_3D { get_prim_restart_kharma(G, coords, P, m_p, blcoord, kscoord, - fx1min, fx1max, should_fill, is_spherical, gam, rs, mdot, length, - x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, - x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, + fx1min, fx1max, should_fill, is_spherical, include_B, gam, rs, mdot, length, + x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, + x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, k, j, i); GRMHD::p_to_u(G,P,m_p,gam,k,j,i,U,m_u); //TODO: shouldn't I do this too? //if (pin->GetOrAddString("b_field", "type", "none") != "none") { diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index e634f633..62bf50f3 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -34,13 +34,15 @@ KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], { //cout << "Hyerin: entered Xtoindex" <& P, const VarMap& m_p, const SphBLCoords& bl, const SphKSCoords& ks, - const Real fx1min, const Real fx1max, const bool should_fill, const bool is_spherical, + const Real fx1min, const Real fx1max, const bool should_fill, const bool is_spherical, const bool include_B, const Real gam, const Real rs, const Real mdot, const hsize_t length[GR_DIM], - const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, - const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridScalar& rho_fill, const GridScalar& u_fill, const GridVector& uvec_fill, + const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, const GridVector& B, + const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridScalar& rho_fill, const GridScalar& u_fill, const GridVector& uvec_fill, const GridVector& B_fill, const int& k, const int& j, const int& i) { Real rho_temp, u_temp; - Real u_prim[NVEC]; + Real u_prim[NVEC], B_prim[NVEC]; GReal X[GR_DIM]; G.coord(k, j, i, Loci::center, X); @@ -125,11 +127,13 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); rho_temp = rho(iblocktemp,ktemp,jtemp,itemp); u_temp = u(iblocktemp,ktemp,jtemp,itemp); + if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); Real T = get_T(r, C1, C2, n, rs); Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; convert_to_utwiddle(G,coords,bl,ks,k,j,i,ucon_bl,u_prim); + } // HyerinTODO: if fname_fill exists and smaller. else if ((should_fill) && ((X[1]>fx1max)||(X[1] Date: Wed, 14 Dec 2022 11:36:34 -0600 Subject: [PATCH 012/219] Aesthetics - plotting and indentation --- kharma/harm_driver.cpp | 8 ++++---- kharma/main.cpp | 4 ++-- tests/emhdmodes/check.py | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/kharma/harm_driver.cpp b/kharma/harm_driver.cpp index 5a16c81c..8c30b7c0 100644 --- a/kharma/harm_driver.cpp +++ b/kharma/harm_driver.cpp @@ -64,16 +64,16 @@ TaskCollection HARMDriver::MakeTaskCollection(BlockList_t &blocks, int stage) TaskCollection tc; TaskID t_none(0); - Real beta = integrator->beta[stage - 1]; - const Real dt = integrator->dt; + Real beta = integrator->beta[stage - 1]; + const Real dt = integrator->dt; auto stage_name = integrator->stage_name; // Which packages we load affects which tasks we'll add to the list auto& pkgs = blocks[0]->packages.AllPackages(); - bool use_b_cd = pkgs.count("B_CD"); + bool use_b_cd = pkgs.count("B_CD"); bool use_b_flux_ct = pkgs.count("B_FluxCT"); bool use_electrons = pkgs.count("Electrons"); - bool use_wind = pkgs.count("Wind"); + bool use_wind = pkgs.count("Wind"); // Allocate the fields ("containers") we need block by block for (int i = 0; i < blocks.size(); i++) { diff --git a/kharma/main.cpp b/kharma/main.cpp index e1053b8b..b180afe7 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -136,9 +136,9 @@ int main(int argc, char *argv[]) signal(SIGSEGV, print_backtrace); #endif - auto pin = pman.pinput.get(); // All parameters in the input file or command line + auto pin = pman.pinput.get(); // All parameters in the input file or command line auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc - auto papp = pman.app_input.get(); // The list of callback functions specified above + auto papp = pman.app_input.get(); // The list of callback functions specified above // Add magnetic field to the problem, initialize ghost zones. // Implemented separately outside of MeshBlock since diff --git a/tests/emhdmodes/check.py b/tests/emhdmodes/check.py index fae56927..c3ab3864 100644 --- a/tests/emhdmodes/check.py +++ b/tests/emhdmodes/check.py @@ -11,7 +11,7 @@ outputdir = './' NVAR = 10 - VARS = ['rho', 'u', 'u1', 'u2', 'u3', 'B1', 'B2', 'B3', 'q', 'deltaP'] + VARS = ['rho', 'u', 'u1', 'u2', 'u3', 'B1', 'B2', 'B3', 'q', 'dP'] RES = [int(r) for r in sys.argv[1].split(",")] LONG = sys.argv[2] SHORT = sys.argv[3] @@ -104,6 +104,7 @@ fail = 1 # plot + colors = ['indigo', 'goldenrod', 'darkgreen', 'crimson', 'xkcd:blue', 'xkcd:magenta', 'green', 'xkcd:yellowgreen', 'xkcd:teal', 'xkcd:olive'] fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(1,1,1) @@ -113,12 +114,12 @@ tracker = 0 for n in range(NVAR): if abs((dvar_cos[n] != 0) or abs(dvar_sin[n] != 0)): - ax.loglog(RES, L1[:,n], marker='o', label=pyharm.pretty(VARS[n])) + ax.loglog(RES, L1[:,n], color=colors[n], marker='o', label=pyharm.pretty(VARS[n])) tracker += 1 ax.loglog([RES[0], RES[-1]], 100*amp*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') plt.xscale('log', base=2) ax.legend() - plt.savefig(os.path.join(outputdir, "emhd_linear_mode_convergence_"+SHORT+".png")) + plt.savefig(os.path.join(outputdir, "emhd_linear_mode_convergence_"+SHORT+".png"), dpi=300) exit(fail) From 84b3cad58bd1b03bdc627a9c563f4a0f82e40b5e Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 14 Dec 2022 14:30:07 -0500 Subject: [PATCH 013/219] minor update in prob, to remove floors --- kharma/prob/problem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 0f336114..89ecbf25 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -158,7 +158,7 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) Flux::PtoU(rc.get(), IndexDomain::interior); // If we're not restarting, apply the floors - if (prob != "resize_restart") { + if ((prob != "resize_restart") && (prob != "resize_restart_kharma")) { // This is purposefully done even if floors are disabled, // as it is required for consistent initialization // Note however we do *not* preserve any inversion flags in this call. From 81d4a413eb20a68f2825781bbb842e002f59d769 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 20 Dec 2022 16:55:11 -0500 Subject: [PATCH 014/219] Solved the issue of B being overwritten when resize_restart_kharma (it was due to ReflectX2). The only issue left is big DivB at the patching boundary. This is confusing because all the primitive and conservative values are all copied over properly --- kharma/b_flux_ct/b_flux_ct.cpp | 7 ++- kharma/b_flux_ct/seed_B_ct.cpp | 35 ++++++++++++++ kharma/boundaries.cpp | 10 +++- kharma/main.cpp | 4 +- kharma/prob/problem.cpp | 6 ++- kharma/prob/resize_restart_kharma.cpp | 53 ++++++++++----------- kharma/prob/resize_restart_kharma.hpp | 66 +++++++++++++++++++++++---- 7 files changed, 140 insertions(+), 41 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 62d6ecf2..ac284ead 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -107,8 +107,11 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack m = Metadata(flags_cons, s_vector); pkg->AddField("cons.B", m); - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::Restart, Metadata::FillGhost}); pkg->AddField("divB", m); + // Hyerin (12/19/22) + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); + pkg->AddField("B_Save", m); // Ensure that prims get filled if (!implicit_b) { @@ -191,7 +194,7 @@ void PtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) const IndexRange jb = bounds.GetBoundsJ(domain); const IndexRange kb = bounds.GetBoundsK(domain); const IndexRange vec = IndexRange({0, B_U.GetDim(4)-1}); - pmb->par_for("UtoP_B", vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("PtoU_B", vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_VEC { // Update the primitive B-fields B_U(mu, k, j, i) = B_P(mu, k, j, i) * G.gdet(Loci::center, j, i); diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 1e57b7bb..fb9add65 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -52,6 +52,18 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) GridScalar rho = rc->Get("prims.rho").data; GridVector B_P = rc->Get("prims.B").data; GridVector B_U = rc->Get("cons.B").data; + GridVector B_Save = rc->Get("B_Save").data; + Real fx1min, fx1max, dx1, fx1min_ghost; + int n1tot; + if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { + fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); + fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); + n1tot = pmb->packages.Get("GRMHD")->Param("rnx1"); + dx1 = (fx1max - fx1min) / n1tot; + fx1min_ghost = fx1min - 4*dx1; + } + auto fname_fill = pin->GetOrAddString("resize_restart", "fname_fill", "none"); + const bool should_fill = !(fname_fill == "none"); Real min_rho_q = pin->GetOrAddReal("b_field", "min_rho_q", 0.2); std::string b_field_type = pin->GetString("b_field", "type"); @@ -310,6 +322,29 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) ); } + if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { + // Hyerin (12/19/22) copy over data after initialization + + pmb->par_for("copy_B_restart_resize_kharma", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA_3D { + GReal X[GR_DIM]; + G.coord(k, j, i, Loci::center, X); + + if ((!should_fill) && (X[1]> &rc, IndexDomain domain, boo auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; const auto& G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin + Real x_EH = pmb->packages.Get("GRMHD")->Param("x_EH"); //Hyerin // q will actually have *both* cons & prims (unless using imex driver) // We'll only need cons.B specifically tho @@ -180,9 +182,13 @@ void ReflectX2(std::shared_ptr> &rc, IndexDomain domain, boo // Side note: this *lags* the X1/X2 corner zones by one step, since X1 is applied first. // this is potentially bad int ics = (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) ? is : is_e; - int ice = (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) ? ie : ie_e; + //int ice = (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) ? ie : ie_e; //int ics = is_e; - //int ice = ie_e; + int ice = ie_e; + if (x1min > x_EH){ + ics = is_e; // overwrite the starting index such that + //ice = ie_e; // the reflectx2 bc is also applied to outermost and innermost boundary + } int ref_tmp, add_tmp, jbs, jbe; if (domain == IndexDomain::inner_x2) { diff --git a/kharma/main.cpp b/kharma/main.cpp index 06751bf2..32a3f138 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -147,8 +147,8 @@ int main(int argc, char *argv[]) std::cout << "Running post-initialization tasks..." << std::endl; auto prob = pin->GetString("parthenon/job", "problem_id"); - //bool is_restart = (prob == "resize_restart") || pman.IsRestart(); - bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); // Hyerin + bool is_restart = (prob == "resize_restart") || pman.IsRestart(); + //bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); // Hyerin bool is_resize = (prob == "resize_restart") && !pman.IsRestart(); KHARMA::PostInitialize(pin, pmesh, is_restart, is_resize); Flag("Post-initialization completed"); diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 89ecbf25..507ca53e 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -82,10 +82,14 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // Hyerin - // save x1min for boundary conditions in boundaries.cpp + // save x1min, x_EH for boundary conditions in boundaries.cpp const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); + const Real a = pin->GetReal("coordinates", "a"); + const GReal x_EH = log(1 + m::sqrt(1 - a*a)); // EH radius if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x1min"))) pmb->packages.Get("GRMHD")->AddParam("x1min", x1min); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x_EH"))) + pmb->packages.Get("GRMHD")->AddParam("x_EH", x_EH); auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 94ae9e5e..86f5453a 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -207,22 +207,13 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co { Flag(rc, "Setting KHARMA restart zones"); auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; auto b_field_type = pmb->packages.Get("GRMHD")->Param("b_field_type"); const bool include_B = (b_field_type != "none"); - GridVector B_P; // refer to reductions/reductions.hpp - if (include_B) { - B_P = rc->Get("prims.B").data; - } + // A placeholder to save the B fields for SeedBField + GridVector B_Save = rc->Get("B_Save").data; auto& G = pmb->coords; - //if (pin->GetOrAddString("b_field", "type", "none") != "none") { - // auto B_host = B_P.GetHostMirror(); - //} - // Size/domain of the MeshBlock we're reading to int is, ie; if (domain == IndexDomain::outer_x1) {// copying from bondi @@ -246,6 +237,18 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co hsize_t n2mb = pmb->packages.Get("GRMHD")->Param("rmbnx2"); hsize_t n3mb = pmb->packages.Get("GRMHD")->Param("rmbnx3"); hsize_t nBlocks = (int) (n1tot*n2tot*n3tot)/(n1mb*n2mb*n3mb); + auto fname = pmb->packages.Get("GRMHD")->Param("fname"); + auto fname_fill = pmb->packages.Get("GRMHD")->Param("fname_fill"); + const bool should_fill = !(fname_fill == "none"); + const Real fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); + const Real fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); + const Real dx1 = (fx1max - fx1min) / n1tot; + const Real fx1min_ghost = fx1min - 4*dx1; + PackIndexMap prims_map, cons_map; + auto P = GRMHD::PackMHDPrims(rc, prims_map); + auto U = GRMHD::PackMHDCons(rc, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + if ((domain != IndexDomain::outer_x1) && (domain != IndexDomain::inner_x1)) { // read from a restart file and save it to static GridScalar //cout << "Hyerin: reading files" << endl; @@ -263,8 +266,6 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co const int block_sz = length[0]*length[1]*length[2]*length[3]; //std::cout << "lengths " << length[0] << " " << length[1] <<" " << length[2]<<" " << length[3] << std::endl; - auto fname = pmb->packages.Get("GRMHD")->Param("fname"); - auto fname_fill = pmb->packages.Get("GRMHD")->Param("fname_fill"); // read from file and stored in device Hyerin (10/18/2022) GridScalar x1_f_device("x1_f_device", length[0], length[1]); @@ -303,7 +304,8 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co hdf5_read_array(rho_file, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(u_file, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_file, "cons.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); @@ -336,16 +338,14 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co hdf5_read_array(rho_filefill, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(u_filefill, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_filefill, "cons.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); hdf5_close(); } - const Real fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); - const Real fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); - // save the grid coordinate values to host array for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { for (int itemp = 0; itemp < length[1]; itemp++) { @@ -388,23 +388,17 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co } } } - std::cout << "Hyerin: first five Bs" << B_file[0] << " " << B_file[1] << " " << B_file[2] << " " << B_file[3] << " " << B_file[4] << std::endl; + //std::cout << "Hyerin: first five Bs" << B_file[0] << " " << B_file[1] << " " << B_file[2] << " " << B_file[3] << " " << B_file[4] << std::endl; //std::cout << "Hyerin: 6,7,8,9,10 B_f " << B_f_host(0,0,0,0,6) << " " << B_f_host(0,0,0,0,7) << " " << B_f_host(0,0,0,0,8) << " " << B_f_host(0,0,0,0,9) << " " << B_f_host(0,0,0,0,10) << std::endl; const bool is_spherical = pmb->packages.Get("GRMHD")->Param("spherical"); const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); - const bool should_fill = !(fname_fill == "none"); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - //cout << "Hyerin: should fill " << should_fill <(G.coords.base); SphBLCoords blcoord = SphBLCoords(kscoord.a); //, kscoord.ext_g); // modified (11/15/22) CoordinateEmbedding coords = G.coords; - PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(rc, prims_map); - auto U = GRMHD::PackMHDCons(rc, cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); // Deep copy to device x1_f_device.DeepCopy(x1_f_host); @@ -436,12 +430,19 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, k, j, i); - GRMHD::p_to_u(G,P,m_p,gam,k,j,i,U,m_u); //TODO: shouldn't I do this too? + //GRMHD::p_to_u(G,P,m_p,gam,k,j,i,U,m_u); //TODO: is this needed? I don't see it in resize_restart.cpp //if (pin->GetOrAddString("b_field", "type", "none") != "none") { // VLOOP B_host(v, k, j, i) = interp_scalar(G, X, startx, stopx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(B_file[v*block_sz])); //} + if (include_B) + get_B_restart_kharma(G, coords, P, m_p, blcoord, kscoord, + fx1min, fx1max, should_fill, length, + x1_f_device, x2_f_device, x3_f_device, B_f_device, + x1_fill_device, x2_fill_device, x3_fill_device, B_fill_device, B_Save, + k, j, i); } ); + //if (include_B) B_FluxCT::PtoU(rc,domain); // added for B fields } return TaskStatus::complete; diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 62bf50f3..0cfec8cb 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -7,6 +7,7 @@ // added by Hyerin (10/07/22) #include "bondi.hpp" +#include "b_flux_ct.hpp" /** * Read the header of an KHARMA HDF5 restart file, and set appropriate parameters @@ -104,7 +105,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons const int& k, const int& j, const int& i) { Real rho_temp, u_temp; - Real u_prim[NVEC], B_prim[NVEC]; + Real u_prim[NVEC]; //, B_prim[NVEC]; GReal X[GR_DIM]; G.coord(k, j, i, Loci::center, X); @@ -127,7 +128,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); rho_temp = rho(iblocktemp,ktemp,jtemp,itemp); u_temp = u(iblocktemp,ktemp,jtemp,itemp); - if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); + //if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); Real T = get_T(r, C1, C2, n, rs); Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); @@ -142,7 +143,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons rho_temp = rho_fill(iblocktemp,ktemp,jtemp,itemp); u_temp = u_fill(iblocktemp,ktemp,jtemp,itemp); VLOOP u_prim[v] = uvec_fill(v,iblocktemp,ktemp,jtemp,itemp); - if (include_B) VLOOP B_prim[v] = B_fill(v,iblocktemp,ktemp,jtemp,itemp); + //if (include_B) VLOOP B_prim[v] = B_fill(v,iblocktemp,ktemp,jtemp,itemp); } else { Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); @@ -152,17 +153,66 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons rho_temp = rho(iblocktemp,ktemp,jtemp,itemp); u_temp = u(iblocktemp,ktemp,jtemp,itemp); VLOOP u_prim[v] = uvec(v,iblocktemp,ktemp,jtemp,itemp); - if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); + //if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); } P(m_p.RHO, k, j, i) = rho_temp; P(m_p.UU, k, j, i) = u_temp; P(m_p.U1, k, j, i) = u_prim[0]; P(m_p.U2, k, j, i) = u_prim[1]; P(m_p.U3, k, j, i) = u_prim[2]; - if (include_B) { // sth like this? Hyerin - P(m_p.B1, k, j, i) = B_prim[0]; // TODO: It should actually B_cons/g - P(m_p.B2, k, j, i) = B_prim[1]; - P(m_p.B3, k, j, i) = B_prim[2]; + //if (include_B) { // sth like this? Hyerin + // P(m_p.B1, k, j, i) = B_prim[0]; // TODO: It should actually B_cons/g + // P(m_p.B2, k, j, i) = B_prim[1]; + // P(m_p.B3, k, j, i) = B_prim[2]; + /* + if (i<5 && j==0 && k==0) { + printf("for i= %i :B field %g %g %g, velocity %g %g %g \n", + i, B_prim[0], B_prim[1], B_prim[2], + u_prim[0], u_prim[1], u_prim[2]); + }*/ + //} + +} + +KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, + const SphBLCoords& bl, const SphKSCoords& ks, + const Real fx1min, const Real fx1max, const bool should_fill, + const hsize_t length[GR_DIM], + const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridVector& B, + const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridVector& B_fill, const GridVector& B_save, + const int& k, const int& j, const int& i) +{ + //Real B_prim[NVEC]; + Real B_cons[NVEC]; + + GReal X[GR_DIM]; + G.coord(k, j, i, Loci::center, X); + GReal del[GR_DIM]; // not really needed now since I am doing nearest neighbor interpolation + int iblocktemp, itemp, jtemp, ktemp; + // Interpolate the value at this location from the global grid + if ((!should_fill) && (X[1]fx1max)||(X[1] Date: Tue, 3 Jan 2023 16:20:34 -0700 Subject: [PATCH 015/219] Only read necessary zones when resizing. Roots of a possible resize test. --- kharma/prob/hdf5_utils.cpp | 10 +- kharma/prob/interpolation.hpp | 197 ++++++++---------------- kharma/prob/post_initialize.cpp | 13 ++ kharma/prob/resize_restart.cpp | 255 +++++++++++++++++++++++--------- pars/resize_orszag_tang.par | 80 ++++++++++ 5 files changed, 349 insertions(+), 206 deletions(-) create mode 100644 pars/resize_orszag_tang.par diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index ab55dbcf..a97da26e 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -394,7 +394,15 @@ int hdf5_read_array(void *data, const char *name, size_t rank, strncpy(path, hdf5_cur_dir, STRLEN); strncat(path, name, STRLEN - strlen(path)); - if(DEBUG) fprintf(stderr,"Reading arr %s\n", path); + if(DEBUG) { + fprintf(stderr,"Reading arr %s:\n", path); + fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); + fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); + fprintf(stderr,"File read size: %llu %llu %llu %llu\n\n", fcount[0], fcount[1], fcount[2], fcount[3]); + + fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n", mstart[0], mstart[1], mstart[2], mstart[3]); + } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index 234cd3e0..ed6a0272 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -35,24 +35,24 @@ #include "decs.hpp" -// For using the ipole routines verbatim. -// Automatically wraps in k so we can avoid ghost zones -#define ind_sph(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (j) * n1 + (i)) -#define ind_periodic(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (((j)+n2) % n2) * n1 + (((i)+n1) % n1) ) - /** - * Routines for interpolating and initializing a KHARMA meshblock from the - * correct area of a global iharm3d restart file, used in resize_restart.cpp. - * Doesn't include "Elliptic maid" solver step for eliminating magnetic field - * divergence, see b_flux_ct for that (as it is divergence-rep dependent) + * Routines for interpolating on a grid, with values given in a flattened array. + * Mostly used in resize_restart.cpp, which must interpolate from a grid corresponding + * to an old simulation, read from a file. + * + * Note that resizing a file nearly always requires fixing the resulting magentic field + * divergence -- see b_cleanup/ for details. */ +namespace Interpolation { + /** - * translates geodesic coordinates to a grid zone and returns offset - * for interpolation purposes. integer index corresponds to the zone - * center "below" the desired point and del[i] \in [0,1) returns the - * offset from that zone center. + * Finds the closest grid zone which lies to the left of the given point in X1,X2, and X3, + * along with the distance 'del' from that center to X in each coordinate, + * for interpolation purposes. * + * Example (from ipole, ) + * * 0 0.5 1 * [ | ] * A B C DE F @@ -67,139 +67,70 @@ * E -> ( 1, 0.0) * F -> ( 1, 0.5) */ -KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal XG[GR_DIM], +KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], const GReal startx[GR_DIM], const GReal dx[GR_DIM], - int& i, int& j, int& k, GReal del[GR_DIM], - bool nearest=false) -{ - // If we ever include ghosts in iharm3d-format restarts, we need to clip phi here - // GReal phi = fmod(XG[3], stopx[3]); - // if (phi < 0.0) // TODO adapt for startx3 != 0? - // phi += stopx[3]; - GReal phi = XG[3]; - - if (nearest) { - // get the index of the zone we are in: >= left corner? - i = (int) ((XG[1] - startx[1]) / dx[1] + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] + 1000) - 1000; - } else { - // Normal operation - // get provisional zone index. see note above function for details. note we - // shift to zone centers because that's where variables are most exact. - i = (int) ((XG[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] - 0.5 + 1000) - 1000; - } - - // now construct del - del[1] = (XG[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; - del[2] = (XG[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; - del[3] = (phi - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; -} - -KOKKOS_INLINE_FUNCTION void ijktoX(const GReal startx[GR_DIM], const GReal dx[GR_DIM], - const int& i, const int& j, const int& k, - GReal XG[GR_DIM]) + int& i, int& j, int& k, GReal del[GR_DIM]) { + // Normal operation // get provisional zone index. see note above function for details. note we // shift to zone centers because that's where variables are most exact. - XG[0] = 0.; - XG[1] = startx[1] + (i + 0.5) * dx[1]; - XG[2] = startx[2] + (j + 0.5) * dx[2]; - XG[3] = startx[3] + (k + 0.5) * dx[3]; + i = (int) ((X[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] - 0.5 + 1000) - 1000; + + // Distance from closest zone center on the left + // i.e., portion of left zone to use vs right when interpolating + del[1] = (X[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; + del[2] = (X[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; + del[3] = (X[3] - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; } /** - * This interpolates a single-array variable 'var' representing a grid of size 'startx' to 'stopx' in - * native coordinates, returning its value at location X - * NOTE: 'startx' must correspond to the grid you are interpolating *from* + * Translates a point X in native coordinates to a grid zone. */ -KOKKOS_INLINE_FUNCTION Real linear_interp(const GRCoordinates& G, const GReal X[GR_DIM], - const GReal startx[GR_DIM], - const GReal dx[GR_DIM], const bool& is_spherical, const bool& weight_by_gdet, - const int& n3, const int& n2, const int& n1, - const Real *var) +KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], + const GReal startx[GR_DIM], + const GReal dx[GR_DIM], + int& i, int& j, int& k) { - // zone and offset from X - // Obtain this in - GReal del[GR_DIM]; - int i, j, k; - Xtoijk(X, startx, dx, i, j, k, del); - - Real interp; - if (is_spherical) { - // For ghost zones, we treat each boundary differently: - // In X1, repeat first & last zones. - if (i < 0) { i = 0; del[1] = 0; } - if (i > n1-2) { i = n1 - 2; del[1] = 1; } - // In X2, stop completely at the last zone - // Left side of leftmost segment - if (j < 0) { j = 0; del[2] = 0; } - // Right side of rightmost segment. Phrased this way to not segfault - if (j > n2-2) { j = n2 - 2; del[2] = 1; } - // k auto-wraps. So do all indices for periodic boxes. - - if (weight_by_gdet) { - GReal Xtmp[GR_DIM]; - ijktoX(startx, dx, i, j, k, Xtmp); - GReal g_ij = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j, k, Xtmp); - GReal g_i1j = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i, j + 1, k, Xtmp); - GReal g_ij1 = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j + 1, k, Xtmp); - GReal g_i1j1 = G.coords.gdet_native(Xtmp); - - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*g_i1j1*del[1]*del[2]; - - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*g_i1j1*del[1]*del[2]); - } - interp /= G.coords.gdet_native(X); - } else { - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*del[1]*del[2]; + // Get the index of the zone this point falls into. + // i.e., are we >= the left corner? + i = (int) ((X[1] - startx[1]) / dx[1] + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] + 1000) - 1000; +} - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } - } - } else { - // interpolate in x1 and x2 - interp = var[ind_periodic(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k)]*del[1]*del[2]; +/** + * Dumb linear interpolation: no special cases for boundaries + * Takes indices i,j,k and a block size n1, n2, n3, + * as well as a flat array var. + * + * TODO version(s) with View(s) for real device-side operation + */ +// For using the ipole routines in a recognizable form on a 1D array +#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_periodic(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } +KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, + const int& n1, const int& n2, const int& n3, + const double del[4], const double *var) +{ + // Interpolate in 1D at a time to avoid reading zones we don't have + Real interp = var[ind(i , j , k)]*(1. - del[1]) + + var[ind(i + 1, j , k)]*del[1]; + if (n2 > 1) { + interp = (1. - del[2])*interp + + del[2]*(var[ind(i , j + 1, k)]*(1. - del[1]) + + var[ind(i + 1, j + 1, k)]*del[1]); + } + if (n3 > 1) { + interp = (1. - del[3])*interp + + del[3]*(var[ind(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + + var[ind(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + + var[ind(i , j + 1, k + 1)]*(1. - del[1])*del[2] + + var[ind(i + 1, j + 1, k + 1)]*del[1]*del[2]); } - return interp; } +} // Interpolation \ No newline at end of file diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 18f342b2..d8c82435 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -179,6 +179,19 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrmesh_data.GetOrAdd("base", 0); diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 5f60e3e9..e3584c51 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -45,19 +45,20 @@ #include #include -// This is gross, but everything else is grosser -// What's a little leaked host mem between friends? -static Real *ptmp = NULL; -static int blocks_initialized = 0; - // TODO: The iharm3d restart format fails to record several things we must guess: // 1. Sometimes, even precise domain boundaries in native coordinates // 2. Which coordinate system was used // 3. Any coordinate system parameters // Better to either: // a. read KHARMA restart files so we can re-grid -// b. use the IL dump format, but in double -// Either are useful capabilities. +// b. use the IL dump format, but in double precision (or even in single w/cleanup) +// Either would be very useful independently + +// This exists to simplify some initializer lists below +// This indicates I know that moving from signed->unsigned is dangerous, +// and sign off that these results are positive (they are) +hsize_t static_max(int i, int n) { return static_cast(m::max(i, n)); } +hsize_t static_min(int i, int n) { return static_cast(m::min(i, n)); } void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin) { @@ -74,7 +75,6 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& std::cout << "Initialized from " << fname << ", file version " << version << std::endl << std::endl; } - // Read what we need from the file, regardless of where we're putting it int n1file, n2file, n3file; hdf5_read_single_val(&n1file, "n1", H5T_STD_I32LE); @@ -236,12 +236,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) { Flag(rc, "Restarting from iharm3d checkpoint file"); - // TODO pack? Probably not worth it auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess const bool regrid_only = pin->GetOrAddBoolean("resize_restart", "regrid_only", false); @@ -271,7 +266,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetInteger("parthenon/mesh", "nx2") != n2tot || pin->GetInteger("parthenon/mesh", "nx3") != n3tot) { printf("Mesh size does not match!\n"); - printf("[%d %d %d] vs [%d %d %d]", + printf("[%d %d %d] vs [%llu %llu %llu]", pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), pin->GetInteger("parthenon/mesh", "nx3"), @@ -307,74 +302,192 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) } } - // TODO there must be a better way to cache this. InitUserData and make it a big variable or something? - if (ptmp == NULL) { - std::cout << "Reading mesh from file to cache..." << std::endl; + if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; - // Declare known sizes for inputting/outputting primitives - // We'll only ever read the full block, so this is the size we want - hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; - hsize_t fstart[] = {0, 0, 0, 0}; - ptmp = new double[nfprim*n3tot*n2tot*n1tot]; // These will include B & thus be double or upconverted to it + // In this section we're dealing with two different meshes: the one we're interpolating *from* (the "file" grid) + // and the one we're interpolating *to* -- the "meshblock." + // Additionally, in the "file" mesh we must deail with global file locations (no ghost zones, global index, prefixed "g") + // as well as local file locations (locations in a cache we read to host memory, prefixed "m") - hdf5_open(fname.c_str()); - hdf5_set_directory("/"); - hdf5_read_array(ptmp, "p", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_close(); + // Size/domain of the MeshBlock we're reading *to*. + // Note that we only fill the block's physical zones -- + // PostInitialize will take care of ghosts with MPI syncs and calls to the domain boundary conditions + IndexDomain domain = IndexDomain::interior; + const IndexRange ib = pmb->cellbounds.GetBoundsI(domain); + const IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); + const IndexRange kb = pmb->cellbounds.GetBoundsK(domain); + const auto& G = pmb->coords; - std::cout << "Read!" << std::endl; + // Total file size + hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; + + // Figure out the subset in global space corresponding to our memory cache + int gis, gjs, gks, gie, gje, gke; + if (regrid_only) { + // For nearest neighbor "interpolation," we don't need any ghost zones + // Global location of first zone of our new grid + double X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + Interpolation::Xtoijk_nearest(X, startx, dx, gis, gjs, gks); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gie, gje, gke); + } else { + // Linear interpolation case: we need ghost zones + // Global location of first zone of our new grid + double tmp[GR_DIM], X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + // Note this will be the *left* side already, so we'll never read below this. + // The values gis,gjs,gks can/will be -1 sometimes + Interpolation::Xtoijk(X, startx, dx, gis, gjs, gks, tmp); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk(X, startx, dx, gie, gje, gke, tmp); + // Include one extra zone in each direction, for right side of linear interp + gke += 1; gje += 1; gie += 1; } - // If we are going to keep a static pointer, keep count so the last guy can kill it - blocks_initialized += 1; + // Truncate the file read sizes so we don't overrun the file data + hsize_t fstart[4] = {0, static_max(gks, 0), static_max(gjs, 0), static_max(gis, 0)}; + // TODO separate nmprim to stop at 8 prims if we don't need e- + hsize_t fstop[4] = {nfprim, static_min(gke, n3tot), static_min(gje, n2tot), static_min(gie, n1tot)}; + hsize_t fcount[4] = {fstop[0] - fstart[0], fstop[1] - fstart[1], fstop[2] - fstart[2], fstop[3] - fstart[3]}; + // If we overran an index on the left, we need to leave a blank row (i.e., start at 1 == true) to reflect this + hsize_t mstart[4] = {0, (gks < 0), (gjs < 0), (gis < 0)}; + // Total memory size is never truncated + hsize_t nmk = gke-gks, nmj = gje-gjs, nmi = gie-gis; + hsize_t mdims[4] = {nfprim, nmk, nmj, nmi}; + // TODO should yell if any of these fired for nearest-neighbor + + // Allocate the array we'll need + hsize_t nmblock = nmk * nmj * nmi; + // TODO this may be float[] if we ever want to read dump files as restarts + double *ptmp = new double[nfprim*nmblock]; + + // Open the file + hdf5_open(fname.c_str()); + hdf5_set_directory("/"); + + // Read the main array + hdf5_read_array(ptmp, "p", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + + // Do some special reads from elsewhere in the file to fill periodic bounds + // Note we do NOT fill outflow/reflecting bounds here -- instead, we treat them specially below + // TODO this could probably be a lot cleaner + hsize_t fstart_tmp[4], fcount_tmp[4], mstart_tmp[4]; +#define RESET_COUNTS DLOOP1 {fstart_tmp[mu] = fstart[mu]; fcount_tmp[mu] = fcount[mu]; mstart_tmp[mu] = mstart[mu];} + if (gks < 0 && pmb->boundary_flag[BoundaryFace::inner_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally LAST rank in X3 + fstart_tmp[1] = n3tot-1; + fcount_tmp[1] = 1; + // Read it to the FIRST rank of our array + mstart_tmp[1] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gke > n3tot && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally FIRST rank in X3 + fstart_tmp[1] = 0; + fcount_tmp[1] = 1; + // Read it to the LAST rank of our array + mstart_tmp[1] = mdims[1]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gjs < 0 && pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = n2tot-1; + fcount_tmp[2] = 1; + mstart_tmp[2] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gje > n2tot && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = 0; + fcount_tmp[2] = 1; + mstart_tmp[2] = mdims[2]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gis < 0 && pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = n1tot-1; + fcount_tmp[3] = 1; + mstart_tmp[3] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gie > n1tot && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = 0; + fcount_tmp[3] = 1; + mstart_tmp[3] = mdims[3]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + + hdf5_close(); + + if (MPIRank0()) std::cout << "Read!" << std::endl; + + // Get the arrays we'll be writing to + // TODO this is probably easier AND more flexible if we pack them + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + GridVector B_P = rc->Get("prims.B").data; auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); auto B_host = B_P.GetHostMirror(); - // Size/domain of the MeshBlock we're reading *to*. - // Note that we only read physical zones. - IndexDomain domain = IndexDomain::interior; - const IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - const IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - const IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - - auto& G = pmb->coords; - - Flag("Reordering meshblock..."); - // Host-side interpolate & copy into the mirror array - // TODO Support restart native coordinates != new native coordinates + Flag("Interpolating meshblock..."); + // Interpolate on the host side & copy into the mirror Views + // Nearest-neighbor interpolation is currently only used when grids exactly correspond -- otherwise, linear interpolation is used + // to minimize the resulting B field divergence. // NOTE: KOKKOS USES < not <=!! Therefore the RangePolicy below will seem like it is too big if (regrid_only) { - // Kokkos::parallel_for("copy_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO Kokkos calls here had problems with CUDA, reintroduce/fix + // OpenMP here conflicts with Kokkos parallel in some cases, so we're stuck for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); double tmp[GR_DIM]; - int gk,gj,gi; Xtoijk(X, startx, dx, gi, gj, gk, tmp, true); - // Fill block cells with global equivalents - rho_host(k, j, i) = ptmp[0*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - u_host(k, j, i) = ptmp[1*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP B_host(v, k, j, i) = ptmp[(5+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - } - // ); + GReal X[GR_DIM]; int gk, gj, gi; + G.coord(k, j, i, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gi, gj, gk); + // TODO verify this never reads zones outside the cache + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Fill cells of the new block with equivalents in the cached block + rho_host(k, j, i) = ptmp[0*nmblock + mk*nmj*nmi + mj*nmi + mi]; + u_host(k, j, i) = ptmp[1*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP B_host(v, k, j, i) = ptmp[(5+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + } } else { - // Kokkos::parallel_for("interp_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO real boundary flags. Repeat on any outflow/reflecting bounds + const bool repeat_x1i = is_spherical; + const bool repeat_x1o = is_spherical; + const bool repeat_x2i = is_spherical; + const bool repeat_x2o = is_spherical; + for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - // Get the zone center location - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - // Interpolate the value at this location from the global grid - rho_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[0*n3tot*n2tot*n1tot])); - u_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[1*n3tot*n2tot*n1tot])); - VLOOP uvec_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(2+v)*n3tot*n2tot*n1tot])); - VLOOP B_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(5+v)*n3tot*n2tot*n1tot])); - } - // ); + GReal X[GR_DIM], del[GR_DIM]; int gk, gj, gi; + // Get the zone center location + G.coord(k, j, i, Loci::center, X); + // Get global indices + Interpolation::Xtoijk(X, startx, dx, gi, gj, gk, del); + // Make any corrections due to global boundaries + // Currently just repeats the last zone, equivalent to falling back to nearest-neighbor + if (repeat_x1i && gi < 0) { gi = 0; del[1] = 0; } + if (repeat_x1o && gi > n1tot-2) { gi = n1tot - 2; del[1] = 1; } + if (repeat_x2i && gj < 0) { gj = 0; del[2] = 0; } + if (repeat_x2o && gj > n2tot-2) { gj = n2tot - 2; del[2] = 1; } + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Interpolate the value at this location from the cached grid + rho_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[0*nmblock])); + u_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[1*nmblock])); + VLOOP uvec_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(2+v)*nmblock])); + VLOOP B_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(5+v)*nmblock])); + } } // Deep copy to device @@ -385,11 +498,9 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) B_P.DeepCopy(B_host); Kokkos::fence(); - // Close the door on our way out - if (blocks_initialized == pmb->pmy_mesh->GetNumMeshBlocksThisRank()) { - std::cout << "Deleting cached mesh" << std::endl; - delete[] ptmp; - } + // Delete our cache. Only we ever used it, so we're safe here. + Flag("Deleting cached interpolation values"); + delete[] ptmp; return TaskStatus::complete; } diff --git a/pars/resize_orszag_tang.par b/pars/resize_orszag_tang.par new file mode 100644 index 00000000..92392107 --- /dev/null +++ b/pars/resize_orszag_tang.par @@ -0,0 +1,80 @@ +# Restart from an iharm3d snapshot file, resizing to specified mesh +# Note most parameters here will carry through to running after +# restarting, as iharm3d restart files do not specify much + + +problem_id = resize_restart + + +refinement = none +numlevel = 1 + +nx1 = 512 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 512 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 512 +nx2 = 512 +nx3 = 1 + + +base = spherical_ks +transform = fmks +a = 0.9375 +hslope = 0.3 +r_out = 1000 + + +tlim = 300000 +integrator = rk2 +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 + + +fname = orszag_tang.out2.00001.h5 +use_tf = false +use_dt = false +skip_b_cleanup = false + + +rel_tolerance = 1.e-9 + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 1.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + + +file_type = rst +dt = 50.0 + + +file_type = hst +dt = 0.1 From c00e72cb206777c71c96932aa73650ba40fc5b53 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Jan 2023 11:12:17 -0600 Subject: [PATCH 016/219] Fixes to indexing vs. sizing errors, more testing groundwork --- kharma/prob/resize_restart.cpp | 49 +++++++++++++++++----------- pars/orszag_tang.par | 15 ++++++--- pars/regrid_orszag_tang.par | 59 ++++++++++++++++++++++++++++++++++ pars/resize_orszag_tang.par | 9 ++---- 4 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 pars/regrid_orszag_tang.par diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index e3584c51..6ad748f8 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -272,18 +272,6 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetInteger("parthenon/mesh", "nx3"), n1tot, n2tot, n3tot); } - - if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), - m::log(pin->GetReal("coordinates", "r_in"))) || - !close_to(pin->GetReal("parthenon/mesh", "x1max"), - m::log(pin->GetReal("coordinates", "r_out")))) { - printf("Mesh shape does not match!"); - printf("Rin %g vs %g, Rout %g vs %g", - m::exp(pin->GetReal("parthenon/mesh", "x1min")), - pin->GetReal("coordinates", "r_in"), - m::exp(pin->GetReal("parthenon/mesh", "x1max")), - pin->GetReal("coordinates", "r_out")); - } if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), startx[1]) || !close_to(pin->GetReal("parthenon/mesh", "x1max"), stopx[1]) || @@ -300,6 +288,22 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetReal("parthenon/mesh", "x3min"), startx[3], pin->GetReal("parthenon/mesh", "x3max"), stopx[3]); } + + if (is_spherical) { + // Check that the coordinate parameters r_{in,out} match the mesh + if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), + m::log(pin->GetReal("coordinates", "r_in"))) || + !close_to(pin->GetReal("parthenon/mesh", "x1max"), + m::log(pin->GetReal("coordinates", "r_out")))) { + printf("Mesh shape does not match!"); + printf("Rin %g vs %g, Rout %g vs %g", + m::exp(pin->GetReal("parthenon/mesh", "x1min")), + pin->GetReal("coordinates", "r_in"), + m::exp(pin->GetReal("parthenon/mesh", "x1max")), + pin->GetReal("coordinates", "r_out")); + } + } + } if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; @@ -319,6 +323,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) const auto& G = pmb->coords; // Total file size + // TODO separate nmprim to stop at 8 prims if we don't need e- hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; // Figure out the subset in global space corresponding to our memory cache @@ -351,14 +356,20 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) // Truncate the file read sizes so we don't overrun the file data hsize_t fstart[4] = {0, static_max(gks, 0), static_max(gjs, 0), static_max(gis, 0)}; - // TODO separate nmprim to stop at 8 prims if we don't need e- - hsize_t fstop[4] = {nfprim, static_min(gke, n3tot), static_min(gje, n2tot), static_min(gie, n1tot)}; - hsize_t fcount[4] = {fstop[0] - fstart[0], fstop[1] - fstart[1], fstop[2] - fstart[2], fstop[3] - fstart[3]}; + // Test gXe against last valid index, i.e. nXtot-1 + hsize_t fstop[4] = {nfprim-1, static_min(gke, n3tot-1), static_min(gje, n2tot-1), static_min(gie, n1tot-1)}; + // We add one here to get sizes from indices + hsize_t fcount[4] = {fstop[0] - fstart[0] + 1, + fstop[1] - fstart[1] + 1, + fstop[2] - fstart[2] + 1, + fstop[3] - fstart[3] + 1}; // If we overran an index on the left, we need to leave a blank row (i.e., start at 1 == true) to reflect this hsize_t mstart[4] = {0, (gks < 0), (gjs < 0), (gis < 0)}; // Total memory size is never truncated - hsize_t nmk = gke-gks, nmj = gje-gjs, nmi = gie-gis; + // This calculation produces XxYx2 arrays for 2D sims w/linear interp but that's fine + hsize_t nmk = gke-gks+1, nmj = gje-gjs+1, nmi = gie-gis+1; hsize_t mdims[4] = {nfprim, nmk, nmj, nmi}; + // TODO these should be const but hdf5_read_array yells about it, fix that // TODO should yell if any of these fired for nearest-neighbor // Allocate the array we'll need @@ -387,7 +398,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[1] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gke > n3tot && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { + if (gke > n3tot-1 && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { RESET_COUNTS // same X1/X2, but take only the globally FIRST rank in X3 fstart_tmp[1] = 0; @@ -403,7 +414,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[2] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gje > n2tot && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { + if (gje > n2tot-1 && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { RESET_COUNTS fstart_tmp[2] = 0; fcount_tmp[2] = 1; @@ -417,7 +428,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[3] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gie > n1tot && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { + if (gie > n1tot-1 && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { RESET_COUNTS fstart_tmp[3] = 0; fcount_tmp[3] = 1; diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index be4869d7..4d2fb46f 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -8,13 +8,13 @@ problem_id = orszag_tang refinement = none numlevel = 1 -nx1 = 768 +nx1 = 256 x1min = -3.141592653589793 x1max = 3.141592653589793 ix1_bc = periodic ox1_bc = periodic -nx2 = 768 +nx2 = 256 x2min = -3.141592653589793 x2max = 3.141592653589793 ix2_bc = periodic @@ -27,8 +27,8 @@ ix3_bc = periodic ox3_bc = periodic -nx1 = 768 -nx2 = 768 +nx1 = 256 +nx2 = 128 nx3 = 1 @@ -44,6 +44,9 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 + +type = imex + verbose = 0 flag_verbose = 0 @@ -58,3 +61,7 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 + + +file_type = rst +dt = 10.0 diff --git a/pars/regrid_orszag_tang.par b/pars/regrid_orszag_tang.par new file mode 100644 index 00000000..28a1e91c --- /dev/null +++ b/pars/regrid_orszag_tang.par @@ -0,0 +1,59 @@ +# Restart from an iharm3d snapshot file, resizing to specified mesh +# Note most parameters here will carry through to running after +# restarting, as iharm3d restart files do not specify much + + +problem_id = resize_restart + + +# Set by restart file + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 300000 +integrator = rk2 +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 + + +fname = orszag_tang.out2.00001.h5 +use_tf = false +use_dt = false +skip_b_cleanup = false +regrid_only = true + + +rel_tolerance = 1.e-9 + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 1.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + + +file_type = rst +dt = 50.0 + + +file_type = hst +dt = 0.1 diff --git a/pars/resize_orszag_tang.par b/pars/resize_orszag_tang.par index 92392107..95340ec6 100644 --- a/pars/resize_orszag_tang.par +++ b/pars/resize_orszag_tang.par @@ -29,15 +29,12 @@ ox3_bc = periodic nx1 = 512 -nx2 = 512 +nx2 = 256 nx3 = 1 -base = spherical_ks -transform = fmks -a = 0.9375 -hslope = 0.3 -r_out = 1000 +base = cartesian_minkowski +transform = none tlim = 300000 From 9814768c4dd084aff054e0805aefba8b0ccff033 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Jan 2023 17:03:05 -0600 Subject: [PATCH 017/219] Formal regrid/resize test. Doubles as a mild divB clean test. --- .gitlab-ci.yml | 6 ++ kharma/prob/hdf5_utils.cpp | 5 +- pars/orszag_tang.par | 11 ++- pars/regrid_orszag_tang.par | 59 ---------------- tests/clean_tests.sh | 2 +- tests/regrid/orszag_tang_with_restarts.par | 67 +++++++++++++++++++ tests/regrid/regrid_orszag_tang.par | 53 +++++++++++++++ {pars => tests/regrid}/resize_orszag_tang.par | 38 +++++------ tests/regrid/run.sh | 52 ++++++++++++++ 9 files changed, 203 insertions(+), 90 deletions(-) delete mode 100644 pars/regrid_orszag_tang.par create mode 100644 tests/regrid/orszag_tang_with_restarts.par create mode 100644 tests/regrid/regrid_orszag_tang.par rename {pars => tests/regrid}/resize_orszag_tang.par (56%) create mode 100755 tests/regrid/run.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ed762d4..6fea13ca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -114,3 +114,9 @@ reinit: script: - cd tests/reinit - ./run.sh + +regrid: + stage: tests + script: + - cd tests/regrid + - ./run.sh diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index 40d1d7ad..a183beb2 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -399,10 +399,9 @@ int hdf5_read_array(void *data, const char *name, size_t rank, fprintf(stderr,"Reading arr %s:\n", path); fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); - fprintf(stderr,"File read size: %llu %llu %llu %llu\n\n", fcount[0], fcount[1], fcount[2], fcount[3]); - + fprintf(stderr,"File read size: %llu %llu %llu %llu\n", fcount[0], fcount[1], fcount[2], fcount[3]); fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); - fprintf(stderr,"Memory start: %llu %llu %llu %llu\n", mstart[0], mstart[1], mstart[2], mstart[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index 4d2fb46f..79a71cd7 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -44,9 +44,6 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 - -type = imex - verbose = 0 flag_verbose = 0 @@ -62,6 +59,8 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 - -file_type = rst -dt = 10.0 +# This problem is generally much too short to need +# checkpointing. However, we have a test which uses it. +# +#file_type = rst +#dt = 10.0 diff --git a/pars/regrid_orszag_tang.par b/pars/regrid_orszag_tang.par deleted file mode 100644 index 28a1e91c..00000000 --- a/pars/regrid_orszag_tang.par +++ /dev/null @@ -1,59 +0,0 @@ -# Restart from an iharm3d snapshot file, resizing to specified mesh -# Note most parameters here will carry through to running after -# restarting, as iharm3d restart files do not specify much - - -problem_id = resize_restart - - -# Set by restart file - - -nx1 = 64 -nx2 = 64 -nx3 = 1 - - -base = cartesian_minkowski -transform = none - - -tlim = 300000 -integrator = rk2 -dt_min = 0.00001 - - -cfl = 0.9 -gamma = 1.666667 - - -fname = orszag_tang.out2.00001.h5 -use_tf = false -use_dt = false -skip_b_cleanup = false -regrid_only = true - - -rel_tolerance = 1.e-9 - - -disable_floors = true - - -verbose = 1 -flag_verbose = 2 -extra_checks = 1 - - -file_type = hdf5 -dt = 1.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag - - -file_type = rst -dt = 50.0 - - -file_type = hst -dt = 0.1 diff --git a/tests/clean_tests.sh b/tests/clean_tests.sh index e0ba4a67..62249051 100755 --- a/tests/clean_tests.sh +++ b/tests/clean_tests.sh @@ -2,4 +2,4 @@ # Cleans all temporary/gitignore files from tests TEST_DIR=$(dirname "$(readlink -f "$0")") -rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* +rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* diff --git a/tests/regrid/orszag_tang_with_restarts.par b/tests/regrid/orszag_tang_with_restarts.par new file mode 100644 index 00000000..c732e718 --- /dev/null +++ b/tests/regrid/orszag_tang_with_restarts.par @@ -0,0 +1,67 @@ +# Orszag-Tang Vortex problem: +# Generate current sheets on short timescales +# Adds a restart file output at 50 time units +# Also uses ImEx driver, so that the restart +# file contains all the primitive variables. +# Also omits history file + + +problem_id = orszag_tang + + +refinement = none +numlevel = 1 + +nx1 = 256 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 256 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 256 +nx2 = 128 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 100.0 +integrator = rk2 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +type = imex + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + + +file_type = hdf5 +dt = 1000.0 # Only output final dump +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon + + +file_type = rst +dt = 10.0 diff --git a/tests/regrid/regrid_orszag_tang.par b/tests/regrid/regrid_orszag_tang.par new file mode 100644 index 00000000..3ac4870a --- /dev/null +++ b/tests/regrid/regrid_orszag_tang.par @@ -0,0 +1,53 @@ +# Regrid an OT vortex, keeping all properties but the block size + + +problem_id = resize_restart + + +# Set by restart file + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 100 +integrator = rk2 + + +cfl = 0.9 + + +type = imex + + +fname = orszag_tang.out1.00005.h5 +use_tf = true +use_dt = false # TODO this is borked somehow +skip_b_cleanup = true +regrid_only = true + + +disable_floors = true + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + +# Have to compare last output file + +file_type = hdf5 +dt = 1000.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + +# Don't check the restart if the last dump matches +# +#file_type = rst +#dt = 1000.0 diff --git a/pars/resize_orszag_tang.par b/tests/regrid/resize_orszag_tang.par similarity index 56% rename from pars/resize_orszag_tang.par rename to tests/regrid/resize_orszag_tang.par index 95340ec6..467090f4 100644 --- a/pars/resize_orszag_tang.par +++ b/tests/regrid/resize_orszag_tang.par @@ -1,6 +1,4 @@ -# Restart from an iharm3d snapshot file, resizing to specified mesh -# Note most parameters here will carry through to running after -# restarting, as iharm3d restart files do not specify much +# Resize an OT vortex, keeping most properties problem_id = resize_restart @@ -37,41 +35,39 @@ base = cartesian_minkowski transform = none -tlim = 300000 +tlim = 100 integrator = rk2 -dt_min = 0.00001 cfl = 0.9 -gamma = 1.666667 + + +type = imex -fname = orszag_tang.out2.00001.h5 +fname = orszag_tang.out1.00009.h5 use_tf = false use_dt = false skip_b_cleanup = false -rel_tolerance = 1.e-9 +rel_tolerance = 1.e-11 disable_floors = true -verbose = 1 -flag_verbose = 2 -extra_checks = 1 +verbose = 0 +flag_verbose = 0 +extra_checks = 0 - -file_type = hdf5 -dt = 1.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag +# +#file_type = hdf5 +#dt = 1000.0 +#single_precision_output = true +#variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag +# We only need to check the last restart file, specifically divB file_type = rst -dt = 50.0 - - -file_type = hst -dt = 0.1 +dt = 1000.0 diff --git a/tests/regrid/run.sh b/tests/regrid/run.sh new file mode 100755 index 00000000..4748290d --- /dev/null +++ b/tests/regrid/run.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Bash script testing a fresh Orszag-Tang vortex vs a version +# re-gridded to 64^2 tiles in the middle of the run, +# and then a version resized to twice the resolution + +# TODO the first comparison should really be binary-identical + +exit_code=0 + +# Set paths +KHARMADIR=../.. + +$KHARMADIR/run.sh -i ./orszag_tang_with_restarts.par >log_orig.txt 2>&1 + +mv orszag_tang.out0.final.phdf orszag_tang.out0.final.orig.phdf + +sleep 1 + +pyharm-convert --to_restart orszag_tang.out1.00005.rhdf orszag_tang.out1.00009.rhdf + +sleep 1 + +$KHARMADIR/run.sh -i ./regrid_orszag_tang.par >log_regrid.txt 2>&1 + +mv resize_restart.out0.final.phdf resize_restart.out0.final.regrid.phdf + +# compare.py allows for small (5e-10) difference +check_code=0 +pyharm-diff orszag_tang.out0.final.orig.phdf resize_restart.out0.final.regrid.phdf -o compare_regrid --rel_tol=0.002 || check_code=$? +if [[ $check_code != 0 ]]; then + echo Regrid test FAIL: $check_code + exit_code=1 +else + echo Regrid test success +fi + +# Finally, test that we can sanely resize the dump, too +# This won't output .phdf files, only restarts (.rhdf) +$KHARMADIR/run.sh -i ./resize_orszag_tang.par >log_resize.txt 2>&1 + +# Check the final .rhdf file for sanity (i.e., divB small) +check_code=0 +pyharm-check-basics resize_restart.out1.final.rhdf || check_code=$? +if [[ $check_code != 0 ]]; then + echo Resize test FAIL: $check_code + exit_code=1 +else + echo Resize test success +fi + +exit $exit_code From d12c805bdc23825d86e3c77304ba54a44443793c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 5 Jan 2023 09:58:27 -0700 Subject: [PATCH 018/219] Doc touch-ups --- kharma/prob/interpolation.hpp | 45 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index ed6a0272..9827bf71 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -36,36 +36,22 @@ #include "decs.hpp" /** - * Routines for interpolating on a grid, with values given in a flattened array. - * Mostly used in resize_restart.cpp, which must interpolate from a grid corresponding - * to an old simulation, read from a file. + * Routines for interpolating on a grid, using values given in a flattened array. + * Mostly used in resize_restart.cpp, which must interpolate from old simulation + * data. * - * Note that resizing a file nearly always requires fixing the resulting magentic field - * divergence -- see b_cleanup/ for details. + * Note that resizing or resampling of magnetic fields usually requires + * fixing a resulting divergence -- see b_cleanup/ for details. */ - namespace Interpolation { /** - * Finds the closest grid zone which lies to the left of the given point in X1,X2, and X3, - * along with the distance 'del' from that center to X in each coordinate, - * for interpolation purposes. - * - * Example (from ipole, ) + * Finds the closest grid zone index (i,j,k) with a center left of the given point. + * Additionally returns the point's proportional distance measured from the left + * zone center to the right (e.g., to (i+1, j, k) in X1) * - * 0 0.5 1 - * [ | ] - * A B C DE F - * - * startx = 0. - * dx = 0.5 - * - * A -> (-1, 0.5) - * B -> ( 0, 0.0) - * C -> ( 0, 0.5) - * D -> ( 0, 0.9) - * E -> ( 1, 0.0) - * F -> ( 1, 0.5) + * This proportion is useful in interpolation, since linear interpolation corresponds to + * del*var[i+1] + (1. - del)*var[i] */ KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], const GReal startx[GR_DIM], @@ -87,7 +73,8 @@ KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], } /** - * Translates a point X in native coordinates to a grid zone. + * Return the grid zone index (i,j,k) corresponding which contains the point X. + * Note this is different from the above! */ KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], const GReal startx[GR_DIM], @@ -101,16 +88,16 @@ KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], k = (int) ((X[3] - startx[3]) / dx[3] + 1000) - 1000; } +// For using the ipole routines in a recognizable form on a 1D array +#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) + /** - * Dumb linear interpolation: no special cases for boundaries + * Dumb linear interpolation: no special cases for boundaries. * Takes indices i,j,k and a block size n1, n2, n3, * as well as a flat array var. * * TODO version(s) with View(s) for real device-side operation */ -// For using the ipole routines in a recognizable form on a 1D array -#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) - KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, const int& n1, const int& n2, const int& n3, const double del[4], const double *var) From 239a913338eb648a16e9dae6c874ac7f9655d32f Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 19 Jan 2023 11:24:46 -0600 Subject: [PATCH 019/219] Updates: 1. Capability to average over bad/unconverged zones in implicit solver (similar to fixup for UtoP) 2. Some updates from #02cf70b8509d77fad7d5a2d386f9dcf2cf711779 and #9ffbfe1493ddbd38cbd5464cd83a8d856a469540 3. PRINTTILE functionality in types.hpp for debugging purposes. Allows printing a 6x6 tile about selected zone 4. Reduced default uflr_geom 5. Modified default floors in sane_emhd.par NOTE: The code compiles. The avergaing functionality is being tested. --- kharma/CMakeLists.txt | 14 +- kharma/floors/floors.hpp | 4 +- kharma/imex_driver.cpp | 16 +- kharma/implicit/fixup.cpp | 143 +++++++++++++++ kharma/implicit/implicit.cpp | 341 ++++++++++++++++++++++------------- kharma/implicit/implicit.hpp | 14 ++ kharma/types.hpp | 90 +++++++-- pars/sane_emhd.par | 12 +- pars/sane_imex.par | 9 +- 9 files changed, 486 insertions(+), 157 deletions(-) create mode 100644 kharma/implicit/fixup.cpp diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index 8d8c6490..787ade3e 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -66,11 +66,6 @@ if(FUSE_FLUX_KERNELS) else() target_compile_definitions(${EXE_NAME} PUBLIC FUSE_FLUX_KERNELS=0) endif() -if(FUSE_EMF_KERNELS) - target_compile_definitions(${EXE_NAME} PUBLIC FUSE_EMF_KERNELS=1) -else() - target_compile_definitions(${EXE_NAME} PUBLIC FUSE_EMF_KERNELS=0) -endif() if(FUSE_FLOOR_KERNELS) target_compile_definitions(${EXE_NAME} PUBLIC FUSE_FLOOR_KERNELS=1) else() @@ -88,7 +83,14 @@ if(KHARMA_TRACE) else() target_compile_definitions(${EXE_NAME} PUBLIC TRACE=0) endif() - +option(KHARMA_DISABLE_IMPLICIT "Compile the implicit solver, requiring kokkos-kernels. Default true" OFF) +option(KHARMA_TRACE "Compile with tracing: print entry and exit of important functions" OFF) +if(KHARMA_DISABLE_IMPLICIT) + message("Compiling without the implicit solver. Extended GRMHD will be disabled!") + target_compile_definitions(${EXE_NAME} PUBLIC ENABLE_IMPLICIT=0) +else() + target_compile_definitions(${EXE_NAME} PUBLIC ENABLE_IMPLICIT=1) +endif() # FLAGS if(CMAKE_BUILD_TYPE) if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index 6719875c..c8e63823 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -237,7 +237,7 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa } else { // Original floors from iharm2d rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + uflr_geom = floors.u_min_geom * m::pow(r, -1.5); //rhoscal/r as in iharm2d } } else { rhoflr_geom = floors.rho_min_geom; @@ -449,7 +449,7 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, co } else { // Original floors from iharm2d rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + uflr_geom = floors.u_min_geom * m::pow(r, -1.5); //rhoscal/r as in iharm2d } } else { rhoflr_geom = floors.rho_min_geom; diff --git a/kharma/imex_driver.cpp b/kharma/imex_driver.cpp index 70fecdd2..4e7eace9 100644 --- a/kharma/imex_driver.cpp +++ b/kharma/imex_driver.cpp @@ -265,17 +265,17 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) auto t_copy_linesearch = t_none; auto t_implicit = t_none; if (linesearch) { - auto t_copy_linesearch = tl.AddTask(t_guess_ready, Update::WeightedSumData>, + t_copy_linesearch = tl.AddTask(t_guess_ready, Update::WeightedSumData>, std::vector({Metadata::Derived}), md_solver.get(), md_solver.get(), 1.0, 0.0, md_linesearch.get()); // Time-step implicit variables by root-finding the residual // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta - auto t_implicit = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + t_implicit = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); } else { - auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), md_flux_src.get(), md_linesearch.get(), md_solver.get(), dt_this); } @@ -337,10 +337,16 @@ TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) t_fix_derived = tl.AddTask(t_fix_derived, GRMHD::FixUtoP, mbd_sub_step_final.get()); } - auto t_set_bc = tl.AddTask(t_fix_derived, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + // Fix unconverged (bad) zones in the solver + auto t_fix_solve = t_none; + if (pkgs.at("GRMHD")->Param("implicit")) { + t_fix_solve = tl.AddTask(t_fix_derived, Implicit::FixSolve, mbd_sub_step_final.get()); + } + + auto t_set_bc = tl.AddTask(t_fix_solve, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); // Electron heating goes where it does in HARMDriver, for the same reasons - auto t_heat_electrons = t_fix_derived; + auto t_heat_electrons = t_set_bc; if (use_electrons) { t_heat_electrons = tl.AddTask(t_fix_derived, Electrons::ApplyElectronHeating, mbd_sub_step_init.get(), mbd_sub_step_final.get()); diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp new file mode 100644 index 00000000..c6009065 --- /dev/null +++ b/kharma/implicit/fixup.cpp @@ -0,0 +1,143 @@ +/* + * File: fixup.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "implicit.hpp" + +#include "floors.hpp" +#include "flux_functions.hpp" + +TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { + + Flag(mbd, "Fixing implicit solver failures"); + // Get MeshBlock pointer and obtain flag for primitives + auto pmb = mbd->GetBlockPointer(); + MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); + + // Get number of implicit variables + PackIndexMap implicit_prims_map; + auto implicit_vars = Implicit::get_ordered_names(mbd, isPrimitive, true); + auto& P = mbd->PackVariables({implicit_vars}, implicit_prims_map); + const int nfvar = P.GetDim(4); + + // Get grid object + const auto& G = pmb->coords; + + GridScalar solve_fail = mbd->Get("solve_fail").data; + GridScalar fflag = mbd->Get("fflag").data; + + const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + const int verbose = pars.Get("verbose"); + const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); + + // Boundaries were synced just before the call to this function (cf. imex_driver.cpp). + // Which means unsuccessful values were copied to ghost zones. Therefore, we need to loop over entire domain. + const IndexRange ib = mbd->GetBoundsI(IndexDomain::entire); + const IndexRange jb = mbd->GetBoundsJ(IndexDomain::entire); + const IndexRange kb = mbd->GetBoundsK(IndexDomain::entire); + + const IndexRange ib_b = mbd->GetBoundsI(IndexDomain::interior); + const IndexRange jb_b = mbd->GetBoundsJ(IndexDomain::interior); + const IndexRange kb_b = mbd->GetBoundsK(IndexDomain::interior); + + pmb->par_for("fix_solver_failures", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA_3D { + // Fix only bad zones + if ((solve_fail(k, j, i)) == SolverStatus::fail) { + double wsum = 0., wsum_x = 0.; + double sum[nfvar] = {0.}, sum_x[nfvar] = {0.}; + // For all neighboring cells... + for (int n = -1; n <= 1; n++) { + for (int m = -1; m <= 1; m++) { + for (int l = -1; l <= 1; l++) { + int ii = i + l, jj = j + m, kk = k + n; + // If we haven't overstepped array bounds... + if (inside(kk, jj, ii, kb, jb, ib)) { + // Weight by distance + double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); + + // Count only the good cells, if we can + if ((solve_fail(kk, jj, ii)) != SolverStatus::fail) { + // Weight by distance. Note interpolated "fixed" cells stay flagged + wsum += w; + FLOOP sum[ip] += w * P(ip, kk, jj, ii); + } + // Just in case, keep a sum of even the bad ones + wsum_x += w; + FLOOP sum_x[ip] += w * P(ip, kk, jj, ii); + } + } + } + } + + if(wsum < 1.e-10) { + // TODO probably should crash here. +#ifndef KOKKOS_ENABLE_SYCL + if (verbose >= 1 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... + printf("No neighbors were available at %d %d %d!\n", i, j, k); +#endif + } else { + FLOOP P(ip, k, j, i) = sum[ip]/wsum; + } + } + } + ); + + // Since floors were applied earlier, we assume the zones obtained by averaging the neighbors also respect the floors. + // Compute new conserved variables + PackIndexMap prims_map, cons_map; + auto& P_all = mbd->PackVariables({isPrimitive}, prims_map); + auto& U_all = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + // Get new sizes + const int nvar = P_all.GetDim(4); + + // Need emhd_params object + EMHD_parameters emhd_params; + if (pmb->packages.AllPackages().count("EMHD")) { + const auto& pars = pmb->packages.Get("EMHD")->AllParams(); + emhd_params = pars.Get("emhd_params"); + } + + pmb->par_for("fix_solver_failures_PtoU", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA_3D { + if (( solve_fail(k, j, i)) == SolverStatus::fail) + Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); + } + ); + + Flag(mbd, "Fixed solver failures"); + return TaskStatus::complete; + +} diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 985e3f57..2ae8d755 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -39,17 +39,9 @@ #include "grmhd_functions.hpp" #include "pack.hpp" -// Implicit nonlinear solve requires several linear solves per-zone -// Use Kokkos-kernels QR decomposition & triangular solve, they're fast. -#include -#include -#include -#include -namespace Implicit +std::vector Implicit::get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit) { - -std::vector get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit=false) { auto pmb0 = rc->GetBlockPointer(); MetadataFlag isImplicit = pmb0->packages.Get("Implicit")->Param("ImplicitFlag"); MetadataFlag isExplicit = pmb0->packages.Get("Implicit")->Param("ExplicitFlag"); @@ -71,7 +63,7 @@ std::vector get_ordered_names(MeshBlockData *rc, const Metada return out; } -std::shared_ptr Initialize(ParameterInput *pin) +std::shared_ptr Implicit::Initialize(ParameterInput *pin) { Flag("Initializing Implicit Package"); auto pkg = std::make_shared("Implicit"); @@ -116,6 +108,7 @@ std::shared_ptr Initialize(ParameterInput *pin) pkg->AddField("solve_norm", m_real); // Integer field that saves where the solver fails (rho + drho < 0 || u + du < 0) // Metadata m_int = Metadata({Metadata::Integer, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariabl // TODO: Find a way to save residuals based on a runtime parameter. We don't want to unnecessarily allocate @@ -167,7 +160,16 @@ std::shared_ptr Initialize(ParameterInput *pin) return pkg; } -TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, +#if ENABLE_IMPLICIT + +// Implicit nonlinear solve requires several linear solves per-zone +// Use Kokkos-kernels QR decomposition & triangular solve, they're fast. +#include +#include +#include +#include + +TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { Flag(md_full_step_init, "Implicit Iteration start, full step"); @@ -349,7 +351,15 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i dU_implicit_s(ip, i) = 0.; solve_norm_s(i) = 0.; - solve_fail_s(i) = 0; + if (iter == 1) { + // New beginnings + solve_fail_s(i) = SolverStatus::converged; + } + else { + // Need this to check if the zone had failed in any of the previous iterations. + // If so, we don't attempt to update it again in the implicit solver. + solve_fail_s(i) = solve_fail_all(b, 0, k, j, i); + } } ); } @@ -393,125 +403,159 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i auto solve_norm = Kokkos::subview(solve_norm_s, i); auto solve_fail = Kokkos::subview(solve_fail_s, i); - if (m_p.Q >= 0) { - EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, emhd_params_sub_step_init, - dU_implicit(m_u.Q), dU_implicit(m_u.DP)); - } + // Perform the solve only if it hadn't failed in any of the previous iterations. + if (solve_fail() != SolverStatus::fail) { + // Now that we know that it isn't a bad zone, reset solve_fail for this iteration + solve_fail() = SolverStatus::converged; - // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver - // since we do a copy in imex_driver just before, but it is required for the subsequent - // iterations of the solver. - PLOOP P_linesearch(ip) = P_solver(ip); - Real lambda = linesearch_lambda; - - // Jacobian calculation - // Requires calculating the residual anyway, so we grab it here - calc_jacobian(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, - flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_solver, - emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); - // Solve against the negative residual - FLOOP delta_prim(ip) = -residual(ip); - - // if (am_rank0 && b == 0 && i == 10 && j == 10 && k == kb.s) { - // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", - // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); - // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", - // m_u.RHO, m_u.UU, m_u.U1, m_u.B1, m_u.Q, m_u.DP); - // printf("P_solver: "); PLOOP printf("%6.5e ", P_solver(ip)); printf("\n"); - // printf("Pi: "); PLOOP printf("%6.5e ", P_full_step_init(ip)); printf("\n"); - // printf("Ui: "); PLOOP printf("%6.5e ", U_full_step_init(ip)); printf("\n"); - // printf("Ps: "); PLOOP printf("%6.5e ", P_sub_step_init(ip)); printf("\n"); - // printf("Us: "); PLOOP printf("%6.5e ", U_sub_step_init(ip)); printf("\n"); - // printf("dUdt: "); PLOOP printf("%6.5e ", dU_implicit(ip)); printf("\n"); - // printf("Initial Jacobian:\n"); for (int jp=0; jp::invoke(jacobian, trans, work); - KokkosBatched::SerialApplyQ - ::invoke(jacobian, trans, delta_prim, work); - } else { - KokkosBatched::SerialLU::invoke(jacobian, tiny); - } - KokkosBatched::SerialTrsv - ::invoke(alpha, jacobian, delta_prim); - - // Check for positive definite values of density and internal energy. - // Break from solve if manual backtracking is not sufficient. - // The primitives will be averaged over good neighbors. - if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { - solve_fail() = 1; - lambda = 0.1; - } - if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { - solve_fail() = 2; - // break; // Doesn't break from the inner par_for. - // Let it continue for now, but we'll average over the zone later - } - - // Linesearch - if (linesearch) { - solve_norm() = 0; - FLOOP solve_norm() += residual(ip) * residual(ip); - solve_norm() = m::sqrt(solve_norm()); - - Real f0 = 0.5 * solve_norm(); - Real fprime0 = -2. * f0; - - for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { - // Take step - FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); + if (m_p.Q >= 0) { + EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, + emhd_params_sub_step_init, dU_implicit(m_u.Q), dU_implicit(m_u.DP)); + } - // Compute solve_norm of the residual (loss function) - calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, - dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, - k, j, i, gam, dt, residual); + // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver + // since we do a copy in imex_driver just before, but it is required for the subsequent + // iterations of the solver. + PLOOP P_linesearch(ip) = P_solver(ip); + Real lambda = linesearch_lambda; + + // Jacobian calculation + // Requires calculating the residual anyway, so we grab it here + calc_jacobian(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, + flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_solver, + emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); + // Solve against the negative residual + FLOOP delta_prim(ip) = -residual(ip); + + #if TRACE + if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { + std::cerr << "Variable ordering: rho " << int(m_p.RHO) << " uu " << int(m_p.UU) << " U1 " << int(m_p.U1) + << " B1 " << int(m_p.B1) << " q " << int(m_p.Q) << " dP " << int(m_p.DP) << std::endl; + std::cerr << "Variable ordering: rho " << int(m_u.RHO) << " uu " << int(m_u.UU) << " U1 " << int(m_u.U1) + << " B1 " << int(m_u.B1) << " q " << int(m_u.Q) << " dP " << int(m_u.DP) << std::endl; + std::cerr << "P_solver: "; + PLOOP {std::cerr << P_solver(ip) << " ";} std::cerr << std::endl; + std::cerr << "Pi: "; + PLOOP {std::cerr << P_full_step_init(ip) << " ";} std::cerr << std::endl; + std::cerr << "Ui: "; + PLOOP {std::cerr << U_full_step_init(ip) << " ";} std::cerr << std::endl; + std::cerr << "Ps: "; + PLOOP {std::cerr << P_sub_step_init(ip) << " ";} std::cerr << std::endl; + std::cerr << "Us: "; + PLOOP {std::cerr << U_sub_step_init(ip) << " ";} std::cerr << std::endl; + std::cerr << "dUdt: "; + PLOOP {std::cerr << dU_implicit(ip) << " ";} std::cerr << std::endl; + std::cerr << "Initial Jacobian:" << std::endl; + for (int jp=0; jp::invoke(jacobian, trans, work); + KokkosBatched::SerialApplyQ + ::invoke(jacobian, trans, delta_prim, work); + } else { + KokkosBatched::SerialLU::invoke(jacobian, tiny); + } + KokkosBatched::SerialTrsv + ::invoke(alpha, jacobian, delta_prim); + + #if TRACE + if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { + std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; + std::cerr<< std::endl; + } + #endif + + // Check for positive definite values of density and internal energy. + // Ignore zone if manual backtracking is not sufficient. + // The primitives will be averaged over good neighbors. + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = SolverStatus::backtrack; + lambda = 0.1; + } + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = SolverStatus::fail; + // break; // Doesn't break from the inner par_for. + // Instead we set all fluid primitives to value at beginning of substep. + // We average over neighboring good zones later. + FLOOP P_solver(ip) = P_sub_step_init(ip); + } + // If the solver failed, we don't want to update the implicit primitives for those zones + if (solve_fail() != SolverStatus::fail) + { + // Linesearch + if (linesearch) { + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); + + Real f0 = 0.5 * solve_norm(); + Real fprime0 = -2. * f0; + + for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { + // Take step + FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); + + // Compute solve_norm of the residual (loss function) + calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, + dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, + k, j, i, gam, dt, residual); + + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); + Real f1 = 0.5 * solve_norm(); + + // Compute new step length + int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); + Real denom = (f1 - f0 - (fprime0 * lambda)) * condition + (1 - condition); + Real lambda_new = -fprime0 * lambda * lambda / denom / 2.; + lambda = lambda * (1 - condition) + (condition * lambda_new); + + // Check if new solution has converged within required tolerance + if (condition == 0) break; + } + } + + // Update the guess + FLOOP P_solver(ip) += lambda * delta_prim(ip); + + calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + + // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { + // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", + // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); + // printf("Final residual: "); PLOOP printf("%6.5e ", residual(ip)); printf("\n"); + // printf("Final delta_prim: "); PLOOP printf("%6.5e ", delta_prim(ip)); printf("\n"); + // printf("Final P_solver: "); PLOOP printf("%6.5e ", P_solver(ip)); printf("\n"); + // } + + // Store for maximum/output + // I would be tempted to store the whole residual, but it's of variable size solve_norm() = 0; FLOOP solve_norm() += residual(ip) * residual(ip); - solve_norm() = m::sqrt(solve_norm()); - Real f1 = 0.5 * solve_norm(); - - // Compute new step length - int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); - Real denom = (f1 - f0 - (fprime0 * lambda)) * condition + (1 - condition); - Real lambda_new = -fprime0 * lambda * lambda / denom / 2.; - lambda = lambda * (1 - condition) + (condition * lambda_new); + solve_norm() = m::sqrt(solve_norm()); // TODO faster to scratch cache & copy? - // Check if new solution has converged within required tolerance - if (condition == 0) break; + // Did we converge to required tolerance? If not, update solve_fail accordingly + if (solve_norm() > rootfind_tol) { + solve_fail() += SolverStatus::beyond_tol; + } } } - - // Update the guess - FLOOP P_solver(ip) += lambda * delta_prim(ip); - - calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); - - // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { - // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", - // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); - // printf("Final residual: "); PLOOP printf("%6.5e ", residual(ip)); printf("\n"); - // printf("Final delta_prim: "); PLOOP printf("%6.5e ", delta_prim(ip)); printf("\n"); - // printf("Final P_solver: "); PLOOP printf("%6.5e ", P_solver(ip)); printf("\n"); - // } - - // Store for maximum/output - // I would be tempted to store the whole residual, but it's of variable size - solve_norm() = 0; - FLOOP solve_norm() += residual(ip) * residual(ip); - solve_norm() = m::sqrt(solve_norm()); // TODO faster to scratch cache & copy? } ); member.team_barrier(); - // Copy out (the good bits of) P_solver to the existing array + // Copy out P_solver to the existing array. + // We'll copy even the values for the failed zones because it doesn't really matter, it'll be averaged over later. // And copy any other diagnostics that are relevant to analyze the solver's performance FLOOP { parthenon::par_for_inner(member, ib.s, ib.e, @@ -535,18 +579,34 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i // If we need to print or exit on the max norm... if (iter >= iter_min || verbose >= 1) { // Take the maximum L2 norm on this rank - Reduce max_norm; + static AllReduce max_norm; Kokkos::Max norm_max(max_norm.val); pmb_sub_step_init->par_reduce("max_norm", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_MESH_3D_REDUCE { if (solve_norm_all(b, 0, k, j, i) > local_result) local_result = solve_norm_all(b, 0, k, j, i); } , norm_max); - // Then MPI reduce it - max_norm.StartReduce(0, MPI_MAX); + // Then MPI reduce AllReduce to copy the global max to every rank + max_norm.StartReduce(MPI_MAX); while (max_norm.CheckReduce() == TaskStatus::incomplete); if (verbose >= 1 && MPIRank0()) printf("Iteration %d max L2 norm: %g\n", iter, max_norm.val); - // Break if it's less than the total tolerance we set. TODO per-zone version of this? + + // Count total number of solver fails + int nfails = 0; + Kokkos::Sum sum_reducer(nfails); + pmb_sub_step_init->par_reduce("count_solver_fails", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + if (solve_fail_all(b, 0, k, j, i) == SolverStatus::fail) ++local_result; + } + , sum_reducer); + // Then MPI reduce AllReduce to copy the global max to every rank + AllReduce nfails_tot; + nfails_tot.val = nfails; + nfails_tot.StartReduce(MPI_SUM); + while (nfails_tot.CheckReduce() == TaskStatus::incomplete); + if (verbose >= 1 && MPIRank0()) printf("Number of failed zones: %d\n", nfails_tot.val); + + // Break if max_norm is less than the total tolerance we set. TODO per-zone version of this? if (iter >= iter_min && max_norm.val < rootfind_tol) break; } } @@ -557,4 +617,37 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i } -} // namespace Implicit +#else + +TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, + MeshData *md_linesearch, MeshData *md_solver, const Real& dt) +{ + Flag("Dummy implicit solve"); + auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); + + MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); + auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions + + // Get number of variables + auto ordered_cons = Implicit::get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); + PackIndexMap cons_map; + auto& U_full_step_init_all = md_full_step_init->PackVariables(ordered_cons, cons_map); + const int nvar = U_full_step_init_all.GetDim(4); + + // Get number of implicit variables + auto implicit_vars = Implicit::get_ordered_names(mbd_full_step_init.get(), isPrimitive, true); + PackIndexMap implicit_prims_map; + auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); + const int nfvar = P_full_step_init_implicit.GetDim(4); + + // RETURN if there aren't any implicit variables to evolve + //std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; + if (nfvar == 0) { + return TaskStatus::complete; + } else { + throw std::runtime_error("Cannot evolve variables implicitly: KHARMA was compiled without implicit solver!"); + } + Flag("End dummy implicit solve"); +} + +#endif diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index 40cfb307..bd72410f 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -71,6 +71,20 @@ std::shared_ptr Initialize(ParameterInput *pin); TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt); +/** + * Get the names of all variables matching 'flag' in a deterministic order, placing implicitly-evolved variables first. + */ +std::vector get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit=false); + + +/** + * @brief Fix bad zones that the implicit solver couldn't integrate. Similar to GRMHD::FixUtoP + * + * @param mbd relevant fluid state + * @return TaskStatus + */ +TaskStatus FixSolve(MeshBlockData *mbd); + /** * Calculate the residual generated by the trial primitives P_test * diff --git a/kharma/types.hpp b/kharma/types.hpp index 64224e12..2ce673f8 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -63,6 +63,15 @@ enum ReconstructionType{donor_cell=0, linear_mc, linear_vl, ppm, mp5, weno5, wen // Only thrown from function in U_to_P.hpp, see that file for meanings enum InversionStatus{success=0, neg_input, max_iter, bad_ut, bad_gamma, neg_rho, neg_u, neg_rhou}; +// Denote implicit solver failures (solve_fail). +// Thrown from Implicit::Step +// Status values: +// `converged`: solver converged to prescribed tolerance +// `fail`: manual backtracking wasn't good enough. FixSolve will be called +// `beyond_tol`: solver didn't converge to prescribed tolerance but didn't fail +// `backtrack`: step length of 1 gave negative rho/uu, but manual backtracking (0.1) sufficed +enum SolverStatus{converged=0, fail, beyond_tol, backtrack}; + // Struct for derived 4-vectors at a point, usually calculated and needed together typedef struct { Real ucon[GR_DIM]; @@ -182,6 +191,10 @@ inline bool IsDomainBound(MeshBlock *pmb, BoundaryFace face) #if TRACE #define PRINTCORNERS 0 #define PRINTZONE 1 +#define PRINTTILE 1 +#define iPRINT 7 +#define jPRINT 111 +#define kPRINT 0 inline void PrintCorner(MeshBlockData *rc) { auto rhop = rc->Get("prims.rho").data.GetHostMirrorAndCopy(); @@ -232,18 +245,69 @@ inline void PrintZone(MeshBlockData *rc) auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); - std::cerr << "RHO: " << rhop(0,108,63) - << " UU: " << up(0,108,63) - << " U: " << uvecp(0,0,108,63) << " " << uvecp(1,0,108,63)<< " " << uvecp(2,0,108,63) - << " B: " << Bp(0,0,108,63) << " " << Bp(1,0,108,63) << " " << Bp(2,0,108,63) - << " q: " << q(0,108,63) - << " dP: " << dP(0,108,63) << std::endl; - std::cerr << "RHO: " << rhoU(0,108,63) - << " UU: " << uU(0,108,63) - << " U: " << uvecU(0,0,108,63) << " " << uvecU(1,0,108,63)<< " " << uvecU(2,0,108,63) - << " B: " << BU(0,0,108,63) << " " << BU(1,0,108,63) << " " << BU(2,0,108,63) - << " q: " << qU(0,108,63) - << " dP: " << dPU(0,108,63) << std::endl; + std::cerr << "(PRIM) RHO: " << rhop(kPRINT,jPRINT,iPRINT) + << " UU: " << up(kPRINT,jPRINT,iPRINT) + << " U: " << uvecp(0,kPRINT,jPRINT,iPRINT) << " " << uvecp(1,kPRINT,jPRINT,iPRINT)<< " " << uvecp(2,kPRINT,jPRINT,iPRINT) + << " B: " << Bp(0,kPRINT,jPRINT,iPRINT) << " " << Bp(1,kPRINT,jPRINT,iPRINT) << " " << Bp(2,kPRINT,jPRINT,iPRINT) + << " q: " << q(kPRINT,jPRINT,iPRINT) + << " dP: " << dP(kPRINT,jPRINT,iPRINT) << std::endl; + std::cerr << "(CONS) RHO: " << rhoU(kPRINT,jPRINT,iPRINT) + << " UU: " << uU(kPRINT,jPRINT,iPRINT) + << " U: " << uvecU(0,kPRINT,jPRINT,iPRINT) << " " << uvecU(1,kPRINT,jPRINT,iPRINT)<< " " << uvecU(2,kPRINT,jPRINT,iPRINT) + << " B: " << BU(0,kPRINT,jPRINT,iPRINT) << " " << BU(1,kPRINT,jPRINT,iPRINT) << " " << BU(2,kPRINT,jPRINT,iPRINT) + << " q: " << qU(kPRINT,jPRINT,iPRINT) + << " dP: " << dPU(kPRINT,jPRINT,iPRINT) << std::endl; +} + +inline void PrintTile(MeshBlockData *rc) +{ + auto rhop = rc->Get("prims.rho").data.GetHostMirrorAndCopy(); + auto up = rc->Get("prims.u").data.GetHostMirrorAndCopy(); + auto uvecp = rc->Get("prims.uvec").data.GetHostMirrorAndCopy(); + auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); + auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); + auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); + + auto rhoU = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); + auto uU = rc->Get("cons.u").data.GetHostMirrorAndCopy(); + auto uvecU = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); + auto BU = rc->Get("cons.B").data.GetHostMirrorAndCopy(); + auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); + auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); + + const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); + const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); + const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); + std::cerr << "q(cons):"; + for (int j=jPRINT-3; j *rc, std::string label) std::cerr << label << std::endl; if(PRINTCORNERS) PrintCorner(rc); if(PRINTZONE) PrintZone(rc); + if(PRINTTILE) PrintTile(rc); } } @@ -268,6 +333,7 @@ inline void Flag(MeshData *md, std::string label) auto rc = md->GetBlockData(0).get(); if(PRINTCORNERS) PrintCorner(rc); if(PRINTZONE) PrintZone(rc); + if(PRINTTILE) PrintTile(rc); } } } diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par index df41df12..dfb2cd6a 100644 --- a/pars/sane_emhd.par +++ b/pars/sane_emhd.par @@ -76,10 +76,12 @@ u_jitter = 0.04 frame = drift -rho_min_geom = 1e-6 -u_min_geom = 1e-8 +rho_min_geom = 1e-3 +u_min_geom = 1e-5 bsq_over_rho_max = 100 -u_over_rho_max = 2 +bsq_over_u_max = 100 +u_over_rho_max = 100 +gamma_max = 10 enable_emhd_limits = true @@ -97,11 +99,11 @@ Tp = 10 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, q, dP, jcon, fflag, pflag, solve_norm, solve_fail, eflag +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, jcon, fflag, pflag, solve_norm, solve_fail, eflag file_type = rst -dt = 100.0 +dt = 5.0 file_type = hst diff --git a/pars/sane_imex.par b/pars/sane_imex.par index 80af3404..1d32c265 100644 --- a/pars/sane_imex.par +++ b/pars/sane_imex.par @@ -67,9 +67,12 @@ u_jitter = 0.04 frame = drift -rho_min_geom = 1e-6 -u_min_geom = 1e-8 +rho_min_geom = 1e-3 +u_min_geom = 1e-5 bsq_over_rho_max = 100 +bsq_over_u_max = 100 +u_over_rho_max = 100 +gamma_max = 10 u_over_rho_max = 2 @@ -91,7 +94,7 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag, solve_n file_type = rst -dt = 100.0 +dt = 5.0 file_type = hst From df70d36f5901bb8c058fb498c836b212d1e1fbb8 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Wed, 25 Jan 2023 13:24:06 -0600 Subject: [PATCH 020/219] Correctly submit argument to apply EMHD instability limits, save EMHD input parameters to '\Params' group, and some TRACE-enabled output in `apply_instability_limits`. --- kharma/emhd/emhd.cpp | 15 +++++++-------- kharma/floors/floors.cpp | 5 +++-- kharma/floors/floors.hpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 0cb04b02..3bd15ea4 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -51,32 +51,31 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack auto pkg = std::make_shared("EMHD"); Params ¶ms = pkg->AllParams(); - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - // EMHD Problem/Closure parameters // GRIM uses a callback to a problem-specific implementation which sets these // We share implementations in one function, controlled by these parameters // These are always necessary for performing EGRMHD. bool higher_order_terms = pin->GetOrAddBoolean("emhd", "higher_order_terms", false); + params.Add("higher_order_terms", higher_order_terms); std::string closure_type = pin->GetOrAddString("emhd", "closure_type", "torus"); + params.Add("closure_type", closure_type); // Should the EMHD sector feedback onto the ideal MHD variables? The default is 'yes'. // So far it's just the viscous Bondi problem that doesn't require feedback bool feedback = pin->GetOrAddBoolean("emhd", "feedback", true); + params.Add("feedback", feedback); Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); + params.Add("conduction_alpha", conduction_alpha); Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); + params.Add("viscosity_alpha", viscosity_alpha); Real kappa = pin->GetOrAddReal("emhd", "kappa", 1.0); + params.Add("kappa", kappa); Real eta = pin->GetOrAddReal("emhd", "eta", 1.0); + params.Add("eta", eta); EMHD_parameters emhd_params; emhd_params.higher_order_terms = higher_order_terms; diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 56f414d0..a612c513 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -138,7 +138,7 @@ std::shared_ptr Initialize(ParameterInput *pin) // Apply limits on heat flux and pressure anisotropy from velocity space instabilities? // We would want this for the torus runs but not for the test problems. // For eg: we know that this affects the viscous bondi problem - bool enable_emhd_limits = pin->GetOrAddBoolean("floors", "emhd_limits", false); + bool enable_emhd_limits = pin->GetOrAddBoolean("floors", "enable_emhd_limits", false); params.Add("enable_emhd_limits", enable_emhd_limits); // Temporary fix just for being able to save field values @@ -192,6 +192,7 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) GridScalar eflag = mbd->Get("eflag").data; const bool enable_emhd_limits = mbd->GetBlockPointer()->packages.Get("Floors")->Param("enable_emhd_limits"); + EMHD::EMHD_parameters emhd_params_tmp; if (enable_emhd_limits) { const auto& pars = pmb->packages.Get("EMHD")->AllParams(); @@ -245,7 +246,7 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) } } ); - pmb->par_for("apply_ceilings", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("apply_instability_limits", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_3D { // Apply limits to the Extended MHD variables if (enable_emhd_limits) diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index c8e63823..236fdff0 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -549,6 +549,22 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Real q, dP; EMHD::convert_prims_to_q_dP(qtilde, dPtilde, rho, Theta, cs*cs, emhd_params, q, dP); + #if TRACE + if (i == iPRINT && j == jPRINT && k == kPRINT) { + std::cerr << "\nInstability limits check (INIT)\n"; + std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " m_p.q, m_p.dP: " << qtilde << " " << dPtilde + << " q, dP: " << q << " " << dP << "\n"; + } + #endif + + //EDIT + if (i == 100 && j == 5 && k == 0) { + std::cerr << "\nInstability limits check (INIT)\n"; + std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " bsq: " << bsq << " pg: " << pg << + " m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q, dP: " << q << " " << dP << "\n"; + } + + Real qmax = 1.07 * rho * m::pow(cs, 3.); Real max_frac = m::max(m::abs(q) / qmax, 1.); if (fabs(q) / qmax > 1.) @@ -572,6 +588,24 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + #if TRACE + if (i == iPRINT && j == jPRINT && k == kPRINT) { + std::cerr << "Instability limits check (FINAL)\n"; + std::cerr << "m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q/qmax: " << q / qmax << " dP/dP_mirror: " + << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; + std::cerr << "eflag: " << eflag << "\n"; + } + #endif + + //EDIT + // if (i == 100 && j == 5 && k == 0) { + // std::cerr << "Instability limits check (FINAL)\n"; + // std::cerr << "m_p.q, m_p.dP: " << P(m_p.Q, k, j, i) << " " << P(m_p.DP, k, j, i) << " q/qmax: " << q / qmax << " dP/dP_mirror: " + // << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; + // std::cerr << "P_par / P_perp: " << dP_comp_ratio << " dP_plus: " << dP_plus << " dP_minus: " << dP_minus << "\n"; + // std::cerr << "eflag: " << eflag << "\n"; + // } + return eflag; } From ab7d8939c3f8d0b3b7cc77fa495ac6a9d1452b85 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 26 Jan 2023 12:19:24 -0600 Subject: [PATCH 021/219] Comment out debug print statements --- kharma/floors/floors.hpp | 10 +++++----- kharma/implicit/fixup.cpp | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index 236fdff0..7995b8b6 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -558,11 +558,11 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons #endif //EDIT - if (i == 100 && j == 5 && k == 0) { - std::cerr << "\nInstability limits check (INIT)\n"; - std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " bsq: " << bsq << " pg: " << pg << - " m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q, dP: " << q << " " << dP << "\n"; - } + // if (i == 100 && j == 5 && k == 0) { + // std::cerr << "\nInstability limits check (INIT)\n"; + // std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " bsq: " << bsq << " pg: " << pg << + // " m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q, dP: " << q << " " << dP << "\n"; + // } Real qmax = 1.07 * rho * m::pow(cs, 3.); diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index c6009065..f338438b 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -134,6 +134,26 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { KOKKOS_LAMBDA_3D { if (( solve_fail(k, j, i)) == SolverStatus::fail) Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); + + //EDIT + // if (i == 160 && j == 120 && k == 0) { + // const Real Theta = (gam - 1) * P_all(m_p.UU, k, j, i) / P_all(m_p.RHO, k, j, i); + // const Real cs2 = gam * (gam - 1) * P_all(m_p.UU, k, j, i) / (P_all(m_p.RHO, k, j, i) + gam * P_all(m_p.UU, k, j, i)); + // std::cerr << "\nCHECK CONSISTENCY\n"; + // std::cerr << "phi, psi, rho, Theta, cs2: " << emhd_params.conduction_alpha << " " << + // emhd_params.viscosity_alpha << " " << P_all(m_p.RHO, k, j, i) << " " << Theta << " " << cs2 << "\n"; + // std::cerr << "qtilde, dPtilde: " << P_all(m_p.Q, k, j, i) << " " << P_all(m_p.DP, k, j, i) << "\n"; + + // Real q, dP; + // Real tau, chi_e, nu_e; + // EMHD::set_parameters(G, P_all, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e, "consistency_check"); + // q = P_all(m_p.Q, k, j, i) * m::sqrt(chi_e * P_all(m_p.RHO, k, j, i) * m::pow(Theta, 2) / tau); + // dP = P_all(m_p.DP, k, j, i) * m::sqrt(nu_e * P_all(m_p.RHO, k, j, i) * Theta / tau); + // std::cerr << "q, dP (from closure parameters): " << q << " " << dP << "\n"; + + // EMHD::convert_prims_to_q_dP(P_all(m_p.Q, k, j, i), P_all(m_p.DP, k, j, i), P_all(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); + // std::cerr << "q, dP (from closure scheme): " << q << " " << dP << "\n\n"; + // } } ); From ba5486fb35b34147a39ffd89e261dc689293fcdf Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 26 Jan 2023 12:49:09 -0600 Subject: [PATCH 022/219] edits from #5db3f8a7f1890418986f808d2aee405d48d17afb and disabling qr in emhd test problem par files --- kharma/debug.cpp | 2 +- machines/delta.sh | 38 ++++++++++++++---------------- make.sh | 15 ++++++++---- pars/bondi_viscous.par | 1 + pars/emhdmodes.par | 2 +- run.sh | 48 ++++++++++++++++++-------------------- tests/bondi_viscous/run.sh | 4 +++- 7 files changed, 57 insertions(+), 53 deletions(-) diff --git a/kharma/debug.cpp b/kharma/debug.cpp index 4a95e498..e5613206 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -123,7 +123,7 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { if (m::isnan(ctop(b, dir-1, k, j, i))) { ++local_result; - fprintf(stderr, "ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT + // fprintf(stderr, "ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT } } , nan_reducer); diff --git a/machines/delta.sh b/machines/delta.sh index 27358e6b..bfb3dae4 100644 --- a/machines/delta.sh +++ b/machines/delta.sh @@ -14,32 +14,30 @@ then HOST_ARCH=ZEN3 DEVICE_ARCH=AMPERE80 - module load cmake + # Load common modules + module purge + module load modtree/gpu cmake + MPI_EXE=mpirun + if [[ $ARGS == *"cuda"* ]] then - if [[ $ARGS == *"gcc"* ]] - then - echo "Using default compiler" - elif [[ $ARGS == *"nvhpc"* ]] - then - # Most recent nvhpc. Keeps system MPI but uses NVHPC's? - #module load nvhpc/22.5 + # GPU Compile + # 4-device MPI + MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=16" + MPI_NUM_PROCS=4 + + if [[ $ARGS == *"nvhpc"* ]]; then + # nvhpc only on request, MPI crashes + module load nvhpc_latest openmpi-5.0_beta C_NATIVE=nvc CXX_NATIVE=nvc++ - else - echo "Using default compiler" + else # TODO NVHPC not-latest + C_NATIVE=gcc + CXX_NATIVE=g++ fi else + # CPU Compile module load modtree/cpu gcc + MPI_NUM_PROCS=1 fi - # In-tree HDF5 - PREFIX_PATH="$SOURCE_DIR/external/hdf5" - - # MPI options - MPI_EXE=mpirun - MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=16" - MPI_NUM_PROCS=4 - KOKKOS_NUM_DEVICES=4 - - module list fi diff --git a/make.sh b/make.sh index e715b2f1..c7c69fc1 100755 --- a/make.sh +++ b/make.sh @@ -175,6 +175,8 @@ elif [[ "$ARGS" == *"cuda"* ]]; then export NVCC_WRAPPER_DEFAULT_COMPILER="$CXX_NATIVE" # Generally Kokkos sets this, so we don't need to #export CXXFLAGS="--expt-relaxed-constexpr $CXXFLAGS" + # New NVHPC complains if we don't set this + export NVHPC_CUDA_HOME=$CUDA_HOME OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -238,8 +240,10 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then HDF_EXTRA="--enable-parallel" fi fi - CC=$HDF_CC sh configure -C $HDF_EXTRA --prefix=$PWD/../hdf5 --enable-build-mode=production \ +set -x + CC=$HDF_CC sh configure -C $HDF_EXTRA --prefix=$SOURCE_DIR/external/hdf5 --enable-build-mode=production \ --disable-dependency-tracking --disable-hl --disable-tests --disable-tools --disable-shared --disable-deprecated-symbols +set +x wait 1 # Compiling C takes less memory @@ -253,7 +257,7 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then cd ../.. fi if [[ "$ARGS" == *"hdf5"* ]]; then - PREFIX_PATH="$PWD/external/hdf5;$PREFIX_PATH" + PREFIX_PATH="$SOURCE_DIR/external/hdf5;$PREFIX_PATH" fi ### Build KHARMA ### @@ -291,6 +295,7 @@ if [[ "$ARGS" == *"clean"* ]]; then fi fi -make -j$NPROC - -cp kharma/kharma.* .. +if [[ "$ARGS" != *"dryrun"* ]]; then + make -j$NPROC + cp kharma/kharma.* .. +fi \ No newline at end of file diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index 04f88ab8..e620bb74 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -46,6 +46,7 @@ jacobian_delta = 4.e-8 linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 +use_qr = false # IMPORTANT: This block must be present and values filled in all EGRMHD simulations diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 8f34e899..c2ed2c5d 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -68,7 +68,7 @@ rootfind_tol = 1.e-20 linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 -use_qr = true +use_qr = false # General verbosity level: diff --git a/run.sh b/run.sh index 610679f3..ec26ed78 100755 --- a/run.sh +++ b/run.sh @@ -1,32 +1,37 @@ #!/bin/bash -### System-specific +### System-specific parameters # Override these with your compile file in machines/! - -# Force a number of OpenMP threads if it doesn't autodetect -#export OMP_NUM_THREADS=28 -# Number of GPUs on the node (doesn't matter for CPU runs) -#export KOKKOS_NUM_DEVICES=2 +# For running different configs on the fly, you can use the options +# -n (number of MPI procs) +# -nt (number of OpenMP threads) +# Note these options must be FIRST and IN ORDER! # Optionally use the Kokkos tools to profile kernels #export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so #export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_nvprof_cnnector.so -# Default MPI parameters: no invocation or same processes as Kokkos devices +# Default MPI parameters: don't use MPI or run with 1 process MPI_EXE=${MPI_EXE:-} MPI_NUM_PROCS=${MPI_NUM_PROCS:-1} MPI_EXTRA_ARGS=${MPI_EXTRA_ARGS:-} -### General run script - -# OpenMP directives: use all available threads +# Default OpenMP directives: use all available threads export OMP_PROC_BIND=${OMP_PROC_BIND:-spread} export OMP_PLACES=${OMP_PLACES:-threads} +# Force a number of OpenMP threads if it doesn't autodetect +#export OMP_NUM_THREADS=28 + + +### General run script + +# Map each MPI rank to one device with Kokkos +export KOKKOS_MAP_DEVICE_ID_BY=mpi_rank # If you see weird GPU race conditions, setting this # to 1 *might* fix them. Maybe. export CUDA_LAUNCH_BLOCKING=0 -# Kokkos can be forced to a particular device: +# Kokkos can be forced to use only a particular device: #export KOKKOS_DEVICE_ID=0 # Choose the kharma binary from compiled options in order of preference @@ -42,10 +47,6 @@ else exit fi -# Optionally use the Kokkos tools to profile kernels -#export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so -#export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_nvprof_cnnector.so - # Load environment from the same files as the compile process HOST=$(hostname -f) ARGS=$(cat $KHARMA_DIR/make_args) @@ -53,11 +54,14 @@ for machine in $KHARMA_DIR/machines/*.sh do source $machine done -export KOKKOS_NUM_DEVICES # Override MPI_NUM_PROCS at user option "-n" +# and OMP_NUM_THREADS at option "-nt" if [[ "$1" == "-n" ]]; then MPI_NUM_PROCS="$2" + if [[ -z $MPI_EXE && $(( $MPI_NUM_PROCS > 1 )) ]]; then + MPI_EXE="mpirun" + fi shift shift fi @@ -70,14 +74,8 @@ fi # Run based on preferences if [ -z "$MPI_EXE" ]; then echo "Running $KHARMA_DIR/$EXE_NAME $@" - $KHARMA_DIR/$EXE_NAME "$@" + exec $KHARMA_DIR/$EXE_NAME "$@" else echo "Running $MPI_EXE -n $MPI_NUM_PROCS $MPI_EXTRA_ARGS $KHARMA_DIR/$EXE_NAME $@" - $MPI_EXE -n $MPI_NUM_PROCS $MPI_EXTRA_ARGS $KHARMA_DIR/$EXE_NAME "$@" -fi - -# Examples: -# Use MPI mapping -#mpirun -n 2 --map-by ppr:1:numa:pe=14 $KHARMA_DIR/$EXE_NAME "$@" -# Use the whole machine w/locality via hpcbind -#$KHARMA_DIR/external/parthenon/external/Kokkos/bin/hpcbind --whole-system -- $KHARMA_DIR/$EXE_NAME "$@" + exec $MPI_EXE -n $MPI_NUM_PROCS $MPI_EXTRA_ARGS $KHARMA_DIR/$EXE_NAME "$@" +fi \ No newline at end of file diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index e6d04e07..79daa128 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -11,9 +11,11 @@ conv_2d() { IFS=',' read -ra RES_LIST <<< "$ALL_RES" for res in "${RES_LIST[@]}" do + # Four blocks + half=$(( $res / 2 )) $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ + parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 mv bondi_viscous.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf From bc46367df13bf897b8333867b74a6c27b993e5f7 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 26 Jan 2023 15:37:07 -0600 Subject: [PATCH 023/219] solver fixup: pararrays --- kharma/implicit/fixup.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index f338438b..da7f155d 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -67,16 +67,24 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { const IndexRange jb = mbd->GetBoundsJ(IndexDomain::entire); const IndexRange kb = mbd->GetBoundsK(IndexDomain::entire); + auto bounds = pmb->cellbounds; + const int n1 = bounds.ncellsi(IndexDomain::entire); + const int n2 = bounds.ncellsj(IndexDomain::entire); + const int n3 = bounds.ncellsk(IndexDomain::entire); + const IndexRange ib_b = mbd->GetBoundsI(IndexDomain::interior); const IndexRange jb_b = mbd->GetBoundsJ(IndexDomain::interior); const IndexRange kb_b = mbd->GetBoundsK(IndexDomain::interior); + ParArrayND sum("sum_good_neighbors", nfvar, n3+1, n2+1, n1+1); + ParArrayND sum_x("sum_all_neighbors", nfvar, n3+1, n2+1, n1+1); + pmb->par_for("fix_solver_failures", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_3D { // Fix only bad zones if ((solve_fail(k, j, i)) == SolverStatus::fail) { double wsum = 0., wsum_x = 0.; - double sum[nfvar] = {0.}, sum_x[nfvar] = {0.}; + // double sum[nfvar] = {0.}, sum_x[nfvar] = {0.}; // For all neighboring cells... for (int n = -1; n <= 1; n++) { for (int m = -1; m <= 1; m++) { @@ -91,11 +99,11 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { if ((solve_fail(kk, jj, ii)) != SolverStatus::fail) { // Weight by distance. Note interpolated "fixed" cells stay flagged wsum += w; - FLOOP sum[ip] += w * P(ip, kk, jj, ii); + FLOOP sum(ip, k, j, i) += w * P(ip, kk, jj, ii); } // Just in case, keep a sum of even the bad ones wsum_x += w; - FLOOP sum_x[ip] += w * P(ip, kk, jj, ii); + FLOOP sum_x(ip, k, j, i) += w * P(ip, kk, jj, ii); } } } @@ -108,7 +116,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { printf("No neighbors were available at %d %d %d!\n", i, j, k); #endif } else { - FLOOP P(ip, k, j, i) = sum[ip]/wsum; + FLOOP P(ip, k, j, i) = sum(ip, k, j, i)/wsum; } } } From d3ead212f157f74f5d3ca30666c8e8b4815c7bfa Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 26 Jan 2023 16:56:26 -0600 Subject: [PATCH 024/219] Initialize sum arrays in implicit/fixup.cpp. Removed Darwin.sh --- kharma/implicit/fixup.cpp | 4 +++ machines/darwin.sh | 55 --------------------------------------- 2 files changed, 4 insertions(+), 55 deletions(-) delete mode 100644 machines/darwin.sh diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index da7f155d..221e7070 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -81,6 +81,10 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { pmb->par_for("fix_solver_failures", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_3D { + FLOOP { + sum(ip, k, j, i) = 0.; + sum_x(ip, k, j, i) = 0.; + } // Fix only bad zones if ((solve_fail(k, j, i)) == SolverStatus::fail) { double wsum = 0., wsum_x = 0.; diff --git a/machines/darwin.sh b/machines/darwin.sh deleted file mode 100644 index 228b2460..00000000 --- a/machines/darwin.sh +++ /dev/null @@ -1,55 +0,0 @@ -# LANL Darwin. A little bit of everything - -# Must list which node you're compiling for: -# ampere for AMD/NVIDIA A100 nodes -# volta for x86/volta of all kinds -# Not working yet: -# arm-nv to compile for devkit ARM/NVIDIA nodes - -if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then - module purge - module load cmake - - # Always our own HDF5 - # Run ""./make.sh hdf5" to build it - PREFIX_PATH="$SOURCE_DIR/external/hdf5" - - # These are - if [[ "$ARGS" == *"arm-nv"* ]]; then - HOST_ARCH="ARMV81" - DEVICE_ARCH="AMPERE80" - module load nvhpc/22.7 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME - elif [[ "$ARGS" == *"ampere"* ]]; then - HOST_ARCH="ZEN3" - DEVICE_ARCH="AMPERE80" - module load nvhpc/22.7 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME - elif [[ "$ARGS" == *"volta"* ]]; then - HOST_ARCH="HSW" - DEVICE_ARCH="VOLTA70" - module load nvhpc/22.7 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME - else - echo "No target arch specified: must list a target arch for Darwin" - exit - fi - - # Runtime - MPI_EXE="mpirun" - MPI_NUM_PROCS=2 - KOKKOS_NUM_DEVICES=2 - MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=8" -fi From 357b33bdd42bec2c55d568d54c2164e4ff2de08a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 26 Jan 2023 16:46:10 -0700 Subject: [PATCH 025/219] Switch to the TeamVector-based QR with column pivoting. Probably *extremely* slow --- kharma/implicit/implicit.cpp | 74 ++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 2ae8d755..8660cacc 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -168,6 +168,8 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin) #include #include #include +#include +#include TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt) @@ -299,7 +301,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, // P_solver, P_linesearch, dU_implicit, three temps (all vars) // solve_norm, solve_fail - const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (11) * var_size_in_bytes + \ + const size_t total_scratch_bytes = tensor_size_in_bytes + (5) * fvar_size_in_bytes + (11) * var_size_in_bytes + \ (2) * scalar_size_in_bytes; // + int_size_in_bytes; @@ -318,6 +320,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad3D jacobian_s(member.team_scratch(scratch_level), nfvar, nfvar, n1); ScratchPad2D residual_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), nfvar, n1); + ScratchPad2D pivot_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D trans_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D work_s(member.team_scratch(scratch_level), nfvar, n1); // Scratchpads for all vars @@ -417,7 +420,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // since we do a copy in imex_driver just before, but it is required for the subsequent // iterations of the solver. PLOOP P_linesearch(ip) = P_solver(ip); - Real lambda = linesearch_lambda; // Jacobian calculation // Requires calculating the residual anyway, so we grab it here @@ -427,7 +429,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); - #if TRACE +#if TRACE if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { std::cerr << "Variable ordering: rho " << int(m_p.RHO) << " uu " << int(m_p.UU) << " U1 " << int(m_p.U1) << " B1 " << int(m_p.B1) << " q " << int(m_p.Q) << " dP " << int(m_p.DP) << std::endl; @@ -450,8 +452,65 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ std::cerr << "Initial residual: "; FLOOP std::cerr << residual(ip) << " "; std::cerr << std::endl; std::cerr << "Initial delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; } - #endif +#endif +#if 1 + } + } + ); + member.team_barrier(); + for (int i = ib.s; i <= ib.e; ++i) { + // Solver variables + auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); + auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); + auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); + auto pivot = Kokkos::subview(pivot_s, Kokkos::ALL(), i); + auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); + auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); + int rank = 0; // Strip const by copying + KokkosBatched::TeamVectorQR_WithColumnPivoting + ::invoke(member, jacobian, trans, pivot, work, rank); + member.team_barrier(); + KokkosBatched::TeamVectorApplyQ + ::invoke(member, jacobian, trans, delta_prim, work); + member.team_barrier(); + KokkosBatched::TeamVectorTrsv + ::invoke(member, alpha, jacobian, delta_prim); + member.team_barrier(); + KokkosBatched::TeamVectorApplyPivot + ::invoke(member, pivot, delta_prim); + member.team_barrier(); + } + parthenon::par_for_inner(member, ib.s, ib.e, + [&](const int& i) { + // Lots of slicing. This still ends up faster & cleaner than alternatives I tried + auto P_full_step_init = Kokkos::subview(P_full_step_init_s, Kokkos::ALL(), i); + auto U_full_step_init = Kokkos::subview(U_full_step_init_s, Kokkos::ALL(), i); + auto P_sub_step_init = Kokkos::subview(P_sub_step_init_s, Kokkos::ALL(), i); + auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); + auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); + auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); + auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); + // Solver variables + auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); + auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); + auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); + auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); + auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); + // Temporaries + auto tmp1 = Kokkos::subview(tmp1_s, Kokkos::ALL(), i); + auto tmp2 = Kokkos::subview(tmp2_s, Kokkos::ALL(), i); + auto tmp3 = Kokkos::subview(tmp3_s, Kokkos::ALL(), i); + // Implicit sources at starting state + auto dU_implicit = Kokkos::subview(dU_implicit_s, Kokkos::ALL(), i); + // Solver performance diagnostics + auto solve_norm = Kokkos::subview(solve_norm_s, i); + auto solve_fail = Kokkos::subview(solve_fail_s, i); + + if (solve_fail() != SolverStatus::fail) { +#else if (use_qr) { // Linear solve by QR decomposition KokkosBatched::SerialQR::invoke(jacobian, trans, work); @@ -464,17 +523,18 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KokkosBatched::SerialTrsv ::invoke(alpha, jacobian, delta_prim); - - #if TRACE +#endif +#if TRACE if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; std::cerr<< std::endl; } - #endif +#endif // Check for positive definite values of density and internal energy. // Ignore zone if manual backtracking is not sufficient. // The primitives will be averaged over good neighbors. + Real lambda = linesearch_lambda; if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { solve_fail() = SolverStatus::backtrack; lambda = 0.1; From 2bdcff10da37e1690ec5c5f62dbcffddf301cdc7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 27 Jan 2023 15:12:09 -0700 Subject: [PATCH 026/219] Revert team-based QR patch, too slow. Use custom kokkos-kernels with serial version --- kharma/implicit/implicit.cpp | 71 +++++------------------------------- 1 file changed, 9 insertions(+), 62 deletions(-) diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 8660cacc..600065ea 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -301,7 +301,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, // P_solver, P_linesearch, dU_implicit, three temps (all vars) // solve_norm, solve_fail - const size_t total_scratch_bytes = tensor_size_in_bytes + (5) * fvar_size_in_bytes + (11) * var_size_in_bytes + \ + const size_t total_scratch_bytes = tensor_size_in_bytes + (6) * fvar_size_in_bytes + (11) * var_size_in_bytes + \ (2) * scalar_size_in_bytes; // + int_size_in_bytes; @@ -322,7 +322,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D pivot_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D trans_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D work_s(member.team_scratch(scratch_level), nfvar, n1); + ScratchPad2D work_s(member.team_scratch(scratch_level), 2*nfvar, n1); // Scratchpads for all vars ScratchPad2D dU_implicit_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D tmp1_s(member.team_scratch(scratch_level), nvar, n1); @@ -394,6 +394,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); + auto pivot = Kokkos::subview(pivot_s, Kokkos::ALL(), i); auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); // Temporaries @@ -453,67 +454,9 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ std::cerr << "Initial delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; } #endif -#if 1 - } - } - ); - member.team_barrier(); - for (int i = ib.s; i <= ib.e; ++i) { - // Solver variables - auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); - auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); - auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); - auto pivot = Kokkos::subview(pivot_s, Kokkos::ALL(), i); - auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); - auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); - int rank = 0; // Strip const by copying - KokkosBatched::TeamVectorQR_WithColumnPivoting - ::invoke(member, jacobian, trans, pivot, work, rank); - member.team_barrier(); - KokkosBatched::TeamVectorApplyQ - ::invoke(member, jacobian, trans, delta_prim, work); - member.team_barrier(); - KokkosBatched::TeamVectorTrsv - ::invoke(member, alpha, jacobian, delta_prim); - member.team_barrier(); - KokkosBatched::TeamVectorApplyPivot - ::invoke(member, pivot, delta_prim); - member.team_barrier(); - } - - parthenon::par_for_inner(member, ib.s, ib.e, - [&](const int& i) { - // Lots of slicing. This still ends up faster & cleaner than alternatives I tried - auto P_full_step_init = Kokkos::subview(P_full_step_init_s, Kokkos::ALL(), i); - auto U_full_step_init = Kokkos::subview(U_full_step_init_s, Kokkos::ALL(), i); - auto P_sub_step_init = Kokkos::subview(P_sub_step_init_s, Kokkos::ALL(), i); - auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); - auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); - auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); - auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); - // Solver variables - auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); - auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); - auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); - auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); - auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); - // Temporaries - auto tmp1 = Kokkos::subview(tmp1_s, Kokkos::ALL(), i); - auto tmp2 = Kokkos::subview(tmp2_s, Kokkos::ALL(), i); - auto tmp3 = Kokkos::subview(tmp3_s, Kokkos::ALL(), i); - // Implicit sources at starting state - auto dU_implicit = Kokkos::subview(dU_implicit_s, Kokkos::ALL(), i); - // Solver performance diagnostics - auto solve_norm = Kokkos::subview(solve_norm_s, i); - auto solve_fail = Kokkos::subview(solve_fail_s, i); - - if (solve_fail() != SolverStatus::fail) { -#else if (use_qr) { // Linear solve by QR decomposition - KokkosBatched::SerialQR::invoke(jacobian, trans, work); + KokkosBatched::SerialQR::invoke(jacobian, trans, pivot, work); KokkosBatched::SerialApplyQ ::invoke(jacobian, trans, delta_prim, work); @@ -523,7 +466,11 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KokkosBatched::SerialTrsv ::invoke(alpha, jacobian, delta_prim); -#endif + if (use_qr) { + // Linear solve by QR decomposition + KokkosBatched::SerialApplyPivot + ::invoke(pivot, delta_prim); + } #if TRACE if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; From fa74f4ab55508a1f50d11bea70dbdf61083ba236 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 30 Jan 2023 16:56:59 -0700 Subject: [PATCH 027/219] Change scratch space ordering & thereby isolate non-convergence-GPU issue to EMHD specifically if it still exists --- kharma/implicit/implicit.cpp | 179 ++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 53 deletions(-) diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 600065ea..f51da615 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -248,6 +248,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const int nvar = U_full_step_init_all.GetDim(4); // Get number of implicit variables auto implicit_vars = get_ordered_names(mbd_full_step_init.get(), isPrimitive, true); + //std::cerr << "Ordered implicit:"; for(auto var: implicit_vars) std::cerr << " " << var; std::cerr << std::endl; PackIndexMap implicit_prims_map; auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P_full_step_init_implicit.GetDim(4); @@ -290,9 +291,9 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // to avoid a bunch of indices in all the device-side operations // See grmhd_functions.hpp for the other approach with overloads const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM - const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nvar, n1); - const size_t fvar_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nfvar, n1); - const size_t tensor_size_in_bytes = parthenon::ScratchPad3D::shmem_size(nfvar, nfvar, n1); + const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(n1, nvar); + const size_t fvar_size_in_bytes = parthenon::ScratchPad2D::shmem_size(n1, nfvar); + const size_t tensor_size_in_bytes = parthenon::ScratchPad3D::shmem_size(nfvar, n1, nfvar); const size_t scalar_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); const size_t int_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); // Allocate enough to cache: @@ -317,42 +318,44 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { const auto& G = U_full_step_init_all.GetCoords(b); // Scratchpads for implicit vars - ScratchPad3D jacobian_s(member.team_scratch(scratch_level), nfvar, nfvar, n1); - ScratchPad2D residual_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D pivot_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D trans_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D work_s(member.team_scratch(scratch_level), 2*nfvar, n1); + ScratchPad3D jacobian_s(member.team_scratch(scratch_level), n1, nfvar, nfvar); + ScratchPad2D residual_s(member.team_scratch(scratch_level), n1, nfvar); + ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), n1, nfvar); + ScratchPad2D pivot_s(member.team_scratch(scratch_level), n1, nfvar); + ScratchPad2D trans_s(member.team_scratch(scratch_level), n1, nfvar); + ScratchPad2D work_s(member.team_scratch(scratch_level), 2*n1, nfvar); // Scratchpads for all vars - ScratchPad2D dU_implicit_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D tmp1_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D tmp2_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D tmp3_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D P_full_step_init_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D U_full_step_init_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D P_sub_step_init_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D U_sub_step_init_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D flux_src_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D P_solver_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D dU_implicit_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D tmp1_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D tmp2_s(member.team_scratch(scratch_level), n1, nfvar); + ScratchPad2D tmp3_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D P_full_step_init_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D U_full_step_init_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D P_sub_step_init_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D U_sub_step_init_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D flux_src_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D P_solver_s(member.team_scratch(scratch_level), n1, nvar); + ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), n1, nvar); // Scratchpads for solver performance diagnostics ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); - // ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); - ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); + ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); // Copy some file contents to scratchpads, so we can slice them - PLOOP { - parthenon::par_for_inner(member, ib.s, ib.e, + for(int ip=0; ip < nvar; ++ip) { + parthenon::par_for_inner(member, 0, n1-1, [&](const int& i) { - P_full_step_init_s(ip, i) = P_full_step_init_all(b)(ip, k, j, i); - U_full_step_init_s(ip, i) = U_full_step_init_all(b)(ip, k, j, i); - P_sub_step_init_s(ip, i) = P_sub_step_init_all(b)(ip, k, j, i); - U_sub_step_init_s(ip, i) = U_sub_step_init_all(b)(ip, k, j, i); - flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); - P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); - P_linesearch_s(ip, i) = P_linesearch_all(b)(ip, k, j, i); - dU_implicit_s(ip, i) = 0.; - + P_full_step_init_s(i, ip) = P_full_step_init_all(b)(ip, k, j, i); + U_full_step_init_s(i, ip) = U_full_step_init_all(b)(ip, k, j, i); + P_sub_step_init_s(i, ip) = P_sub_step_init_all(b)(ip, k, j, i); + U_sub_step_init_s(i, ip) = U_sub_step_init_all(b)(ip, k, j, i); + flux_src_s(i, ip) = flux_src_all(b)(ip, k, j, i); + P_solver_s(i, ip) = P_solver_all(b)(ip, k, j, i); + P_linesearch_s(i, ip) = P_linesearch_all(b)(ip, k, j, i); + dU_implicit_s(i, ip) = 0.; + tmp1_s(i, ip) = 0.; + tmp3_s(i, ip) = 0.; + + // TODO these are run repeatedly a bunch of times solve_norm_s(i) = 0.; if (iter == 1) { // New beginnings @@ -367,6 +370,23 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ); } member.team_barrier(); + // For implicit only + for(int ip=0; ip < nfvar; ++ip) { + parthenon::par_for_inner(member, 0, n1-1, + [&](const int& i) { + for(int jp=0; jp < nfvar; ++jp) + jacobian_s(ip, jp, i) = 0.; + residual_s(i, ip) = 0.; + delta_prim_s(i, ip) = 0.; + pivot_s(i, ip) = 0; + trans_s(i, ip) = 0.; + work_s(i, ip) = 0.; + work_s(ip+nfvar, i) = 0.; + tmp2_s(i, ip) = 0.; + } + ); + } + member.team_barrier(); // Copy in the guess or current solution // Note this replaces the implicit portion of P_solver_s -- @@ -374,7 +394,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // FLOOP { // Loop over just the implicit "fluid" portion of primitive vars // parthenon::par_for_inner(member, ib.s, ib.e, // [&](const int& i) { - // P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); + // P_solver_s(i, ip) = P_solver_all(b)(ip, k, j, i); // } // ); // } @@ -383,26 +403,26 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { // Lots of slicing. This still ends up faster & cleaner than alternatives I tried - auto P_full_step_init = Kokkos::subview(P_full_step_init_s, Kokkos::ALL(), i); - auto U_full_step_init = Kokkos::subview(U_full_step_init_s, Kokkos::ALL(), i); - auto P_sub_step_init = Kokkos::subview(P_sub_step_init_s, Kokkos::ALL(), i); - auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); - auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); - auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); - auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); + auto P_full_step_init = Kokkos::subview(P_full_step_init_s, i, Kokkos::ALL()); + auto U_full_step_init = Kokkos::subview(U_full_step_init_s, i, Kokkos::ALL()); + auto P_sub_step_init = Kokkos::subview(P_sub_step_init_s, i, Kokkos::ALL()); + auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, i, Kokkos::ALL()); + auto flux_src = Kokkos::subview(flux_src_s, i, Kokkos::ALL()); + auto P_solver = Kokkos::subview(P_solver_s, i, Kokkos::ALL()); + auto P_linesearch = Kokkos::subview(P_linesearch_s, i, Kokkos::ALL()); // Solver variables - auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); - auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); - auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); - auto pivot = Kokkos::subview(pivot_s, Kokkos::ALL(), i); - auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); - auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); + auto residual = Kokkos::subview(residual_s, i, Kokkos::ALL()); + auto jacobian = Kokkos::subview(jacobian_s, i, Kokkos::ALL(), Kokkos::ALL()); + auto delta_prim = Kokkos::subview(delta_prim_s, i, Kokkos::ALL()); + auto pivot = Kokkos::subview(pivot_s, i, Kokkos::ALL()); + auto trans = Kokkos::subview(trans_s, i, Kokkos::ALL()); + auto work = Kokkos::subview(work_s, i, Kokkos::ALL()); // Temporaries - auto tmp1 = Kokkos::subview(tmp1_s, Kokkos::ALL(), i); - auto tmp2 = Kokkos::subview(tmp2_s, Kokkos::ALL(), i); - auto tmp3 = Kokkos::subview(tmp3_s, Kokkos::ALL(), i); + auto tmp1 = Kokkos::subview(tmp1_s, i, Kokkos::ALL()); + auto tmp2 = Kokkos::subview(tmp2_s, i, Kokkos::ALL()); + auto tmp3 = Kokkos::subview(tmp3_s, i, Kokkos::ALL()); // Implicit sources at starting state - auto dU_implicit = Kokkos::subview(dU_implicit_s, Kokkos::ALL(), i); + auto dU_implicit = Kokkos::subview(dU_implicit_s, i, Kokkos::ALL()); // Solver performance diagnostics auto solve_norm = Kokkos::subview(solve_norm_s, i); auto solve_fail = Kokkos::subview(solve_fail_s, i); @@ -453,6 +473,24 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ std::cerr << "Initial residual: "; FLOOP std::cerr << residual(ip) << " "; std::cerr << std::endl; std::cerr << "Initial delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; } +#endif +#if 1 + } + } + ); + member.team_barrier(); + parthenon::par_for_inner(member, ib.s, ib.e, + [&](const int& i) { + // Solver variables + auto residual = Kokkos::subview(residual_s, i, Kokkos::ALL()); + auto jacobian = Kokkos::subview(jacobian_s, i, Kokkos::ALL(), Kokkos::ALL()); + auto delta_prim = Kokkos::subview(delta_prim_s, i, Kokkos::ALL()); + auto pivot = Kokkos::subview(pivot_s, i, Kokkos::ALL()); + auto trans = Kokkos::subview(trans_s, i, Kokkos::ALL()); + auto work = Kokkos::subview(work_s, i, Kokkos::ALL()); + auto solve_fail = Kokkos::subview(solve_fail_s, i); + + if (solve_fail() != SolverStatus::fail) { #endif if (use_qr) { // Linear solve by QR decomposition @@ -471,6 +509,41 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KokkosBatched::SerialApplyPivot ::invoke(pivot, delta_prim); } +#if 1 + } + } + ); + member.team_barrier(); + + parthenon::par_for_inner(member, ib.s, ib.e, + [&](const int& i) { + // Lots of slicing. This still ends up faster & cleaner than alternatives I tried + auto P_full_step_init = Kokkos::subview(P_full_step_init_s, i, Kokkos::ALL()); + auto U_full_step_init = Kokkos::subview(U_full_step_init_s, i, Kokkos::ALL()); + auto P_sub_step_init = Kokkos::subview(P_sub_step_init_s, i, Kokkos::ALL()); + auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, i, Kokkos::ALL()); + auto flux_src = Kokkos::subview(flux_src_s, i, Kokkos::ALL()); + auto P_solver = Kokkos::subview(P_solver_s, i, Kokkos::ALL()); + auto P_linesearch = Kokkos::subview(P_linesearch_s, i, Kokkos::ALL()); + // Solver variables + auto residual = Kokkos::subview(residual_s, i, Kokkos::ALL()); + auto jacobian = Kokkos::subview(jacobian_s, i, Kokkos::ALL(), Kokkos::ALL()); + auto delta_prim = Kokkos::subview(delta_prim_s, i, Kokkos::ALL()); + auto pivot = Kokkos::subview(pivot_s, i, Kokkos::ALL()); + auto trans = Kokkos::subview(trans_s, i, Kokkos::ALL()); + auto work = Kokkos::subview(work_s, i, Kokkos::ALL()); + // Temporaries + auto tmp1 = Kokkos::subview(tmp1_s, i, Kokkos::ALL()); + auto tmp2 = Kokkos::subview(tmp2_s, i, Kokkos::ALL()); + auto tmp3 = Kokkos::subview(tmp3_s, i, Kokkos::ALL()); + // Implicit sources at starting state + auto dU_implicit = Kokkos::subview(dU_implicit_s, i, Kokkos::ALL()); + // Solver performance diagnostics + auto solve_norm = Kokkos::subview(solve_norm_s, i); + auto solve_fail = Kokkos::subview(solve_fail_s, i); + + if (solve_fail() != SolverStatus::fail) { +#endif #if TRACE if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; @@ -567,9 +640,9 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ FLOOP { parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { - P_solver_all(b)(ip, k, j, i) = P_solver_s(ip, i); + P_solver_all(b)(ip, k, j, i) = P_solver_s(i, ip); // if (save_residual) { - // residual_all(b, ip, k, j, i) = residual_s(ip, i); + // residual_all(b, ip, k, j, i) = residual_s(i, ip); // } } ); From d453232322c5eefb7200197891abfba72e570a94 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 31 Jan 2023 11:31:28 -0700 Subject: [PATCH 028/219] Fix a few issues with scratch space reordering. Restore compiling with trace for GPU --- kharma/floors/floors.hpp | 30 ++++++------- kharma/implicit/implicit.cpp | 87 ++++++++++++++++++++---------------- kharma/types.hpp | 4 +- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index 7995b8b6..bffdf90d 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -549,13 +549,13 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Real q, dP; EMHD::convert_prims_to_q_dP(qtilde, dPtilde, rho, Theta, cs*cs, emhd_params, q, dP); - #if TRACE - if (i == iPRINT && j == jPRINT && k == kPRINT) { - std::cerr << "\nInstability limits check (INIT)\n"; - std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " m_p.q, m_p.dP: " << qtilde << " " << dPtilde - << " q, dP: " << q << " " << dP << "\n"; - } - #endif + // #if TRACE + // if (i == iPRINT && j == jPRINT && k == kPRINT) { + // std::cerr << "\nInstability limits check (INIT)\n"; + // std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " m_p.q, m_p.dP: " << qtilde << " " << dPtilde + // << " q, dP: " << q << " " << dP << "\n"; + // } + // #endif //EDIT // if (i == 100 && j == 5 && k == 0) { @@ -588,14 +588,14 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); - #if TRACE - if (i == iPRINT && j == jPRINT && k == kPRINT) { - std::cerr << "Instability limits check (FINAL)\n"; - std::cerr << "m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q/qmax: " << q / qmax << " dP/dP_mirror: " - << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; - std::cerr << "eflag: " << eflag << "\n"; - } - #endif + // #if TRACE + // if (i == iPRINT && j == jPRINT && k == kPRINT) { + // std::cerr << "Instability limits check (FINAL)\n"; + // std::cerr << "m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q/qmax: " << q / qmax << " dP/dP_mirror: " + // << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; + // std::cerr << "eflag: " << eflag << "\n"; + // } + // #endif //EDIT // if (i == 100 && j == 5 && k == 0) { diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index f51da615..0b1f2aa6 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -174,10 +174,10 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin) TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { - Flag(md_full_step_init, "Implicit Iteration start, full step"); - Flag(md_sub_step_init, "Implicit Iteration start, sub step"); - Flag(md_flux_src, "Implicit Iteration start, divF and sources"); - Flag(md_linesearch, "Linesearch"); + //Flag(md_full_step_init, "Implicit Iteration start, full step"); + //Flag(md_sub_step_init, "Implicit Iteration start, sub step"); + //Flag(md_flux_src, "Implicit Iteration start, divF and sources"); + //Flag(md_linesearch, "Linesearch"); auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); @@ -316,6 +316,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "implicit_solve", pmb_sub_step_init->exec_space, total_scratch_bytes, scratch_level, block.s, block.e, kb.s, kb.e, jb.s, jb.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { + //printf("Start\n"); const auto& G = U_full_step_init_all.GetCoords(b); // Scratchpads for implicit vars ScratchPad3D jacobian_s(member.team_scratch(scratch_level), n1, nfvar, nfvar); @@ -323,7 +324,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), n1, nfvar); ScratchPad2D pivot_s(member.team_scratch(scratch_level), n1, nfvar); ScratchPad2D trans_s(member.team_scratch(scratch_level), n1, nfvar); - ScratchPad2D work_s(member.team_scratch(scratch_level), 2*n1, nfvar); + ScratchPad2D work_s(member.team_scratch(scratch_level), n1, 2*nfvar); // Scratchpads for all vars ScratchPad2D dU_implicit_s(member.team_scratch(scratch_level), n1, nvar); ScratchPad2D tmp1_s(member.team_scratch(scratch_level), n1, nvar); @@ -340,6 +341,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); + //printf("Scratchpads\n"); + // Copy some file contents to scratchpads, so we can slice them for(int ip=0; ip < nvar; ++ip) { parthenon::par_for_inner(member, 0, n1-1, @@ -369,24 +372,24 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } ); } - member.team_barrier(); // For implicit only for(int ip=0; ip < nfvar; ++ip) { parthenon::par_for_inner(member, 0, n1-1, [&](const int& i) { for(int jp=0; jp < nfvar; ++jp) - jacobian_s(ip, jp, i) = 0.; + jacobian_s(i, ip, jp) = 0.; residual_s(i, ip) = 0.; delta_prim_s(i, ip) = 0.; pivot_s(i, ip) = 0; trans_s(i, ip) = 0.; work_s(i, ip) = 0.; - work_s(ip+nfvar, i) = 0.; + work_s(i, ip+nfvar) = 0.; tmp2_s(i, ip) = 0.; } ); } member.team_barrier(); + //printf("Scratchpad copies\n"); // Copy in the guess or current solution // Note this replaces the implicit portion of P_solver_s -- @@ -450,35 +453,38 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); -#if TRACE - if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { - std::cerr << "Variable ordering: rho " << int(m_p.RHO) << " uu " << int(m_p.UU) << " U1 " << int(m_p.U1) - << " B1 " << int(m_p.B1) << " q " << int(m_p.Q) << " dP " << int(m_p.DP) << std::endl; - std::cerr << "Variable ordering: rho " << int(m_u.RHO) << " uu " << int(m_u.UU) << " U1 " << int(m_u.U1) - << " B1 " << int(m_u.B1) << " q " << int(m_u.Q) << " dP " << int(m_u.DP) << std::endl; - std::cerr << "P_solver: "; - PLOOP {std::cerr << P_solver(ip) << " ";} std::cerr << std::endl; - std::cerr << "Pi: "; - PLOOP {std::cerr << P_full_step_init(ip) << " ";} std::cerr << std::endl; - std::cerr << "Ui: "; - PLOOP {std::cerr << U_full_step_init(ip) << " ";} std::cerr << std::endl; - std::cerr << "Ps: "; - PLOOP {std::cerr << P_sub_step_init(ip) << " ";} std::cerr << std::endl; - std::cerr << "Us: "; - PLOOP {std::cerr << U_sub_step_init(ip) << " ";} std::cerr << std::endl; - std::cerr << "dUdt: "; - PLOOP {std::cerr << dU_implicit(ip) << " ";} std::cerr << std::endl; - std::cerr << "Initial Jacobian:" << std::endl; - for (int jp=0; jp *md_full_step_init, MeshData *md_ ); member.team_barrier(); + //printf("Solve\n"); + parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { // Lots of slicing. This still ends up faster & cleaner than alternatives I tried @@ -544,12 +552,12 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ if (solve_fail() != SolverStatus::fail) { #endif -#if TRACE - if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { - std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; - std::cerr<< std::endl; - } -#endif +// #if TRACE +// if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { +// std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; +// std::cerr<< std::endl; +// } +// #endif // Check for positive definite values of density and internal energy. // Ignore zone if manual backtracking is not sufficient. @@ -634,6 +642,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ); member.team_barrier(); + //printf("Residuals\n"); + // Copy out P_solver to the existing array. // We'll copy even the values for the failed zones because it doesn't really matter, it'll be averaged over later. // And copy any other diagnostics that are relevant to analyze the solver's performance @@ -653,6 +663,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ solve_fail_all(b, 0, k, j, i) = solve_fail_s(i); } ); + //printf("Copy back\n"); } ); diff --git a/kharma/types.hpp b/kharma/types.hpp index 2ce673f8..adeb7632 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -190,8 +190,8 @@ inline bool IsDomainBound(MeshBlock *pmb, BoundaryFace face) */ #if TRACE #define PRINTCORNERS 0 -#define PRINTZONE 1 -#define PRINTTILE 1 +#define PRINTZONE 0 +#define PRINTTILE 0 #define iPRINT 7 #define jPRINT 111 #define kPRINT 0 From 409bbb9438ebfb1fd0a1a39b1f1578d681b8bac5 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 31 Jan 2023 11:42:09 -0700 Subject: [PATCH 029/219] Cleanup from Implicit OOB debugging --- kharma/implicit/implicit.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 0b1f2aa6..b8b28912 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -174,10 +174,10 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin) TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { - //Flag(md_full_step_init, "Implicit Iteration start, full step"); - //Flag(md_sub_step_init, "Implicit Iteration start, sub step"); - //Flag(md_flux_src, "Implicit Iteration start, divF and sources"); - //Flag(md_linesearch, "Linesearch"); + Flag(md_full_step_init, "Implicit Iteration start, full step"); + Flag(md_sub_step_init, "Implicit Iteration start, sub step"); + Flag(md_flux_src, "Implicit Iteration start, divF and sources"); + Flag(md_linesearch, "Linesearch"); auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); @@ -316,7 +316,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "implicit_solve", pmb_sub_step_init->exec_space, total_scratch_bytes, scratch_level, block.s, block.e, kb.s, kb.e, jb.s, jb.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { - //printf("Start\n"); const auto& G = U_full_step_init_all.GetCoords(b); // Scratchpads for implicit vars ScratchPad3D jacobian_s(member.team_scratch(scratch_level), n1, nfvar, nfvar); @@ -341,8 +340,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); - //printf("Scratchpads\n"); - // Copy some file contents to scratchpads, so we can slice them for(int ip=0; ip < nvar; ++ip) { parthenon::par_for_inner(member, 0, n1-1, @@ -389,7 +386,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ); } member.team_barrier(); - //printf("Scratchpad copies\n"); // Copy in the guess or current solution // Note this replaces the implicit portion of P_solver_s -- @@ -482,9 +478,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } ); member.team_barrier(); - - //printf("Fill Jacobian\n"); - parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { // Solver variables @@ -521,8 +514,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ); member.team_barrier(); - //printf("Solve\n"); - parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { // Lots of slicing. This still ends up faster & cleaner than alternatives I tried @@ -642,8 +633,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ); member.team_barrier(); - //printf("Residuals\n"); - // Copy out P_solver to the existing array. // We'll copy even the values for the failed zones because it doesn't really matter, it'll be averaged over later. // And copy any other diagnostics that are relevant to analyze the solver's performance @@ -663,7 +652,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ solve_fail_all(b, 0, k, j, i) = solve_fail_s(i); } ); - //printf("Copy back\n"); } ); From 92e8aba39e8f25662892de171215582a31e23363 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Thu, 16 Feb 2023 21:40:01 -0600 Subject: [PATCH 030/219] Feature: Can now evolve EMHD problems with no heat flux, ie., only pressure anisotropy. Fixes: Done away with mesh decomposition for viscous Bondi along X1, since it messes with the evolution of dP (presumably by modifying B1 in some insidious manner). --- kharma/emhd/emhd.cpp | 66 +++++--- kharma/emhd/emhd.hpp | 264 +++++++++++++++++------------ kharma/emhd/emhd_sources.hpp | 47 ++--- kharma/floors/floors.cpp | 5 - kharma/floors/floors.hpp | 87 ++++------ kharma/flux_functions.hpp | 49 ++++-- kharma/implicit/fixup.cpp | 20 --- kharma/implicit/implicit.cpp | 114 +------------ kharma/implicit/implicit.hpp | 33 ++-- kharma/prob/emhd/fm_torus_emhd.cpp | 19 ++- pars/bondi_viscous.par | 18 +- pars/emhdmodes.par | 5 +- tests/bondi_viscous/check.py | 21 +-- tests/bondi_viscous/run.sh | 8 +- tests/emhdmodes/check.py | 2 +- 15 files changed, 353 insertions(+), 405 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 3bd15ea4..c38ebfcc 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -66,6 +66,11 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack bool feedback = pin->GetOrAddBoolean("emhd", "feedback", true); params.Add("feedback", feedback); + bool conduction = pin->GetOrAddBoolean("emhd", "conduction", true); + params.Add("conduction", conduction); + bool viscosity = pin->GetOrAddBoolean("emhd", "viscosity", true); + params.Add("viscosity", viscosity); + Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); params.Add("conduction_alpha", conduction_alpha); @@ -91,6 +96,8 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack } else { throw std::invalid_argument("Invalid Closure type: "+closure_type+". Use constant, sound_speed, or torus"); } + emhd_params.conduction = conduction; + emhd_params.viscosity = viscosity; emhd_params.tau = tau; emhd_params.conduction_alpha = conduction_alpha; emhd_params.viscosity_alpha = viscosity_alpha; @@ -120,11 +127,15 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack Metadata::FillGhost, Metadata::Restart, isPrimitive, isEMHD}); // Heat conduction - pkg->AddField("cons.q", m_con); - pkg->AddField("prims.q", m_prim); + if (conduction) { + pkg->AddField("cons.q", m_con); + pkg->AddField("prims.q", m_prim); + } // Pressure anisotropy - pkg->AddField("cons.dP", m_con); - pkg->AddField("prims.dP", m_prim); + if (viscosity) { + pkg->AddField("cons.dP", m_con); + pkg->AddField("prims.dP", m_prim); + } // If we want to register an EMHD-specific UtoP for some reason? // Likely we'll only use the post-step summary hook @@ -212,30 +223,33 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) DLOOP2 div_ucon += G.gcon(Loci::center, j, i, mu, nu) * grad_ucov[mu][nu]; // Compute+add explicit source terms (conduction and viscosity) - const Real& rho = P(b)(m_p.RHO, k, j, i); - const Real& qtilde = P(b)(m_p.Q, k, j, i); - const Real& dPtilde = P(b)(m_p.DP, k, j, i); - - Real q0 = 0; - DLOOP1 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * grad_Theta[mu]; - DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * theta_s(b, k, j, i) * D.ucon[nu] * grad_ucov[nu][mu]; - - Real dP0 = -rho * nu_e * div_ucon; - DLOOP2 dP0 += 3. * rho * nu_e * (D.bcon[mu] * D.bcon[nu] / bsq) * grad_ucov[mu][nu]; - - Real q0_tilde = q0; - Real dP0_tilde = dP0; - if (emhd_params.higher_order_terms) { - q0_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho * pow(theta_s(b, k, j, i), 2)) ) : 0.; - dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * theta_s(b, k, j, i)) ) : 0.; + const Real& rho = P(b)(m_p.RHO, k, j, i); + + if (emhd_params.conduction) { + const Real& qtilde = P(b)(m_p.Q, k, j, i); + Real q0 = 0; + DLOOP1 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * grad_Theta[mu]; + DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * theta_s(b, k, j, i) * D.ucon[nu] * grad_ucov[nu][mu]; + Real q0_tilde = q0; + if (emhd_params.higher_order_terms) + q0_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho * pow(theta_s(b, k, j, i), 2)) ) : 0.; + + dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; + if (emhd_params.higher_order_terms) + dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; } - dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; - dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * dP0_tilde / tau; - - if (emhd_params.higher_order_terms) { - dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; - dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; + if (emhd_params.viscosity) { + const Real& dPtilde = P(b)(m_p.DP, k, j, i); + Real dP0 = -rho * nu_e * div_ucon; + DLOOP2 dP0 += 3. * rho * nu_e * (D.bcon[mu] * D.bcon[nu] / bsq) * grad_ucov[mu][nu]; + Real dP0_tilde = dP0; + if (emhd_params.higher_order_terms) + dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * theta_s(b, k, j, i)) ) : 0.; + + dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * dP0_tilde / tau; + if (emhd_params.higher_order_terms) + dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; } } ); diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index d5b04947..973aea1c 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -58,6 +58,10 @@ class EMHD_parameters { bool feedback; ClosureType type; Real tau; + + bool conduction; + bool viscosity; + Real conduction_alpha; Real viscosity_alpha; @@ -91,24 +95,30 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& if (emhd_params.type == ClosureType::constant) { // Set tau, nu, chi to constants - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha; - nu_e = emhd_params.viscosity_alpha; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha; } else if (emhd_params.type == ClosureType::soundspeed) { // Set tau=const, chi/nu prop. to sound speed squared Real cs2 = (gam * (gam - 1.) * P(m_p.UU)) / (P(m_p.RHO) + (gam * P(m_p.UU))); - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha * cs2 * tau; - nu_e = emhd_params.viscosity_alpha * cs2 * tau; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha * cs2 * tau; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha * cs2 * tau; } else if (emhd_params.type == ClosureType::kappa_eta){ // Set tau = const, chi = kappa / rho, nu = eta / rho - tau = emhd_params.tau; - chi_e = emhd_params.kappa / m::max(P(m_p.RHO), SMALL); - nu_e = emhd_params.eta / m::max(P(m_p.RHO), SMALL); + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.kappa / m::max(P(m_p.RHO), SMALL); + if (emhd_params.viscosity) + nu_e = emhd_params.eta / m::max(P(m_p.RHO), SMALL); } else if (emhd_params.type == ClosureType::torus) { FourVectors Dtmp; @@ -133,40 +143,46 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& Real f_fmin = 0.; // Correction due to heat conduction - Real q = P(m_p.Q); - if (emhd_params.higher_order_terms) - q *= sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * m::pow(cs, 2.) * m::pow(Theta, 2.)); - Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * m::pow(cs, 3.); - Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); + if (emhd_params.conduction) { + Real q = P(m_p.Q); + if (emhd_params.higher_order_terms) + q *= sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * m::pow(cs, 2.) * m::pow(Theta, 2.)); + Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * m::pow(cs, 3.); + Real q_ratio = fabs(q) / q_max; + inv_exp_g = exp(-(q_ratio - 1.) / lambda); + f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + + tau = m::min(tau, f_fmin * tau_dyn); + } // Correction due to pressure anisotropy - Real dP = P(m_p.DP); - if (emhd_params.higher_order_terms) - dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * m::pow(cs, 2.) * Theta); - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); - Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; - - Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); - inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); + if (emhd_params.viscosity) { + Real dP = P(m_p.DP); + if (emhd_params.higher_order_terms) + dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * m::pow(cs, 2.) * Theta); + Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); + Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); + Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); + + Real dP_max = 0.; + if (dP > 0.) + dP_max = dP_plus; + else + dP_max = dP_minus; + + Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); + inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); + f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + + tau = m::min(tau, f_fmin * tau_dyn); + } // Update thermal diffusivity and kinematic viscosity Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + if (emhd_params.conduction) + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; + if (emhd_params.viscosity) + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; } // else yell? } @@ -179,29 +195,35 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& if (emhd_params.type == ClosureType::constant) { // Set tau, nu, chi to constants // So far none of our problems use this. Also, the expressions are not quite right based on dimensional analysis. - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha; - nu_e = emhd_params.viscosity_alpha; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha; + } else if (emhd_params.type == ClosureType::soundspeed) { // Set tau=const, chi/nu prop. to sound speed squared const Real cs2 = (gam * (gam - 1.) * P(m_p.UU, k, j, i)) / (P(m_p.RHO, k, j, i) + (gam * P(m_p.UU, k, j, i))); - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha * cs2 * tau; - nu_e = emhd_params.viscosity_alpha * cs2 * tau; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha * cs2 * tau; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha * cs2 * tau; + } else if (emhd_params.type == ClosureType::kappa_eta){ // Set tau = const, chi = kappa / rho, nu = eta / rho - tau = emhd_params.tau; - chi_e = emhd_params.kappa / m::max(P(m_p.RHO, k, j, i), SMALL); - nu_e = emhd_params.eta / m::max(P(m_p.RHO, k, j, i), SMALL); + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.kappa / m::max(P(m_p.RHO, k, j, i), SMALL); + if (emhd_params.viscosity) + nu_e = emhd_params.eta / m::max(P(m_p.RHO, k, j, i), SMALL); } else if (emhd_params.type == ClosureType::torus) { - Real rho = P(m_p.RHO, k, j, i); - Real uu = P(m_p.UU, k, j, i); - Real qtilde = P(m_p.Q, k, j, i); - Real dPtilde = P(m_p.DP, k, j, i); + Real rho = P(m_p.RHO, k, j, i); + Real uu = P(m_p.UU, k, j, i); FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); @@ -225,40 +247,48 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& Real f_fmin = 0.; // Correction due to heat conduction - Real q = qtilde; - if (emhd_params.higher_order_terms) - q *= (rho * emhd_params.conduction_alpha * pow(cs, 2.) * pow(Theta, 2.)); - Real q_max = emhd_params.conduction_alpha * rho * pow(cs, 3.); - Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); + if (emhd_params.conduction) { + Real qtilde = P(m_p.Q, k, j, i); + Real q = qtilde; + if (emhd_params.higher_order_terms) + q *= (rho * emhd_params.conduction_alpha * pow(cs, 2.) * pow(Theta, 2.)); + Real q_max = emhd_params.conduction_alpha * rho * pow(cs, 3.); + Real q_ratio = fabs(q) / q_max; + inv_exp_g = exp(-(q_ratio - 1.) / lambda); + f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + + tau = m::min(tau, f_fmin * tau_dyn); + } // Correction due to pressure anisotropy - Real dP = dPtilde; - if (emhd_params.higher_order_terms) - dP *= sqrt(rho * emhd_params.viscosity_alpha * pow(cs, 2.) * Theta); - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); - Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; - - Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); - inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); + if (emhd_params.viscosity) { + Real dPtilde = P(m_p.DP, k, j, i); + Real dP = dPtilde; + if (emhd_params.higher_order_terms) + dP *= sqrt(rho * emhd_params.viscosity_alpha * pow(cs, 2.) * Theta); + Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); + Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); + Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); + + Real dP_max = 0.; + if (dP > 0.) + dP_max = dP_plus; + else + dP_max = dP_minus; + + Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); + inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); + f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + + tau = m::min(tau, f_fmin * tau_dyn); + } // Update thermal diffusivity and kinematic viscosity Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + if (emhd_params.conduction) + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; + if (emhd_params.viscosity) + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; } // else yell? } @@ -270,22 +300,28 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Real& r { if (emhd_params.type == ClosureType::constant) { // Set tau, nu, chi to constants - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha; - nu_e = emhd_params.viscosity_alpha; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha; } else if (emhd_params.type == ClosureType::soundspeed) { // Set tau=const, chi/nu prop. to sound speed squared const Real cs2 = (gam * (gam - 1.) * u) / (rho + (gam * u)); - tau = emhd_params.tau; - chi_e = emhd_params.conduction_alpha * cs2 * tau; - nu_e = emhd_params.viscosity_alpha * cs2 * tau; + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.conduction_alpha * cs2 * tau; + if (emhd_params.viscosity) + nu_e = emhd_params.viscosity_alpha * cs2 * tau; } else if (emhd_params.type == ClosureType::kappa_eta){ // Set tau = const, chi = kappa / rho, nu = eta / rho - tau = emhd_params.tau; - chi_e = emhd_params.kappa / m::max(rho, SMALL); - nu_e = emhd_params.eta / m::max(rho, SMALL); + tau = emhd_params.tau; + if (emhd_params.conduction) + chi_e = emhd_params.kappa / m::max(rho, SMALL); + if (emhd_params.viscosity) + nu_e = emhd_params.eta / m::max(rho, SMALL); } // else yell? } @@ -307,20 +343,15 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Real& rho, const Real& u, const Re const Real eta = pgas + rho + u + bsq; const Real ptot = pgas + 0.5 * bsq; - if (!emhd_params.feedback) { - DLOOP1 { - emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] - + ptot * (dir == mu) - - D.bcon[dir] * D.bcov[mu]; - } - } else { - DLOOP1 { - emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] - + ptot * (dir == mu) - - D.bcon[dir] * D.bcov[mu] - + (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])) - - dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); - } + DLOOP1 emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + ptot * (dir == mu) - D.bcon[dir] * D.bcov[mu]; + + if (emhd_params.feedback) { + if (emhd_params.conduction) + DLOOP1 + emhd[mu] += (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])); + if (emhd_params.viscosity) + DLOOP1 + emhd[mu] -= dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); } } @@ -330,16 +361,23 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea const Real& rho, const Real& Theta, const Real& cs2, const EMHD_parameters& emhd_params, Real& q, Real& dP) { - q = q_tilde; - dP = dP_tilde; - - if (emhd_params.higher_order_terms) { - if (emhd_params.type == ClosureType::kappa_eta) { - q *= m::sqrt(emhd_params.kappa * m::pow(Theta, 2) / emhd_params.tau); - dP *= m::sqrt(emhd_params.eta * Theta / emhd_params.tau); - } else { - q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); - dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); + if (emhd_params.conduction) { + q = q_tilde; + if (emhd_params.higher_order_terms) { + if (emhd_params.type == ClosureType::kappa_eta) + q *= m::sqrt(emhd_params.kappa * m::pow(Theta, 2) / emhd_params.tau); + else + q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); + } + } + + if (emhd_params.viscosity) { + dP = dP_tilde; + if (emhd_params.higher_order_terms) { + if (emhd_params.type == ClosureType::kappa_eta) + dP *= m::sqrt(emhd_params.eta * Theta / emhd_params.tau); + else + dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); } } } diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index a39e11b6..ccc4dac6 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -58,8 +58,10 @@ KOKKOS_INLINE_FUNCTION void implicit_sources(const GRCoordinates& G, const Local // These are intentionally the tilde versions! Real tau, chi_e, nu_e; EMHD::set_parameters(G, P_tau, m_p, emhd_params_tau, gam, k, j, i, tau, chi_e, nu_e); - dUq = -G.gdet(Loci::center, j, i) * (P(m_p.Q) / tau); - dUdP = -G.gdet(Loci::center, j, i) * (P(m_p.DP) / tau); + if (emhd_params_tau.conduction) + dUq = -G.gdet(Loci::center, j, i) * (P(m_p.Q) / tau); + if (emhd_params_tau.viscosity) + dUdP = -G.gdet(Loci::center, j, i) * (P(m_p.DP) / tau); } /** @@ -101,29 +103,32 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons // TEMPORAL SOURCE TERMS const Real& rho = P(m_p.RHO); - const Real& qtilde = P(m_p.Q); - const Real& dPtilde = P(m_p.DP); const Real& Theta = (gam-1) * P(m_p.UU) / P(m_p.RHO); - Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; - DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; - - Real dP0 = -rho * nu_e * div_ucon; - DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; - - Real q0_tilde = q0; - Real dP0_tilde = dP0; - if (emhd_params.higher_order_terms) { - q0_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho * pow(Theta, 2)) ) : 0.; - dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * Theta) ) : 0.; + if (emhd_params.conduction) { + const Real& qtilde = P(m_p.Q); + Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; + DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; + Real q0_tilde = q0; + if (emhd_params.higher_order_terms) + q0_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho * pow(Theta, 2)) ) : 0.; + + dUq = G.gdet(Loci::center, j, i) * (q0_tilde / tau); + if (emhd_params.higher_order_terms) + dUq += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; } - dUq = G.gdet(Loci::center, j, i) * (q0_tilde / tau); - dUdP = G.gdet(Loci::center, j, i) * (dP0_tilde / tau); - - if (emhd_params.higher_order_terms) { - dUq += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; - dUdP += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; + if (emhd_params.viscosity) { + const Real& dPtilde = P(m_p.DP); + Real dP0 = -rho * nu_e * div_ucon; + DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; + Real dP0_tilde = dP0; + if (emhd_params.higher_order_terms) + dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * Theta) ) : 0.; + + dUdP = G.gdet(Loci::center, j, i) * (dP0_tilde / tau); + if (emhd_params.higher_order_terms) + dUdP += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; } } diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index a612c513..3ad83c58 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -148,11 +148,6 @@ std::shared_ptr Initialize(ParameterInput *pin) // Similar to fflag - will register zones where limits on q and dP are hit pkg->AddField("eflag", m); - // bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); - // if (do_emhd && enable_emhd_limits) { - // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - // pkg->AddField("eflag", m); - // } // Floors should be applied to primitive ("Derived") variables just after they are calculated. pkg->PostFillDerivedBlock = Floors::PostFillDerivedBlock; diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index bffdf90d..5e059dba 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -532,8 +532,11 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Real rho = P(m_p.RHO, k, j, i); Real uu = P(m_p.UU, k, j, i); - Real qtilde = P(m_p.Q, k, j, i); - Real dPtilde = P(m_p.DP, k, j, i); + Real qtilde, dPtilde; + if (emhd_params.conduction) + qtilde = P(m_p.Q, k, j, i); + if (emhd_params.viscosity) + dPtilde = P(m_p.DP, k, j, i); Real pg = (gam - 1.) * uu; Real Theta = pg / rho; @@ -549,62 +552,34 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons Real q, dP; EMHD::convert_prims_to_q_dP(qtilde, dPtilde, rho, Theta, cs*cs, emhd_params, q, dP); - // #if TRACE - // if (i == iPRINT && j == jPRINT && k == kPRINT) { - // std::cerr << "\nInstability limits check (INIT)\n"; - // std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " m_p.q, m_p.dP: " << qtilde << " " << dPtilde - // << " q, dP: " << q << " " << dP << "\n"; - // } - // #endif - - //EDIT - // if (i == 100 && j == 5 && k == 0) { - // std::cerr << "\nInstability limits check (INIT)\n"; - // std::cerr << "tau, chi, nu: " << tau << " " << chi_e << " " << nu_e << " bsq: " << bsq << " pg: " << pg << - // " m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q, dP: " << q << " " << dP << "\n"; - // } - - - Real qmax = 1.07 * rho * m::pow(cs, 3.); - Real max_frac = m::max(m::abs(q) / qmax, 1.); - if (fabs(q) / qmax > 1.) - eflag |= HIT_Q_LIMIT; - - P(m_p.Q, k, j, i) = P(m_p.Q, k, j, i) / max_frac; - - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(1.07 * 0.5 * bsq * dP_comp_ratio, 1.49 * pg); - Real dP_minus = m::max(-1.07 * bsq, -2.99 * pg); - - if (dP > 0. && (dP / dP_plus > 1.)) - eflag |= HIT_DP_LIMIT; - else if (dP < 0. && (dP / dP_minus > 1.)) - eflag |= HIT_DP_LIMIT; - - if (dP > 0.) - P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_plus, 1.)); - else - P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_minus, 1.)); - Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + if (emhd_params.conduction) { + Real qmax = 1.07 * rho * m::pow(cs, 3.); + Real max_frac = m::max(m::abs(q) / qmax, 1.); + if (fabs(q) / qmax > 1.) + eflag |= HIT_Q_LIMIT; + + P(m_p.Q, k, j, i) = P(m_p.Q, k, j, i) / max_frac; + } + + if (emhd_params.viscosity) { + + Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); + Real dP_plus = m::min(1.07 * 0.5 * bsq * dP_comp_ratio, 1.49 * pg); + Real dP_minus = m::max(-1.07 * bsq, -2.99 * pg); - // #if TRACE - // if (i == iPRINT && j == jPRINT && k == kPRINT) { - // std::cerr << "Instability limits check (FINAL)\n"; - // std::cerr << "m_p.q, m_p.dP: " << qtilde << " " << dPtilde << " q/qmax: " << q / qmax << " dP/dP_mirror: " - // << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; - // std::cerr << "eflag: " << eflag << "\n"; - // } - // #endif - - //EDIT - // if (i == 100 && j == 5 && k == 0) { - // std::cerr << "Instability limits check (FINAL)\n"; - // std::cerr << "m_p.q, m_p.dP: " << P(m_p.Q, k, j, i) << " " << P(m_p.DP, k, j, i) << " q/qmax: " << q / qmax << " dP/dP_mirror: " - // << dP / dP_plus << " dP/dP_firehose: " << dP / dP_minus << "\n"; - // std::cerr << "P_par / P_perp: " << dP_comp_ratio << " dP_plus: " << dP_plus << " dP_minus: " << dP_minus << "\n"; - // std::cerr << "eflag: " << eflag << "\n"; - // } + if (dP > 0. && (dP / dP_plus > 1.)) + eflag |= HIT_DP_LIMIT; + else if (dP < 0. && (dP / dP_minus > 1.)) + eflag |= HIT_DP_LIMIT; + + if (dP > 0.) + P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_plus, 1.)); + else + P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_minus, 1.)); + } + + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); return eflag; diff --git a/kharma/flux_functions.hpp b/kharma/flux_functions.hpp index 9ae62dd7..2fde9e9c 100644 --- a/kharma/flux_functions.hpp +++ b/kharma/flux_functions.hpp @@ -53,12 +53,17 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& dir, Real T[GR_DIM]) { - if (m_p.Q >= 0) { + if (emhd_params.conduction || emhd_params.viscosity) { // Apply higher-order terms conversion if necessary Real q, dP; + Real qtilde, dPtilde; + if (emhd_params.conduction) + qtilde = P(m_p.Q); + if (emhd_params.viscosity) + dPtilde = P(m_p.DP); const Real Theta = (gam - 1) * P(m_p.UU) / P(m_p.RHO); const Real cs2 = gam * (gam - 1) * P(m_p.UU) / (P(m_p.RHO) + gam * P(m_p.UU)); - EMHD::convert_prims_to_q_dP(P(m_p.Q), P(m_p.DP), P(m_p.RHO), Theta, cs2, emhd_params, q, dP); + EMHD::convert_prims_to_q_dP(qtilde, dPtilde, P(m_p.RHO), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor EMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), emhd_params, q, dP, D, dir, T); @@ -77,13 +82,18 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, const int& k, const int& j, const int& i, const int& dir, Real T[GR_DIM]) { - if (m_p.Q >= 0) { + if (emhd_params.conduction || emhd_params.viscosity) { // Apply higher-order terms conversion if necessary Real q, dP; + Real qtilde, dPtilde; + if (emhd_params.conduction) + qtilde = P(m_p.Q, k, j, i); + if (emhd_params.viscosity) + dPtilde = P(m_p.DP, k, j, i); const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); - EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); + EMHD::convert_prims_to_q_dP(qtilde, dPtilde, P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), emhd_params, q, dP, D, dir, T); @@ -171,10 +181,10 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, } // EMHD Variables: advect like rho - if (m_p.Q >= 0) { + if (emhd_params.conduction) flux(m_u.Q) = P(m_p.Q) * D.ucon[dir] * gdet; + if (emhd_params.viscosity) flux(m_u.DP) = P(m_p.DP) * D.ucon[dir] * gdet; - } // Electrons: normalized by density if (m_p.KTOT >= 0) { @@ -206,13 +216,19 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - if (m_p.Q >= 0) { + if (emhd_params.conduction || emhd_params.viscosity) { // Apply higher-order terms conversion if necessary Real q, dP; + Real qtilde, dPtilde; + if (emhd_params.conduction) + qtilde = P(m_p.Q, k, j, i); + if (emhd_params.viscosity) + dPtilde = P(m_p.DP, k, j, i); + const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); - EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); + EMHD::convert_prims_to_q_dP(qtilde, dPtilde, P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), emhd_params, q, dP, D, dir, T); @@ -223,7 +239,6 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P // GRHD stress-energy tensor w/ first index up, second index down GRHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); } - // if (i == 11 && j == 11) printf("mhd: %6.5e %6.5e %6.5e %6.5e %6.5e\n", flux(m_u.RHO), T[0], T[1], T[2], T[3]); flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); flux(m_u.U1, k, j, i) = T[1] * gdet; flux(m_u.U2, k, j, i) = T[2] * gdet; @@ -253,10 +268,10 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P } // EMHD Variables: advect like rho - if (m_p.Q >= 0) { + if (emhd_params.conduction) flux(m_u.Q, k, j, i) = P(m_p.Q, k, j, i) * D.ucon[dir] * gdet; + if (emhd_params.viscosity) flux(m_u.DP, k, j, i) = P(m_p.DP, k, j, i) * D.ucon[dir] * gdet; - } // Electrons: normalized by density if (m_p.KTOT >= 0) { @@ -288,7 +303,6 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Local& P, const FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, j, i, loc, Dtmp); // TODO switch GRHD/GRMHD? prim_to_flux(G, P, m_p, Dtmp, emhd_params, gam, j, i, 0, U, m_u, loc); - // printf("%d %d %6.5e %6.5e\n", i, j, P(m_p.Q), P(m_p.DP)); } template @@ -316,7 +330,7 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real ef = P(m.RHO) + gam * P(m.UU); const Real cs2 = gam * (gam - 1) * P(m.UU) / ef; Real cms2; - if (m.Q > 0) { + if (emhd_params.conduction || emhd_params.viscosity) { // Get the EGRMHD parameters Real tau, chi_e, nu_e; EMHD::set_parameters(G, P, m, emhd_params, gam, k, j, i, tau, chi_e, nu_e); @@ -326,8 +340,13 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real ee = bsq + ef; const Real va2 = bsq / ee; - const Real cvis2 = (4./3.) / (P(m.RHO) + (gam * P(m.UU)) ) * P(m.RHO) * emhd_params.viscosity_alpha * cs2; - const Real ccond2 = (gam - 1.) * emhd_params.conduction_alpha * cs2; + Real ccond2 = 0.; + Real cvis2 = 0.; + + if (emhd_params.conduction) + ccond2 = (gam - 1.) * emhd_params.conduction_alpha * cs2; + if (emhd_params.viscosity) + cvis2 = (4./3.) / (P(m.RHO) + (gam * P(m.UU)) ) * P(m.RHO) * emhd_params.viscosity_alpha * cs2; const Real cscond = 0.5*(cs2 + ccond2 + sqrt(cs2*cs2 + ccond2*ccond2) ) ; const Real cs2_emhd = cscond + cvis2; diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index 221e7070..a846af1f 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -146,26 +146,6 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { KOKKOS_LAMBDA_3D { if (( solve_fail(k, j, i)) == SolverStatus::fail) Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); - - //EDIT - // if (i == 160 && j == 120 && k == 0) { - // const Real Theta = (gam - 1) * P_all(m_p.UU, k, j, i) / P_all(m_p.RHO, k, j, i); - // const Real cs2 = gam * (gam - 1) * P_all(m_p.UU, k, j, i) / (P_all(m_p.RHO, k, j, i) + gam * P_all(m_p.UU, k, j, i)); - // std::cerr << "\nCHECK CONSISTENCY\n"; - // std::cerr << "phi, psi, rho, Theta, cs2: " << emhd_params.conduction_alpha << " " << - // emhd_params.viscosity_alpha << " " << P_all(m_p.RHO, k, j, i) << " " << Theta << " " << cs2 << "\n"; - // std::cerr << "qtilde, dPtilde: " << P_all(m_p.Q, k, j, i) << " " << P_all(m_p.DP, k, j, i) << "\n"; - - // Real q, dP; - // Real tau, chi_e, nu_e; - // EMHD::set_parameters(G, P_all, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e, "consistency_check"); - // q = P_all(m_p.Q, k, j, i) * m::sqrt(chi_e * P_all(m_p.RHO, k, j, i) * m::pow(Theta, 2) / tau); - // dP = P_all(m_p.DP, k, j, i) * m::sqrt(nu_e * P_all(m_p.RHO, k, j, i) * Theta / tau); - // std::cerr << "q, dP (from closure parameters): " << q << " " << dP << "\n"; - - // EMHD::convert_prims_to_q_dP(P_all(m_p.Q, k, j, i), P_all(m_p.DP, k, j, i), P_all(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); - // std::cerr << "q, dP (from closure scheme): " << q << " " << dP << "\n\n"; - // } } ); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index b8b28912..d2a5cbae 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -111,51 +111,6 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin) m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariabl - // TODO: Find a way to save residuals based on a runtime parameter. We don't want to unnecessarily allocate - // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. - - // Should the solve save the residual vector field? Useful for debugging purposes. Default is NO. - // bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); - // params.Add("save_residual", save_residual); - - // Vector field to store residual components (only for those variables that are evolved implicitly) - // if (save_residual) { - // auto driver_type = pin->GetString("driver", "type"); - // bool grmhd_implicit = (driver_type == "imex") && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); - // bool implicit_b = (driver_type == "imex") && (pin->GetOrAddBoolean("b_field", "implicit", grmhd_implicit)); - // bool emhd_enabled = pin->GetOrAddBoolean("emhd", "on", false); - // int nvars_implicit = 0; - // if (grmhd_implicit){ - // if (emhd_enabled) { - // if (implicit_b) { - // nvars_implicit = 10; - // } - // else - // nvars_implicit = 7; - // } else { - // if (implicit_b) { - // nvars_implicit = 8; - // } - // else - // nvars_implicit = 6; - // } - // } - // const int nfvar = nvars_implicit; - - // // flags_vec = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - // // auto flags_vec(flags_vec); - // // flags_vec.push_back(Metadata::Vector); - // std::vector s_vector({nfvar}); - // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); - // pkg->AddField("residual", m); - // } - - - // Anything we need to run from this package on callbacks - // Maybe a post-step L2 or flag count or similar - // pkg->PostFillDerivedBlock = Implicit::PostFillDerivedBlock; - // pkg->PostStepDiagnosticsMesh = Implicit::PostStepDiagnostics; - Flag("Initialized"); return pkg; } @@ -256,11 +211,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Pull fields associated with the solver's performance auto& solve_norm_all = md_solver->PackVariables(std::vector{"solve_norm"}); auto& solve_fail_all = md_solver->PackVariables(std::vector{"solve_fail"}); - // auto& solve_fail_all = md_solver->GetBlockData(0)->Get("solve_fail").data; - - // if (save_residual) { - // auto& residual_all = md_solver->GetBlockData(0)->Get("residual").data; - // } auto bounds = pmb_sub_step_init->cellbounds; const int n1 = bounds.ncellsi(IndexDomain::entire); @@ -387,18 +337,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } member.team_barrier(); - // Copy in the guess or current solution - // Note this replaces the implicit portion of P_solver_s -- - // any explicit portion was initialized above - // FLOOP { // Loop over just the implicit "fluid" portion of primitive vars - // parthenon::par_for_inner(member, ib.s, ib.e, - // [&](const int& i) { - // P_solver_s(i, ip) = P_solver_all(b)(ip, k, j, i); - // } - // ); - // } - // member.team_barrier(); - parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { // Lots of slicing. This still ends up faster & cleaner than alternatives I tried @@ -431,9 +369,14 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Now that we know that it isn't a bad zone, reset solve_fail for this iteration solve_fail() = SolverStatus::converged; - if (m_p.Q >= 0) { + if (emhd_params_sub_step_init.conduction || emhd_params_sub_step_init.viscosity) { + Real dUq, dUdP; EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, - emhd_params_sub_step_init, dU_implicit(m_u.Q), dU_implicit(m_u.DP)); + emhd_params_sub_step_init, dUq, dUdP); + if (emhd_params_sub_step_init.conduction) + dU_implicit(m_u.Q) = dUq; + if (emhd_params_sub_step_init.viscosity) + dU_implicit(m_u.DP) = dUdP; } // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver @@ -449,30 +392,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); -// #if TRACE -// if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { -// std::cerr << "Variable ordering: rho " << int(m_p.RHO) << " uu " << int(m_p.UU) << " U1 " << int(m_p.U1) -// << " B1 " << int(m_p.B1) << " q " << int(m_p.Q) << " dP " << int(m_p.DP) << std::endl; -// std::cerr << "Variable ordering: rho " << int(m_u.RHO) << " uu " << int(m_u.UU) << " U1 " << int(m_u.U1) -// << " B1 " << int(m_u.B1) << " q " << int(m_u.Q) << " dP " << int(m_u.DP) << std::endl; -// std::cerr << "P_solver: "; -// PLOOP {std::cerr << P_solver(ip) << " ";} std::cerr << std::endl; -// std::cerr << "Pi: "; -// PLOOP {std::cerr << P_full_step_init(ip) << " ";} std::cerr << std::endl; -// std::cerr << "Ui: "; -// PLOOP {std::cerr << U_full_step_init(ip) << " ";} std::cerr << std::endl; -// std::cerr << "Ps: "; -// PLOOP {std::cerr << P_sub_step_init(ip) << " ";} std::cerr << std::endl; -// std::cerr << "Us: "; -// PLOOP {std::cerr << U_sub_step_init(ip) << " ";} std::cerr << std::endl; -// std::cerr << "dUdt: "; -// PLOOP {std::cerr << dU_implicit(ip) << " ";} std::cerr << std::endl; -// std::cerr << "Initial Jacobian:" << std::endl; -// for (int jp=0; jp *md_full_step_init, MeshData *md_ if (solve_fail() != SolverStatus::fail) { #endif -// #if TRACE -// if (am_rank0 && b == 0 && i == iPRINT && j == jPRINT && k == kPRINT) { -// std::cerr << "Final delta_prim: "; FLOOP std::cerr << delta_prim(ip) << " "; std::cerr << std::endl; -// std::cerr<< std::endl; -// } -// #endif // Check for positive definite values of density and internal energy. // Ignore zone if manual backtracking is not sufficient. @@ -609,14 +522,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); - // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { - // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", - // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); - // printf("Final residual: "); PLOOP printf("%6.5e ", residual(ip)); printf("\n"); - // printf("Final delta_prim: "); PLOOP printf("%6.5e ", delta_prim(ip)); printf("\n"); - // printf("Final P_solver: "); PLOOP printf("%6.5e ", P_solver(ip)); printf("\n"); - // } - // Store for maximum/output // I would be tempted to store the whole residual, but it's of variable size solve_norm() = 0; @@ -640,9 +545,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { P_solver_all(b)(ip, k, j, i) = P_solver_s(i, ip); - // if (save_residual) { - // residual_all(b, ip, k, j, i) = residual_s(i, ip); - // } } ); } @@ -679,7 +581,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } , sum_reducer); // Then MPI reduce AllReduce to copy the global max to every rank - AllReduce nfails_tot; + static AllReduce nfails_tot; nfails_tot.val = nfails; nfails_tot.StartReduce(MPI_SUM); while (nfails_tot.CheckReduce() == TaskStatus::incomplete); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index bd72410f..cbf8a2f8 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -107,36 +107,39 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P // (U_test - Ui)/dt - dudt_explicit ... FLOOP residual(ip) = (tmp(ip) - Ui(ip)) / dt - dudt_explicit(ip); - if (m_p.Q >= 0) { + if (emhd_params.conduction || emhd_params.viscosity) { // Compute new implicit source terms and time derivative source terms Real dUq, dUdP; // Don't need full array for these EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_s, dUq, dUdP); // dU_new // ... - 0.5*(dU_new(ip) + dUi(ip)) ... - residual(m_u.Q) -= 0.5*(dUq + dUi(m_u.Q)); - residual(m_u.DP) -= 0.5*(dUdP + dUi(m_u.DP)); - // if (i == 11 && j == 11) { - // printf("Implicit sources: "); printf("%6.5e %6.5e", dUq - dUi(m_u.Q), dUdP - dUi(m_u.DP)); printf("\n"); - // } + if (emhd_params.conduction) + residual(m_u.Q) -= 0.5*(dUq + dUi(m_u.Q)); + if (emhd_params.viscosity) + residual(m_u.DP) -= 0.5*(dUdP + dUi(m_u.DP)); + EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params_s, gam, dt, k, j, i, dUq, dUdP); // dU_time // ... - dU_time(ip) - residual(m_u.Q) -= dUq; - residual(m_u.DP) -= dUdP; - // if (i == 11 && j == 11) { - // printf("Time derivative sources: "); printf("%6.5e %6.5e", dUq, dUdP); printf("\n"); - // } + if (emhd_params.conduction) + residual(m_u.Q) -= dUq; + if (emhd_params.viscosity) + residual(m_u.DP) -= dUdP; // Normalize Real tau, chi_e, nu_e; EMHD::set_parameters(G, Ps, m_p, emhd_params_s, gam, k, j, i, tau, chi_e, nu_e); - residual(m_u.Q) *= tau; - residual(m_u.DP) *= tau; + if (emhd_params.conduction) + residual(m_u.Q) *= tau; + if (emhd_params.viscosity) + residual(m_u.DP) *= tau; if (emhd_params.higher_order_terms){ Real rho = Ps(m_p.RHO); Real uu = Ps(m_p.UU); Real Theta = (gam - 1.) * uu / rho; - residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; - residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; + if (emhd_params.conduction) + residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; + if (emhd_params.viscosity) + residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; } } diff --git a/kharma/prob/emhd/fm_torus_emhd.cpp b/kharma/prob/emhd/fm_torus_emhd.cpp index f0041de9..0022ae2f 100644 --- a/kharma/prob/emhd/fm_torus_emhd.cpp +++ b/kharma/prob/emhd/fm_torus_emhd.cpp @@ -53,9 +53,16 @@ TaskStatus InitializeFMTorusEMHD(MeshBlockData *rc, ParameterInput *pin) // This problem init is exclusively for the EMHD torus; get copies of q and dP const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", true); - GridVector q = rc->Get("prims.q").data; - GridVector dP = rc->Get("prims.dP").data; - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); + const bool conduction = pmb->packages.Get("EMHD")->Param("conduction"); + const bool viscosity = pmb->packages.Get("EMHD")->Param("viscosity"); + + // Proxy initializations + auto q = rho; + auto dP = rho; + if (conduction) + q = rc->Get("prims.q").data; + if (viscosity) + dP = rc->Get("prims.dP").data; const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); @@ -149,8 +156,10 @@ TaskStatus InitializeFMTorusEMHD(MeshBlockData *rc, ParameterInput *pin) uvec(1, k, j, i) = u_prim[1]; uvec(2, k, j, i) = u_prim[2]; // EMHD variables - q(k, j, i) = 0.; - dP(k, j, i) = 0.; + if (conduction) + q(k, j, i) = 0.; + if (viscosity) + dP(k, j, i) = 0.; } } ); diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index e620bb74..0e1bfe1f 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -40,13 +40,14 @@ initial_cleanup = false type = imex +min_nonlinear_iter = 1 max_nonlinear_iter = 3 rootfind_tol = 1.e-20 jacobian_delta = 4.e-8 linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 -use_qr = false +use_qr = true # IMPORTANT: This block must be present and values filled in all EGRMHD simulations @@ -54,17 +55,20 @@ on = true higher_order_terms = true feedback = false -closure_type = kappa_eta -tau = 30. -kappa = 0.0 -eta = 0.01 +conduction = false +viscosity = true + +closure_type = kappa_eta +tau = 30. +eta = 0.01 mdot = 1.0 rs = 8.0 -disable_floors = true +disable_floors = true +enable_emhd_limits = false check_inflow_outer = false @@ -76,7 +80,7 @@ verbose = 1 file_type = hdf5 dt = 100.0 single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.dP, solve_norm, solve_fail file_type = hst diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index c2ed2c5d..f89404ae 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -68,7 +68,7 @@ rootfind_tol = 1.e-20 linesearch = true max_linesearch_iter = 3 linesearch_eps = 1.e-4 -use_qr = false +use_qr = true # General verbosity level: @@ -88,6 +88,9 @@ on = true higher_order_terms = false feedback = true +conduction = true +viscosity = true + closure_type = sound_speed tau = 1.0 conduction_alpha = 1.0 diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index fef28d82..de26b944 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -29,21 +29,22 @@ usecols=(0,1,3), unpack=True) # load code data - dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') + dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) - rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) - dP_tilde = np.squeeze(dfile['prims'][Ellipsis,9][()]) + params = dump.params + rho = np.squeeze(dump['RHO']) + uu = np.squeeze(dump['UU']) + dP_tilde = np.squeeze(dump['prims'][8,Ellipsis]) - t = dfile['t'][()] - gam = dfile['header/gam'][()] - higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') + t = dump['t'] + gam = params['gam'] + tau = params['tau'] + eta = params['eta'] + higher_order_terms = params['higher_order_terms'] # compute dP - if higher_order_terms=="TRUE": + if higher_order_terms=="true": print("Res: "+str(res)+"; higher order terms enabled") - tau = 30. - eta = 0.01 P = (gam - 1.) * uu Theta = P / rho nu_emhd = eta / rho diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index 79daa128..a5a01b31 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -12,19 +12,19 @@ conv_2d() { for res in "${RES_LIST[@]}" do # Four blocks - half=$(( $res / 2 )) + # half=$(( $res / 2 )) $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ + parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 mv bondi_viscous.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf mv bondi_viscous.out0.final.phdf emhd_2d_${res}_end_${1}.phdf done check_code=0 - pyharm-convert --double *.phdf + # pyharm-convert --double *.phdf python check.py $ALL_RES $1 2d || check_code=$? - rm -r *.phdf + # rm -r *.phdf rm -r *.xdmf rm -r *.out0* if [[ $check_code != 0 ]]; then diff --git a/tests/emhdmodes/check.py b/tests/emhdmodes/check.py index c3ab3864..59991b57 100644 --- a/tests/emhdmodes/check.py +++ b/tests/emhdmodes/check.py @@ -25,7 +25,7 @@ var0[6] = 0.3 # L1 initialization - L1 = np.zeros([len(RES), NVAR]) + L1 = np.zeros([len(RES), NVAR]) fit = np.zeros([len(RES), NVAR]) # perturbation (for 2D EMHD wave) From 56b8c1c53ae55f2e22b8fcb65372d7e0410230af Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Fri, 17 Feb 2023 17:03:00 -0500 Subject: [PATCH 031/219] Added FixX1Flux feature --- kharma/b_flux_ct/b_flux_ct.cpp | 105 ++++++++++++++++++++++++++++++++- kharma/b_flux_ct/b_flux_ct.hpp | 3 + kharma/grmhd/grmhd.cpp | 3 + 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index ac284ead..36d20cd9 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -63,6 +63,8 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // Diagnostic & inadvisable flags bool fix_flux = pin->GetOrAddBoolean("b_field", "fix_polar_flux", true); params.Add("fix_polar_flux", fix_flux); + bool fix_flux_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_x1", false); + params.Add("fix_flux_x1", fix_flux_x1); // WARNING this disables constrained transport, so the field will quickly pick up a divergence. // To use another transport, just specify it instead of this one. bool disable_flux_ct = pin->GetOrAddBoolean("b_field", "disable_flux_ct", false); @@ -107,7 +109,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack m = Metadata(flags_cons, s_vector); pkg->AddField("cons.B", m); - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::Restart, Metadata::FillGhost}); + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::Restart}); //, Metadata::FillGhost}); pkg->AddField("divB", m); // Hyerin (12/19/22) m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); @@ -307,7 +309,7 @@ TaskStatus FixPolarFlux(MeshData *md) if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { - pmb->par_for("fix_flux_b_l", ks, ke_e, js, js, is, ie+1, + pmb->par_for("fix_flux_b_l", ks-1, ke_e+1, js, js, is-1, ie+1+1, // Hyerin (12/28/22) KOKKOS_LAMBDA_3D { B_F.flux(X1DIR, V2, k, j-1, i) = -B_F.flux(X1DIR, V2, k, js, i); if (ndim > 1) B_F.flux(X2DIR, V2, k, j, i) = 0; @@ -317,7 +319,7 @@ TaskStatus FixPolarFlux(MeshData *md) } if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { - pmb->par_for("fix_flux_b_r", ks, ke_e, je_e, je_e, is, ie+1, + pmb->par_for("fix_flux_b_r", ks-1, ke_e+1, je_e, je_e, is-1, ie+1+1, // Hyerin (12/28/22) KOKKOS_LAMBDA_3D { B_F.flux(X1DIR, V2, k, j, i) = -B_F.flux(X1DIR, V2, k, je, i); if (ndim > 1) B_F.flux(X2DIR, V2, k, j, i) = 0; @@ -331,6 +333,77 @@ TaskStatus FixPolarFlux(MeshData *md) return TaskStatus::complete; } +TaskStatus FixX1Flux(MeshData *md) +{ + Flag(md, "Fixing X1 fluxes"); + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + + IndexDomain domain = IndexDomain::interior; + int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); + int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); + int js_all = pmb0->cellbounds.js(IndexDomain::entire), je_all = pmb0->cellbounds.je(IndexDomain::entire); // added by Hyerin (12/28/22) + int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); + int ks_all = pmb0->cellbounds.ks(IndexDomain::entire), ke_all = pmb0->cellbounds.ke(IndexDomain::entire); // added by Hyerin (12/28/22) + const int ndim = pmesh->ndim; + + int je_e = (ndim > 1) ? je + 1 : je; + //int je_e = (ndim > 1) ? je_all + 1 : je_all; // test Hyerin(12/28/22) + int ke_e = (ndim > 2) ? ke + 1 : ke; + //int ke_e = (ndim > 2) ? ke_all + 1 : ke_all; // test Hyerin (12/28/22) + + Real x1min = pmb0->packages.Get("GRMHD")->Param("x1min"); //Hyerin (01/31/23) + + // Assuming the fluxes through the pole are 0, + // make sure the polar EMFs are 0 when performing fluxCT + // TODO only invoke one kernel? We avoid invocation except on boundaries anyway + for (auto &pmb : pmesh->block_list) { + auto& rc = pmb->meshblock_data.Get(); + auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); + + //added by Hyerin (12/23/22) + if ((pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) && (x1min>1) ) // only apply fix flux for inner bc when it is far from the EH + { + //pmb->par_for("fix_flux_b_l", ks-1, ke_e+1, js-1, je_e+1, is, is, // test Hyerin (12/28/22) + pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // test Hyerin (12/28/22) + KOKKOS_LAMBDA_3D { + /* previous prescription to make the X1DIR flux = 0 + B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is); + if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is); + */ + // (02/06/23) a prescription that allows nonzero flux across X1 boundary but still keeps divB=0 + if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) + B_F.flux(X1DIR, V2, k, j-1, is); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) + B_F.flux(X1DIR, V3, k-1, j, is); + /* + if (k == 30 && j==30) { + printf("HYERIN: i,j,k = (%i %i %i) sum is (%g %g %g %g) \n", i, j, k, B_F.flux(X2DIR,V1,k,j,i-1), B_F.flux(X2DIR,V1,k,j,i), B_F.flux(X1DIR,V2,k,j,i), B_F.flux(X1DIR,V2,k,j-1,i)); + } + */ + } + ); + } + if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) + { + pmb->par_for("fix_flux_b_r", ks-1, ke_e+1, js-1, je_e+1, ie+1, ie+1, // test Hyerin (12/28/22) + KOKKOS_LAMBDA_3D { + /* previous prescription to make the X1DIR flux = 0 + B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie); + if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie); + */ + // (02/06/23) a prescription that allows nonzero flux across X1 boundary but still keeps divB=0 + if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); + } + ); + } + } + + Flag(md, "Fixed X1 B"); + return TaskStatus::complete; +} + TaskStatus TransportB(MeshData *md) { auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -338,6 +411,10 @@ TaskStatus TransportB(MeshData *md) && pmb0->coords.coords.spherical()) { FixPolarFlux(md); } + if (pmb0->packages.Get("B_FluxCT")->Param("fix_flux_x1") // added by Hyerin + && pmb0->coords.coords.spherical()) { + FixX1Flux(md); + } FluxCT(md); return TaskStatus::complete; } @@ -446,6 +523,7 @@ void CalcDivB(MeshData *md, std::string divb_field_name) const int je = IsDomainBound(pmb, BoundaryFace::outer_x2) ? jb.e : jb.e + 1; const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; + printf("Hyerin: for calcDivB ks is %i.\n", ks); pmb->par_for("calc_divB", ks, ke, js, je, is, ie, KOKKOS_LAMBDA_3D { @@ -474,12 +552,33 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin) const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); + // changed by Hyerin (12/21/22) + //const IndexRange ib = rc->GetBoundsI(IndexDomain::entire); + //const IndexRange jb = rc->GetBoundsJ(IndexDomain::entire); + //const IndexRange kb = rc->GetBoundsK(IndexDomain::entire); const int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; const int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; const int js = (IsDomainBound(pmb, BoundaryFace::inner_x2) && ndim > 1) ? jb.s + 1 : jb.s; const int je = (IsDomainBound(pmb, BoundaryFace::outer_x2) || ndim <=1) ? jb.e : jb.e + 1; const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; + /* + int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; + int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; + int js = (IsDomainBound(pmb, BoundaryFace::inner_x2) && ndim > 1) ? jb.s + 1 : jb.s; + int je = (IsDomainBound(pmb, BoundaryFace::outer_x2) || ndim <=1) ? jb.e : jb.e + 1; + int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; + int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; + + if (ndim > 2) { // modified by Hyerin (12/21/22), just to calculate at the ghost zone + is = ib.s + 1; + ie = ib.e; + js = jb.s + 1; + je = jb.e; + ks = kb.s + 1; + ke = kb.e; + } + */ pmb->par_for("divB_output", ks, ke, js, je, is, ie, KOKKOS_LAMBDA_3D { diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 4b666f0f..e16147a8 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -85,6 +85,9 @@ TaskStatus FluxCT(MeshData *md); */ TaskStatus FixPolarFlux(MeshData *md); +// added by Hyerin +TaskStatus FixX1Flux(MeshData *md); + /** * Task combining the above two (polar fix and FluxCT) for simplicity */ diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 7fd185c3..b36430e8 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -156,6 +156,9 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // Ensure fluxes through the zero-size face at the pole are zero bool fix_flux_pole = pin->GetOrAddBoolean("bounds", "fix_flux_pole", true); params.Add("fix_flux_pole", fix_flux_pole); + // Ensure fluxes through the zero-size face at the x1 boundary are zero + bool fix_flux_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_x1", false); + params.Add("fix_flux_x1", fix_flux_x1); // Driver options // The two current drivers are "harm" or "imex", with the former being the usual KHARMA From 13aa7dc58e9623c436c5867956bfbcc5ad507a6e Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Fri, 17 Feb 2023 17:06:13 -0500 Subject: [PATCH 032/219] patching B field improved --- kharma/prob/resize_restart_kharma.cpp | 11 ++++---- kharma/prob/resize_restart_kharma.hpp | 36 ++++++++++----------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 86f5453a..5d927dd8 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -210,7 +210,8 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co auto b_field_type = pmb->packages.Get("GRMHD")->Param("b_field_type"); const bool include_B = (b_field_type != "none"); // A placeholder to save the B fields for SeedBField - GridVector B_Save = rc->Get("B_Save").data; + GridVector B_Save; + if (include_B) B_Save = rc->Get("B_Save").data; auto& G = pmb->coords; @@ -243,7 +244,9 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co const Real fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); const Real fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); const Real dx1 = (fx1max - fx1min) / n1tot; - const Real fx1min_ghost = fx1min - 4*dx1; + const bool fghostzones = pmb->packages.Get("GRMHD")->Param("rghostzones"); + int fnghost = pmb->packages.Get("GRMHD")->Param("rnghost"); + const Real fx1min_ghost = fx1min - fnghost*dx1; PackIndexMap prims_map, cons_map; auto P = GRMHD::PackMHDPrims(rc, prims_map); auto U = GRMHD::PackMHDCons(rc, cons_map); @@ -253,8 +256,6 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co // read from a restart file and save it to static GridScalar //cout << "Hyerin: reading files" << endl; - const bool fghostzones = pmb->packages.Get("GRMHD")->Param("rghostzones"); - int fnghost = pmb->packages.Get("GRMHD")->Param("rnghost"); if (! fghostzones) fnghost=0; // reset to 0 int x3factor=1; @@ -426,7 +427,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, KOKKOS_LAMBDA_3D { get_prim_restart_kharma(G, coords, P, m_p, blcoord, kscoord, - fx1min, fx1max, should_fill, is_spherical, include_B, gam, rs, mdot, length, + fx1min, fx1max, fnghost, should_fill, is_spherical, include_B, gam, rs, mdot, length, x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, k, j, i); diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 0cfec8cb..303c3abe 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -98,7 +98,7 @@ KOKKOS_INLINE_FUNCTION void convert_to_utwiddle(const GRCoordinates& G, const Co KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, const SphBLCoords& bl, const SphKSCoords& ks, - const Real fx1min, const Real fx1max, const bool should_fill, const bool is_spherical, const bool include_B, + const Real fx1min, const Real fx1max, const Real fnghost, const bool should_fill, const bool is_spherical, const bool include_B, const Real gam, const Real rs, const Real mdot, const hsize_t length[GR_DIM], const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, const GridVector& B, const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridScalar& rho_fill, const GridScalar& u_fill, const GridVector& uvec_fill, const GridVector& B_fill, @@ -123,13 +123,22 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1]; - + // copy over smallest radius states - Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); + //Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); + itemp = fnghost; // in order to copy over the physical region, not the ghost region + // (02/08/23) instead in order to set the vacuum homogeneous instead of having theta phi dependence, set j and k values + jtemp = fnghost; + ktemp = fnghost; rho_temp = rho(iblocktemp,ktemp,jtemp,itemp); u_temp = u(iblocktemp,ktemp,jtemp,itemp); - //if (include_B) VLOOP B_prim[v] = B(v,iblocktemp,ktemp,jtemp,itemp); Real T = get_T(r, C1, C2, n, rs); + + // (02/08/23) instead in order to set the vacuum homogeneous instead of having theta phi dependence, set to the bondi radius values (assume r_B ~ r_s**2) + //Real T_temp = get_T(m::pow(rs,2), C1, C2, n, rs); + //rho_temp = m::pow(T_temp, n); + //u_temp = rho_temp * T_temp * n; + //Real T = get_T(r, C1, C2, n, rs); Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; @@ -147,8 +156,6 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons } else { Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); - //std::cout << "Hyerin: X = " << X[1] << " " << X[2]<< " " << X[3] << std::endl; - //std::cout << "Hyerin: x_interp = " << x1(iblocktemp,itemp) << " " << x2(iblock,jtemp)<< " " << x3(iblock,ktemp) < Date: Wed, 22 Feb 2023 20:28:22 -0500 Subject: [PATCH 033/219] Added ReflectX1 and updated FixX1Flux such that it actually allows nonzero flux across X1 bdry by serial operation. However it's likely we're not going to use these features --- kharma/b_flux_ct/b_flux_ct.cpp | 126 ++++++++++++++++++++++++++++----- kharma/boundaries.cpp | 117 +++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 20 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 36d20cd9..bbdd1b6e 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -262,6 +262,13 @@ TaskStatus FluxCT(MeshData *md) B_F(b).flux(X1DIR, V1, k, j, i) = 0.0; B_F(b).flux(X1DIR, V2, k, j, i) = 0.5 * (emf3(b, k, j, i) + emf3(b, k, j+1, i)); if (ndim > 2) B_F(b).flux(X1DIR, V3, k, j, i) = -0.5 * (emf2(b, k, j, i) + emf2(b, k+1, j, i)); + + /* + if (k <15 && k>13 && j>jb.s-1 && jpar_for("flux_ct_2", block.s, block.e, kb.s, kb.e, jl.s, jl.e, ib.s, ib.e, @@ -280,7 +287,7 @@ TaskStatus FluxCT(MeshData *md) } ); } - + Flag(md, "CT Finished"); return TaskStatus::complete; } @@ -351,6 +358,8 @@ TaskStatus FixX1Flux(MeshData *md) //int je_e = (ndim > 1) ? je_all + 1 : je_all; // test Hyerin(12/28/22) int ke_e = (ndim > 2) ? ke + 1 : ke; //int ke_e = (ndim > 2) ? ke_all + 1 : ke_all; // test Hyerin (12/28/22) + int js_new, je_new; // Hyerin (02/21/23) + bool in_x2, out_x2; // Hyerin Real x1min = pmb0->packages.Get("GRMHD")->Param("x1min"); //Hyerin (01/31/23) @@ -360,43 +369,127 @@ TaskStatus FixX1Flux(MeshData *md) for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); + + // update the j and k bounds (Hyerin 02/21/23) + js_new = js+1; //js-1; + je_new = je_e+1; //je_e+1; + in_x2 = false; + out_x2 = false; + if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { + in_x2 = true; + js_new = js; + } + if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { + out_x2 = true; + je_new = je_e; + } - //added by Hyerin (12/23/22) + //added by Hyerin (12/23/22) TODO: it has to ask if x2 boundary is inner_x2 or outer_x2 and update the jj bounds if ((pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) && (x1min>1) ) // only apply fix flux for inner bc when it is far from the EH - { - //pmb->par_for("fix_flux_b_l", ks-1, ke_e+1, js-1, je_e+1, is, is, // test Hyerin (12/28/22) - pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // test Hyerin (12/28/22) + { + for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { + for (int jtemp = js_new; jtemp <= je_new; jtemp++) { + pmb->par_for("fix_flux_b_l", ktemp, ktemp, jtemp, jtemp, is, is, // Hyerin (02/20/23) for 3rd prescription, sequential + //pmb->par_for("fix_flux_b_l", ks_all+2, ke_all, js_new, je_new, is, is, // Hyerin (02/20/23) for 3rd prescription + //pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // Hyerin (12/28/22) for 1st & 2nd prescription KOKKOS_LAMBDA_3D { - /* previous prescription to make the X1DIR flux = 0 + /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is); */ - // (02/06/23) a prescription that allows nonzero flux across X1 boundary but still keeps divB=0 - if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) + B_F.flux(X1DIR, V2, k, j-1, is); - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) + B_F.flux(X1DIR, V3, k-1, j, is); + // (02/06/23) 2nd prescription that allows nonzero flux across X1 boundary but still keeps divB=0 (turns out effectively to have 0 flux) + //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) + B_F.flux(X1DIR, V2, k, j-1, is); + //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) + B_F.flux(X1DIR, V3, k-1, j, is); + // + // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux + if (ndim > 1) { + B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) - B_F.flux(X1DIR, V2, k, j-2, is) + B_F.flux(X2DIR, V1, k, j-1, is) + B_F.flux(X2DIR, V1, k, j-1, is-1); + } + if (ndim > 2) { + B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) - B_F.flux(X1DIR, V3, k-2, j, is) + B_F.flux(X3DIR, V1, k-1, j, is) + B_F.flux(X3DIR, V1, k-1, j, is-1); + } + + if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) + B_F.flux(X2DIR, V1,k,j,i-1) = -B_F.flux(X1DIR,V2,k,j,i+1) -B_F.flux(X1DIR,V2,k,j-1,i+1); + B_F.flux(X2DIR, V1,k,j,i) = -0.5*B_F.flux(X2DIR,V1,k,j,i-1); + } + if (out_x2 && (j==je_e)) {// (corners are tricky) + //B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, je, is) - B_F.flux(X2DIR, V1, k, je, is-1) + // +B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je-1, is); + //B_F.flux(X2DIR, V1, k, j, i-1) = -2.*B_F.flux(X1DIR, V2, k, je-1, is) -B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je+1, is) + // +2.*B_F.flux(X2DIR, V1, k, je, is) + 2.*B_F.flux(X2DIR, V1, k, je, is-1); + B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); + B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + } + + /* - if (k == 30 && j==30) { - printf("HYERIN: i,j,k = (%i %i %i) sum is (%g %g %g %g) \n", i, j, k, B_F.flux(X2DIR,V1,k,j,i-1), B_F.flux(X2DIR,V1,k,j,i), B_F.flux(X1DIR,V2,k,j,i), B_F.flux(X1DIR,V2,k,j-1,i)); + if (k == ke_all-5 && j>js-1 && jboundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_b_r", ks-1, ke_e+1, js-1, je_e+1, ie+1, ie+1, // test Hyerin (12/28/22) + for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { + for (int jtemp = js_new; jtemp <= je_new; jtemp++) { + pmb->par_for("fix_flux_b_r", ktemp, ktemp, jtemp, jtemp, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription, sequential + //pmb->par_for("fix_flux_b_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription + //pmb->par_for("fix_flux_b_r", ks_all+1, ke_all+1, js_all+1, je_all+1, ie+1, ie+1, // Hyerin (12/28/22) for 1st & 2nd prescription KOKKOS_LAMBDA_3D { - /* previous prescription to make the X1DIR flux = 0 + /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie); */ - // (02/06/23) a prescription that allows nonzero flux across X1 boundary but still keeps divB=0 - if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); + // (02/06/23) 2nd prescription that allows nonzero flux across X1 boundary but still keeps divB=0 + //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); + //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); + // + // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux + if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, ie+1) + - B_F.flux(X1DIR, V2, k, j-2, ie+1) + B_F.flux(X2DIR, V1, k, j-1, ie) + B_F.flux(X2DIR, V1, k, j-1, ie+1); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, ie+1) + - B_F.flux(X1DIR, V3, k-2, j, ie+1) + B_F.flux(X3DIR, V1, k-1, j, ie) + B_F.flux(X3DIR, V1, k-1, j, ie+1); + + if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) + B_F.flux(X2DIR, V1,k,j,i) = -B_F.flux(X1DIR,V2,k,j,ie) -B_F.flux(X1DIR,V2,k,j-1,ie); + B_F.flux(X2DIR, V1,k,j,i-1) = -0.5*B_F.flux(X2DIR,V1,k,j,i); + } + if (out_x2 && (j==je_e)) {// (corners are tricky) + //B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, je, ie) - B_F.flux(X2DIR, V1, k, je, ie+1) + // +B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je-1, ie+1); + //B_F.flux(X2DIR, V1, k, j, i) = -2.*B_F.flux(X1DIR, V2, k, je-1, ie+1) -B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je+1, ie+1) + // +2.*B_F.flux(X2DIR, V1, k, je, ie) + 2.*B_F.flux(X2DIR, V1, k, je, ie+1); + B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); + B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + } } ); + } + } } } @@ -523,7 +616,6 @@ void CalcDivB(MeshData *md, std::string divb_field_name) const int je = IsDomainBound(pmb, BoundaryFace::outer_x2) ? jb.e : jb.e + 1; const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; - printf("Hyerin: for calcDivB ks is %i.\n", ks); pmb->par_for("calc_divB", ks, ke, js, je, is, ie, KOKKOS_LAMBDA_3D { diff --git a/kharma/boundaries.cpp b/kharma/boundaries.cpp index c4115429..335393ed 100644 --- a/kharma/boundaries.cpp +++ b/kharma/boundaries.cpp @@ -227,6 +227,87 @@ void ReflectX2(std::shared_ptr> &rc, IndexDomain domain, boo } } +// Single reflecting boundary function for inner and outer bounds +// copied from ReflectX2 +void ReflectX1(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { + Flag(rc.get(), "Applying KHARMA reflecting X1 bound"); + std::shared_ptr pmb = rc->GetBlockPointer(); + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const auto& G = pmb->coords; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin + Real x_EH = pmb->packages.Get("GRMHD")->Param("x_EH"); //Hyerin + + // q will actually have *both* cons & prims (unless using imex driver) + // We'll only need cons.B specifically tho + PackIndexMap prims_map, ghosts_map; + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); + auto q = rc->PackVariables({Metadata::FillGhost}, ghosts_map, coarse); + //auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); // instead, just directly alter flux to being 0 consistent with B field (check if the flux calculation is called later though) + const VarMap m_u(ghosts_map, true), m_p(prims_map, false); + // If we're running imex, q is the *primitive* variables + bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; + + // KHARMA is very particular about corner boundaries, see above + IndexDomain ldomain = IndexDomain::interior; + int is = bounds.is(ldomain), ie = bounds.ie(ldomain); + int js = bounds.js(ldomain), je = bounds.je(ldomain); + int ks = bounds.ks(ldomain), ke = bounds.ke(ldomain); + ldomain = IndexDomain::entire; + int is_e = bounds.is(ldomain), ie_e = bounds.ie(ldomain); + int js_e = bounds.js(ldomain), je_e = bounds.je(ldomain); + int ks_e = bounds.ks(ldomain), ke_e = bounds.ke(ldomain); + + int ref_tmp, add_tmp, ibs, ibe; + if (domain == IndexDomain::inner_x1) { + add_tmp = -1; + ref_tmp = bounds.GetBoundsI(IndexDomain::interior).s; + ibs = is_e; + ibe = is - 1; + } else if (domain == IndexDomain::outer_x1) { + add_tmp = 1; + ref_tmp = bounds.GetBoundsI(IndexDomain::interior).e; + ibs = ie + 1; + ibe = ie_e; + } else { + throw std::invalid_argument("KHARMA Reflecting boundaries only implemented in X1!"); + } + const int ref = ref_tmp; + const int add = add_tmp; + + // This first loop copies all variables with the "FillGhost" tag into the outer zones + // This includes some we may replace below + /* + pmb->par_for("ReflectX1", 0, q.GetDim(4) - 1, ks_e, ke_e, js_e, je_e, ibs, ibe, + KOKKOS_LAMBDA_VARS { + if (k == ks_e && j == js_e && i == ibs) printf("Hyerin: p = %i, m_u.U1 = %i, ghosts_map[prims.U1] =%i \n",p, m_u.U1, ghosts_map["prims.uvec"].first); + //Real reflect = q.VectorComponent(p) == X1DIR ? -1.0 : 1.0; + //if (p != m_u.B1 && p != m_p.B2 && p != m_p.B3) { // Hyerin (02/12/23) don't change the B fields because this is done in b_flux_ct's FixX1Flux routine + //q(p, k, j, i) = reflect * q(p, k, j, (ref + add) + (ref - i)); + //} + } + ); + */ + int idx = ghosts_map["prims.uvec"].first; + pmb->par_for("ReflectX1", ks_e, ke_e, js_e, je_e, ibs, ibe, + KOKKOS_LAMBDA_3D { // Hyerin (02/13/23) only do for velocities + q(idx, k, j, i) = (-1.) * q(idx, k, j, (ref + add) + (ref - i)); + q(idx+1, k, j, i) = q(idx+1, k, j, (ref + add) + (ref - i)); + q(idx+2, k, j, i) = q(idx+2, k, j, (ref + add) + (ref - i)); + } + ); + if (!prim_ghosts) { + // Normal operation: see above + pmb->par_for("ReflectX1_PtoU", ks_e, ke_e, js_e, je_e, ibs, ibe, + KOKKOS_LAMBDA_3D { + //if (m_p.B1 >= 0) + //VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); + GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); + } + ); + } +} + // Interface calls into the preceding functions void KBoundaries::InnerX1(std::shared_ptr> &rc, bool coarse) { @@ -241,8 +322,10 @@ void KBoundaries::InnerX1(std::shared_ptr> &rc, bool coarse) } else if ((prob == "resize_restart_kharma")&& (x1min>1)){ // Hyerin (if the inner x1 bound is far from BH, constant bc) SetKharmaRestart(rc.get(), IndexDomain::inner_x1,coarse); + //ReflectX1(rc, IndexDomain::inner_x1, coarse); // Hyerin (02/12/23) reflecting bc instead of porous bc } else if ((prob == "bondi") && (x1min>1)){ // Hyerin SetBondi(rc.get(), IndexDomain::inner_x1,coarse); + //ReflectX1(rc, IndexDomain::inner_x1, coarse); //} else if ((prob == "gizmo_shell") && (x1min>1)){ // Hyerin // SetGizmoShell(rc.get(), IndexDomain::inner_x1,coarse); } else { @@ -261,12 +344,14 @@ void KBoundaries::OuterX1(std::shared_ptr> &rc, bool coarse) //SetHubble(rc.get(), IndexDomain::outer_x1, coarse); } else if (prob == "bondi") { SetBondi(rc.get(), IndexDomain::outer_x1, coarse); + //ReflectX1(rc, IndexDomain::outer_x1, coarse); } else if (prob == "conducting_atmosphere"){ dirichlet_bc(rc.get(), IndexDomain::outer_x1, coarse); } else if (prob == "bondi_viscous") { SetBondiViscous(rc.get(), IndexDomain::outer_x1, coarse); } else if (prob == "resize_restart_kharma") { // Hyerin, constant boundary condition SetKharmaRestart(rc.get(),IndexDomain::outer_x1, coarse); + //ReflectX1(rc, IndexDomain::outer_x1, coarse); } else { OutflowX1(rc, IndexDomain::outer_x1, coarse); } @@ -303,6 +388,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) bool check_inflow_inner = pmb0->packages.Get("GRMHD")->Param("check_inflow_inner"); bool check_inflow_outer = pmb0->packages.Get("GRMHD")->Param("check_inflow_outer"); bool fix_flux_pole = pmb0->packages.Get("GRMHD")->Param("fix_flux_pole"); + bool fix_flux_x1 = pmb0->packages.Get("GRMHD")->Param("fix_flux_x1"); IndexDomain domain = IndexDomain::interior; const int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); @@ -316,13 +402,14 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) const int ie_l = ie + 1; const int je_l = (ndim > 1) ? je + 1 : je; //const int ke_l = (ndim > 2) ? ke + 1 : ke; - + for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); PackIndexMap cons_map; auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); const int m_rho = cons_map["cons.rho"].first; + const int m_B = cons_map["cons.B"].first; // Hyerin (12/22/22) if (check_inflow_inner) { if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { @@ -345,23 +432,47 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) // This is a lot of zero fluxes! if (fix_flux_pole) { + //printf("HYERIN: m_B=%i m_rho=%i dim = (%i %i %i %i %i %i)\n",m_B, m_rho,F.GetDim(1),F.GetDim(2), F.GetDim(3), F.GetDim(4), F.GetDim(5),F.GetDim(6)); if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { // This loop covers every flux we need - pmb->par_for("fix_flux_pole_l", 0, F.GetDim(4) - 1, ks, ke, js, js, is, ie, + pmb->par_for("fix_flux_pole_l", 0, F.GetDim(4) - 1, ks-1, ke+1, js, js, is-1, ie+1, // Hyerin: expanded i and k ranges. see FluxCT. they care about these KOKKOS_LAMBDA_VARS { F.flux(X2DIR, p, k, j, i) = 0.; + //if (p==7 && k==15 && i==is-1){ + // printf("HYERIN: BC B flux %i %i %i = (%g %g %g)\n",i,j,k,F.flux(X2DIR,m_B,ks,js,i),F.flux(X2DIR,m_B+1,ks,js,i),F.flux(X2DIR,m_B+2,ks,js,i)); + //} } ); } if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { - pmb->par_for("fix_flux_pole_r", 0, F.GetDim(4) - 1, ks, ke, je_l, je_l, is, ie, + pmb->par_for("fix_flux_pole_r", 0, F.GetDim(4) - 1, ks-1, ke+1, je_l, je_l, is-1, ie+1, KOKKOS_LAMBDA_VARS { F.flux(X2DIR, p, k, j, i) = 0.; } ); } } + + /* Hyerin (01/03/23) I don't think this is needed. Same thing is applied on FixX1Flux + if (fix_flux_x1) { + // Hyerin (12/22/22) ensure no ghost zone B field change + if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, + KOKKOS_LAMBDA_3D { + VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones + } + ); + } + if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, + KOKKOS_LAMBDA_3D { + VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones + } + ); + } + } + */ } Flag("Fixed fluxes"); From d73018841ecae1e54319aa2381cfcdd96efd75ff Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 22 Feb 2023 20:29:57 -0500 Subject: [PATCH 034/219] modified bondi a little bit --- kharma/prob/bondi.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 119940c1..52a48567 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -150,12 +150,18 @@ KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const Coordin Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; if (r Date: Wed, 22 Feb 2023 20:31:10 -0500 Subject: [PATCH 035/219] running scripts --- tests/bclean/run.sh | 135 ++++++++++++++++++++++++++++++++++++++++++++ tests/bflux/run.sh | 132 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100755 tests/bclean/run.sh create mode 100755 tests/bflux/run.sh diff --git a/tests/bclean/run.sh b/tests/bclean/run.sh new file mode 100755 index 00000000..84a619ff --- /dev/null +++ b/tests/bclean/run.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# Hyerin (02/17/23) copied from Ben's code + +# Bash script testing b_clean + +# User specified values here +KERR=false +DIM=3 +NZONES=2 #7 +BASE=8 +NRUNS=2 +START_RUN=0 +DRTAG="bondi_multizone_021823_sane_b_clean" + +# Set paths +KHARMADIR=../.. +PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory +DR="${PDR}data/${DRTAG}" +#parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file +parfilename="${PDR}/sane.par" # parameter file + +# other values determined automatically +turn_around=$(($NZONES-1)) +start_time=0 +out_to_in=1 +iteration=1 +r_out=$((${BASE}**($turn_around+2))) +r_in=$((${BASE}**$turn_around)) + +# if the directories are not present, make them. +if [ ! -d "${DR}" ]; then + mkdir "${DR}" +fi +if [ ! -d "${PDR}logs/${DRTAG}" ]; then + mkdir "${PDR}logs/${DRTAG}" +fi + +### Start running zone by zone +for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) +do + args=() + echo "iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" + logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO + runtime=`echo "scale=0; e($logruntime)+1" | bc -l` + log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) + start_time=$(($start_time+$runtime)) + + #parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_$(printf %05d ${VAR}).par" # parameter file + + # set problem type and cleanup + if [ $VAR -eq 0 ]; then + prob="torus" #"bondi" + init_c=0 + else + prob="resize_restart_kharma" + init_c=1 + fi + + # set BH spin + if [[ $KERR == "true" ]]; then + spin=0.99 + else + spin=0.0 + fi + + # output time steps + output0_dt=$((${runtime}/100*10)) + output1_dt=$((${runtime}/20*10)) + output2_dt=$((${runtime}/1000*10)) + + # dt, fname, fname_fill + if [ $VAR -ne 0 ]; then + # update dt from the previous run + tag=($( tail -n 10 ${PDR}/logs/${DRTAG}/log_multizone$(printf %05d $((${VAR}-1)))_out )) + dt=$(printf "%.18g" "${tag[2]:3}") # previous dt + dt_new=$(echo "scale=14; $dt*sqrt($BASE^(-3*$out_to_in))/4" | bc -l) # new dt ## TODO: r^3/2 + if (( $(echo "$dt_new > 0.00001" |bc -l) )); then + dt_new=$dt_new + else + dt_new=0.00001 + fi + fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" + fname=$(find ${fname_dir} -type f -iname "*final.rhdf") + if [ $VAR -ge $NZONES ]; then + fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) + fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" + fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") + else + fname_fill="none" + fi + args+=(" resize_restart/fname=$fname parthenon/time/dt_min=$dt_new") + args+=(" resize_restart/fname_fill=$fname_fill ") + else + r_shell=$((${r_out}/2)) + args+=(" bondi/r_shell=$r_shell ") + fi + + # data_dir, logfiles + data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" + out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" + err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" + + srun --mpi=pmix ${PDR}/kharma_fork/kharma.cuda -i ${parfilename} \ + parthenon/job/problem_id=$prob \ + parthenon/time/tlim=${start_time} \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} \ + bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ + parthenon/output0/dt=$output0_dt \ + parthenon/output1/dt=$output1_dt \ + parthenon/output2/dt=$output2_dt \ + ${args[@]} \ + -d ${data_dir} 1> ${out_fn} 2>${err_fn} + #parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ + #parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=32 parthenon/meshblock/nx3=64 \ + #coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin \ + #b_field/type=vertical b_field/solver=flux_ct \ + + if [ $VAR -ne 0 ]; then + if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then + out_to_in=$(($out_to_in*(-1))) + iteration=$(($iteration+1)) + fi + fi + + if [ $out_to_in -gt 0 ]; then + # half the radii + r_out=$((${r_out}/$BASE)) + r_in=$((${r_in}/$BASE)) + else + # double the radii + r_out=$((${r_out}*$BASE)) + r_in=$((${r_in}*$BASE)) + fi +done diff --git a/tests/bflux/run.sh b/tests/bflux/run.sh new file mode 100755 index 00000000..683deff3 --- /dev/null +++ b/tests/bflux/run.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# Hyerin (02/17/23) copied from Ben's code + +# Bash script testing nonzero b flux + +# User specified values here +KERR=false +DIM=3 +NZONES=2 #7 +BASE=8 +NRUNS=14 +START_RUN=0 +DRTAG="bondi_multizone_022223_n2b8_bondi_1e-3_flows" + +# Set paths +KHARMADIR=../.. +PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory +DR="${PDR}data/${DRTAG}" +parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file + +# other values determined automatically +turn_around=$(($NZONES-1)) +start_time=0 +out_to_in=1 +iteration=1 +r_out=$((${BASE}**($turn_around+2))) +r_in=$((${BASE}**$turn_around)) + +# if the directories are not present, make them. +if [ ! -d "${DR}" ]; then + mkdir "${DR}" +fi +if [ ! -d "${PDR}logs/${DRTAG}" ]; then + mkdir "${PDR}logs/${DRTAG}" +fi + +### Start running zone by zone +for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) +do + args=() + echo "iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" + logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO + runtime=`echo "scale=0; e($logruntime)+1" | bc -l` + log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) + start_time=$(($start_time+$runtime)) + + #parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_$(printf %05d ${VAR}).par" # parameter file + + # set problem type and cleanup + if [ $VAR -eq 0 ]; then + prob="bondi" + else + prob="resize_restart_kharma" + fi + + # set BH spin + if [[ $KERR == "true" ]]; then + spin=0.99 + else + spin=0.0 + fi + + # output time steps + output0_dt=$((${runtime}/100*10)) + #output1_dt=$((${runtime}/20*10)) + output1_dt=$((${runtime}/200*10)) # test Hyerin (02/20/23) + output2_dt=$((${runtime}/1000*10)) + + # dt, fname, fname_fill + if [ $VAR -ne 0 ]; then + # update dt from the previous run + tag=($( tail -n 10 ${PDR}/logs/${DRTAG}/log_multizone$(printf %05d $((${VAR}-1)))_out )) + dt=$(printf "%.18g" "${tag[2]:3}") # previous dt + dt_new=$(echo "scale=14; $dt*sqrt($BASE^(-3*$out_to_in))/4" | bc -l) # new dt ## TODO: r^3/2 + if (( $(echo "$dt_new > 0.00001" |bc -l) )); then + dt_new=$dt_new + else + dt_new=0.00001 + fi + fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" + fname=$(find ${fname_dir} -type f -iname "*final.rhdf") + if [ $VAR -ge $NZONES ]; then + fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) + fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" + fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") + else + fname_fill="none" + fi + args+=(" resize_restart/fname=$fname parthenon/time/dt_min=$dt_new") + args+=(" resize_restart/fname_fill=$fname_fill ") + else + r_shell=$((${r_out}/2)) + args+=(" bondi/r_shell=$r_shell ") + fi + + # data_dir, logfiles + data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" + out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" + err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" + + srun --mpi=pmix ${PDR}/kharma_fork/kharma.cuda -i ${parfilename} \ + parthenon/job/problem_id=$prob \ + parthenon/time/tlim=${start_time} parthenon/time/nlim=$((5000*($VAR+1))) \ + parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ + parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=false\ + bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + b_field/type=vertical b_field/solver=flux_ct b_field/bz=1e-3 \ + b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ + parthenon/output0/dt=$output0_dt \ + parthenon/output1/dt=$output1_dt \ + parthenon/output2/dt=$output2_dt \ + ${args[@]} \ + -d ${data_dir} 1> ${out_fn} 2>${err_fn} + + if [ $VAR -ne 0 ]; then + if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then + out_to_in=$(($out_to_in*(-1))) + iteration=$(($iteration+1)) + fi + fi + + if [ $out_to_in -gt 0 ]; then + # half the radii + r_out=$((${r_out}/$BASE)) + r_in=$((${r_in}/$BASE)) + else + # double the radii + r_out=$((${r_out}*$BASE)) + r_in=$((${r_in}*$BASE)) + fi +done From 3ba11b340f569d6bd4aef628fdebb01cd64b1480 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 22 Feb 2023 20:34:33 -0500 Subject: [PATCH 036/219] minor updates --- kharma/prob/resize_restart_kharma.cpp | 1 + kharma/prob/resize_restart_kharma.hpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 5d927dd8..2d315f72 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -266,6 +266,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co n3mb+2*fnghost*x3factor}; const int block_sz = length[0]*length[1]*length[2]*length[3]; //std::cout << "lengths " << length[0] << " " << length[1] <<" " << length[2]<<" " << length[3] << std::endl; + //printf("lengths %i %i %i %i \n", length[0], length[1], length[2], length[3]); // read from file and stored in device Hyerin (10/18/2022) diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 303c3abe..8dcb9cb1 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -125,7 +125,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons GReal r = Xembed[1]; // copy over smallest radius states - //Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); + Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); itemp = fnghost; // in order to copy over the physical region, not the ghost region // (02/08/23) instead in order to set the vacuum homogeneous instead of having theta phi dependence, set j and k values jtemp = fnghost; @@ -156,7 +156,6 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons } else { Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); - rho_temp = rho(iblocktemp,ktemp,jtemp,itemp); u_temp = u(iblocktemp,ktemp,jtemp,itemp); VLOOP u_prim[v] = uvec(v,iblocktemp,ktemp,jtemp,itemp); From bd8e23ecf994339350d294c9bb8184555a21ad41 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 22 Feb 2023 20:35:48 -0500 Subject: [PATCH 037/219] minor changes --- machines/cannon_ramesh.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/machines/cannon_ramesh.sh b/machines/cannon_ramesh.sh index 04b21ef1..42b59dd6 100755 --- a/machines/cannon_ramesh.sh +++ b/machines/cannon_ramesh.sh @@ -1,26 +1,32 @@ # Harvard Cannon -if [[ $HOST == *"rc.fas.harvard.edu" ]]; then +#if [[ $HOST == *"rc.fas.harvard.edu" ]]; then +if [[ $(hostname -f) == *"rc.fas.harvard.edu" ]]; then echo CANNON HOST_ARCH=HSW EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON" + echo $ARGS + echo "after printing out" module unload hdf5 module unload Anaconda3/2020.11 + #module load gcc/12.1.0-fasrc01 + #module load openmpi/4.1.3-fasrc01 + module load cmake/3.17.3-fasrc01 # newer versions are usually better + #C_NATIVE=gcc + #CXX_NATIVE=g++ + #module load cmake/3.23.2-fasrc01 # newer versions are usually better if [[ "$ARGS" == *"cuda"* ]]; then #DEVICE_ARCH=VOLTA70 ## test, (old GPUs) DEVICE_ARCH=AMPERE80 ## blackhole_gpu, itc_gpu module load gcc/9.3.0-fasrc01 module load openmpi/4.0.5-fasrc01 - module load cmake/3.17.3-fasrc01 #module load cuda/11.1.0-fasrc01 module load cuda/11.6.2-fasrc01 export PATH=/n/home09/hyerincho/packages/hdf5-openmpi4.1.1:$PATH else module load intel/19.0.5-fasrc01 module load openmpi/4.0.1-fasrc01 - module load cmake/3.17.3-fasrc01 - #module load cuda/11.1.0-fasrc01 export PATH=/n/home09/hyerincho/packages/hdf5-openmpi4.0.1:$PATH export PATH=/n/helmod/apps/centos7/Core/gcc/9.3.0-fasrc01/bin:$PATH export LIBRARY_PATH=/n/helmod/apps/centos7/Core/gcc/9.3.0-fasrc01/lib64:$LIBRARY_PATH From 8b86e389c686a982c6680add418357c99db52ac3 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Jan 2023 16:20:34 -0700 Subject: [PATCH 038/219] Only read necessary zones when resizing. Roots of a possible resize test. --- kharma/prob/hdf5_utils.cpp | 10 +- kharma/prob/interpolation.hpp | 197 ++++++++---------------- kharma/prob/post_initialize.cpp | 13 ++ kharma/prob/resize_restart.cpp | 255 +++++++++++++++++++++++--------- pars/resize_orszag_tang.par | 80 ++++++++++ 5 files changed, 349 insertions(+), 206 deletions(-) create mode 100644 pars/resize_orszag_tang.par diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index ab55dbcf..a97da26e 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -394,7 +394,15 @@ int hdf5_read_array(void *data, const char *name, size_t rank, strncpy(path, hdf5_cur_dir, STRLEN); strncat(path, name, STRLEN - strlen(path)); - if(DEBUG) fprintf(stderr,"Reading arr %s\n", path); + if(DEBUG) { + fprintf(stderr,"Reading arr %s:\n", path); + fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); + fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); + fprintf(stderr,"File read size: %llu %llu %llu %llu\n\n", fcount[0], fcount[1], fcount[2], fcount[3]); + + fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n", mstart[0], mstart[1], mstart[2], mstart[3]); + } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index 234cd3e0..ed6a0272 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -35,24 +35,24 @@ #include "decs.hpp" -// For using the ipole routines verbatim. -// Automatically wraps in k so we can avoid ghost zones -#define ind_sph(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (j) * n1 + (i)) -#define ind_periodic(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (((j)+n2) % n2) * n1 + (((i)+n1) % n1) ) - /** - * Routines for interpolating and initializing a KHARMA meshblock from the - * correct area of a global iharm3d restart file, used in resize_restart.cpp. - * Doesn't include "Elliptic maid" solver step for eliminating magnetic field - * divergence, see b_flux_ct for that (as it is divergence-rep dependent) + * Routines for interpolating on a grid, with values given in a flattened array. + * Mostly used in resize_restart.cpp, which must interpolate from a grid corresponding + * to an old simulation, read from a file. + * + * Note that resizing a file nearly always requires fixing the resulting magentic field + * divergence -- see b_cleanup/ for details. */ +namespace Interpolation { + /** - * translates geodesic coordinates to a grid zone and returns offset - * for interpolation purposes. integer index corresponds to the zone - * center "below" the desired point and del[i] \in [0,1) returns the - * offset from that zone center. + * Finds the closest grid zone which lies to the left of the given point in X1,X2, and X3, + * along with the distance 'del' from that center to X in each coordinate, + * for interpolation purposes. * + * Example (from ipole, ) + * * 0 0.5 1 * [ | ] * A B C DE F @@ -67,139 +67,70 @@ * E -> ( 1, 0.0) * F -> ( 1, 0.5) */ -KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal XG[GR_DIM], +KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], const GReal startx[GR_DIM], const GReal dx[GR_DIM], - int& i, int& j, int& k, GReal del[GR_DIM], - bool nearest=false) -{ - // If we ever include ghosts in iharm3d-format restarts, we need to clip phi here - // GReal phi = fmod(XG[3], stopx[3]); - // if (phi < 0.0) // TODO adapt for startx3 != 0? - // phi += stopx[3]; - GReal phi = XG[3]; - - if (nearest) { - // get the index of the zone we are in: >= left corner? - i = (int) ((XG[1] - startx[1]) / dx[1] + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] + 1000) - 1000; - } else { - // Normal operation - // get provisional zone index. see note above function for details. note we - // shift to zone centers because that's where variables are most exact. - i = (int) ((XG[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] - 0.5 + 1000) - 1000; - } - - // now construct del - del[1] = (XG[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; - del[2] = (XG[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; - del[3] = (phi - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; -} - -KOKKOS_INLINE_FUNCTION void ijktoX(const GReal startx[GR_DIM], const GReal dx[GR_DIM], - const int& i, const int& j, const int& k, - GReal XG[GR_DIM]) + int& i, int& j, int& k, GReal del[GR_DIM]) { + // Normal operation // get provisional zone index. see note above function for details. note we // shift to zone centers because that's where variables are most exact. - XG[0] = 0.; - XG[1] = startx[1] + (i + 0.5) * dx[1]; - XG[2] = startx[2] + (j + 0.5) * dx[2]; - XG[3] = startx[3] + (k + 0.5) * dx[3]; + i = (int) ((X[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] - 0.5 + 1000) - 1000; + + // Distance from closest zone center on the left + // i.e., portion of left zone to use vs right when interpolating + del[1] = (X[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; + del[2] = (X[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; + del[3] = (X[3] - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; } /** - * This interpolates a single-array variable 'var' representing a grid of size 'startx' to 'stopx' in - * native coordinates, returning its value at location X - * NOTE: 'startx' must correspond to the grid you are interpolating *from* + * Translates a point X in native coordinates to a grid zone. */ -KOKKOS_INLINE_FUNCTION Real linear_interp(const GRCoordinates& G, const GReal X[GR_DIM], - const GReal startx[GR_DIM], - const GReal dx[GR_DIM], const bool& is_spherical, const bool& weight_by_gdet, - const int& n3, const int& n2, const int& n1, - const Real *var) +KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], + const GReal startx[GR_DIM], + const GReal dx[GR_DIM], + int& i, int& j, int& k) { - // zone and offset from X - // Obtain this in - GReal del[GR_DIM]; - int i, j, k; - Xtoijk(X, startx, dx, i, j, k, del); - - Real interp; - if (is_spherical) { - // For ghost zones, we treat each boundary differently: - // In X1, repeat first & last zones. - if (i < 0) { i = 0; del[1] = 0; } - if (i > n1-2) { i = n1 - 2; del[1] = 1; } - // In X2, stop completely at the last zone - // Left side of leftmost segment - if (j < 0) { j = 0; del[2] = 0; } - // Right side of rightmost segment. Phrased this way to not segfault - if (j > n2-2) { j = n2 - 2; del[2] = 1; } - // k auto-wraps. So do all indices for periodic boxes. - - if (weight_by_gdet) { - GReal Xtmp[GR_DIM]; - ijktoX(startx, dx, i, j, k, Xtmp); - GReal g_ij = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j, k, Xtmp); - GReal g_i1j = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i, j + 1, k, Xtmp); - GReal g_ij1 = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j + 1, k, Xtmp); - GReal g_i1j1 = G.coords.gdet_native(Xtmp); - - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*g_i1j1*del[1]*del[2]; - - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*g_i1j1*del[1]*del[2]); - } - interp /= G.coords.gdet_native(X); - } else { - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*del[1]*del[2]; + // Get the index of the zone this point falls into. + // i.e., are we >= the left corner? + i = (int) ((X[1] - startx[1]) / dx[1] + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] + 1000) - 1000; +} - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } - } - } else { - // interpolate in x1 and x2 - interp = var[ind_periodic(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k)]*del[1]*del[2]; +/** + * Dumb linear interpolation: no special cases for boundaries + * Takes indices i,j,k and a block size n1, n2, n3, + * as well as a flat array var. + * + * TODO version(s) with View(s) for real device-side operation + */ +// For using the ipole routines in a recognizable form on a 1D array +#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_periodic(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } +KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, + const int& n1, const int& n2, const int& n3, + const double del[4], const double *var) +{ + // Interpolate in 1D at a time to avoid reading zones we don't have + Real interp = var[ind(i , j , k)]*(1. - del[1]) + + var[ind(i + 1, j , k)]*del[1]; + if (n2 > 1) { + interp = (1. - del[2])*interp + + del[2]*(var[ind(i , j + 1, k)]*(1. - del[1]) + + var[ind(i + 1, j + 1, k)]*del[1]); + } + if (n3 > 1) { + interp = (1. - del[3])*interp + + del[3]*(var[ind(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + + var[ind(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + + var[ind(i , j + 1, k + 1)]*(1. - del[1])*del[2] + + var[ind(i + 1, j + 1, k + 1)]*del[1]*del[2]); } - return interp; } +} // Interpolation \ No newline at end of file diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 18f342b2..d8c82435 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -179,6 +179,19 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrmesh_data.GetOrAdd("base", 0); diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 5f60e3e9..e3584c51 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -45,19 +45,20 @@ #include #include -// This is gross, but everything else is grosser -// What's a little leaked host mem between friends? -static Real *ptmp = NULL; -static int blocks_initialized = 0; - // TODO: The iharm3d restart format fails to record several things we must guess: // 1. Sometimes, even precise domain boundaries in native coordinates // 2. Which coordinate system was used // 3. Any coordinate system parameters // Better to either: // a. read KHARMA restart files so we can re-grid -// b. use the IL dump format, but in double -// Either are useful capabilities. +// b. use the IL dump format, but in double precision (or even in single w/cleanup) +// Either would be very useful independently + +// This exists to simplify some initializer lists below +// This indicates I know that moving from signed->unsigned is dangerous, +// and sign off that these results are positive (they are) +hsize_t static_max(int i, int n) { return static_cast(m::max(i, n)); } +hsize_t static_min(int i, int n) { return static_cast(m::min(i, n)); } void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin) { @@ -74,7 +75,6 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& std::cout << "Initialized from " << fname << ", file version " << version << std::endl << std::endl; } - // Read what we need from the file, regardless of where we're putting it int n1file, n2file, n3file; hdf5_read_single_val(&n1file, "n1", H5T_STD_I32LE); @@ -236,12 +236,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) { Flag(rc, "Restarting from iharm3d checkpoint file"); - // TODO pack? Probably not worth it auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess const bool regrid_only = pin->GetOrAddBoolean("resize_restart", "regrid_only", false); @@ -271,7 +266,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetInteger("parthenon/mesh", "nx2") != n2tot || pin->GetInteger("parthenon/mesh", "nx3") != n3tot) { printf("Mesh size does not match!\n"); - printf("[%d %d %d] vs [%d %d %d]", + printf("[%d %d %d] vs [%llu %llu %llu]", pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), pin->GetInteger("parthenon/mesh", "nx3"), @@ -307,74 +302,192 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) } } - // TODO there must be a better way to cache this. InitUserData and make it a big variable or something? - if (ptmp == NULL) { - std::cout << "Reading mesh from file to cache..." << std::endl; + if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; - // Declare known sizes for inputting/outputting primitives - // We'll only ever read the full block, so this is the size we want - hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; - hsize_t fstart[] = {0, 0, 0, 0}; - ptmp = new double[nfprim*n3tot*n2tot*n1tot]; // These will include B & thus be double or upconverted to it + // In this section we're dealing with two different meshes: the one we're interpolating *from* (the "file" grid) + // and the one we're interpolating *to* -- the "meshblock." + // Additionally, in the "file" mesh we must deail with global file locations (no ghost zones, global index, prefixed "g") + // as well as local file locations (locations in a cache we read to host memory, prefixed "m") - hdf5_open(fname.c_str()); - hdf5_set_directory("/"); - hdf5_read_array(ptmp, "p", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_close(); + // Size/domain of the MeshBlock we're reading *to*. + // Note that we only fill the block's physical zones -- + // PostInitialize will take care of ghosts with MPI syncs and calls to the domain boundary conditions + IndexDomain domain = IndexDomain::interior; + const IndexRange ib = pmb->cellbounds.GetBoundsI(domain); + const IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); + const IndexRange kb = pmb->cellbounds.GetBoundsK(domain); + const auto& G = pmb->coords; - std::cout << "Read!" << std::endl; + // Total file size + hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; + + // Figure out the subset in global space corresponding to our memory cache + int gis, gjs, gks, gie, gje, gke; + if (regrid_only) { + // For nearest neighbor "interpolation," we don't need any ghost zones + // Global location of first zone of our new grid + double X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + Interpolation::Xtoijk_nearest(X, startx, dx, gis, gjs, gks); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gie, gje, gke); + } else { + // Linear interpolation case: we need ghost zones + // Global location of first zone of our new grid + double tmp[GR_DIM], X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + // Note this will be the *left* side already, so we'll never read below this. + // The values gis,gjs,gks can/will be -1 sometimes + Interpolation::Xtoijk(X, startx, dx, gis, gjs, gks, tmp); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk(X, startx, dx, gie, gje, gke, tmp); + // Include one extra zone in each direction, for right side of linear interp + gke += 1; gje += 1; gie += 1; } - // If we are going to keep a static pointer, keep count so the last guy can kill it - blocks_initialized += 1; + // Truncate the file read sizes so we don't overrun the file data + hsize_t fstart[4] = {0, static_max(gks, 0), static_max(gjs, 0), static_max(gis, 0)}; + // TODO separate nmprim to stop at 8 prims if we don't need e- + hsize_t fstop[4] = {nfprim, static_min(gke, n3tot), static_min(gje, n2tot), static_min(gie, n1tot)}; + hsize_t fcount[4] = {fstop[0] - fstart[0], fstop[1] - fstart[1], fstop[2] - fstart[2], fstop[3] - fstart[3]}; + // If we overran an index on the left, we need to leave a blank row (i.e., start at 1 == true) to reflect this + hsize_t mstart[4] = {0, (gks < 0), (gjs < 0), (gis < 0)}; + // Total memory size is never truncated + hsize_t nmk = gke-gks, nmj = gje-gjs, nmi = gie-gis; + hsize_t mdims[4] = {nfprim, nmk, nmj, nmi}; + // TODO should yell if any of these fired for nearest-neighbor + + // Allocate the array we'll need + hsize_t nmblock = nmk * nmj * nmi; + // TODO this may be float[] if we ever want to read dump files as restarts + double *ptmp = new double[nfprim*nmblock]; + + // Open the file + hdf5_open(fname.c_str()); + hdf5_set_directory("/"); + + // Read the main array + hdf5_read_array(ptmp, "p", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + + // Do some special reads from elsewhere in the file to fill periodic bounds + // Note we do NOT fill outflow/reflecting bounds here -- instead, we treat them specially below + // TODO this could probably be a lot cleaner + hsize_t fstart_tmp[4], fcount_tmp[4], mstart_tmp[4]; +#define RESET_COUNTS DLOOP1 {fstart_tmp[mu] = fstart[mu]; fcount_tmp[mu] = fcount[mu]; mstart_tmp[mu] = mstart[mu];} + if (gks < 0 && pmb->boundary_flag[BoundaryFace::inner_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally LAST rank in X3 + fstart_tmp[1] = n3tot-1; + fcount_tmp[1] = 1; + // Read it to the FIRST rank of our array + mstart_tmp[1] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gke > n3tot && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally FIRST rank in X3 + fstart_tmp[1] = 0; + fcount_tmp[1] = 1; + // Read it to the LAST rank of our array + mstart_tmp[1] = mdims[1]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gjs < 0 && pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = n2tot-1; + fcount_tmp[2] = 1; + mstart_tmp[2] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gje > n2tot && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = 0; + fcount_tmp[2] = 1; + mstart_tmp[2] = mdims[2]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gis < 0 && pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = n1tot-1; + fcount_tmp[3] = 1; + mstart_tmp[3] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gie > n1tot && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = 0; + fcount_tmp[3] = 1; + mstart_tmp[3] = mdims[3]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + + hdf5_close(); + + if (MPIRank0()) std::cout << "Read!" << std::endl; + + // Get the arrays we'll be writing to + // TODO this is probably easier AND more flexible if we pack them + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + GridVector B_P = rc->Get("prims.B").data; auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); auto B_host = B_P.GetHostMirror(); - // Size/domain of the MeshBlock we're reading *to*. - // Note that we only read physical zones. - IndexDomain domain = IndexDomain::interior; - const IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - const IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - const IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - - auto& G = pmb->coords; - - Flag("Reordering meshblock..."); - // Host-side interpolate & copy into the mirror array - // TODO Support restart native coordinates != new native coordinates + Flag("Interpolating meshblock..."); + // Interpolate on the host side & copy into the mirror Views + // Nearest-neighbor interpolation is currently only used when grids exactly correspond -- otherwise, linear interpolation is used + // to minimize the resulting B field divergence. // NOTE: KOKKOS USES < not <=!! Therefore the RangePolicy below will seem like it is too big if (regrid_only) { - // Kokkos::parallel_for("copy_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO Kokkos calls here had problems with CUDA, reintroduce/fix + // OpenMP here conflicts with Kokkos parallel in some cases, so we're stuck for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); double tmp[GR_DIM]; - int gk,gj,gi; Xtoijk(X, startx, dx, gi, gj, gk, tmp, true); - // Fill block cells with global equivalents - rho_host(k, j, i) = ptmp[0*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - u_host(k, j, i) = ptmp[1*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP B_host(v, k, j, i) = ptmp[(5+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - } - // ); + GReal X[GR_DIM]; int gk, gj, gi; + G.coord(k, j, i, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gi, gj, gk); + // TODO verify this never reads zones outside the cache + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Fill cells of the new block with equivalents in the cached block + rho_host(k, j, i) = ptmp[0*nmblock + mk*nmj*nmi + mj*nmi + mi]; + u_host(k, j, i) = ptmp[1*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP B_host(v, k, j, i) = ptmp[(5+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + } } else { - // Kokkos::parallel_for("interp_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO real boundary flags. Repeat on any outflow/reflecting bounds + const bool repeat_x1i = is_spherical; + const bool repeat_x1o = is_spherical; + const bool repeat_x2i = is_spherical; + const bool repeat_x2o = is_spherical; + for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - // Get the zone center location - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - // Interpolate the value at this location from the global grid - rho_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[0*n3tot*n2tot*n1tot])); - u_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[1*n3tot*n2tot*n1tot])); - VLOOP uvec_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(2+v)*n3tot*n2tot*n1tot])); - VLOOP B_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(5+v)*n3tot*n2tot*n1tot])); - } - // ); + GReal X[GR_DIM], del[GR_DIM]; int gk, gj, gi; + // Get the zone center location + G.coord(k, j, i, Loci::center, X); + // Get global indices + Interpolation::Xtoijk(X, startx, dx, gi, gj, gk, del); + // Make any corrections due to global boundaries + // Currently just repeats the last zone, equivalent to falling back to nearest-neighbor + if (repeat_x1i && gi < 0) { gi = 0; del[1] = 0; } + if (repeat_x1o && gi > n1tot-2) { gi = n1tot - 2; del[1] = 1; } + if (repeat_x2i && gj < 0) { gj = 0; del[2] = 0; } + if (repeat_x2o && gj > n2tot-2) { gj = n2tot - 2; del[2] = 1; } + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Interpolate the value at this location from the cached grid + rho_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[0*nmblock])); + u_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[1*nmblock])); + VLOOP uvec_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(2+v)*nmblock])); + VLOOP B_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(5+v)*nmblock])); + } } // Deep copy to device @@ -385,11 +498,9 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) B_P.DeepCopy(B_host); Kokkos::fence(); - // Close the door on our way out - if (blocks_initialized == pmb->pmy_mesh->GetNumMeshBlocksThisRank()) { - std::cout << "Deleting cached mesh" << std::endl; - delete[] ptmp; - } + // Delete our cache. Only we ever used it, so we're safe here. + Flag("Deleting cached interpolation values"); + delete[] ptmp; return TaskStatus::complete; } diff --git a/pars/resize_orszag_tang.par b/pars/resize_orszag_tang.par new file mode 100644 index 00000000..92392107 --- /dev/null +++ b/pars/resize_orszag_tang.par @@ -0,0 +1,80 @@ +# Restart from an iharm3d snapshot file, resizing to specified mesh +# Note most parameters here will carry through to running after +# restarting, as iharm3d restart files do not specify much + + +problem_id = resize_restart + + +refinement = none +numlevel = 1 + +nx1 = 512 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 512 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 512 +nx2 = 512 +nx3 = 1 + + +base = spherical_ks +transform = fmks +a = 0.9375 +hslope = 0.3 +r_out = 1000 + + +tlim = 300000 +integrator = rk2 +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 + + +fname = orszag_tang.out2.00001.h5 +use_tf = false +use_dt = false +skip_b_cleanup = false + + +rel_tolerance = 1.e-9 + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 1.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + + +file_type = rst +dt = 50.0 + + +file_type = hst +dt = 0.1 From 39d4d566bc96ce38db1e70ef9711853b65e43749 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Jan 2023 11:12:17 -0600 Subject: [PATCH 039/219] Fixes to indexing vs. sizing errors, more testing groundwork --- kharma/prob/resize_restart.cpp | 49 +++++++++++++++++----------- pars/orszag_tang.par | 15 ++++++--- pars/regrid_orszag_tang.par | 59 ++++++++++++++++++++++++++++++++++ pars/resize_orszag_tang.par | 9 ++---- 4 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 pars/regrid_orszag_tang.par diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index e3584c51..6ad748f8 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -272,18 +272,6 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetInteger("parthenon/mesh", "nx3"), n1tot, n2tot, n3tot); } - - if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), - m::log(pin->GetReal("coordinates", "r_in"))) || - !close_to(pin->GetReal("parthenon/mesh", "x1max"), - m::log(pin->GetReal("coordinates", "r_out")))) { - printf("Mesh shape does not match!"); - printf("Rin %g vs %g, Rout %g vs %g", - m::exp(pin->GetReal("parthenon/mesh", "x1min")), - pin->GetReal("coordinates", "r_in"), - m::exp(pin->GetReal("parthenon/mesh", "x1max")), - pin->GetReal("coordinates", "r_out")); - } if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), startx[1]) || !close_to(pin->GetReal("parthenon/mesh", "x1max"), stopx[1]) || @@ -300,6 +288,22 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetReal("parthenon/mesh", "x3min"), startx[3], pin->GetReal("parthenon/mesh", "x3max"), stopx[3]); } + + if (is_spherical) { + // Check that the coordinate parameters r_{in,out} match the mesh + if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), + m::log(pin->GetReal("coordinates", "r_in"))) || + !close_to(pin->GetReal("parthenon/mesh", "x1max"), + m::log(pin->GetReal("coordinates", "r_out")))) { + printf("Mesh shape does not match!"); + printf("Rin %g vs %g, Rout %g vs %g", + m::exp(pin->GetReal("parthenon/mesh", "x1min")), + pin->GetReal("coordinates", "r_in"), + m::exp(pin->GetReal("parthenon/mesh", "x1max")), + pin->GetReal("coordinates", "r_out")); + } + } + } if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; @@ -319,6 +323,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) const auto& G = pmb->coords; // Total file size + // TODO separate nmprim to stop at 8 prims if we don't need e- hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; // Figure out the subset in global space corresponding to our memory cache @@ -351,14 +356,20 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) // Truncate the file read sizes so we don't overrun the file data hsize_t fstart[4] = {0, static_max(gks, 0), static_max(gjs, 0), static_max(gis, 0)}; - // TODO separate nmprim to stop at 8 prims if we don't need e- - hsize_t fstop[4] = {nfprim, static_min(gke, n3tot), static_min(gje, n2tot), static_min(gie, n1tot)}; - hsize_t fcount[4] = {fstop[0] - fstart[0], fstop[1] - fstart[1], fstop[2] - fstart[2], fstop[3] - fstart[3]}; + // Test gXe against last valid index, i.e. nXtot-1 + hsize_t fstop[4] = {nfprim-1, static_min(gke, n3tot-1), static_min(gje, n2tot-1), static_min(gie, n1tot-1)}; + // We add one here to get sizes from indices + hsize_t fcount[4] = {fstop[0] - fstart[0] + 1, + fstop[1] - fstart[1] + 1, + fstop[2] - fstart[2] + 1, + fstop[3] - fstart[3] + 1}; // If we overran an index on the left, we need to leave a blank row (i.e., start at 1 == true) to reflect this hsize_t mstart[4] = {0, (gks < 0), (gjs < 0), (gis < 0)}; // Total memory size is never truncated - hsize_t nmk = gke-gks, nmj = gje-gjs, nmi = gie-gis; + // This calculation produces XxYx2 arrays for 2D sims w/linear interp but that's fine + hsize_t nmk = gke-gks+1, nmj = gje-gjs+1, nmi = gie-gis+1; hsize_t mdims[4] = {nfprim, nmk, nmj, nmi}; + // TODO these should be const but hdf5_read_array yells about it, fix that // TODO should yell if any of these fired for nearest-neighbor // Allocate the array we'll need @@ -387,7 +398,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[1] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gke > n3tot && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { + if (gke > n3tot-1 && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { RESET_COUNTS // same X1/X2, but take only the globally FIRST rank in X3 fstart_tmp[1] = 0; @@ -403,7 +414,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[2] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gje > n2tot && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { + if (gje > n2tot-1 && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { RESET_COUNTS fstart_tmp[2] = 0; fcount_tmp[2] = 1; @@ -417,7 +428,7 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) mstart_tmp[3] = 0; hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); } - if (gie > n1tot && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { + if (gie > n1tot-1 && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { RESET_COUNTS fstart_tmp[3] = 0; fcount_tmp[3] = 1; diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index be4869d7..4d2fb46f 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -8,13 +8,13 @@ problem_id = orszag_tang refinement = none numlevel = 1 -nx1 = 768 +nx1 = 256 x1min = -3.141592653589793 x1max = 3.141592653589793 ix1_bc = periodic ox1_bc = periodic -nx2 = 768 +nx2 = 256 x2min = -3.141592653589793 x2max = 3.141592653589793 ix2_bc = periodic @@ -27,8 +27,8 @@ ix3_bc = periodic ox3_bc = periodic -nx1 = 768 -nx2 = 768 +nx1 = 256 +nx2 = 128 nx3 = 1 @@ -44,6 +44,9 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 + +type = imex + verbose = 0 flag_verbose = 0 @@ -58,3 +61,7 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 + + +file_type = rst +dt = 10.0 diff --git a/pars/regrid_orszag_tang.par b/pars/regrid_orszag_tang.par new file mode 100644 index 00000000..28a1e91c --- /dev/null +++ b/pars/regrid_orszag_tang.par @@ -0,0 +1,59 @@ +# Restart from an iharm3d snapshot file, resizing to specified mesh +# Note most parameters here will carry through to running after +# restarting, as iharm3d restart files do not specify much + + +problem_id = resize_restart + + +# Set by restart file + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 300000 +integrator = rk2 +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 + + +fname = orszag_tang.out2.00001.h5 +use_tf = false +use_dt = false +skip_b_cleanup = false +regrid_only = true + + +rel_tolerance = 1.e-9 + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 1.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + + +file_type = rst +dt = 50.0 + + +file_type = hst +dt = 0.1 diff --git a/pars/resize_orszag_tang.par b/pars/resize_orszag_tang.par index 92392107..95340ec6 100644 --- a/pars/resize_orszag_tang.par +++ b/pars/resize_orszag_tang.par @@ -29,15 +29,12 @@ ox3_bc = periodic nx1 = 512 -nx2 = 512 +nx2 = 256 nx3 = 1 -base = spherical_ks -transform = fmks -a = 0.9375 -hslope = 0.3 -r_out = 1000 +base = cartesian_minkowski +transform = none tlim = 300000 From 0ab19446c4cb6ec03b36916b0050b9532ebfdffc Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Mon, 5 Dec 2022 18:03:55 -0500 Subject: [PATCH 040/219] updated Bondi --- kharma/prob/bondi.hpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 53245a4d..de456c00 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -55,23 +55,36 @@ TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin); * * Used for initialization and boundary conditions */ -TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); +TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); // (Hyerin) why did you change it to interior? /** * Supporting functions for Bondi flow calculations * * Adapted from M. Chandra + * Modified by Hyerin Cho and Ramesh Narayan */ KOKKOS_INLINE_FUNCTION Real get_Tfunc(const Real T, const GReal r, const Real C1, const Real C2, const Real n) { return m::pow(1. + (1. + n) * T, 2.) * (1. - 2. / r + m::pow(C1 / m::pow(r,2) / m::pow(T, n), 2.)) - C2; } -KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n) +KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n, const Real rs) { Real rtol = 1.e-12; Real ftol = 1.e-14; - Real Tmin = 0.6 * (m::sqrt(C2) - 1.) / (n + 1); - Real Tmax = m::pow(C1 * m::sqrt(2. / m::pow(r,3)), 1. / n); + Real Tinf = (m::sqrt(C2) - 1.) / (n + 1); // temperature at infinity + Real Tnear = m::pow(C1 * m::sqrt(2. / m::pow(r,3)), 1. / n); // temperature near the BH + Real Tmin, Tmax; + + // There are two branches of solutions (see Michel et al. 1971) and the two branches cross at rs. + // These bounds are set to only select the inflowing solution only. + if (r 0) return -1; - Th = (f1 * T0 - f0 * T1) / (f1 - f0); + Th = (T0 + T1) / 2.; // a simple bisection method which is stable and fast fh = get_Tfunc(Th, r, C1, C2, n); Real epsT = rtol * (Tmin + Tmax); while (m::abs(Th - T0) > epsT && m::abs(Th - T1) > epsT && m::abs(fh) > ftol) { - if (fh * f0 < 0.) { + if (fh * f0 > 0.) { T0 = Th; f0 = fh; } else { @@ -94,7 +107,7 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c f1 = fh; } - Th = (f1 * T0 - f0 * T1) / (f1 - f0); + Th = (T0 + T1) / 2.; fh = get_Tfunc(Th, r, C1, C2, n); } @@ -128,7 +141,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const Coordin // be a little cautious about initializing the Ergosphere zones if (ks.a > 0.1 && r < 2) return; - Real T = get_T(r, C1, C2, n); + Real T = get_T(r, C1, C2, n, rs); Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real rho = m::pow(T, n); Real u = rho * T * n; From 77ab5c31ee999a4358a5aa7d51ed5cccfc0b2ac9 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Wed, 7 Dec 2022 08:21:42 -0500 Subject: [PATCH 041/219] fixed hdf5_utils to be more generally used --- kharma/prob/hdf5_utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index a97da26e..40d1d7ad 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -383,10 +383,11 @@ int hdf5_read_single_val(void *val, const char *name, hsize_t hdf5_type) int hdf5_read_array(void *data, const char *name, size_t rank, hsize_t *fdims, hsize_t *fstart, hsize_t *fcount, hsize_t *mdims, hsize_t *mstart, hsize_t hdf5_type) { - hid_t filespace = H5Screate_simple(4, fdims, NULL); + //hid_t filespace = H5Screate_simple(4, fdims, NULL); + hid_t filespace = H5Screate_simple(rank, fdims, NULL); // edited by Hyerin H5Sselect_hyperslab(filespace, H5S_SELECT_SET, fstart, NULL, fcount, NULL); - hid_t memspace = H5Screate_simple(4, mdims, NULL); + hid_t memspace = H5Screate_simple(rank, mdims, NULL); H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mstart, NULL, fcount, NULL); From 954eeb95a3d601f15cae104f305653cb98533156 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Jan 2023 17:03:05 -0600 Subject: [PATCH 042/219] Formal regrid/resize test. Doubles as a mild divB clean test. --- .gitlab-ci.yml | 2 +- kharma/prob/hdf5_utils.cpp | 5 +- pars/orszag_tang.par | 11 ++- pars/regrid_orszag_tang.par | 59 ---------------- tests/clean_tests.sh | 2 +- tests/regrid/orszag_tang_with_restarts.par | 67 +++++++++++++++++++ tests/regrid/regrid_orszag_tang.par | 53 +++++++++++++++ {pars => tests/regrid}/resize_orszag_tang.par | 38 +++++------ tests/regrid/run.sh | 52 ++++++++++++++ 9 files changed, 198 insertions(+), 91 deletions(-) delete mode 100644 pars/regrid_orszag_tang.par create mode 100644 tests/regrid/orszag_tang_with_restarts.par create mode 100644 tests/regrid/regrid_orszag_tang.par rename {pars => tests/regrid}/resize_orszag_tang.par (56%) create mode 100755 tests/regrid/run.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86052584..c93d3da0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,5 +72,5 @@ tests: - ./run.sh parallel: matrix: - - TEST: [bondi, bz_monopole, emhdmodes, mhdmodes, noh, reinit, restart, tilt_init, torus_sanity] + - TEST: [bondi, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index 40d1d7ad..a183beb2 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -399,10 +399,9 @@ int hdf5_read_array(void *data, const char *name, size_t rank, fprintf(stderr,"Reading arr %s:\n", path); fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); - fprintf(stderr,"File read size: %llu %llu %llu %llu\n\n", fcount[0], fcount[1], fcount[2], fcount[3]); - + fprintf(stderr,"File read size: %llu %llu %llu %llu\n", fcount[0], fcount[1], fcount[2], fcount[3]); fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); - fprintf(stderr,"Memory start: %llu %llu %llu %llu\n", mstart[0], mstart[1], mstart[2], mstart[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index 4d2fb46f..79a71cd7 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -44,9 +44,6 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 - -type = imex - verbose = 0 flag_verbose = 0 @@ -62,6 +59,8 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 - -file_type = rst -dt = 10.0 +# This problem is generally much too short to need +# checkpointing. However, we have a test which uses it. +# +#file_type = rst +#dt = 10.0 diff --git a/pars/regrid_orszag_tang.par b/pars/regrid_orszag_tang.par deleted file mode 100644 index 28a1e91c..00000000 --- a/pars/regrid_orszag_tang.par +++ /dev/null @@ -1,59 +0,0 @@ -# Restart from an iharm3d snapshot file, resizing to specified mesh -# Note most parameters here will carry through to running after -# restarting, as iharm3d restart files do not specify much - - -problem_id = resize_restart - - -# Set by restart file - - -nx1 = 64 -nx2 = 64 -nx3 = 1 - - -base = cartesian_minkowski -transform = none - - -tlim = 300000 -integrator = rk2 -dt_min = 0.00001 - - -cfl = 0.9 -gamma = 1.666667 - - -fname = orszag_tang.out2.00001.h5 -use_tf = false -use_dt = false -skip_b_cleanup = false -regrid_only = true - - -rel_tolerance = 1.e-9 - - -disable_floors = true - - -verbose = 1 -flag_verbose = 2 -extra_checks = 1 - - -file_type = hdf5 -dt = 1.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag - - -file_type = rst -dt = 50.0 - - -file_type = hst -dt = 0.1 diff --git a/tests/clean_tests.sh b/tests/clean_tests.sh index e0ba4a67..62249051 100755 --- a/tests/clean_tests.sh +++ b/tests/clean_tests.sh @@ -2,4 +2,4 @@ # Cleans all temporary/gitignore files from tests TEST_DIR=$(dirname "$(readlink -f "$0")") -rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* +rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* diff --git a/tests/regrid/orszag_tang_with_restarts.par b/tests/regrid/orszag_tang_with_restarts.par new file mode 100644 index 00000000..c732e718 --- /dev/null +++ b/tests/regrid/orszag_tang_with_restarts.par @@ -0,0 +1,67 @@ +# Orszag-Tang Vortex problem: +# Generate current sheets on short timescales +# Adds a restart file output at 50 time units +# Also uses ImEx driver, so that the restart +# file contains all the primitive variables. +# Also omits history file + + +problem_id = orszag_tang + + +refinement = none +numlevel = 1 + +nx1 = 256 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 256 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 256 +nx2 = 128 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 100.0 +integrator = rk2 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +type = imex + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + + +file_type = hdf5 +dt = 1000.0 # Only output final dump +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon + + +file_type = rst +dt = 10.0 diff --git a/tests/regrid/regrid_orszag_tang.par b/tests/regrid/regrid_orszag_tang.par new file mode 100644 index 00000000..3ac4870a --- /dev/null +++ b/tests/regrid/regrid_orszag_tang.par @@ -0,0 +1,53 @@ +# Regrid an OT vortex, keeping all properties but the block size + + +problem_id = resize_restart + + +# Set by restart file + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 100 +integrator = rk2 + + +cfl = 0.9 + + +type = imex + + +fname = orszag_tang.out1.00005.h5 +use_tf = true +use_dt = false # TODO this is borked somehow +skip_b_cleanup = true +regrid_only = true + + +disable_floors = true + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + +# Have to compare last output file + +file_type = hdf5 +dt = 1000.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + +# Don't check the restart if the last dump matches +# +#file_type = rst +#dt = 1000.0 diff --git a/pars/resize_orszag_tang.par b/tests/regrid/resize_orszag_tang.par similarity index 56% rename from pars/resize_orszag_tang.par rename to tests/regrid/resize_orszag_tang.par index 95340ec6..467090f4 100644 --- a/pars/resize_orszag_tang.par +++ b/tests/regrid/resize_orszag_tang.par @@ -1,6 +1,4 @@ -# Restart from an iharm3d snapshot file, resizing to specified mesh -# Note most parameters here will carry through to running after -# restarting, as iharm3d restart files do not specify much +# Resize an OT vortex, keeping most properties problem_id = resize_restart @@ -37,41 +35,39 @@ base = cartesian_minkowski transform = none -tlim = 300000 +tlim = 100 integrator = rk2 -dt_min = 0.00001 cfl = 0.9 -gamma = 1.666667 + + +type = imex -fname = orszag_tang.out2.00001.h5 +fname = orszag_tang.out1.00009.h5 use_tf = false use_dt = false skip_b_cleanup = false -rel_tolerance = 1.e-9 +rel_tolerance = 1.e-11 disable_floors = true -verbose = 1 -flag_verbose = 2 -extra_checks = 1 +verbose = 0 +flag_verbose = 0 +extra_checks = 0 - -file_type = hdf5 -dt = 1.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag +# +#file_type = hdf5 +#dt = 1000.0 +#single_precision_output = true +#variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag +# We only need to check the last restart file, specifically divB file_type = rst -dt = 50.0 - - -file_type = hst -dt = 0.1 +dt = 1000.0 diff --git a/tests/regrid/run.sh b/tests/regrid/run.sh new file mode 100755 index 00000000..4748290d --- /dev/null +++ b/tests/regrid/run.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Bash script testing a fresh Orszag-Tang vortex vs a version +# re-gridded to 64^2 tiles in the middle of the run, +# and then a version resized to twice the resolution + +# TODO the first comparison should really be binary-identical + +exit_code=0 + +# Set paths +KHARMADIR=../.. + +$KHARMADIR/run.sh -i ./orszag_tang_with_restarts.par >log_orig.txt 2>&1 + +mv orszag_tang.out0.final.phdf orszag_tang.out0.final.orig.phdf + +sleep 1 + +pyharm-convert --to_restart orszag_tang.out1.00005.rhdf orszag_tang.out1.00009.rhdf + +sleep 1 + +$KHARMADIR/run.sh -i ./regrid_orszag_tang.par >log_regrid.txt 2>&1 + +mv resize_restart.out0.final.phdf resize_restart.out0.final.regrid.phdf + +# compare.py allows for small (5e-10) difference +check_code=0 +pyharm-diff orszag_tang.out0.final.orig.phdf resize_restart.out0.final.regrid.phdf -o compare_regrid --rel_tol=0.002 || check_code=$? +if [[ $check_code != 0 ]]; then + echo Regrid test FAIL: $check_code + exit_code=1 +else + echo Regrid test success +fi + +# Finally, test that we can sanely resize the dump, too +# This won't output .phdf files, only restarts (.rhdf) +$KHARMADIR/run.sh -i ./resize_orszag_tang.par >log_resize.txt 2>&1 + +# Check the final .rhdf file for sanity (i.e., divB small) +check_code=0 +pyharm-check-basics resize_restart.out1.final.rhdf || check_code=$? +if [[ $check_code != 0 ]]; then + echo Resize test FAIL: $check_code + exit_code=1 +else + echo Resize test success +fi + +exit $exit_code From d6a5c811e9a7befc40779abf98223b80c9cba869 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 5 Jan 2023 09:58:27 -0700 Subject: [PATCH 043/219] Doc touch-ups --- kharma/prob/interpolation.hpp | 45 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index ed6a0272..9827bf71 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -36,36 +36,22 @@ #include "decs.hpp" /** - * Routines for interpolating on a grid, with values given in a flattened array. - * Mostly used in resize_restart.cpp, which must interpolate from a grid corresponding - * to an old simulation, read from a file. + * Routines for interpolating on a grid, using values given in a flattened array. + * Mostly used in resize_restart.cpp, which must interpolate from old simulation + * data. * - * Note that resizing a file nearly always requires fixing the resulting magentic field - * divergence -- see b_cleanup/ for details. + * Note that resizing or resampling of magnetic fields usually requires + * fixing a resulting divergence -- see b_cleanup/ for details. */ - namespace Interpolation { /** - * Finds the closest grid zone which lies to the left of the given point in X1,X2, and X3, - * along with the distance 'del' from that center to X in each coordinate, - * for interpolation purposes. - * - * Example (from ipole, ) + * Finds the closest grid zone index (i,j,k) with a center left of the given point. + * Additionally returns the point's proportional distance measured from the left + * zone center to the right (e.g., to (i+1, j, k) in X1) * - * 0 0.5 1 - * [ | ] - * A B C DE F - * - * startx = 0. - * dx = 0.5 - * - * A -> (-1, 0.5) - * B -> ( 0, 0.0) - * C -> ( 0, 0.5) - * D -> ( 0, 0.9) - * E -> ( 1, 0.0) - * F -> ( 1, 0.5) + * This proportion is useful in interpolation, since linear interpolation corresponds to + * del*var[i+1] + (1. - del)*var[i] */ KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], const GReal startx[GR_DIM], @@ -87,7 +73,8 @@ KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], } /** - * Translates a point X in native coordinates to a grid zone. + * Return the grid zone index (i,j,k) corresponding which contains the point X. + * Note this is different from the above! */ KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], const GReal startx[GR_DIM], @@ -101,16 +88,16 @@ KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], k = (int) ((X[3] - startx[3]) / dx[3] + 1000) - 1000; } +// For using the ipole routines in a recognizable form on a 1D array +#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) + /** - * Dumb linear interpolation: no special cases for boundaries + * Dumb linear interpolation: no special cases for boundaries. * Takes indices i,j,k and a block size n1, n2, n3, * as well as a flat array var. * * TODO version(s) with View(s) for real device-side operation */ -// For using the ipole routines in a recognizable form on a 1D array -#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) - KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, const int& n1, const int& n2, const int& n3, const double del[4], const double *var) From b385685fcbc9a226a2c4a8c6f03d637d13aa0b49 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 21 Mar 2023 11:57:33 -0600 Subject: [PATCH 044/219] Port multizone (kharma-restart and boundaries changes) to kharma-next. Compiles --- .github/workflows/gitlab.yml | 20 + .gitignore | 1 + .gitlab-ci-docker.yml | 116 + .gitlab-ci.yml | 112 +- .gitmodules | 7 +- CMakeLists.txt | 17 +- README.md | 7 + bin/nvcc_wrapper | 4 +- cmake/FindFFTW.cmake | 98 + external/kokkos-kernels | 1 - ...tched_ApplyHouseholder_Serial_Internal.hpp | 105 + .../KokkosBatched_ApplyPivot_Decl.hpp | 39 + .../KokkosBatched_ApplyPivot_Impl.hpp | 212 ++ .../KokkosBatched_ApplyPivot_Internal.hpp | 348 +++ .../KokkosBatched_ApplyQ_Decl.hpp | 88 + .../KokkosBatched_ApplyQ_Serial_Impl.hpp | 56 + .../KokkosBatched_ApplyQ_Serial_Internal.hpp | 193 ++ external/kokkos-kernels/KokkosBatched_Dot.hpp | 161 ++ .../KokkosBatched_Dot_Internal.hpp | 435 ++++ .../KokkosBatched_FindAmax_Internal.hpp | 68 + .../KokkosBatched_Gemm_Serial_Internal.hpp | 137 ++ .../KokkosBatched_Gemv_Serial_Internal.hpp | 98 + ...kosBatched_Householder_Serial_Internal.hpp | 78 + .../KokkosBatched_InnerGemmFixC_Decl.hpp | 61 + ...okkosBatched_InnerGemmFixC_Serial_Impl.hpp | 1560 ++++++++++++ .../KokkosBatched_InnerLU_Decl.hpp | 31 + .../KokkosBatched_InnerLU_Serial_Impl.hpp | 394 ++++ ...osBatched_InnerMultipleDotProduct_Decl.hpp | 33 + ...ed_InnerMultipleDotProduct_Serial_Impl.hpp | 305 +++ .../KokkosBatched_InnerTrsm_Decl.hpp | 106 + .../KokkosBatched_InnerTrsm_Serial_Impl.hpp | 1577 +++++++++++++ .../kokkos-kernels/KokkosBatched_LU_Decl.hpp | 57 + .../KokkosBatched_LU_Serial_Impl.hpp | 78 + .../KokkosBatched_LU_Serial_Internal.hpp | 128 + .../kokkos-kernels/KokkosBatched_QR_Decl.hpp | 80 + .../KokkosBatched_QR_Serial_Impl.hpp | 27 + .../KokkosBatched_QR_Serial_Internal.hpp | 151 ++ .../KokkosBatched_Trsv_Decl.hpp | 205 ++ .../KokkosBatched_Trsv_Serial_Impl.hpp | 322 +++ .../KokkosBatched_Trsv_Serial_Internal.hpp | 208 ++ .../kokkos-kernels/KokkosBatched_Util.hpp | 903 +++++++ .../kokkos-kernels/KokkosBatched_Vector.hpp | 297 +++ .../KokkosBatched_Vector_SIMD.hpp | 810 +++++++ .../KokkosBatched_Vector_SIMD_Arith.hpp | 887 +++++++ .../KokkosBatched_Vector_SIMD_Logical.hpp | 123 + .../KokkosBatched_Vector_SIMD_Math.hpp | 301 +++ .../KokkosBatched_Vector_SIMD_Misc.hpp | 165 ++ .../KokkosBatched_Vector_SIMD_Relation.hpp | 68 + .../KokkosBatched_Vector_SIMD_View.hpp | 262 +++ .../KokkosBlas1_serial_scal_impl.hpp | 86 + .../kokkos-kernels/KokkosBlas1_set_impl.hpp | 166 ++ .../kokkos-kernels/KokkosKernels_Half.hpp | 91 + .../kokkos-kernels/KokkosKernels_Macros.hpp | 119 + .../KokkosKernels_SimpleUtils.hpp | 412 ++++ .../kokkos-kernels}/KokkosKernels_config.h | 0 .../kokkos-kernels/Kokkos_ArithTraits.hpp | 2083 +++++++++++++++++ external/kokkos-kernels/LICENSE | 46 + external/parthenon | 2 +- .../parthenon-use-gr-coordinates.patch | 34 + ...vironm.patch => variant-fix-xl-sycl.patch} | 0 kharma/CMakeLists.txt | 41 +- kharma/b_cd/b_cd.cpp | 98 +- kharma/b_cd/b_cd.hpp | 15 +- kharma/b_cd/seed_B_cd.cpp | 17 +- kharma/b_cleanup/b_cleanup.cpp | 145 +- kharma/b_cleanup/b_cleanup.hpp | 11 +- kharma/b_flux_ct/b_flux_ct.cpp | 444 ++-- kharma/b_flux_ct/b_flux_ct.hpp | 96 +- kharma/b_flux_ct/seed_B_ct.cpp | 48 +- kharma/boundaries/boundaries.cpp | 370 +++ kharma/{ => boundaries}/boundaries.hpp | 94 +- .../boundaries_forked_cpp.txt} | 18 +- kharma/coordinates/coordinate_embedding.hpp | 9 +- kharma/coordinates/coordinate_systems.hpp | 22 +- kharma/coordinates/gr_coordinates.cpp | 4 +- kharma/coordinates/gr_coordinates.hpp | 30 +- kharma/current/current.cpp | 16 +- kharma/current/current.hpp | 2 +- kharma/debug.cpp | 241 +- kharma/debug.hpp | 38 +- kharma/decs.hpp | 76 +- kharma/driver/imex_step.cpp | 292 +++ kharma/driver/kharma_driver.cpp | 275 +++ kharma/driver/kharma_driver.hpp | 144 ++ kharma/driver/kharma_step.cpp | 262 +++ kharma/driver/simple_step.cpp | 167 ++ kharma/electrons/electrons.cpp | 278 ++- kharma/electrons/electrons.hpp | 55 +- kharma/electrons/gaussian.hpp | 37 + kharma/emhd/emhd.cpp | 128 +- kharma/emhd/emhd.hpp | 132 +- kharma/emhd/emhd_limits.hpp | 154 ++ kharma/emhd/emhd_sources.hpp | 12 +- kharma/emhd/emhd_utils.hpp | 102 +- kharma/floors/floors.cpp | 179 +- kharma/floors/floors.hpp | 520 +--- kharma/floors/floors_functions.hpp | 414 ++++ kharma/flux.cpp | 119 - kharma/flux/flux.cpp | 178 ++ kharma/flux/flux.hpp | 90 + kharma/{ => flux}/flux_functions.hpp | 96 +- kharma/{flux.hpp => flux/get_flux.hpp} | 149 +- kharma/grmhd/grmhd.cpp | 287 +-- kharma/grmhd/grmhd.hpp | 53 +- kharma/grmhd/grmhd_functions.hpp | 39 +- kharma/grmhd/grmhd_reductions.hpp | 134 ++ kharma/grmhd/pack.hpp | 35 +- kharma/grmhd/source.cpp | 93 - kharma/harm_driver.cpp | 288 --- kharma/harm_driver.hpp | 80 - kharma/imex_driver.cpp | 362 --- kharma/implicit/implicit.cpp | 333 ++- kharma/implicit/implicit.hpp | 38 +- kharma/{grmhd => inverter}/fixup.cpp | 121 +- kharma/inverter/invert_template.hpp | 89 + kharma/inverter/inverter.cpp | 165 ++ kharma/inverter/inverter.hpp | 81 + .../{grmhd/U_to_P.hpp => inverter/onedw.hpp} | 138 +- kharma/kharma.cpp | 307 ++- kharma/kharma.hpp | 71 +- kharma/kharma_package.cpp | 229 ++ kharma/kharma_package.hpp | 163 ++ kharma/kharma_utils.hpp | 89 +- kharma/main.cpp | 77 +- kharma/mpi.hpp | 65 - kharma/prob/b_field_tools.cpp | 165 -- kharma/prob/b_field_tools.hpp | 30 +- kharma/prob/blob.hpp | 4 +- kharma/prob/bondi.cpp | 134 +- kharma/prob/bondi.hpp | 97 +- kharma/prob/bz_monopole.cpp | 8 +- kharma/prob/bz_monopole.hpp | 2 +- kharma/prob/elec/driven_turbulence.hpp | 207 ++ kharma/prob/elec/gaussian.cpp | 122 + kharma/prob/elec/hubble.cpp | 215 ++ .../{imex_driver.hpp => prob/elec/hubble.hpp} | 45 +- kharma/prob/{ => elec}/noh.hpp | 76 +- kharma/prob/emhd/anisotropic_conduction.hpp | 6 +- kharma/prob/emhd/bondi_viscous.cpp | 122 - kharma/prob/emhd/bondi_viscous.hpp | 160 -- kharma/prob/emhd/conducting_atmosphere.cpp | 252 +- kharma/prob/emhd/conducting_atmosphere.hpp | 4 +- kharma/prob/emhd/emhdmodes.hpp | 14 +- kharma/prob/emhd/emhdshock.hpp | 7 +- kharma/prob/explosion.hpp | 8 +- kharma/prob/fm_torus.cpp | 79 +- kharma/prob/fm_torus.hpp | 9 +- kharma/prob/hdf5_utils.cpp | 9 +- kharma/prob/interpolation.hpp | 212 +- kharma/prob/kelvin_helmholtz.hpp | 10 +- kharma/prob/mhdmodes.hpp | 86 +- kharma/prob/orszag_tang.hpp | 6 +- kharma/prob/post_initialize.cpp | 200 +- kharma/prob/post_initialize.hpp | 11 +- kharma/prob/prob_common.hpp | 22 + kharma/prob/problem.cpp | 87 +- kharma/prob/resize_restart.cpp | 297 ++- kharma/prob/resize_restart.hpp | 4 +- kharma/prob/resize_restart_kharma.cpp | 23 +- kharma/prob/resize_restart_kharma.hpp | 4 +- kharma/prob/shock_tube.hpp | 4 +- kharma/reconstruction.hpp | 174 +- kharma/reductions/reductions.cpp | 301 ++- kharma/reductions/reductions.hpp | 336 +-- kharma/types.hpp | 148 +- kharma/wind/wind.cpp | 18 +- kharma/wind/wind.hpp | 6 +- machines/bp.sh | 44 +- machines/illinois.sh | 2 +- machines/incite.sh | 6 +- make.sh | 20 +- pars/bondi.par | 17 +- pars/bondi_viscous.par | 32 +- pars/conducting_atmosphere.par | 42 +- pars/driven_turbulence.par | 87 + pars/emhdmodes.par | 23 +- pars/hubble.par | 86 + pars/mhdmodes.par | 28 +- pars/noh.par | 25 +- pars/orszag_tang.par | 12 +- pars/rest_conserve.par | 80 + pars/sane.par | 16 +- pars/sane2d.par | 2 +- pars/sane2d_cooling.par | 97 + pars/sane_divb_2d.par | 3 - pars/sane_emhd.par | 108 + pars/sane_imex.par | 98 + pars/sane_perf.par | 2 +- pars/sane_tilt.par | 2 +- pars/shocks/noh_43.par | 76 + pars/shocks/noh_53.par | 76 + scripts/batch/delta.sb | 2 +- tests/README.md | 41 +- tests/bondi/check.py | 61 +- tests/bondi/run.sh | 19 +- tests/bondi_viscous/check.py | 95 +- tests/bondi_viscous/check.sh | 15 - tests/bondi_viscous/run.sh | 54 +- tests/bz_monopole/run.sh | 2 +- tests/clean_tests.sh | 2 +- tests/conducting_atmosphere/check.py | 71 +- tests/conducting_atmosphere/check.sh | 19 - tests/conducting_atmosphere/run.sh | 46 +- tests/hubble/make_plots.py | 40 + tests/mhdmodes/check.py | 17 +- tests/mhdmodes/run.sh | 75 +- tests/noh/check.py | 17 +- tests/noh/run.sh | 6 +- tests/regrid/orszag_tang_with_restarts.par | 67 + tests/regrid/regrid_orszag_tang.par | 53 + tests/regrid/resize_orszag_tang.par | 73 + tests/regrid/run.sh | 52 + 212 files changed, 23187 insertions(+), 5882 deletions(-) create mode 100644 .github/workflows/gitlab.yml create mode 100644 .gitlab-ci-docker.yml create mode 100644 cmake/FindFFTW.cmake delete mode 160000 external/kokkos-kernels create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyHouseholder_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyPivot_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyPivot_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyPivot_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyQ_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Dot.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_FindAmax_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Gemm_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Gemv_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Householder_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerLU_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerLU_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerMultipleDotProduct_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerMultipleDotProduct_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerTrsm_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_InnerTrsm_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_LU_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_LU_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_LU_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_QR_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_QR_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_QR_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Trsv_Decl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Trsv_Serial_Impl.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Trsv_Serial_Internal.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Util.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_Arith.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_Logical.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_Math.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_Misc.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_Relation.hpp create mode 100644 external/kokkos-kernels/KokkosBatched_Vector_SIMD_View.hpp create mode 100644 external/kokkos-kernels/KokkosBlas1_serial_scal_impl.hpp create mode 100644 external/kokkos-kernels/KokkosBlas1_set_impl.hpp create mode 100644 external/kokkos-kernels/KokkosKernels_Half.hpp create mode 100644 external/kokkos-kernels/KokkosKernels_Macros.hpp create mode 100644 external/kokkos-kernels/KokkosKernels_SimpleUtils.hpp rename {kharma/implicit => external/kokkos-kernels}/KokkosKernels_config.h (100%) create mode 100644 external/kokkos-kernels/Kokkos_ArithTraits.hpp create mode 100644 external/kokkos-kernels/LICENSE create mode 100644 external/patches/parthenon-use-gr-coordinates.patch rename external/patches/{0001-Fix-compiling-variant-under-IBM-XL-and-SYCL-environm.patch => variant-fix-xl-sycl.patch} (100%) create mode 100644 kharma/boundaries/boundaries.cpp rename kharma/{ => boundaries}/boundaries.hpp (50%) rename kharma/{boundaries.cpp => boundaries/boundaries_forked_cpp.txt} (97%) create mode 100644 kharma/driver/imex_step.cpp create mode 100644 kharma/driver/kharma_driver.cpp create mode 100644 kharma/driver/kharma_driver.hpp create mode 100644 kharma/driver/kharma_step.cpp create mode 100644 kharma/driver/simple_step.cpp create mode 100644 kharma/electrons/gaussian.hpp create mode 100644 kharma/emhd/emhd_limits.hpp create mode 100644 kharma/floors/floors_functions.hpp delete mode 100644 kharma/flux.cpp create mode 100644 kharma/flux/flux.cpp create mode 100644 kharma/flux/flux.hpp rename kharma/{ => flux}/flux_functions.hpp (85%) rename kharma/{flux.hpp => flux/get_flux.hpp} (71%) create mode 100644 kharma/grmhd/grmhd_reductions.hpp delete mode 100644 kharma/grmhd/source.cpp delete mode 100644 kharma/harm_driver.cpp delete mode 100644 kharma/harm_driver.hpp delete mode 100644 kharma/imex_driver.cpp rename kharma/{grmhd => inverter}/fixup.cpp (58%) create mode 100644 kharma/inverter/invert_template.hpp create mode 100644 kharma/inverter/inverter.cpp create mode 100644 kharma/inverter/inverter.hpp rename kharma/{grmhd/U_to_P.hpp => inverter/onedw.hpp} (78%) create mode 100644 kharma/kharma_package.cpp create mode 100644 kharma/kharma_package.hpp delete mode 100644 kharma/mpi.hpp delete mode 100644 kharma/prob/b_field_tools.cpp create mode 100644 kharma/prob/elec/driven_turbulence.hpp create mode 100644 kharma/prob/elec/gaussian.cpp create mode 100644 kharma/prob/elec/hubble.cpp rename kharma/{imex_driver.hpp => prob/elec/hubble.hpp} (53%) rename kharma/prob/{ => elec}/noh.hpp (62%) delete mode 100644 kharma/prob/emhd/bondi_viscous.cpp delete mode 100644 kharma/prob/emhd/bondi_viscous.hpp create mode 100644 pars/driven_turbulence.par create mode 100644 pars/hubble.par create mode 100644 pars/rest_conserve.par create mode 100644 pars/sane2d_cooling.par create mode 100644 pars/sane_emhd.par create mode 100644 pars/sane_imex.par create mode 100644 pars/shocks/noh_43.par create mode 100644 pars/shocks/noh_53.par delete mode 100755 tests/bondi_viscous/check.sh delete mode 100755 tests/conducting_atmosphere/check.sh create mode 100644 tests/hubble/make_plots.py create mode 100644 tests/regrid/orszag_tang_with_restarts.par create mode 100644 tests/regrid/regrid_orszag_tang.par create mode 100644 tests/regrid/resize_orszag_tang.par create mode 100755 tests/regrid/run.sh diff --git a/.github/workflows/gitlab.yml b/.github/workflows/gitlab.yml new file mode 100644 index 00000000..9603a859 --- /dev/null +++ b/.github/workflows/gitlab.yml @@ -0,0 +1,20 @@ +name: Mirror and run GitLab CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Mirror + trigger CI + uses: SvanBoxel/gitlab-mirror-and-ci-action@master + with: + args: "https://gitlab.com/AFD-Illinois/kharma" + env: + FORCE_PUSH: "false" + GITLAB_HOSTNAME: "gitlab.com" + GITLAB_USERNAME: "bprather" + GITLAB_PASSWORD: ${{ secrets.GITLAB_PASSWORD }} + GITLAB_PROJECT_ID: "19796382" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 82062861..eb9e862e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ convergence.txt *.h5 *.png *.mp4 +*.webm core.* frames_*/ diff --git a/.gitlab-ci-docker.yml b/.gitlab-ci-docker.yml new file mode 100644 index 00000000..d111f37b --- /dev/null +++ b/.gitlab-ci-docker.yml @@ -0,0 +1,116 @@ +# Continuous Integration testing for KHARMA +# a.k.a did we break the basics? + +# Build on Nvidia image. +# Can pretty easily change this out, with changes to build +# Someday we'll build & push a KHARMA image, then test that +image: nvcr.io/nvidia/nvhpc:23.1-devel-cuda12.0-rockylinux8 + +variables: + OMP_NUM_THREADS: 28 + OMP_PROC_BIND: "false" + MPI_EXE: mpirun + MPI_NUM_PROCS: 2 + OMPI_ALLOW_RUN_AS_ROOT: 1 + OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 + GIT_SUBMODULE_STRATEGY: recursive + +### DEFAULT TEST BEHAVIOR ### +default: + # Be default: install pyharm, then run test in cwd + # For new tests, write one run.sh script which runs/verifies + # interleaved, and prints a summary of results. + before_script: + - export PATH="$HOME/.local/bin:$PATH" + - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" + - bash Miniforge3.sh -b -p "/home/conda" + - source "/home/conda/etc/profile.d/conda.sh" + - conda install h5py + - git clone https://github.com/AFD-Illinois/pyharm.git /home/pyharm + - conda activate + - cd /home/pyharm + - pip install --user . + - cd - + + # Always keep logs and plots. Results should be printed to console! + artifacts: + when: always + paths: + - tests/*/*.png + - tests/*/*.txt + +# Tests can be executed in parallel, +# but be careful about GPU arch +stages: + - build + - tests + +# Build, obviously overrides script/artifacts +build: + stage: build + variables: + NPROC: "" + HOST_ARCH: HSW + before_script: + - echo "Skipping pyharm install in build." + script: + - export PREFIX_PATH=$PWD/external/hdf5 + - ./make.sh clean cuda hdf5 + artifacts: + paths: + - kharma.* + - make_args + +bondi: + stage: tests + script: + - cd tests/bondi + - ./run.sh + +mhdmodes: + stage: tests + script: + - cd tests/mhdmodes + - ./run.sh + +emhdmodes: + stage: tests + script: + - cd tests/emhdmodes + - ./run.sh + +noh: + stage: tests + script: + - cd tests/noh + - ./run.sh + +bz_monopole: + stage: tests + script: + - cd tests/bz_monopole + - ./run.sh + +tilt_init: + stage: tests + script: + - cd tests/tilt_init + - ./run.sh + +torus_sanity: + stage: tests + script: + - cd tests/torus_sanity + - ./run.sh + +restart: + stage: tests + script: + - cd tests/restart + - ./run.sh + +reinit: + stage: tests + script: + - cd tests/reinit + - ./run.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 23a1abda..98a700cc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,35 +1,32 @@ # Continuous Integration testing for KHARMA # a.k.a did we break the basics? - -# Build on Nvidia image. -# Can pretty easily change this out, with changes to build -# Someday we'll build & push a KHARMA image, then test that -image: nvcr.io/nvidia/nvhpc:22.9-devel-cuda_multi-rockylinux8 +# This version run on LANL Darwin +# See .gitlab-ci-docker.yml for a generic version, +# which can be run on any Docker runner w/GPUs variables: - OMP_NUM_THREADS: 8 + GIT_SUBMODULE_STRATEGY: recursive + SCHEDULER_PARAMETERS: "-N 1 --qos=debug -p volta-x86" + HOST_ARCH: HSW + NPROC: "" + OMP_NUM_THREADS: 28 OMP_PROC_BIND: "false" MPI_EXE: mpirun - OMPI_ALLOW_RUN_AS_ROOT: 1 - OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 - GIT_SUBMODULE_STRATEGY: recursive + MPI_NUM_PROCS: 2 + HTTP_PROXY: http://proxyout.lanl.gov:8080 + http_proxy: http://proxyout.lanl.gov:8080 + HTTPS_PROXY: http://proxyout.lanl.gov:8080 + https_proxy: http://proxyout.lanl.gov:8080 + NO_PROXY: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 + no_proxy: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 ### DEFAULT TEST BEHAVIOR ### default: - # Be default: install pyharm, then run test in cwd - # For new tests, write one run.sh script which runs/verifies - # interleaved, and prints a summary of results. + tags: + - darwin-slurm-shared + # Load Python before_script: - - export PATH="$HOME/.local/bin:$PATH" - - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - - bash Miniforge3.sh -b -p "/home/conda" - - source "/home/conda/etc/profile.d/conda.sh" - - conda install h5py - - git clone https://github.com/AFD-Illinois/pyharm.git /home/pyharm - - conda activate - - cd /home/pyharm - - pip install --user . - - cd - + - module load miniconda3 # Always keep logs and plots. Results should be printed to console! artifacts: @@ -44,72 +41,35 @@ stages: - build - tests +# Default rules +.default-rules: + rules: + - if: $CI_COMMIT_BRANCH == "dev" + when: always + - when: manual + allow_failure: false + # Build, obviously overrides script/artifacts build: + extends: .default-rules stage: build - variables: - NPROC: 24 - HOST_ARCH: HSW before_script: - echo "Skipping pyharm install in build." script: - export PREFIX_PATH=$PWD/external/hdf5 - - ./make.sh clean cuda hdf5 + - ./make.sh clean cuda hdf5 volta artifacts: paths: - kharma.* - make_args -bondi: - stage: tests - script: - - cd tests/bondi - - ./run.sh - -mhdmodes: - stage: tests - script: - - cd tests/mhdmodes - - ./run.sh - -emhdmodes: - stage: tests - script: - - cd tests/emhdmodes - - ./run.sh - -noh: - stage: tests - script: - - cd tests/noh - - ./run.sh - -bz_monopole: - stage: tests - script: - - cd tests/bz_monopole - - ./run.sh - -tilt_init: - stage: tests - script: - - cd tests/tilt_init - - ./run.sh - -torus_sanity: - stage: tests - script: - - cd tests/torus_sanity - - ./run.sh - -restart: - stage: tests - script: - - cd tests/restart - - ./run.sh - -reinit: +# Run all tests in parallel +tests: + extends: .default-rules stage: tests script: - - cd tests/reinit + - cd tests/$TEST - ./run.sh + parallel: + matrix: + - TEST: [bondi, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] diff --git a/.gitmodules b/.gitmodules index f05132ea..d5ec6b1b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,10 +1,7 @@ [submodule "external/parthenon"] path = external/parthenon - url = https://github.com/AFD-Illinois/parthenon.git - branch = kharma-stable + url = https://github.com/parthenon-hpc-lab/parthenon.git + branch = bprather/backport-bicgstab [submodule "external/variant"] path = external/variant url = https://github.com/mpark/variant.git -[submodule "external/kokkos-kernels"] - path = external/kokkos-kernels - url = https://github.com/kokkos/kokkos-kernels diff --git a/CMakeLists.txt b/CMakeLists.txt index b76a5971..ad4b435a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,9 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") set(PARTHENON_ENABLE_CPP17 ON CACHE BOOL "KHARMA Override") +# Set the path to include cmake/ dir +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + # Parthenon options set(PARTHENON_DISABLE_EXAMPLES ON CACHE BOOL "KHARMA Override") set(PARTHENON_LINT_DEFAULT OFF CACHE BOOL "KHARMA Override") @@ -39,7 +42,6 @@ set(Kokkos_ENABLE_AGGRESSIVE_VECTORIZATION ON CACHE BOOL "KHARMA Override") set(KokkosKernels_ENABLE_TPL_CUSPARSE OFF CACHE BOOL "KHARMA Override") set(KokkosKernels_ENABLE_TPL_CUBLAS OFF CACHE BOOL "KHARMA Override") - # Offer a KHARMA option to disable the MPI requirement # The only difference from setting PARTHENON_DISABLE_MPI is that # the configure step no longer searches for/fails without it @@ -59,17 +61,8 @@ add_subdirectory(external/parthenon) include_directories(external/parthenon/src) # mpark::variant is header only, don't build anything include_directories(external/variant/include) -# Kokkos kernels: don't build them (very slow), just import all headers -# Requires KokkosKernels_config.h shipped with KHARMA, YMMV -# In case of issues, uncomment the following line to build them -#add_subdirectory(external/kokkos-kernels) -include_directories(external/kokkos-kernels/src) -include_directories(external/kokkos-kernels/src/batched) -include_directories(external/kokkos-kernels/src/common) -include_directories(external/kokkos-kernels/src/batched/dense) -include_directories(external/kokkos-kernels/src/batched/dense/impl) -include_directories(external/kokkos-kernels/src/blas) -include_directories(external/kokkos-kernels/src/blas/impl) +# Our hacked-up version of the Kokkos kernels +include_directories(external/kokkos-kernels) # Finally, build KHARMA add_subdirectory(kharma) diff --git a/README.md b/README.md index 5b437897..fd768d76 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,10 @@ Except for performance tuning, KHARMA has no compile time parameters: all of the ## Hacking KHARMA has some preliminary documentation for developers, hosted in its GitHub [wiki](https://github.com/AFD-Illinois/kharma/wiki). + +## Licenses +KHARMA is made available under the BSD 3-clause license included in each file and in the file LICENSE at the root of this repository. + +This repository also carries a substantial portion of the [Kokkos Kernels](https://github.com/kokkos/kokkos-kernels), in the directory `kharma/implicit/kokkos-kernels-pivoted`, which is provided under the license included in that directory. + +Submodules of this repository, [Parthenon](https://github.com/parthenon-hpc-lab/parthenon) and [mpark::variant](https://github.com/mpark/variant) are made available under their own licenses. \ No newline at end of file diff --git a/bin/nvcc_wrapper b/bin/nvcc_wrapper index efc987ca..8ef357ff 100755 --- a/bin/nvcc_wrapper +++ b/bin/nvcc_wrapper @@ -12,8 +12,8 @@ # or g++ as their back-end compiler. The defaults can be overwritten # by using the usual arguments (e.g., -arch=sm_30 -ccbin icpc). -default_arch="sm_35" -#default_arch="sm_50" +#default_arch="sm_35" +default_arch="sm_70" # # The default C++ compiler. diff --git a/cmake/FindFFTW.cmake b/cmake/FindFFTW.cmake new file mode 100644 index 00000000..b331516d --- /dev/null +++ b/cmake/FindFFTW.cmake @@ -0,0 +1,98 @@ +# - Find the FFTW library +# +# Usage: +# find_package(FFTW [REQUIRED] [QUIET] ) +# +# It sets the following variables: +# FFTW_FOUND ... true if fftw is found on the system +# FFTW_LIBRARIES ... full path to fftw library +# FFTW_INCLUDES ... fftw include directory +# +# The following variables will be checked by the function +# FFTW_USE_STATIC_LIBS ... if true, only static libraries are found +# FFTW_ROOT ... if set, the libraries are exclusively searched +# under this path +# FFTW_LIBRARY ... fftw library to use +# FFTW_INCLUDE_DIR ... fftw include directory +# +#If environment variable FFTWDIR is specified, it has same effect as FFTW_ROOT +if( NOT FFTW_ROOT AND ENV{FFTWDIR} ) + set( FFTW_ROOT $ENV{FFTWDIR} ) +endif() +# Check if we can use PkgConfig +include(CMakeFindDependencyMacro) +find_dependency(PkgConfig) +#Determine from PKG +if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT ) + pkg_check_modules( PKG_FFTW QUIET "fftw3" ) +endif() +#Check whether to search static or dynamic libs +set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} ) +if( ${FFTW_USE_STATIC_LIBS} ) + set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} ) +else() + set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} ) +endif() +if( FFTW_ROOT ) + #find libs + find_library( + FFTW_LIB + NAMES "fftw3" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + find_library( + FFTWF_LIB + NAMES "fftw3f" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + find_library( + FFTWL_LIB + NAMES "fftw3l" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "lib" "lib64" + NO_DEFAULT_PATH + ) + #find includes + find_path( + FFTW_INCLUDES + NAMES "fftw3.h" + PATHS ${FFTW_ROOT} + PATH_SUFFIXES "include" + NO_DEFAULT_PATH + ) +else() + find_library( + FFTW_LIB + NAMES "fftw3" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + find_library( + FFTWF_LIB + NAMES "fftw3f" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + find_library( + FFTWL_LIB + NAMES "fftw3l" + PATHS ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} + ) + find_path( + FFTW_INCLUDES + NAMES "fftw3.h" + PATHS ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} + ) +endif() +set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB}) +if(FFTWL_LIB) + set(FFTW_LIBRARIES ${FFTW_LIBRARIES} ${FFTWL_LIB}) +endif() +set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} ) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFTW DEFAULT_MSG + FFTW_INCLUDES FFTW_LIBRARIES) +mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB) + diff --git a/external/kokkos-kernels b/external/kokkos-kernels deleted file mode 160000 index 04821ac3..00000000 --- a/external/kokkos-kernels +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04821ac3bb916b19fad6b3dabc1f4b9e1049aa0e diff --git a/external/kokkos-kernels/KokkosBatched_ApplyHouseholder_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_ApplyHouseholder_Serial_Internal.hpp new file mode 100644 index 00000000..5f051000 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyHouseholder_Serial_Internal.hpp @@ -0,0 +1,105 @@ +#ifndef __KOKKOSBATCHED_APPLY_HOUSEHOLDER_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_APPLY_HOUSEHOLDER_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== +/// +/// this impl follows the flame interface of householder transformation +/// +struct SerialApplyLeftHouseholderInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const ValueType* tau, + /* */ ValueType* u2, const int u2s, + /* */ ValueType* a1t, const int a1ts, + /* */ ValueType* A2, const int as0, + const int as1, + /* */ ValueType* w1t) { + typedef ValueType value_type; + + /// u2 m x 1 + /// a1t 1 x n + /// A2 m x n + + // apply a single householder transform H from the left to a row vector a1t + // and a matrix A2 + const value_type inv_tau = value_type(1) / (*tau); + + // compute the followings: + // a1t -= inv(tau)(a1t + u2'A2) + // A2 -= u2 inv(tau)(a1t + u2'A2) + + // w1t = a1t + u2'A2 = A2^T conj(u2) + // w1t /= tau + for (int j = 0; j < n; ++j) { + value_type tmp = a1t[j * a1ts]; + for (int i = 0; i < m; ++i) + tmp += Kokkos::Details::ArithTraits::conj(u2[i * u2s]) * + A2[i * as0 + j * as1]; + w1t[j] = tmp * inv_tau; // /= (*tau); + } + + // a1t -= w1t (axpy) + for (int j = 0; j < n; ++j) a1t[j * a1ts] -= w1t[j]; + + // A2 -= u2 w1t (ger) + for (int j = 0; j < n; ++j) + for (int i = 0; i < m; ++i) A2[i * as0 + j * as1] -= u2[i * u2s] * w1t[j]; + + return 0; + } +}; + +struct SerialApplyRightHouseholderInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const ValueType* tau, + /* */ ValueType* u2, const int u2s, + /* */ ValueType* a1, const int a1s, + /* */ ValueType* A2, const int as0, + const int as1, + /* */ ValueType* w1) { + typedef ValueType value_type; + /// u2 n x 1 + /// a1 m x 1 + /// A2 m x n + + // apply a single householder transform H from the left to a row vector a1t + // and a matrix A2 + const value_type inv_tau = value_type(1) / (*tau); + + // compute the followings: + // a1 -= inv(tau)(a1 + A2 u2) + // A2 -= inv(tau)(a1 + A2 u2) u2' + + // w1 = a1 + A2 u2 + // w1 /= tau + for (int i = 0; i < m; ++i) { + value_type tmp = a1[i * a1s]; + for (int j = 0; j < n; ++j) tmp += A2[i * as0 + j * as1] * u2[j * u2s]; + w1[i] = tmp * inv_tau; // \= (*tau); + } + + // a1 -= w1 (axpy) + for (int i = 0; i < m; ++i) a1[i * a1s] -= w1[i]; + + // A2 -= w1 * u2' (ger with conjugate) + for (int j = 0; j < n; ++j) + for (int i = 0; i < m; ++i) + A2[i * as0 + j * as1] -= + w1[i] * Kokkos::Details::ArithTraits::conj(u2[j * u2s]); + + return 0; + } +}; + +} // end namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyPivot_Decl.hpp b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Decl.hpp new file mode 100644 index 00000000..d5d64030 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Decl.hpp @@ -0,0 +1,39 @@ +#ifndef __KOKKOSBATCHED_APPLY_PIVOT_DECL_HPP__ +#define __KOKKOSBATCHED_APPLY_PIVOT_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// TeamVector +/// ========== +template +struct TeamVectorApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, const AViewType &A); + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const PivViewType piv, + const AViewType &A); +}; + +template +struct SerialApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int piv, const AViewType &A); + + template + KOKKOS_INLINE_FUNCTION static int invoke(const PivViewType piv, + const AViewType &A); +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_ApplyPivot_Impl.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyPivot_Impl.hpp b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Impl.hpp new file mode 100644 index 00000000..59c48896 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Impl.hpp @@ -0,0 +1,212 @@ +#ifndef __KOKKOSBATCHED_APPLY_PIVOT_IMPL_HPP__ +#define __KOKKOSBATCHED_APPLY_PIVOT_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_ApplyPivot_Internal.hpp" + +namespace KokkosBatched { + +/// +/// TeamVector Internal Impl +/// ======================== + +/// +/// Forward pivot apply +/// + +/// row swap +template +struct TeamVectorApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, const AViewType &A) { + if (AViewType::rank == 1) { + const int as0 = A.stride(0); + TeamVectorApplyPivotVectorForwardInternal::invoke(member, piv, A.data(), + as0); + } else if (AViewType::rank == 2) { + const int n = A.extent(1), as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixForwardInternal::invoke(member, n, piv, + A.data(), as0, as1); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const PivViewType piv, + const AViewType &A) { + if (AViewType::rank == 1) { + const int plen = piv.extent(0), ps0 = piv.stride(0), as0 = A.stride(0); + TeamVectorApplyPivotVectorForwardInternal::invoke( + member, plen, piv.data(), ps0, A.data(), as0); + } else if (AViewType::rank == 2) { + // row permutation + const int plen = piv.extent(0), ps0 = piv.stride(0), n = A.extent(1), + as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixForwardInternal::invoke( + member, n, plen, piv.data(), ps0, A.data(), as0, as1); + } + return 0; + } +}; + +/// column swap +template +struct TeamVectorApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, const AViewType &A) { + if (AViewType::rank == 1) { + const int as0 = A.stride(0); + TeamVectorApplyPivotVectorForwardInternal::invoke(member, piv, A.data(), + as0); + } else if (AViewType::rank == 2) { + const int m = A.extent(0), as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixForwardInternal::invoke(member, m, piv, + A.data(), as1, as0); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const PivViewType &piv, + const AViewType &A) { + if (AViewType::rank == 1) { + const int plen = piv.extent(0), as0 = A.stride(0); + TeamVectorApplyPivotVectorForwardInternal ::invoke( + member, plen, piv.data(), A.data(), as0); + } else if (AViewType::rank == 2) { + // column permutation + const int plen = piv.extent(0), ps = piv.stride(0), m = A.extent(0), + as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixForwardInternal ::invoke( + member, m, plen, piv.data(), ps, A.data(), as1, as0); + } + return 0; + } +}; + +/// +/// Backward pivot apply +/// + +/// row swap +template +struct TeamVectorApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, const AViewType &A) { + if (AViewType::rank == 1) { + const int as0 = A.stride(0); + TeamVectorApplyPivotVectorBackwardInternal::invoke(member, piv, A.data(), + as0); + } else if (AViewType::rank == 2) { + const int n = A.extent(1), as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixBackwardInternal::invoke(member, n, piv, + A.data(), as0, as1); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const PivViewType piv, + const AViewType &A) { + if (AViewType::rank == 1) { + const int plen = piv.extent(0), ps0 = piv.stride(0), as0 = A.stride(0); + TeamVectorApplyPivotVectorBackwardInternal::invoke( + member, plen, piv.data(), ps0, A.data(), as0); + } else if (AViewType::rank == 2) { + // row permutation + const int plen = piv.extent(0), ps0 = piv.stride(0), n = A.extent(1), + as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixBackwardInternal::invoke( + member, n, plen, piv.data(), ps0, A.data(), as0, as1); + } + return 0; + } +}; + +/// column swap +template +struct TeamVectorApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, const AViewType &A) { + if (AViewType::rank == 1) { + const int as0 = A.stride(0); + TeamVectorApplyPivotVectorBackwardInternal::invoke(member, piv, A.data(), + as0); + } else if (AViewType::rank == 2) { + const int m = A.extent(0), as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixBackwardInternal::invoke(member, m, piv, + A.data(), as1, as0); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const PivViewType &piv, + const AViewType &A) { + if (AViewType::rank == 1) { + const int plen = piv.extent(0), as0 = A.stride(0); + TeamVectorApplyPivotVectorBackwardInternal ::invoke( + member, plen, piv.data(), A.data(), as0); + } else if (AViewType::rank == 2) { + // column permutation + const int plen = piv.extent(0), ps = piv.stride(0), m = A.extent(0), + as0 = A.stride(0), as1 = A.stride(1); + TeamVectorApplyPivotMatrixBackwardInternal ::invoke( + member, m, plen, piv.data(), ps, A.data(), as1, as0); + } + return 0; + } +}; + +/// +/// Backward pivot apply +/// + +/// row swap +template<> +struct SerialApplyPivot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int piv, const AViewType &A) { + if (AViewType::rank == 1) { + const int as0 = A.stride(0); + SerialApplyPivotVectorBackwardInternal::invoke(piv, A.data(), + as0); + } else if (AViewType::rank == 2) { + const int n = A.extent(1), as0 = A.stride(0), as1 = A.stride(1); + SerialApplyPivotMatrixBackwardInternal::invoke(n, piv, + A.data(), as0, as1); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const PivViewType piv, + const AViewType &A) { + if (AViewType::rank == 1) { + const int plen = piv.extent(0), ps0 = piv.stride(0), as0 = A.stride(0); + SerialApplyPivotVectorBackwardInternal::invoke( + plen, piv.data(), ps0, A.data(), as0); + } else if (AViewType::rank == 2) { + // row permutation + const int plen = piv.extent(0), ps0 = piv.stride(0), n = A.extent(1), + as0 = A.stride(0), as1 = A.stride(1); + SerialApplyPivotMatrixBackwardInternal::invoke( + n, plen, piv.data(), ps0, A.data(), as0, as1); + } + return 0; + } +}; + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyPivot_Internal.hpp b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Internal.hpp new file mode 100644 index 00000000..c26d442c --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyPivot_Internal.hpp @@ -0,0 +1,348 @@ +#ifndef __KOKKOSBATCHED_APPLY_PIVOT_INTERNAL_HPP__ +#define __KOKKOSBATCHED_APPLY_PIVOT_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// TeamVector Internal Impl +/// ======================== + +/// +/// Forward +/// +struct TeamVectorApplyPivotVectorForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + if (piv != 0) { + Kokkos::single(Kokkos::PerTeam(member), [&]() { + const int idx_p = piv * as0; + const ValueType tmp = A[0]; + A[0] = A[idx_p]; + A[idx_p] = tmp; + }); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + Kokkos::single(Kokkos::PerTeam(member), [&]() { + for (int i = 0; i < plen; ++i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A[idx_i]; + A[idx_i] = A[idx_p]; + A[idx_p] = tmp; + } + } + }); + return 0; + } +}; + +/// Pivot a row +struct TeamVectorApplyPivotMatrixForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int n, const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (piv != 0) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(member, n), + [&](const int &j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const int idx_p = piv * as0; + const ValueType tmp = A_at_j[0]; + A_at_j[0] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + }); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int n, const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(member, n), [&](const int &j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + for (int i = 0; i < plen; ++i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A_at_j[idx_i]; + A_at_j[idx_i] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + } + } + }); + return 0; + } +}; + +/// +/// Backward +/// +struct TeamVectorApplyPivotVectorBackwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + if (piv != 0) { + Kokkos::single(Kokkos::PerTeam(member), [&]() { + const int idx_p = piv * as0; + const ValueType tmp = A[0]; + A[0] = A[idx_p]; + A[idx_p] = tmp; + }); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + Kokkos::single(Kokkos::PerTeam(member), [&]() { + for (int i = (plen - 1); i >= 0; --i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A[idx_i]; + A[idx_i] = A[idx_p]; + A[idx_p] = tmp; + } + } + }); + return 0; + } +}; + +/// Pivot a row +struct TeamVectorApplyPivotMatrixBackwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int n, const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (piv != 0) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(member, n), + [&](const int &j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const int idx_p = piv * as0; + const ValueType tmp = A_at_j[0]; + A_at_j[0] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + }); + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int n, const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(member, n), [&](const int &j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + for (int i = (plen - 1); i >= 0; --i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A_at_j[idx_i]; + A_at_j[idx_i] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + } + } + }); + return 0; + } +}; + +/// +/// Serial Internal Impl +/// ======================== + +/// +/// Forward +/// + +struct SerialApplyPivotVectorForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + if (piv != 0) { + const int idx_p = piv * as0; + const ValueType tmp = A[0]; + A[0] = A[idx_p]; + A[idx_p] = tmp; + } + return 0; + } + + // template + // KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + // const int plen, + // const IntType *KOKKOS_RESTRICT p, + // const int ps0, + // /* */ ValueType *KOKKOS_RESTRICT A, + // const int as0) { + // Kokkos::single(Kokkos::PerTeam(member), [&]() { + // for (int i = 0; i < plen; ++i) { + // const int piv = p[i * ps0]; + // if (piv != 0) { + // const int idx_i = i * as0, idx_p = (i + piv) * as0; + // const ValueType tmp = A[idx_i]; + // A[idx_i] = A[idx_p]; + // A[idx_p] = tmp; + // } + // } + // }); + // return 0; + // } +}; + +/// Pivot a row +struct SerialApplyPivotMatrixForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int n, const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (piv != 0) { + for (int j=0; j < n; j++) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const int idx_p = piv * as0; + const ValueType tmp = A_at_j[0]; + A_at_j[0] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + } + } + return 0; + } + + // template + // KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + // const int n, const int plen, + // const IntType *KOKKOS_RESTRICT p, + // const int ps0, + // /* */ ValueType *KOKKOS_RESTRICT A, + // const int as0, const int as1) { + // Kokkos::parallel_for(Kokkos::TeamVectorRange(member, n), [&](const int &j) { + // ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + // for (int i = 0; i < plen; ++i) { + // const int piv = p[i * ps0]; + // if (piv != 0) { + // const int idx_i = i * as0, idx_p = (i + piv) * as0; + // const ValueType tmp = A_at_j[idx_i]; + // A_at_j[idx_i] = A_at_j[idx_p]; + // A_at_j[idx_p] = tmp; + // } + // } + // }); + // return 0; + // } +}; + +/// +/// Backward +/// +struct SerialApplyPivotVectorBackwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + if (piv != 0) { + const int idx_p = piv * as0; + const ValueType tmp = A[0]; + A[0] = A[idx_p]; + A[idx_p] = tmp; + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + for (int i = (plen - 1); i >= 0; --i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A[idx_i]; + A[idx_i] = A[idx_p]; + A[idx_p] = tmp; + } + } + return 0; + } +}; + +/// Pivot a row +struct SerialApplyPivotMatrixBackwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int n, const int piv, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (piv != 0) { + for (int j=0; j < n; ++j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const int idx_p = piv * as0; + const ValueType tmp = A_at_j[0]; + A_at_j[0] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + } + } + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const int n, const int plen, + const IntType *KOKKOS_RESTRICT p, + const int ps0, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + for (int j=0; j < n; ++j) { + ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + for (int i = (plen - 1); i >= 0; --i) { + const int piv = p[i * ps0]; + if (piv != 0) { + const int idx_i = i * as0, idx_p = (i + piv) * as0; + const ValueType tmp = A_at_j[idx_i]; + A_at_j[idx_i] = A_at_j[idx_p]; + A_at_j[idx_p] = tmp; + } + } + } + return 0; + } +}; + + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyQ_Decl.hpp b/external/kokkos-kernels/KokkosBatched_ApplyQ_Decl.hpp new file mode 100644 index 00000000..d2ac403c --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyQ_Decl.hpp @@ -0,0 +1,88 @@ +#ifndef __KOKKOSBATCHED_APPLY_Q_DECL_HPP__ +#define __KOKKOSBATCHED_APPLY_Q_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial ApplyQ +/// + +template +struct SerialApplyQ { + template + KOKKOS_INLINE_FUNCTION static int invoke(const AViewType &A, + const tViewType &t, + const BViewType &B, + const wViewType &w); +}; + +/// +/// Team ApplyQ +/// + +template +struct TeamApplyQ { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const AViewType &A, + const tViewType &t, + const BViewType &B, + const wViewType &w); +}; + +/// +/// TeamVector ApplyQ +/// + +template +struct TeamVectorApplyQ { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const AViewType &A, + const tViewType &t, + const BViewType &B, + const wViewType &w); +}; + +/// +/// Selective Interface +/// +template +struct ApplyQ { + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke(const MemberType &member, + const AViewType &A, + const tViewType &t, + const BViewType &B, + const wViewType &w) { + int r_val = 0; + if (std::is_same::value) { + r_val = SerialApplyQ::invoke(A, t, B, w); + } else if (std::is_same::value) { + r_val = TeamApplyQ::invoke( + member, A, t, B, w); + } else if (std::is_same::value) { + r_val = TeamVectorApplyQ::invoke( + member, A, t, B, w); + } + return r_val; + } +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_ApplyQ_Serial_Impl.hpp" +//#include "KokkosBatched_ApplyQ_TeamVector_Impl.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Impl.hpp new file mode 100644 index 00000000..755aa1cb --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Impl.hpp @@ -0,0 +1,56 @@ +#ifndef __KOKKOSBATCHED_APPLY_Q_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_APPLY_Q_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_ApplyQ_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Impl +/// =========== + +template <> +template +KOKKOS_INLINE_FUNCTION int +SerialApplyQ::invoke( + const AViewType &A, const tViewType &t, const BViewType &B, + const wViewType &w) { + return SerialApplyQ_LeftForwardInternal::invoke( + B.extent(0), B.extent(1), A.extent(1), A.data(), A.stride_0(), + A.stride_1(), t.data(), t.stride_0(), B.data(), B.stride_0(), + B.stride_1(), w.data()); +} + +template <> +template +KOKKOS_INLINE_FUNCTION int +SerialApplyQ::invoke( + const AViewType &A, const tViewType &t, const BViewType &B, + const wViewType &w) { + return SerialApplyQ_LeftBackwardInternal::invoke( + B.extent(0), B.extent(1), A.extent(1), A.data(), A.stride_0(), + A.stride_1(), t.data(), t.stride_0(), B.data(), B.stride_0(), + B.stride_1(), w.data()); +} + +template <> +template +KOKKOS_INLINE_FUNCTION int +SerialApplyQ::invoke( + const AViewType &A, const tViewType &t, const BViewType &B, + const wViewType &w) { + return SerialApplyQ_RightForwardInternal::invoke( + B.extent(0), B.extent(1), A.extent(1), A.data(), A.stride_0(), + A.stride_1(), t.data(), t.stride_0(), B.data(), B.stride_0(), + B.stride_1(), w.data()); +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Internal.hpp new file mode 100644 index 00000000..31b9f192 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_ApplyQ_Serial_Internal.hpp @@ -0,0 +1,193 @@ +#ifndef __KOKKOSBATCHED_APPLY_Q_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_APPLY_Q_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_ApplyHouseholder_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== +/// +/// this impl follows the flame interface of householder transformation +/// + +struct SerialApplyQ_LeftForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const int k, + /* */ ValueType *A, const int as0, + const int as1, + /* */ ValueType *t, const int ts, + /* */ ValueType *B, const int bs0, + const int bs1, + /* */ ValueType *w) { + typedef ValueType value_type; + + /// Given a matrix A that includes a series of householder vectors, + /// it applies a unitary matrix Q to B from left without transpose + /// B = Q B = (H0 H1 H2 H3 ... H(k-1)) B + /// where + /// A is m x k (holding H0, H1 ... H(k-1) + /// t is k x 1 + /// B is m x n + + // partitions used for loop iteration + Partition2x2 A_part2x2(as0, as1); + Partition3x3 A_part3x3(as0, as1); + + Partition2x1 t_part2x1(ts); + Partition3x1 t_part3x1(ts); + + Partition2x1 B_part2x1(bs0); + Partition3x1 B_part3x1(bs0); + + // initial partition of A where ATL has a zero dimension + A_part2x2.partWithABR(A, m, k, m - k, 0); + t_part2x1.partWithAB(t, k, 0); + B_part2x1.partWithAB(B, m, m - k); + + for (int m_A0 = (k - 1); m_A0 >= 0; --m_A0) { + // part 2x2 into 3x3 + A_part3x3.partWithATL(A_part2x2, 1, 1); + t_part3x1.partWithAT(t_part2x1, 1); + value_type *tau = t_part3x1.A1; + + B_part3x1.partWithAT(B_part2x1, 1); + const int m_A2 = m - m_A0 - 1; + /// ----------------------------------------------------- + // left apply householder to partitioned B1 and B2 + SerialApplyLeftHouseholderInternal::invoke(m_A2, n, tau, A_part3x3.A21, + as0, B_part3x1.A1, bs1, + B_part3x1.A2, bs0, bs1, w); + + /// ----------------------------------------------------- + A_part2x2.mergeToABR(A_part3x3); + t_part2x1.mergeToAB(t_part3x1); + B_part2x1.mergeToAB(B_part3x1); + } + return 0; + } +}; + +struct SerialApplyQ_LeftBackwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const int k, + /* */ ValueType *A, const int as0, + const int as1, + /* */ ValueType *t, const int ts, + /* */ ValueType *B, const int bs0, + const int bs1, + /* */ ValueType *w) { + typedef ValueType value_type; + + /// Given a matrix A that includes a series of householder vectors, + /// it applies a unitary matrix Q to B from left without transpose + /// B = Q^H B = (H(k-1) H(k-2) ... H0) B + /// where + /// A is m x k (holding H0, H1 ... H(k-1) + /// t is k x 1 + /// B is m x n + + // partitions used for loop iteration + Partition2x2 A_part2x2(as0, as1); + Partition3x3 A_part3x3(as0, as1); + + Partition2x1 t_part2x1(ts); + Partition3x1 t_part3x1(ts); + + Partition2x1 B_part2x1(bs0); + Partition3x1 B_part3x1(bs0); + + // initial partition of A where ATL has a zero dimension + A_part2x2.partWithATL(A, m, k, 0, 0); + t_part2x1.partWithAT(t, k, 0); + B_part2x1.partWithAT(B, m, 0); + + for (int m_A0 = 0; m_A0 < k; ++m_A0) { + // part 2x2 into 3x3 + A_part3x3.partWithABR(A_part2x2, 1, 1); + t_part3x1.partWithAB(t_part2x1, 1); + value_type *tau = t_part3x1.A1; + + B_part3x1.partWithAB(B_part2x1, 1); + const int m_A2 = m - m_A0 - 1; + /// ----------------------------------------------------- + // left apply householder to partitioned B1 and B2 + SerialApplyLeftHouseholderInternal::invoke(m_A2, n, tau, A_part3x3.A21, + as0, B_part3x1.A1, bs1, + B_part3x1.A2, bs0, bs1, w); + + /// ----------------------------------------------------- + A_part2x2.mergeToATL(A_part3x3); + t_part2x1.mergeToAT(t_part3x1); + B_part2x1.mergeToAT(B_part3x1); + } + return 0; + } +}; + +struct SerialApplyQ_RightForwardInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const int k, + /* */ ValueType *A, const int as0, + const int as1, + /* */ ValueType *t, const int ts, + /* */ ValueType *B, const int bs0, + const int bs1, + /* */ ValueType *w) { + typedef ValueType value_type; + + /// Given a matrix A that includes a series of householder vectors, + /// it applies a unitary matrix Q to B from left without transpose + /// B = B Q = B (H0 H1 H2 H3 ... H(k-1)) + /// where + /// A is n x k (holding H0, H1 ... H(k-1) + /// t is k x 1 + /// B is m x n + + // partitions used for loop iteration + Partition2x2 A_part2x2(as0, as1); + Partition3x3 A_part3x3(as0, as1); + + Partition2x1 t_part2x1(ts); + Partition3x1 t_part3x1(ts); + + Partition1x2 B_part1x2(bs1); + Partition1x3 B_part1x3(bs1); + + // initial partition of A where ATL has a zero dimension + A_part2x2.partWithATL(A, n, k, 0, 0); + t_part2x1.partWithAT(t, k, 0); + B_part1x2.partWithAL(B, n, 0); + + for (int n_A0 = 0; n_A0 < k; ++n_A0) { + // part 2x2 into 3x3 + A_part3x3.partWithABR(A_part2x2, 1, 1); + t_part3x1.partWithAB(t_part2x1, 1); + value_type *tau = t_part3x1.A1; + + B_part1x3.partWithAR(B_part1x2, 1); + const int n_B2 = n - n_A0 - 1; + /// ----------------------------------------------------- + // right apply householder to partitioned B1 and B2 + SerialApplyRightHouseholderInternal::invoke(m, n_B2, tau, A_part3x3.A21, + as0, B_part1x3.A1, bs0, + B_part1x3.A2, bs0, bs1, w); + /// ----------------------------------------------------- + A_part2x2.mergeToATL(A_part3x3); + t_part2x1.mergeToAT(t_part3x1); + B_part1x2.mergeToAL(B_part1x3); + } + return 0; + } +}; + +} // end namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Dot.hpp b/external/kokkos-kernels/KokkosBatched_Dot.hpp new file mode 100644 index 00000000..43d8c5ee --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Dot.hpp @@ -0,0 +1,161 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.4 +// Copyright (2021) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +#ifndef __KOKKOSBATCHED_DOT_HPP__ +#define __KOKKOSBATCHED_DOT_HPP__ + +/// \author Kim Liegeois (knliege@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Vector.hpp" + +namespace KokkosBatched { + +/// \brief Serial Batched DOT: +/// +/// Depending on the ArgTrans template, the dot product is +/// row-based (ArgTrans == Trans::NoTranspose): +/// +/// dot_l <- (x_l:, y_l:) for all l = 1, ..., N +/// where: +/// * N is the second dimension of X. +/// +/// Or column-based: +/// dot_l <- (x_:l, y_:l) for all l = 1, ..., n +/// where: +/// * n is the second dimension of X. +/// +/// \tparam ArgTrans: type of dot product (Trans::NoTranspose by default) +/// \tparam XViewType: Input type for X, needs to be a 2D view +/// \tparam YViewType: Input type for Y, needs to be a 2D view +/// \tparam alphaViewType: Input type for alpha, needs to be a 1D view +/// +/// \param X [in]: Input vector X, a rank 2 view +/// \param Y [in]: Input vector Y, a rank 2 view +/// \param dot [out]: Computed dot product, a rank 1 view +/// +/// No nested parallel_for is used inside of the function. +/// + +template +struct SerialDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const XViewType &X, + const YViewType &Y, + const NormViewType &dot); +}; + +/// \brief Team Batched DOT: +/// +/// Depending on the ArgTrans template, the dot product is +/// row-based (ArgTrans == Trans::NoTranspose): +/// +/// dot_l <- (x_l:, y_l:) for all l = 1, ..., N +/// where: +/// * N is the second dimension of X. +/// +/// Or column-based: +/// dot_l <- (x_:l, y_:l) for all l = 1, ..., n +/// where: +/// * n is the second dimension of X. +/// +/// \tparam ArgTrans: type of dot product (Trans::NoTranspose by default) +/// \tparam XViewType: Input type for X, needs to be a 2D view +/// \tparam YViewType: Input type for Y, needs to be a 2D view +/// \tparam alphaViewType: Input type for alpha, needs to be a 1D view +/// +/// \param X [in]: Input vector X, a rank 2 view +/// \param Y [in]: Input vector Y, a rank 2 view +/// \param dot [out]: Computed dot product, a rank 1 view +/// +/// A nested parallel_for with TeamThreadRange is used. +/// + +template +struct TeamDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot); +}; + +/// \brief TeamVector Batched DOT: +/// +/// Depending on the ArgTrans template, the dot product is +/// row-based (ArgTrans == Trans::NoTranspose): +/// +/// dot_l <- (x_l:, y_l:) for all l = 1, ..., N +/// where: +/// * N is the second dimension of X. +/// +/// Or column-based: +/// dot_l <- (x_:l, y_:l) for all l = 1, ..., n +/// where: +/// * n is the second dimension of X. +/// +/// \tparam ArgTrans: type of dot product (Trans::NoTranspose by default) +/// \tparam XViewType: Input type for X, needs to be a 2D view +/// \tparam YViewType: Input type for Y, needs to be a 2D view +/// \tparam alphaViewType: Input type for alpha, needs to be a 1D view +/// +/// \param X [in]: Input vector X, a rank 2 view +/// \param Y [in]: Input vector Y, a rank 2 view +/// \param dot [out]: Computed dot product, a rank 1 view +/// +/// Two nested parallel_for with both TeamThreadRange and ThreadVectorRange +/// (or one with TeamVectorRange) are used inside. +/// + +template +struct TeamVectorDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot); +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_Dot_Internal.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp b/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp new file mode 100644 index 00000000..e7374341 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp @@ -0,0 +1,435 @@ +#ifndef __KOKKOSBATCHED_DOT_INTERNAL_HPP__ +#define __KOKKOSBATCHED_DOT_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== + +struct SerialDotInternal { + // i \in [0,m) + // C = conj(A(:))*B(:) + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const int m, const ValueType *KOKKOS_RESTRICT A, const int as0, + const ValueType *KOKKOS_RESTRICT B, const int bs0, + /* */ MagnitudeType *KOKKOS_RESTRICT C) { + using ats = Kokkos::ArithTraits; + C[0] = ValueType(0); +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int i = 0; i < m; ++i) { + const int idx_a = i * as0, idx_b = i * bs0; + C[0] += ats::conj(A[idx_a]) * B[idx_b]; + } + return 0; + } + + // j \in [0,n), i \in [0,m) + // C(j) = conj(A(:,j))*B(:,j) + template + KOKKOS_INLINE_FUNCTION static int invoke( + const int m, const int n, const ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1, const ValueType *KOKKOS_RESTRICT B, + const int bs0, const int bs1, + /* */ MagnitudeType *KOKKOS_RESTRICT C, const int cs) { + for (int j = 0; j < n; ++j) + invoke(m, A + j * as1, as0, B + j * bs1, bs0, C + j * cs); + return 0; + } +}; + +/// +/// Team Internal Impl +/// ======================== + +// i \in [0,m) +// C = conj(A(:))*B(:) +struct TeamDotInternal { + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const MemberType &member, const int m, const ValueType *KOKKOS_RESTRICT A, + const int as0, const ValueType *KOKKOS_RESTRICT B, const int bs0, + /* */ MagnitudeType *KOKKOS_RESTRICT C) { + using ats = Kokkos::ArithTraits; + ValueType t(0); + Kokkos::parallel_reduce( + Kokkos::TeamThreadRange(member, m), + [&](const int &i, ValueType &update) { + const int idx_a = i * as0, idx_b = i * bs0; + update += ats::conj(A[idx_a]) * B[idx_b]; + }, + t); + Kokkos::single(Kokkos::PerThread(member), [&]() { C[0] = t; }); + return 0; + } + + // j \in [0,n), i \in [0,m) + // C(j) = conj(A(:,j))*B(:,j) + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const MemberType &member, const int m, const int n, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1, + /* */ MagnitudeType *KOKKOS_RESTRICT C, const int cs) { + using ats = Kokkos::ArithTraits; + Kokkos::parallel_for(Kokkos::TeamThreadRange(member, n), [&](const int &j) { + ValueType t(0); + const ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const ValueType *KOKKOS_RESTRICT B_at_j = B + j * bs1; + for (int i = 0; i < m; ++i) { + const int idx_a = i * as0, idx_b = i * bs0; + t += ats::conj(A_at_j[idx_a]) * B_at_j[idx_b]; + } + Kokkos::single(Kokkos::PerThread(member), [&]() { C[j * cs] = t; }); + }); + return 0; + } +}; + +/// +/// TeamVector Internal Impl +/// ======================== + +// i \in [0,m) +// C = conj(A(:))*B(:) +struct TeamVectorDotInternal { + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const MemberType &member, const int m, const ValueType *KOKKOS_RESTRICT A, + const int as0, const ValueType *KOKKOS_RESTRICT B, const int bs0, + /* */ MagnitudeType *KOKKOS_RESTRICT C) { + using ats = Kokkos::ArithTraits; + ValueType t(0); + Kokkos::parallel_reduce( + Kokkos::TeamVectorRange(member, m), + [&](const int &i, ValueType &update) { + const int idx_a = i * as0, idx_b = i * bs0; + update += ats::conj(A[idx_a]) * B[idx_b]; + }, + t); + Kokkos::single(Kokkos::PerThread(member), [&]() { C[0] = t; }); + return 0; + } + + // j \in [0,n), i \in [0,m) + // C(j) = conj(A(:,j))*B(:,j) + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const MemberType &member, const int m, const int n, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1, + /* */ MagnitudeType *KOKKOS_RESTRICT C, const int cs) { + using ats = Kokkos::ArithTraits; + Kokkos::parallel_for(Kokkos::TeamThreadRange(member, n), [&](const int &j) { + ValueType t(0); + const ValueType *KOKKOS_RESTRICT A_at_j = A + j * as1; + const ValueType *KOKKOS_RESTRICT B_at_j = B + j * bs1; + Kokkos::parallel_reduce( + Kokkos::ThreadVectorRange(member, m), + [&](const int &i, ValueType &update) { + const int idx_a = i * as0, idx_b = i * bs0; + update += ats::conj(A_at_j[idx_a]) * B_at_j[idx_b]; + }, + t); + Kokkos::single(Kokkos::PerThread(member), [&]() { C[j * cs] = t; }); + }); + return 0; + } +}; + +/// +/// Serial Impl +/// =========== +template <> +struct SerialDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(1) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Second dimension of X and alpha do not match: " + "X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return SerialDotInternal::template invoke< + typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + X.extent(0), X.extent(1), X.data(), X.stride_0(), X.stride_1(), + Y.data(), Y.stride_0(), Y.stride_1(), dot.data(), dot.stride_0()); + } +}; + +template <> +struct SerialDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(0) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: First dimension of X and alpha do not match: X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return SerialDotInternal::template invoke< + typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + X.extent(1), X.extent(0), X.data(), X.stride_1(), X.stride_0(), + Y.data(), Y.stride_1(), Y.stride_0(), dot.data(), dot.stride_0()); + } +}; + +/// +/// Team Impl +/// =============== +template +struct TeamDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(1) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Second dimension of X and alpha do not match: " + "X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return TeamDotInternal::template invoke< + MemberType, typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + member, X.extent(0), X.extent(1), X.data(), X.stride_0(), X.stride_1(), + Y.data(), Y.stride_0(), Y.stride_1(), dot.data(), dot.stride_0()); + } +}; + +template +struct TeamDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(0) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: First dimension of X and alpha do not match: X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return TeamDotInternal::template invoke< + MemberType, typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + member, X.extent(1), X.extent(0), X.data(), X.stride_1(), X.stride_0(), + Y.data(), Y.stride_1(), Y.stride_0(), dot.data(), dot.stride_0()); + } +}; + +/// +/// TeamVector Impl +/// =============== +template +struct TeamVectorDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(1) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Second dimension of X and alpha do not match: " + "X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return TeamVectorDotInternal::template invoke< + MemberType, typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + member, X.extent(0), X.extent(1), X.data(), X.stride_0(), X.stride_1(), + Y.data(), Y.stride_0(), Y.stride_1(), dot.data(), dot.stride_0()); + } +}; + +template +struct TeamVectorDot { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const XViewType &X, + const YViewType &Y, + const NormViewType &dot) { +#if (KOKKOSKERNELS_DEBUG_LEVEL > 0) + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: XViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: YViewType is not a Kokkos::View."); + static_assert(Kokkos::is_view::value, + "KokkosBatched::dot: NormViewType is not a Kokkos::View."); + static_assert(XViewType::Rank == 2, + "KokkosBatched::dot: XViewType must have rank 2."); + static_assert(YViewType::Rank == 2, + "KokkosBatched::dot: YViewType must have rank 2."); + static_assert(NormViewType::Rank == 1, + "KokkosBatched::dot: NormViewType must have rank 1."); + + // Check compatibility of dimensions at run time. + if (X.extent(0) != Y.extent(0) || X.extent(1) != Y.extent(1)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: Dimensions of X and Y do not match: X: %d x %d, " + "Y: %d x %d\n", + (int)X.extent(0), (int)X.extent(1), (int)Y.extent(0), + (int)Y.extent(1)); + return 1; + } + if (X.extent(0) != dot.extent(0)) { + KOKKOS_IMPL_DO_NOT_USE_PRINTF( + "KokkosBatched::dot: First dimension of X and alpha do not match: X: " + "%d x %d, dot: %d\n", + (int)X.extent(0), (int)X.extent(1), (int)dot.extent(0)); + return 1; + } +#endif + return TeamVectorDotInternal::template invoke< + MemberType, typename XViewType::non_const_value_type, + typename NormViewType::non_const_value_type>( + member, X.extent(1), X.extent(0), X.data(), X.stride_1(), X.stride_0(), + Y.data(), Y.stride_1(), Y.stride_0(), dot.data(), dot.stride_0()); + } +}; + +} // end namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_FindAmax_Internal.hpp b/external/kokkos-kernels/KokkosBatched_FindAmax_Internal.hpp new file mode 100644 index 00000000..32980219 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_FindAmax_Internal.hpp @@ -0,0 +1,68 @@ +#ifndef __KOKKOSBATCHED_FIND_AMAX_INTERNAL_HPP__ +#define __KOKKOSBATCHED_FIND_AMAX_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ===================== +struct SerialFindAmaxInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, + const ValueType *KOKKOS_RESTRICT A, + const int as0, + /**/ IntType *KOKKOS_RESTRICT idx) { + ValueType max_val(A[0]); + IntType val_loc(0); + for (int i = 1; i < m; ++i) { + const int idx_a = i * as0; + if (A[idx_a] > max_val) { + max_val = A[idx_a]; + val_loc = i; + } + } + *idx = val_loc; + return 0; + } +}; + +/// +/// TeamVector Internal Impl +/// ======================== +struct TeamVectorFindAmaxInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int m, + const ValueType *KOKKOS_RESTRICT A, + const int as0, + /**/ IntType *KOKKOS_RESTRICT idx) { + if (m > 0) { + using reducer_value_type = + typename Kokkos::MaxLoc::value_type; + reducer_value_type value{}; + Kokkos::MaxLoc reducer_value(value); + Kokkos::parallel_reduce( + Kokkos::TeamVectorRange(member, m), + [&](const int &i, reducer_value_type &update) { + const int idx_a = i * as0; + if (A[idx_a] > update.val) { + update.val = A[idx_a]; + update.loc = i; + } + }, + reducer_value); + Kokkos::single(Kokkos::PerTeam(member), [&]() { *idx = value.loc; }); + } else { + Kokkos::single(Kokkos::PerTeam(member), [&]() { *idx = 0; }); + } + return 0; + } +}; + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Gemm_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_Gemm_Serial_Internal.hpp new file mode 100644 index 00000000..1548d602 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Gemm_Serial_Internal.hpp @@ -0,0 +1,137 @@ +#ifndef __KOKKOSBATCHED_GEMM_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_GEMM_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +#include "KokkosBlas1_set_impl.hpp" +#include "KokkosBlas1_serial_scal_impl.hpp" + +#include "KokkosBatched_InnerGemmFixC_Serial_Impl.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== + +template +struct SerialGemmInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke( + const int m, const int n, const int k, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1, + const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT C, const int cs0, const int cs1); +}; + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialGemmInternal::invoke( + const int m, const int n, const int k, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1, + const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT C, const int cs0, const int cs1) { + // C = beta C + alpha A B + // C (m x n), A(m x k), B(k x n) + + const ScalarType one(1.0), zero(0.0); + + if (beta == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, n, zero, C, cs0, cs1); + else if (beta != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, n, beta, C, cs0, cs1); + + if (alpha != zero) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + + ValueType *KOKKOS_RESTRICT pC = C; + for (int p = 0; p < k; ++p) { + const ValueType *KOKKOS_RESTRICT pA = A + p * as1, + *KOKKOS_RESTRICT pB = B + p * bs0; + for (int i = 0; i < m; ++i) { + const ValueType tA(alpha * pA[i * as0]); +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) pC[i * cs0 + j * cs1] += tA * pB[j * bs1]; + } + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialGemmInternal::invoke( + const int m, const int n, const int k, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1, + const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT C, const int cs0, const int cs1) { + // C = beta C + alpha A B + // C (m x n), A(m x k), B(k x n) + + constexpr int mbAlgo = Algo::Gemm::Blocked::mb(); + constexpr int nbAlgo = Algo::Gemm::Blocked::mb(); + + const ScalarType one(1.0), zero(0.0); + + if (beta == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, n, zero, C, cs0, cs1); + else if (beta != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, n, beta, C, cs0, cs1); + + if (alpha != zero) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + const ValueType alpha_value(alpha); + + InnerGemmFixC inner(as0, as1, bs0, bs1, cs0, cs1); + auto gemm = [&](const int ib, const int jb, const int pb, + const ValueType *KOKKOS_RESTRICT AA, + const ValueType *KOKKOS_RESTRICT BB, + /**/ ValueType *KOKKOS_RESTRICT CC) { + const int mb = mbAlgo, nb = nbAlgo; + for (int i = 0; i < ib; i += mb) + for (int j = 0; j < jb; j += nb) + inner.serial_invoke(alpha_value, AA + i * as0, BB + j * bs1, + (i + mb) > ib ? (ib - i) : mb, + (j + nb) > jb ? (jb - j) : nb, pb, + CC + i * cs0 + j * cs1); + }; + + const bool is_small = true; //(m*n*k <= 64*64*64); + if (is_small) { + gemm(m, n, k, A, B, C); + } else { + // // cache blocking + // const int + // nc = nb*10, kc = mb*4, mc = mb*4; + + // for (int jj=0;jj +struct SerialGemvInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke( + const int m, const int n, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT x, const int xs0, const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT y, const int ys0); +}; + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialGemvInternal::invoke( + const int m, const int n, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT x, const int xs0, const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT y, const int ys0) { + const ScalarType one(1.0), zero(0.0); + + // y = beta y + alpha A x + // y (m), A(m x n), B(n) + + if (beta == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, y, ys0); + else if (beta != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, beta, y, ys0); + + if (alpha != zero) { + if (m <= 0 || n <= 0) return 0; + + for (int i = 0; i < m; ++i) { + ValueType t(0); + const ValueType *KOKKOS_RESTRICT tA = (A + i * as0); + +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) t += tA[j * as1] * x[j * xs0]; + y[i * ys0] += alpha * t; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialGemvInternal::invoke( + const int m, const int n, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + const ValueType *KOKKOS_RESTRICT x, const int xs0, const ScalarType beta, + /**/ ValueType *KOKKOS_RESTRICT y, const int ys0) { + const ScalarType one(1.0), zero(0.0); + + // y = beta y + alpha A x + // y (m), A(m x n), B(n) + + constexpr int mbAlgo = Algo::Gemv::Blocked::mb(); + + if (beta == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, y, ys0); + else if (beta != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, beta, y, ys0); + + if (alpha != zero) { + if (m <= 0 || n <= 0) return 0; + + InnerMultipleDotProduct inner(as0, as1, xs0, ys0); + const int mb = mbAlgo; + for (int i = 0; i < m; i += mb) + inner.serial_invoke(alpha, A + i * as0, x, (i + mb) > m ? (m - i) : mb, n, + y + i * ys0); + } + return 0; +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Householder_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_Householder_Serial_Internal.hpp new file mode 100644 index 00000000..565807b3 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Householder_Serial_Internal.hpp @@ -0,0 +1,78 @@ +#ifndef __KOKKOSBATCHED_HOUSEHOLDER_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_HOUSEHOLDER_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== +/// +/// this impl follows the flame interface of householder transformation +/// +struct SerialLeftHouseholderInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m_x2, + /* */ ValueType* chi1, + /* */ ValueType* x2, const int x2s, + /* */ ValueType* tau) { + typedef ValueType value_type; + typedef typename Kokkos::Details::ArithTraits::mag_type mag_type; + + const mag_type zero(0); + const mag_type half(0.5); + const mag_type one(1); + const mag_type minus_one(-1); + + /// compute the 2norm of x2 + mag_type norm_x2_square(0); + for (int i = 0; i < m_x2; ++i) { + const auto x2_at_i = x2[i * x2s]; + norm_x2_square += x2_at_i * x2_at_i; + } + + /// if norm_x2 is zero, return with trivial values + if (norm_x2_square == zero) { + *chi1 = -(*chi1); + *tau = half; + + return 0; + } + + /// compute magnitude of chi1, equal to norm2 of chi1 + const mag_type norm_chi1 = + Kokkos::Details::ArithTraits::abs(*chi1); + + /// compute 2 norm of x using norm_chi1 and norm_x2 + const mag_type norm_x = Kokkos::Details::ArithTraits::sqrt( + norm_x2_square + norm_chi1 * norm_chi1); + + /// compute alpha + const mag_type alpha = (*chi1 < 0 ? one : minus_one) * norm_x; + + /// overwrite x2 with u2 + const value_type chi1_minus_alpha = *chi1 - alpha; + const value_type inv_chi1_minus_alpha = one / chi1_minus_alpha; + for (int i = 0; i < m_x2; ++i) x2[i * x2s] *= inv_chi1_minus_alpha; + + // later consider to use the following + // SerialScaleInternal::invoke(m_x2, inv_chi1_minus_alpha, x2, x2s); + + /// compute tau + const mag_type chi1_minus_alpha_square = + chi1_minus_alpha * chi1_minus_alpha; + *tau = half + half * (norm_x2_square / chi1_minus_alpha_square); + + /// overwrite chi1 with alpha + *chi1 = alpha; + + return 0; + } +}; + +} // end namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Decl.hpp b/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Decl.hpp new file mode 100644 index 00000000..a6946097 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Decl.hpp @@ -0,0 +1,61 @@ +#ifndef __KOKKOSBATCHED_INNER_GEMM_FIX_C_DECL_HPP__ +#define __KOKKOSBATCHED_INNER_GEMM_FIX_C_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +namespace KokkosBatched { + +template +struct InnerGemmFixC { + const int _as0, _as1, _bs0, _bs1, _cs0, _cs1; + + KOKKOS_INLINE_FUNCTION + InnerGemmFixC(const int as0, const int as1, const int bs0, const int bs1, + const int cs0, const int cs1) + : _as0(as0), _as1(as1), _bs0(bs0), _bs1(bs1), _cs0(cs0), _cs1(cs1) {} + + // serial rank update + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, + const int k, + /**/ ValueType *KOKKOS_RESTRICT C); + + // serial rank update for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, + const int m, const int k, + /**/ ValueType *KOKKOS_RESTRICT C); + + // serial rank update for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, + const int m, const int n, + const int k, + /**/ ValueType *KOKKOS_RESTRICT C); + + template + KOKKOS_INLINE_FUNCTION int team_invoke(const MemberType &member, + const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, + const int k, + /**/ ValueType *KOKKOS_RESTRICT C); + + // team rank update for remainder + template + KOKKOS_INLINE_FUNCTION int team_invoke(const MemberType &member, + const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, + const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C); +}; +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Serial_Impl.hpp new file mode 100644 index 00000000..247f232d --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerGemmFixC_Serial_Impl.hpp @@ -0,0 +1,1560 @@ +#ifndef __KOKKOSBATCHED_INNER_GEMM_FIX_C_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_INNER_GEMM_FIX_C_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_InnerGemmFixC_Decl.hpp" + +namespace KokkosBatched { + +/// +/// Inner kernel (5x5) +/// ================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, + c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, c_04 = 0, a_1p, b_p1, c_10 = 0, + c_11 = 0, c_12 = 0, c_13 = 0, c_14 = 0, a_2p, b_p2, c_20 = 0, c_21 = 0, + c_22 = 0, c_23 = 0, c_24 = 0, a_3p, b_p3, c_30 = 0, c_31 = 0, c_32 = 0, + c_33 = 0, c_34 = 0, a_4p, b_p4, c_40 = 0, c_41 = 0, c_42 = 0, c_43 = 0, + c_44 = 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, + j3 = 3 * _bs1, j4 = 4 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + b_p3 = B[p * _bs0 + j3]; + a_4p = A[i4 + p * _as1]; + b_p4 = B[p * _bs0 + j4]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_04 += a_0p * b_p4; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_14 += a_1p * b_p4; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + c_24 += a_2p * b_p4; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + c_33 += a_3p * b_p3; + c_34 += a_3p * b_p4; + c_40 += a_4p * b_p0; + c_41 += a_4p * b_p1; + c_42 += a_4p * b_p2; + c_43 += a_4p * b_p3; + c_44 += a_4p * b_p4; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[0 * _cs0 + 4 * _cs1] += alpha * c_04; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[1 * _cs0 + 4 * _cs1] += alpha * c_14; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + C[2 * _cs0 + 4 * _cs1] += alpha * c_24; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + C[3 * _cs0 + 3 * _cs1] += alpha * c_33; + C[3 * _cs0 + 4 * _cs1] += alpha * c_34; + C[4 * _cs0 + 0 * _cs1] += alpha * c_40; + C[4 * _cs0 + 1 * _cs1] += alpha * c_41; + C[4 * _cs0 + 2 * _cs1] += alpha * c_42; + C[4 * _cs0 + 3 * _cs1] += alpha * c_43; + C[4 * _cs0 + 4 * _cs1] += alpha * c_44; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, a_1p, b_p1, + c_10 = 0, c_11 = 0, c_12 = 0, c_13 = 0, a_2p, b_p2, + c_20 = 0, c_21 = 0, c_22 = 0, c_23 = 0, a_3p, b_p3, + c_30 = 0, c_31 = 0, c_32 = 0, c_33 = 0, a_4p, c_40 = 0, + c_41 = 0, c_42 = 0, c_43 = 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, + j3 = 3 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + b_p3 = B[p * _bs0 + j3]; + a_4p = A[i4 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + c_33 += a_3p * b_p3; + c_40 += a_4p * b_p0; + c_41 += a_4p * b_p1; + c_42 += a_4p * b_p2; + c_43 += a_4p * b_p3; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + C[3 * _cs0 + 3 * _cs1] += alpha * c_33; + C[4 * _cs0 + 0 * _cs1] += alpha * c_40; + C[4 * _cs0 + 1 * _cs1] += alpha * c_41; + C[4 * _cs0 + 2 * _cs1] += alpha * c_42; + C[4 * _cs0 + 3 * _cs1] += alpha * c_43; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, a_1p, b_p1, c_10 = 0, + c_11 = 0, c_12 = 0, a_2p, b_p2, c_20 = 0, c_21 = 0, + c_22 = 0, a_3p, c_30 = 0, c_31 = 0, c_32 = 0, a_4p, + c_40 = 0, c_41 = 0, c_42 = 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + a_4p = A[i4 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + c_40 += a_4p * b_p0; + c_41 += a_4p * b_p1; + c_42 += a_4p * b_p2; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + C[4 * _cs0 + 0 * _cs1] += alpha * c_40; + C[4 * _cs0 + 1 * _cs1] += alpha * c_41; + C[4 * _cs0 + 2 * _cs1] += alpha * c_42; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, a_1p, b_p1, c_10 = 0, c_11 = 0, + a_2p, c_20 = 0, c_21 = 0, a_3p, c_30 = 0, c_31 = 0, + a_4p, c_40 = 0, c_41 = 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + a_3p = A[i3 + p * _as1]; + a_4p = A[i4 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_40 += a_4p * b_p0; + c_41 += a_4p * b_p1; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[4 * _cs0 + 0 * _cs1] += alpha * c_40; + C[4 * _cs0 + 1 * _cs1] += alpha * c_41; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, a_1p, c_10 = 0, a_2p, c_20 = 0, a_3p, + c_30 = 0, a_4p, c_40 = 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0, j0 = 0 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + a_2p = A[i2 + p * _as1]; + a_3p = A[i3 + p * _as1]; + a_4p = A[i4 + p * _as1]; + + c_00 += a_0p * b_p0; + c_10 += a_1p * b_p0; + c_20 += a_2p * b_p0; + c_30 += a_3p * b_p0; + c_40 += a_4p * b_p0; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[4 * _cs0 + 0 * _cs1] += alpha * c_40; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, c_04 = 0, a_1p, + b_p1, c_10 = 0, c_11 = 0, c_12 = 0, c_13 = 0, c_14 = 0, + a_2p, b_p2, c_20 = 0, c_21 = 0, c_22 = 0, c_23 = 0, + c_24 = 0, a_3p, b_p3, c_30 = 0, c_31 = 0, c_32 = 0, + c_33 = 0, c_34 = 0, + /**/ b_p4; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, j3 = 3 * _bs1, + j4 = 4 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + b_p3 = B[p * _bs0 + j3]; + /**/ b_p4 = B[p * _bs0 + j4]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_04 += a_0p * b_p4; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_14 += a_1p * b_p4; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + c_24 += a_2p * b_p4; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + c_33 += a_3p * b_p3; + c_34 += a_3p * b_p4; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[0 * _cs0 + 4 * _cs1] += alpha * c_04; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[1 * _cs0 + 4 * _cs1] += alpha * c_14; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + C[2 * _cs0 + 4 * _cs1] += alpha * c_24; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + C[3 * _cs0 + 3 * _cs1] += alpha * c_33; + C[3 * _cs0 + 4 * _cs1] += alpha * c_34; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, c_04 = 0, a_1p, + b_p1, c_10 = 0, c_11 = 0, c_12 = 0, c_13 = 0, c_14 = 0, + a_2p, b_p2, c_20 = 0, c_21 = 0, c_22 = 0, c_23 = 0, + c_24 = 0, + /**/ b_p3, + /**/ b_p4; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, j0 = 0 * _bs1, + j1 = 1 * _bs1, j2 = 2 * _bs1, j3 = 3 * _bs1, j4 = 4 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + /**/ b_p4 = B[p * _bs0 + j4]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_04 += a_0p * b_p4; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_14 += a_1p * b_p4; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + c_24 += a_2p * b_p4; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[0 * _cs0 + 4 * _cs1] += alpha * c_04; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[1 * _cs0 + 4 * _cs1] += alpha * c_14; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + C[2 * _cs0 + 4 * _cs1] += alpha * c_24; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, c_04 = 0, a_1p, + b_p1, c_10 = 0, c_11 = 0, c_12 = 0, c_13 = 0, c_14 = 0, + /**/ b_p2, + /**/ b_p3, + /**/ b_p4; + + const int i0 = 0 * _as0, i1 = 1 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, + j2 = 2 * _bs1, j3 = 3 * _bs1, j4 = 4 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + /**/ b_p4 = B[p * _bs0 + j4]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_04 += a_0p * b_p4; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_14 += a_1p * b_p4; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[0 * _cs0 + 4 * _cs1] += alpha * c_04; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[1 * _cs0 + 4 * _cs1] += alpha * c_14; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = 0, c_01 = 0, c_02 = 0, c_03 = 0, c_04 = 0, + /**/ b_p1, + /**/ b_p2, + /**/ b_p3, + /**/ b_p4; + + const int i0 = 0 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, + j3 = 3 * _bs1, j4 = 4 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + /**/ b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + /**/ b_p4 = B[p * _bs0 + j4]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_04 += a_0p * b_p4; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[0 * _cs0 + 4 * _cs1] += alpha * c_04; + + return 0; +} +/// +/// Inner kernel (4x4) +/// ================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, + c_00 = ValueType(0), c_01 = ValueType(0), c_02 = ValueType(0), + c_03 = ValueType(0), a_1p, b_p1, c_10 = ValueType(0), c_11 = ValueType(0), + c_12 = ValueType(0), c_13 = ValueType(0), a_2p, b_p2, c_20 = ValueType(0), + c_21 = ValueType(0), c_22 = ValueType(0), c_23 = ValueType(0), a_3p, b_p3, + c_30 = ValueType(0), c_31 = ValueType(0), c_32 = ValueType(0), + c_33 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, j3 = 3 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + b_p3 = B[p * _bs0 + j3]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + c_33 += a_3p * b_p3; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + C[3 * _cs0 + 3 * _cs1] += alpha * c_33; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, + c_00 = ValueType(0), c_01 = ValueType(0), c_02 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0), c_12 = ValueType(0), a_2p, b_p2, + c_20 = ValueType(0), c_21 = ValueType(0), c_22 = ValueType(0), a_3p, + c_30 = ValueType(0), c_31 = ValueType(0), c_32 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + a_3p = A[i3 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + c_32 += a_3p * b_p2; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + C[3 * _cs0 + 2 * _cs1] += alpha * c_32; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0), a_2p, + c_20 = ValueType(0), c_21 = ValueType(0), a_3p, + c_30 = ValueType(0), c_31 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + j0 = 0 * _bs1, j1 = 1 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + a_3p = A[i3 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_30 += a_3p * b_p0; + c_31 += a_3p * b_p1; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + C[3 * _cs0 + 1 * _cs1] += alpha * c_31; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), a_1p, c_10 = ValueType(0), a_2p, + c_20 = ValueType(0), a_3p, c_30 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + j0 = 0 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + a_2p = A[i2 + p * _as1]; + a_3p = A[i3 + p * _as1]; + + c_00 += a_0p * b_p0; + c_10 += a_1p * b_p0; + c_20 += a_2p * b_p0; + c_30 += a_3p * b_p0; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[3 * _cs0 + 0 * _cs1] += alpha * c_30; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, + c_00 = ValueType(0), c_01 = ValueType(0), c_02 = ValueType(0), + c_03 = ValueType(0), a_1p, b_p1, c_10 = ValueType(0), c_11 = ValueType(0), + c_12 = ValueType(0), c_13 = ValueType(0), a_2p, b_p2, c_20 = ValueType(0), + c_21 = ValueType(0), c_22 = ValueType(0), c_23 = ValueType(0), + /**/ b_p3; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, j0 = 0 * _bs1, + j1 = 1 * _bs1, j2 = 2 * _bs1, j3 = 3 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + c_23 += a_2p * b_p3; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + C[2 * _cs0 + 3 * _cs1] += alpha * c_23; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), + c_02 = ValueType(0), c_03 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0), + c_12 = ValueType(0), c_13 = ValueType(0), + /**/ b_p2, + /**/ b_p3; + + const int i0 = 0 * _as0, i1 = 1 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, + j2 = 2 * _bs1, j3 = 3 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_13 += a_1p * b_p3; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[1 * _cs0 + 3 * _cs1] += alpha * c_13; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), + c_02 = ValueType(0), c_03 = ValueType(0), + /**/ b_p1, + /**/ b_p2, + /**/ b_p3; + + const int i0 = 0 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1, + j3 = 3 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + /**/ b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + /**/ b_p3 = B[p * _bs0 + j3]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_03 += a_0p * b_p3; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[0 * _cs0 + 3 * _cs1] += alpha * c_03; + + return 0; +} + +/// +/// Inner kernel (3x3) +/// ================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, + c_00 = ValueType(0), c_01 = ValueType(0), c_02 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0), c_12 = ValueType(0), a_2p, b_p2, + c_20 = ValueType(0), c_21 = ValueType(0), c_22 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, j0 = 0 * _bs1, + j1 = 1 * _bs1, j2 = 2 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + b_p2 = B[p * _bs0 + j2]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + c_22 += a_2p * b_p2; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + C[2 * _cs0 + 2 * _cs1] += alpha * c_22; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0), a_2p, + c_20 = ValueType(0), c_21 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, j0 = 0 * _bs1, + j1 = 1 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + a_2p = A[i2 + p * _as1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_20 += a_2p * b_p0; + c_21 += a_2p * b_p1; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + C[2 * _cs0 + 1 * _cs1] += alpha * c_21; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), a_1p, c_10 = ValueType(0), a_2p, + c_20 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, j0 = 0 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + a_2p = A[i2 + p * _as1]; + + c_00 += a_0p * b_p0; + c_10 += a_1p * b_p0; + c_20 += a_2p * b_p0; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[2 * _cs0 + 0 * _cs1] += alpha * c_20; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), + c_02 = ValueType(0), a_1p, b_p1, c_10 = ValueType(0), + c_11 = ValueType(0), c_12 = ValueType(0), + /**/ b_p2; + + const int i0 = 0 * _as0, i1 = 1 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, + j2 = 2 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + c_12 += a_1p * b_p2; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + C[1 * _cs0 + 2 * _cs1] += alpha * c_12; + + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), + c_02 = ValueType(0), + /**/ b_p1, + /**/ b_p2; + + const int i0 = 0 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1, j2 = 2 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + /**/ b_p1 = B[p * _bs0 + j1]; + /**/ b_p2 = B[p * _bs0 + j2]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_02 += a_0p * b_p2; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[0 * _cs0 + 2 * _cs1] += alpha * c_02; + + return 0; +} + +/// +/// Inner kernel (2x2) +/// ================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), a_1p, b_p1, + c_10 = ValueType(0), c_11 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + b_p1 = B[p * _bs0 + j1]; + + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + c_10 += a_1p * b_p0; + c_11 += a_1p * b_p1; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + C[1 * _cs0 + 1 * _cs1] += alpha * c_11; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), a_1p, c_10 = ValueType(0); + + const int i0 = 0 * _as0, i1 = 1 * _as0, j0 = 0 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + a_1p = A[i1 + p * _as1]; + + c_00 += a_0p * b_p0; + c_10 += a_1p * b_p0; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[1 * _cs0 + 0 * _cs1] += alpha * c_10; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0), c_01 = ValueType(0), + /**/ b_p1; + const int i0 = 0 * _as0, j0 = 0 * _bs1, j1 = 1 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + /* */ b_p1 = B[p * _bs0 + j1]; + c_00 += a_0p * b_p0; + c_01 += a_0p * b_p1; + } + + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + C[0 * _cs0 + 1 * _cs1] += alpha * c_01; + + return 0; +} + +/// +/// Inner kernel (1x1) +/// ================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (k <= 0) return 0; + + ValueType a_0p, b_p0, c_00 = ValueType(0); + + const int i0 = 0 * _as0, j0 = 0 * _bs1; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < k; ++p) { + a_0p = A[i0 + p * _as1]; + b_p0 = B[p * _bs0 + j0]; + c_00 += a_0p * b_p0; + } + C[0 * _cs0 + 0 * _cs1] += alpha * c_00; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<0, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || k <= 0) return 0; + + switch (m) { + case 5: { + InnerGemmFixC<5, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 4: { + InnerGemmFixC<4, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 3: { + InnerGemmFixC<3, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 2: { + InnerGemmFixC<2, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 1: { + InnerGemmFixC<1, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + default: { + Kokkos::abort("InnerGemmFixC<0,1>::serial_invoke, assert failure (m<=5)"); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<5, 5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + if (!(m <= 5 && n <= 5)) + Kokkos::abort( + "InnerGemmFixC<5,5>::serial_invoke, assert failure (m<=5 && n<=5)"); + + switch (m * 10 + n) { + case 55: { + InnerGemmFixC<5, 5> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 54: { + InnerGemmFixC<5, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 53: { + InnerGemmFixC<5, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 52: { + InnerGemmFixC<5, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 51: { + InnerGemmFixC<5, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 45: { + InnerGemmFixC<4, 5> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 35: { + InnerGemmFixC<3, 5> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 25: { + InnerGemmFixC<2, 5> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 15: { + InnerGemmFixC<1, 5> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + default: { + InnerGemmFixC<4, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, m, n, k, C); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<4, 4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + if (!(m <= 4 && n <= 4)) + Kokkos::abort( + "InnerGemmFixC<4,4>::serial_invoke, assert failure (m<=4 && n<=4)"); + + switch (m * 10 + n) { + case 44: { + InnerGemmFixC<4, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 43: { + InnerGemmFixC<4, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 42: { + InnerGemmFixC<4, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 41: { + InnerGemmFixC<4, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 34: { + InnerGemmFixC<3, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 24: { + InnerGemmFixC<2, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 14: { + InnerGemmFixC<1, 4> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + default: { + InnerGemmFixC<3, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, m, n, k, C); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<3, 3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + if (!(m <= 3 && n <= 3)) + Kokkos::abort( + "InnerGemmFixC<3,3>::serial_invoke, assert failure (m<=3 && n<=3)"); + + switch (m * 10 + n) { + case 33: { + InnerGemmFixC<3, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 32: { + InnerGemmFixC<3, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 31: { + InnerGemmFixC<3, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 23: { + InnerGemmFixC<2, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 13: { + InnerGemmFixC<1, 3> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + default: { + InnerGemmFixC<2, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, m, n, k, C); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<2, 2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + if (!(m <= 2 && n <= 2)) + Kokkos::abort( + "InnerGemmFixC<2,2>::serial_invoke, assert failure (m<=2 && n<=2)"); + + switch (m * 10 + n) { + case 22: { + InnerGemmFixC<2, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 21: { + InnerGemmFixC<2, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 12: { + InnerGemmFixC<1, 2> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + case 11: { + InnerGemmFixC<1, 1> inner(_as0, _as1, _bs0, _bs1, _cs0, _cs1); + inner.serial_invoke(alpha, A, B, k, C); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerGemmFixC<1, 1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT B, const int m, const int n, const int k, + /**/ ValueType *KOKKOS_RESTRICT C) { + if (m <= 0 || n <= 0 || k <= 0) return 0; + if (!(m <= 1 && n <= 1)) + Kokkos::abort( + "InnerGemmFixC<1,1>::serial_invoke, assert failure (m<=1 && n<=1)"); + + return serial_invoke(alpha, A, B, k, C); + ; +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerLU_Decl.hpp b/external/kokkos-kernels/KokkosBatched_InnerLU_Decl.hpp new file mode 100644 index 00000000..484377ff --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerLU_Decl.hpp @@ -0,0 +1,31 @@ +#ifndef __KOKKOSBATCHED_INNER_LU_DECL_HPP__ +#define __KOKKOSBATCHED_INNER_LU_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +namespace KokkosBatched { + +template +struct InnerLU { + const int _as0, _as1; + + KOKKOS_INLINE_FUNCTION + InnerLU(const int as0, const int as1) : _as0(as0), _as1(as1) {} + + // lu + template + KOKKOS_INLINE_FUNCTION int serial_invoke(ValueType *KOKKOS_RESTRICT A); + + // for remainder square + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const int m, + ValueType *KOKKOS_RESTRICT A); + + // for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const int m, const int n, + ValueType *KOKKOS_RESTRICT A); +}; +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerLU_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_InnerLU_Serial_Impl.hpp new file mode 100644 index 00000000..d50e6bdd --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerLU_Serial_Impl.hpp @@ -0,0 +1,394 @@ +#ifndef __KOKKOSBATCHED_INNER_LU_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_INNER_LU_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_InnerLU_Decl.hpp" + +namespace KokkosBatched { + +/// +/// Fixed size LU +/// ================ + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<5>::serial_invoke( + ValueType *KOKKOS_RESTRICT A) { + // load + ValueType a_00 = A[0 * _as0 + 0 * _as1], a_01 = A[0 * _as0 + 1 * _as1], + a_02 = A[0 * _as0 + 2 * _as1], a_03 = A[0 * _as0 + 3 * _as1], + a_04 = A[0 * _as0 + 4 * _as1], a_10 = A[1 * _as0 + 0 * _as1], + a_11 = A[1 * _as0 + 1 * _as1], a_12 = A[1 * _as0 + 2 * _as1], + a_13 = A[1 * _as0 + 3 * _as1], a_14 = A[1 * _as0 + 4 * _as1], + a_20 = A[2 * _as0 + 0 * _as1], a_21 = A[2 * _as0 + 1 * _as1], + a_22 = A[2 * _as0 + 2 * _as1], a_23 = A[2 * _as0 + 3 * _as1], + a_24 = A[2 * _as0 + 4 * _as1], a_30 = A[3 * _as0 + 0 * _as1], + a_31 = A[3 * _as0 + 1 * _as1], a_32 = A[3 * _as0 + 2 * _as1], + a_33 = A[3 * _as0 + 3 * _as1], a_34 = A[3 * _as0 + 4 * _as1], + a_40 = A[4 * _as0 + 0 * _as1], a_41 = A[4 * _as0 + 1 * _as1], + a_42 = A[4 * _as0 + 2 * _as1], a_43 = A[4 * _as0 + 3 * _as1], + a_44 = A[4 * _as0 + 4 * _as1]; + + // 0 iteration + a_10 /= a_00; + a_11 -= a_10 * a_01; + a_12 -= a_10 * a_02; + a_13 -= a_10 * a_03; + a_14 -= a_10 * a_04; + a_20 /= a_00; + a_21 -= a_20 * a_01; + a_22 -= a_20 * a_02; + a_23 -= a_20 * a_03; + a_24 -= a_20 * a_04; + a_30 /= a_00; + a_31 -= a_30 * a_01; + a_32 -= a_30 * a_02; + a_33 -= a_30 * a_03; + a_34 -= a_30 * a_04; + a_40 /= a_00; + a_41 -= a_40 * a_01; + a_42 -= a_40 * a_02; + a_43 -= a_40 * a_03; + a_44 -= a_40 * a_04; + + // 1 iteration + a_21 /= a_11; + a_22 -= a_21 * a_12; + a_23 -= a_21 * a_13; + a_24 -= a_21 * a_14; + a_31 /= a_11; + a_32 -= a_31 * a_12; + a_33 -= a_31 * a_13; + a_34 -= a_31 * a_14; + a_41 /= a_11; + a_42 -= a_41 * a_12; + a_43 -= a_41 * a_13; + a_44 -= a_41 * a_14; + + // 2 iteration + a_32 /= a_22; + a_33 -= a_32 * a_23; + a_34 -= a_32 * a_24; + a_42 /= a_22; + a_43 -= a_42 * a_23; + a_44 -= a_42 * a_24; + + // 3 iteration + a_43 /= a_33; + a_44 -= a_43 * a_34; + + // store + A[1 * _as0 + 0 * _as1] = a_10; + A[1 * _as0 + 1 * _as1] = a_11; + A[1 * _as0 + 2 * _as1] = a_12; + A[1 * _as0 + 3 * _as1] = a_13; + A[1 * _as0 + 4 * _as1] = a_14; + A[2 * _as0 + 0 * _as1] = a_20; + A[2 * _as0 + 1 * _as1] = a_21; + A[2 * _as0 + 2 * _as1] = a_22; + A[2 * _as0 + 3 * _as1] = a_23; + A[2 * _as0 + 4 * _as1] = a_24; + A[3 * _as0 + 0 * _as1] = a_30; + A[3 * _as0 + 1 * _as1] = a_31; + A[3 * _as0 + 2 * _as1] = a_32; + A[3 * _as0 + 3 * _as1] = a_33; + A[3 * _as0 + 4 * _as1] = a_34; + A[4 * _as0 + 0 * _as1] = a_40; + A[4 * _as0 + 1 * _as1] = a_41; + A[4 * _as0 + 2 * _as1] = a_42; + A[4 * _as0 + 3 * _as1] = a_43; + A[4 * _as0 + 4 * _as1] = a_44; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<4>::serial_invoke( + ValueType *KOKKOS_RESTRICT A) { + // load + ValueType a_00 = A[0 * _as0 + 0 * _as1], a_01 = A[0 * _as0 + 1 * _as1], + a_02 = A[0 * _as0 + 2 * _as1], a_03 = A[0 * _as0 + 3 * _as1], + a_10 = A[1 * _as0 + 0 * _as1], a_11 = A[1 * _as0 + 1 * _as1], + a_12 = A[1 * _as0 + 2 * _as1], a_13 = A[1 * _as0 + 3 * _as1], + a_20 = A[2 * _as0 + 0 * _as1], a_21 = A[2 * _as0 + 1 * _as1], + a_22 = A[2 * _as0 + 2 * _as1], a_23 = A[2 * _as0 + 3 * _as1], + a_30 = A[3 * _as0 + 0 * _as1], a_31 = A[3 * _as0 + 1 * _as1], + a_32 = A[3 * _as0 + 2 * _as1], a_33 = A[3 * _as0 + 3 * _as1]; + + // 0 iteration + a_10 /= a_00; + a_11 -= a_10 * a_01; + a_12 -= a_10 * a_02; + a_13 -= a_10 * a_03; + a_20 /= a_00; + a_21 -= a_20 * a_01; + a_22 -= a_20 * a_02; + a_23 -= a_20 * a_03; + a_30 /= a_00; + a_31 -= a_30 * a_01; + a_32 -= a_30 * a_02; + a_33 -= a_30 * a_03; + + // 1 iteration + a_21 /= a_11; + a_22 -= a_21 * a_12; + a_23 -= a_21 * a_13; + a_31 /= a_11; + a_32 -= a_31 * a_12; + a_33 -= a_31 * a_13; + + // 2 iteration + a_32 /= a_22; + a_33 -= a_32 * a_23; + + // store + A[1 * _as0 + 0 * _as1] = a_10; + A[1 * _as0 + 1 * _as1] = a_11; + A[1 * _as0 + 2 * _as1] = a_12; + A[1 * _as0 + 3 * _as1] = a_13; + A[2 * _as0 + 0 * _as1] = a_20; + A[2 * _as0 + 1 * _as1] = a_21; + A[2 * _as0 + 2 * _as1] = a_22; + A[2 * _as0 + 3 * _as1] = a_23; + A[3 * _as0 + 0 * _as1] = a_30; + A[3 * _as0 + 1 * _as1] = a_31; + A[3 * _as0 + 2 * _as1] = a_32; + A[3 * _as0 + 3 * _as1] = a_33; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<3>::serial_invoke( + ValueType *KOKKOS_RESTRICT A) { + // load + ValueType a_00 = A[0 * _as0 + 0 * _as1], a_01 = A[0 * _as0 + 1 * _as1], + a_02 = A[0 * _as0 + 2 * _as1], a_10 = A[1 * _as0 + 0 * _as1], + a_11 = A[1 * _as0 + 1 * _as1], a_12 = A[1 * _as0 + 2 * _as1], + a_20 = A[2 * _as0 + 0 * _as1], a_21 = A[2 * _as0 + 1 * _as1], + a_22 = A[2 * _as0 + 2 * _as1]; + + // 0 iteration + a_10 /= a_00; + a_11 -= a_10 * a_01; + a_12 -= a_10 * a_02; + a_20 /= a_00; + a_21 -= a_20 * a_01; + a_22 -= a_20 * a_02; + + // 1 iteration + a_21 /= a_11; + a_22 -= a_21 * a_12; + + // store + A[1 * _as0 + 0 * _as1] = a_10; + A[1 * _as0 + 1 * _as1] = a_11; + A[1 * _as0 + 2 * _as1] = a_12; + A[2 * _as0 + 0 * _as1] = a_20; + A[2 * _as0 + 1 * _as1] = a_21; + A[2 * _as0 + 2 * _as1] = a_22; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<2>::serial_invoke( + ValueType *KOKKOS_RESTRICT A) { + // load + ValueType a_00 = A[0 * _as0 + 0 * _as1], a_01 = A[0 * _as0 + 1 * _as1], + a_10 = A[1 * _as0 + 0 * _as1], a_11 = A[1 * _as0 + 1 * _as1]; + + // 0 iteration + a_10 /= a_00; + a_11 -= a_10 * a_01; + + // store + A[1 * _as0 + 0 * _as1] = a_10; + A[1 * _as0 + 1 * _as1] = a_11; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<1>::serial_invoke( + ValueType *KOKKOS_RESTRICT /* A */) { + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<5>::serial_invoke( + const int m, ValueType *KOKKOS_RESTRICT A) { + if (m > 5) Kokkos::abort("InnerLU<5>::serial_invoke, assert failure (m<=5)"); + if (m <= 0) return 0; + + switch (m) { + case 5: { + InnerLU<5> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 4: { + InnerLU<4> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 3: { + InnerLU<3> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 2: { + InnerLU<2> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 1: { + InnerLU<1> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<4>::serial_invoke( + const int m, ValueType *KOKKOS_RESTRICT A) { + if (m > 4) Kokkos::abort("InnerLU<4>::serial_invoke, assert failure (m<=4)"); + if (m <= 0) return 0; + + switch (m) { + case 4: { + InnerLU<4> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 3: { + InnerLU<3> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 2: { + InnerLU<2> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 1: { + InnerLU<1> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<3>::serial_invoke( + const int m, ValueType *KOKKOS_RESTRICT A) { + if (m > 3) Kokkos::abort("InnerLU<3>::serial_invoke, assert failure (m<=3)"); + if (m <= 0) return 0; + + switch (m) { + case 3: { + InnerLU<3> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 2: { + InnerLU<2> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 1: { + InnerLU<1> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<2>::serial_invoke( + const int m, ValueType *KOKKOS_RESTRICT A) { + if (m > 2) Kokkos::abort("InnerLU<2>::serial_invoke, assert failure (m<=2)"); + if (m <= 0) return 0; + + switch (m) { + case 2: { + InnerLU<2> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + case 1: { + InnerLU<1> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerLU<1>::serial_invoke( + const int m, ValueType *KOKKOS_RESTRICT A) { + if (m > 1) Kokkos::abort("InnerLU<1>::serial_invoke, assert failure (m<=1)"); + if (m <= 0) return 0; + + switch (m) { + case 1: { + InnerLU<1> inner(_as0, _as1); + inner.serial_invoke(A); + break; + } + } + return 0; +} + +// template +// template +// KOKKOS_INLINE_FUNCTION +// int +// InnerLU:: +// serial_invoke(const int m, const int n, +// ValueType *KOKKOS_RESTRICT A) { +// if (m <= 0 || n <= 0) return 0; +// const int k = m < n ? m : n; +// for (int p=0;p +struct InnerMultipleDotProduct { + const int _as0, _as1, _xs0, _ys0; + + KOKKOS_INLINE_FUNCTION + InnerMultipleDotProduct(const int as0, const int as1, const int xs0, + const int ys0) + : _as0(as0), _as1(as1), _xs0(xs0), _ys0(ys0) {} + + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, + const int n, + /**/ ValueType *KOKKOS_RESTRICT y); + + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, + const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y); +}; +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerMultipleDotProduct_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_InnerMultipleDotProduct_Serial_Impl.hpp new file mode 100644 index 00000000..70354c5e --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerMultipleDotProduct_Serial_Impl.hpp @@ -0,0 +1,305 @@ +#ifndef __KOKKOSBATCHED_INNER_MULTIPLE_DOT_PRODUCT_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_INNER_MULTIPLE_DOT_PRODUCT_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_InnerMultipleDotProduct_Decl.hpp" + +namespace KokkosBatched { + +/// +/// Dot Product for GEMV +/// ==================== + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (n <= 0) return 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0, + i4 = 4 * _as0; + + // unroll by rows + ValueType y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0, y_4 = 0; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) { + const int jj = j * _as1; + const ValueType x_j = x[j * _xs0]; + + y_0 += A[i0 + jj] * x_j; + y_1 += A[i1 + jj] * x_j; + y_2 += A[i2 + jj] * x_j; + y_3 += A[i3 + jj] * x_j; + y_4 += A[i4 + jj] * x_j; + } + + y[0 * _ys0] += alpha * y_0; + y[1 * _ys0] += alpha * y_1; + y[2 * _ys0] += alpha * y_2; + y[3 * _ys0] += alpha * y_3; + y[4 * _ys0] += alpha * y_4; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (!n) return 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0, i3 = 3 * _as0; + + // unroll by rows + ValueType y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) { + const int jj = j * _as1; + const ValueType x_j = x[j * _xs0]; + + y_0 += A[i0 + jj] * x_j; + y_1 += A[i1 + jj] * x_j; + y_2 += A[i2 + jj] * x_j; + y_3 += A[i3 + jj] * x_j; + } + + y[0 * _ys0] += alpha * y_0; + y[1 * _ys0] += alpha * y_1; + y[2 * _ys0] += alpha * y_2; + y[3 * _ys0] += alpha * y_3; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (n <= 0) return 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0, i2 = 2 * _as0; + + // unroll by rows + ValueType y_0 = 0, y_1 = 0, y_2 = 0; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) { + const int jj = j * _as1; + const ValueType x_j = x[j * _xs0]; + + y_0 += A[i0 + jj] * x_j; + y_1 += A[i1 + jj] * x_j; + y_2 += A[i2 + jj] * x_j; + } + + y[0 * _ys0] += alpha * y_0; + y[1 * _ys0] += alpha * y_1; + y[2 * _ys0] += alpha * y_2; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (n <= 0) return 0; + + const int i0 = 0 * _as0, i1 = 1 * _as0; + + // unroll by rows + ValueType y_0 = 0, y_1 = 0; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) { + const int jj = j * _as1; + const ValueType x_j = x[j * _xs0]; + + y_0 += A[i0 + jj] * x_j; + y_1 += A[i1 + jj] * x_j; + } + + y[0 * _ys0] += alpha * y_0; + y[1 * _ys0] += alpha * y_1; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (n <= 0) return 0; + + // unroll by rows + ValueType y_0 = 0; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < n; ++j) y_0 += A[j * _as1] * x[j * _xs0]; + + y[0] += alpha * y_0; + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<5>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 5: { + InnerMultipleDotProduct<5> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 4: { + InnerMultipleDotProduct<4> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 3: { + InnerMultipleDotProduct<3> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 2: { + InnerMultipleDotProduct<2> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 1: { + InnerMultipleDotProduct<1> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<4>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 4: { + InnerMultipleDotProduct<4> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 3: { + InnerMultipleDotProduct<3> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 2: { + InnerMultipleDotProduct<2> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 1: { + InnerMultipleDotProduct<1> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<3>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 3: { + InnerMultipleDotProduct<3> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 2: { + InnerMultipleDotProduct<2> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 1: { + InnerMultipleDotProduct<1> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<2>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 2: { + InnerMultipleDotProduct<2> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + case 1: { + InnerMultipleDotProduct<1> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerMultipleDotProduct<1>::serial_invoke( + const ScalarType alpha, const ValueType *KOKKOS_RESTRICT A, + const ValueType *KOKKOS_RESTRICT x, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT y) { + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 1: { + InnerMultipleDotProduct<1> inner(_as0, _as1, _xs0, _ys0); + inner.serial_invoke(alpha, A, x, n, y); + break; + } + } + return 0; +} +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerTrsm_Decl.hpp b/external/kokkos-kernels/KokkosBatched_InnerTrsm_Decl.hpp new file mode 100644 index 00000000..a78df609 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerTrsm_Decl.hpp @@ -0,0 +1,106 @@ +#ifndef __KOKKOSBATCHED_INNER_TRSM_DECL_HPP__ +#define __KOKKOSBATCHED_INNER_TRSM_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +namespace KokkosBatched { + +// specialized for different m and n +// Solve L(m x m) X(m x n) = B(m x n) +template +struct InnerTrsmLeftLowerUnitDiag { + const int _as0, _as1, _bs0, _bs1; + + KOKKOS_INLINE_FUNCTION + InnerTrsmLeftLowerUnitDiag(const int as0, const int as1, const int bs0, + const int bs1) + : _as0(as0), _as1(as1), _bs0(bs0), _bs1(bs1) {} + + // trisolve + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int n, + /**/ ValueType *KOKKOS_RESTRICT B); + + // for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B); +}; + +// specialized for different m and n +// Solve L(m x m) X(m x n) = B(m x n) +template +struct InnerTrsmLeftLowerNonUnitDiag { + const int _as0, _as1, _bs0, _bs1; + + KOKKOS_INLINE_FUNCTION + InnerTrsmLeftLowerNonUnitDiag(const int as0, const int as1, const int bs0, + const int bs1) + : _as0(as0), _as1(as1), _bs0(bs0), _bs1(bs1) {} + + // trisolve + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int n, + /**/ ValueType *KOKKOS_RESTRICT B); + + // for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B); +}; + +// specialized for different m and n +// Solve U(m x m) X(m x n) = B(m x n) +template +struct InnerTrsmLeftUpperUnitDiag { + const int _as0, _as1, _bs0, _bs1; + + KOKKOS_INLINE_FUNCTION + InnerTrsmLeftUpperUnitDiag(const int as0, const int as1, const int bs0, + const int bs1) + : _as0(as0), _as1(as1), _bs0(bs0), _bs1(bs1) {} + + // trisolve + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int n, + /**/ ValueType *KOKKOS_RESTRICT B); + + // for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B); +}; + +// specialized for different m and n +// Solve U(m x m) X(m x n) = B(m x n) +template +struct InnerTrsmLeftUpperNonUnitDiag { + const int _as0, _as1, _bs0, _bs1; + + KOKKOS_INLINE_FUNCTION + InnerTrsmLeftUpperNonUnitDiag(const int as0, const int as1, const int bs0, + const int bs1) + : _as0(as0), _as1(as1), _bs0(bs0), _bs1(bs1) {} + + // trisolve + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int n, + /**/ ValueType *KOKKOS_RESTRICT B); + + // for remainder + template + KOKKOS_INLINE_FUNCTION int serial_invoke(const ValueType *KOKKOS_RESTRICT A, + const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B); +}; + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_InnerTrsm_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_InnerTrsm_Serial_Impl.hpp new file mode 100644 index 00000000..401b13df --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_InnerTrsm_Serial_Impl.hpp @@ -0,0 +1,1577 @@ +#ifndef __KOKKOSBATCHED_INNER_TRSM_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_INNER_TRSM_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_InnerTrsm_Decl.hpp" + +namespace KokkosBatched { + +/// +/// Fixed size TRSM +/// ================ +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1], a_30 = A[3 * _as0 + 0 * _as1], + a_31 = A[3 * _as0 + 1 * _as1], a_32 = A[3 * _as0 + 2 * _as1], + a_40 = A[4 * _as0 + 0 * _as1], a_41 = A[4 * _as0 + 1 * _as1], + a_42 = A[4 * _as0 + 2 * _as1], a_43 = A[4 * _as0 + 3 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p, ValueType &b_4p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + b_4p = B[4 * _bs0 + p * _bs1]; + + // 0 iteration + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + b_3p -= a_30 * b_0p; + b_4p -= a_40 * b_0p; + + // 1 iteration + b_2p -= a_21 * b_1p; + b_3p -= a_31 * b_1p; + b_4p -= a_41 * b_1p; + + // 2 iteration + b_3p -= a_32 * b_2p; + b_4p -= a_42 * b_2p; + + // 3 iteration + b_4p -= a_43 * b_3p; + + // store + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + B[4 * _bs0 + p * _bs1] = b_4p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[5]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3], b_p[4]); + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1], a_30 = A[3 * _as0 + 0 * _as1], + a_31 = A[3 * _as0 + 1 * _as1], a_32 = A[3 * _as0 + 2 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + + // 0 iteration + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + b_3p -= a_30 * b_0p; + + // 1 iteration + b_2p -= a_21 * b_1p; + b_3p -= a_31 * b_1p; + + // 2 iteration + b_3p -= a_32 * b_2p; + + // store + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[4]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3]); + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + + // 0 iteration + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + + // 1 iteration + b_2p -= a_21 * b_1p; + + // store + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[3]; + trsv(p, b_p[0], b_p[1], b_p[2]); + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + + // 0 iteration + b_1p -= a_10 * b_0p; + + // store + B[1 * _bs0 + p * _bs1] = b_1p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[2]; + trsv(p, b_p[0], b_p[1]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT /* A */, const int /* n */, + /**/ ValueType *KOKKOS_RESTRICT /* B */) { + return 0; +} + +/// +/// TRSM +/// ==== +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 5) + Kokkos::abort( + "InnerTrsmLeftLowerUnitDiag<5>::serial_invoke, assert failure (m<=5)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 5: { + InnerTrsmLeftLowerUnitDiag<5> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 4: { + InnerTrsmLeftLowerUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftLowerUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 4) + Kokkos::abort( + "InnerTrsmLeftLowerUnitDiag<4>::serial_invoke, assert failure (m<=4)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 4: { + InnerTrsmLeftLowerUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftLowerUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 3) + Kokkos::abort( + "InnerTrsmLeftLowerUnitDiag<3>::serial_invoke, assert failure (m<=3)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 3: { + InnerTrsmLeftLowerUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 2) + Kokkos::abort( + "InnerTrsmLeftLowerUnitDiag<2>::serial_invoke, assert failure (m<=2)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 2: { + InnerTrsmLeftLowerUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 1) + Kokkos::abort( + "InnerTrsmLeftLowerUnitDiag<1>::serial_invoke, assert failure (m<=1)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 1: { + InnerTrsmLeftLowerUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} + +/// +/// Fixed size TRSM +/// ================ +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1], a_30 = A[3 * _as0 + 0 * _as1], + a_31 = A[3 * _as0 + 1 * _as1], a_32 = A[3 * _as0 + 2 * _as1], + a_40 = A[4 * _as0 + 0 * _as1], a_41 = A[4 * _as0 + 1 * _as1], + a_42 = A[4 * _as0 + 2 * _as1], a_43 = A[4 * _as0 + 3 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1], + // a_33 = A[3*_as0+3*_as1], + // a_44 = A[4*_as0+4*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1], + inv_a_33 = + static_cast(1.0) / A[3 * _as0 + 3 * _as1], + inv_a_44 = + static_cast(1.0) / A[4 * _as0 + 4 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p, ValueType &b_4p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + b_4p = B[4 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p *= inv_a_00; /* b_0p /= a_00;*/ + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + b_3p -= a_30 * b_0p; + b_4p -= a_40 * b_0p; + + // 1 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_2p -= a_21 * b_1p; + b_3p -= a_31 * b_1p; + b_4p -= a_41 * b_1p; + + // 2 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + b_3p -= a_32 * b_2p; + b_4p -= a_42 * b_2p; + + // 3 iteration + b_3p *= inv_a_33; /* b_3p /= a_33; */ + b_4p -= a_43 * b_3p; + + // 4 iteration + b_4p *= inv_a_44; /* b_4p /= a_44; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + B[4 * _bs0 + p * _bs1] = b_4p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[5]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3], b_p[4]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1], a_30 = A[3 * _as0 + 0 * _as1], + a_31 = A[3 * _as0 + 1 * _as1], a_32 = A[3 * _as0 + 2 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1], + // a_33 = A[3*_as0+3*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1], + inv_a_33 = + static_cast(1.0) / A[3 * _as0 + 3 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p *= inv_a_00; /* b_0p /= a_00;*/ + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + b_3p -= a_30 * b_0p; + + // 1 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_2p -= a_21 * b_1p; + b_3p -= a_31 * b_1p; + + // 2 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + b_3p -= a_32 * b_2p; + + // 3 iteration + b_3p *= inv_a_33; /* b_3p /= a_33; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[4]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1], a_20 = A[2 * _as0 + 0 * _as1], + a_21 = A[2 * _as0 + 1 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p *= inv_a_00; /* b_0p /= a_00;*/ + b_1p -= a_10 * b_0p; + b_2p -= a_20 * b_0p; + + // 1 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_2p -= a_21 * b_1p; + + // 2 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[3]; + trsv(p, b_p[0], b_p[1], b_p[2]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_10 = A[1 * _as0 + 0 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p *= inv_a_00; /* b_0p /= a_00;*/ + b_1p -= a_10 * b_0p; + + // 1 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[2]; + trsv(p, b_p[0], b_p[1]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + // const ValueType + // a_00 = A[0*_as0+0*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1]; + + auto trsv = [&](const int p, ValueType & /* b_0p */) { + B[0 * _bs0 + p * _bs1] *= inv_a_00; /* b_0p /= a_00;*/ + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p; + trsv(p, b_p); + } + + return 0; +} + +/// +/// TRSM +/// ============== +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 5) + Kokkos::abort( + "InnerTrsmLeftLowerNonUnitDiag<5>::serial_invoke, assert failure " + "(m<=5)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 5: { + InnerTrsmLeftLowerNonUnitDiag<5> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 4: { + InnerTrsmLeftLowerNonUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftLowerNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 4) + Kokkos::abort( + "InnerTrsmLeftLowerNonUnitDiag<4>::serial_invoke, assert failure " + "(m<=4)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 4: { + InnerTrsmLeftLowerNonUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftLowerNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 3) + Kokkos::abort( + "InnerTrsmLeftLowerNonUnitDiag<3>::serial_invoke, assert failure " + "(m<=3)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 3: { + InnerTrsmLeftLowerNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftLowerNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 2) + Kokkos::abort( + "InnerTrsmLeftLowerNonUnitDiag<2>::serial_invoke, assert failure " + "(m<=2)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 2: { + InnerTrsmLeftLowerNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftLowerNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftLowerNonUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 1) + Kokkos::abort( + "InnerTrsmLeftLowerNonUnitDiag<1>::serial_invoke, assert failure " + "(m<=1)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 1: { + InnerTrsmLeftLowerNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} + +/// +/// Fixed size TRSM +/// ================ +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + a_03 = A[0 * _as0 + 3 * _as1], a_04 = A[0 * _as0 + 4 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1], + a_13 = A[1 * _as0 + 3 * _as1], a_14 = A[1 * _as0 + 4 * _as1], + /**/ a_23 = A[2 * _as0 + 3 * _as1], + a_24 = A[2 * _as0 + 4 * _as1], + /**/ a_34 = A[3 * _as0 + 4 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p, ValueType &b_4p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + b_4p = B[4 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p -= a_04 * b_4p; + b_1p -= a_14 * b_4p; + b_2p -= a_24 * b_4p; + b_3p -= a_34 * b_4p; + + // 1 iteration + b_0p -= a_03 * b_3p; + b_1p -= a_13 * b_3p; + b_2p -= a_23 * b_3p; + + // 2 iteration + b_0p -= a_02 * b_2p; + b_1p -= a_12 * b_2p; + + // 1 iteration + b_0p -= a_01 * b_1p; + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[5]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3], b_p[4]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + a_03 = A[0 * _as0 + 3 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1], + a_13 = A[1 * _as0 + 3 * _as1], + /**/ a_23 = A[2 * _as0 + 3 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p -= a_03 * b_3p; + b_1p -= a_13 * b_3p; + b_2p -= a_23 * b_3p; + + // 1 iteration + b_0p -= a_02 * b_2p; + b_1p -= a_12 * b_2p; + + // 2 iteration + b_0p -= a_01 * b_1p; + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[4]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p -= a_02 * b_2p; + b_1p -= a_12 * b_2p; + + // 1 iteration + b_0p -= a_01 * b_1p; + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[3]; + trsv(p, b_p[0], b_p[1], b_p[2]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + + // 0 iteration + b_0p -= a_01 * b_1p; + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[2]; + trsv(p, b_p[0], b_p[1]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT /* A */, const int /* n */, + /**/ ValueType *KOKKOS_RESTRICT /* B */) { + return 0; +} + +/// +/// TRSM +/// ==== +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 5) + Kokkos::abort( + "InnerTrsmLeftUpperUnitDiag<5>::serial_invoke, assert failure (m<=5)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 5: { + InnerTrsmLeftUpperUnitDiag<5> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 4: { + InnerTrsmLeftUpperUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftUpperUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 4) + Kokkos::abort( + "InnerTrsmLeftUpperUnitDiag<4>::serial_invoke, assert failure (m<=4)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 4: { + InnerTrsmLeftUpperUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftUpperUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 3) + Kokkos::abort( + "InnerTrsmLeftUpperUnitDiag<3>::serial_invoke, assert failure (m<=3)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 3: { + InnerTrsmLeftUpperUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 2) + Kokkos::abort( + "InnerTrsmLeftUpperUnitDiag<2>::serial_invoke, assert failure (m<=2)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 2: { + InnerTrsmLeftUpperUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 1) + Kokkos::abort( + "InnerTrsmLeftUpperUnitDiag<1>::serial_invoke, assert failure (m<=1)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 1: { + InnerTrsmLeftUpperUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} + +/// +/// Fixed size TRSM +/// ================ +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + a_03 = A[0 * _as0 + 3 * _as1], a_04 = A[0 * _as0 + 4 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1], + a_13 = A[1 * _as0 + 3 * _as1], a_14 = A[1 * _as0 + 4 * _as1], + /**/ a_23 = A[2 * _as0 + 3 * _as1], + a_24 = A[2 * _as0 + 4 * _as1], + /**/ a_34 = A[3 * _as0 + 4 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1], + // a_33 = A[3*_as0+3*_as1], + // a_44 = A[4*_as0+4*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1], + inv_a_33 = + static_cast(1.0) / A[3 * _as0 + 3 * _as1], + inv_a_44 = + static_cast(1.0) / A[4 * _as0 + 4 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p, ValueType &b_4p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + b_4p = B[4 * _bs0 + p * _bs1]; + + // 0 iteration + b_4p *= inv_a_44; /* b_4p /= a_44;*/ + b_3p -= a_34 * b_4p; + b_2p -= a_24 * b_4p; + b_1p -= a_14 * b_4p; + b_0p -= a_04 * b_4p; + + // 1 iterationls + b_3p *= inv_a_33; /* b_3p /= a_33;*/ + b_2p -= a_23 * b_3p; + b_1p -= a_13 * b_3p; + b_0p -= a_03 * b_3p; + + // 2 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + b_1p -= a_12 * b_2p; + b_0p -= a_02 * b_2p; + + // 3 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_0p -= a_01 * b_1p; + + // 4 iteration + b_0p *= inv_a_00; /* b_0p /= a_00; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + B[4 * _bs0 + p * _bs1] = b_4p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[5]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3], b_p[4]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + a_03 = A[0 * _as0 + 3 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1], + a_13 = A[1 * _as0 + 3 * _as1], + /**/ a_23 = A[2 * _as0 + 3 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1], + // a_33 = A[3*_as0+3*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1], + inv_a_33 = + static_cast(1.0) / A[3 * _as0 + 3 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p, ValueType &b_3p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + b_3p = B[3 * _bs0 + p * _bs1]; + + // 0 iteration + b_3p *= inv_a_33; /* b_3p /= a_33;*/ + b_2p -= a_23 * b_3p; + b_1p -= a_13 * b_3p; + b_0p -= a_03 * b_3p; + + // 1 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + b_1p -= a_12 * b_2p; + b_0p -= a_02 * b_2p; + + // 2 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_0p -= a_01 * b_1p; + + // 3 iteration + b_0p *= inv_a_00; /* b_0p /= a_00; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + B[3 * _bs0 + p * _bs1] = b_3p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[4]; + trsv(p, b_p[0], b_p[1], b_p[2], b_p[3]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1], a_02 = A[0 * _as0 + 2 * _as1], + /**/ a_12 = A[1 * _as0 + 2 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1], + // a_22 = A[2*_as0+2*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1], + inv_a_22 = + static_cast(1.0) / A[2 * _as0 + 2 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p, + ValueType &b_2p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + b_2p = B[2 * _bs0 + p * _bs1]; + + // 0 iteration + b_2p *= inv_a_22; /* b_2p /= a_22; */ + b_1p -= a_12 * b_2p; + b_0p -= a_02 * b_2p; + + // 1 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_0p -= a_01 * b_1p; + + // 2 iteration + b_0p *= inv_a_00; /* b_0p /= a_00; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + B[2 * _bs0 + p * _bs1] = b_2p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[3]; + trsv(p, b_p[0], b_p[1], b_p[2]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + const ValueType a_01 = A[0 * _as0 + 1 * _as1]; + + // const ValueType + // a_00 = A[0*_as0+0*_as1], + // a_11 = A[1*_as0+1*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1], + inv_a_11 = + static_cast(1.0) / A[1 * _as0 + 1 * _as1]; + + auto trsv = [&](const int p, ValueType &b_0p, ValueType &b_1p) { + // load + b_0p = B[0 * _bs0 + p * _bs1]; + b_1p = B[1 * _bs0 + p * _bs1]; + + // 2 iteration + b_1p *= inv_a_11; /* b_1p /= a_11; */ + b_0p -= a_01 * b_1p; + + // 3 iteration + b_0p *= inv_a_00; /* b_0p /= a_00; */ + + // store + B[0 * _bs0 + p * _bs1] = b_0p; + B[1 * _bs0 + p * _bs1] = b_1p; + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p[2]; + trsv(p, b_p[0], b_p[1]); + } + + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (n <= 0) return 0; + + // const ValueType + // a_00 = A[0*_as0+0*_as1]; + + const ValueType inv_a_00 = + static_cast(1.0) / A[0 * _as0 + 0 * _as1]; + + auto trsv = [&](const int p, ValueType & /* b_0p */) { + // 0 iteration + B[0 * _bs0 + p * _bs1] *= inv_a_00; /* b_0p /= a_00; */ + }; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int p = 0; p < n; ++p) { + ValueType b_p; + trsv(p, b_p); + } + + return 0; +} + +/// +/// TRSM +/// ==== +/// L(m x m) X(m x n) = B (m x n) + +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<5>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 5) + Kokkos::abort( + "InnerTrsmLeftUpperNonUnitDiag<5>::serial_invoke, assert failure " + "(m<=5)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 5: { + InnerTrsmLeftUpperNonUnitDiag<5> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 4: { + InnerTrsmLeftUpperNonUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftUpperNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<4>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 4) + Kokkos::abort( + "InnerTrsmLeftUpperNonUnitDiag<4>::serial_invoke, assert failure " + "(m<=4)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 4: { + InnerTrsmLeftUpperNonUnitDiag<4> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 3: { + InnerTrsmLeftUpperNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<3>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 3) + Kokkos::abort( + "InnerTrsmLeftUpperNonUnitDiag<3>::serial_invoke, assert failure " + "(m<=3)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 3: { + InnerTrsmLeftUpperNonUnitDiag<3> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 2: { + InnerTrsmLeftUpperNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<2>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 2) + Kokkos::abort( + "InnerTrsmLeftUpperNonUnitDiag<2>::serial_invoke, assert failure " + "(m<=2)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 2: { + InnerTrsmLeftUpperNonUnitDiag<2> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + case 1: { + InnerTrsmLeftUpperNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} +template <> +template +KOKKOS_INLINE_FUNCTION int InnerTrsmLeftUpperNonUnitDiag<1>::serial_invoke( + const ValueType *KOKKOS_RESTRICT A, const int m, const int n, + /**/ ValueType *KOKKOS_RESTRICT B) { + if (m > 1) + Kokkos::abort( + "InnerTrsmLeftUpperNonUnitDiag<1>::serial_invoke, assert failure " + "(m<=1)"); + if (m <= 0 || n <= 0) return 0; + switch (m) { + case 1: { + InnerTrsmLeftUpperNonUnitDiag<1> inner(_as0, _as1, _bs0, _bs1); + inner.serial_invoke(A, n, B); + break; + } + } + return 0; +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_LU_Decl.hpp b/external/kokkos-kernels/KokkosBatched_LU_Decl.hpp new file mode 100644 index 00000000..58e76267 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_LU_Decl.hpp @@ -0,0 +1,57 @@ +#ifndef __KOKKOSBATCHED_LU_DECL_HPP__ +#define __KOKKOSBATCHED_LU_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Vector.hpp" + +namespace KokkosBatched { + +template +struct SerialLU { + // no piv version + template + KOKKOS_INLINE_FUNCTION static int invoke( + const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny = 0); +}; + +template +struct TeamLU { + // no piv version + template + KOKKOS_INLINE_FUNCTION static int invoke( + const MemberType &member, const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny = 0); +}; + +/// +/// Selective Interface +/// +template +struct LU { + // no piv version + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke( + const MemberType &member, const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny = 0) { + int r_val = 0; + if (std::is_same::value) { + r_val = SerialLU::invoke(A, tiny); + } else if (std::is_same::value) { + r_val = TeamLU::invoke(member, A, tiny); + } + return r_val; + } +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_LU_Serial_Impl.hpp" +//#include "KokkosBatched_LU_Team_Impl.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_LU_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_LU_Serial_Impl.hpp new file mode 100644 index 00000000..89173aed --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_LU_Serial_Impl.hpp @@ -0,0 +1,78 @@ +#ifndef __KOKKOSBATCHED_LU_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_LU_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_LU_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Impl +/// ========= + +/// +/// SerialLU no piv +/// + +#if defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__) +template <> +template +KOKKOS_INLINE_FUNCTION int SerialLU::invoke( + const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny) { + typedef typename AViewType::value_type vector_type; + // typedef typename vector_type::value_type value_type; + + const int m = A.extent(0), n = A.extent(1); + + static_assert(is_vector::value, "value type is not vector type"); + static_assert( + vector_type::vector_length == 4 || vector_type::vector_length == 8, + "AVX, AVX2 and AVX512 is supported"); + const MKL_COMPACT_PACK format = + vector_type::vector_length == 8 ? MKL_COMPACT_AVX512 : MKL_COMPACT_AVX; + + int r_val = 0; + if (A.stride_0() == 1) { + mkl_dgetrfnp_compact(MKL_COL_MAJOR, m, n, (double *)A.data(), A.stride_1(), + (MKL_INT *)&r_val, format, + (MKL_INT)vector_type::vector_length); + } else if (A.stride_1() == 1) { + mkl_dgetrfnp_compact(MKL_ROW_MAJOR, m, n, (double *)A.data(), A.stride_0(), + (MKL_INT *)&r_val, format, + (MKL_INT)vector_type::vector_length); + } else { + r_val = -1; + } + return r_val; +} +#endif + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialLU::invoke( + const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny) { + return SerialLU_Internal::invoke( + A.extent(0), A.extent(1), A.data(), A.stride_0(), A.stride_1(), tiny); +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialLU::invoke( + const AViewType &A, + const typename MagnitudeScalarType< + typename AViewType::non_const_value_type>::type tiny) { + return SerialLU_Internal::invoke( + A.extent(0), A.extent(1), A.data(), A.stride_0(), A.stride_1(), tiny); +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_LU_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_LU_Serial_Internal.hpp new file mode 100644 index 00000000..5523f206 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_LU_Serial_Internal.hpp @@ -0,0 +1,128 @@ +#ifndef __KOKKOSBATCHED_LU_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_LU_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Vector.hpp" +#include "KokkosBatched_InnerLU_Serial_Impl.hpp" +#include "KokkosBatched_InnerTrsm_Serial_Impl.hpp" +#include "KokkosBatched_Gemm_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== + +template +struct SerialLU_Internal { + template + KOKKOS_INLINE_FUNCTION static int invoke( + const int m, const int n, ValueType *KOKKOS_RESTRICT A, const int as0, + const int as1, const typename MagnitudeScalarType::type tiny); +}; + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialLU_Internal::invoke( + const int m, const int n, ValueType *KOKKOS_RESTRICT A, const int as0, + const int as1, const typename MagnitudeScalarType::type tiny) { + const int k = (m < n ? m : n); + if (k <= 0) return 0; + + using mst = typename MagnitudeScalarType::type; + const auto abs_tiny = tiny > 0 ? tiny : mst(-tiny); + const auto minus_abs_tiny = -abs_tiny; + + for (int p = 0; p < k; ++p) { + const int iend = m - p - 1, jend = n - p - 1; + + const ValueType *KOKKOS_RESTRICT a12t = A + (p)*as0 + (p + 1) * as1; + + ValueType *KOKKOS_RESTRICT a21 = A + (p + 1) * as0 + (p)*as1, + *KOKKOS_RESTRICT A22 = + A + (p + 1) * as0 + (p + 1) * as1; + + if (tiny != 0) { + ValueType &alpha11_reference = A[p * as0 + p * as1]; + const auto alpha11_real = + Kokkos::Details::ArithTraits::real(alpha11_reference); + alpha11_reference += minus_abs_tiny * ValueType(alpha11_real < 0); + alpha11_reference += abs_tiny * ValueType(alpha11_real >= 0); + } + + const ValueType alpha11 = A[p * as0 + p * as1]; + + for (int i = 0; i < iend; ++i) { + // a21[i*as0] *= inv_alpha11; + a21[i * as0] /= alpha11; + +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int j = 0; j < jend; ++j) + A22[i * as0 + j * as1] -= a21[i * as0] * a12t[j * as1]; + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialLU_Internal::invoke( + const int m, const int n, ValueType *KOKKOS_RESTRICT A, const int as0, + const int as1, + const typename MagnitudeScalarType::type /*tiny*/) { + constexpr int mbAlgo = Algo::LU::Blocked::mb(); + const typename MagnitudeScalarType::type one(1.0), minus_one(-1.0); + + const int k = (m < n ? m : n); + if (k <= 0) return 0; + + InnerLU lu(as0, as1); + + InnerTrsmLeftLowerUnitDiag trsm_llu(as0, as1, as0, as1); + InnerTrsmLeftLowerNonUnitDiag trsm_run(as1, as0, as1, as0); + + auto lu_factorize = [&](const int ib, const int jb, + ValueType *KOKKOS_RESTRICT AA) { + const int mb = mbAlgo; + const int kb = ib < jb ? ib : jb; + for (int p = 0; p < kb; p += mb) { + const int pb = (p + mb) > kb ? (kb - p) : mb; + + // diagonal block + ValueType *KOKKOS_RESTRICT Ap = AA + p * as0 + p * as1; + + // lu on a block + lu.serial_invoke(pb, Ap); + + // dimension ABR + const int m_abr = ib - p - mb, n_abr = jb - p - mb; + + // trsm update + trsm_llu.serial_invoke(Ap, pb, n_abr, Ap + mb * as1); + trsm_run.serial_invoke(Ap, pb, m_abr, Ap + mb * as0); + + // gemm update + SerialGemmInternal::invoke( + m_abr, n_abr, pb, minus_one, Ap + mb * as0, as0, as1, Ap + mb * as1, + as0, as1, one, Ap + mb * as0 + mb * as1, as0, as1); + } + }; + + const bool is_small = true; //(m*n <= 64*64); + if (is_small) { + lu_factorize(m, n, A); + } else { + // // some cache blocking may need (not priority yet); + // lu_factorize(m, n, A); + } + + return 0; +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_QR_Decl.hpp b/external/kokkos-kernels/KokkosBatched_QR_Decl.hpp new file mode 100644 index 00000000..5e7778f1 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_QR_Decl.hpp @@ -0,0 +1,80 @@ +#ifndef __KOKKOSBATCHED_QR_DECL_HPP__ +#define __KOKKOSBATCHED_QR_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +namespace KokkosBatched { + +/// +/// Serial QR +/// + +template +struct SerialQR { + template + KOKKOS_INLINE_FUNCTION static int invoke(const AViewType &A, + const tViewType &t, + const pViewType &p, + const wViewType &w); +}; + +/// +/// Team QR +/// + +template +struct TeamQR { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType & /*member*/, + const AViewType & /*A*/, + const tViewType & /*t*/, + const wViewType & /*w*/) { + /// not implemented + return -1; + } +}; + +/// +/// TeamVector QR +/// + +template +struct TeamVectorQR { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const AViewType &A, + const tViewType &t, + const wViewType &w); +}; + +/// +/// Selective Interface +/// +template +struct QR { + template + KOKKOS_FORCEINLINE_FUNCTION static int invoke(const MemberType &member, + const AViewType &A, + const tViewType &t, + const pViewType &p, + const wViewType &w) { + int r_val = 0; + if (std::is_same::value) { + r_val = SerialQR::invoke(A, t, p, w); + } else if (std::is_same::value) { + r_val = TeamQR::invoke(member, A, t, w); + } else if (std::is_same::value) { + r_val = TeamVectorQR::invoke(member, A, t, w); + } + return r_val; + } +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_QR_Serial_Impl.hpp" +//#include "KokkosBatched_QR_TeamVector_Impl.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_QR_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_QR_Serial_Impl.hpp new file mode 100644 index 00000000..30ee2427 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_QR_Serial_Impl.hpp @@ -0,0 +1,27 @@ +#ifndef __KOKKOSBATCHED_QR_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_QR_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_QR_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Impl +/// =========== + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialQR::invoke( + const AViewType &A, const tViewType &t, const pViewType &p, const wViewType &w) { + return SerialQR_Internal::invoke(A.extent(0), A.extent(1), A.data(), + A.stride_0(), A.stride_1(), t.data(), + t.stride_0(), p.data(), p.stride_0(), + w.data()); +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_QR_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_QR_Serial_Internal.hpp new file mode 100644 index 00000000..221405da --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_QR_Serial_Internal.hpp @@ -0,0 +1,151 @@ +#ifndef __KOKKOSBATCHED_QR_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_QR_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Householder_Serial_Internal.hpp" +#include "KokkosBatched_ApplyHouseholder_Serial_Internal.hpp" +#include "KokkosBatched_ApplyPivot_Internal.hpp" +#include "KokkosBatched_FindAmax_Internal.hpp" +#include "KokkosBatched_Dot.hpp" +#include "KokkosBatched_Dot_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== +/// +/// this impl follows the flame interface of householder transformation +/// +struct SerialUpdateColumnNormsInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int n, const ValueType *KOKKOS_RESTRICT a, + const int as0, + /* */ ValueType *KOKKOS_RESTRICT norm, + const int ns0) { + using ats = Kokkos::ArithTraits; + for (int j=0; j < n; ++j) { + const int idx_a = j * as0, idx_n = j * ns0; + norm[idx_n] -= ats::conj(a[idx_a]) * a[idx_a]; + } + return 0; + } +}; + +struct SerialQR_Internal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, // m = NumRows(A) + const int n, // n = NumCols(A) + /* */ ValueType *A, const int as0, + const int as1, + /* */ ValueType *t, const int ts0, + /* */ IntType *p, const int ps0, + /* */ ValueType *w) { + using value_type = ValueType; + using int_type = IntType; + using ats = Kokkos::ArithTraits; + + /// Given a matrix A, it computes QR decomposition of the matrix + /// - t is to store tau and w is for workspace + + // partitions used for loop iteration + Partition2x2 A_part2x2(as0, as1); + Partition3x3 A_part3x3(as0, as1); + + Partition2x1 t_part2x1(ts0); + Partition3x1 t_part3x1(ts0); + + // row vector for norm and p (size of n) + Partition1x2 p_part1x2(ps0); + Partition1x3 p_part1x3(ps0); + + Partition1x2 norm_part1x2(1); + Partition1x3 norm_part1x3(1); + + // loop size + const int min_mn = m < n ? m : n; + + // workspace (norm and householder application, 2*max(m,n) is needed) + value_type *norm = w; + w += n; + + // initial partition of A where ATL has a zero dimension + A_part2x2.partWithATL(A, m, n, 0, 0); + t_part2x1.partWithAT(t, min_mn, 0); + + p_part1x2.partWithAL(p, n, 0); + norm_part1x2.partWithAL(norm, n, 0); + + // compute initial column norms (replaced by dot product) + SerialDotInternal::invoke(m, n, A, as0, as1, A, as0, as1, norm, + 1); + + int matrix_rank = min_mn; + value_type max_diag(0); + for (int m_atl = 0; m_atl < min_mn; ++m_atl) { + const int n_AR = n - m_atl; + + // part 2x2 into 3x3 + A_part3x3.partWithABR(A_part2x2, 1, 1); + const int m_A22 = m - m_atl - 1; + const int n_A22 = n - m_atl - 1; + + t_part3x1.partWithAB(t_part2x1, 1); + value_type *tau = t_part3x1.A1; + + p_part1x3.partWithAR(p_part1x2, 1); + int_type *pividx = p_part1x3.A1; + + norm_part1x3.partWithAR(norm_part1x2, 1); + + /// ----------------------------------------------------- + // find max location + SerialFindAmaxInternal::invoke(n_AR, norm_part1x2.AR, 1, + pividx); + + // apply pivot + SerialApplyPivotVectorForwardInternal::invoke(*pividx, norm_part1x2.AR, 1); + SerialApplyPivotMatrixForwardInternal::invoke( + m, *pividx, A_part2x2.ATR, as1, as0); + + + // perform householder transformation + SerialLeftHouseholderInternal::invoke(m_A22, A_part3x3.A11, A_part3x3.A21, + as0, tau); + + // left apply householder to A22 + SerialApplyLeftHouseholderInternal::invoke( + m_A22, n_A22, tau, A_part3x3.A21, as0, A_part3x3.A12, as1, + A_part3x3.A22, as0, as1, w); + + // break condition + if (matrix_rank == min_mn) { + if (m_atl == 0) max_diag = ats::abs(A[0]); + const value_type val_diag = ats::abs(A_part3x3.A11[0]), + threshold(10 * max_diag * ats::epsilon()); + if (val_diag < threshold) { + matrix_rank = m_atl; + //if (finish_when_rank_found) break; + } + } + + // norm update + SerialUpdateColumnNormsInternal::invoke(n_A22, A_part3x3.A12, + as1, norm_part1x3.A2, 1); + + /// ----------------------------------------------------- + A_part2x2.mergeToATL(A_part3x3); + t_part2x1.mergeToAT(t_part3x1); + p_part1x2.mergeToAL(p_part1x3); + norm_part1x2.mergeToAL(norm_part1x3); + } + + return 0; + } +}; + +} // end namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Trsv_Decl.hpp b/external/kokkos-kernels/KokkosBatched_Trsv_Decl.hpp new file mode 100644 index 00000000..0e719058 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Trsv_Decl.hpp @@ -0,0 +1,205 @@ +#ifndef __KOKKOSBATCHED_TRSV_DECL_HPP__ +#define __KOKKOSBATCHED_TRSV_DECL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Vector.hpp" + +namespace KokkosBatched { + +/// +/// Serial Trsv +/// + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType /*alpha*/, + const AViewType & /*A*/, + const bViewType & /*b*/) { + assert(false && "Error: encounter dummy impl"); + return 0; + } +}; + +/// +/// Team Trsv +/// + +template +struct TeamTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType & /*member*/, + const ScalarType /*alpha*/, + const AViewType & /*A*/, + const bViewType & /*b*/) { + assert(false && "Error: encounter dummy impl"); + return 0; + } +}; + +/// +/// TeamVector Trsv +/// + +template +struct TeamVectorTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType & /*member*/, + const ScalarType /*alpha*/, + const AViewType & /*A*/, + const bViewType & /*b*/) { + assert(false && "Error: encounter dummy impl"); + return 0; + } +}; + +/// +/// Selective Interface +/// +template +struct Trsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + int r_val = 0; + if (std::is_same::value) { + r_val = + SerialTrsv::invoke(alpha, A, b); + } else if (std::is_same::value) { + r_val = TeamTrsv::invoke( + member, alpha, A, b); + } else if (std::is_same::value) { + r_val = TeamVectorTrsv::invoke(member, alpha, A, b); + } + return r_val; + } +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_Trsv_Serial_Impl.hpp" +//#include "KokkosBatched_Trsv_Team_Impl.hpp" +//#include "KokkosBatched_Trsv_TeamVector_Impl.hpp" + +#define KOKKOSBATCHED_SERIAL_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::SerialTrsvInternalLower::invoke( \ + DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_SERIAL_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::SerialTrsvInternalUpper::invoke( \ + DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_SERIAL_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::SerialTrsvInternalUpper::invoke( \ + DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_SERIAL_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::SerialTrsvInternalLower::invoke( \ + DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_TEAM_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamTrsvInternalLower::invoke( \ + MEMBER, DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_TEAM_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamTrsvInternalUpper::invoke( \ + MEMBER, DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_TEAM_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamTrsvInternalUpper::invoke( \ + MEMBER, DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_TEAM_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamTrsvInternalLower::invoke( \ + MEMBER, DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_TEAMVECTOR_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamVectorTrsvInternalLower::invoke( \ + MEMBER, DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_TEAMVECTOR_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamVectorTrsvInternalUpper::invoke( \ + MEMBER, DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_TEAMVECTOR_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamVectorTrsvInternalUpper::invoke( \ + MEMBER, DIAG::use_unit_diag, M, ALPHA, A, AS0, AS1, B, BS) + +#define KOKKOSBATCHED_TEAMVECTOR_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + KokkosBatched::TeamVectorTrsvInternalLower::invoke( \ + MEMBER, DIAG::use_unit_diag, N, ALPHA, A, AS1, AS0, B, BS) + +#define KOKKOSBATCHED_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + MODETYPE, ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + if (std::is_same::value) { \ + KOKKOSBATCHED_SERIAL_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAM_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAMVECTOR_TRSV_LOWER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } + +#define KOKKOSBATCHED_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + MODETYPE, ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + if (std::is_same::value) { \ + KOKKOSBATCHED_SERIAL_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAM_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAMVECTOR_TRSV_LOWER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } + +#define KOKKOSBATCHED_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + MODETYPE, ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + if (std::is_same::value) { \ + KOKKOSBATCHED_SERIAL_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAM_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAMVECTOR_TRSV_UPPER_NO_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } + +#define KOKKOSBATCHED_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + MODETYPE, ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS) \ + if (std::is_same::value) { \ + KOKKOSBATCHED_SERIAL_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAM_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } else if (std::is_same::value) { \ + KOKKOSBATCHED_TEAMVECTOR_TRSV_UPPER_TRANSPOSE_INTERNAL_INVOKE( \ + ALGOTYPE, MEMBER, DIAG, M, N, ALPHA, A, AS0, AS1, B, BS); \ + } + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Impl.hpp b/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Impl.hpp new file mode 100644 index 00000000..6e26b0fe --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Impl.hpp @@ -0,0 +1,322 @@ +#ifndef __KOKKOSBATCHED_TRSV_SERIAL_IMPL_HPP__ +#define __KOKKOSBATCHED_TRSV_SERIAL_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" +#include "KokkosBatched_Trsv_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Impl +/// =========== + +/// +/// Implemented: +/// L/NT, U/NT, L/T, U/T +/// +/// Not yet implemented +/// L/CT, U/CT + +/// +/// L/NT +/// + +#if defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__) +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + typedef typename bViewType::value_type vector_type; + // typedef typename vector_type::value_type value_type; + + const int m = b.extent(0), n = 1; + + static_assert(is_vector::value, + "value type is not vector type"); + static_assert( + vector_type::vector_length == 4 || vector_type::vector_length == 8, + "AVX, AVX2 and AVX512 is supported"); + const MKL_COMPACT_PACK format = + vector_type::vector_length == 8 ? MKL_COMPACT_AVX512 : MKL_COMPACT_AVX; + + // no error check + int r_val = 0; + if (A.stride_0() == 1) { + mkl_dtrsm_compact(MKL_COL_MAJOR, MKL_LEFT, MKL_LOWER, MKL_NOTRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else if (A.stride_1() == 1) { + mkl_dtrsm_compact(MKL_ROW_MAJOR, MKL_LEFT, MKL_LOWER, MKL_NOTRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else { + r_val = -1; + } + return r_val; + } +}; +#endif + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalLower::invoke( + ArgDiag::use_unit_diag, A.extent(0), alpha, A.data(), A.stride_0(), + A.stride_1(), b.data(), b.stride_0()); + } +}; + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalLower::invoke( + ArgDiag::use_unit_diag, A.extent(0), alpha, A.data(), A.stride_0(), + A.stride_1(), b.data(), b.stride_0()); + } +}; + +/// +/// L/T +/// + +#if defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__) +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + typedef typename bViewType::value_type vector_type; + // typedef typename vector_type::value_type value_type; + + const int m = b.extent(0), n = 1; + + static_assert(is_vector::value, + "value type is not vector type"); + static_assert( + vector_type::vector_length == 4 || vector_type::vector_length == 8, + "AVX, AVX2 and AVX512 is supported"); + const MKL_COMPACT_PACK format = + vector_type::vector_length == 8 ? MKL_COMPACT_AVX512 : MKL_COMPACT_AVX; + + // no error check + int r_val = 0; + if (A.stride_0() == 1) { + mkl_dtrsm_compact(MKL_COL_MAJOR, MKL_LEFT, MKL_LOWER, MKL_TRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else if (A.stride_1() == 1) { + mkl_dtrsm_compact(MKL_ROW_MAJOR, MKL_LEFT, MKL_LOWER, MKL_TRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else { + r_val = -1; + } + return r_val; + } +}; +#endif + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalUpper::invoke( + ArgDiag::use_unit_diag, A.extent(1), alpha, A.data(), A.stride_1(), + A.stride_0(), b.data(), b.stride_0()); + } +}; + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalUpper::invoke( + ArgDiag::use_unit_diag, A.extent(1), alpha, A.data(), A.stride_1(), + A.stride_0(), b.data(), b.stride_0()); + } +}; + +/// +/// U/NT +/// + +#if defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__) +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + typedef typename bViewType::value_type vector_type; + // typedef typename vector_type::value_type value_type; + + const int m = b.extent(0), n = 1; + + static_assert(is_vector::value, + "value type is not vector type"); + static_assert( + vector_type::vector_length == 4 || vector_type::vector_length == 8, + "AVX, AVX2 and AVX512 is supported"); + const MKL_COMPACT_PACK format = + vector_type::vector_length == 8 ? MKL_COMPACT_AVX512 : MKL_COMPACT_AVX; + + // no error check + int r_val = 0; + if (A.stride_0() == 1) { + mkl_dtrsm_compact(MKL_COL_MAJOR, MKL_LEFT, MKL_UPPER, MKL_NOTRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else if (A.stride_1() == 1) { + mkl_dtrsm_compact(MKL_ROW_MAJOR, MKL_LEFT, MKL_UPPER, MKL_NOTRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else { + r_val = -1; + } + return r_val; + } +}; +#endif + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalUpper::invoke( + ArgDiag::use_unit_diag, A.extent(0), alpha, A.data(), A.stride_0(), + A.stride_1(), b.data(), b.stride_0()); + } +}; + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalUpper::invoke( + ArgDiag::use_unit_diag, A.extent(0), alpha, A.data(), A.stride_0(), + A.stride_1(), b.data(), b.stride_0()); + } +}; + +/// +/// U/T +/// + +#if defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__) && \ + defined(__KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__) +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + typedef typename bViewType::value_type vector_type; + // typedef typename vector_type::value_type value_type; + + const int m = b.extent(0), n = 1; + + static_assert(is_vector::value, + "value type is not vector type"); + static_assert( + vector_type::vector_length == 4 || vector_type::vector_length == 8, + "AVX, AVX2 and AVX512 is supported"); + const MKL_COMPACT_PACK format = + vector_type::vector_length == 8 ? MKL_COMPACT_AVX512 : MKL_COMPACT_AVX; + + // no error check + int r_val = 0; + if (A.stride_0() == 1) { + mkl_dtrsm_compact(MKL_COL_MAJOR, MKL_LEFT, MKL_UPPER, MKL_TRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else if (A.stride_1() == 1) { + mkl_dtrsm_compact(MKL_ROW_MAJOR, MKL_LEFT, MKL_UPPER, MKL_TRANS, + ArgDiag::use_unit_diag ? MKL_UNIT : MKL_NONUNIT, m, n, + alpha, (const double *)A.data(), A.stride_0(), + (double *)b.data(), b.stride_0(), format, + (MKL_INT)vector_type::vector_length); + } else { + r_val = -1; + } + return r_val; + } +}; +#endif + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalLower::invoke( + ArgDiag::use_unit_diag, A.extent(1), alpha, A.data(), A.stride_1(), + A.stride_0(), b.data(), b.stride_0()); + } +}; + +template +struct SerialTrsv { + template + KOKKOS_INLINE_FUNCTION static int invoke(const ScalarType alpha, + const AViewType &A, + const bViewType &b) { + return SerialTrsvInternalLower::invoke( + ArgDiag::use_unit_diag, A.extent(1), alpha, A.data(), A.stride_1(), + A.stride_0(), b.data(), b.stride_0()); + } +}; + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Internal.hpp b/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Internal.hpp new file mode 100644 index 00000000..92600308 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Trsv_Serial_Internal.hpp @@ -0,0 +1,208 @@ +#ifndef __KOKKOSBATCHED_TRSV_SERIAL_INTERNAL_HPP__ +#define __KOKKOSBATCHED_TRSV_SERIAL_INTERNAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +#include "KokkosBlas1_set_impl.hpp" +#include "KokkosBlas1_serial_scal_impl.hpp" +#include "KokkosBatched_InnerTrsm_Serial_Impl.hpp" +#include "KokkosBatched_Gemv_Serial_Internal.hpp" + +namespace KokkosBatched { + +/// +/// Serial Internal Impl +/// ==================== + +/// +/// Lower +/// + +template +struct SerialTrsvInternalLower { + template + KOKKOS_INLINE_FUNCTION static int invoke(const bool use_unit_diag, + const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, + const int bs0); +}; + +template <> +template +KOKKOS_INLINE_FUNCTION int +SerialTrsvInternalLower::invoke( + const bool use_unit_diag, const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, const int bs0) { + const ScalarType one(1.0), zero(0.0); + + if (alpha == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, b, bs0); + else { + if (alpha != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, alpha, b, bs0); + if (m <= 0) return 0; + + for (int p = 0; p < m; ++p) { + const int iend = m - p - 1; + + const ValueType *KOKKOS_RESTRICT a21 = + iend ? A + (p + 1) * as0 + p * as1 : NULL; + + ValueType *KOKKOS_RESTRICT beta1 = b + p * bs0, + *KOKKOS_RESTRICT b2 = + iend ? beta1 + bs0 : NULL; + + // with KOKKOS_RESTRICT a compiler assumes that the pointer is not + // accessed by others op(/=) uses this pointer and changes the associated + // values, which brings a compiler problem + if (!use_unit_diag) *beta1 = *beta1 / A[p * as0 + p * as1]; + + for (int i = 0; i < iend; ++i) b2[i * bs0] -= a21[i * as0] * (*beta1); + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialTrsvInternalLower::invoke( + const bool use_unit_diag, const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, const int bs0) { + const ScalarType one(1.0), zero(0.0), minus_one(-1.0); + + constexpr int mbAlgo = Algo::Trsv::Blocked::mb(); + + if (alpha == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, b, bs0); + else { + if (alpha != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, alpha, b, bs0); + if (m <= 0) return 0; + + /// case GPU: team size is large and blocksize (mb,nb) is small + InnerTrsmLeftLowerUnitDiag trsm_u(as0, as1, bs0, 0); + InnerTrsmLeftLowerNonUnitDiag trsm_n(as0, as1, bs0, 0); + + const int mb = mbAlgo; + for (int p = 0; p < m; p += mb) { + const int pb = ((p + mb) > m ? (m - p) : mb); + + // trsm update + const ValueType *KOKKOS_RESTRICT Ap = A + p * as0 + p * as1; + /**/ ValueType *KOKKOS_RESTRICT bp = b + p * bs0; + + if (use_unit_diag) + trsm_u.serial_invoke(Ap, pb, 1, bp); + else + trsm_n.serial_invoke(Ap, pb, 1, bp); + + // gemv update + SerialGemvInternal::invoke( + m - p - pb, pb, minus_one, Ap + pb * as0, as0, as1, bp, bs0, one, + bp + pb * bs0, bs0); + } + } + return 0; +} + +/// +/// Upper +/// + +template +struct SerialTrsvInternalUpper { + template + KOKKOS_INLINE_FUNCTION static int invoke(const bool use_unit_diag, + const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, + const int bs0); +}; + +template <> +template +KOKKOS_INLINE_FUNCTION int +SerialTrsvInternalUpper::invoke( + const bool use_unit_diag, const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, const int bs0) { + const ScalarType one(1.0), zero(0.0); + + if (alpha == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, b, bs0); + else { + if (alpha != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, alpha, b, bs0); + if (m <= 0) return 0; + + ValueType *KOKKOS_RESTRICT b0 = b; + for (int p = (m - 1); p >= 0; --p) { + const int iend = p; + + const ValueType *KOKKOS_RESTRICT a01 = A + p * as1; + /**/ ValueType *KOKKOS_RESTRICT beta1 = b + p * bs0; + + // with KOKKOS_RESTRICT a compiler assumes that the pointer is not + // accessed by others op(/=) uses this pointer and changes the associated + // values, which brings a compiler problem + if (!use_unit_diag) *beta1 = *beta1 / A[p * as0 + p * as1]; + + for (int i = 0; i < iend; ++i) b0[i * bs0] -= a01[i * as0] * (*beta1); + } + } + return 0; +} + +template <> +template +KOKKOS_INLINE_FUNCTION int SerialTrsvInternalUpper::invoke( + const bool use_unit_diag, const int m, const ScalarType alpha, + const ValueType *KOKKOS_RESTRICT A, const int as0, const int as1, + /**/ ValueType *KOKKOS_RESTRICT b, const int bs0) { + const ScalarType one(1.0), zero(0.0), minus_one(-1.0); + + constexpr int mbAlgo = Algo::Trsm::Blocked::mb(); + + // note that parallel range is different ( m*n vs m-1*n); + if (alpha == zero) + KokkosBlas::Impl::SerialSetInternal::invoke(m, zero, b, bs0); + else { + if (alpha != one) + KokkosBlas::Impl::SerialScaleInternal::invoke(m, alpha, b, bs0); + if (m <= 0) return 0; + + InnerTrsmLeftUpperUnitDiag trsm_u(as0, as1, bs0, 0); + InnerTrsmLeftUpperNonUnitDiag trsm_n(as0, as1, bs0, 0); + + const int mb = mbAlgo; + for (int pp = 0; pp < m; pp += mb) { + const int ptmp = (m - pp - mb), p = (ptmp < 0 ? 0 : ptmp), + pb = (mb + (ptmp < 0) * ptmp); + + // trsm update + const ValueType *KOKKOS_RESTRICT Ap = A + p * as0 + p * as1; + /**/ ValueType *KOKKOS_RESTRICT bp = b + p * bs0; + + if (use_unit_diag) + trsm_u.serial_invoke(Ap, pb, 1, bp); + else + trsm_n.serial_invoke(Ap, pb, 1, bp); + + // gemv update + SerialGemvInternal::invoke( + p, pb, minus_one, Ap - p * as0, as0, as1, bp, bs0, one, b, bs0); + } + } + return 0; +} + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Util.hpp b/external/kokkos-kernels/KokkosBatched_Util.hpp new file mode 100644 index 00000000..46b97ee0 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Util.hpp @@ -0,0 +1,903 @@ +#ifndef __KOKKOSBATCHED_UTIL_HPP__ +#define __KOKKOSBATCHED_UTIL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +// no experimental name space guard for trilinos +#define __KOKKOSBATCHED_PROMOTION__ 1 + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "Kokkos_Complex.hpp" + +#include "KokkosKernels_config.h" +#include "KokkosKernels_SimpleUtils.hpp" + +// TPL macros +#if defined(KOKKOSKERNELS_ENABLE_TPL_MKL) +#define __KOKKOSBATCHED_ENABLE_INTEL_MKL__ 1 +#include "mkl_version.h" +#if __INTEL_MKL__ >= 2018 +#define __KOKKOSBATCHED_ENABLE_INTEL_MKL_BATCHED__ 1 +#define __KOKKOSBATCHED_ENABLE_INTEL_MKL_COMPACT_BATCHED__ 1 +#include "mkl.h" +//#include "mkl_types.h" +#endif +#endif + +#if defined(KOKKOSKERNELS_ENABLE_TPL_LAPACKE) +#define __KOKKOSBATCHED_ENABLE_LAPACKE__ 1 +#include "lapacke.h" +#endif + +namespace KokkosBatched { + +//////// Helper macros, functions, and classes //////// +#define Int2StringHelper(A) #A +#define Int2String(A) Int2StringHelper(A) +#define StringCat(A, B) A B + +void print_compiler_info(); + +template +struct is_vector : public std::false_type {}; + +template +struct is_same_mag_type { + static const bool is_specialized = + (Kokkos::Details::ArithTraits::is_specialized && + Kokkos::Details::ArithTraits::is_specialized); + + static const bool is_mag_type_same = + std::is_same::mag_type, + typename Kokkos::Details::ArithTraits::mag_type>::value; + + static const bool value = is_specialized && is_mag_type_same; +}; + +// to use double, std::complex, Kokkos::complex +using std::max; +using std::min; + +// view manipulation +template +using MemoryTraits = Kokkos::MemoryTraits; + +template +using UnmanagedViewType = Kokkos::View< + typename ViewType::data_type, typename ViewType::array_layout, + typename ViewType::device_type, + MemoryTraits >; + +template +using ConstViewType = Kokkos::View< + typename ViewType::const_data_type, typename ViewType::array_layout, + typename ViewType::device_type, typename ViewType::memory_traits>; +template +using ConstUnmanagedViewType = ConstViewType >; + +template +using ScratchViewType = Kokkos::View< + typename ViewType::data_type, typename ViewType::array_layout, + typename ViewType::execution_space::scratch_memory_space, + MemoryTraits >; + +// helper for vector type +template +KOKKOS_INLINE_FUNCTION + typename std::enable_if::value, size_t>::type + adjustDimension(const size_t &m) { + return m; +} + +template +KOKKOS_INLINE_FUNCTION + typename std::enable_if::value, size_t>::type + adjustDimension(const size_t &m) { + return (m / T::vector_length + (m % T::vector_length > 0)); +} + +template +struct Flush { + typedef double value_type; + + // flush a large host buffer + Kokkos::View _buf; + Flush() : _buf("Flush::buf", BufSize / sizeof(double)) { + Kokkos::deep_copy(_buf, 1); + } + + KOKKOS_INLINE_FUNCTION + void init(value_type &update) { update = 0; } + + KOKKOS_INLINE_FUNCTION + void join(value_type &update, const value_type &input) { update += input; } + + KOKKOS_INLINE_FUNCTION + void operator()(const int i, value_type &update) const { update += _buf[i]; } + + void run() { + double sum = 0; + Kokkos::parallel_reduce( + Kokkos::RangePolicy(0, BufSize / sizeof(double)), *this, + sum); + SpaceType().fence(); + FILE *fp = fopen("/dev/null", "w"); + fprintf(fp, "%f\n", sum); + fclose(fp); + } +}; + +template +struct Random; + +template +struct Random::value || + std::is_same::value, + T>::type> { + Random(const unsigned int seed = 0) { srand(seed); } + T value() { + const auto val = (rand() / ((T)RAND_MAX) - 0.5) * 2.0; + return val > 0 ? val + 1.0e-3 : val - 1.0e-3; + } +}; + +template +struct Random >::value || + std::is_same >::value || + std::is_same >::value || + std::is_same >::value, + T>::type> { + Random(const unsigned int seed = 0) { srand(seed); } + T value() { + const auto rval = (rand() / ((double)RAND_MAX) - 0.5) * 2.0; + const auto ival = (rand() / ((double)RAND_MAX) - 0.5) * 2.0; + return T(rval > 0 ? rval + 1.0e-3 : rval - 1.0e-3, + ival > 0 ? ival + 1.0e-3 : ival - 1.0e-3); + } +}; + +struct Timer { + std::string _label; + Kokkos::Timer _clock; + Timer(const std::string label) : _label(label), _clock(){}; + + void reset() { _clock.reset(); } + double seconds() { return _clock.seconds(); } + ~Timer() { + Kokkos::fence(); + const double t = _clock.seconds(); + std::string label = _label; + label.resize(24); + std::cout << "KokkosKernels::Timer:: " << std::setw(26) << label + << std::setw(15) << std::scientific << t << " [sec] " + << std::endl; + } +}; + +// Implicit vectorization +template +struct SIMD { + static_assert(std::is_same::value || std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same >::value || + std::is_same >::value || + std::is_same >::value || + std::is_same >::value || + std::is_same::value || + std::is_same::value, + "KokkosKernels:: Invalid SIMD<> type."); + using value_type = T; +}; + +// Intel AVX instruction device (explicit vectorization) +template +struct AVX { + static_assert(std::is_same::value || + std::is_same::value || + std::is_same >::value || + std::is_same >::value, + "KokkosKernels:: Invalid AVX<> type."); + using value_type = T; +}; + +//////// Tags for BLAS //////// +struct Trans { + struct Transpose {}; + struct NoTranspose {}; + struct ConjTranspose {}; +}; + +struct Side { + struct Left {}; + struct Right {}; +}; + +struct Uplo { + struct Upper {}; + struct Lower {}; +}; + +struct Diag { + struct Unit { + static const bool use_unit_diag = true; + }; + struct NonUnit { + static const bool use_unit_diag = false; + }; +}; + +/// BatchLayout class used to specify where the batch dimension is +/// allocated in the input views for host-level Batched BLAS/LAPACK routines. +struct BatchLayout { + /// Batch dimension is the leftmost dimension within input views + struct Left {}; + /// Batch dimension is the rightmost dimension within input views + struct Right {}; +}; + +/// ResultsPerThread class used to specify how to divide a given BLAS/LAPACK +/// operation among Kokkos threads +struct ResultsPerThread { + /// Each Kokkos thread calculates a 0-rank result + struct Rank0 {}; + /// Each Kokkos thread calculates a 1-rank result + struct Rank1 {}; + /// Each Kokkos thread calculates a 2-rank result + struct Rank2 {}; +}; + +/// BoundsCheck class used to specify whether to check view bounds in +/// BLAS/LAPACK DblBuf algorithms. +struct BoundsCheck { + /// Use functor with bounds check + struct Yes {}; + /// Use functor without bounds check + struct No {}; +}; + +/// AlphaTag class used to specify where to apply alpha in BLAS/LAPACK DblBuf +/// algorithms. +struct AlphaTag { + /// Use function with alpha factor + struct Yes {}; + /// Use function without alpha factor + struct No {}; +}; + +struct Direct { + struct Forward {}; + struct Backward {}; +}; + +struct Mode { + struct Serial { + static const char *name() { return "Serial"; } + }; + struct Team { + static const char *name() { return "Team"; } + }; + struct TeamVector { + static const char *name() { return "TeamVector"; } + }; +}; + +#if !defined(KOKKOS_IF_ON_HOST) + +template +struct algo_level3_blocked_mb_impl; +template <> +struct algo_level3_blocked_mb_impl { + static constexpr int value = 4; +}; +#if defined(KOKKOS_ENABLE_CUDA) +template <> +struct algo_level3_blocked_mb_impl { + static constexpr int value = 2; +}; +#endif +#if defined(KOKKOS_ENABLE_HIP) +template <> +struct algo_level3_blocked_mb_impl { + static constexpr int value = 2; +}; +#endif +#if defined(KOKKOS_ENABLE_SYCL) +template <> +struct algo_level3_blocked_mb_impl { + static constexpr int value = 2; +}; +#endif + +template +struct algo_level2_blocked_mb_impl; +template <> +struct algo_level2_blocked_mb_impl { + static constexpr int value = 4; +}; +#if defined(KOKKOS_ENABLE_CUDA) +template <> +struct algo_level2_blocked_mb_impl { + static constexpr int value = 1; +}; +#endif +#if defined(KOKKOS_ENABLE_HIP) +template <> +struct algo_level2_blocked_mb_impl { + static constexpr int value = 1; +}; +#endif +#if defined(KOKKOS_ENABLE_SYCL) +template <> +struct algo_level2_blocked_mb_impl { + static constexpr int value = 1; +}; +#endif + +#endif + +struct Algo { + struct Level3 { + struct Unblocked { + static const char *name() { return "Unblocked"; } + }; + struct Blocked { + static const char *name() { return "Blocked"; } + // TODO:: for now harwire the blocksizes; this should reflect + // regieter blocking (not about team parallelism). + // this mb should vary according to + // - team policy (smaller) or range policy (bigger) + // - space (gpu vs host) + // - blocksize input (blk <= 4 mb = 2, otherwise mb = 4), etc. +#if defined(KOKKOS_IF_ON_HOST) + static constexpr KOKKOS_FUNCTION int mb() { + KOKKOS_IF_ON_HOST((return 4;)) + KOKKOS_IF_ON_DEVICE((return 2;)) + } + +#else // FIXME remove when requiring minimum version of Kokkos 3.6 + static constexpr KOKKOS_FUNCTION int mb() { + return algo_level3_blocked_mb_impl< + Kokkos::Impl::ActiveExecutionMemorySpace>::value; + } + +#endif + }; + struct MKL { + static const char *name() { return "MKL"; } + }; + struct CompactMKL { + static const char *name() { return "CompactMKL"; } + }; + + // When this is first developed, unblocked algorithm is a naive + // implementation and blocked algorithm uses register blocking variant of + // algorithm (manual unrolling). This distinction is almost meaningless and + // it just adds more complications. Eventually, the blocked version will be + // removed and we only use the default algorithm. For testing and + // development purpose, we still leave algorithm tag in the template + // arguments. + using Default = Unblocked; + }; + + using Gemm = Level3; + using Trsm = Level3; + using Trmm = Level3; + using Trtri = Level3; + using LU = Level3; + using InverseLU = Level3; + using SolveLU = Level3; + using QR = Level3; + using UTV = Level3; + + struct Level2 { + struct Unblocked {}; + struct Blocked { + // TODO:: for now harwire the blocksizes; this should reflect + // regieter blocking (not about team parallelism). + // this mb should vary according to + // - team policy (smaller) or range policy (bigger) + // - space (cuda vs host) + // - blocksize input (blk <= 4 mb = 2, otherwise mb = 4), etc. +#if defined(KOKKOS_IF_ON_HOST) + static constexpr KOKKOS_FUNCTION int mb() { + KOKKOS_IF_ON_HOST((return 4;)) + KOKKOS_IF_ON_DEVICE((return 1;)) + } + +#else // FIXME remove when requiring minimum version of Kokkos 3.6 + static constexpr KOKKOS_FUNCTION int mb() { + return algo_level2_blocked_mb_impl< + Kokkos::Impl::ActiveExecutionMemorySpace>::value; + } + +#endif + }; + struct MKL {}; + struct CompactMKL {}; + + // When this is first developed, unblocked algorithm is a naive + // implementation and blocked algorithm uses register blocking variant of + // algorithm (manual unrolling). This distinction is almost meaningless and + // it just adds more complications. Eventually, the blocked version will be + // removed and we only use the default algorithm. For testing and + // development purpose, we still leave algorithm tag in the template + // arguments. + using Default = Unblocked; + }; + + using Gemv = Level2; + using Trsv = Level2; + using ApplyQ = Level2; +}; + +struct Util { + template + KOKKOS_INLINE_FUNCTION static void packColMajor( + ValueType *KOKKOS_RESTRICT A, const int m, const int n, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1) { + for (int j = 0; j < n; ++j) + for (int i = 0; i < m; ++i) A[i + j * m] = B[i * bs0 + j * bs1]; + } + + template + KOKKOS_INLINE_FUNCTION static void packRowMajor( + ValueType *KOKKOS_RESTRICT A, const int m, const int n, + const ValueType *KOKKOS_RESTRICT B, const int bs0, const int bs1) { + for (int i = 0; i < m; ++i) + for (int j = 0; j < n; ++j) A[i * n + j] = B[i * bs0 + j * bs1]; + } +}; + +template +struct Partition1x2; +template +struct Partition1x3; + +template +struct Partition1x2 { + const int as1; + ValueType *AL, *AR; + + KOKKOS_INLINE_FUNCTION + Partition1x2(const int arg_as1) : as1(arg_as1), AL(NULL), AR(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithAL(ValueType *A, const int /* nA */, const int nAL) { + AL = A; + AR = AL + nAL * as1; + } + + KOKKOS_INLINE_FUNCTION + void partWithAR(ValueType *A, const int nA, const int nAR) { + AL = A; + AR = AL + (nA - nAR) * as1; + } + + // A0 A1 are merged into AL + KOKKOS_INLINE_FUNCTION + void mergeToAL(const Partition1x3 &part) { + AL = part.A0; + AR = part.A2; + } + + // A0 A1 are merged into AL + KOKKOS_INLINE_FUNCTION + void mergeToAR(const Partition1x3 &part) { + AL = part.A0; + AR = part.A1; + } +}; + +template +struct Partition1x3 { + const int as1; + ValueType *A0, *A1, *A2; + + KOKKOS_INLINE_FUNCTION + Partition1x3(const int arg_as1) + : as1(arg_as1), A0(NULL), A1(NULL), A2(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithAL(const Partition1x2 &part, const int mA1) { + A0 = part.AL; + A2 = part.AR; + A1 = A2 - mA1 * as1; + } + KOKKOS_INLINE_FUNCTION + void partWithAR(const Partition1x2 &part, const int mA1) { + A0 = part.AL; + A1 = part.AR; + A2 = A1 + mA1 * as1; + } +}; + +template +struct Partition2x1; +template +struct Partition3x1; + +template +struct Partition2x1 { + const int as0; + ValueType *AT, *AB; + + KOKKOS_INLINE_FUNCTION + Partition2x1(const int arg_as0) : as0(arg_as0), AT(NULL), AB(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithAT(ValueType *A, const int /* mA */, const int mAT) { + AT = A; + AB = AT + mAT * as0; + } + + KOKKOS_INLINE_FUNCTION + void partWithAB(ValueType *A, const int mA, const int mAB) { + partWithAT(A, mA, mA - mAB); + } + + // A0 + // A1 is merged into AT + KOKKOS_INLINE_FUNCTION + void mergeToAT(const Partition3x1 &part) { + AT = part.A0; + AB = part.A2; + } + + KOKKOS_INLINE_FUNCTION + void mergeToAB(const Partition3x1 &part) { + AT = part.A0; + AB = part.A1; + } +}; + +template +struct Partition3x1 { + const int as0; + ValueType *A0, + /* */ *A1, + /* */ *A2; + + KOKKOS_INLINE_FUNCTION + Partition3x1(const int arg_as0) + : as0(arg_as0), A0(NULL), A1(NULL), A2(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithAB(const Partition2x1 &part, const int mA1) { + A0 = part.AT; + A1 = part.AB; + A2 = A1 + mA1 * as0; + } + + KOKKOS_INLINE_FUNCTION + void partWithAT(const Partition2x1 &part, const int mA1) { + A0 = part.AT; + A1 = part.AB - mA1 * as0; + A2 = part.AB; + } +}; + +template +struct Partition2x2; +template +struct Partition3x3; + +template +struct Partition2x2 { + const int as0, as1; + ValueType *ATL, *ATR, *ABL, *ABR; + + KOKKOS_INLINE_FUNCTION + Partition2x2(const int arg_as0, const int arg_as1) + : as0(arg_as0), + as1(arg_as1), + ATL(NULL), + ATR(NULL), + ABL(NULL), + ABR(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithATL(ValueType *A, const int /* mA */, const int /* nA */, + const int mATL, const int nATL) { + ATL = A; + ATR = ATL + nATL * as1; + ABL = ATL + mATL * as0; + ABR = ABL + nATL * as1; + } + + KOKKOS_INLINE_FUNCTION + void partWithABR(ValueType *A, const int mA, const int nA, const int mABR, + const int nABR) { + partWithATL(A, mA, nA, mA - mABR, nA - nABR); + } + + // A00 A01 + // A10 A11 is merged into ATL + KOKKOS_INLINE_FUNCTION + void mergeToATL(const Partition3x3 &part) { + ATL = part.A00; + ATR = part.A02; + ABL = part.A20; + ABR = part.A22; + } + + KOKKOS_INLINE_FUNCTION + void mergeToABR(const Partition3x3 &part) { + ATL = part.A00; + ATR = part.A01; + ABL = part.A10; + ABR = part.A11; + } +}; + +template +struct Partition3x3 { + const int as0, as1; + ValueType *A00, *A01, *A02, + /* */ *A10, *A11, *A12, + /* */ *A20, *A21, *A22; + + KOKKOS_INLINE_FUNCTION + Partition3x3(const int arg_as0, const int arg_as1) + : as0(arg_as0), + as1(arg_as1), + A00(NULL), + A01(NULL), + A02(NULL), + A10(NULL), + A11(NULL), + A12(NULL), + A20(NULL), + A21(NULL), + A22(NULL) {} + + KOKKOS_INLINE_FUNCTION + void partWithABR(const Partition2x2 &part, const int mA11, + const int nA11) { + A00 = part.ATL; + A01 = part.ATR; + A02 = part.ATR + nA11 * as1; + A10 = part.ABL; + A11 = part.ABR; + A12 = part.ABR + nA11 * as1; + A20 = part.ABL + mA11 * as0; + A21 = part.ABR + mA11 * as0; + A22 = part.ABR + mA11 * as0 + nA11 * as1; + } + + KOKKOS_INLINE_FUNCTION + void partWithATL(const Partition2x2 &part, const int mA11, + const int nA11) { + A00 = part.ATL; + A01 = part.ATR - nA11 * as1; + A02 = part.ATR; + A10 = part.ABL - mA11 * as0; + A11 = part.ABR - mA11 * as0 - nA11 * as1; + A12 = part.ABR - mA11 * as0; + A20 = part.ABL; + A21 = part.ABR - nA11 * as1; + A22 = part.ABR; + } +}; + +template +KOKKOS_INLINE_FUNCTION + typename std::enable_if::value, + void>::type + getIndices(const OrdinalType iTemp, const OrdinalType /*numRows*/, + const OrdinalType numMatrices, OrdinalType &iRow, + OrdinalType &iMatrix) { + iRow = iTemp / numMatrices; + iMatrix = iTemp % numMatrices; +} + +template +KOKKOS_INLINE_FUNCTION + typename std::enable_if::value, + void>::type + getIndices(const OrdinalType iTemp, const OrdinalType numRows, + const OrdinalType /*numMatrices*/, OrdinalType &iRow, + OrdinalType &iMatrix) { + iRow = iTemp % numRows; + iMatrix = iTemp / numRows; +} + +template +KOKKOS_INLINE_FUNCTION + typename std::enable_if::value, + void>::type + getIndices(const OrdinalType iTemp, const OrdinalType /*numRows*/, + const OrdinalType numMatrices, OrdinalType &iRow, + OrdinalType &iMatrix) { + iRow = iTemp / numMatrices; + iMatrix = iTemp % numMatrices; +} + +template +KOKKOS_INLINE_FUNCTION auto transpose_2d_view(ViewType v, const int *order) { + constexpr int rank = 2; + const int dim[] = {v.extent_int(1), v.extent_int(0)}; + using view_value_type = typename ViewType::value_type; + using execution_space_type = typename ViewType::execution_space; + using view_type = Kokkos::View; + Kokkos::LayoutStride stride = + Kokkos::LayoutStride::order_dimensions(rank, order, dim); + + return view_type(v.data(), stride); +} + +template +KOKKOS_INLINE_FUNCTION auto transpose_2d_view(ViewType v, + const BatchLayout::Left &) { + const int order[] = {0, 1}; // v is LayoutRight + return transpose_2d_view(v, order); +} + +template +KOKKOS_INLINE_FUNCTION auto transpose_2d_view(ViewType v, + const BatchLayout::Right &) { + const int order[] = {1, 0}; // v is LayoutLeft + return transpose_2d_view(v, order); +} + +///// subview_wrapper overloads for handling 3-rank BatchLayout::Left views +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, + IdxType2 i2, IdxType3 i3, + const BatchLayout::Left &) { + return Kokkos::subview(v, i1, i2, i3); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, + IdxType2 i2, IdxType3 i3, + const BatchLayout::Left &layout_tag, + const Trans::NoTranspose) { + return subview_wrapper(v, i1, i2, i3, layout_tag); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, + Kokkos::Impl::ALL_t i2, + Kokkos::Impl::ALL_t i3, + const BatchLayout::Left &layout_tag, + const Trans::Transpose) { + auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); + + return transpose_2d_view(sv_nt, layout_tag); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, + IdxType2 i2, IdxType3 i3, + const BatchLayout::Left &layout_tag, + const Trans::Transpose) { + auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); + + return sv_nt; +} + +//// subview_wrapper overloads for handling 3-rank BatchLayout::Right views +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, + IdxType2 i2, IdxType3 i3, + const BatchLayout::Right &) { + return Kokkos::subview(v, i2, i3, i1); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper( + ViewType v, IdxType1 i1, IdxType2 i2, IdxType3 i3, + const BatchLayout::Right &layout_tag, const Trans::NoTranspose &) { + return subview_wrapper(v, i1, i2, i3, layout_tag); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper( + ViewType v, IdxType1 i1, Kokkos::Impl::ALL_t i2, Kokkos::Impl::ALL_t i3, + const BatchLayout::Right &layout_tag, const Trans::Transpose &) { + auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); + + return transpose_2d_view(sv_nt, layout_tag); +} +template +KOKKOS_INLINE_FUNCTION auto subview_wrapper( + ViewType v, IdxType1 i1, IdxType2 i2, IdxType3 i3, + const BatchLayout::Right &layout_tag, const Trans::Transpose &) { + auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); + + return sv_nt; +} + +/** + * + * @tparam ViewValueType The value type (Scalar or Vector) of each view element + * @tparam ViewType The view type + * @param v The view handle + * @param m The requested row index of v + * @param n The requested col index of v + * @return If m and n are within the extents of v, a valid element of v; + * otherwise, the last element of v. + */ +template +KOKKOS_INLINE_FUNCTION ViewValueType +access_view_bounds_check(ViewType v, int m, int n, const BoundsCheck::Yes &) { + return v(KOKKOSKERNELS_MACRO_MIN(m, v.extent_int(0) - 1), + KOKKOSKERNELS_MACRO_MIN(n, v.extent_int(1) - 1)); +} + +template +KOKKOS_INLINE_FUNCTION ViewValueType +access_view_bounds_check(ViewType v, int m, int n, const BoundsCheck::No &) { + return v(m, n); +} + +template +KOKKOS_INLINE_FUNCTION ViewValueType fma_alpha(ViewValueType reg_c, + ScalarType alpha, + const AlphaTag::Yes &) { + return reg_c * alpha; +} + +template +KOKKOS_INLINE_FUNCTION ViewValueType fma_alpha(ViewValueType reg_c, + ScalarType /*alpha*/, + const AlphaTag::No &) { + return reg_c; +} + +template +KOKKOS_INLINE_FUNCTION void fma_bounds_check(ViewType v, SizeType m, SizeType n, + ViewValueType reg_c, + ScalarType alpha, ScalarType beta, + const ArgAlphaFmaTag &alpha_tag, + const BoundsCheck::Yes &) { + if (m < v.extent_int(0) && n < v.extent_int(1)) + v(m, n) = fma_alpha(reg_c, alpha, alpha_tag) + v(m, n) * beta; +} + +template +KOKKOS_INLINE_FUNCTION void fma_bounds_check(ViewType v, SizeType m, SizeType n, + ViewValueType reg_c, + ScalarType alpha, ScalarType beta, + const ArgAlphaFmaTag &alpha_tag, + const BoundsCheck::No &) { + v(m, n) = fma_alpha(reg_c, alpha, alpha_tag) + v(m, n) * beta; +} + +template +KOKKOS_INLINE_FUNCTION void fma_bounds_check(ViewType v, SizeType m, SizeType n, + ViewValueType reg_c, + ScalarType alpha, + const ArgAlphaFmaTag &alpha_tag, + const BoundsCheck::Yes &) { + if (m < v.extent_int(0) && n < v.extent_int(1)) + v(m, n) = fma_alpha(reg_c, alpha, alpha_tag); +} + +template +KOKKOS_INLINE_FUNCTION void fma_bounds_check(ViewType v, SizeType m, SizeType n, + ViewValueType reg_c, + ScalarType alpha, + const ArgAlphaFmaTag &alpha_tag, + const BoundsCheck::No &) { + v(m, n) = fma_alpha(reg_c, alpha, alpha_tag); +} + +} // namespace KokkosBatched +#endif // __KOKKOSBATCHED_UTIL_HPP__ diff --git a/external/kokkos-kernels/KokkosBatched_Vector.hpp b/external/kokkos-kernels/KokkosBatched_Vector.hpp new file mode 100644 index 00000000..f91e3dea --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector.hpp @@ -0,0 +1,297 @@ +#ifndef __KOKKOSBATCHED_VECTOR_HPP__ +#define __KOKKOSBATCHED_VECTOR_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "KokkosBatched_Util.hpp" + +// forward declaration +namespace KokkosBatched { + +template +class Vector; + +template +struct is_vector, l>> : public std::true_type {}; + +template +struct DefaultVectorLength { + enum : int { value = 1 }; +}; + +template <> +struct DefaultVectorLength { +#if defined(__AVX512F__) + enum : int{value = 16}; +#elif defined(__AVX__) || defined(__AVX2__) + enum : int{value = 8}; +#elif defined(__ARM_ARCH) + enum : int{value = 8}; +#else + enum : int { value = 8 }; +#endif +}; +template <> +struct DefaultVectorLength { +#if defined(__AVX512F__) + enum : int{value = 8}; +#elif defined(__AVX__) || defined(__AVX2__) + enum : int{value = 4}; +#elif defined(__ARM_ARCH) + enum : int{value = 4}; +#else + enum : int { value = 4 }; +#endif +}; +template <> +struct DefaultVectorLength, Kokkos::HostSpace> { +#if defined(__AVX512F__) + enum : int{value = 8}; +#elif defined(__AVX__) || defined(__AVX2__) + enum : int{value = 4}; +#elif defined(__ARM_ARCH) + enum : int{value = 4}; +#else + enum : int { value = 4 }; +#endif +}; +template <> +struct DefaultVectorLength, Kokkos::HostSpace> { +#if defined(__AVX512F__) + enum : int{value = 4}; +#elif defined(__AVX__) || defined(__AVX2__) + enum : int{value = 2}; +#elif defined(__ARM_ARCH) + enum : int{value = 2}; +#else + enum : int { value = 2 }; +#endif +}; + +#if defined(KOKKOS_ENABLE_CUDA) +template <> +struct DefaultVectorLength { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength, Kokkos::CudaSpace> { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength, Kokkos::CudaSpace> { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength, Kokkos::CudaUVMSpace> { + enum : int { value = 8 }; +}; +template <> +struct DefaultVectorLength, Kokkos::CudaUVMSpace> { + enum : int { value = 8 }; +}; +#endif + +#if defined(KOKKOS_ENABLE_HIP) +template <> +struct DefaultVectorLength { + enum : int { value = 16 }; +}; +template <> +struct DefaultVectorLength { + enum : int { value = 16 }; +}; +template <> +struct DefaultVectorLength, + Kokkos::Experimental::HIPSpace> { + enum : int { value = 16 }; +}; +template <> +struct DefaultVectorLength, + Kokkos::Experimental::HIPSpace> { + enum : int { value = 16 }; +}; +#endif + +template +struct DefaultInternalVectorLength { + enum : int { value = 1 }; +}; +template +struct DefaultInternalVectorLength { + enum : int { + value = DefaultVectorLength::value + }; +}; + +#if defined(KOKKOS_ENABLE_CUDA) +template <> +struct DefaultInternalVectorLength { + enum : int { value = 4 }; +}; +template <> +struct DefaultInternalVectorLength { + enum : int { value = 2 }; +}; +template <> +struct DefaultInternalVectorLength, Kokkos::CudaSpace> { + enum : int { value = 2 }; +}; +template <> +struct DefaultInternalVectorLength, Kokkos::CudaSpace> { + enum : int { value = 1 }; +}; +template <> +struct DefaultInternalVectorLength { + enum : int { value = 4 }; +}; +template <> +struct DefaultInternalVectorLength { + enum : int { value = 2 }; +}; +template <> +struct DefaultInternalVectorLength, + Kokkos::CudaUVMSpace> { + enum : int { value = 2 }; +}; +template <> +struct DefaultInternalVectorLength, + Kokkos::CudaUVMSpace> { + enum : int { value = 1 }; +}; +#endif + +#if defined(KOKKOS_ENABLE_HIP) +template <> +struct DefaultInternalVectorLength { + enum : int { value = 8 }; +}; +template <> +struct DefaultInternalVectorLength { + enum : int { value = 4 }; +}; +template <> +struct DefaultInternalVectorLength, + Kokkos::Experimental::HIPSpace> { + enum : int { value = 4 }; +}; +template <> +struct DefaultInternalVectorLength, + Kokkos::Experimental::HIPSpace> { + enum : int { value = 2 }; +}; +#endif + +template +struct MagnitudeScalarType; + +template <> +struct MagnitudeScalarType { + typedef float type; +}; +template <> +struct MagnitudeScalarType { + typedef double type; +}; +template <> +struct MagnitudeScalarType> { + typedef float type; +}; +template <> +struct MagnitudeScalarType> { + typedef double type; +}; + +template +struct MagnitudeScalarType, l>> { + typedef float type; +}; +template +struct MagnitudeScalarType, l>> { + typedef double type; +}; +template +struct MagnitudeScalarType>, l>> { + typedef float type; +}; +template +struct MagnitudeScalarType>, l>> { + typedef double type; +}; + +} // namespace KokkosBatched + +#include "KokkosBatched_Vector_SIMD.hpp" + +// arith traits overload for vector types +namespace Kokkos { +namespace Details { + +// do not use Vector alone as other can use the name. + +template +class ArithTraits, l>> { + public: + typedef typename ArithTraits::val_type val_scalar_type; + typedef typename ArithTraits::mag_type mag_scalar_type; + + typedef KokkosBatched::Vector, l> + val_type; + typedef KokkosBatched::Vector, l> + mag_type; + + static KOKKOS_FORCEINLINE_FUNCTION mag_type real(const val_type &val) { + return val; + } + + static const bool is_specialized = ArithTraits::is_specialized; + static const bool is_signed = ArithTraits::is_signed; + static const bool is_integer = ArithTraits::is_integer; + static const bool is_exact = ArithTraits::is_exact; + static const bool is_complex = ArithTraits::is_complex; +}; + +template +class ArithTraits< + KokkosBatched::Vector>, l>> { + public: + typedef typename ArithTraits::val_type val_scalar_type; + typedef typename ArithTraits::mag_type mag_scalar_type; + + typedef KokkosBatched::Vector< + KokkosBatched::SIMD>, l> + val_type; + typedef KokkosBatched::Vector, l> + mag_type; + + static KOKKOS_FORCEINLINE_FUNCTION mag_type real(const val_type &val) { + mag_type r_val; + for (int i = 0; i < l; ++i) { + r_val[i] = val[i].real(); + } + return r_val; + } + static KOKKOS_FORCEINLINE_FUNCTION mag_type imag(const val_type &val) { + mag_type r_val; + for (int i = 0; i < l; ++i) { + r_val[i] = val[i].imag(); + } + return r_val; + } +}; + +} // namespace Details +} // namespace Kokkos + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD.hpp new file mode 100644 index 00000000..0deba872 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD.hpp @@ -0,0 +1,810 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include +#include "KokkosBatched_Vector.hpp" +#include "KokkosKernels_Macros.hpp" + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#undef __KOKKOSBATCHED_ENABLE_AVX__ +#else +// compiler bug with AVX in some architectures +#define __KOKKOSBATCHED_ENABLE_AVX__ +#endif + +namespace KokkosBatched { + +template +class Vector, l> { + public: + using type = Vector, l>; + using value_type = T; + using mag_type = typename Kokkos::Details::ArithTraits::mag_type; + + enum : int { vector_length = l }; + + typedef value_type data_type[vector_length]; + + KOKKOS_INLINE_FUNCTION + static const char *label() { return "SIMD"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + KOKKOS_INLINE_FUNCTION Vector() { + // NOTE Not meant to be instantiated for CUDA + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) _data[i] = 0; + } + template + KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) _data[i] = val; + } + template + KOKKOS_INLINE_FUNCTION Vector( + const Vector, vector_length> &b) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) _data[i] = b[i]; + } + + KOKKOS_INLINE_FUNCTION + type &loadAligned(const value_type *p) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) _data[i] = p[i]; + return *this; + } + + KOKKOS_INLINE_FUNCTION + type &loadUnaligned(const value_type *p) { return loadAligned(p); } + + KOKKOS_INLINE_FUNCTION + void storeAligned(value_type *p) const { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) p[i] = _data[i]; + } + + KOKKOS_INLINE_FUNCTION + void storeUnaligned(value_type *p) const { storeAligned(p); } + + KOKKOS_INLINE_FUNCTION + value_type &operator[](const int &i) const { return _data[i]; } +}; +} // namespace KokkosBatched + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +namespace KokkosBatched { + +template <> +class Vector, 2> { + public: + using type = Vector, 2>; + using value_type = float; + using mag_type = float; + + enum : int { vector_length = 2 }; + typedef float2 data_type; + + KOKKOS_INLINE_FUNCTION + static const char *label() { return "GpuFloat2"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + KOKKOS_INLINE_FUNCTION Vector() { + _data.x = 0; + _data.y = 0; + } + KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { + _data.x = val; + _data.y = val; + } + KOKKOS_INLINE_FUNCTION Vector(const type &b) { + _data.x = b._data.x; + _data.y = b._data.y; + } + KOKKOS_INLINE_FUNCTION Vector(const float2 &val) { + _data.x = val.x; + _data.y = val.y; + } + + template + KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) { + _data.x = val; + _data.y = val; + } + + template + KOKKOS_INLINE_FUNCTION Vector( + const Vector, vector_length> &b) { + _data.x = b[0]; + _data.y = b[1]; + } + + KOKKOS_INLINE_FUNCTION + type &operator=(const float2 &val) { + _data.x = val.x; + _data.y = val.y; + return *this; + } + + KOKKOS_INLINE_FUNCTION + float2 float2() const { return _data; } + + KOKKOS_INLINE_FUNCTION + type &loadAligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + return *this; + } + + KOKKOS_INLINE_FUNCTION + type &loadUnaligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + return *this; + } + + KOKKOS_INLINE_FUNCTION + void storeAligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + } + + KOKKOS_INLINE_FUNCTION + void storeUnaligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + } + + KOKKOS_INLINE_FUNCTION + value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +template <> +class Vector, 2> { + public: + using type = Vector, 2>; + using value_type = double; + using mag_type = double; + + enum : int { vector_length = 2 }; + typedef double2 data_type; + + KOKKOS_INLINE_FUNCTION + static const char *label() { return "GpuDouble2"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + KOKKOS_INLINE_FUNCTION Vector() { + _data.x = 0; + _data.y = 0; + } + KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { + _data.x = val; + _data.y = val; + } + KOKKOS_INLINE_FUNCTION Vector(const type &b) { + _data.x = b._data.x; + _data.y = b._data.y; + } + KOKKOS_INLINE_FUNCTION Vector(const double2 &val) { + _data.x = val.x; + _data.y = val.y; + } + + template + KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) { + _data.x = val; + _data.y = val; + } + + template + KOKKOS_INLINE_FUNCTION Vector( + const Vector, vector_length> &b) { + _data.x = b[0]; + _data.y = b[1]; + } + + KOKKOS_INLINE_FUNCTION + type &operator=(const double2 &val) { + _data.x = val.x; + _data.y = val.y; + return *this; + } + + KOKKOS_INLINE_FUNCTION + double2 double2() const { return _data; } + + KOKKOS_INLINE_FUNCTION + type &loadAligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + return *this; + } + + KOKKOS_INLINE_FUNCTION + type &loadUnaligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + return *this; + } + + KOKKOS_INLINE_FUNCTION + void storeAligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + } + + KOKKOS_INLINE_FUNCTION + void storeUnaligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + } + + KOKKOS_INLINE_FUNCTION + value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +template <> +class Vector, 4> { + public: + using type = Vector, 4>; + using value_type = float; + using mag_type = float; + + enum : int { vector_length = 4 }; + typedef float4 data_type; + + KOKKOS_INLINE_FUNCTION + static const char *label() { return "GpuFloat4"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + KOKKOS_INLINE_FUNCTION Vector() { + _data.x = 0; + _data.y = 0; + _data.z = 0; + _data.w = 0; + } + KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { + _data.x = val; + _data.y = val; + _data.z = val; + _data.w = val; + } + KOKKOS_INLINE_FUNCTION Vector(const type &b) { + _data.x = b._data.x; + _data.y = b._data.y; + _data.z = b._data.z; + _data.w = b._data.w; + } + KOKKOS_INLINE_FUNCTION Vector(const float4 &val) { + _data.x = val.x; + _data.y = val.y; + _data.z = val.z; + _data.w = val.w; + } + + template + KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) { + _data.x = val; + _data.y = val; + _data.z = val; + _data.w = val; + } + + template + KOKKOS_INLINE_FUNCTION Vector( + const Vector, vector_length> &b) { + _data.x = b[0]; + _data.y = b[1]; + _data.z = b[2]; + _data.w = b[3]; + } + + KOKKOS_INLINE_FUNCTION + type &operator=(const float4 &val) { + _data.x = val.x; + _data.y = val.y; + _data.z = val.z; + _data.w = val.w; + return *this; + } + + KOKKOS_INLINE_FUNCTION + float4 float4() const { return _data; } + + KOKKOS_INLINE_FUNCTION + type &loadAligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + _data.z = *(p + 2); + _data.w = *(p + 3); + return *this; + } + + KOKKOS_INLINE_FUNCTION + type &loadUnaligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + _data.z = *(p + 2); + _data.w = *(p + 3); + return *this; + } + + KOKKOS_INLINE_FUNCTION + void storeAligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + *(p + 2) = _data.z; + *(p + 3) = _data.w; + } + + KOKKOS_INLINE_FUNCTION + void storeUnaligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + *(p + 2) = _data.z; + *(p + 3) = _data.w; + } + + KOKKOS_INLINE_FUNCTION + value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +template <> +class Vector, 4> { + public: + using type = Vector, 4>; + using value_type = double; + using mag_type = double; + + enum : int { vector_length = 4 }; + typedef double4 data_type; + + KOKKOS_INLINE_FUNCTION + static const char *label() { return "GpuDouble4"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + KOKKOS_INLINE_FUNCTION Vector() { + _data.x = 0; + _data.y = 0; + _data.z = 0; + _data.w = 0; + } + KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { + _data.x = val; + _data.y = val; + _data.z = val; + _data.w = val; + } + KOKKOS_INLINE_FUNCTION Vector(const type &b) { + _data.x = b._data.x; + _data.y = b._data.y; + _data.z = b._data.z; + _data.w = b._data.w; + } + KOKKOS_INLINE_FUNCTION Vector(const double4 &val) { + _data.x = val.x; + _data.y = val.y; + _data.z = val.z; + _data.w = val.w; + } + + template + KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) { + _data.x = val; + _data.y = val; + _data.z = val; + _data.w = val; + } + + template + KOKKOS_INLINE_FUNCTION Vector( + const Vector, vector_length> &b) { + _data.x = b[0]; + _data.y = b[1]; + _data.z = b[2]; + _data.w = b[3]; + } + + KOKKOS_INLINE_FUNCTION + type &operator=(const double4 &val) { + _data.x = val.x; + _data.y = val.y; + _data.z = val.z; + _data.w = val.w; + return *this; + } + + KOKKOS_INLINE_FUNCTION + double4 double4() const { return _data; } + + KOKKOS_INLINE_FUNCTION + type &loadAligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + _data.z = *(p + 2); + _data.w = *(p + 3); + return *this; + } + + KOKKOS_INLINE_FUNCTION + type &loadUnaligned(const value_type *p) { + _data.x = *(p); + _data.y = *(p + 1); + _data.z = *(p + 2); + _data.w = *(p + 3); + return *this; + } + + KOKKOS_INLINE_FUNCTION + void storeAligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + *(p + 2) = _data.z; + *(p + 3) = _data.w; + } + + KOKKOS_INLINE_FUNCTION + void storeUnaligned(value_type *p) const { + *(p) = _data.x; + *(p + 1) = _data.y; + *(p + 2) = _data.z; + *(p + 3) = _data.w; + } + + KOKKOS_INLINE_FUNCTION + value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +} // namespace KokkosBatched +#endif + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX__) || defined(__AVX2__) +#include + +namespace KokkosBatched { + +template <> +class Vector, 4> { + public: + using type = Vector, 4>; + using value_type = double; + using mag_type = double; + + enum : int { vector_length = 4 }; + typedef __m256d data_type __attribute__((aligned(32))); + + inline static const char *label() { return "AVX256"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + inline Vector() { _data = _mm256_setzero_pd(); } + inline Vector(const value_type &val) { _data = _mm256_set1_pd(val); } + inline Vector(const type &b) { _data = b._data; } + inline Vector(const __m256d &val) { _data = val; } + + template + inline Vector(const ArgValueType &val) { + auto d = reinterpret_cast(&_data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) d[i] = val; + } + + template + inline Vector(const Vector, vector_length> &b) { + auto dd = reinterpret_cast(&_data); + auto bb = reinterpret_cast(&b._data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) dd[i] = bb[i]; + } + + inline type &operator=(const __m256d &val) { + _data = val; + return *this; + } + + inline operator __m256d() const { return _data; } + + inline type &loadAligned(const value_type *p) { + _data = _mm256_load_pd(p); + return *this; + } + + inline type &loadUnaligned(const value_type *p) { + _data = _mm256_loadu_pd(p); + return *this; + } + + inline void storeAligned(value_type *p) const { _mm256_store_pd(p, _data); } + + inline void storeUnaligned(value_type *p) const { + _mm256_storeu_pd(p, _data); + } + + inline value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +template <> +class Vector >, 2> { + public: + using type = Vector >, 2>; + using value_type = Kokkos::complex; + using mag_type = double; + + static const int vector_length = 2; + typedef __m256d data_type __attribute__((aligned(32))); + + inline static const char *label() { return "AVX256"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + inline Vector() { _data = _mm256_setzero_pd(); } + inline Vector(const value_type &val) { + _data = _mm256_broadcast_pd((const __m128d *)&val); + } + inline Vector(const mag_type &val) { + const value_type a(val); + _data = _mm256_broadcast_pd((__m128d const *)&a); + } + inline Vector(const type &b) { _data = b._data; } + inline Vector(const __m256d &val) { _data = val; } + + // template + // inline Vector(const ArgValueType val) { + // #if defined( KOKKOS_ENABLE_PRAGMA_IVDEP ) + // #pragma ivdep + // #endif + // #if defined( KOKKOS_ENABLE_PRAGMA_VECTOR ) + // #pragma vector always + // #endif + // for (int i=0;i + inline Vector(const Vector, vector_length> &b) { + auto dd = reinterpret_cast(&_data); + auto bb = reinterpret_cast(&b._data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) dd[i] = bb[i]; + } + + inline type &operator=(const __m256d &val) { + _data = val; + return *this; + } + + inline operator __m256d() const { return _data; } + + inline type &loadAligned(const value_type *p) { + _data = _mm256_load_pd((mag_type *)p); + return *this; + } + + inline type &loadUnaligned(const value_type *p) { + _data = _mm256_loadu_pd((mag_type *)p); + return *this; + } + + inline void storeAligned(value_type *p) const { + _mm256_store_pd((mag_type *)p, _data); + } + + inline void storeUnaligned(value_type *p) const { + _mm256_storeu_pd((mag_type *)p, _data); + } + + inline value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; +} // namespace KokkosBatched +#endif /* #if defined(__AVX__) || defined(__AVX2__) */ + +#if defined(__AVX512F__) +#include + +namespace KokkosBatched { + +template <> +class Vector, 8> { + public: + using type = Vector, 8>; + using value_type = double; + using mag_type = double; + + enum : int { vector_length = 8 }; + typedef __m512d data_type __attribute__((aligned(64))); + + inline static const char *label() { return "AVX512"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + inline Vector() { _data = _mm512_setzero_pd(); } + inline Vector(const value_type &val) { _data = _mm512_set1_pd(val); } + inline Vector(const type &b) { _data = b._data; } + inline Vector(const __m512d &val) { _data = val; } + + template + inline Vector(const ArgValueType &val) { + auto d = reinterpret_cast(&_data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) d[i] = val; + } + template + inline Vector(const Vector, vector_length> &b) { + auto dd = reinterpret_cast(&_data); + auto bb = reinterpret_cast(&b._data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) dd[i] = bb[i]; + } + + inline type &operator=(const __m512d &val) { + _data = val; + return *this; + } + + inline operator __m512d() const { return _data; } + + inline type &loadAligned(const value_type *p) { + _data = _mm512_load_pd(p); + return *this; + } + + inline type &loadUnaligned(const value_type *p) { + _data = _mm512_loadu_pd(p); + return *this; + } + + inline void storeAligned(value_type *p) const { _mm512_store_pd(p, _data); } + + inline void storeUnaligned(value_type *p) const { + _mm512_storeu_pd(p, _data); + } + + inline value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; + +template <> +class Vector >, 4> { + public: + using type = Vector >, 4>; + using value_type = Kokkos::complex; + using mag_type = double; + + enum : int { vector_length = 4 }; + typedef __m512d data_type __attribute__((aligned(64))); + + inline static const char *label() { return "AVX512"; } + + template + friend class Vector; + + private: + mutable data_type _data; + + public: + inline Vector() { _data = _mm512_setzero_pd(); } + inline Vector(const value_type &val) { + _data = _mm512_mask_broadcast_f64x4(_mm512_set1_pd(val.imag()), 0x55, + _mm256_set1_pd(val.real())); + } + inline Vector(const mag_type &val) { + _data = _mm512_mask_broadcast_f64x4(_mm512_setzero_pd(), 0x55, + _mm256_set1_pd(val)); + } + inline Vector(const type &b) { _data = b._data; } + inline Vector(const __m512d &val) { _data = val; } + + template + inline Vector(const ArgValueType &val) { + auto d = reinterpret_cast(&_data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) d[i] = val; + } + template + inline Vector(const Vector, vector_length> &b) { + auto dd = reinterpret_cast(&_data); + auto bb = reinterpret_cast(&b._data); + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < vector_length; ++i) dd[i] = bb[i]; + } + + inline type &operator=(const __m512d &val) { + _data = val; + return *this; + } + + inline operator __m512d() const { return _data; } + + inline type &loadAligned(const value_type *p) { + _data = _mm512_load_pd((mag_type *)p); + return *this; + } + + inline type &loadUnaligned(const value_type *p) { + _data = _mm512_loadu_pd((mag_type *)p); + return *this; + } + + inline void storeAligned(value_type *p) const { + _mm512_store_pd((mag_type *)p, _data); + } + + inline void storeUnaligned(value_type *p) const { + _mm512_storeu_pd((mag_type *)p, _data); + } + + inline value_type &operator[](const int &i) const { + return reinterpret_cast(&_data)[i]; + } +}; +} // namespace KokkosBatched + +#endif /* #if defined(__AVX512F__) */ +#endif /* #if defined(__KOKKOSBATCHED_ENABLE_AVX__) */ + +#include "KokkosBatched_Vector_SIMD_Arith.hpp" +#include "KokkosBatched_Vector_SIMD_Logical.hpp" +#include "KokkosBatched_Vector_SIMD_Relation.hpp" +#include "KokkosBatched_Vector_SIMD_Math.hpp" +#include "KokkosBatched_Vector_SIMD_Misc.hpp" +#include "KokkosBatched_Vector_SIMD_View.hpp" + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Arith.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Arith.hpp new file mode 100644 index 00000000..90bf2e4d --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Arith.hpp @@ -0,0 +1,887 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_ARITH_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_ARITH_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Complex.hpp" +#include "KokkosKernels_Macros.hpp" + +namespace KokkosBatched { + +#define KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) Vector, l> +#define KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE(T, l) \ + Vector, l> & + +/// simd, simd +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 8) operator+( + const Vector, 8> &a, const Vector, 8> &b) { + return _mm512_add_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator+(const Vector >, 4> &a, + const Vector >, 4> &b) { + return _mm512_add_pd(a, b); +} +#endif + +#endif +#if defined(__AVX__) || defined(__AVX2__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator+( + const Vector, 4> &a, const Vector, 4> &b) { + return _mm256_add_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 2) +operator+(const Vector >, 2> &a, + const Vector >, 2> &b) { + return _mm256_add_pd(a, b); +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator+(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; + if (std::is_fundamental::value) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < l; ++i) r_val[i] = a[i] + b[i]; + } else { + for (int i = 0; i < l; ++i) r_val[i] = a[i] + b[i]; + } + return r_val; +} + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 2) operator+( + const Vector, 2> &a, const Vector, 2> &b) { + float2 r_val; + r_val.x = a.float2().x + b.float2().x; + r_val.y = a.float2().y + b.float2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 2) operator+( + const Vector, 2> &a, const Vector, 2> &b) { + double2 r_val; + r_val.x = a.double2().x + b.double2().x; + r_val.y = a.double2().y + b.double2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 4) operator+( + const Vector, 4> &a, const Vector, 4> &b) { + float4 r_val; + r_val.x = a.float4().x + b.float4().x; + r_val.y = a.float4().y + b.float4().y; + r_val.z = a.float4().z + b.float4().z; + r_val.w = a.float4().w + b.float4().w; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator+( + const Vector, 4> &a, const Vector, 4> &b) { + double4 r_val; + r_val.x = a.double4().x + b.double4().x; + r_val.y = a.double4().y + b.double4().y; + r_val.z = a.double4().z + b.double4().z; + r_val.w = a.double4().w + b.double4().w; + return r_val; +} + +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator+=(Vector, l> &a, const Vector, l> &b) { + a = a + b; + return a; +} + +/// simd, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator+(const Vector, l> &a, const T b) { + return a + Vector, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator+(const T a, const Vector, l> &b) { + return Vector, l>(a) + b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator+=(Vector, l> &a, const T b) { + a = a + b; + return a; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator++(Vector, l> &a, int) { + Vector, l> a0 = a; + a = a + typename Kokkos::Details::ArithTraits::mag_type(1); + return a0; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator++(Vector, l> &a) { + a = a + typename Kokkos::Details::ArithTraits::mag_type(1); + return a; +} + +/// simd complex, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator+(const Vector >, l> &a, const T b) { + return a + Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator+(const T a, const Vector >, l> &b) { + return Vector >, l>(a) + b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator+=(Vector >, l> &a, const T b) { + a = a + b; + return a; +} + +/// simd complex, complex + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator+(const Vector >, l> &a, + const Kokkos::complex b) { + return a + Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator+(const Kokkos::complex a, + const Vector >, l> &b) { + return Vector >, l>(a) + b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator+=(Vector >, l> &a, + const Kokkos::complex b) { + a = a + b; + return a; +} + +/// --------------------------------------------------------------------------------------------------- + +/// simd, simd + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 8) operator-( + const Vector, 8> &a, const Vector, 8> &b) { + return _mm512_sub_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator-(const Vector >, 4> &a, + const Vector >, 4> &b) { + return _mm512_sub_pd(a, b); +} +#endif + +#endif +#if defined(__AVX__) || defined(__AVX2__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator-( + const Vector, 4> &a, const Vector, 4> &b) { + return _mm256_sub_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 2) +operator-(const Vector >, 2> &a, + const Vector >, 2> &b) { + return _mm256_sub_pd(a, b); +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator-(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; + if (std::is_fundamental::value) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < l; ++i) r_val[i] = a[i] - b[i]; + } else { + for (int i = 0; i < l; ++i) r_val[i] = a[i] - b[i]; + } + return r_val; +} + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 2) operator-( + const Vector, 2> &a, const Vector, 2> &b) { + float2 r_val; + r_val.x = a.float2().x - b.float2().x; + r_val.y = a.float2().y - b.float2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 2) operator-( + const Vector, 2> &a, const Vector, 2> &b) { + double2 r_val; + r_val.x = a.double2().x - b.double2().x; + r_val.y = a.double2().y - b.double2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 4) operator-( + const Vector, 4> &a, const Vector, 4> &b) { + float4 r_val; + r_val.x = a.float4().x - b.float4().x; + r_val.y = a.float4().y - b.float4().y; + r_val.z = a.float4().z - b.float4().z; + r_val.w = a.float4().w - b.float4().w; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator-( + const Vector, 4> &a, const Vector, 4> &b) { + double4 r_val; + r_val.x = a.double4().x - b.double4().x; + r_val.y = a.double4().y - b.double4().y; + r_val.z = a.double4().z - b.double4().z; + r_val.w = a.double4().w - b.double4().w; + return r_val; +} +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator-(const Vector, l> &a) { + Vector, l> r_val; + if (std::is_fundamental::value) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < l; ++i) r_val[i] = -a[i]; + } else { + for (int i = 0; i < l; ++i) r_val[i] = -a[i]; + } + return r_val; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator-=(Vector, l> &a, const Vector, l> &b) { + a = a - b; + return a; +} + +/// simd, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator-(const Vector, l> &a, const T b) { + return a - Vector, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator-(const T a, const Vector, l> &b) { + return Vector, l>(a) - b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator-=(Vector, l> &a, const T b) { + a = a - b; + return a; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator--(Vector, l> &a, int) { + Vector, l> a0 = a; + a = a - typename Kokkos::Details::ArithTraits::mag_type(1); + return a0; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator--(Vector, l> &a) { + a = a - typename Kokkos::Details::ArithTraits::mag_type(1); + return a; +} + +/// simd complex, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator-(const Vector >, l> &a, const T b) { + return a - Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator-(const T a, const Vector >, l> &b) { + return Vector >, l>(a) - b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator-=(Vector >, l> &a, const T b) { + a = a - b; + return a; +} + +/// simd complex, complex + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator-(const Vector >, l> &a, + const Kokkos::complex b) { + return a - Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator-(const Kokkos::complex a, + const Vector >, l> &b) { + return Vector >, l>(a) - b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator-=(Vector >, l> &a, + const Kokkos::complex b) { + a = a - b; + return a; +} + +/// --------------------------------------------------------------------------------------------------- + +/// simd, simd + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 8) operator*( + const Vector, 8> &a, const Vector, 8> &b) { + return _mm512_mul_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) operator + *(const Vector >, 4> &a, + const Vector >, 4> &b) { + const __m512d as = _mm512_permute_pd(a, 0x55), + br = _mm512_permute_pd(b, 0x00), + bi = _mm512_permute_pd(b, 0xff); + +#if defined(__FMA__) + // latency 7, throughput 0.5 + return _mm512_fmaddsub_pd(a, br, _mm512_mul_pd(as, bi)); +#else + return _mm512_add_pd( + _mm512_mul_pd(a, br), + _mm512_castsi512_pd(_mm512_xor_si512( + _mm512_castpd_si512(_mm512_mul_pd(as, bi)), + _mm512_castpd_si512(_mm512_mask_broadcast_f64x4( + _mm512_setzero_pd(), 0x55, _mm256_set1_pd(-0.0)))))); + // const __mm512d cc = _mm512_mul_pd(as, bi); + // return _mm512_mask_sub_pd(_mm512_mask_add_pd(_mm512_mul_pd(a, br), 0x55, + // cc), 0xaa, cc); +#endif +} +#endif + +#endif +#if defined(__AVX__) || defined(__AVX2__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator*( + const Vector, 4> &a, const Vector, 4> &b) { + return _mm256_mul_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static Vector >, 2> operator*( + const Vector >, 2> &a, + const Vector >, 2> &b) { + const __m256d as = _mm256_permute_pd(a, 0x5), br = _mm256_permute_pd(b, 0x0), + bi = _mm256_permute_pd(b, 0xf); + +#if defined(__FMA__) + return _mm256_fmaddsub_pd(a, br, _mm256_mul_pd(as, bi)); +#else + return _mm256_add_pd(_mm256_mul_pd(a, br), + _mm256_xor_pd(_mm256_mul_pd(as, bi), + _mm256_set_pd(0.0, -0.0, 0.0, -0.0))); +#endif +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator*(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; + if (std::is_fundamental::value) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < l; ++i) r_val[i] = a[i] * b[i]; + } else { + for (int i = 0; i < l; ++i) r_val[i] = a[i] * b[i]; + } + return r_val; +} + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 2) operator*( + const Vector, 2> &a, const Vector, 2> &b) { + float2 r_val; + r_val.x = a.float2().x * b.float2().x; + r_val.y = a.float2().y * b.float2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 2) operator*( + const Vector, 2> &a, const Vector, 2> &b) { + double2 r_val; + r_val.x = a.double2().x * b.double2().x; + r_val.y = a.double2().y * b.double2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 4) operator*( + const Vector, 4> &a, const Vector, 4> &b) { + float4 r_val; + r_val.x = a.float4().x * b.float4().x; + r_val.y = a.float4().y * b.float4().y; + r_val.z = a.float4().z * b.float4().z; + r_val.w = a.float4().w * b.float4().w; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator*( + const Vector, 4> &a, const Vector, 4> &b) { + double4 r_val; + r_val.x = a.double4().x * b.double4().x; + r_val.y = a.double4().y * b.double4().y; + r_val.z = a.double4().z * b.double4().z; + r_val.w = a.double4().w * b.double4().w; + return r_val; +} +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator*=(Vector, l> &a, const Vector, l> &b) { + a = a * b; + return a; +} + +/// simd, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator*(const Vector, l> &a, const T b) { + return a * Vector, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator*(const T a, const Vector, l> &b) { + return Vector, l>(a) * b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator*=(Vector, l> &a, const T b) { + a = a * b; + return a; +} + +/// simd complex, real + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator*(const Vector >, 4> &a, const double b) { + return _mm512_mul_pd(a, _mm512_set1_pd(b)); +} +#endif + +#endif +#if defined(__AVX__) || defined(__AVX2__) + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 2) operator + *(const Vector >, 2> &a, const double b) { + return _mm256_mul_pd(a, _mm256_set1_pd(b)); +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator*(const Vector >, l> &a, const T b) { + return a * Vector >, l>(b); +} + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator*(const double a, const Vector >, 4> &b) { + return _mm512_mul_pd(_mm512_set1_pd(a), b); +} +#endif + +#endif +#if defined(__AVX__) || defined(__AVX2__) + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 2) operator + *(const double a, const Vector >, 2> &b) { + return _mm256_mul_pd(_mm256_set1_pd(a), b); +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator*(const T a, const Vector >, l> &b) { + return Vector >, l>(a) * b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator*=(Vector >, l> &a, const T b) { + a = a * b; + return a; +} + +/// simd complex, complex + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator*(const Vector >, l> &a, + const Kokkos::complex b) { + return a * Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator*(const Kokkos::complex a, + const Vector >, l> &b) { + return Vector >, l>(a) * b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator*=(Vector >, l> &a, + const Kokkos::complex b) { + a = a * b; + return a; +} + +/// --------------------------------------------------------------------------------------------------- + +/// simd, simd + +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 8) operator/( + const Vector, 8> &a, const Vector, 8> &b) { + return _mm512_div_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator/(const Vector >, 4> &a, + const Vector >, 4> &b) { + const __m512d as = _mm512_permute_pd(a, 0x55), + cb = _mm512_castsi512_pd(_mm512_xor_si512( + _mm512_castpd_si512(b), + _mm512_castpd_si512(_mm512_mask_broadcast_f64x4( + _mm512_setzero_pd(), 0xAA, _mm256_set1_pd(-0.0))))), + br = _mm512_permute_pd(cb, 0x00), + bi = _mm512_permute_pd(cb, 0xff); + +#if defined(__FMA__) + return _mm512_div_pd(_mm512_fmaddsub_pd(a, br, _mm512_mul_pd(as, bi)), + _mm512_fmadd_pd(br, br, _mm512_mul_pd(bi, bi))); +#else + return _mm512_div_pd( + _mm512_add_pd( + _mm512_mul_pd(a, br), + _mm512_castsi512_pd(_mm512_xor_si512( + _mm512_castpd_si512(_mm512_mul_pd(as, bi)), + _mm512_castpd_si512(_mm512_mask_broadcast_f64x4( + _mm512_setzero_pd(), 0xAA, _mm256_set1_pd(-0.0)))))), + _mm512_add_pd(_mm512_mul_pd(br, br), _mm512_mul_pd(bi, bi))); + // const __mm512d cc = _mm512_mul_pd(as, bi); + // return _mm512_div_pd(_mm512_mask_sub_pd(_mm512_mask_add_pd(_mm512_mul_pd(a, + // br), 0x55, cc), 0xaa, cc), + // _mm512_add_pd(_mm512_mul_pd(br, br), _mm512_mul_pd(bi, + // bi))); +#endif +} +#endif + +#endif + +#if defined(__AVX__) || defined(__AVX2__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator/( + const Vector, 4> &a, const Vector, 4> &b) { + return _mm256_div_pd(a, b); +} + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 2) +operator/(Vector >, 2> const &a, + Vector >, 2> const &b) { + const __m256d as = _mm256_permute_pd(a, 0x5), + cb = _mm256_xor_pd(b, _mm256_set_pd(-0.0, 0.0, -0.0, 0.0)), + br = _mm256_permute_pd(cb, 0x0), + bi = _mm256_permute_pd(cb, 0xf); + +#if defined(__FMA__) + return _mm256_div_pd( + _mm256_fmaddsub_pd(a, br, _mm256_mul_pd(as, bi)), + _mm256_add_pd(_mm256_mul_pd(br, br), _mm256_mul_pd(bi, bi))); +#else + return _mm256_div_pd( + _mm256_add_pd(_mm256_mul_pd(a, br), + _mm256_xor_pd(_mm256_mul_pd(as, bi), + _mm256_set_pd(0.0, -0.0, 0.0, -0.0))), + _mm256_add_pd(_mm256_mul_pd(br, br), _mm256_mul_pd(bi, bi))); +#endif +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator/(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; + if (std::is_fundamental::value) { + KOKKOSKERNELS_FORCE_SIMD + for (int i = 0; i < l; ++i) r_val[i] = a[i] / b[i]; + } else { + for (int i = 0; i < l; ++i) r_val[i] = a[i] / b[i]; + } + return r_val; +} + +#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 2) operator/( + const Vector, 2> &a, const Vector, 2> &b) { + float2 r_val; + r_val.x = a.float2().x / b.float2().x; + r_val.y = a.float2().y / b.float2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 2) operator/( + const Vector, 2> &a, const Vector, 2> &b) { + double2 r_val; + r_val.x = a.double2().x / b.double2().x; + r_val.y = a.double2().y / b.double2().y; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(float, 4) operator/( + const Vector, 4> &a, const Vector, 4> &b) { + float4 r_val; + r_val.x = a.float4().x / b.float4().x; + r_val.y = a.float4().y / b.float4().y; + r_val.z = a.float4().z / b.float4().z; + r_val.w = a.float4().w / b.float4().w; + return r_val; +} +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(double, 4) operator/( + const Vector, 4> &a, const Vector, 4> &b) { + double4 r_val; + r_val.x = a.double4().x / b.double4().x; + r_val.y = a.double4().y / b.double4().y; + r_val.z = a.double4().z / b.double4().z; + r_val.w = a.double4().w / b.double4().w; + return r_val; +} +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator/=(Vector, l> &a, const Vector, l> &b) { + a = a / b; + return a; +} + +/// simd, real +#if defined(__KOKKOSBATCHED_ENABLE_AVX__) +#if defined(__AVX512F__) + +#if !defined(KOKKOS_COMPILER_GNU) +KOKKOS_FORCEINLINE_FUNCTION +static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(Kokkos::complex, 4) +operator/(const Vector >, 4> &a, const double b) { + return _mm512_div_pd(a, _mm512_set1_pd(b)); +} +#endif + +#endif +#endif + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator/(const Vector, l> &a, const T b) { + return a / Vector, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE(T, l) +operator/(const T a, const Vector, l> &b) { + return Vector, l>(a) / b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + T, l) +operator/=(Vector, l> &a, const T b) { + a = a / b; + return a; +} + +/// simd complex, real + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator/(const Vector >, l> &a, const T b) { + return a / Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator/(const T a, const Vector >, l> &b) { + return Vector >, l>(a) / b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator/=(Vector >, l> &a, const T b) { + a = a / b; + return a; +} + +/// simd complex, complex + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator/(const Vector >, l> &a, + const Kokkos::complex b) { + return a / Vector >, l>(b); +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE( + Kokkos::complex, l) +operator/(const Kokkos::complex a, + const Vector >, l> &b) { + return Vector >, l>(a) / b; +} + +template +KOKKOS_FORCEINLINE_FUNCTION static KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE( + Kokkos::complex, l) +operator/=(Vector >, l> &a, + const Kokkos::complex b) { + a = a / b; + return a; +} +#undef KOKKOSKERNELS_SIMD_ARITH_RETURN_TYPE +#undef KOKKOSKERNELS_SIMD_ARITH_RETURN_REFERENCE_TYPE +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Logical.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Logical.hpp new file mode 100644 index 00000000..23f20490 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Logical.hpp @@ -0,0 +1,123 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_LOGICAL_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_LOGICAL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Complex.hpp" + +namespace KokkosBatched { + +#define KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, T1, l) \ + typename std::enable_if::value && \ + std::is_integral::value, \ + const Vector, l> >::type + +template +KOKKOS_INLINE_FUNCTION static + typename std::enable_if::value, + const Vector, l> >::type + operator!(const Vector, l> &a) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = !a[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator||(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a[i] || b[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator&&(const Vector, l> &a, const Vector, l> &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a[i] && b[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator||(const Vector, l> &a, const T1 &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a[i] || b; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator&&(const Vector, l> &a, const T1 &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a[i] && b; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator||(const T0 &a, const Vector, l> &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a || b[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE(T0, + T1, l) +operator&&(const T0 &a, const Vector, l> &b) { + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = a && b[i]; + return r_val; +} +#undef KOKKOSKERNELS_SIMD_LOGICAL_RETURN_BOOL_TYPE +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Math.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Math.hpp new file mode 100644 index 00000000..fca23e0a --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Math.hpp @@ -0,0 +1,301 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_MATH_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_MATH_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Complex.hpp" + +namespace KokkosBatched { + +#define KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) Vector, l> +#define KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) \ + typename std::enable_if::value, \ + Vector, l> >::type + +/// simd + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) + sqrt(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::sqrt(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) + cbrt(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::cbrt(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) + log(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::log(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) + log10(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::log10(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T, l) + exp(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::exp(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T0, l) + pow(const Vector, l> &a, const Vector, l> &b) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::pow(a[i], b[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T0, l) + pow(const T0 &a, const Vector, l> &b) { + return pow(Vector, l>(a), b); +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE(T0, l) + pow(const Vector, l> &a, const T1 &b) { + return pow(a, Vector, l>(b)); +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + sin(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::sin(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + cos(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::cos(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + tan(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::tan(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + sinh(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::sinh(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + cosh(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::cosh(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + tanh(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::tanh(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + asin(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::asin(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + acos(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::acos(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + atan(const Vector, l> &a) { + typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = ats::atan(a[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + atan2(const Vector, l> &a, const Vector, l> &b) { + // typedef Kokkos::Details::ArithTraits ats; + Vector, l> r_val; +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#pragma ivdep +#endif +#if defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#pragma vector always +#endif + for (int i = 0; i < l; ++i) r_val[i] = std::atan2(a[i], b[i]); + + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + atan2(const T &a, const Vector, l> &b) { + return atan2(Vector, l>(a), b); +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE(T, l) + atan2(const Vector, l> &a, const T &b) { + return atan2(a, Vector, l>(b)); +} + +#undef KOKKOSKERNELS_SIMD_MATH_RETURN_TYPE +#undef KOKKOSKERNELS_SIMD_MATH_RETURN_FLOAT_TYPE +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Misc.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Misc.hpp new file mode 100644 index 00000000..a07d8ab1 --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Misc.hpp @@ -0,0 +1,165 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_MISC_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_MISC_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Complex.hpp" + +namespace KokkosBatched { + +#define KOKKOSKERNELS_SIMD_MISC_RETURN_TYPE(T, l) Vector, l> +#define KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE(T0, T1, T2, l) void +// typename std::enable_if::value && +// std::is_convertible< T2 , T0 >::value, void >::type + +// scalar, scalar + +template +KOKKOS_INLINE_FUNCTION static T conditional_assign(const bool cond, + const T &if_true_val, + const T &if_false_val) { + return cond ? if_true_val : if_false_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE( + T0, T1, T2, l) + conditional_assign(/* */ T0 &r_val, const bool cond, const T1 &if_true_val, + const T2 &if_false_val) { + r_val = cond ? if_true_val : if_false_val; +} + +// vector, scalar + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_RETURN_TYPE(T, l) + conditional_assign(const Vector, l> &cond, + const Vector, l> &if_true_val, + const T &if_false_val) { + Vector, l> r_val; + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val[i] : if_false_val; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE( + T0, T1, T2, l) conditional_assign(/* */ Vector, l> &r_val, + const Vector, l> &cond, + const Vector, l> &if_true_val, + const T2 &if_false_val) { + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val[i] : if_false_val; +} + +// scalar, vector + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_RETURN_TYPE(T, l) + conditional_assign(const Vector, l> &cond, const T &if_true_val, + const Vector, l> &if_false_val) { + Vector, l> r_val; + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val : if_false_val[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE( + T0, T1, T2, l) + conditional_assign(/* */ Vector, l> &r_val, + const Vector, l> &cond, const T1 &if_true_val, + const Vector, l> &if_false_val) { + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val : if_false_val[i]; +} + +// vector, vector + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_RETURN_TYPE(T, l) + conditional_assign(const Vector, l> &cond, + const Vector, l> &if_true_val, + const Vector, l> &if_false_val) { + Vector, l> r_val; + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val[i] : if_false_val[i]; + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE( + T0, T1, T2, l) conditional_assign(/* */ Vector, l> &r_val, + const Vector, l> &cond, + const Vector, l> &if_true_val, + const Vector, l> &if_false_val) { + for (int i = 0; i < l; ++i) + r_val[i] = cond[i] ? if_true_val[i] : if_false_val[i]; +} + +template +KOKKOS_INLINE_FUNCTION static T reduce(const Vector, l> &val, + const BinaryOp &func) { + T r_val = val[0]; + for (int i = 1; i < l; ++i) r_val = func(r_val, val[i]); + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static T reduce(const Vector, l> &val, + const BinaryOp &func, const T init) { + T r_val = init; + for (int i = 0; i < l; ++i) r_val = func(r_val, val[i]); + return r_val; +} + +template +KOKKOS_INLINE_FUNCTION static bool is_all_true( + const Vector, l> &cond) { + return reduce(cond, [](const bool left, const bool right) -> bool { + return (left && right); + }); +} + +template +KOKKOS_INLINE_FUNCTION static bool is_any_true( + const Vector, l> &cond) { + return reduce(cond, [](const bool left, const bool right) -> bool { + return left || right; + }); +} + +template +KOKKOS_INLINE_FUNCTION static T min(const Vector, l> &val) { + return reduce(val, [](const T left, const T right) -> T { + const auto tmp = left < right; + return tmp * left + !tmp * right; + }); +} + +template +KOKKOS_INLINE_FUNCTION static T max(const Vector, l> &val) { + return reduce(val, [](const T left, const T right) -> T { + const auto tmp = left > right; + return tmp * left + !tmp * right; + }); +} + +template +KOKKOS_INLINE_FUNCTION static T sum(const Vector, l> &val) { + return reduce( + val, [](const T left, const T right) -> T { return left + right; }, T(0)); +} + +template +KOKKOS_INLINE_FUNCTION static T prod(const Vector, l> &val) { + return reduce( + val, [](const T left, const T right) -> T { return left * right; }, T(1)); +} + +#undef KOKKOSKERNELS_SIMD_MISC_RETURN_TYPE +#undef KOKKOSKERNELS_SIMD_MISC_CONVERTIBLE_RETURN_VOID_TYPE + +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Relation.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Relation.hpp new file mode 100644 index 00000000..4283d77d --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_Relation.hpp @@ -0,0 +1,68 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_RELATION_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_RELATION_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Complex.hpp" + +namespace KokkosBatched { + +// vector, vector + +#undef KOKKOSBATCHED_RELATION_OPERATOR +#define KOKKOSBATCHED_RELATION_OPERATOR(op) \ + template \ + KOKKOS_INLINE_FUNCTION const Vector, l> operator op( \ + const Vector, l> &a, const Vector, l> &b) { \ + Vector, l> r_val; \ + for (int i = 0; i < l; ++i) r_val[i] = a[i] op b[i]; \ + return r_val; \ + } + +KOKKOSBATCHED_RELATION_OPERATOR(<) +KOKKOSBATCHED_RELATION_OPERATOR(>) +KOKKOSBATCHED_RELATION_OPERATOR(<=) +KOKKOSBATCHED_RELATION_OPERATOR(>=) +KOKKOSBATCHED_RELATION_OPERATOR(==) +KOKKOSBATCHED_RELATION_OPERATOR(!=) + +// vector, scalar +#undef KOKKOSBATCHED_RELATION_OPERATOR +#define KOKKOSBATCHED_RELATION_OPERATOR(op) \ + template \ + KOKKOS_INLINE_FUNCTION const Vector, l> operator op( \ + const Vector, l> &a, const T2 &b) { \ + Vector, l> r_val; \ + for (int i = 0; i < l; ++i) r_val[i] = a[i] op b; \ + return r_val; \ + } + +KOKKOSBATCHED_RELATION_OPERATOR(<) +KOKKOSBATCHED_RELATION_OPERATOR(>) +KOKKOSBATCHED_RELATION_OPERATOR(<=) +KOKKOSBATCHED_RELATION_OPERATOR(>=) +KOKKOSBATCHED_RELATION_OPERATOR(==) +KOKKOSBATCHED_RELATION_OPERATOR(!=) + +// scalar, vector +#undef KOKKOSBATCHED_RELATION_OPERATOR +#define KOKKOSBATCHED_RELATION_OPERATOR(op) \ + template \ + KOKKOS_INLINE_FUNCTION const Vector, l> operator op( \ + const T1 &a, const Vector, l> &b) { \ + Vector, l> r_val; \ + for (int i = 0; i < l; ++i) r_val[i] = a op b[i]; \ + return r_val; \ + } + +KOKKOSBATCHED_RELATION_OPERATOR(<) +KOKKOSBATCHED_RELATION_OPERATOR(>) +KOKKOSBATCHED_RELATION_OPERATOR(<=) +KOKKOSBATCHED_RELATION_OPERATOR(>=) +KOKKOSBATCHED_RELATION_OPERATOR(==) +KOKKOSBATCHED_RELATION_OPERATOR(!=) + +#undef KOKKOSBATCHED_RELATION_OPERATOR +} // namespace KokkosBatched + +#endif diff --git a/external/kokkos-kernels/KokkosBatched_Vector_SIMD_View.hpp b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_View.hpp new file mode 100644 index 00000000..90a8528c --- /dev/null +++ b/external/kokkos-kernels/KokkosBatched_Vector_SIMD_View.hpp @@ -0,0 +1,262 @@ +#ifndef __KOKKOSBATCHED_VECTOR_SIMD_VIEW_HPP__ +#define __KOKKOSBATCHED_VECTOR_SIMD_VIEW_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + +namespace KokkosBatched { + +template +struct PackDim { + enum : int { value = dim }; +}; + +// temporary solution until kokkos support SIMD layout or I do support it +template +struct SimdViewAccess { + private: + ViewType _a; + + public: + typedef typename ViewType::reference_type reference_simd_type; + typedef typename ViewType::pointer_type pointer_simd_type; + typedef typename ViewType::value_type value_simd_type; + + typedef typename value_simd_type::value_type value_type; + typedef value_type &reference_type; + typedef value_type *pointer_type; + + enum : int { rank = ViewType::rank }; + enum : int { pack_dim = PackDim::value }; + enum : int { vector_length = value_simd_type::vector_length }; + + SimdViewAccess() : _a() {} + SimdViewAccess(const ViewType &a) : _a(a) {} + + SimdViewAccess &operator=(const ViewType &b) { + _a = b; + return *this; + } + SimdViewAccess &operator=(const SimdViewAccess &b) { + if (this != &b) { + _a = b._a; + } + return *this; + } + + template + KOKKOS_INLINE_FUNCTION constexpr + typename std::enable_if::value, size_t>::type + extent(const iType &r) const { + return _a.extent(r) * (r == PackDim::value ? vector_length : 1); + } + + template + KOKKOS_INLINE_FUNCTION constexpr + typename std::enable_if::value, int>::type + extent_int(const iType &r) const { + return static_cast(_a.extent(r) * + (r == PackDim::value ? vector_length : 1)); + } + + KOKKOS_INLINE_FUNCTION constexpr size_t size() const { + return (_a.size() * vector_length); + } + + KOKKOS_INLINE_FUNCTION constexpr size_t span() const { + return _a.span() * vector_length; + } + KOKKOS_INLINE_FUNCTION constexpr bool span_span_is_contiguous() const { + return _a.span_span_is_contiguous(); + } + KOKKOS_INLINE_FUNCTION constexpr pointer_type data() const { + return _a.data(); + } + + /// rank 0 + /// this does not make sense as this is flat view to simd view + + /// rank 1 + template + KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if::value && + 1 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, Args... /*args*/) const { + return _a(i0 / vector_length)[i0 % vector_length]; + } + + /// rank 2 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && 2 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, Args... /*args*/) const { + switch (PackDim::value) { + case 0: return _a(i0 / vector_length, i1)[i0 % vector_length]; + case 1: break; + default: break; + } + return _a(i0, i1 / vector_length)[i1 % vector_length]; + } + + /// rank 3 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 3 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, Args... /*args*/) const { + switch (PackDim::value) { + case 0: return _a(i0 / vector_length, i1, i2)[i0 % vector_length]; + case 1: return _a(i0, i1 / vector_length, i2)[i1 % vector_length]; + case 2: break; + default: break; + } + return _a(i0, i1, i2 / vector_length)[i2 % vector_length]; + } + + /// rank 4 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 4 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, + Args... /*args*/) const { + switch (PackDim::value) { + case 0: return _a(i0 / vector_length, i1, i2, i3)[i0 % vector_length]; + case 1: return _a(i0, i1 / vector_length, i2, i3)[i1 % vector_length]; + case 2: return _a(i0, i1, i2 / vector_length, i3)[i2 % vector_length]; + case 3: break; + default: break; + } + return _a(i0, i1, i2, i3 / vector_length)[i3 % vector_length]; + } + + /// rank 5 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 5 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, + const I4 &i4, Args... /*args*/) const { + switch (PackDim::value) { + case 0: return _a(i0 / vector_length, i1, i2, i3, i4)[i0 % vector_length]; + case 1: return _a(i0, i1 / vector_length, i2, i3, i4)[i1 % vector_length]; + case 2: return _a(i0, i1, i2 / vector_length, i3, i4)[i2 % vector_length]; + case 3: return _a(i0, i1, i2, i3 / vector_length, i4)[i3 % vector_length]; + case 4: break; + default: break; + } + return _a(i0, i1, i2, i3, i4 / vector_length)[i4 % vector_length]; + } + + /// rank 6 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 6 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, + const I4 &i4, const I5 &i5, Args... /*args*/) const { + switch (PackDim::value) { + case 0: + return _a(i0 / vector_length, i1, i2, i3, i4, i5)[i0 % vector_length]; + case 1: + return _a(i0, i1 / vector_length, i2, i3, i4, i5)[i1 % vector_length]; + case 2: + return _a(i0, i1, i2 / vector_length, i3, i4, i5)[i2 % vector_length]; + case 3: + return _a(i0, i1, i2, i3 / vector_length, i4, i5)[i3 % vector_length]; + case 4: + return _a(i0, i1, i2, i3, i4 / vector_length, i5)[i4 % vector_length]; + case 5: break; + default: break; + } + return _a(i0, i1, i2, i3, i4, i5 / vector_length)[i5 % vector_length]; + } + + /// rank 7 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 7 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, + const I4 &i4, const I5 &i5, const I6 &i6, Args... /*args*/) const { + switch (PackDim::value) { + case 0: + return _a(i0 / vector_length, i1, i2, i3, i4, i5, + i6)[i0 % vector_length]; + case 1: + return _a(i0, i1 / vector_length, i2, i3, i4, i5, + i6)[i1 % vector_length]; + case 2: + return _a(i0, i1, i2 / vector_length, i3, i4, i5, + i6)[i2 % vector_length]; + case 3: + return _a(i0, i1, i2, i3 / vector_length, i4, i5, + i6)[i3 % vector_length]; + case 4: + return _a(i0, i1, i2, i3, i4 / vector_length, i5, + i6)[i4 % vector_length]; + case 5: + return _a(i0, i1, i2, i3, i4, i5 / vector_length, + i6)[i5 % vector_length]; + case 6: break; + default: break; + } + return _a(i0, i1, i2, i3, i4, i5, i6 / vector_length)[i6 % vector_length]; + } + + /// rank 8 + template + KOKKOS_FORCEINLINE_FUNCTION typename std::enable_if< + Kokkos::Impl::are_integral::value && + 8 == ViewType::rank, + reference_type>::type + operator()(const I0 &i0, const I1 &i1, const I2 &i2, const I3 &i3, + const I4 &i4, const I5 &i5, const I6 &i6, const I7 &i7, + Args... /*args*/) const { + switch (PackDim::value) { + case 0: + return _a(i0 / vector_length, i1, i2, i3, i4, i5, i6, + i7)[i0 % vector_length]; + case 1: + return _a(i0, i1 / vector_length, i2, i3, i4, i5, i6, + i7)[i1 % vector_length]; + case 2: + return _a(i0, i1, i2 / vector_length, i3, i4, i5, i6, + i7)[i2 % vector_length]; + case 3: + return _a(i0, i1, i2, i3 / vector_length, i4, i5, i6, + i7)[i3 % vector_length]; + case 4: + return _a(i0, i1, i2, i3, i4 / vector_length, i5, i6, + i7)[i4 % vector_length]; + case 5: + return _a(i0, i1, i2, i3, i4, i5 / vector_length, i6, + i7)[i5 % vector_length]; + case 6: + return _a(i0, i1, i2, i3, i4, i5, i6 / vector_length, + i7)[i6 % vector_length]; + case 7: break; + default: break; + } + return _a(i0, i1, i2, i3, i4, i5, i6, + i7 / vector_length)[i7 % vector_length]; + } +}; +} // namespace KokkosBatched + +#pragma GCC diagnostic pop + +#endif diff --git a/external/kokkos-kernels/KokkosBlas1_serial_scal_impl.hpp b/external/kokkos-kernels/KokkosBlas1_serial_scal_impl.hpp new file mode 100644 index 00000000..bb411ef4 --- /dev/null +++ b/external/kokkos-kernels/KokkosBlas1_serial_scal_impl.hpp @@ -0,0 +1,86 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef KOKKOSBLAS1_SERIAL_SCAL_IMPL_HPP_ +#define KOKKOSBLAS1_SERIAL_SCAL_IMPL_HPP_ + +#include + +namespace KokkosBlas { +namespace Impl { + +/// +/// Serial Internal Impl +/// ==================== +struct SerialScaleInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int i = 0; i < m; ++i) A[i * as0] *= alpha; + + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (as0 > as1) + for (int i = 0; i < m; ++i) invoke(n, alpha, A + i * as0, as1); + else + for (int j = 0; j < n; ++j) invoke(m, alpha, A + j * as1, as0); + + return 0; + } +}; + +} // namespace Impl +} // namespace KokkosBlas + +#endif diff --git a/external/kokkos-kernels/KokkosBlas1_set_impl.hpp b/external/kokkos-kernels/KokkosBlas1_set_impl.hpp new file mode 100644 index 00000000..a3870a2e --- /dev/null +++ b/external/kokkos-kernels/KokkosBlas1_set_impl.hpp @@ -0,0 +1,166 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef __KOKKOSBLAS_SET_IMPL_HPP__ +#define __KOKKOSBLAS_SET_IMPL_HPP__ + +/// \author Kyungjoo Kim (kyukim@sandia.gov) + +#include "Kokkos_Core.hpp" + +namespace KokkosBlas { +namespace Impl { + +/// +/// Serial Internal Impl +/// ==================== +struct SerialSetInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { +#if defined(KOKKOS_ENABLE_PRAGMA_UNROLL) +#pragma unroll +#endif + for (int i = 0; i < m; ++i) A[i * as0] = alpha; + + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const int m, const int n, + const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (as0 > as1) + for (int i = 0; i < m; ++i) invoke(n, alpha, A + i * as0, as1); + else + for (int j = 0; j < n; ++j) invoke(m, alpha, A + j * as1, as0); + + return 0; + } +}; + +/// +/// Team Internal Impl +/// ================== +struct TeamSetInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int m, const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + Kokkos::parallel_for(Kokkos::TeamThreadRange(member, m), + [&](const int &i) { A[i * as0] = alpha; }); + // member.team_barrier(); + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int m, const int n, + const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (m > n) { + Kokkos::parallel_for( + Kokkos::TeamThreadRange(member, m), [&](const int &i) { + SerialSetInternal::invoke(n, alpha, A + i * as0, as1); + }); + } else { + Kokkos::parallel_for( + Kokkos::TeamThreadRange(member, n), [&](const int &j) { + SerialSetInternal::invoke(m, alpha, A + j * as1, as0); + }); + } + // member.team_barrier(); + return 0; + } +}; + +/// +/// TeamVector Internal Impl +/// ======================== +struct TeamVectorSetInternal { + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int m, const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0) { + Kokkos::parallel_for(Kokkos::TeamVectorRange(member, m), + [&](const int &i) { A[i * as0] = alpha; }); + // member.team_barrier(); + return 0; + } + + template + KOKKOS_INLINE_FUNCTION static int invoke(const MemberType &member, + const int m, const int n, + const ScalarType alpha, + /* */ ValueType *KOKKOS_RESTRICT A, + const int as0, const int as1) { + if (m > n) { + Kokkos::parallel_for( + Kokkos::TeamThreadRange(member, m), [&](const int &i) { + Kokkos::parallel_for( + Kokkos::ThreadVectorRange(member, n), + [&](const int &j) { A[i * as0 + j * as1] = alpha; }); + }); + } else { + Kokkos::parallel_for( + Kokkos::ThreadVectorRange(member, m), [&](const int &i) { + Kokkos::parallel_for( + Kokkos::TeamThreadRange(member, n), + [&](const int &j) { A[i * as0 + j * as1] = alpha; }); + }); + } + // member.team_barrier(); + return 0; + } +}; + +} // namespace Impl +} // namespace KokkosBlas + +#endif diff --git a/external/kokkos-kernels/KokkosKernels_Half.hpp b/external/kokkos-kernels/KokkosKernels_Half.hpp new file mode 100644 index 00000000..cf778c12 --- /dev/null +++ b/external/kokkos-kernels/KokkosKernels_Half.hpp @@ -0,0 +1,91 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef KOKKOSKERNELS_HALF_HPP +#define KOKKOSKERNELS_HALF_HPP + +#include "Kokkos_Core.hpp" + +namespace KokkosKernels { +namespace Experimental { +////////////// BEGIN FP16/binary16 limits ////////////// +#define KOKKOSKERNELS_IMPL_FP16_MAX 65504.0F // Maximum normalized number +#define KOKKOSKERNELS_IMPL_FP16_MIN \ + 0.000000059604645F // Minimum normalized positive half precision number +#define KOKKOSKERNELS_IMPL_FP16_RADIX \ + 2 // Value of the base of the exponent representation. TODO: on all archs? +#define KOKKOSKERNELS_IMPL_FP16_MANT_DIG \ + 15 // Number of digits in the matissa that can be represented without losing + // precision. TODO: Confirm this +#define KOKKOSKERNELS_IMPL_FP16_MIN_EXP \ + -14 // This is the smallest possible exponent value +#define KOKKOSKERNELS_IMPL_FP16_MAX_EXP \ + 15 // This is the largest possible exponent value +#define KOKKOSKERNELS_IMPL_FP16_SIGNIFICAND_BITS 10 +#define KOKKOSKERNELS_IMPL_FP16_EPSILON 0.0009765625F // 1/2^10 +#define KOKKOSKERNELS_IMPL_HUGE_VALH 0x7c00 // bits [10,14] set. +////////////// END FP16/binary16 limits ////////////// + +////////////// BEGIN BF16/float16 limits ////////////// +#define KOKKOSKERNELS_IMPL_BF16_MAX 3.38953139e38 // Maximum normalized number +#define KOKKOSKERNELS_IMPL_BF16_MIN \ + 1.1754494351e-38 // Minimum normalized positive bhalf number +#define KOKKOSKERNELS_IMPL_BF16_RADIX \ + 2 // Value of the base of the exponent representation. TODO: on all archs? +#define KOKKOSKERNELS_IMPL_BF16_MANT_DIG_MIN 2 +#define KOKKOSKERNELS_IMPL_BF16_MANT_DIG_MAX 3 +#define KOKKOSKERNELS_IMPL_BF16_MANT_DIG \ + KOKKOSKERNELS_IMPL_BF16_MANT_DIG_MIN // Number of digits in the matissa that + // can be represented without losing + // precision. +#define KOKKOSKERNELS_IMPL_BF16_MIN_EXP \ + -126 // This is the smallest possible exponent value +#define KOKKOSKERNELS_IMPL_BF16_MAX_EXP \ + 127 // This is the largest possible exponent value +#define KOKKOSKERNELS_IMPL_BF16_EPSILON 0.0078125F // 1/2^7 +////////////// END BF16/bfloat16 limits ////////////// + +} // namespace Experimental +} // namespace KokkosKernels +#endif // KOKKOSKERNELS_HALF_HPP diff --git a/external/kokkos-kernels/KokkosKernels_Macros.hpp b/external/kokkos-kernels/KokkosKernels_Macros.hpp new file mode 100644 index 00000000..cef3a917 --- /dev/null +++ b/external/kokkos-kernels/KokkosKernels_Macros.hpp @@ -0,0 +1,119 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER + +// File contains common macro definitions which are both generated by cmake in +// KokkosKernels_config.h and written in this header file. + +#ifndef KOKKOSKERNELS_MACROS_HPP_ +#define KOKKOSKERNELS_MACROS_HPP_ + +/****** BEGIN macros populated by CMake ******/ +#include "KokkosKernels_config.h" +/****** END macros populated by CMake ******/ + +/****** BEGIN other helper macros ******/ +#ifndef KOKKOSKERNELS_DEBUG_LEVEL +#define KOKKOSKERNELS_DEBUG_LEVEL 1 +#endif + +// If KOKKOSKERNELS_ENABLE_OMP_SIMD is defined, it's legal to place +// "#pragma omp simd" before a for loop. It's never defined if a GPU-type device +// is enabled, since in that case, Kokkos::ThreadVectorRange should be used +// instead for SIMD parallel loops. + +#if !defined(KOKKOS_ENABLE_CUDA) && !defined(KOKKOS_ENABLE_HIP) && \ + defined(KOKKOS_ENABLE_OPENMP) +// For clang OpenMP support, see +// https://clang.llvm.org/docs/OpenMPSupport.html#id1 +#if defined(KOKKOS_COMPILER_GNU) || defined(KOKKOS_COMPILER_CLANG) +// GCC 4.8.5 and older do not support #pragma omp simd +// Do not enable when using GCC 7.2.0 or 7.3.0 + C++17 due to a bug in gcc +#if (KOKKOS_COMPILER_GNU > 485) && \ + !(KOKKOS_COMPILER_GNU == 720 && defined(KOKKOS_ENABLE_CXX17)) && \ + !(KOKKOS_COMPILER_GNU == 730 && defined(KOKKOS_ENABLE_CXX17)) +#define KOKKOSKERNELS_ENABLE_OMP_SIMD +#endif +// TODO: Check for a clang version that supports #pragma omp simd +#else +// All other Kokkos-supported compilers support it. +#define KOKKOSKERNELS_ENABLE_OMP_SIMD +#endif +#endif + +// Macro to place before an ordinary loop to force vectorization, based +// on the pragmas that are supported by the compiler. "Force" means to +// override the compiler's heuristics and always vectorize. +// This respects the fact that "omp simd" is incompatible with +// "vector always" and "ivdep" in the Intel OneAPI toolchain. +#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD +#define KOKKOSKERNELS_FORCE_SIMD _Pragma("omp simd") +#else +#if defined(KOKKOS_ENABLE_PRAGMA_IVDEP) && defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#define KOKKOSKERNELS_FORCE_SIMD _Pragma("ivdep") _Pragma("vector always") +#elif defined(KOKKOS_ENABLE_PRAGMA_IVDEP) +#define KOKKOSKERNELS_FORCE_SIMD _Pragma("ivdep") +#elif defined(KOKKOS_ENABLE_PRAGMA_VECTOR) +#define KOKKOSKERNELS_FORCE_SIMD _Pragma("vector always") +#else +// No macros available to suggest vectorization +#define KOKKOSKERNELS_FORCE_SIMD +#endif +#endif + +// Macro that tells GCC not to worry if a variable isn't being used. +// Generalized attributes were not implemented in GCC until 4.8: +// +// https://gcc.gnu.org/gcc-4.7/cxx0x_status.html +// https://gcc.gnu.org/gcc-4.8/cxx0x_status.html +// +// Thus, we can't use [[unused]]; we have to use the older GCC syntax +// for variable attributes. Be careful also of compilers that define +// the __GNUC__ macro but might not necessarily actually be GCC +// compliant. +#if defined(__GNUC__) && !defined(KOKKOSKERNELS_UNUSED_ATTRIBUTE) +#define KOKKOSKERNELS_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define KOKKOSKERNELS_UNUSED_ATTRIBUTE +#endif // __GNUC__ +/******* END other helper macros *******/ + +#endif // KOKKOSKERNELS_MACROS_HPP_ diff --git a/external/kokkos-kernels/KokkosKernels_SimpleUtils.hpp b/external/kokkos-kernels/KokkosKernels_SimpleUtils.hpp new file mode 100644 index 00000000..bb2a6d43 --- /dev/null +++ b/external/kokkos-kernels/KokkosKernels_SimpleUtils.hpp @@ -0,0 +1,412 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ +#ifndef _KOKKOSKERNELS_SIMPLEUTILS_HPP +#define _KOKKOSKERNELS_SIMPLEUTILS_HPP +#include "Kokkos_Core.hpp" +#include "Kokkos_ArithTraits.hpp" +#include + +#define KOKKOSKERNELS_MACRO_MIN(x, y) ((x) < (y) ? (x) : (y)) +#define KOKKOSKERNELS_MACRO_MAX(x, y) ((x) < (y) ? (y) : (x)) +#define KOKKOSKERNELS_MACRO_ABS(x) \ + Kokkos::Details::ArithTraits::type>::abs(x) + +namespace KokkosKernels { + +namespace Impl { + +template +class SquareRootFunctor { + public: + typedef typename ViewType::execution_space execution_space; + typedef typename ViewType::size_type size_type; + + SquareRootFunctor(const ViewType &theView) : theView_(theView) {} + + KOKKOS_INLINE_FUNCTION void operator()(const size_type i) const { + typedef typename ViewType::value_type value_type; + theView_(i) = Kokkos::Details::ArithTraits::sqrt(theView_(i)); + } + + private: + ViewType theView_; +}; + +template +struct ExclusiveParallelPrefixSum { + typedef typename view_t::value_type idx; + view_t array_sum; + ExclusiveParallelPrefixSum(view_t arr_) : array_sum(arr_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, size_t &update, const bool final) const { + idx val = array_sum(ii); + if (final) { + array_sum(ii) = idx(update); + } + update += val; + } +}; + +template +struct InclusiveParallelPrefixSum { + typedef typename array_type::value_type idx; + array_type array_sum; + InclusiveParallelPrefixSum(array_type arr_) : array_sum(arr_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, size_t &update, const bool final) const { + update += array_sum(ii); + if (final) { + array_sum(ii) = idx(update); + } + } +}; + +/*** + * \brief Function performs the exclusive parallel prefix sum. That is each + * entry holds the sum until itself. \param num_elements: size of the array + * \param arr: the array for which the prefix sum will be performed. + */ +template +inline void kk_exclusive_parallel_prefix_sum( + typename view_t::value_type num_elements, view_t arr) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_scan("KokkosKernels::Common::PrefixSum", + my_exec_space(0, num_elements), + ExclusiveParallelPrefixSum(arr)); +} + +/*** + * \brief Function performs the inclusive parallel prefix sum. That is each + * entry holds the sum until itself including itself. \param num_elements: size + * of the array \param arr: the array for which the prefix sum will be + * performed. + */ +template +void kk_inclusive_parallel_prefix_sum( + typename forward_array_type::value_type num_elements, + forward_array_type arr) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_scan("KokkosKernels::Common::PrefixSum", + my_exec_space(0, num_elements), + InclusiveParallelPrefixSum(arr)); +} + +template +struct ReductionFunctor { + view_t array_sum; + ReductionFunctor(view_t arr_) : array_sum(arr_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, typename view_t::value_type &update) const { + update += array_sum(ii); + } +}; + +template +struct ReductionFunctor2 { + view_t array_sum; + ReductionFunctor2(view_t arr_) : array_sum(arr_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, size_t &update) const { + update += array_sum(ii); + } +}; + +template +struct DiffReductionFunctor { + view_t array_begins; + view2_t array_ends; + DiffReductionFunctor(view_t begins, view2_t ends) + : array_begins(begins), array_ends(ends) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, + typename view_t::non_const_value_type &update) const { + update += (array_ends(ii) - array_begins(ii)); + } +}; + +template +inline void kk_reduce_diff_view( + size_t num_elements, view_t smaller, view2_t bigger, + typename view_t::non_const_value_type &reduction) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_reduce( + "KokkosKernels::Common::ReduceDiffView", my_exec_space(0, num_elements), + DiffReductionFunctor(smaller, bigger), reduction); +} + +template +struct DiffReductionFunctorP { + const it *array_begins; + const it *array_ends; + DiffReductionFunctorP(const it *begins, const it *ends) + : array_begins(begins), array_ends(ends) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t ii, it &update) const { + update += (array_ends[ii] - array_begins[ii]); + } +}; + +template +inline void kkp_reduce_diff_view(const size_t num_elements, const it *smaller, + const it *bigger, it &reduction) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_reduce( + "KokkosKernels::Common::ReduceDiffView", my_exec_space(0, num_elements), + DiffReductionFunctorP(smaller, bigger), reduction); +} + +/*** + * \brief Function performs the a reduction + * until itself. + * \param num_elements: size of the array + * \param arr: the array for which the prefix sum will be performed. + */ +template +inline void kk_reduce_view(size_t num_elements, view_t arr, + typename view_t::value_type &reduction) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_reduce("KokkosKernels::Common::ReduceView", + my_exec_space(0, num_elements), + ReductionFunctor(arr), reduction); +} + +template +inline void kk_reduce_view2(size_t num_elements, view_t arr, + size_t &reduction) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_reduce("KokkosKernels::Common::ReduceView2", + my_exec_space(0, num_elements), + ReductionFunctor2(arr), reduction); +} + +template ::mag_type> +struct IsIdenticalFunctor { + view_type1 view1; + view_type2 view2; + eps_type eps; + + IsIdenticalFunctor(view_type1 view1_, view_type2 view2_, eps_type eps_) + : view1(view1_), view2(view2_), eps(eps_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t &i, size_t &is_equal) const { + typedef typename view_type2::non_const_value_type val_type; + typedef Kokkos::Details::ArithTraits KAT; + typedef typename KAT::mag_type mag_type; + const mag_type val_diff = KAT::abs(view1(i) - view2(i)); + + if (val_diff > eps) { + is_equal += 1; + } + } +}; + +template +bool kk_is_identical_view(view_type1 view1, view_type2 view2, eps_type eps) { + if (view1.extent(0) != view2.extent(0)) { + return false; + } + + size_t num_elements = view1.extent(0); + + typedef Kokkos::RangePolicy my_exec_space; + size_t issame = 0; + Kokkos::parallel_reduce( + "KokkosKernels::Common::IsIdenticalView", my_exec_space(0, num_elements), + IsIdenticalFunctor(view1, view2, eps), + issame); + MyExecSpace().fence(); + if (issame > 0) { + return false; + } else { + return true; + } +} + +template ::mag_type> +struct IsRelativelyIdenticalFunctor { + view_type1 view1; + view_type2 view2; + eps_type eps; + + IsRelativelyIdenticalFunctor(view_type1 view1_, view_type2 view2_, + eps_type eps_) + : view1(view1_), view2(view2_), eps(eps_) {} + + KOKKOS_INLINE_FUNCTION + void operator()(const size_t &i, size_t &is_equal) const { + typedef typename view_type2::non_const_value_type val_type; + typedef Kokkos::Details::ArithTraits KAT; + typedef typename KAT::mag_type mag_type; + typedef Kokkos::Details::ArithTraits KATM; + + mag_type val_diff = KAT::abs(view1(i) - view2(i)); + if (KAT::abs(view1(i)) > KATM::zero() && + KAT::abs(view2(i)) > KATM::zero()) { + val_diff = val_diff / KAT::abs(view2(i)); + } + + if (val_diff > eps) { + is_equal += 1; + } + } +}; + +template +bool kk_is_relatively_identical_view(view_type1 view1, view_type2 view2, + eps_type eps) { + if (view1.extent(0) != view2.extent(0)) { + return false; + } + + size_t num_elements = view1.extent(0); + + typedef Kokkos::RangePolicy my_exec_space; + size_t issame = 0; + Kokkos::parallel_reduce( + "KokkosKernels::Common::IsRelativelyIdenticalView", + my_exec_space(0, num_elements), + IsRelativelyIdenticalFunctor( + view1, view2, eps), + issame); + MyExecSpace().fence(); + if (issame > 0) { + return false; + } else { + return true; + } +} + +template +struct ReduceMaxFunctor { + view_type view_to_reduce; + typedef typename view_type::non_const_value_type value_type; + const value_type min_val; + ReduceMaxFunctor(view_type view_to_reduce_) + : view_to_reduce(view_to_reduce_), + min_val((std::numeric_limits::lowest())) {} + KOKKOS_INLINE_FUNCTION + void operator()(const size_t &i, value_type &max_reduction) const { + value_type val = view_to_reduce(i); + if (max_reduction < val) { + max_reduction = val; + } + } + KOKKOS_INLINE_FUNCTION + void join(value_type &dst, const value_type &src) const { + if (dst < src) { + dst = src; + } + } + + KOKKOS_INLINE_FUNCTION + void init(value_type &dst) const { + // The identity under max is -Inf. + // Kokkos does not come with a portable way to access + // floating -point Inf and NaN. Trilinos does , however; + // see Kokkos :: ArithTraits in the Tpetra package. + dst = min_val; + } +}; + +template +void kk_view_reduce_max( + size_t num_elements, view_type view_to_reduce, + typename view_type::non_const_value_type &max_reduction) { + typedef Kokkos::RangePolicy my_exec_space; + Kokkos::parallel_reduce( + "KokkosKernels::Common::ReduceMax", my_exec_space(0, num_elements), + ReduceMaxFunctor(view_to_reduce), max_reduction); +} + +// xorshift hash/pseudorandom function (supported for 32- and 64-bit integer +// types only) +template +KOKKOS_FORCEINLINE_FUNCTION Value xorshiftHash(Value v) { + static_assert(std::is_unsigned::value, + "xorshiftHash: value must be an unsigned integer type"); + uint64_t x = v; + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + return std::is_same::value + ? static_cast((x * 2685821657736338717ULL - 1) >> 16) + : static_cast(x * 2685821657736338717ULL - 1); +} + +template +struct SequentialFillFunctor { + using size_type = typename V::size_type; + using val_type = typename V::non_const_value_type; + SequentialFillFunctor(const V &v_, val_type start_) : v(v_), start(start_) {} + KOKKOS_INLINE_FUNCTION void operator()(size_type i) const { + v(i) = start + (val_type)i; + } + V v; + val_type start; +}; + +template +void sequential_fill(const V &v, typename V::non_const_value_type start = 0) { + Kokkos::parallel_for( + Kokkos::RangePolicy(0, v.extent(0)), + SequentialFillFunctor(v, start)); +} + +} // namespace Impl +} // namespace KokkosKernels + +#endif diff --git a/kharma/implicit/KokkosKernels_config.h b/external/kokkos-kernels/KokkosKernels_config.h similarity index 100% rename from kharma/implicit/KokkosKernels_config.h rename to external/kokkos-kernels/KokkosKernels_config.h diff --git a/external/kokkos-kernels/Kokkos_ArithTraits.hpp b/external/kokkos-kernels/Kokkos_ArithTraits.hpp new file mode 100644 index 00000000..f6fc1b47 --- /dev/null +++ b/external/kokkos-kernels/Kokkos_ArithTraits.hpp @@ -0,0 +1,2083 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + +#ifndef KOKKOS_ARITHTRAITS_HPP +#define KOKKOS_ARITHTRAITS_HPP + +/// \file Kokkos_ArithTraits.hpp +/// \brief Declaration and definition of Kokkos::Details::ArithTraits + +#include "KokkosKernels_config.h" +#include +#include +#include +#include +#include "KokkosKernels_Half.hpp" + +#include + +#include +#include +#include +#include // std::complex +#include // std::numeric_limits +#ifdef __CUDACC__ +#include +#endif + +namespace { // anonymous + +/// \fn intPowImpl +/// \tparam IntType A built-in integer type. +/// \brief Implementation of intPowSigned and intPowUnsigned. +/// +/// \pre x != 0 +/// \pre y > 0 +/// +/// Use intPowSigned or intPowUnsigned for general y. +template +KOKKOS_FORCEINLINE_FUNCTION IntType intPowImpl(const IntType x, + const IntType y) { + // Recursion (unrolled into while loop): pow(x, 2y) = (x^y)^2 + IntType prod = x; + IntType y_cur = 1; + // If y == 1, then prod stays x. + while (y_cur < y) { + prod = prod * prod; + y_cur = y_cur << 1; + } + // abs(y - y_cur) < floor(log2(y)), so it won't hurt asymptotic run + // time to finish the remainder in a linear iteration. + if (y > y_cur) { + const IntType left = y - y_cur; + for (IntType k = 0; k < left; ++k) { + prod = prod * x; + } + } else if (y < y_cur) { + // There's probably a better way to do this in order to avoid the + // (expensive) integer division, but I'm not motivated to think of + // it at the moment. + const IntType left = y_cur - y; + for (IntType k = 0; k < left; ++k) { + prod = prod / x; + } + } + return prod; + + // y = 8: + // + // x,1 -> x^2,2 + // x^2,2 -> x^4,4 + // x^4,4 -> x^8,8 + // + // y = 9: + // + // x,1 -> x^2,2 + // x^2,2 -> x^4,4 + // x^4,4 -> x^8,8 + // + // y - y_cur is what's left over. Just do it one at a time. + // + // y = 3: + // x,1 -> x^2,2 + // x^2,2 -> x^4,4 +} + +// Warning free abs function for types where we don't know whether they are +// signed (like char) +template ::is_signed> +struct integer_abs { + static KOKKOS_INLINE_FUNCTION T abs(const T& val); +}; + +template +struct integer_abs { + static KOKKOS_INLINE_FUNCTION T abs(const T& x) { return x < 0 ? -x : x; } +}; + +template +struct integer_abs { + static KOKKOS_INLINE_FUNCTION T abs(const T& x) { return x; } +}; + +/// \fn intPowSigned +/// \tparam IntType A built-in signed integer type. +/// \brief Compute x raised to the power y. +/// +/// If the arguments are invalid (e.g., if x and y are both zero), the +/// result of this function is undefined. However, this function will +/// not throw an exception in that case. +template +KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if::is_signed, + IntType>::type + intPowSigned(const IntType x, const IntType y) { + // It's not entirely clear what to return if x and y are both zero. + // In the case of floating-point numbers, 0^0 is NaN. Here, though, + // I think it's safe to return 0. + if (x == 0) { + return 0; + } else if (y == 0) { + return 1; + } else if (y < 0) { + if (x == 1) { + return 1; + } else if (x == -1) { + return (y % 2 == 0) ? 1 : -1; + } else { + return 0; // round the fraction to zero + } + } + return intPowImpl(x, y); +} +template +KOKKOS_FORCEINLINE_FUNCTION + typename std::enable_if::is_signed, + IntType>::type + intPowSigned(const IntType x, const IntType y) { + // It's not entirely clear what to return if x and y are both zero. + // In the case of floating-point numbers, 0^0 is NaN. Here, though, + // I think it's safe to return 0. + if (x == 0) { + return 0; + } else if (y == 0) { + return 1; + } + return intPowImpl(x, y); +} + +/// \fn intPowUnsigned +/// \tparam IntType A built-in unsigned integer type. +/// \brief Compute x raised to the power y. +/// +/// If the arguments are invalid (e.g., if x and y are both zero), the +/// result of this function is undefined. However, this function will +/// not throw an exception in that case. +template +KOKKOS_FORCEINLINE_FUNCTION IntType intPowUnsigned(const IntType x, + const IntType y) { + // It's not entirely clear what to return if x and y are both zero. + // In the case of floating-point numbers, 0^0 is NaN. Here, though, + // I think it's safe to return 0. + if (x == 0) { + return 0; + } else if (y == 0) { + return 1; + } else { + return intPowImpl(x, y); + } +} + +// It might make sense to use special sqrt() approximations for +// integer arguments, like those presented on the following web site: +// +// http://www.azillionmonkeys.com/qed/sqroot.html#implementations +// +// Note that some of the implementations on the above page break ANSI +// C(++) aliasing rules (by assigning to the results of +// reinterpret_cast-ing between int and float). It's also just a +// performance optimization and not required for a reasonable +// implementation. + +} // namespace + +namespace Kokkos { +namespace Details { + +// Macro to automate the wrapping of Kokkos Mathematical Functions +// in the ArithTraits struct for real floating point types, hopefully +// this can be expanded to Kokkos::half_t and Kokkos::bhalf_t +#define KOKKOSKERNELS_ARITHTRAITS_REAL_FP(FUNC_QUAL) \ + static FUNC_QUAL val_type zero() { return static_cast(0); } \ + static FUNC_QUAL val_type one() { return static_cast(1); } \ + static FUNC_QUAL val_type min() { \ + return Kokkos::Experimental::finite_min::value; \ + } \ + static FUNC_QUAL val_type max() { \ + return Kokkos::Experimental::finite_max::value; \ + } \ + static FUNC_QUAL val_type infinity() { \ + return Kokkos::Experimental::infinity::value; \ + } \ + static FUNC_QUAL val_type nan() { \ + return Kokkos::Experimental::quiet_NaN::value; \ + } \ + static FUNC_QUAL mag_type epsilon() { \ + return Kokkos::Experimental::epsilon::value; \ + } \ + static FUNC_QUAL mag_type sfmin() { \ + return Kokkos::Experimental::norm_min::value; \ + } \ + static FUNC_QUAL int base() { \ + return Kokkos::Experimental::radix::value; \ + } \ + static FUNC_QUAL mag_type prec() { \ + return epsilon() * static_cast(base()); \ + } \ + static FUNC_QUAL int t() { \ + return Kokkos::Experimental::digits::value; \ + } \ + static FUNC_QUAL mag_type rnd() { return one(); } \ + static FUNC_QUAL int emin() { \ + return Kokkos::Experimental::min_exponent::value; \ + } \ + static FUNC_QUAL mag_type rmin() { \ + return Kokkos::Experimental::norm_min::value; \ + } \ + static FUNC_QUAL int emax() { \ + return Kokkos::Experimental::max_exponent::value; \ + } \ + static FUNC_QUAL mag_type rmax() { \ + return Kokkos::Experimental::finite_max::value; \ + } \ + \ + static FUNC_QUAL bool isInf(const val_type x) { return Kokkos::isinf(x); } \ + static FUNC_QUAL bool isNan(const val_type x) { return Kokkos::isnan(x); } \ + static FUNC_QUAL mag_type abs(const val_type x) { return Kokkos::abs(x); } \ + static FUNC_QUAL mag_type real(const val_type x) { return Kokkos::real(x); } \ + static FUNC_QUAL mag_type imag(const val_type x) { return Kokkos::imag(x); } \ + static FUNC_QUAL val_type conj(const val_type x) { return x; } \ + static FUNC_QUAL val_type pow(const val_type x, const val_type y) { \ + return Kokkos::pow(x, y); \ + } \ + static FUNC_QUAL val_type sqrt(const val_type x) { return Kokkos::sqrt(x); } \ + static FUNC_QUAL val_type cbrt(const val_type x) { return Kokkos::cbrt(x); } \ + static FUNC_QUAL val_type exp(const val_type x) { return Kokkos::exp(x); } \ + static FUNC_QUAL val_type log(const val_type x) { return Kokkos::log(x); } \ + static FUNC_QUAL val_type log10(const val_type x) { \ + return Kokkos::log10(x); \ + } \ + static FUNC_QUAL val_type sin(const val_type x) { return Kokkos::sin(x); } \ + static FUNC_QUAL val_type cos(const val_type x) { return Kokkos::cos(x); } \ + static FUNC_QUAL val_type tan(const val_type x) { return Kokkos::tan(x); } \ + static FUNC_QUAL val_type sinh(const val_type x) { return Kokkos::sinh(x); } \ + static FUNC_QUAL val_type cosh(const val_type x) { return Kokkos::cosh(x); } \ + static FUNC_QUAL val_type tanh(const val_type x) { return Kokkos::tanh(x); } \ + static FUNC_QUAL val_type asin(const val_type x) { return Kokkos::asin(x); } \ + static FUNC_QUAL val_type acos(const val_type x) { return Kokkos::acos(x); } \ + static FUNC_QUAL val_type atan(const val_type x) { return Kokkos::atan(x); } \ + \ + static FUNC_QUAL bool isnaninf(const val_type x) { \ + return isNan(x) || isInf(x); \ + } \ + static FUNC_QUAL magnitudeType magnitude(const val_type x) { \ + return abs(x); \ + } \ + static FUNC_QUAL val_type conjugate(const val_type x) { return conj(x); } \ + static FUNC_QUAL val_type squareroot(const val_type x) { return sqrt(x); } \ + static FUNC_QUAL mag_type eps() { return epsilon(); } + +#define KOKKOSKERNELS_ARITHTRAITS_CMPLX_FP(FUNC_QUAL) \ + \ + static constexpr bool is_specialized = true; \ + static constexpr bool is_signed = true; \ + static constexpr bool is_integer = false; \ + static constexpr bool is_exact = false; \ + static constexpr bool is_complex = true; \ + static constexpr bool has_infinity = true; \ + \ + using magnitudeType = mag_type; \ + using halfPrecision = \ + ::Kokkos::complex::halfPrecision>; \ + using doublePrecision = \ + ::Kokkos::complex::doublePrecision>; \ + \ + static constexpr bool isComplex = true; \ + static constexpr bool isOrdinal = false; \ + static constexpr bool isComparable = false; \ + static constexpr bool hasMachineParameters = \ + ArithTraits::hasMachineParameters; \ + \ + static FUNC_QUAL val_type zero() { \ + return val_type(ArithTraits::zero(), \ + ArithTraits::zero()); \ + } \ + static FUNC_QUAL val_type one() { \ + return val_type(ArithTraits::one(), \ + ArithTraits::zero()); \ + } \ + static FUNC_QUAL val_type min() { \ + return val_type(ArithTraits::min(), \ + ArithTraits::min()); \ + } \ + static FUNC_QUAL val_type max() { \ + return val_type(ArithTraits::max(), \ + ArithTraits::max()); \ + } \ + static FUNC_QUAL val_type infinity() { \ + return val_type(ArithTraits::infinity(), \ + ArithTraits::infinity()); \ + } \ + static FUNC_QUAL val_type nan() { \ + return val_type(ArithTraits::nan(), \ + ArithTraits::nan()); \ + } \ + static FUNC_QUAL mag_type epsilon() { \ + return ArithTraits::epsilon(); \ + } \ + static FUNC_QUAL mag_type sfmin() { return ArithTraits::sfmin(); } \ + static FUNC_QUAL int base() { return ArithTraits::base(); } \ + static FUNC_QUAL mag_type prec() { return ArithTraits::prec(); } \ + static FUNC_QUAL int t() { return ArithTraits::t(); } \ + static FUNC_QUAL mag_type rnd() { return ArithTraits::rnd(); } \ + static FUNC_QUAL int emin() { return ArithTraits::emin(); } \ + static FUNC_QUAL mag_type rmin() { return ArithTraits::rmin(); } \ + static FUNC_QUAL int emax() { return ArithTraits::emax(); } \ + static FUNC_QUAL mag_type rmax() { return ArithTraits::rmax(); } \ + static FUNC_QUAL bool isInf(const val_type x) { \ + return ArithTraits::isInf(x.real()) || \ + ArithTraits::isInf(x.imag()); \ + } \ + static FUNC_QUAL bool isNan(const val_type x) { \ + return ArithTraits::isNan(x.real()) || \ + ArithTraits::isNan(x.imag()); \ + } \ + static FUNC_QUAL mag_type abs(const val_type x) { return ::Kokkos::abs(x); } \ + static FUNC_QUAL mag_type real(const val_type x) { return x.real(); } \ + static FUNC_QUAL mag_type imag(const val_type x) { return x.imag(); } \ + static FUNC_QUAL val_type conj(const val_type x) { \ + return ::Kokkos::conj(x); \ + } \ + static FUNC_QUAL val_type pow(const val_type x, const val_type y) { \ + return Kokkos::pow(x, y); \ + } \ + static FUNC_QUAL val_type pow(const val_type x, const mag_type y) { \ + return Kokkos::pow(x, y); \ + } \ + static FUNC_QUAL val_type pow(const mag_type x, const val_type y) { \ + return Kokkos::pow(x, y); \ + } \ + static FUNC_QUAL val_type sqrt(const val_type x) { \ + return ::Kokkos::sqrt(x); \ + } \ + static FUNC_QUAL val_type exp(const val_type x) { return Kokkos::exp(x); } \ + static FUNC_QUAL val_type log(const val_type x) { return Kokkos::log(x); } \ + static FUNC_QUAL val_type log10(const val_type x) { \ + return Kokkos::log10(x); \ + } \ + static FUNC_QUAL val_type sin(const val_type x) { return Kokkos::sin(x); } \ + static FUNC_QUAL val_type cos(const val_type x) { return Kokkos::cos(x); } \ + static FUNC_QUAL val_type tan(const val_type x) { return Kokkos::tan(x); } \ + static FUNC_QUAL val_type sinh(const val_type x) { return Kokkos::sinh(x); } \ + static FUNC_QUAL val_type cosh(const val_type x) { return Kokkos::cosh(x); } \ + static FUNC_QUAL val_type tanh(const val_type x) { return Kokkos::tanh(x); } \ + static FUNC_QUAL val_type asin(const val_type x) { return Kokkos::asin(x); } \ + static FUNC_QUAL val_type acos(const val_type x) { return Kokkos::acos(x); } \ + static FUNC_QUAL val_type atan(const val_type x) { return Kokkos::atan(x); } \ + static FUNC_QUAL bool isnaninf(const val_type& x) { \ + return isNan(x) || isInf(x); \ + } \ + static FUNC_QUAL mag_type magnitude(const val_type x) { return abs(x); } \ + static FUNC_QUAL val_type conjugate(const val_type x) { return conj(x); } \ + static FUNC_QUAL val_type squareroot(const val_type x) { return sqrt(x); } \ + static FUNC_QUAL mag_type eps() { return epsilon(); } + +template +static KOKKOS_FUNCTION + typename std::enable_if::is_signed, + val_type>::type + KokkosKernelsAbs(const val_type x) { + return Kokkos::abs(x); +} + +template +static KOKKOS_FUNCTION + typename std::enable_if::is_signed, + val_type>::type + KokkosKernelsAbs(const val_type x) { + return x; +} + +template +static KOKKOS_FUNCTION + typename std::enable_if::is_signed, + val_type>::type + KokkosKernelsNan() { + return -1; +} + +template +static KOKKOS_FUNCTION + typename std::enable_if::is_signed, + val_type>::type + KokkosKernelsNan() { + return Kokkos::Experimental::finite_max::value; +} + +#define KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() \ + \ + static constexpr bool is_specialized = true; \ + static constexpr bool is_integer = true; \ + static constexpr bool is_exact = true; \ + static constexpr bool is_complex = false; \ + static constexpr bool has_infinity = false; \ + \ + using magnitudeType = mag_type; \ + using halfPrecision = val_type; \ + using doublePrecision = val_type; \ + \ + static constexpr bool isComplex = false; \ + static constexpr bool isOrdinal = true; \ + static constexpr bool isComparable = true; \ + static constexpr bool hasMachineParameters = false; \ + \ + static KOKKOS_FUNCTION val_type zero() { return static_cast(0); } \ + static KOKKOS_FUNCTION val_type one() { return static_cast(1); } \ + static KOKKOS_FUNCTION val_type min() { \ + return Kokkos::Experimental::finite_min::value; \ + } \ + static KOKKOS_FUNCTION val_type max() { \ + return Kokkos::Experimental::finite_max::value; \ + } \ + static KOKKOS_FUNCTION val_type infinity() { \ + return static_cast(0); \ + } \ + static KOKKOS_FUNCTION val_type nan() { \ + return KokkosKernelsNan(); \ + } \ + static KOKKOS_FUNCTION bool isInf(const val_type) { return false; } \ + static KOKKOS_FUNCTION bool isNan(const val_type) { return false; } \ + static KOKKOS_FUNCTION mag_type abs(const val_type x) { \ + return KokkosKernelsAbs(x); \ + } \ + static KOKKOS_FUNCTION mag_type real(const val_type x) { \ + return Kokkos::real(x); \ + } \ + static KOKKOS_FUNCTION mag_type imag(const val_type) { return zero(); } \ + static KOKKOS_FUNCTION val_type conj(const val_type x) { return x; } \ + static KOKKOS_FUNCTION val_type pow(const val_type x, const val_type y) { \ + return Kokkos::pow(x, y); \ + } \ + static KOKKOS_FUNCTION val_type sqrt(const val_type x) { \ + return static_cast(Kokkos::sqrt(abs(x))); \ + } \ + static KOKKOS_FUNCTION val_type cbrt(const val_type x) { \ + return static_cast(Kokkos::cbrt(abs(x))); \ + } \ + static KOKKOS_FUNCTION val_type exp(const val_type x) { \ + return static_cast(Kokkos::exp(abs(x))); \ + } \ + static KOKKOS_FUNCTION val_type log(const val_type x) { \ + return static_cast(Kokkos::log(abs(x))); \ + } \ + static KOKKOS_FUNCTION val_type log10(const val_type x) { \ + return static_cast(Kokkos::log10(abs(x))); \ + } \ + static KOKKOS_FUNCTION mag_type epsilon() { return zero(); } \ + static KOKKOS_FUNCTION magnitudeType magnitude(const val_type x) { \ + return abs(x); \ + } \ + static KOKKOS_FUNCTION val_type conjugate(const val_type x) { \ + return conj(x); \ + } \ + static KOKKOS_FUNCTION bool isnaninf(const val_type) { return false; } \ + static KOKKOS_FUNCTION val_type squareroot(const val_type x) { \ + return sqrt(x); \ + } + +/// \class ArithTraits +/// \brief Traits class for arithmetic on type T. +/// \tparam T "Scalar" type of interest +/// +/// This is a traits class for the "arithmetic" type T. "Arithmetic +/// types" include built-in signed and unsigned integer types, +/// floating-point types, complex-valued types, and anything else that +/// looks like these. This class is useful for implementing numerical +/// algorithms that are generic on the data type. You may also use +/// this class to query attributes of T, like whether it is signed or +/// complex, or its precision. +/// +/// We really did not want to implement this class or expose it to +/// users. It would be much better to use existing traits classes +/// like std::numeric_limits. We decided to implement and expose this +/// class for the following reasons: +///
    +///
  1. std::numeric_limits class methods cannot be used in CUDA +/// device functions, since they themselves are not device +/// functions
  2. +///
  3. Existing traits classes like std::numeric_limits do not +/// provide enough information to implement algorithms that are +/// agnostic of whether T is real-valued or complex-valued.
  4. +///
+/// +/// All class methods must be suitable for parallel kernels, if the +/// type T itself is suitable for parallel kernels. In particular, +/// specializations for types T that make sense to use on a CUDA +/// device must mark all class methods as device (and host) functions, +/// using the KOKKOS_FORCEINLINE_FUNCTION macro. All class methods must be +/// callable both inside and outside a parallel kernel (for CUDA, this +/// means they must be marked as both device and host functions). +/// +/// \section Kokkos_ArithTraits_compat Compatibility +/// +/// Whenever possible, class methods in ArithTraits use the same names +/// as their equivalents in the C++ Standard Library. If this was not +/// possible, for example with isInf and isNan, we explain why in +/// their documentation. +/// +/// This class has redundant typedefs and methods in order to maintain +/// backwards compatibility with Teuchos::ScalarTraits, while +/// preferring forwards (partial) compatibility with +/// std::numeric_limits. Users should prefer typedefs, \c bool +/// constants, and class methods compatible with std::numeric_limits, +/// to those from Teuchos::ScalarTraits. The latter may go away at +/// any time. Furthermore, Teuchos::ScalarTraits contains methods +/// that do not make sense for use as parallel device functions, in +/// particular those relating to pseudorandom number generation that +/// refer to hidden state, so we will never include all class methods +/// from Teuchos::ScalarTraits in ArithTraits. +/// +/// \section Kokkos_ArithTraits_unsupp Unsupported types on CUDA devices +/// +/// CUDA does not support long double or std::complex in device +/// functions. ArithTraits does have specializations for these types, +/// but the class methods therein are not marked as device functions. +/// +/// \section Kokkos_ArithTraits_whyNotC99 What about C99 integer types? +/// +/// C99 and C++11 include typedefs int${N}_t and uint${N}_t, where N +/// is the number of bits in the integer. These typedefs are useful +/// because they make the length of the type explicit. Users are +/// welcome to use these types as the template parameter of +/// ArithTraits. +/// +/// We chose not to use these types when defining full +/// specializations of ArithTraits. This is because the C99 integer +/// types are typedefs, not types in themselves. This makes it +/// impossible to avoid duplicate or missing full specializations of +/// ArithTraits. For example, on my Mac, for CUDA 5.5, gcc 4.2.1, and +/// Clang 3.2, int64_t is a typedef of long long, +/// but long long and long are separate types, even +/// though they have the same length (64 bits). In contrast, on +/// Windows (even Win64), long is a 32-bit type (but a +/// distinct type from int), and long long is a +/// 64-bit type. Thus, if we define full specializations of +/// ArithTraits using only the C99 integer types, we will be +/// missing a specialization for long on at least one +/// platform. +/// +/// Rather than trouble ourselves with trying to figure this out for +/// each platform, we decided to provide specializations only for the +/// integer types in the C89 and C++03 language standards. This +/// includes signed and unsigned versions of char, +/// short, int, and long. We also include +/// long long if your platform supports it. We may thus have +/// left out some C99 integer type, but this is only possible if the +/// C89 / C++03 integer types do not have complete coverage of all +/// powers of two bits from 8 up to the longest provided length (e.g., +/// 64 on a 64-bit system). On all platforms I have encountered, +/// char has 8 bits and short has 16 bits, so I am +/// not worried about missing specializations for int16_t or +/// uint16_t. If you should find that either of these +/// specializations are missing, though, please let us know. +/// +/// Note that char, signed char, and unsigned +/// char are distinct types, whether char is signed or +/// unsigned. (The language standards do not specify whether +/// char is signed or unsigned.) That is, char is +/// not a typedef of signed char or unsigned +/// char. This is why we provide full specializations of +/// ArithTraits for each of these types. Interestingly enough, on my +/// system, char and int8_t are different types, but +/// signed char and int8_t are the same. +/// +/// \section Kokkos_ArithTraits_impl Implementation notes +/// +/// This section contains notes to developers who which to add a +/// partial specialization of this class for a new type T. If you +/// decide to write a default templated implementation, it must not +/// declare any methods as device functions. This ensures correct +/// behavior for arbitrary T, but does require specializations for +/// common types like T = float and double, as well as for other types +/// T that make sense to use on a CUDA device. +template +class ArithTraits { + public: + /// \brief A type that acts like T and works with Kokkos. + /// + /// This is usually just an alias for T. However, some types T do + /// not work well with Kokkos. In that case, we use a mostly + /// equivalent type here. For example, ArithTraits + /// >::val_type is Kokkos::complex. + using val_type = T; + /// \brief The type of the magnitude (absolute value) of T. + /// + /// We define this as the type returned by abs() in this class. If + /// T is real (not complex), then \c val_type and \c mag_type are + /// usually the same. If T is std::complex for some R, + /// then R and \c mag_type are usually the same. + using mag_type = T; + + //! Whether ArithTraits has a specialization for T. + static constexpr bool is_specialized = false; + //! Whether T is a signed type (has negative values). + static constexpr bool is_signed = false; + //! Whether T is an integer type. + static constexpr bool is_integer = false; + /// \brief Whether T "uses exact representations." + /// + /// The opposite of is_exact is "is approximate," that is, "may + /// commit rounding error." + static constexpr bool is_exact = false; + //! Whether T is a complex-valued type. + static constexpr bool is_complex = false; + + /// \brief Whether x is Inf. + /// + /// This can only be true for floating-point types T that support + /// Inf. If T is a complex type, we say that a T instance x is Inf + /// if and only if isinf(real(x)) || isinf(imag(x)). + /// + /// Unfortunately we can't call this "isinf" (the equivalent C99 + /// function), because CUDA appears to implement that function using + /// a macro, rather than using a function (as C++11 requires). + static KOKKOS_FUNCTION bool isInf(const T& x); + + /// \brief Whether x is NaN (not a number). + /// + /// This can only be true for floating-point types T that support + /// NaN. If T is a complex type, we say that a T instance x is NaN + /// if and only if isNan(real(x)) || isNan(imag(x)). + /// + /// Unfortunately we can't call this "isnan" (the equivalent C99 + /// function), because CUDA appears to implement that function using + /// a macro, rather than using a function (as C++11 requires). + static KOKKOS_FUNCTION bool isNan(const T& x); + + //! The absolute value (magnitude) of x. + static KOKKOS_FUNCTION mag_type abs(const T& x); + + //! The zero value of T; the arithmetic identity. + static KOKKOS_FUNCTION T zero(); + + //! The one value of T; the multiplicative identity. + static KOKKOS_FUNCTION T one(); + + /// \brief True if this type T is capable of representing the + /// positive infinity as a distinct special value, as with + /// std::numeric_limits::has_infinity. + static constexpr bool has_infinity = false; + + /// \brief Returns the special value "positive infinity", as + /// represented by the floating-point type T. Only meaningful if + /// KokkosArithTraits::has_infinity == true. Provides same + /// functionality as std::numeric_limits::infinity(). + /// + /// \note Would have liked to mark it as constexpr but then would + /// not be able to provide the specialization for std::complex + /// since its constructor only becomes constexpr with C++14. + static KOKKOS_FUNCTION T infinity(); + + /// \brief The minimum possible value of T. + /// + /// If T is a real floating-point type, then this is the minimum + /// positive value, as with std::numeric_limits::min(). + static KOKKOS_FUNCTION T min(); + + //! The maximum possible value of T. + static KOKKOS_FUNCTION T max(); + + /// \brief The real part of x. + /// + /// If \c is_complex is false, then this just returns x. + static KOKKOS_FUNCTION mag_type real(const T& x); + + /// \brief The imaginary part of x. + /// + /// If \c is_complex is false, then this just returns zero(). + static KOKKOS_FUNCTION mag_type imag(const T&); + + /// \brief The complex conjugate of x. + /// + /// If \c is_complex is false, then this just returns x. + static KOKKOS_FUNCTION T conj(const T&); + + //! x raised to the power y. + static KOKKOS_FUNCTION T pow(const T& x, const T& y); + + /// \brief The square root of x. + /// + /// If T is an integer type, this is the floor of the square root. + /// If T is a complex-valued type, then this method returns the + /// principal branch of the square root. + /// + /// If T is real-valued and x is negative, the result of the square + /// root is undefined in general. (CUDA does not allow throwing + /// exceptions in device functions.) Implementations should return + /// NaN if the type T supports this. Of course, in that case, the + /// square of the result will not equal x. + static KOKKOS_FUNCTION T sqrt(const T& x); + + /// \brief The cubic root of x. + /// + /// If T is an integer type, this is the floor of the cubic root. + /// If T is a complex-valued type, then this method returns the + /// principal branch of the cubic root. + /// + /// If T is real-valued and x is negative, the result of the cubic + /// root is undefined in general. (CUDA does not allow throwing + /// exceptions in device functions.) Implementations should return + /// NaN if the type T supports this. Of course, in that case, the + /// cubic of the result will not equal x. + static KOKKOS_FUNCTION T cbrt(const T& x); + + /// \brief The natural (base e) exponential function of x. + /// + /// If T is an integer type, this is the floor of the exponential + /// function. If T is a complex-valued type, then this method + /// returns \f$e^{x+iy} = e^x ( cos(y) + i sin(y) )\f$. + /// + static KOKKOS_FUNCTION T exp(const T& x); + + /// \brief The natural (base e) logarithm of x. + /// + /// If T is an integer type, this is the floor of the logarithm. If + /// T is a complex-valued type, then this method returns the + /// principal branch of the logarithm. + /// + /// If T is real-valued and x is negative, the result of the + /// logarithm is undefined in general. (CUDA does not allow + /// throwing exceptions in device functions.) Implementations + /// should return NaN if the type T supports this. Of course, in + /// that case, if y is the result, \f$e^y\f$ will not equal x. + static KOKKOS_FUNCTION T log(const T& x); + + /// \brief The base ten logarithm of the input. + /// + /// If T is an integer type, this is the floor of the logarithm. If + /// T is a complex-valued type, then this method returns the + /// principal branch of the logarithm. + /// + /// If T is real-valued and x is negative, the result of the + /// logarithm is undefined in general. (CUDA does not allow + /// throwing exceptions in device functions.) Implementations + /// should return NaN if the type T supports this. Of course, in + /// that case, if y is the result, \f$10^y\f$ will not equal x. + static KOKKOS_FUNCTION T log10(const T& x); + + /// Trigonometric and hyperbolic functions are not available + /// for integer types. This is because asin(sin(x)) is not x + /// when x is integer with a rounding error. + /// + /// KJ: log, exp also has this problem. We probably need to + /// disable them for integer types instead of providing + /// functionality with floor. + + /// \brief The sin function of x + /// + static KOKKOS_FUNCTION T sin(const T& x); + + /// \brief The cos function of x + /// + static KOKKOS_FUNCTION T cos(const T& x); + + /// \brief The tan function of x + /// + static KOKKOS_FUNCTION T tan(const T& x); + + /// \brief The sin hyperbolic function of x + /// + static KOKKOS_FUNCTION T sinh(const T& x); + + /// \brief The cos hyperbolic function of x + /// + static KOKKOS_FUNCTION T cosh(const T& x); + + /// \brief The tan hyperbolic function of x + /// + static KOKKOS_FUNCTION T tanh(const T& x); + + /// \brief The asin function of x + /// + static KOKKOS_FUNCTION T asin(const T& x); + + /// \brief The acos function of x + /// + static KOKKOS_FUNCTION T acos(const T& x); + + /// \brief The atan function of x + /// + static KOKKOS_FUNCTION T atan(const T& x); + + /// \brief Return a silent NaN, if appropriate for T. + /// + /// If T does not implement a silent NaN, the return value is + /// undefined, but calling this method is still allowed. + static KOKKOS_FUNCTION T nan(); + + /// \brief Machine epsilon. + /// + /// If T is an integer type (std::numeric_traits::is_exact is + /// true), then epsilon() returns 0. Otherwise, if T is a + /// floating-point type, it returns machine epsilon that T. + static KOKKOS_FUNCTION mag_type epsilon(); + + //@{ + /// \name Traits defined for backwards compatibility with + /// Teuchos::ScalarTraits + /// + /// All of the typedefs, \c bool constants, and class methods in + /// this section are defined in order that one may replace most uses + /// of Teuchos::ScalarTraits with ArithTraits. Users who do not + /// have this backwards compatibility requirement should prefer + /// equivalents in other sections. Those class methods which have + /// the same name and meaning in both Teuchos::ScalarTraits and this + /// class, such as log() and pow(), are not in this section. + + //! Same as mag_type; the type of the absolute value (magnitude) of T. + using magnitudeType = T; + + /// \brief The type with "half the precision" of T. + /// + /// This typedef only makes sense if T is a floating-point type. + using halfPrecision = T; + + /// \brief The type with "twice the the precision" of T. + /// + /// This typedef only makes sense if T is a floating-point type. + using doublePrecision = T; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = false; + + /// \brief True if this type T has floating-point parameters. + /// + /// This is true if and only if this specialization of ArithTraits + /// has "machine-specific" parameters eps(), sfmin(), base(), + /// prec(), t(), rnd(), emin(), rmin(), emax(), and rmax(), relating + /// to floating-point types. + static constexpr bool hasMachineParameters = false; + + //! Return relative machine precision. + static KOKKOS_FUNCTION mag_type eps(); + + //! Return safe minimum (sfmin), such that 1/sfmin does not overflow. + static KOKKOS_FUNCTION mag_type sfmin(); + + //! Return the base of the scalar type T. + static KOKKOS_FUNCTION int base(); + + //! Return eps*base. + static KOKKOS_FUNCTION mag_type prec(); + + //! Returns the number of (base) digits in the significand. + static KOKKOS_FUNCTION int t(); + + //! 1.0 when rounding occurs in addition, else 0.0. + static KOKKOS_FUNCTION mag_type rnd(); + + //! Returns the minimum exponent before (gradual) underflow. + static KOKKOS_FUNCTION int emin(); + + //! Returns the underflow threshold: base^(emin-1) + static KOKKOS_FUNCTION mag_type rmin(); + + //! Returns the largest exponent before overflow. + static KOKKOS_FUNCTION int emax(); + + //! Overflow theshold: (base^emax)*(1-eps) + static KOKKOS_FUNCTION mag_type rmax(); + + //! Same as abs(); return the magnitude of x. + static KOKKOS_FUNCTION magnitudeType magnitude(const T& x); + + //! Same as conj(); return the complex conjugate of x. + static KOKKOS_FUNCTION T conjugate(const T& x); + + /// \brief Whether x is (silent) NaN or Inf. + /// + /// This is the same as isNan(x) || isInf(x). + static KOKKOS_FUNCTION bool isnaninf(const T& x); + + /// \brief The string name of T. + /// + /// Note that this is not a device function. + static std::string name(); + + //! Same as sqrt(x); the square root of x. + static KOKKOS_FUNCTION T squareroot(const T& x); + //@} +}; + +// Since Kokkos::Experimental::half_t falls back to float, only define +// ArithTraits if half_t is a backend specialization +#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT +template <> +class ArithTraits { + public: + using val_type = Kokkos::Experimental::half_t; + using mag_type = val_type; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + + static constexpr bool has_infinity = true; + static KOKKOS_FUNCTION val_type infinity() { + return Kokkos::Experimental::cast_to_half( + Kokkos::Experimental::infinity::value); + } + + static KOKKOS_FUNCTION bool isInf(const val_type x) { +#ifndef __CUDA_ARCH__ + using std::isinf; +#endif + return isinf(Kokkos::Experimental::cast_from_half(x)); + } + static KOKKOS_FUNCTION bool isNan(const val_type x) { +#ifndef __CUDA_ARCH__ + using std::isnan; +#endif + return isnan(Kokkos::Experimental::cast_from_half(x)); + } + static KOKKOS_FUNCTION mag_type abs(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::abs(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type zero() { + return Kokkos::Experimental::cast_to_half(0.0); + } + static KOKKOS_FUNCTION val_type one() { + return Kokkos::Experimental::cast_to_half(1.0); + } + static KOKKOS_FUNCTION val_type min() { + return Kokkos::Experimental::cast_to_half(-KOKKOSKERNELS_IMPL_FP16_MAX); + } + static KOKKOS_FUNCTION val_type max() { + return Kokkos::Experimental::cast_to_half(KOKKOSKERNELS_IMPL_FP16_MAX); + } + static KOKKOS_FUNCTION mag_type real(const val_type x) { return x; } + static KOKKOS_FUNCTION mag_type imag(const val_type) { return zero(); } + static KOKKOS_FUNCTION val_type conj(const val_type x) { return x; } + static KOKKOS_FUNCTION val_type pow(const val_type x, const val_type y) { + return Kokkos::Experimental::cast_to_half( + Kokkos::pow(Kokkos::Experimental::cast_from_half(x), + Kokkos::Experimental::cast_from_half(y))); + } + static KOKKOS_FUNCTION val_type sqrt(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::sqrt(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type cbrt(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::cbrt(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type exp(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::exp(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type log(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::log(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type log10(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::log10(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type sin(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::sin(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type cos(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::cos(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type tan(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::tan(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type sinh(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::sinh(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type cosh(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::cosh(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type tanh(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::tanh(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type asin(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::asin(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type acos(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::acos(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION val_type atan(const val_type x) { + return Kokkos::Experimental::cast_to_half( + Kokkos::atan(Kokkos::Experimental::cast_from_half(x))); + } + static KOKKOS_FUNCTION mag_type epsilon() { + return Kokkos::Experimental::cast_to_half(KOKKOSKERNELS_IMPL_FP16_EPSILON); + } + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + // C++ doesn't have a standard "half-float" type. + using halfPrecision = val_type; + using doublePrecision = double; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + static KOKKOS_FUNCTION bool isnaninf(const val_type x) { + return isNan(x) || isInf(x); + } + static KOKKOS_FUNCTION magnitudeType magnitude(const val_type x) { + return abs(x); + } + static KOKKOS_FUNCTION val_type conjugate(const val_type x) { + return conj(x); + } + static std::string name() { return "half"; } + static KOKKOS_FUNCTION val_type squareroot(const val_type x) { + return sqrt(x); + } + static KOKKOS_FUNCTION val_type nan() { + return Kokkos::Experimental::cast_to_half( + Kokkos::Experimental::quiet_NaN::value); + } + static KOKKOS_FUNCTION mag_type eps() { return epsilon(); } + static KOKKOS_FUNCTION mag_type sfmin() { + return Kokkos::Experimental::cast_to_half(KOKKOSKERNELS_IMPL_FP16_MIN); + } + static KOKKOS_FUNCTION int base() { return KOKKOSKERNELS_IMPL_FP16_RADIX; } + // Use float to allow running on both host and device + static KOKKOS_FUNCTION float prec() { + float e = KOKKOSKERNELS_IMPL_FP16_EPSILON; + float b = (float)base(); + float r = e * b; + return r; + } + static KOKKOS_FUNCTION int t() { return KOKKOSKERNELS_IMPL_FP16_MANT_DIG; } + static KOKKOS_FUNCTION mag_type rnd() { return one(); } + static KOKKOS_FUNCTION int emin() { return KOKKOSKERNELS_IMPL_FP16_MIN_EXP; } + static KOKKOS_FUNCTION mag_type rmin() { + return Kokkos::Experimental::cast_to_half(KOKKOSKERNELS_IMPL_FP16_MIN); + } + static KOKKOS_FUNCTION int emax() { return KOKKOSKERNELS_IMPL_FP16_MAX_EXP; } + static KOKKOS_FUNCTION mag_type rmax() { + return Kokkos::Experimental::cast_to_half(KOKKOSKERNELS_IMPL_FP16_MAX); + } +}; +#endif // KOKKOS_HALF_T_IS_FLOAT && KOKKOS_ENABLE_CUDA_HALF + +// Since Kokkos::Experimental::bhalf_t falls back to float, only define +// ArithTraits if bhalf_t is a backend specialization +#if defined(KOKKOS_BHALF_T_IS_FLOAT) && !KOKKOS_BHALF_T_IS_FLOAT +template <> +class ArithTraits { + public: + using val_type = Kokkos::Experimental::bhalf_t; + using mag_type = val_type; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + + static constexpr bool has_infinity = true; + static KOKKOS_FUNCTION val_type infinity() { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::Experimental::infinity::value); + } + + static KOKKOS_FUNCTION bool isInf(const val_type x) { + return Kokkos::isinf(Kokkos::Experimental::cast_from_bhalf(x)); + } + static KOKKOS_FUNCTION bool isNan(const val_type x) { + return Kokkos::isnan(Kokkos::Experimental::cast_from_bhalf(x)); + } + static KOKKOS_FUNCTION mag_type abs(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::abs(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type zero() { + return Kokkos::Experimental::cast_to_bhalf(0.0F); + } + static KOKKOS_FUNCTION val_type one() { + return Kokkos::Experimental::cast_to_bhalf(1.0F); + } + static KOKKOS_FUNCTION val_type min() { + return Kokkos::Experimental::cast_to_bhalf(-KOKKOSKERNELS_IMPL_BF16_MAX); + } + static KOKKOS_FUNCTION val_type max() { + return Kokkos::Experimental::cast_to_bhalf(KOKKOSKERNELS_IMPL_BF16_MAX); + } + static KOKKOS_FUNCTION mag_type real(const val_type x) { return x; } + static KOKKOS_FUNCTION mag_type imag(const val_type) { + return Kokkos::Experimental::cast_to_bhalf(0.0F); + } + static KOKKOS_FUNCTION val_type conj(const val_type x) { return x; } + static KOKKOS_FUNCTION val_type pow(const val_type x, const val_type y) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::pow(Kokkos::Experimental::cast_from_bhalf(x), + Kokkos::Experimental::cast_from_bhalf(y))); + } + static KOKKOS_FUNCTION val_type sqrt(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::sqrt(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type cbrt(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::cbrt(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type exp(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::exp(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type log(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::log(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type log10(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::log10(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type sin(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::sin(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type cos(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::cos(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type tan(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::tan(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type sinh(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::sinh(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type cosh(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::cosh(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type tanh(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::tanh(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type asin(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::asin(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type acos(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::acos(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION val_type atan(const val_type x) { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::atan(Kokkos::Experimental::cast_from_bhalf(x))); + } + static KOKKOS_FUNCTION mag_type epsilon() { + // return ::pow(2, -KOKKOSKERNELS_IMPL_BF16_SIGNIFICAND_BITS); + return Kokkos::Experimental::cast_to_bhalf(KOKKOSKERNELS_IMPL_BF16_EPSILON); + } + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + // C++ doesn't have a standard "bhalf-float" type. + using bhalfPrecision = val_type; + using doublePrecision = double; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + static KOKKOS_FUNCTION bool isnaninf(const val_type x) { + return isNan(x) || isInf(x); + } + static KOKKOS_FUNCTION magnitudeType magnitude(const val_type x) { + return abs(x); + } + static KOKKOS_FUNCTION val_type conjugate(const val_type x) { + return conj(x); + } + static std::string name() { return "bhalf"; } + static KOKKOS_FUNCTION val_type squareroot(const val_type x) { + return sqrt(x); + } + static KOKKOS_FUNCTION val_type nan() { + return Kokkos::Experimental::cast_to_bhalf( + Kokkos::Experimental::quiet_NaN::value); + } + static KOKKOS_FUNCTION mag_type eps() { return epsilon(); } + static KOKKOS_FUNCTION mag_type sfmin() { + return Kokkos::Experimental::cast_to_bhalf(KOKKOSKERNELS_IMPL_BF16_MIN); + } + static KOKKOS_FUNCTION int base() { return KOKKOSKERNELS_IMPL_BF16_RADIX; } + // Use float to allow running on both host and device + static KOKKOS_FUNCTION float prec() { + float e = KOKKOSKERNELS_IMPL_BF16_EPSILON; + float b = (float)base(); + float r = e * b; + return r; + } + static KOKKOS_FUNCTION int t() { return KOKKOSKERNELS_IMPL_BF16_MANT_DIG; } + static KOKKOS_FUNCTION mag_type rnd() { return one(); } + static KOKKOS_FUNCTION int emin() { return KOKKOSKERNELS_IMPL_BF16_MIN_EXP; } + static KOKKOS_FUNCTION mag_type rmin() { + return Kokkos::Experimental::cast_to_bhalf(KOKKOSKERNELS_IMPL_BF16_MIN); + } + static KOKKOS_FUNCTION int emax() { return KOKKOSKERNELS_IMPL_BF16_MAX_EXP; } + static KOKKOS_FUNCTION mag_type rmax() { + return Kokkos::Experimental::cast_to_bhalf(KOKKOSKERNELS_IMPL_BF16_MAX); + } +}; +#endif // KOKKOS_BHALF_T_IS_FLOAT + +template <> +class ArithTraits { + public: + using val_type = float; + using mag_type = val_type; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + static constexpr bool has_infinity = true; + + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + using halfPrecision = float; // Should we switch to Kokkos::half_t + using doublePrecision = double; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + + static std::string name() { return "float"; } + + KOKKOSKERNELS_ARITHTRAITS_REAL_FP(KOKKOS_FUNCTION) +}; + +template <> +class ArithTraits { + public: + using val_type = double; + using mag_type = val_type; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + static constexpr bool has_infinity = true; + + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + using halfPrecision = float; +#if defined(__CUDA_ARCH__) + using doublePrecision = + double; // CUDA doesn't support long double, unfortunately +#elif defined(__HIP_DEVICE_COMPILE__) + using doublePrecision = + double; // HIP does not support long double unfortunately +#else + using doublePrecision = long double; +#endif // __CUDA_ARCH__ + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + + static std::string name() { return "double"; } + + KOKKOSKERNELS_ARITHTRAITS_REAL_FP(KOKKOS_FUNCTION) +}; + +// CUDA and HIP do not support long double in device functions, +// so none of the class methods in this specialization are marked +// as device functions. +template <> +class ArithTraits { + public: + using val_type = long double; + using mag_type = long double; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + static constexpr bool has_infinity = true; + + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + using halfPrecision = double; + // It might be appropriate to use QD's qd_real here. + // For now, long double is the most you get. + using doublePrecision = val_type; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + + static std::string name() { return "long double"; } + + KOKKOSKERNELS_ARITHTRAITS_REAL_FP() +}; // long double specialization + +#if defined(KOKKOS_ENABLE_LIBQUADMATH) +// CUDA does not support __float128 in device functions, so none of +// the class methods in this specialization are marked as device +// functions. +template <> +class ArithTraits<__float128> { + public: + using val_type = __float128; + using mag_type = val_type; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = false; + static constexpr bool has_infinity = true; + + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + using halfPrecision = double; + // Unfortunately, we can't rely on a standard __float256 type. + using doublePrecision = __float128; + + static constexpr bool isComplex = false; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = true; + static constexpr bool hasMachineParameters = true; + + static std::string name() { return "__float128"; } + + KOKKOSKERNELS_ARITHTRAITS_REAL_FP() +}; // __float128 specialization +#endif // KOKKOS_ENABLE_LIBQUADMATH + +template <> +class ArithTraits< ::Kokkos::complex > { + public: + using val_type = ::Kokkos::complex; + using mag_type = float; + + static std::string name() { return "Kokkos::complex"; } + + KOKKOSKERNELS_ARITHTRAITS_CMPLX_FP(KOKKOS_FUNCTION) +}; + +template <> +class ArithTraits< ::Kokkos::complex > { + public: + using val_type = ::Kokkos::complex; + using mag_type = double; + + static std::string name() { return "Kokkos::complex"; } + + KOKKOSKERNELS_ARITHTRAITS_CMPLX_FP(KOKKOS_FUNCTION) +}; + +/// \brief Partial specialization for std::complex. +/// +/// The C++ Standard Library (with C++03 at least) only allows +/// std::complex for RealFloatType = float, double, or +/// long double. +template +class ArithTraits > { + public: + //! Kokkos internally replaces std::complex with Kokkos::complex. + using val_type = ::Kokkos::complex; + using mag_type = RealFloatType; + + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool is_complex = true; + + static constexpr bool has_infinity = true; + static std::complex infinity() { + return std::complex(ArithTraits::infinity(), + ArithTraits::infinity()); + } + +#ifdef KOKKOS_ENABLE_SYCL + template + static bool isInf(const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST + using std::isinf; +#elif defined(KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL) + using sycl::isinf; +#endif + return isinf(real(x)) || isinf(imag(x)); + } + template <> + static bool isInf(const std::complex& x) { + Kokkos::abort("isInf not available for std::complex!\n"); + return true; + } +#else + static bool isInf(const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST + using std::isinf; +#endif + return isinf(real(x)) || isinf(imag(x)); + } +#endif +#ifdef KOKKOS_ENABLE_SYCL + template + static bool isNan(const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST + using std::isnan; +#elif defined(KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL) + using sycl::isnan; +#endif + return isnan(real(x)) || isnan(imag(x)); + } + template <> + static bool isNan(const std::complex& x) { + Kokkos::abort("isNan not available for std::complex!\n"); + return true; + } +#else + static bool isNan(const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST + using std::isnan; +#endif + return isnan(real(x)) || isnan(imag(x)); + } +#endif + static mag_type abs(const std::complex& x) { + return std::abs(x); + } + static std::complex zero() { + return std::complex(ArithTraits::zero(), + ArithTraits::zero()); + } + static std::complex one() { + return std::complex(ArithTraits::one(), + ArithTraits::zero()); + } + static std::complex min() { + return std::complex(ArithTraits::min(), + ArithTraits::zero()); + } + static std::complex max() { + return std::complex(ArithTraits::max(), + ArithTraits::zero()); + } + static mag_type real(const std::complex& x) { + return std::real(x); + } + static mag_type imag(const std::complex& x) { + return std::imag(x); + } + static std::complex conj( + const std::complex& x) { + return std::conj(x); + } + static std::complex pow(const std::complex& x, + const std::complex& y) { + // Fix for some weird gcc 4.2.1 inaccuracy. + if (y == one()) { + return x; + } else if (y == one() + one()) { + return x * x; + } else { + return std::pow(x, y); + } + } + static std::complex pow(const std::complex& x, + const RealFloatType& y) { + // Fix for some weird gcc 4.2.1 inaccuracy. + if (y == ArithTraits::one()) { + return x; + } else if (y == ArithTraits::one() + + ArithTraits::one()) { + return x * x; + } else { + return std::pow(x, y); + } + } + static std::complex sqrt( + const std::complex& x) { + return std::sqrt(x); + } + static std::complex cbrt( + const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::cbrt(x); +#else + return ::cbrt(x); +#endif + } + static std::complex exp(const std::complex& x) { + return std::exp(x); + } + static std::complex log(const std::complex& x) { + return std::log(x); + } + static std::complex log10( + const std::complex& x) { + return std::log10(x); + } + static std::complex sin(const std::complex& x) { + return std::sin(x); + } + static std::complex cos(const std::complex& x) { + return std::cos(x); + } + static std::complex tan(const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::tan(x); +#else + return std::tan(x); +#endif + } + static std::complex sinh( + const std::complex& x) { + return std::sinh(x); + } + static std::complex cosh( + const std::complex& x) { + return std::cosh(x); + } + static std::complex tanh( + const std::complex& x) { + return std::tanh(x); + } + static std::complex asin( + const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::asin(x); +#else + return ::asin(x); +#endif + } + static std::complex acos( + const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::acos(x); +#else + return ::acos(x); +#endif + } + static std::complex atan( + const std::complex& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + using sycl::atan; +#else + using std::atan; +#endif + return atan(x); + } + static std::complex nan() { + const mag_type mag_nan = ArithTraits::nan(); + return std::complex(mag_nan, mag_nan); + } + static mag_type epsilon() { return ArithTraits::epsilon(); } + + // Backwards compatibility with Teuchos::ScalarTraits. + using magnitudeType = mag_type; + using halfPrecision = + std::complex::halfPrecision>; + using doublePrecision = + std::complex::doublePrecision>; + + static constexpr bool isComplex = true; + static constexpr bool isOrdinal = false; + static constexpr bool isComparable = false; + static constexpr bool hasMachineParameters = true; + static bool isnaninf(const std::complex& x) { + return isNan(x) || isInf(x); + } + static mag_type magnitude(const std::complex& x) { + return abs(x); + } + static std::complex conjugate( + const std::complex& x) { + return conj(x); + } + static std::string name() { + return std::string("std::complex<") + ArithTraits::name() + ">"; + } + static std::complex squareroot( + const std::complex& x) { + return sqrt(x); + } + static mag_type eps() { return epsilon(); } + static mag_type sfmin() { return ArithTraits::sfmin(); } + static int base() { return ArithTraits::base(); } + static mag_type prec() { return ArithTraits::prec(); } + static int t() { return ArithTraits::t(); } + static mag_type rnd() { return ArithTraits::one(); } + static int emin() { return ArithTraits::emin(); } + static mag_type rmin() { return ArithTraits::rmin(); } + static int emax() { return ArithTraits::emax(); } + static mag_type rmax() { return ArithTraits::rmax(); } +}; + +template <> +class ArithTraits { + public: + using val_type = char; + using mag_type = val_type; + + // The C(++) standard does not require that char be signed. In + // fact, signed char, unsigned char, and char are distinct types. + // We can use std::numeric_limits here because it's a const bool, + // not a class method. + static constexpr bool is_signed = std::numeric_limits::is_signed; + + static std::string name() { return "char"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = signed char; + using mag_type = val_type; + + static constexpr bool is_signed = true; + + static std::string name() { return "signed char"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = unsigned char; + using mag_type = val_type; + + static constexpr bool is_signed = false; + + static std::string name() { return "unsigned char"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = short; + using mag_type = val_type; + + static constexpr bool is_signed = true; + + static std::string name() { return "short"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = unsigned short; + using mag_type = val_type; + + static constexpr bool is_signed = false; + + static std::string name() { return "unsigned short"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = int; + using mag_type = val_type; + + static constexpr bool is_signed = true; + + static std::string name() { return "int"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = unsigned int; + using mag_type = val_type; + + static constexpr bool is_signed = false; + + static std::string name() { return "unsigned int"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = long; + using mag_type = val_type; + + static constexpr bool is_signed = true; + + static std::string name() { return "long"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = unsigned long; + using mag_type = val_type; + + static constexpr bool is_signed = false; + + static std::string name() { return "unsigned long"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = long long; + using mag_type = val_type; + + static constexpr bool is_signed = true; + + static std::string name() { return "long long"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +template <> +class ArithTraits { + public: + using val_type = unsigned long long; + using mag_type = val_type; + + static constexpr bool is_signed = false; + + static std::string name() { return "unsigned long long"; } + + KOKKOSKERNELS_ARITHTRAITS_INTEGRAL() +}; + +// dd_real and qd_real are floating-point types provided by the QD +// library of David Bailey (LBNL): +// +// http://crd-legacy.lbl.gov/~dhbailey/mpdist/ +// +// dd_real uses two doubles (128 bits), and qd_real uses four doubles +// (256 bits). +// +// Kokkos does not currently support these types in device +// functions. It should be possible to use Kokkos' support for +// aggregate types to implement device function support for dd_real +// and qd_real, but we have not done this yet (as of 09 Jan 2015). +// Hence, the class methods of the ArithTraits specializations for +// dd_real and qd_real are not marked as device functions. +#ifdef HAVE_KOKKOS_QD +// LBV: I would like to deprecate this strange optional +// dependency on the lbnl package, is there anyone actully +// using this? It certainly is never tested by CI or nightly +// so probably does not work... +template <> +struct [[deprecated]] ArithTraits { + typedef dd_real val_type; + typedef dd_real mag_type; + + static const bool is_specialized = true; + static const bool is_signed = true; + static const bool is_integer = false; + static const bool is_exact = false; + static const bool is_complex = false; + + static inline bool isInf(const val_type& x) { return isinf(x); } + static inline bool isNan(const val_type& x) { return isnan(x); } + static inline mag_type abs(const val_type& x) { return ::abs(x); } + static inline val_type zero() { return val_type(0.0); } + static inline val_type one() { return val_type(1.0); } + static inline val_type min() { return std::numeric_limits::min(); } + static inline val_type max() { return std::numeric_limits::max(); } + static inline mag_type real(const val_type& x) { return x; } + static inline mag_type imag(const val_type&) { return zero(); } + static inline val_type conj(const val_type& x) { return x; } + static inline val_type pow(const val_type& x, const val_type& y) { + return ::pow(x, y); + } + static inline val_type sqrt(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::sqrt(x); +#else + return ::sqrt(x); +#endif + } + static inline val_type cbrt(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::cbrt(x); +#else + return ::cbrt(x); +#endif + } + static inline val_type exp(const val_type& x) { return ::exp(x); } + static inline val_type log(const val_type& x) { + // dd_real puts its transcendental functions in the global namespace. + return ::log(x); + } + static inline val_type log10(const val_type& x) { return ::log10(x); } + static KOKKOS_FUNCTION val_type sin(const val_type x) { return ::sin(x); } + static KOKKOS_FUNCTION val_type cos(const val_type x) { return ::cos(x); } + static KOKKOS_FUNCTION val_type tan(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::tan(x); +#else + return std::tan(x); +#endif + } + static KOKKOS_FUNCTION val_type sinh(const val_type x) { return ::sinh(x); } + static KOKKOS_FUNCTION val_type cosh(const val_type x) { return ::cosh(x); } + static KOKKOS_FUNCTION val_type tanh(const val_type x) { return ::tanh(x); } + static KOKKOS_FUNCTION val_type asin(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::asin(x); +#else + return ::asin(x); +#endif + } + static KOKKOS_FUNCTION val_type acos(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::acos(x); +#else + return ::acos(x); +#endif + } + static KOKKOS_FUNCTION val_type atan(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::atan(x); +#else + return ::atan(x); +#endif + } + static inline val_type nan() { return val_type::_nan; } + static val_type epsilon() { return std::numeric_limits::epsilon(); } + + typedef dd_real magnitudeType; + typedef double halfPrecision; + typedef qd_real doublePrecision; + + static const bool isComplex = false; + static const bool isOrdinal = false; + static const bool isComparable = true; + static const bool hasMachineParameters = true; + + static mag_type eps() { return epsilon(); } + static mag_type sfmin() { return min(); } + static int base() { return std::numeric_limits::radix; } + static mag_type prec() { return eps() * base(); } + static int t() { return std::numeric_limits::digits; } + static mag_type rnd() { + return std::numeric_limits::round_style == std::round_to_nearest + ? one() + : zero(); + } + static int emin() { return std::numeric_limits::min_exponent; } + static mag_type rmin() { return std::numeric_limits::min(); } + static int emax() { return std::numeric_limits::max_exponent; } + static mag_type rmax() { return std::numeric_limits::max(); } + static mag_type magnitude(const val_type& x) { return ::abs(x); } + static val_type conjugate(const val_type& x) { return conj(x); } + static bool isnaninf(const val_type& x) { return isNan(x) || isInf(x); } + static std::string name() { return "dd_real"; } + static val_type squareroot(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::sqrt(x); +#else + return ::sqrt(x); +#endif + } +}; + +template <> +struct [[deprecated]] ArithTraits { + typedef qd_real val_type; + typedef qd_real mag_type; + + static const bool is_specialized = true; + static const bool is_signed = true; + static const bool is_integer = false; + static const bool is_exact = false; + static const bool is_complex = false; + + static inline bool isInf(const val_type& x) { return isinf(x); } + static inline bool isNan(const val_type& x) { return isnan(x); } + static inline mag_type abs(const val_type& x) { return ::abs(x); } + static inline val_type zero() { return val_type(0.0); } + static inline val_type one() { return val_type(1.0); } + static inline val_type min() { return std::numeric_limits::min(); } + static inline val_type max() { return std::numeric_limits::max(); } + static inline mag_type real(const val_type& x) { return x; } + static inline mag_type imag(const val_type&) { return zero(); } + static inline val_type conj(const val_type& x) { return x; } + static inline val_type pow(const val_type& x, const val_type& y) { + return ::pow(x, y); + } + static inline val_type sqrt(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::sqrt(x); +#else + return ::sqrt(x); +#endif + } + static inline val_type cbrt(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::cbrt(x); +#else + return ::cbrt(x); +#endif + } + static inline val_type exp(const val_type& x) { return ::exp(x); } + static inline val_type log(const val_type& x) { + // val_type puts its transcendental functions in the global namespace. + return ::log(x); + } + static inline val_type log10(const val_type& x) { return ::log10(x); } + static KOKKOS_FUNCTION val_type sin(const val_type x) { return ::sin(x); } + static KOKKOS_FUNCTION val_type cos(const val_type x) { return ::cos(x); } + static KOKKOS_FUNCTION val_type tan(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::tan(x); +#else + return std::tan(x); +#endif + } + static KOKKOS_FUNCTION val_type sinh(const val_type x) { return ::sinh(x); } + static KOKKOS_FUNCTION val_type cosh(const val_type x) { return ::cosh(x); } + static KOKKOS_FUNCTION val_type tanh(const val_type x) { return ::tanh(x); } + static KOKKOS_FUNCTION val_type asin(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::asin(x); +#else + return ::asin(x); +#endif + } + static KOKKOS_FUNCTION val_type acos(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::acos(x); +#else + return ::acos(x); +#endif + } + static KOKKOS_FUNCTION val_type atan(const val_type x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::atan(x); +#else + return ::atan(x); +#endif + } + static inline val_type nan() { return val_type::_nan; } + static inline val_type epsilon() { + return std::numeric_limits::epsilon(); + } + + typedef qd_real magnitudeType; + typedef dd_real halfPrecision; + // The QD library does not have an "oct-double real" class. One + // could use an arbitrary-precision library like MPFR or ARPREC, + // with the precision set appropriately, to get an + // extended-precision type for qd_real. + typedef qd_real doublePrecision; + + static const bool isComplex = false; + static const bool isOrdinal = false; + static const bool isComparable = true; + static const bool hasMachineParameters = true; + + static mag_type eps() { return epsilon(); } + static mag_type sfmin() { return min(); } + static int base() { return std::numeric_limits::radix; } + static mag_type prec() { return eps() * base(); } + static int t() { return std::numeric_limits::digits; } + static mag_type rnd() { + return std::numeric_limits::round_style == std::round_to_nearest + ? one() + : zero(); + } + static int emin() { return std::numeric_limits::min_exponent; } + static mag_type rmin() { return std::numeric_limits::min(); } + static int emax() { return std::numeric_limits::max_exponent; } + static mag_type rmax() { return std::numeric_limits::max(); } + static mag_type magnitude(const val_type& x) { return ::abs(x); } + static val_type conjugate(const val_type& x) { return conj(x); } + static bool isnaninf(const val_type& x) { return isNan(x) || isInf(x); } + static std::string name() { return "qd_real"; } + static val_type squareroot(const val_type& x) { +#ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_SYCL + return sycl::sqrt(x); +#else + return ::sqrt(x); +#endif + } +}; +#endif // HAVE_KOKKOS_QD + +} // namespace Details + +// Promote ArithTraits into Kokkos namespace. At some point, we +// will remove it from the Details namespace completely. We leave +// it there for now, because a lot of code depends on it being +// there. +using Details::ArithTraits; +} // namespace Kokkos + +#endif // KOKKOS_ARITHTRAITS_HPP diff --git a/external/kokkos-kernels/LICENSE b/external/kokkos-kernels/LICENSE new file mode 100644 index 00000000..bdcc6965 --- /dev/null +++ b/external/kokkos-kernels/LICENSE @@ -0,0 +1,46 @@ +/* +//@HEADER +// ************************************************************************ +// +// Kokkos v. 3.0 +// Copyright (2020) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// KokkosKernels is licensed under 3-clause BSD terms of use: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the Corporation nor the names of the +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact Siva Rajamanickam (srajama@sandia.gov) +// +// ************************************************************************ +//@HEADER +*/ + diff --git a/external/parthenon b/external/parthenon index f1e110f9..fd7d58e7 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit f1e110f9ff33eb0df103cd3e738847ab546d447b +Subproject commit fd7d58e759df72f403f78611553b4dcfc2320514 diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch new file mode 100644 index 00000000..36ada3a1 --- /dev/null +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -0,0 +1,34 @@ +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 81a8a1bd..65ba74f8 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) + set(COMPILER_COMMAND "") # TODO: Put something more descriptive here + set(COMPILER_FLAGS "") # TODO: Put something more descriptive here + +-set(COORDINATE_TYPE UniformCartesian) # TODO: Make this an option when more are available ++set(COORDINATE_TYPE GRCoordinates) # TODO: Make this an option when more are available + + configure_file(config.hpp.in generated/config.hpp @ONLY) + +@@ -279,6 +279,8 @@ lint_target(parthenon) + target_include_directories(parthenon PUBLIC + $ + $ ++ $ ++ $ + $ + ) + +diff --git a/src/coordinates/coordinates.hpp b/src/coordinates/coordinates.hpp +index d1290dee..50bfc840 100644 +--- a/src/coordinates/coordinates.hpp ++++ b/src/coordinates/coordinates.hpp +@@ -16,6 +16,7 @@ + #include "config.hpp" + + #include "uniform_cartesian.hpp" ++#include "coordinates/gr_coordinates.hpp" + + namespace parthenon { + diff --git a/external/patches/0001-Fix-compiling-variant-under-IBM-XL-and-SYCL-environm.patch b/external/patches/variant-fix-xl-sycl.patch similarity index 100% rename from external/patches/0001-Fix-compiling-variant-under-IBM-XL-and-SYCL-environm.patch rename to external/patches/variant-fix-xl-sycl.patch diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index d53d0969..643a7b08 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -11,35 +11,46 @@ endif() AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob/elec EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob/emhd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/coordinates EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/flux EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_cd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/boundaries EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/current EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/driver EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/electrons EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/emhd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/floors EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/grmhd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/implicit EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/inverter EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/reductions EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/emhd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/wind EXE_NAME_SRC) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob/elec) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob/emhd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/coordinates) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/flux) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_cd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/boundaries) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/current) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/driver) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/electrons) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/emhd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/floors) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/grmhd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/implicit) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inverter) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/reductions) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/emhd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/wind) @@ -47,13 +58,24 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/wind) add_executable(${EXE_NAME} ${EXE_NAME_SRC}) target_link_libraries(${EXE_NAME} PUBLIC kokkos) -# We actually only need the header -#target_link_libraries(${EXE_NAME} PUBLIC kokkoskernels) target_link_libraries(${EXE_NAME} PUBLIC parthenon) # Sometimes helps with OpenMP #target_link_libraries(${EXE_NAME} PUBLIC gomp) target_link_libraries(${EXE_NAME} PUBLIC z) - +# Link FFTW3 if available +# Let the code know not to use it otherwise +if (NOT Kokkos_ENABLE_CUDA) + find_package(FFTW) + if (FFTW_FOUND) + target_compile_definitions(${EXE_NAME} PUBLIC USE_FFTW=1) + target_link_libraries(${EXE_NAME} PUBLIC fftw3) + else() + target_compile_definitions(${EXE_NAME} PUBLIC USE_FFTW=0) + message(WARNING "Cannot find FFTW! Compiling without driven turbulence test.") + endif() +else() + target_compile_definitions(${EXE_NAME} PUBLIC USE_FFTW=0) +endif() # OPTIONS # These are almost universally performance trade-offs @@ -76,13 +98,20 @@ if(FAST_CARTESIAN) else() target_compile_definitions(${EXE_NAME} PUBLIC FAST_CARTESIAN=0) endif() -option(KHARMA_DISABLE_IMPLICIT "Compile the implicit solver, requiring kokkos-kernels. Default true" OFF) +option(KHARMA_DISABLE_IMPLICIT "Disable the implicit solver, which requires bundled kokkos-kernels. Default false" OFF) +option(KHARMA_DISABLE_CLEANUP "Disable the magnetic field cleanup module, which requires recent Parthenon. Default false" OFF) option(KHARMA_TRACE "Compile with tracing: print entry and exit of important functions" OFF) if(KHARMA_DISABLE_IMPLICIT) message("Compiling without the implicit solver. Extended GRMHD will be disabled!") - target_compile_definitions(${EXE_NAME} PUBLIC ENABLE_IMPLICIT=0) + target_compile_definitions(${EXE_NAME} PUBLIC DISABLE_IMPLICIT=1) +else() + target_compile_definitions(${EXE_NAME} PUBLIC DISABLE_IMPLICIT=0) +endif() +if(KHARMA_DISABLE_CLEANUP) + message("Compiling without global Conjugate Gradients. B field cleanup will be disabled!") + target_compile_definitions(${EXE_NAME} PUBLIC DISABLE_CLEANUP=1) else() - target_compile_definitions(${EXE_NAME} PUBLIC ENABLE_IMPLICIT=1) + target_compile_definitions(${EXE_NAME} PUBLIC DISABLE_CLEANUP=0) endif() # Tracing can be added in the command-line make.sh call: "./make.sh [OPTIONS] trace" if(KHARMA_TRACE) diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index 16c15059..6042db1f 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -35,41 +35,47 @@ #include "b_cd.hpp" #include "kharma.hpp" -#include "mpi.hpp" using namespace parthenon; namespace B_CD { -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { - auto pkg = std::make_shared("B_CD"); + auto pkg = std::make_shared("B_CD"); Params ¶ms = pkg->AllParams(); - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - // Constraint damping options - // Factor "lambda" in + // Factor "lambda" in Dedner TODO tune Real damping = pin->GetOrAddReal("b_field", "damping", 0.1); params.Add("damping", damping); - std::vector s_vector({NVEC}); + // Accumulator for maximum ctop within an MPI process + // That is, this value does NOT generally reflect the actual maximum + params.Add("ctop_max", 0.0, true); + // Maximum between MPI processes, updated after each step; that is, always a maximum. + params.Add("ctop_max_last", 0.0, true); + + // Update variable numbers + // auto& driver = packages->Get("Driver")->AllParams(); + // if (implicit_b) { + // int n_current = driver.Get("n_implicit_vars"); + // driver.Update("n_implicit_vars", n_current+3); + // } else { + // int n_current = driver.Get("n_explicit_vars"); + // driver.Update("n_explicit_vars", n_current+3); + // } - MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); + std::vector s_vector({NVEC}); // B field as usual + // TODO allow for implicit B here Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, Metadata::Restart, Metadata::Conserved, Metadata::WithFluxes, Metadata::Vector}, s_vector); pkg->AddField("cons.B", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, isPrimitive, Metadata::Vector}, s_vector); + Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::Vector}, s_vector); pkg->AddField("prims.B", m); // Constraint damping scalar field psi. Prim and cons forms correspond to B field forms, @@ -79,15 +85,19 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack Metadata::Restart, Metadata::Conserved, Metadata::WithFluxes}); pkg->AddField("cons.psi_cd", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, isPrimitive}); + Metadata::Restart, Metadata::GetUserFlag("Primitive")}); pkg->AddField("prims.psi_cd", m); // We only update the divB field for output m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("divB", m); - pkg->FillDerivedBlock = B_CD::FillDerived; + pkg->AddSource = B_CD::AddSource; + + pkg->BlockUtoP = B_CD::BlockUtoP; + pkg->PostStepDiagnosticsMesh = B_CD::PostStepDiagnostics; + pkg->MeshPostStepUserWorkInLoop = B_CD::UpdateCtopMax; // List (vector) of HistoryOutputVar that will all be enrolled as output variables parthenon::HstVar_list hst_vars = {}; @@ -99,7 +109,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack return pkg; } -void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag(rc, "B field UtoP"); auto pmb = rc->GetBlockPointer(); @@ -116,7 +126,7 @@ void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) IndexRange jb = bounds.GetBoundsJ(domain); IndexRange kb = bounds.GetBoundsK(domain); pmb->par_for("UtoP_B", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Update the primitive B-fields Real gdet = G.gdet(Loci::center, j, i); VLOOP B_P(v, k, j, i) = B_U(v, k, j, i) / gdet; @@ -152,35 +162,35 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; pmb0->par_for("AddSource_B_CD", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(b); // Add a source term to B based on psi GReal alpha_c = 1. / m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); GReal gdet_c = G.gdet(Loci::center, j, i); - double divB = ((B_U(b).flux(X1DIR, V1, k, j, i+1) - B_U(b).flux(X1DIR, V1, k, j, i)) / G.dx1v(i) + - (B_U(b).flux(X2DIR, V2, k, j+1, i) - B_U(b).flux(X2DIR, V2, k, j, i)) / G.dx2v(j)); - if (ndim > 2) divB += (B_U(b).flux(X3DIR, V3, k+1, j, i) - B_U(b).flux(X3DIR, V3, k, j, i)) / G.dx3v(k); + double divB = ((B_U(b).flux(X1DIR, V1, k, j, i+1) - B_U(b).flux(X1DIR, V1, k, j, i)) / G.Dxc<1>(i) + + (B_U(b).flux(X2DIR, V2, k, j+1, i) - B_U(b).flux(X2DIR, V2, k, j, i)) / G.Dxc<2>(j)); + if (ndim > 2) divB += (B_U(b).flux(X3DIR, V3, k+1, j, i) - B_U(b).flux(X3DIR, V3, k, j, i)) / G.Dxc<3>(k); // TODO this needs to include the time derivative right? VLOOP { // First term: gradient of psi B_DU(b, v, k, j, i) += alpha_c * G.gcon(Loci::center, j, i, v+1, 1) * - (psi_U(b).flux(X1DIR, 0, k, j, i+1) - psi_U(b).flux(X1DIR, 0, k, j, i)) / G.dx1v(i) + + (psi_U(b).flux(X1DIR, 0, k, j, i+1) - psi_U(b).flux(X1DIR, 0, k, j, i)) / G.Dxc<1>(i) + alpha_c * G.gcon(Loci::center, j, i, v+1, 2) * - (psi_U(b).flux(X2DIR, 0, k, j+1, i) - psi_U(b).flux(X2DIR, 0, k, j, i)) / G.dx2v(j); + (psi_U(b).flux(X2DIR, 0, k, j+1, i) - psi_U(b).flux(X2DIR, 0, k, j, i)) / G.Dxc<2>(j); if (ndim > 2) B_DU(b, v, k, j, i) += alpha_c * G.gcon(Loci::center, j, i, v+1, 3) * - (psi_U(b).flux(X3DIR, 0, k+1, j, i) - psi_U(b).flux(X3DIR, 0, k, j, i)) / G.dx3v(k); + (psi_U(b).flux(X3DIR, 0, k+1, j, i) - psi_U(b).flux(X3DIR, 0, k, j, i)) / G.Dxc<3>(k); // Second term: beta^i divB B_DU(b, v, k, j, i) += G.gcon(Loci::center, j, i, 0, v+1) * alpha_c * alpha_c * divB; } // Update psi using the analytic solution for the source term GReal dalpha1 = ( (1. / m::sqrt(-G.gcon(Loci::face1, j, i+1, 0, 0))) / G.gdet(Loci::face1, j, i+1) - - (1. / m::sqrt(-G.gcon(Loci::face1, j, i, 0, 0))) / G.gdet(Loci::face1, j, i)) / G.dx1v(i); + - (1. / m::sqrt(-G.gcon(Loci::face1, j, i, 0, 0))) / G.gdet(Loci::face1, j, i)) / G.Dxc<1>(i); GReal dalpha2 = ( (1. / m::sqrt(-G.gcon(Loci::face2, j+1, i, 0, 0))) / G.gdet(Loci::face2, j+1, i) - - (1. / m::sqrt(-G.gcon(Loci::face2, j, i, 0, 0))) / G.gdet(Loci::face2, j, i)) / G.dx2v(i); + - (1. / m::sqrt(-G.gcon(Loci::face2, j, i, 0, 0))) / G.gdet(Loci::face2, j, i)) / G.Dxc<2>(i); // There is not dalpha3, the coordinate system is symmetric along x3 psi_DU(b, 0, k, j, i) += B_U(b, V1, k, j, i) * dalpha1 + B_U(b, V2, k, j, i) * dalpha2 - alpha_c * lambda * psi_U(b, 0, k, j, i); } @@ -213,11 +223,11 @@ Real MaxDivB(MeshData *md) Real bsq_max; Kokkos::Max bsq_max_reducer(bsq_max); pmb0->par_reduce("B_field_bsqmax", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { const auto& G = B.GetCoords(b); - double divb_local = ((B(b).flux(1, V1, k, j, i+1) - B(b).flux(1, V1, k, j, i)) / G.dx1v(i)+ - (B(b).flux(2, V2, k, j+1, i) - B(b).flux(2, V2, k, j, i)) / G.dx2v(j)); - if (ndim > 2) divb_local += (B(b).flux(3, V3, k+1, j, i) - B(b).flux(3, V3, k, j, i)) / G.dx3v(k); + double divb_local = ((B(b).flux(1, V1, k, j, i+1) - B(b).flux(1, V1, k, j, i)) / G.Dxc<1>(i)+ + (B(b).flux(2, V2, k, j+1, i) - B(b).flux(2, V2, k, j, i)) / G.Dxc<2>(j)); + if (ndim > 2) divb_local += (B(b).flux(3, V3, k+1, j, i) - B(b).flux(3, V3, k, j, i)) / G.Dxc<3>(k); if(divb_local > local_result) local_result = divb_local; } @@ -231,7 +241,7 @@ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) auto pmesh = md->GetMeshPointer(); // Print this unless we quash everything - int verbose = pmesh->packages.Get("B_CD")->Param("verbose"); + int verbose = pmesh->packages.Get("Globals")->Param("verbose"); if (verbose >= 0) { static Reduce max_divb; max_divb.val = B_CD::MaxDivB(md); @@ -274,14 +284,28 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin) const auto& G = pmb->coords; pmb->par_for("B_field_bsqmax", kl.s, kl.e, jl.s, jl.e, il.s, il.e, - KOKKOS_LAMBDA_3D { - double divb_local = ((F1(V1, k, j, i+1) - F1(V1, k, j, i)) / G.dx1v(i) + - (F2(V2, k, j+1, i) - F2(V2, k, j, i)) / G.dx2v(j)); - if (ndim > 2) divb_local += (F3(V3, k+1, j, i) - F3(V3, k, j, i)) / G.dx3v(k); + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + double divb_local = ((F1(V1, k, j, i+1) - F1(V1, k, j, i)) / G.Dxc<1>(i) + + (F2(V2, k, j+1, i) - F2(V2, k, j, i)) / G.Dxc<2>(j)); + if (ndim > 2) divb_local += (F3(V3, k+1, j, i) - F3(V3, k, j, i)) / G.Dxc<3>(k); divB(k, j, i) = divb_local; } ); } +void UpdateCtopMax(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + // Reduce and record the maximum sound speed on the grid, to propagate + // phi at that speed next step. + // Just needs to run after every step, so we use the KHARMA callback at that point. + auto& params = pmesh->packages.Get("B_CD")->AllParams(); + static AllReduce ctop_max_last_r; + ctop_max_last_r.val = params.Get("ctop_max"); + ctop_max_last_r.StartReduce(MPI_MAX); + while (ctop_max_last_r.CheckReduce() == TaskStatus::incomplete); + params.Update("ctop_max_last", ctop_max_last_r.val); + params.Update("ctop_max", 0.0); // Reset for next max calculation +} + } // namespace B_CD diff --git a/kharma/b_cd/b_cd.hpp b/kharma/b_cd/b_cd.hpp index dcebfe27..42014db0 100644 --- a/kharma/b_cd/b_cd.hpp +++ b/kharma/b_cd/b_cd.hpp @@ -43,9 +43,9 @@ using namespace parthenon; /** - * This physics package implements B field transport with Flux-CT (Toth 2000) + * This physics package implements B field transport with Constraint-Damping (Dedner et al 2002) * - * This requires only the values at cell centers + * This requires only the values at cell centers, and preserves a cell-centered divergence representation * * This implementation includes conversion from "primitive" to "conserved" B and back, * i.e. between field strength and flux via multiplying by gdet. @@ -54,7 +54,7 @@ namespace B_CD { /** * Declare fields, initialize (few) parameters */ -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". @@ -65,8 +65,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack * input: Conserved B = sqrt(-gdet) * B^i * output: Primitive B = B^i */ -void UtoP(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); -inline void FillDerived(MeshBlockData *rc) { UtoP(rc); } +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); /** * Add the source term to dUdt, before it is applied to U @@ -81,6 +80,12 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt); */ Real MaxDivB(MeshData *md); +/** + * Find the maximum wavespeed across the whole grid, to use in propagating + * the phi field. + */ +void UpdateCtopMax(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); + /** * Diagnostics printed/computed after each step * Currently nothing, divB is calculated in fluxes.cpp diff --git a/kharma/b_cd/seed_B_cd.cpp b/kharma/b_cd/seed_B_cd.cpp index b027e47b..962460d6 100644 --- a/kharma/b_cd/seed_B_cd.cpp +++ b/kharma/b_cd/seed_B_cd.cpp @@ -91,7 +91,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Shortcut to field values for easy fields if (b_field_flag == BSeedType::constant) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly B_P(0, k, j, i) = b10; B_P(1, k, j, i) = b20; @@ -101,7 +101,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) return TaskStatus::complete; } else if (b_field_flag == BSeedType::monopole) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly by normalizing B_P(0, k, j, i) = b10 / G.gdet(Loci::center, j, i); B_P(1, k, j, i) = 0.; @@ -115,7 +115,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) ParArrayND A3("A", n2, n1); // TODO figure out double vs Real here pmb->par_for("B_field_A", js+1, je, is+1, ie, - KOKKOS_LAMBDA_2D { + KOKKOS_LAMBDA (const int& j, const int& i) { GReal Xembed[GR_DIM]; G.coord_embed(0, j, i, Loci::center, Xembed); GReal r = Xembed[1], th = Xembed[2]; @@ -131,7 +131,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) break; case BSeedType::ryan: // BR's smoothed poloidal in-torus - q = m::pow(sin(th), 3) * m::pow(r / rin, 3) * exp(-r / 400) * rho_av - min_rho_q; + q = m::pow(sin(th), 3) * m::pow(r / rin, 3) * m::exp(-r / 400) * rho_av - min_rho_q; break; case BSeedType::r3s3: // Just the r^3 sin^3 th term, proposed EHT standard MAD @@ -146,7 +146,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) Real x = (r / rin) * sin(th); Real sigma = 2 / m::sqrt(2 * log(2)); Real u = x / m::abs(sigma); - q = (1 / (m::sqrt(2 * M_PI) * m::abs(sigma))) * exp(-u * u / 2); + q = (1 / (m::sqrt(2 * M_PI) * m::abs(sigma))) * m::exp(-u * u / 2); } break; default: @@ -160,14 +160,13 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Calculate B-field pmb->par_for("B_field_B", ks, ke, js+1, je-1, is+1, ie-1, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Take the curl - B_P(0, k, j, i) = (A3(j + 1, i) - A3(j-1, i)) / (2 * G.dx2v(j) * G.gdet(Loci::center, j, i)); - B_P(1, k, j, i) = -(A3(j, i + 1) - A3(j, i-1)) / (2 * G.dx1v(i) * G.gdet(Loci::center, j, i)); + B_P(0, k, j, i) = (A3(j + 1, i) - A3(j-1, i)) / (2 * G.Dxc<2>(j) * G.gdet(Loci::center, j, i)); + B_P(1, k, j, i) = -(A3(j, i + 1) - A3(j, i-1)) / (2 * G.Dxc<1>(i) * G.gdet(Loci::center, j, i)); B_P(2, k, j, i) = 0.; } ); - B_FluxCT::PtoU(rc); return TaskStatus::complete; } diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 7e06b2f2..dfc1bc13 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -31,10 +31,6 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include -#include - #include "b_cleanup.hpp" // For a bunch of utility functions @@ -42,9 +38,23 @@ #include "boundaries.hpp" #include "decs.hpp" +#include "kharma_driver.hpp" #include "grmhd.hpp" #include "kharma.hpp" -#include "mpi.hpp" + +#if DISABLE_CLEANUP + +// The package should never be loaded if there is not a global solve to be done. +// Therefore we yell at load time rather than waiting for the first solve +std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{throw std::runtime_error("KHARMA was compiled without global solvers! Cannot clean B Field!");} +// We still need a stub for CleanupDivergence() in order to compile, but it will never be called +void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) {} + +#else + +#include +#include using namespace parthenon; using namespace parthenon::solvers; @@ -52,24 +62,12 @@ using namespace parthenon::solvers; // TODO get the transport manager working later // Needs a call every X steps option, probably return a TaskList or TaskRegion -namespace B_Cleanup -{ - -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::shared_ptr& packages) { Flag("Initializing B Field Cleanup"); - auto pkg = std::make_shared("B_Cleanup"); + auto pkg = std::make_shared("B_Cleanup"); Params ¶ms = pkg->AllParams(); - // OPTIONS - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - // Solver options // Allow setting tolerance relative to starting value. Off by default Real rel_tolerance = pin->GetOrAddReal("b_cleanup", "rel_tolerance", 1.); @@ -88,10 +86,6 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack bool always_solve = pin->GetOrAddBoolean("b_cleanup", "always_solve", false); params.Add("always_solve", always_solve); - // TODO find a way to add this to the list every N steps - int cleanup_interval = pin->GetOrAddInteger("b_cleanup", "cleanup_interval", 0); - params.Add("cleanup_interval", cleanup_interval); - // Finally, initialize the solver // Translate parameters params.Add("bicgstab_max_iterations", max_iterations); @@ -109,7 +103,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // Construct a solver. We don't need the template parameter, so we use 'int' BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor()); // Set callback - solver.user_MatVec = CornerLaplacian; + solver.user_MatVec = B_Cleanup::CornerLaplacian; params.Add("solver", solver); @@ -126,58 +120,25 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack pkg->AddField("divB_RHS", m); - // If there's not another B field transport (dangerous!), take care of it ourselves. - // Allocate the field, register most of the B_FluxCT callbacks - // TODO check if B is allocated and set this if not - bool manage_field = pin->GetOrAddBoolean("b_cleanup", "manage_field", false); + // Optionally take care of B field transport ourselves. Inadvisable. + // We've already set a default, so only do this if we're *explicitly* asked + // TODO there's a long list of stuff to enable this if someone really wants it + bool manage_field = pin->GetString("b_field", "solver") == "b_cleanup"; params.Add("manage_field", manage_field); - if (manage_field) { - MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isMHD = packages.Get("GRMHD")->Param("MHDFlag"); - - // B fields. "Primitive" form is field, "conserved" is flux - // Note: when changing metadata, keep these in lockstep with grmhd.cpp!! - // See notes there about changes for the Imex driver - std::vector flags_prim, flags_cons; - auto imex_driver = pin->GetString("driver", "type") == "imex"; - if (!imex_driver) { - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, - isPrimitive, isMHD, Metadata::Vector}); - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, - Metadata::Restart, Metadata::Conserved, isMHD, Metadata::WithFluxes, Metadata::Vector}); - } else { - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Restart, - isPrimitive, isMHD, Metadata::Vector}); - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, - Metadata::Conserved, isMHD, Metadata::WithFluxes, Metadata::Vector}); - } - - m = Metadata(flags_prim, s_vector); - pkg->AddField("prims.B", m); - m = Metadata(flags_cons, s_vector); - pkg->AddField("cons.B", m); - - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - pkg->AddField("divB", m); - - pkg->FillDerivedMesh = B_FluxCT::FillDerivedMesh; - pkg->FillDerivedBlock = B_FluxCT::FillDerivedBlock; - pkg->PostStepDiagnosticsMesh = B_FluxCT::PostStepDiagnostics; + int cleanup_interval = pin->GetOrAddInteger("b_cleanup", "cleanup_interval", manage_field ? 10 : -1); + params.Add("cleanup_interval", cleanup_interval); - // List (vector) of HistoryOutputVar that will all be enrolled as output variables - parthenon::HstVar_list hst_vars = {}; - // The definition of MaxDivB we care about actually changes per-transport. Use our function. - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_FluxCT::MaxDivB, "MaxDivB")); - // add callbacks for HST output to the Params struct, identified by the `hist_param_key` - pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + // Declare fields if we're doing that + if (manage_field) { + throw std::runtime_error("B Cleanup package as transport not implemented!"); } return pkg; } -void CleanupDivergence(std::shared_ptr>& md) +void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) { - Flag(md.get(), "Cleaning up divB"); + Flag(md, "Cleaning up divB"); auto pmesh = md->GetMeshPointer(); auto pkg = pmesh->packages.Get("B_Cleanup"); @@ -188,10 +149,8 @@ void CleanupDivergence(std::shared_ptr>& md) auto fail_flag = pkg->Param("fail_without_convergence"); auto warn_flag = pkg->Param("warn_without_convergence"); auto always_solve = pkg->Param("always_solve"); - auto verbose = pkg->Param("verbose"); auto solver = pkg->Param>("solver"); - MetadataFlag isMHD = pmesh->packages.Get("GRMHD")->Param("MHDFlag"); - + auto verbose = pmesh->packages.Get("Globals")->Param("verbose"); if (MPIRank0() && verbose > 0) { std::cout << "Cleaning divB to relative tolerance " << rel_tolerance << std::endl; @@ -215,7 +174,7 @@ void CleanupDivergence(std::shared_ptr>& md) // This gets signed divB on all physical corners (total (N+1)^3) // and syncs ghost zones B_FluxCT::CalcDivB(md.get(), "divB_RHS"); - KBoundaries::SyncAllBounds(md); + KHARMADriver::SyncAllBounds(md); // Add a solver container and associated MeshData for (auto& pmb : pmesh->block_list) { @@ -228,7 +187,7 @@ void CleanupDivergence(std::shared_ptr>& md) // There's no MeshData-wide 'Remove' so we go block-by-block for (auto& pmb : pmesh->block_list) { auto rc_s = pmb->meshblock_data.Get("solve"); - auto varlabels = rc_s->GetVariablesByFlag({isMHD}, true).labels(); + auto varlabels = rc_s->GetVariablesByFlag({Metadata::GetUserFlag("MHD")}).labels(); for (auto varlabel : varlabels) { rc_s->Remove(varlabel); } @@ -243,7 +202,7 @@ void CleanupDivergence(std::shared_ptr>& md) auto t_solve_step = solver.CreateTaskList(t_none, 0, tr, md, msolve); while (!tr.Execute()); // Make sure solution's ghost zones are sync'd - KBoundaries::SyncAllBounds(msolve); + KHARMADriver::SyncAllBounds(msolve); // Apply the result if (MPIRank0() && verbose > 0) { @@ -253,7 +212,7 @@ void CleanupDivergence(std::shared_ptr>& md) B_Cleanup::ApplyP(msolve.get(), md.get()); // Synchronize to update ghost zones - KBoundaries::SyncAllBounds(md); + KHARMADriver::SyncAllBounds(md); // Recalculate divB max for one last check const double divb_end = B_FluxCT::GlobalMaxDivB(md.get()); @@ -261,10 +220,30 @@ void CleanupDivergence(std::shared_ptr>& md) std::cout << "Magnetic field divergence after cleanup: " << divb_end << std::endl; } - Flag(md.get(), "Cleaned"); + Flag(md, "Cleaned"); +} + +// TODO TODO NEEDED? Can we remove the package instead? +TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) +{ + // If we aren't needed to clean anything... + if (! (blocks[0]->packages.Get("B_Cleanup")->Param("cleanup_interval") > 0)) { + // remove the internal BiCGStab variables by name, + // to prevent them weighing down MPI exchanges + // TODO anything FillGhost & not Conserved or Primitive + for (auto& pmb : blocks) { + auto rc_s = pmb->meshblock_data.Get(); + //auto varlabels = rc_s->GetVariablesByName({"pk0", "res0", "divB_RHS", "p"}).labels(); + for (auto varlabel : {"pk0", "res0", "divB_RHS", "p"}) { + if (rc_s->HasCellVariable(varlabel)) + rc_s->Remove(varlabel); + } + } + } + return TaskStatus::complete; } -TaskStatus ApplyP(MeshData *msolve, MeshData *md) +TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) { Flag(md, "Applying correction from P"); // Apply on physical zones only, we'll be syncing/updating ghosts @@ -280,7 +259,7 @@ TaskStatus ApplyP(MeshData *msolve, MeshData *md) // dB = grad(p), defined at cell centers, subtract to make field divergence-free pmb0->par_for("gradient_P", 0, P.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); double b1, b2, b3; B_FluxCT::center_grad(G, P, b, k, j, i, ndim > 2, b1, b2, b3); @@ -290,12 +269,12 @@ TaskStatus ApplyP(MeshData *msolve, MeshData *md) } ); - B_FluxCT::UtoP(md, IndexDomain::entire); + B_FluxCT::MeshUtoP(md, IndexDomain::entire); return TaskStatus::complete; } -TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, const std::string& lap_var) +TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_var, MeshData* md_again, const std::string& lap_var) { Flag(md, "Calculating & summing divB"); // Cover ghost cells; maximize since both ops have stencil >1 @@ -321,7 +300,7 @@ TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, const s // Need a halo one zone *left*, as corner_div will read that. // Therefore B's ghosts need to be up to date! pmb0->par_for("gradient_P", 0, P.GetDim(5) - 1, kb_l.s, kb_l.e, jb_l.s, jb_l.e, ib_l.s, ib_l.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); double b1, b2, b3; B_FluxCT::center_grad(G, P, b, k, j, i, ndim > 2, b1, b2, b3); @@ -333,7 +312,7 @@ TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, const s // lap = div(dB), defined at cell corners pmb0->par_for("laplacian_dB", 0, lap.GetDim(5) - 1, kb_r.s, kb_r.e, jb_r.s, jb_r.e, ib_r.s, ib_r.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = lap.GetCoords(b); // This is the inverse diagonal element of a fictional a_ij Laplacian operator lap(b, 0, k, j, i) = B_FluxCT::corner_div(G, dB, b, k, j, i, ndim > 2); @@ -343,4 +322,4 @@ TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, const s return TaskStatus::complete; } -} // namespace B_Cleanup +#endif \ No newline at end of file diff --git a/kharma/b_cleanup/b_cleanup.hpp b/kharma/b_cleanup/b_cleanup.hpp index c547c16d..05f74373 100644 --- a/kharma/b_cleanup/b_cleanup.hpp +++ b/kharma/b_cleanup/b_cleanup.hpp @@ -53,7 +53,7 @@ namespace B_Cleanup { /** * Declare fields, initialize (few) parameters */ -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Single-call divergence cleanup. Lots of MPI syncs, probably slow to use in task lists. @@ -66,10 +66,17 @@ void CleanupDivergence(std::shared_ptr>& md); */ //void AddBCleanupTasks(TaskList tl, TaskID t_dep); +/** + * Remove the extra solver fields which B_Cleanup added during initialization. + * Must be run before every step as the meshblocks are reconstructed per-step from + * package variable lists. + */ +TaskStatus RemoveExtraFields(BlockList_t &blocks); + /** * Calculate the laplacian using divergence at corners */ -TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, const std::string& lap_var); +TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, MeshData* md_again, const std::string& lap_var); /** * Apply B -= grad(P) to subtract divergence from the magnetic field diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index bbdd1b6e..af801fbc 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -39,103 +39,138 @@ #include "decs.hpp" #include "grmhd.hpp" #include "kharma.hpp" -#include "mpi.hpp" +#include "reductions.hpp" using namespace parthenon; namespace B_FluxCT { -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +// Reductions: phi uses global machinery, but divB is too +// Can also sum the hemispheres independently to be fancy (TODO?) +KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) { - auto pkg = std::make_shared("B_FluxCT"); - Params ¶ms = pkg->AllParams(); + // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 + return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B +} + +Real ReducePhi0(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); +} +Real ReducePhi5(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); +} - // OPTIONS - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + auto pkg = std::make_shared("B_FluxCT"); + Params ¶ms = pkg->AllParams(); // Diagnostic & inadvisable flags - bool fix_flux = pin->GetOrAddBoolean("b_field", "fix_polar_flux", true); - params.Add("fix_polar_flux", fix_flux); - bool fix_flux_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_x1", false); - params.Add("fix_flux_x1", fix_flux_x1); - // WARNING this disables constrained transport, so the field will quickly pick up a divergence. - // To use another transport, just specify it instead of this one. + // This enables flux corrections to ensure divB preservation even with zero flux of B2 on the polar "face." + // It effectively makes the pole a superconducting rod + bool spherical = pin->GetBoolean("coordinates", "spherical"); // TODO could do package + bool fix_polar_flux = pin->GetOrAddBoolean("b_field", "fix_polar_flux", spherical); + params.Add("fix_polar_flux", fix_polar_flux); + // These options do the same to the inner and outer edges. They are NOT as well tested, and it's + // questionable whether you'd want to do this anyway. + // They would require at least B1 to be reflected across the EH, probably straight-up reflecting conditions + bool fix_eh_flux = pin->GetOrAddBoolean("b_field", "fix_eh_flux", false); + params.Add("fix_eh_flux", fix_eh_flux); + bool fix_exterior_flux = pin->GetOrAddBoolean("b_field", "fix_exterior_flux", false); + params.Add("fix_exterior_flux", fix_exterior_flux); + // This option uses a different (better but slower) fix which allows magnetic flux through the X1 boundaries, + // at the cost of some speed and potentially some instability due to the non-local nature of the solve. + // Much better tested than above options + bool fix_x1_flux = pin->GetOrAddBoolean("b_field", "fix_x1_flux", false); + params.Add("fix_x1_flux", fix_x1_flux); + + // KHARMA requires some kind of field transport if there is a magnetic field allocated + // Use this if you actually want to disable all magnetic field flux corrections, + // and allow a field divergence to grow unchecked, usually for debugging or comparison reasons bool disable_flux_ct = pin->GetOrAddBoolean("b_field", "disable_flux_ct", false); params.Add("disable_flux_ct", disable_flux_ct); // Driver type & implicit marker - // By default, solve B implicitly if GRMHD is - auto driver_type = pin->GetString("driver", "type"); - bool grmhd_implicit = packages.Get("GRMHD")->Param("implicit"); - bool implicit_b = (driver_type == "imex" && pin->GetOrAddBoolean("b_field", "implicit", grmhd_implicit)); + // By default, solve B explicitly + auto& driver = packages->Get("Driver")->AllParams(); + bool implicit_b = pin->GetOrAddBoolean("b_field", "implicit", false); params.Add("implicit", implicit_b); + // Update variable numbers + if (implicit_b) { + int n_current = driver.Get("n_implicit_vars"); + driver.Update("n_implicit_vars", n_current+3); + } else { + int n_current = driver.Get("n_explicit_vars"); + driver.Update("n_explicit_vars", n_current+3); + } + + params.Add("divb_reducer", AllReduce()); + // FIELDS std::vector s_vector({NVEC}); - MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isMHD = packages.Get("GRMHD")->Param("MHDFlag"); - - // B fields. "Primitive" form is field, "conserved" is flux - // See notes there about changes for the Imex driver - std::vector flags_prim, flags_cons; - if (driver_type == "harm") { - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::FillGhost, Metadata::Restart, // added by Hyerin (12/09/2022) - isPrimitive, isMHD, Metadata::Vector}); - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, - Metadata::Restart, Metadata::Conserved, isMHD, Metadata::WithFluxes, Metadata::Vector}); - } else if (driver_type == "imex") { - // See grmhd.cpp for full notes on flag changes for ImEx driver - // Note that default for B is *explicit* evolution - MetadataFlag areWeImplicit = (implicit_b) ? packages.Get("Implicit")->Param("ImplicitFlag") - : packages.Get("Implicit")->Param("ExplicitFlag"); - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, - Metadata::Restart, isPrimitive, isMHD, areWeImplicit, Metadata::Vector}); - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, - Metadata::WithFluxes, isMHD, areWeImplicit, Metadata::Vector}); - } + // Mark if we're evolving implicitly + MetadataFlag areWeImplicit = (implicit_b) ? Metadata::GetUserFlag("Implicit") + : Metadata::GetUserFlag("Explicit"); + + // Flags for B fields. "Primitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); m = Metadata(flags_cons, s_vector); pkg->AddField("cons.B", m); - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::Restart}); //, Metadata::FillGhost}); - pkg->AddField("divB", m); // Hyerin (12/19/22) + // TODO declare this only on "resize_kharma_restart" m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); pkg->AddField("B_Save", m); - // Ensure that prims get filled + // We exist basically to do this + pkg->FixFlux = B_FluxCT::FixFlux; + + // Also ensure that prims get filled, *if* we're evolved explicitly if (!implicit_b) { - //pkg->FillDerivedMesh = B_FluxCT::FillDerivedMesh; - pkg->FillDerivedBlock = B_FluxCT::FillDerivedBlock; + pkg->MeshUtoP = B_FluxCT::MeshUtoP; + pkg->BlockUtoP = B_FluxCT::BlockUtoP; } // Register the other callbacks pkg->PostStepDiagnosticsMesh = B_FluxCT::PostStepDiagnostics; + // The definition of MaxDivB we care about actually changes per-transport, + // so calculating it is handled by the transport package + // We'd only ever need to declare or calculate divB for output (getting the max is independent) + if (KHARMA::FieldIsOutput(pin, "divB")) { + pkg->BlockUserWorkBeforeOutput = B_FluxCT::FillOutput; + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("divB", m); + } + // List (vector) of HistoryOutputVars that will all be enrolled as output variables parthenon::HstVar_list hst_vars = {}; - // The definition of MaxDivB we care about actually changes per-transport. Use our function, - // which calculates divB at cell corners hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_FluxCT::MaxDivB, "MaxDivB")); + // Event horizon magnetization. Might be the same or different for different representations? + if (pin->GetBoolean("coordinates", "spherical")) { + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi0, "Phi_0")); + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi5, "Phi_EH")); + } // add callbacks for HST output to the Params struct, identified by the `hist_param_key` pkg->AddParam<>(parthenon::hist_param_key, hst_vars); return pkg; } -void UtoP(MeshData *md, IndexDomain domain, bool coarse) +// TODO template and use as a model for future +void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) { Flag(md, "B UtoP Mesh"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -151,14 +186,14 @@ void UtoP(MeshData *md, IndexDomain domain, bool coarse) IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; pmb0->par_for("UtoP_B", block.s, block.e, vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_VEC { + KOKKOS_LAMBDA (const int& b, const int &mu, const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(b); // Update the primitive B-fields B_P(b, mu, k, j, i) = B_U(b, mu, k, j, i) / G.gdet(Loci::center, j, i); } ); } -void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag(rc, "B UtoP Block"); auto pmb = rc->GetBlockPointer(); @@ -173,38 +208,38 @@ void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) const IndexRange jb = bounds.GetBoundsJ(domain); const IndexRange kb = bounds.GetBoundsK(domain); const IndexRange vec = IndexRange({0, B_U.GetDim(4)-1}); + pmb->par_for("UtoP_B", vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_VEC { + KOKKOS_LAMBDA (const int &mu, const int &k, const int &j, const int &i) { // Update the primitive B-fields B_P(mu, k, j, i) = B_U(mu, k, j, i) / G.gdet(Loci::center, j, i); } ); } -void PtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) +void FixFlux(MeshData *md) { - Flag(rc, "B PtoU Block"); - auto pmb = rc->GetBlockPointer(); - - auto B_U = rc->PackVariables(std::vector{"cons.B"}); - auto B_P = rc->PackVariables(std::vector{"prims.B"}); - - const auto& G = pmb->coords; - - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const IndexRange ib = bounds.GetBoundsI(domain); - const IndexRange jb = bounds.GetBoundsJ(domain); - const IndexRange kb = bounds.GetBoundsK(domain); - const IndexRange vec = IndexRange({0, B_U.GetDim(4)-1}); - pmb->par_for("PtoU_B", vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_VEC { - // Update the primitive B-fields - B_U(mu, k, j, i) = B_P(mu, k, j, i) * G.gdet(Loci::center, j, i); - } - ); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto& params = pmb0->packages.Get("B_FluxCT")->AllParams(); + if (params.Get("fix_polar_flux")) { + FixBoundaryFlux(md, IndexDomain::inner_x2, false); + FixBoundaryFlux(md, IndexDomain::outer_x2, false); + } + if (params.Get("fix_x1_flux")) { + FixX1Flux(md); + } + if (params.Get("fix_eh_flux")) { + FixBoundaryFlux(md, IndexDomain::inner_x1, false); + } + if (params.Get("fix_exterior_flux")) { + FixBoundaryFlux(md, IndexDomain::outer_x1, false); + } + FluxCT(md); } -TaskStatus FluxCT(MeshData *md) +// INTERNAL + +void FluxCT(MeshData *md) { Flag(md, "Flux CT"); // Pointers @@ -212,7 +247,7 @@ TaskStatus FluxCT(MeshData *md) auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Exit on trivial operations const int ndim = pmesh->ndim; - if (ndim < 2) return TaskStatus::complete; + if (ndim < 2) return; // Pack variables const auto& B_F = md->PackVariablesAndFluxes(std::vector{"cons.B"}); @@ -228,7 +263,7 @@ TaskStatus FluxCT(MeshData *md) const IndexRange kl = (ndim > 2) ? IndexRange{kb.s, kb.e + 1} : kb; // Declare temporaries - // TODO make these a true Edge field of B_FluxCT? Could then output, use elsewhere, skip re-declaring + // TODO make these a true Edge field when that's available const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); const int n2 = pmb0->cellbounds.ncellsj(IndexDomain::entire); const int n3 = pmb0->cellbounds.ncellsk(IndexDomain::entire); @@ -240,7 +275,7 @@ TaskStatus FluxCT(MeshData *md) // Calculate emf around each face Flag(md, "Calc EMFs"); pmb0->par_for("flux_ct_emf", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { emf3(b, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - B_F(b).flux(X2DIR, V1, k, j, i) - B_F(b).flux(X2DIR, V1, k, j, i-1)); if (ndim > 2) { @@ -258,7 +293,7 @@ TaskStatus FluxCT(MeshData *md) // Note these each have different domains, eg il vs ib. The former extends one index farther if appropriate pmb0->par_for("flux_ct_1", block.s, block.e, kb.s, kb.e, jb.s, jb.e, il.s, il.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { B_F(b).flux(X1DIR, V1, k, j, i) = 0.0; B_F(b).flux(X1DIR, V2, k, j, i) = 0.5 * (emf3(b, k, j, i) + emf3(b, k, j+1, i)); if (ndim > 2) B_F(b).flux(X1DIR, V3, k, j, i) = -0.5 * (emf2(b, k, j, i) + emf2(b, k+1, j, i)); @@ -272,7 +307,7 @@ TaskStatus FluxCT(MeshData *md) } ); pmb0->par_for("flux_ct_2", block.s, block.e, kb.s, kb.e, jl.s, jl.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { B_F(b).flux(X2DIR, V1, k, j, i) = -0.5 * (emf3(b, k, j, i) + emf3(b, k, j, i+1)); B_F(b).flux(X2DIR, V2, k, j, i) = 0.0; if (ndim > 2) B_F(b).flux(X2DIR, V3, k, j, i) = 0.5 * (emf1(b, k, j, i) + emf1(b, k+1, j, i)); @@ -280,7 +315,7 @@ TaskStatus FluxCT(MeshData *md) ); if (ndim > 2) { pmb0->par_for("flux_ct_3", block.s, block.e, kl.s, kl.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { B_F(b).flux(X3DIR, V1, k, j, i) = 0.5 * (emf2(b, k, j, i) + emf2(b, k, j, i+1)); B_F(b).flux(X3DIR, V2, k, j, i) = -0.5 * (emf1(b, k, j, i) + emf1(b, k, j+1, i)); B_F(b).flux(X3DIR, V3, k, j, i) = 0.0; @@ -289,55 +324,119 @@ TaskStatus FluxCT(MeshData *md) } Flag(md, "CT Finished"); - return TaskStatus::complete; } -TaskStatus FixPolarFlux(MeshData *md) +void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) { Flag(md, "Fixing polar B fluxes"); auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - IndexDomain domain = IndexDomain::interior; - int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); - int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); - int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); + auto pmb0 = pmesh->block_list[0]; const int ndim = pmesh->ndim; + if (ndim < 2) return; - int je_e = (ndim > 1) ? je + 1 : je; - int ke_e = (ndim > 2) ? ke + 1 : ke; - - // Assuming the fluxes through the pole are 0, - // make sure the polar EMFs are 0 when performing fluxCT - // TODO only invoke one kernel? We avoid invocation except on boundaries anyway + auto bounds = coarse ? pmb0->c_cellbounds : pmb0->cellbounds; + const IndexRange ib = bounds.GetBoundsI(IndexDomain::interior); + const IndexRange jb = bounds.GetBoundsJ(IndexDomain::interior); + const IndexRange kb = bounds.GetBoundsK(IndexDomain::interior); + + // Imagine a corner of the domain, with ghost and physical zones + // as below, denoted w/'g' and 'p' respectively. + // + // g | p | p + //----------- + // g | p | p + //xxx-------- + // g | g | g + // + // The flux through 'x' is not important for updating a physical zone, + // as it does not border any. However, FluxCT considers it when updating + // nearby fluxes, two of which affect physical zones. + // Therefore in e.g. X1 faces, we need to update fluxes on the domain: + // [0,N1+1],[-1,N2+1],[-1,N3+1] + // These indices arrange for that. + + // For faces + const IndexRange ibf = IndexRange{ib.s, ib.e + 1}; + const IndexRange jbf = IndexRange{jb.s, jb.e + 1}; + // Won't need X3 faces + //const IndexRange kbf = IndexRange{kb.s, kb.e + (ndim > 2)}; + // For sides + const IndexRange ibs = IndexRange{ib.s - 1, ib.e + 1}; + const IndexRange jbs = IndexRange{jb.s - (ndim > 1), jb.e + (ndim > 1)}; + const IndexRange kbs = IndexRange{kb.s - (ndim > 2), kb.e + (ndim > 2)}; + + // Make sure the polar EMFs are 0 when performing fluxCT + // Compare this section with calculation of emf3 in FluxCT: + // these changes ensure that boundary emfs emf3(i,js,k)=0, etc. for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); - if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) - { - pmb->par_for("fix_flux_b_l", ks-1, ke_e+1, js, js, is-1, ie+1+1, // Hyerin (12/28/22) - KOKKOS_LAMBDA_3D { - B_F.flux(X1DIR, V2, k, j-1, i) = -B_F.flux(X1DIR, V2, k, js, i); - if (ndim > 1) B_F.flux(X2DIR, V2, k, j, i) = 0; - if (ndim > 2) B_F.flux(X3DIR, V2, k, j-1, i) = -B_F.flux(X3DIR, V2, k, js, i); + if (domain == IndexDomain::inner_x2 && + pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_l", kbs.s, kbs.e, jbf.s, jbf.s, ibs.s, ibs.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X2DIR, V1, k, j, i) = 0.; + B_F.flux(X2DIR, V3, k, j, i) = 0.; + B_F.flux(X1DIR, V2, k, j - 1, i) = -B_F.flux(X1DIR, V2, k, j, i); + if (ndim > 2) B_F.flux(X3DIR, V2, k, j - 1, i) = -B_F.flux(X3DIR, V2, k, j, i); } ); } - if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) - { - pmb->par_for("fix_flux_b_r", ks-1, ke_e+1, je_e, je_e, is-1, ie+1+1, // Hyerin (12/28/22) - KOKKOS_LAMBDA_3D { - B_F.flux(X1DIR, V2, k, j, i) = -B_F.flux(X1DIR, V2, k, je, i); - if (ndim > 1) B_F.flux(X2DIR, V2, k, j, i) = 0; - if (ndim > 2) B_F.flux(X3DIR, V2, k, j, i) = -B_F.flux(X3DIR, V2, k, je, i); + + if (domain == IndexDomain::outer_x2 && + pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_r", kbs.s, kbs.e, jbf.e, jbf.e, ibs.s, ibs.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X2DIR, V1, k, j, i) = 0.; + B_F.flux(X2DIR, V3, k, j, i) = 0.; + B_F.flux(X1DIR, V2, k, j, i) = -B_F.flux(X1DIR, V2, k, j - 1, i); + if (ndim > 2) B_F.flux(X3DIR, V2, k, j, i) = -B_F.flux(X3DIR, V2, k, j - 1, i); + } + ); + } + + // TODO the following is dead without an accompanying inverted-B1 or reflecting boundary + // for magnetic fields in KBoundaries. (Unless you want to reflect everything, which, don't.) + // Keeping special boundaries for this silly test kicking around KBoundaries was ugly, so they're + // removed. Could investigate further when Parthenon's better boundary support appears. + + // We can do the same with the outflow bounds. Kind of. + // See, actually, outflow bounds will *always* generate divergence on the domain face. + // So if we want to clean it up here, we would need to arrange for B1 to be inverted in ghost cells. + // This is no longer pure outflow, but might be thought of as a "nicer" version of + // reflecting conditions: + // 1. Since B1 is inverted, B1 on the domain face will tend to 0 (it's not quite reflected, but basically) + // (obviously don't enable this for monopole test problems!) + // 2. However, B2 and B3 are normal outflow conditions -- despite the fluxes here, the outflow + // conditions will set them equal to the last zone. + if (domain == IndexDomain::inner_x1 && + pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_in", kbs.s, kbs.e, jbs.s, jbs.e, ibf.s, ibf.s, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X1DIR, V2, k, j, i) = 0.; + B_F.flux(X1DIR, V3, k, j, i) = 0.; + B_F.flux(X2DIR, V1, k, j, i - 1) = -B_F.flux(X2DIR, V1, k, j, i); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i - 1) = -B_F.flux(X3DIR, V1, k, j, i); + } + ); + } + + if (domain == IndexDomain::outer_x1 && + pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_out", kbs.s, kbs.e, jbs.s, jbs.e, ibf.e, ibf.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X1DIR, V2, k, j, i) = 0.; + B_F.flux(X1DIR, V3, k, j, i) = 0.; + B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, i - 1); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, i - 1); } ); } + } Flag(md, "Fixed polar B"); - return TaskStatus::complete; } TaskStatus FixX1Flux(MeshData *md) @@ -392,7 +491,7 @@ TaskStatus FixX1Flux(MeshData *md) pmb->par_for("fix_flux_b_l", ktemp, ktemp, jtemp, jtemp, is, is, // Hyerin (02/20/23) for 3rd prescription, sequential //pmb->par_for("fix_flux_b_l", ks_all+2, ke_all, js_new, je_new, is, is, // Hyerin (02/20/23) for 3rd prescription //pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // Hyerin (12/28/22) for 1st & 2nd prescription - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; @@ -458,7 +557,7 @@ TaskStatus FixX1Flux(MeshData *md) pmb->par_for("fix_flux_b_r", ktemp, ktemp, jtemp, jtemp, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription, sequential //pmb->par_for("fix_flux_b_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription //pmb->par_for("fix_flux_b_r", ks_all+1, ke_all+1, js_all+1, je_all+1, ie+1, ie+1, // Hyerin (12/28/22) for 1st & 2nd prescription - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; @@ -497,19 +596,17 @@ TaskStatus FixX1Flux(MeshData *md) return TaskStatus::complete; } -TaskStatus TransportB(MeshData *md) +// Outflow boundary conditions without the fix_eh_flux special sauce *always* generate divB. +// Don't report it, as we expect it. +// TODO we could stay off x2 if two_sync, but I wanna drive home that's weird for a cycle +IndexRange ValidDivBX1(MeshBlock *pmb) { - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - if (pmb0->packages.Get("B_FluxCT")->Param("fix_polar_flux") - && pmb0->coords.coords.spherical()) { - FixPolarFlux(md); - } - if (pmb0->packages.Get("B_FluxCT")->Param("fix_flux_x1") // added by Hyerin - && pmb0->coords.coords.spherical()) { - FixX1Flux(md); - } - FluxCT(md); - return TaskStatus::complete; + const IndexRange ibl = pmb->meshblock_data.Get()->GetBoundsI(IndexDomain::interior); + bool avoid_inner = (!pmb->packages.Get("B_FluxCT")->Param("fix_eh_flux") && + pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user); + bool avoid_outer = (!pmb->packages.Get("B_FluxCT")->Param("fix_exterior_flux") && + pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user); + return IndexRange{ibl.s + (avoid_inner), ibl.e + (!avoid_outer)}; } double MaxDivB(MeshData *md) @@ -521,33 +618,27 @@ double MaxDivB(MeshData *md) // Packing out here avoids frequent per-mesh packs. Do we need to? auto B_U = md->PackVariables(std::vector{"cons.B"}); - const IndexRange ib = md->GetBoundsI(IndexDomain::interior); - const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = md->GetBoundsK(IndexDomain::interior); + const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); + + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + // TODO Keep zone of max! Also applies to ctop. + // This is one kernel call per block, because each block will have different bounds. // Could consolidate at the cost of lots of bounds checking. - // TODO redo as nested parallel like Parthenon sparse vars? double max_divb = 0.0; for (int b = block.s; b <= block.e; ++b) { auto pmb = md->GetBlockData(b)->GetBlockPointer().get(); - // Note this is a stencil-4 (or -8) function, which would involve zones outside the - // domain unless we stay off the left edges. - // However, *inside* the domain we want to catch all corners, including those at 0/N+1 - // bordering other meshblocks. - const int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; - const int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; - const int js = (IsDomainBound(pmb, BoundaryFace::inner_x2) && ndim > 1) ? jb.s + 1 : jb.s; - const int je = (IsDomainBound(pmb, BoundaryFace::outer_x2) || ndim <=1) ? jb.e : jb.e + 1; - const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; - const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; + const IndexRange ib = ValidDivBX1(pmb); double max_divb_block; Kokkos::Max max_reducer(max_divb_block); - pmb->par_reduce("divB_max", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D_REDUCE { + pmb->par_reduce("divB_max", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { const auto& G = B_U.GetCoords(b); const double local_divb = m::abs(corner_div(G, B_U, b, k, j, i, ndim > 2)); if (local_divb > local_result) local_result = local_divb; @@ -577,7 +668,7 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md) // Since this is in the history file now, I don't bother printing it // unless we're being verbose. It's not costly to calculate though - if (pmb0->packages.Get("B_FluxCT")->Param("verbose") >= 1) { + if (pmb0->packages.Get("Globals")->Param("verbose") >= 1) { Flag(md, "Printing divB"); // Calculate the maximum from/on all nodes const double divb_max = B_FluxCT::GlobalMaxDivB(md); @@ -591,6 +682,8 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md) return TaskStatus::complete; } +// TODO unify these by adding FillOutputMesh option + void CalcDivB(MeshData *md, std::string divb_field_name) { Flag(md, "Calculating divB for output"); @@ -601,24 +694,21 @@ void CalcDivB(MeshData *md, std::string divb_field_name) auto B_U = md->PackVariables(std::vector{"cons.B"}); auto divB = md->PackVariables(std::vector{divb_field_name}); - const IndexRange ib = md->GetBoundsI(IndexDomain::interior); - const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = md->GetBoundsK(IndexDomain::interior); + const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); + + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; // See MaxDivB for details for (int b = block.s; b <= block.e; ++b) { auto pmb = md->GetBlockData(b)->GetBlockPointer().get(); - const int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; - const int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; - const int js = IsDomainBound(pmb, BoundaryFace::inner_x2) ? jb.s + 1 : jb.s; - const int je = IsDomainBound(pmb, BoundaryFace::outer_x2) ? jb.e : jb.e + 1; - const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; - const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; + const IndexRange ib = ValidDivBX1(pmb); - pmb->par_for("calc_divB", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + pmb->par_for("calc_divB", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(b); divB(b, 0, k, j, i) = corner_div(G, B_U, b, k, j, i, ndim > 2); } @@ -637,45 +727,19 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin) auto B_U = rc->PackVariables(std::vector{"cons.B"}); auto divB = rc->PackVariables(std::vector{"divB"}); - // Note this is a stencil-4 (or -8) function, which would involve zones outside the - // domain unless we stay off the left edges. - // However, *inside* the domain we want to catch all corners, including those at 0/N+1 - // bordering other meshblocks. - const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); - const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); - // changed by Hyerin (12/21/22) - //const IndexRange ib = rc->GetBoundsI(IndexDomain::entire); - //const IndexRange jb = rc->GetBoundsJ(IndexDomain::entire); - //const IndexRange kb = rc->GetBoundsK(IndexDomain::entire); - const int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; - const int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; - const int js = (IsDomainBound(pmb, BoundaryFace::inner_x2) && ndim > 1) ? jb.s + 1 : jb.s; - const int je = (IsDomainBound(pmb, BoundaryFace::outer_x2) || ndim <=1) ? jb.e : jb.e + 1; - const int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; - const int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; - /* - int is = IsDomainBound(pmb, BoundaryFace::inner_x1) ? ib.s + 1 : ib.s; - int ie = IsDomainBound(pmb, BoundaryFace::outer_x1) ? ib.e : ib.e + 1; - int js = (IsDomainBound(pmb, BoundaryFace::inner_x2) && ndim > 1) ? jb.s + 1 : jb.s; - int je = (IsDomainBound(pmb, BoundaryFace::outer_x2) || ndim <=1) ? jb.e : jb.e + 1; - int ks = (IsDomainBound(pmb, BoundaryFace::inner_x3) && ndim > 2) ? kb.s + 1 : kb.s; - int ke = (IsDomainBound(pmb, BoundaryFace::outer_x3) || ndim <= 2) ? kb.e : kb.e + 1; - - if (ndim > 2) { // modified by Hyerin (12/21/22), just to calculate at the ghost zone - is = ib.s + 1; - ie = ib.e; - js = jb.s + 1; - je = jb.e; - ks = kb.s + 1; - ke = kb.e; - } - */ + const IndexRange jbl = rc->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = rc->GetBoundsK(IndexDomain::interior); + + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + + const IndexRange ib = ValidDivBX1(pmb); - pmb->par_for("divB_output", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + pmb->par_for("divB_output", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(); - divB(0, k, j, i) = corner_div(G, B_U, 0, k, j, i, ndim > 2, ndim > 1); + divB(0, k, j, i) = corner_div(G, B_U, 0, k, j, i, ndim > 2); } ); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index e16147a8..8f021b61 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -51,7 +51,7 @@ namespace B_FluxCT { /** * Declare fields, initialize (few) parameters */ -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". @@ -62,36 +62,29 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack * input: Conserved B = sqrt(-gdet) * B^i * output: Primitive B = B^i */ -void UtoP(MeshData *md, IndexDomain domain=IndexDomain::entire, bool coarse=false); -inline void FillDerivedMesh(MeshData *md) { UtoP(md); } -inline TaskStatus FillDerivedMeshTask(MeshData *md) { UtoP(md); return TaskStatus::complete; } -void UtoP(MeshBlockData *md, IndexDomain domain=IndexDomain::entire, bool coarse=false); -inline void FillDerivedBlock(MeshBlockData *rc) { UtoP(rc); } -inline TaskStatus FillDerivedBlockTask(MeshBlockData *rc) { UtoP(rc); return TaskStatus::complete; } +void BlockUtoP(MeshBlockData *md, IndexDomain domain, bool coarse=false); +void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** - * Inverse of above. Generally only for initialization. + * All flux corrections required by this package */ -void PtoU(MeshBlockData *md, IndexDomain domain=IndexDomain::interior, bool coarse=false); - +void FixFlux(MeshData *md); /** * Modify the B field fluxes to take a constrained-transport step as in Toth (2000) */ -TaskStatus FluxCT(MeshData *md); - +void FluxCT(MeshData *md); /** - * Modify the B field fluxes just beyond the polar boundary so as to ensure no flux through it, - * after applying FluxCT + * Modify the B field fluxes just beyond the polar (or radial) boundary so as to + * ensure no flux through the boundary after applying FluxCT */ -TaskStatus FixPolarFlux(MeshData *md); - -// added by Hyerin -TaskStatus FixX1Flux(MeshData *md); +void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse); /** - * Task combining the above two (polar fix and FluxCT) for simplicity + * Alternate B field fix for X1 boundary, keeps zero divergence while permitting flux + * through the boundary, at the cost of a short non-local solve. */ -TaskStatus TransportB(MeshData *md); +// added by Hyerin +TaskStatus FixX1Flux(MeshData *md); /** * Calculate maximum corner-centered divergence of magnetic field, @@ -106,13 +99,6 @@ double MaxDivB(MeshData *md); */ double GlobalMaxDivB(MeshData *md); -/** - * Clean the magnetic field divergence via successive over-relaxation - * Currently only used when resizing inputs. - * TODO option to sprinkle into updates every N steps - */ -void CleanupDivergence(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior, bool coarse=false); - /** * Diagnostics printed/computed after each step * Currently just max divB @@ -120,11 +106,9 @@ void CleanupDivergence(MeshBlockData *rc, IndexDomain domain=IndexDomain:: TaskStatus PrintGlobalMaxDivB(MeshData *md); inline TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) { return PrintGlobalMaxDivB(md); } -// Block version; unused now, kept for future fiascos -TaskStatus PrintMaxBlockDivB(MeshBlockData *rc, bool prims, std::string tag); /** - * Fill fields which are calculated only for output to file + * Fill fields which are calculated only for output to file, i.e., divB */ void FillOutput(MeshBlock *pmb, ParameterInput *pin); /** @@ -138,20 +122,15 @@ void CalcDivB(MeshData *md, std::string divb_field_name="divB"); */ template KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& B_U, const int& b, - const int& k, const int& j, const int& i, const bool& do_3D, const bool& do_2D=true) + const int& k, const int& j, const int& i, const bool& do_3D) { - const double norm = (do_2D) ? ((do_3D) ? 0.25 : 0.5) : 1.; - // 1D divergence - double term1 = B_U(b, V1, k, j, i) - B_U(b, V1, k, j, i-1); - double term2 = 0.; + const double norm = (do_3D) ? 0.25 : 0.5; + // 2D divergence, averaging to corners + double term1 = B_U(b, V1, k, j, i) - B_U(b, V1, k, j, i-1) + + B_U(b, V1, k, j-1, i) - B_U(b, V1, k, j-1, i-1); + double term2 = B_U(b, V2, k, j, i) - B_U(b, V2, k, j-1, i) + + B_U(b, V2, k, j, i-1) - B_U(b, V2, k, j-1, i-1); double term3 = 0.; - if (do_2D) { - // 2D divergence, averaging to corners - term1 += B_U(b, V1, k, j-1, i) - B_U(b, V1, k, j-1, i-1); - term2 += B_U(b, V2, k, j, i) + B_U(b, V2, k, j, i-1) - - B_U(b, V2, k, j-1, i) - B_U(b, V2, k, j-1, i-1); - term3 += 0.; - } if (do_3D) { // Average to corners in 3D, add 3rd flux term1 += B_U(b, V1, k-1, j, i) + B_U(b, V1, k-1, j-1, i) @@ -163,7 +142,32 @@ KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& B - B_U(b, V3, k-1, j, i) - B_U(b, V3, k-1, j-1, i) - B_U(b, V3, k-1, j, i-1) - B_U(b, V3, k-1, j-1, i-1); } - return norm*term1/G.dx1v(i) + norm*term2/G.dx2v(j) + norm*term3/G.dx3v(k); + return norm*term1/G.Dxc<1>(i) + norm*term2/G.Dxc<2>(j) + norm*term3/G.Dxc<3>(k); +} +template +KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& P, const VarMap& m_p, + const int& b, const int& k, const int& j, const int& i, + const bool& do_3D) +{ + const double norm = (do_3D) ? 0.25 : 0.5; + // 2D divergence, averaging to corners + double term1 = P(b, m_p.B1, k, j, i) - P(b, m_p.B1, k, j, i-1) + + P(b, m_p.B1, k, j-1, i) - P(b, m_p.B1, k, j-1, i-1); + double term2 = P(b, m_p.B2, k, j, i) - P(b, m_p.B2, k, j-1, i) + + P(b, m_p.B2, k, j, i-1) - P(b, m_p.B2, k, j-1, i-1); + double term3 = 0.; + if (do_3D) { + // Average to corners in 3D, add 3rd flux + term1 += P(b, m_p.B1, k-1, j, i) + P(b, m_p.B1, k-1, j-1, i) + - P(b, m_p.B1, k-1, j, i-1) - P(b, m_p.B1, k-1, j-1, i-1); + term2 += P(b, m_p.B2, k-1, j, i) + P(b, m_p.B2, k-1, j, i-1) + - P(b, m_p.B2, k-1, j-1, i) - P(b, m_p.B2, k-1, j-1, i-1); + term3 = P(b, m_p.B3, k, j, i) + P(b, m_p.B3, k, j-1, i) + + P(b, m_p.B3, k, j, i-1) + P(b, m_p.B3, k, j-1, i-1) + - P(b, m_p.B3, k-1, j, i) - P(b, m_p.B3, k-1, j-1, i) + - P(b, m_p.B3, k-1, j, i-1) - P(b, m_p.B3, k-1, j-1, i-1); + } + return norm*term1/G.Dxc<1>(i) + norm*term2/G.Dxc<2>(j) + norm*term3/G.Dxc<3>(k); } /** @@ -193,9 +197,9 @@ KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, - P(b, 0, k, j+1, i+1) - P(b, 0, k, j, i+1) - P(b, 0, k, j+1, i) - P(b, 0, k, j, i); } - B1 = norm*term1/G.dx1v(i); - B2 = norm*term2/G.dx2v(j); - B3 = norm*term3/G.dx3v(k); + B1 = norm*term1/G.Dxc<1>(i); + B2 = norm*term2/G.Dxc<2>(j); + B3 = norm*term3/G.Dxc<3>(k); } } diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index fb9add65..5a5f38a9 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -98,10 +98,10 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) if (!is_torus) throw std::invalid_argument("Magnetic field seed "+b_field_type+" supports only torus problems!"); // Torus parameters - rin = pin->GetReal("torus", "rin"); - rmax = pin->GetReal("torus", "rmax"); + rin = pin->GetReal("torus", "rin"); + rmax = pin->GetReal("torus", "rmax"); kappa = pin->GetReal("torus", "kappa"); - tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; + tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; // Other things we need only for torus evaluation gam = pmb->packages.Get("GRMHD")->Param("gamma"); rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); @@ -126,26 +126,36 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Shortcut to field values for easy fields if (b_field_flag == BSeedType::constant) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly B_P(V1, k, j, i) = b10; B_P(V2, k, j, i) = b20; B_P(V3, k, j, i) = b30; } ); - B_FluxCT::PtoU(rc); return TaskStatus::complete; } else if (b_field_flag == BSeedType::monopole) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly by normalizing B_P(V1, k, j, i) = b10 / G.gdet(Loci::center, j, i); B_P(V2, k, j, i) = 0.; B_P(V3, k, j, i) = 0.; } ); - B_FluxCT::PtoU(rc); return TaskStatus::complete; + } else if (b_field_flag == BSeedType::monopole_cube) { + pmb->par_for("B_field_B", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // This ignores rin_bondi to keep divB consistent + // B \prop r^-3 + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, Xembed); + B_P(V1, k, j, i) = 1/(Xembed[1]*Xembed[1]*Xembed[1]); + B_P(V2, k, j, i) = 0.; + B_P(V3, k, j, i) = 0.; + } + ); } // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, @@ -153,7 +163,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // TODO there should be an ncornersi,j,k ParArrayND A("A", NVEC, n3+1, n2+1, n1+1); pmb->par_for("B_field_A", ks, ke+1, js, je+1, is, ie+1, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal Xnative[GR_DIM]; GReal Xembed[GR_DIM], Xmidplane[GR_DIM]; G.coord(k, j, i, Loci::corner, Xnative); @@ -263,7 +273,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Calculate B-field if (ndim > 2) { pmb->par_for("B_field_B_3D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Take a flux-ct step from the corner potentials. // This needs to be 3D because post-tilt A may not point in the phi direction only @@ -277,7 +287,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j) - (A2c3f - A2c3b) / G.dx3v(k); + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); // A1,3 derivative const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + @@ -289,7 +299,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; - B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.dx3v(k) - (A3c1f - A3c1b) / G.dx1v(i); + B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); // A2,1 derivative const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + @@ -301,21 +311,21 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; - B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.dx1v(i) - (A1c2f - A1c2b) / G.dx2v(j); + B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } ); } else { pmb->par_for("B_field_B_2D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // A3,2 derivative const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j); + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j); // A3,1 derivative const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; - B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.dx1v(i); + B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.Dxc<1>(i); B_U(V3, k, j, i) = 0; } @@ -326,7 +336,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Hyerin (12/19/22) copy over data after initialization pmb->par_for("copy_B_restart_resize_kharma", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal X[GR_DIM]; G.coord(k, j, i, Loci::center, X); @@ -339,14 +349,10 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } } ); - - // update conserved values - //B_FluxCT::PtoU(rc,IndexDomain::entire); - B_FluxCT::UtoP(rc,IndexDomain::entire); } // Then make sure the primitive versions are updated, too - B_FluxCT::UtoP(rc); + B_FluxCT::BlockUtoP(rc, IndexDomain::interior); return TaskStatus::complete; } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp new file mode 100644 index 00000000..52a541dc --- /dev/null +++ b/kharma/boundaries/boundaries.cpp @@ -0,0 +1,370 @@ +/* + * File: boundaries.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "boundaries.hpp" + +#include "decs.hpp" +#include "kharma.hpp" +#include "flux.hpp" +#include "flux_functions.hpp" +#include "grmhd_functions.hpp" +#include "pack.hpp" +#include "types.hpp" + +// Parthenon's boundaries +#include + +std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + Flag("Initializing Boundaries"); + + auto pkg = std::make_shared("Boundaries"); + Params ¶ms = pkg->AllParams(); + + // Prevent inflow at boundaries. + // This is two separate checks, but default to enabling/disabling together + bool spherical = pin->GetBoolean("coordinates", "spherical"); + bool check_inflow = pin->GetOrAddBoolean("boundaries", "check_inflow", spherical); + bool check_inflow_inner = pin->GetOrAddBoolean("boundaries", "check_inflow_inner", check_inflow); + params.Add("check_inflow_inner", check_inflow_inner); + bool check_inflow_flux_inner = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_inner", check_inflow_inner); + params.Add("check_inflow_flux_inner", check_inflow_flux_inner); + bool check_inflow_outer = pin->GetOrAddBoolean("boundaries", "check_inflow_outer", check_inflow); + params.Add("check_inflow_outer", check_inflow_outer); + bool check_inflow_flux_outer = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_outer", check_inflow_outer); + params.Add("check_inflow_flux_outer", check_inflow_flux_outer); + + // Ensure fluxes through the zero-size face at the pole are zero + bool fix_flux_pole = pin->GetOrAddBoolean("boundaries", "fix_flux_pole", spherical); + params.Add("fix_flux_pole", fix_flux_pole); + + // Fix the X1/X2 corner by replacing the reflecting condition with the inflow + // Only needed if x1min is inside BH event horizon, otherwise a nuisance for divB on corners + bool inside_eh = spherical && pin->GetBoolean("coordinates", "r_min") < pin->GetBoolean("coordinates", "Rhor"); + bool fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", inside_eh); + params.Add("fix_corner", fix_corner); + + // Allocate space for Dirichlet boundaries if they'll be used + // We have to trust the user here since the problem will set the function pointers later + // TODO specify which boundaries individually for cleanliness? + bool use_dirichlet = pin->GetOrAddBoolean("boundaries", "use_dirichlet", false); + if (use_dirichlet) { + auto& driver = packages->Get("Driver")->AllParams(); + int nvar = driver.Get("n_explicit_vars") + driver.Get("n_implicit_vars"); + std::cout << "Allocating Dirichlet boundaries for " << nvar << " variables." << std::endl; + // TODO We also don't know the mesh size, since it's not constructed. Infer. + int ng = pin->GetInteger("parthenon/mesh", "nghost"); + int nx1 = pin->GetInteger("parthenon/meshblock", "nx1"); + int n1 = nx1 + 2*ng; + int nx2 = pin->GetInteger("parthenon/meshblock", "nx2"); + int n2 = (nx2 == 1) ? nx2 : nx2 + 2*ng; + int nx3 = pin->GetInteger("parthenon/meshblock", "nx3"); + int n3 = (nx3 == 1) ? nx3 : nx3 + 2*ng; + + // These are declared *backward* from how they will be indexed + std::vector s_x1({ng, n2, n3, nvar}); + std::vector s_x2({n1, ng, n3, nvar}); + std::vector s_x3({n1, n2, ng, nvar}); + Metadata m_x1 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x1); + Metadata m_x2 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x2); + Metadata m_x3 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x3); + pkg->AddField("bound.inner_x1", m_x1); + pkg->AddField("bound.outer_x1", m_x1); + pkg->AddField("bound.inner_x2", m_x2); + pkg->AddField("bound.outer_x2", m_x2); + pkg->AddField("bound.inner_x3", m_x3); + pkg->AddField("bound.outer_x3", m_x3); + } + + // Callbacks + // Fix flux + pkg->FixFlux = KBoundaries::FixFlux; + + // KHARMA boundary functions take a domain and are trusted to handle it + pkg->KHARMAInnerX1Boundary = KBoundaries::DefaultBoundary; + pkg->KHARMAOuterX1Boundary = KBoundaries::DefaultBoundary; + pkg->KHARMAInnerX2Boundary = KBoundaries::DefaultBoundary; + pkg->KHARMAOuterX2Boundary = KBoundaries::DefaultBoundary; + + Flag("Initialized"); + return pkg; +} + +void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse) +{ + Flag("Applying a KHARMA boundary"); + // KHARMA has to do some extra tasks in addition to just applying the usual + // boundary conditions. Therefore, we "wrap" Parthenon's (or our own) + // boundary functions with this one. + // TODO call for all packages? + + auto pmb = rc->GetBlockPointer(); + auto pkg = static_cast(pmb->packages.Get("Boundaries").get()); + + // Disambiguate in order to call our pointers + int dir = BoundarySide(domain); + if (dir == 1) { + if (BoundaryIsInner(domain)) { + pkg->KHARMAInnerX1Boundary(rc, domain, coarse); + } else { + pkg->KHARMAOuterX1Boundary(rc, domain, coarse); + } + } else if (dir == 2) { + if (BoundaryIsInner(domain)) { + pkg->KHARMAInnerX2Boundary(rc, domain, coarse); + } else { + pkg->KHARMAOuterX2Boundary(rc, domain, coarse); + } + } + + // Respect the fluid primitives on boundaries (*not* B) + Flux::BlockPtoUMHD(rc.get(), domain, coarse); + // For everything else, respect conserved variables + Packages::BlockUtoPExceptMHD(rc.get(), domain, coarse); + + Flag("Applied boundary"); +} + +void KBoundaries::CheckInflow(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Checking inflow"); + std::shared_ptr pmb = rc->GetBlockPointer(); + const auto& G = pmb->coords; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + bool check_inner = pmb->packages.Get("Boundaries")->Param("check_inflow_inner"); + bool check_outer = pmb->packages.Get("Boundaries")->Param("check_inflow_outer"); + const bool check_inflow = ((check_inner && domain == IndexDomain::inner_x1) + || (check_outer && domain == IndexDomain::outer_x1)); + if (!check_inflow) return; + + PackIndexMap prims_map; + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); + const VarMap m_p(prims_map, false); + + // Inflow check + // Iterate over zones w/p=0 + pmb->par_for_bndry("Outflow_check_inflow", IndexRange{0,0}, domain, coarse, + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); + } + ); + + Flag(rc, "Checked"); +} + +void KBoundaries::FixCorner(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Fixing X1/X2 corner block"); + std::shared_ptr pmb = rc->GetBlockPointer(); + if (pmb->pmy_mesh->ndim < 2 || + !pmb->packages.Get("Boundaries")->Param("fix_corner")) + return; + + // If we're on the interior edge, re-apply that edge for our block by calling + // exactly the same function that Parthenon does. This ensures we're applying + // the same thing, just emulating calling it after X2. + if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + ApplyBoundary(rc, IndexDomain::inner_x1, coarse); + } + + Flag(rc, "Fixed"); +} + +void KBoundaries::CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Correcting the B field w/metric"); + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + auto B_P = rc->PackVariables(std::vector{"prims.B"}); + // Return if no field to correct + if (B_P.GetDim(4) == 0) return; + + const auto& G = pmb->coords; + + const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundarySide(domain); + const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) + : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) + : bounds.GetBoundsK(IndexDomain::interior)); + const int ref = BoundaryIsInner(domain) ? range.s : range.e; + + pmb->par_for_bndry("Correct_B_P", IndexRange{0,NVEC-1}, domain, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) + / G.gdet(Loci::center, j, i); + } + ); + + Flag(rc, "Corrected"); +} + +void KBoundaries::DefaultBoundary(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + // Default function for applying any (non-periodic) boundary condition: + // outflow in X1 with inflow check, Reflect in X2 with corner fix + auto pmb = rc->GetBlockPointer(); + const int dir = BoundarySide(domain); + if (dir == 1) { + if (BoundaryIsInner(domain)) { + parthenon::BoundaryFunction::OutflowInnerX1(rc, coarse); + } else { + parthenon::BoundaryFunction::OutflowOuterX1(rc, coarse); + } + CheckInflow(rc, domain, coarse); + } else if (dir == 2) { + if (BoundaryIsInner(domain)) { + parthenon::BoundaryFunction::ReflectInnerX2(rc, coarse); + } else { + parthenon::BoundaryFunction::ReflectOuterX2(rc, coarse); + } + FixCorner(rc, domain, coarse); + } +} + +void KBoundaries::Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Applying dirichlet bound"); + + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + auto q = rc->PackVariables({Metadata::FillGhost}, coarse); + auto bound = rc->Get("bound."+BoundaryName(domain)).data; + + PackIndexMap prims_map; + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); + const VarMap m_p(prims_map, false); // In case we need it + + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; + const bool right = !BoundaryIsInner(domain); + + // Subtract off the starting index if we're on the right + const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundarySide(domain); + const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior)+1 : 0; + const int je = (dir == 2) ? bounds.je(IndexDomain::interior)+1 : 0; + const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior)+1 : 0; + + const auto& G = pmb->coords; + + pmb->par_for_bndry("dirichlet_boundary", vars, domain, coarse, + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + if (right) { + q(p, k, j, i) = bound(p, k-ke, j-je, i-ie); + } else { + q(p, k, j, i) = bound(p, k, j, i); + } + } + ); + + Flag(rc, "Applied"); +} + +TaskStatus KBoundaries::FixFlux(MeshData *md) +{ + Flag("Fixing fluxes"); + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + + auto& params = pmb0->packages.Get("Boundaries")->AllParams(); + bool check_inflow_inner = params.Get("check_inflow_flux_inner"); + bool check_inflow_outer = params.Get("check_inflow_flux_outer"); + bool fix_flux_pole = params.Get("fix_flux_pole"); + + IndexDomain domain = IndexDomain::interior; + const int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); + const int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); + const int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); + const int ndim = pmesh->ndim; + + // Fluxes are defined at faces, so there is one more valid flux than + // valid cell in the face direction. That is, e.g. F1 is valid on + // an (N1+1)xN2xN3 grid, F2 on N1x(N2+1)xN3, etc. + // These functions do *not* need an extra row outside the domain, + // like B_FluxCT::FixBoundaryFlux does. + const int ie_l = ie + 1; + const int je_l = (ndim > 1) ? je + 1 : je; + //const int ke_l = (ndim > 2) ? ke + 1 : ke; + + for (auto &pmb : pmesh->block_list) { + auto& rc = pmb->meshblock_data.Get(); + + PackIndexMap cons_map; + auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); + const int m_rho = cons_map["cons.rho"].first; + + if (check_inflow_inner) { + if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); + } + ); + } + } + if (check_inflow_outer) { + if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + F.flux(X1DIR, m_rho, k, j, i) = m::max(F.flux(X1DIR, m_rho, k, j, i), 0.); + } + ); + } + } + + // This is a lot of zero fluxes! + if (fix_flux_pole) { + if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { + // This loop covers every flux we need + pmb->par_for("fix_flux_pole_l", 0, F.GetDim(4) - 1, ks, ke, js, js, is, ie, + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + F.flux(X2DIR, p, k, j, i) = 0.; + } + ); + } + + if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { + pmb->par_for("fix_flux_pole_r", 0, F.GetDim(4) - 1, ks, ke, je_l, je_l, is, ie, + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + F.flux(X2DIR, p, k, j, i) = 0.; + } + ); + } + } + } + + Flag("Fixed fluxes"); + return TaskStatus::complete; +} \ No newline at end of file diff --git a/kharma/boundaries.hpp b/kharma/boundaries/boundaries.hpp similarity index 50% rename from kharma/boundaries.hpp rename to kharma/boundaries/boundaries.hpp index 8ee57385..764095ce 100644 --- a/kharma/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -35,63 +35,97 @@ #include "decs.hpp" -#include "bondi.hpp" +#include "flux.hpp" #include "grmhd_functions.hpp" /** - * Any functions related to KHARMA's treatment of boundary conditions. - * These largely build on/fill in Parthenon's boundary functions, - * which KHARMA uses to handle all MPI & periodic boundaries. + * This package has any functions related to KHARMA's treatment of "domain" boundary conditions: + * the exterior simulation edges, as opposed to internal meshblock boundaries. * - * Thus this Namespace is for outflow, reflecting, and problem-specific - * bounds, which KHARMA has to handle separately from Parthenon. + * This package implements Parthenon's "user" boundary conditions in order to add some + * features related to GRMHD. */ namespace KBoundaries { /** - * Any KHARMA-defined boundaries. - * These usually behave like Parthenon's Outflow in X1 and Reflect in X2, except - * that they operate on the fluid primitive variables p,u,u1,u2,u3. - * All other variables are unchanged. + * Choose which boundary conditions will be used based on inputs, + * declare any fields needed to store e.g. constant boundary conditions + */ +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + +/** + * Generic KHARMA override function for Parthenon domain boundary conditions. + * This is registered as the "user" boundary condition with Parthenon, and + * replaces Parthenon's reflecting or outflow boundary conditions wherever those + * would be applied. * - * These functions also handle calling through to problem-defined boundaries e.g. Bondi outer X1 + * Mostly calls "DefaultBoundary," unless overridden by a problem. * - * LOCKSTEP: these functions respect P and return consistent P<->U + * LOCKSTEP: respects P and return consistent P<->U + */ +void ApplyBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse); +// Template version to conform to Parthenon's calling convention. See above. +template +inline void ApplyBoundaryTemplate(std::shared_ptr> &rc, bool coarse) +{ ApplyBoundary(rc, domain, coarse); } + +/** + * Boundary conditions when not overridden by a problem (or handled by Parthenon). + * Outflow boundaries in X1 with an optional check for inflow, Reflecting boundaries in X2 + */ +void DefaultBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse); + +/** + * Dirichlet boundaries implementation. + * Problems can assign these to the KHARMA*Boundary callbacks, then fill the "bound.*" + * fields populated as a part of the "Boundaries" package. */ -void InnerX1(std::shared_ptr> &rc, bool coarse); -void OuterX1(std::shared_ptr> &rc, bool coarse); -void InnerX2(std::shared_ptr> &rc, bool coarse); -void OuterX2(std::shared_ptr> &rc, bool coarse); +void Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse); /** - * Fix fluxes on physical boundaries. Ensure no inflow flux, correct B fields on reflecting conditions. + * Fix fluxes on physical boundaries. + * 1. Ensure no inflow of density onto the domain + * 2. Ensure flux through the size-zero faces on poles is zero + * The latter may be unnecessary */ TaskStatus FixFlux(MeshData *rc); +// INTERNAL FUNCTIONS + /** - * Add a synchronization step to a task list tl, dependent upon taskID t_start, syncing mesh mc1 - * - * This sequence is used identically in several places, so it makes sense - * to define once and use elsewhere. - * TODO could make member of a HARMDriver/ImExDriver superclass? + * Check for inflowing material on an outflow boundary, and + * reset the velocity of such material so it is no longer inflowing. + */ +void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse); + +/** + * KHARMA is very particular about corner boundaries. + * In particular, we apply the outflow boundary over ALL X2 & X3. + * Then we apply the polar bound only where outflow is not applied, + * and periodic bounds only where neither other bound applies. + * The latter is accomplished regardless of Parthenon's definitions, + * since these functions are run after Parthenon's MPI boundary syncs & + * replace whatever they've done. + * However, the former must be added after the X2 boundary call, + * replacing the reflecting conditions in the X1/X2 corner (or in 3D, edge) + * with outflow conditions based on the updated ghost cells. */ -TaskID AddBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1); +void FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse); /** - * Single call to sync all boundary conditions. - * Used anytime boundary sync is needed outside the usual loop of steps. + * We apply Parthenon's boundary condition implementations, which are not GR-aware. + * When applied to the magnetic field values, the result must be scaled by the relative change + * in metric determinant. This function applies that change. */ -void SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds=true); +void CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse); /** - * Check for flow into simulation and reset velocity to eliminate it - * TODO does Parthenon do something like this for outflow bounds already? - * - * @param type: 0 to check outflow from EH, 1 to check inflow from outer edge + * Check for velocity toward the simulation domain in a zone, and eliminate it. */ KOKKOS_INLINE_FUNCTION void check_inflow(const GRCoordinates &G, const VariablePack& P, const IndexDomain domain, const int& index_u1, const int& k, const int& j, const int& i) { + // TODO fewer temporaries? Real uvec[NVEC], ucon[GR_DIM]; VLOOP uvec[v] = P(index_u1 + v, k, j, i); GRMHD::calc_ucon(G, uvec, k, j, i, Loci::center, ucon); diff --git a/kharma/boundaries.cpp b/kharma/boundaries/boundaries_forked_cpp.txt similarity index 97% rename from kharma/boundaries.cpp rename to kharma/boundaries/boundaries_forked_cpp.txt index 335393ed..34e5fde6 100644 --- a/kharma/boundaries.cpp +++ b/kharma/boundaries/boundaries_forked_cpp.txt @@ -122,7 +122,7 @@ void OutflowX1(std::shared_ptr> &rc, IndexDomain domain, boo // Inflow check if (check_inflow) { pmb->par_for("OutflowX1_check", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); @@ -131,7 +131,7 @@ void OutflowX1(std::shared_ptr> &rc, IndexDomain domain, boo // Normal operation: We copied both both prim & con GRMHD variables, but we want to apply // the boundaries based on just the former, so we run P->U pmb->par_for("OutflowX1_PtoU", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // TODO move these steps into FillDerivedDomain, make a GRMHD::PtoU call the last in that series // Correct primitive B if (m_p.B1 >= 0) @@ -218,7 +218,7 @@ void ReflectX2(std::shared_ptr> &rc, IndexDomain domain, boo if (!prim_ghosts) { // Normal operation: see above pmb->par_for("ReflectX2_PtoU", ks_e, ke_e, jbs, jbe, ics, ice, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { if (m_p.B1 >= 0) VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); @@ -290,7 +290,7 @@ void ReflectX1(std::shared_ptr> &rc, IndexDomain domain, boo */ int idx = ghosts_map["prims.uvec"].first; pmb->par_for("ReflectX1", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_3D { // Hyerin (02/13/23) only do for velocities + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Hyerin (02/13/23) only do for velocities q(idx, k, j, i) = (-1.) * q(idx, k, j, (ref + add) + (ref - i)); q(idx+1, k, j, i) = q(idx+1, k, j, (ref + add) + (ref - i)); q(idx+2, k, j, i) = q(idx+2, k, j, (ref + add) + (ref - i)); @@ -299,7 +299,7 @@ void ReflectX1(std::shared_ptr> &rc, IndexDomain domain, boo if (!prim_ghosts) { // Normal operation: see above pmb->par_for("ReflectX1_PtoU", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { //if (m_p.B1 >= 0) //VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); @@ -414,7 +414,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (check_inflow_inner) { if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); } ); @@ -423,7 +423,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (check_inflow_outer) { if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { F.flux(X1DIR, m_rho, k, j, i) = m::max(F.flux(X1DIR, m_rho, k, j, i), 0.); } ); @@ -459,14 +459,14 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) // Hyerin (12/22/22) ensure no ghost zone B field change if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones } ); } if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones } ); diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index db6bd0c9..22d8118c 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -123,7 +123,6 @@ class CoordinateEmbedding { } // Convenience functions to get common things - // TODO add a gcon_embed, gdet_embed KOKKOS_INLINE_FUNCTION bool spherical() const { return mpark::visit( [&](const auto& self) { @@ -154,6 +153,14 @@ class CoordinateEmbedding { return false; } } + KOKKOS_INLINE_FUNCTION bool is_cart_minkowski() const + { + if (mpark::holds_alternative(base) && mpark::holds_alternative(transform)) { + return true; + } else { + return false; + } + } // Spell out the interface we take from BaseCoords // TODO add a gcon_embed, gdet_embed diff --git a/kharma/coordinates/coordinate_systems.hpp b/kharma/coordinates/coordinate_systems.hpp index 3d9aa89d..6030228e 100644 --- a/kharma/coordinates/coordinate_systems.hpp +++ b/kharma/coordinates/coordinate_systems.hpp @@ -262,7 +262,7 @@ class ExponentialTransform { KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const { Xembed[0] = Xnative[0]; - Xembed[1] = exp(Xnative[1]); + Xembed[1] = m::exp(Xnative[1]); #if LEGACY_TH Xembed[2] = excise(excise(Xnative[2], 0.0, SMALL), M_PI, SMALL); #else @@ -284,7 +284,7 @@ class ExponentialTransform { { gzero2(dxdX); dxdX[0][0] = 1.; - dxdX[1][1] = exp(Xnative[1]); + dxdX[1][1] = m::exp(Xnative[1]); dxdX[2][2] = 1.; dxdX[3][3] = 1.; } @@ -295,7 +295,7 @@ class ExponentialTransform { { gzero2(dXdx); dXdx[0][0] = 1.; - dXdx[1][1] = 1 / exp(Xnative[1]); + dXdx[1][1] = 1 / m::exp(Xnative[1]); dXdx[2][2] = 1.; dXdx[3][3] = 1.; } @@ -316,7 +316,7 @@ class ModifyTransform { KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const { Xembed[0] = Xnative[0]; - Xembed[1] = exp(Xnative[1]); + Xembed[1] = m::exp(Xnative[1]); #if LEGACY_TH const GReal th = M_PI*Xnative[2] + ((1. - hslope)/2.)*sin(2.*M_PI*Xnative[2]); Xembed[2] = excise(excise(th, 0.0, SMALL), M_PI, SMALL); @@ -340,7 +340,7 @@ class ModifyTransform { { gzero2(dxdX); dxdX[0][0] = 1.; - dxdX[1][1] = exp(Xnative[1]); + dxdX[1][1] = m::exp(Xnative[1]); dxdX[2][2] = M_PI - (hslope - 1.)*M_PI*cos(2.*M_PI*Xnative[2]); dxdX[3][3] = 1.; } @@ -351,7 +351,7 @@ class ModifyTransform { { gzero2(dXdx); dXdx[0][0] = 1.; - dXdx[1][1] = 1 / exp(Xnative[1]); + dXdx[1][1] = 1 / m::exp(Xnative[1]); dXdx[2][2] = 1 / (M_PI - (hslope - 1.)*M_PI*cos(2.*M_PI*Xnative[2])); dXdx[3][3] = 1.; } @@ -378,16 +378,16 @@ class FunkyTransform { KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const { Xembed[0] = Xnative[0]; - Xembed[1] = exp(Xnative[1]); + Xembed[1] = m::exp(Xnative[1]); const GReal thG = M_PI*Xnative[2] + ((1. - hslope)/2.)*sin(2.*M_PI*Xnative[2]); const GReal y = 2*Xnative[2] - 1.; const GReal thJ = poly_norm * y * (1. + m::pow(y/poly_xt,poly_alpha) / (poly_alpha + 1.)) + 0.5 * M_PI; #if LEGACY_TH - const GReal th = thG + exp(mks_smooth * (startx1 - Xnative[1])) * (thJ - thG); + const GReal th = thG + m::exp(mks_smooth * (startx1 - Xnative[1])) * (thJ - thG); Xembed[2] = excise(excise(th, 0.0, SMALL), M_PI, SMALL); #else - Xembed[2] = thG + exp(mks_smooth * (startx1 - Xnative[1])) * (thJ - thG); + Xembed[2] = thG + m::exp(mks_smooth * (startx1 - Xnative[1])) * (thJ - thG); #endif Xembed[3] = Xnative[3]; } @@ -406,7 +406,7 @@ class FunkyTransform { { gzero2(dxdX); dxdX[0][0] = 1.; - dxdX[1][1] = exp(Xnative[1]); + dxdX[1][1] = m::exp(Xnative[1]); dxdX[2][1] = -exp(mks_smooth * (startx1 - Xnative[1])) * mks_smooth * ( M_PI / 2. - @@ -417,7 +417,7 @@ class FunkyTransform { / (1 + poly_alpha)) - 1. / 2. * (1. - hslope) * sin(2. * M_PI * Xnative[2])); dxdX[2][2] = M_PI + (1. - hslope) * M_PI * cos(2. * M_PI * Xnative[2]) - + exp(mks_smooth * (startx1 - Xnative[1])) + + m::exp(mks_smooth * (startx1 - Xnative[1])) * (-M_PI + 2. * poly_norm * (1. diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index 508a9f82..7a0f45d9 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -164,7 +164,7 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { auto gdet_conn_local = G.gdet_conn_direct; Kokkos::parallel_for("init_geom", MDRangePolicy>({0,0}, {n2+1, n1+1}), - KOKKOS_LAMBDA_2D { + KOKKOS_LAMBDA (const int& j, const int& i) { // Iterate through locations. This could be done in fancy ways, but // this highlights what's actually going on. for (int iloc =0; iloc < NLOC; iloc++) { @@ -251,7 +251,7 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { ); if (CONN_CORRECTIONS) { Kokkos::parallel_for("geom_corrections", MDRangePolicy>({0,0}, {n2, n1}), - KOKKOS_LAMBDA_2D { + KOKKOS_LAMBDA (const int& j, const int& i) { // In the two directions the grid changes, make sure that we *exactly* // satisfy the req't gdet*conn^mu_mu_nu = d_nu gdet, when evaluated on faces // This will make the source term exactly balance the flux differences, diff --git a/kharma/coordinates/gr_coordinates.hpp b/kharma/coordinates/gr_coordinates.hpp index 68e50d6a..4d1a6f4b 100644 --- a/kharma/coordinates/gr_coordinates.hpp +++ b/kharma/coordinates/gr_coordinates.hpp @@ -159,29 +159,29 @@ KOKKOS_INLINE_FUNCTION void GRCoordinates::coord(const int& k, const int& j, con switch(loc) { case Loci::face1: - X[1] = x1f(i); - X[2] = x2v(j); - X[3] = x3v(k); + X[1] = Xf<1>(i); + X[2] = Xc<2>(j); + X[3] = Xc<3>(k); break; case Loci::face2: - X[1] = x1v(i); - X[2] = x2f(j); - X[3] = x3v(k); + X[1] = Xc<1>(i); + X[2] = Xf<2>(j); + X[3] = Xc<3>(k); break; case Loci::face3: - X[1] = x1v(i); - X[2] = x2v(j); - X[3] = x3f(k); + X[1] = Xc<1>(i); + X[2] = Xc<2>(j); + X[3] = Xf<3>(k); break; case Loci::center: - X[1] = x1v(i); - X[2] = x2v(j); - X[3] = x3v(k); + X[1] = Xc<1>(i); + X[2] = Xc<2>(j); + X[3] = Xc<3>(k); break; case Loci::corner: - X[1] = x1f(i); - X[2] = x2f(j); - X[3] = x3f(k); + X[1] = Xf<1>(i); + X[2] = Xf<2>(j); + X[3] = Xf<3>(k); break; } } diff --git a/kharma/current/current.cpp b/kharma/current/current.cpp index 42c82e57..961f27f6 100644 --- a/kharma/current/current.cpp +++ b/kharma/current/current.cpp @@ -34,9 +34,9 @@ #include "current.hpp" -std::shared_ptr Current::Initialize(ParameterInput *pin) +std::shared_ptr Current::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - auto pkg = std::make_shared("Current"); + auto pkg = std::make_shared("Current"); Params ¶ms = pkg->AllParams(); // 4-current jcon. Calculated only for output @@ -44,6 +44,8 @@ std::shared_ptr Current::Initialize(ParameterInput *pin) auto m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_fourvector); pkg->AddField("jcon", m); + pkg->BlockUserWorkBeforeOutput = Current::FillOutput; + return pkg; } @@ -75,7 +77,7 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockDatacellbounds.GetBoundsK(IndexDomain::entire); const IndexRange nv = IndexRange{0, NVEC-1}; pmb->par_for("get_center", nv.s, nv.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_VARS { + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { uvec_c(p, k, j, i) = 0.5*(uvec_old(p, k, j, i) + uvec_new(p, k, j, i)); B_P_c(p, k, j, i) = 0.5*(B_P_old(p, k, j, i) + B_P_new(p, k, j, i)); } @@ -87,7 +89,7 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockDatacellbounds.GetBoundsK(IndexDomain::interior); const IndexRange n4v = IndexRange{0, GR_DIM-1}; pmb->par_for("jcon_calc", n4v.s, n4v.e, kb_i.s, kb_i.e, jb_i.s, jb_i.e, ib_i.s, ib_i.e, - KOKKOS_LAMBDA_VEC { + KOKKOS_LAMBDA (const int &mu, const int &k, const int &j, const int &i) { // Get sqrt{-g}*F^{mu nu} at neighboring points const Real gF0p = get_gdet_Fcon(G, uvec_new, B_P_new, 0, mu, k, j, i); const Real gF0m = get_gdet_Fcon(G, uvec_old, B_P_old, 0, mu, k, j, i); @@ -101,9 +103,9 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockData(i)) + + (gF2p - gF2m) / (2. * G.Dxc<2>(j)) + + (gF3p - gF3m) / (2. * G.Dxc<3>(k))); } ); diff --git a/kharma/current/current.hpp b/kharma/current/current.hpp index 535f6bd2..deb05fe3 100644 --- a/kharma/current/current.hpp +++ b/kharma/current/current.hpp @@ -44,7 +44,7 @@ namespace Current /** * Initialize output field jcon */ -std::shared_ptr Initialize(ParameterInput *pin); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Fill outputs, namely jcon. Just calls CalculateCurrent below. diff --git a/kharma/debug.cpp b/kharma/debug.cpp index 0e01e246..4701c1ed 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -38,61 +38,8 @@ #include "floors.hpp" #include "grmhd_functions.hpp" -#include "mpi.hpp" #include "types.hpp" -// TODO have nice ways to print vectors, areas, geometry, etc for debugging new modules - -/** - * Counts occurrences of a particular floor bitflag - */ -int CountFFlag(MeshData *md, const int& flag_val, IndexDomain domain) -{ - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - // Pack variables - auto& fflag = md->PackVariables(std::vector{"fflag"}); - - // Get sizes - IndexRange ib = md->GetBoundsI(domain); - IndexRange jb = md->GetBoundsJ(domain); - IndexRange kb = md->GetBoundsK(domain); - IndexRange block = IndexRange{0, fflag.GetDim(5) - 1}; - - int n_flag; - Kokkos::Sum flag_ct(n_flag); - pmb0->par_reduce("count_fflag", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { - if (((int) fflag(b, 0, k, j, i)) & flag_val) ++local_result; - } - , flag_ct); - return n_flag; -} - -/** - * Counts occurrences of a particular inversion failure mode - */ -int CountPFlag(MeshData *md, const int& flag_val, IndexDomain domain) -{ - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - // Pack variables - auto& pflag = md->PackVariables(std::vector{"pflag"}); - - // Get sizes - IndexRange ib = md->GetBoundsI(domain); - IndexRange jb = md->GetBoundsJ(domain); - IndexRange kb = md->GetBoundsK(domain); - IndexRange block = IndexRange{0, pflag.GetDim(5) - 1}; - - int n_flag; - Kokkos::Sum flag_ct(n_flag); - pmb0->par_reduce("count_pflag", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { - if (((int) pflag(b, 0, k, j, i)) == flag_val) ++local_result; - } - , flag_ct); - return n_flag; -} - TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) { Flag("Checking ctop for NaNs"); @@ -113,16 +60,17 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) Kokkos::Sum zero_reducer(nzero); Kokkos::Sum nan_reducer(nnan); pmb0->par_reduce("ctop_zeros", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { if (ctop(b, dir-1, k, j, i) <= 0.) { ++local_result; } } , zero_reducer); pmb0->par_reduce("ctop_nans", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { if (m::isnan(ctop(b, dir-1, k, j, i))) { ++local_result; + printf("ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT } } , nan_reducer); @@ -141,7 +89,7 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) if (MPIRank0() && (nzero > 0 || nnan > 0)) { // TODO string formatting in C++ that doesn't suck - fprintf(stderr, "Max signal speed ctop was 0 or NaN, direction %d (%d zero, %d NaN)", dir, nzero, nnan); + printf("Max signal speed ctop was 0 or NaN, direction %d (%d zero, %d NaN)", dir, nzero, nnan); throw std::runtime_error("Bad ctop!"); } @@ -170,7 +118,7 @@ TaskStatus CheckNegative(MeshData *md, IndexDomain domain) int nless = 0; Kokkos::Sum sum_reducer(nless); pmb0->par_reduce("count_negative_U", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { if (rho_c(b, 0, k, j, i) < 0.) ++local_result; } , sum_reducer); @@ -179,12 +127,12 @@ TaskStatus CheckNegative(MeshData *md, IndexDomain domain) Kokkos::Sum sum_reducer_rho(nless_rho); Kokkos::Sum sum_reducer_u(nless_u); pmb0->par_reduce("count_negative_RHO", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { if (rho_p(b, 0, k, j, i) < 0.) ++local_result; } , sum_reducer_rho); pmb0->par_reduce("count_negative_UU", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { if (u_p(b, 0, k, j, i) < 0.) ++local_result; } , sum_reducer_u); @@ -213,178 +161,3 @@ TaskStatus CheckNegative(MeshData *md, IndexDomain domain) return TaskStatus::complete; } - -int CountPFlags(MeshData *md, IndexDomain domain, int verbose) -{ - Flag("Counting inversion failures"); - int nflags = 0; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - // Pack variables - auto& pflag = md->PackVariables(std::vector{"pflag"}); - - // Get sizes - IndexRange ib = md->GetBoundsI(domain); - IndexRange jb = md->GetBoundsJ(domain); - IndexRange kb = md->GetBoundsK(domain); - IndexRange block = IndexRange{0, pflag.GetDim(5) - 1}; - - // Count all nonzero values - Kokkos::Sum sum_reducer(nflags); - pmb0->par_reduce("count_all_pflags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { - if ((int) pflag(b, 0, k, j, i) > InversionStatus::success) ++local_result; - } - , sum_reducer); - - // Need the total on all ranks to evaluate the if statement below - static AllReduce n_tot; - n_tot.val = nflags; - n_tot.StartReduce(MPI_SUM); - while (n_tot.CheckReduce() == TaskStatus::incomplete); - nflags = n_tot.val; - - // If necessary, count each flag - // This is slow, but it can be slow: it's not called for normal operation - if (verbose > 0 && nflags > 0) { - // These are necessary because iterating enums still sucks - // Would love a clean single-spot way to do this... - std::vector all_status_vals = {InversionStatus::neg_input, - InversionStatus::max_iter, - InversionStatus::bad_ut, - InversionStatus::bad_gamma, - InversionStatus::neg_rho, - InversionStatus::neg_u, - InversionStatus::neg_rhou}; - std::vector all_status_names = {"Negative input", - "Hit max iter", - "Velocity invalid", - "Gamma invalid", - "Negative rho", - "Negative U", - "Negative rho & U"}; - - // Overlap reductions to save time - static Reduce n_cells_r; - n_cells_r.val = (block.e - block.s + 1) * (kb.e - kb.s + 1) * (jb.e - jb.s + 1) * (ib.e - ib.s + 1); - n_cells_r.StartReduce(0, MPI_SUM); - static std::vector>> reducers; - if (reducers.size() == 0) { - for (InversionStatus status : all_status_vals) { - std::shared_ptr> reducer = std::make_shared>(); - reducers.push_back(reducer); - } - } - for (int i=0; i < reducers.size(); ++i) { - reducers[i]->val = CountPFlag(md, all_status_vals[i], domain); - reducers[i]->StartReduce(0, MPI_SUM); - } - while (n_cells_r.CheckReduce() == TaskStatus::incomplete); - const int n_cells = n_cells_r.val; - std::vector n_status_present; - for (std::shared_ptr> reducer : reducers) { - while (reducer->CheckReduce() == TaskStatus::incomplete); - n_status_present.push_back(reducer->val); - } - - if (MPIRank0()) { - std::cout << "PFLAGS: " << nflags << " (" << (int)(((double) nflags )/n_cells * 100) << "% of all cells)" << std::endl; - if (verbose > 1) { - for (int i=0; i < all_status_vals.size(); ++i) { - if (n_status_present[i] > 0) std::cout << all_status_names[i] << ": " << n_status_present[i] << std::endl; - } - std::cout << std::endl; - } - } - - // TODO Print zone locations of bad inversions - } - - Flag("Counted"); - return nflags; -} - -int CountFFlags(MeshData *md, IndexDomain domain, int verbose) -{ - Flag("Couting floor hits"); - int nflags = 0; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - // Pack variables - auto& fflag = md->PackVariables(std::vector{"fflag"}); - - // Get sizes - IndexRange ib = md->GetBoundsI(domain); - IndexRange jb = md->GetBoundsJ(domain); - IndexRange kb = md->GetBoundsK(domain); - IndexRange block = IndexRange{0, fflag.GetDim(5) - 1}; - - // Count all nonzero values - Kokkos::Sum sum_reducer(nflags); - pmb0->par_reduce("count_all_fflags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE_INT { - if ((int) fflag(b, 0, k, j, i) != 0) ++local_result; - } - , sum_reducer); - - // Need this on all nodes to evaluate the following if statement - static AllReduce n_tot; - n_tot.val = nflags; - n_tot.StartReduce(MPI_SUM); - while (n_tot.CheckReduce() == TaskStatus::incomplete); - nflags = n_tot.val; - - if (verbose > 0 && nflags > 0) { - std::vector all_flag_vals = {HIT_FLOOR_GEOM_RHO, - HIT_FLOOR_GEOM_U, - HIT_FLOOR_B_RHO, - HIT_FLOOR_B_U, - HIT_FLOOR_TEMP, - HIT_FLOOR_GAMMA, - HIT_FLOOR_KTOT}; - std::vector all_flag_names = {"GEOM_RHO", - "GEOM_U", - "B_RHO", - "B_U", - "TEMPERATURE", - "GAMMA", - "KTOT"}; - - // Overlap reductions to save time - static Reduce n_cells_r; - n_cells_r.val = (block.e - block.s + 1) * (kb.e - kb.s + 1) * (jb.e - jb.s + 1) * (ib.e - ib.s + 1); - n_cells_r.StartReduce(0, MPI_SUM); - static std::vector>> reducers; - // TODO there's absolutely reduction-of-view examples. C'mon - if (reducers.size() == 0) { - for (int flag : all_flag_vals) { - std::shared_ptr> reducer = std::make_shared>(); - reducers.push_back(reducer); - } - } - for (int i=0; i < reducers.size(); ++i) { - reducers[i]->val = CountFFlag(md, all_flag_vals[i], domain); - reducers[i]->StartReduce(0, MPI_SUM); - } - while (n_cells_r.CheckReduce() == TaskStatus::incomplete); - const int n_cells = n_cells_r.val; - std::vector n_flag_present; - for (std::shared_ptr> reducer : reducers) { - while (reducer->CheckReduce() == TaskStatus::incomplete); - n_flag_present.push_back(reducer->val); - } - - if (MPIRank0()) { - std::cout << "FLOORS: " << nflags << " (" << (int)(((double) nflags) / n_cells * 100) << "% of all cells)" << std::endl; - if (verbose > 1) { - for (int i=0; i < all_flag_vals.size(); ++i) { - if (n_flag_present[i] > 0) std::cout << all_flag_names[i] << ": " << n_flag_present[i] << std::endl; - } - std::cout << std::endl; - } - } - } - - Flag("Counted"); - return nflags; -} diff --git a/kharma/debug.hpp b/kharma/debug.hpp index 2d40c6df..ec792836 100644 --- a/kharma/debug.hpp +++ b/kharma/debug.hpp @@ -34,9 +34,10 @@ #pragma once #include "decs.hpp" -#include "mpi.hpp" #include "types.hpp" +// TODO TODO Namespace + /** * Check the max signal speed (ctop) for 0-values or NaNs. * This is a final warning that something is very wrong and we should crash. @@ -48,38 +49,3 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain=IndexDomain: * That is: primitive rho, u, conserved rho*u^t */ TaskStatus CheckNegative(MeshData *md, IndexDomain domain=IndexDomain::interior); - -/** - * Function for counting & printing pflags. - * Note that domain::entire will double-count overlapping zones - */ -int CountPFlags(MeshData *md, IndexDomain domain=IndexDomain::interior, int verbose=0); - -/** - * Function for counting & printing pflags. - * Note that domain::entire will double-count overlapping zones - */ -int CountFFlags(MeshData *md, IndexDomain domain=IndexDomain::interior, int verbose=0); - -// Miscellaneous print functions. -KOKKOS_INLINE_FUNCTION void print_matrix(const std::string name, const double g[GR_DIM][GR_DIM], bool kill_on_nan=false) -{ - // Print a name and a matrix - printf("%s:\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n", name.c_str(), - g[0][0], g[0][1], g[0][2], g[0][3], g[1][0], g[1][1], g[1][2], - g[1][3], g[2][0], g[2][1], g[2][2], g[2][3], g[3][0], g[3][1], - g[3][2], g[3][3]); - - if (kill_on_nan) { - // Additionally kill things if/when we hit NaNs - DLOOP2 if (m::isnan(g[mu][nu])) exit(-1); - } -} -KOKKOS_INLINE_FUNCTION void print_vector(const std::string name, const double v[GR_DIM], bool kill_on_nan=false) -{ - printf("%s: %g\t%g\t%g\t%g\n", name.c_str(), v[0], v[1], v[2], v[3]); - - if (kill_on_nan) { - DLOOP2 if (m::isnan(v[nu])) exit(-1); - } -} diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 93464f55..87bcaee5 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -53,20 +53,21 @@ // Libraries I need directly #include "Kokkos_Core.hpp" -#if 1 -// Resolve math functions to new Kokkos versions. Fast? +#if 0 +// Resolve math functions to new Kokkos versions. Faster, maybe namespace m = Kokkos::Experimental; #else // Resolve to standard library namespace m = std; #endif -// TODO CUDA? +// TODO CUDA library explicitly? // Bare Parthenon defs // Anything more leads to circular deps from gr_coordinates.hpp -// TODO update, this was from very early Parthenon +// TODO update (carefully), this was from very early Parthenon #include "parthenon_arrays.hpp" #include "parthenon_mpi.hpp" +#include "globals.hpp" #include "bvals/bvals_interfaces.hpp" #include "mesh/domain.hpp" @@ -81,6 +82,10 @@ using GReal = double; #define SMALL 1e-20 // GEOMETRY +// This stuff needs to be in decs.h as it's used by functions in coordinates/, +// which must be imported *inside Parthenon* in order to use GRCoodrdinates +// in there +// TODO version DLOOP(mu,nu)? #define GR_DIM 4 #define DLOOP1 for(int mu = 0; mu < GR_DIM; ++mu) #define DLOOP2 DLOOP1 for(int nu = 0; nu < GR_DIM; ++nu) @@ -89,18 +94,12 @@ using GReal = double; #define NVEC 3 #define VLOOP for(int v = 0; v < NVEC; ++v) -#define VLOOP2 VLOOP for(int w = 0; w < NVEC; ++w) - -// And an odd but useful loop for ex-iharm3d code -// This requires nvar to be defined in caller! -// It is not a const/global anymore. So, use this loop carefully -#define PLOOP for(int ip=0; ip < nvar; ++ip) // Useful Enums to avoid lots of #defines // See following functions and coord() in gr_coordinates.hpp to // get an idea of these locations. All faces/corner are *left* of center #define NLOC 5 -enum Loci{face1=0, face2, face3, center, corner}; +enum class Loci{face1=0, face2, face3, center, corner}; // Return the face location corresponding to the direction 'dir' KOKKOS_INLINE_FUNCTION Loci loc_of(const int& dir) @@ -134,43 +133,28 @@ KOKKOS_INLINE_FUNCTION int dir_of(const Loci loc) } } -// Emulate old names for possible stronger typing later, -// and for readability -// TODO specify ParArrayXD instead of generic? +#ifdef MPI_PARALLEL +/** + * Am I rank 0? Saves typing vs comparing the global every time + */ +inline bool MPIRank0() +{ + return (parthenon::Globals::my_rank == 0 ? true : false); +} +#else +/** + * Am I rank 0? Saves typing vs comparing the global every time. + * DUMMY function for no-MPI case: constexpr return for slight optimizations. + */ +inline bool MPIRank0() { return true; } +#endif // MPI_PARALLEL + +// A few generic "NDArray" overloads for readability. +// TODO torn on futures of these, as they're used inconsistently +// Shape+3D ("Grid") arrays using GridScalar = parthenon::ParArrayND; using GridVector = parthenon::ParArrayND; -using GridVars = parthenon::ParArrayND; // TODO ELIM -using GridInt = parthenon::ParArrayND; - +// Shape+2D ("Geom") versions for symmetric geometry using GeomScalar = parthenon::ParArrayND; -using GeomVector = parthenon::ParArrayND; using GeomTensor2 = parthenon::ParArrayND; using GeomTensor3 = parthenon::ParArrayND; - -// Specific lambdas for our array shapes -#define KOKKOS_LAMBDA_1D KOKKOS_LAMBDA (const int& i) -#define KOKKOS_LAMBDA_2D KOKKOS_LAMBDA (const int& j, const int& i) -#define KOKKOS_LAMBDA_3D KOKKOS_LAMBDA (const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_4D KOKKOS_LAMBDA (const int& l, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_5D KOKKOS_LAMBDA (const int& m, const int& l, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_VARS KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_VEC KOKKOS_LAMBDA (const int &mu, const int &k, const int &j, const int &i) -// Same things for mesh-wide ops -#define KOKKOS_LAMBDA_MESH_1D KOKKOS_LAMBDA (const int& b, const int& i) -#define KOKKOS_LAMBDA_MESH_2D KOKKOS_LAMBDA (const int& b, const int& j, const int& i) -#define KOKKOS_LAMBDA_MESH_3D KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_MESH_4D KOKKOS_LAMBDA (const int& b, const int& l, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_MESH_5D KOKKOS_LAMBDA (const int& b, const int& m, const int& l, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_MESH_VARS KOKKOS_LAMBDA (const int& b, const int &p, const int &k, const int &j, const int &i) -#define KOKKOS_LAMBDA_MESH_VEC KOKKOS_LAMBDA (const int& b, const int &mu, const int &k, const int &j, const int &i) - -// TODO separate macros for return type if this becomes a thing? Or don't macro at all -#define KOKKOS_LAMBDA_1D_REDUCE KOKKOS_LAMBDA (const int &i, parthenon::Real &local_result) -// This is used for timestep and divB, which are explicitly double -#define KOKKOS_LAMBDA_2D_REDUCE KOKKOS_LAMBDA (const int &j, const int &i, double &local_result) -#define KOKKOS_LAMBDA_3D_REDUCE KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) -#define KOKKOS_LAMBDA_3D_REDUCE_INT KOKKOS_LAMBDA (const int &k, const int &j, const int &i, int &local_result) -// Versions for full mesh -#define KOKKOS_LAMBDA_MESH_3D_REDUCE KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) -#define KOKKOS_LAMBDA_MESH_3D_REDUCE_INT KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) -#define KOKKOS_LAMBDA_MESH_4D_REDUCE KOKKOS_LAMBDA (const int &b, const int &v, const int &k, const int &j, const int &i, double &local_result) diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp new file mode 100644 index 00000000..da489215 --- /dev/null +++ b/kharma/driver/imex_step.cpp @@ -0,0 +1,292 @@ +/* + * File: imex_step.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kharma_driver.hpp" + +#include "decs.hpp" + +//Packages +#include "b_flux_ct.hpp" +#include "b_cd.hpp" +#include "b_cleanup.hpp" +#include "electrons.hpp" +#include "grmhd.hpp" +#include "wind.hpp" +// Other headers +#include "boundaries.hpp" +#include "debug.hpp" +#include "flux.hpp" +#include "resize_restart.hpp" +#include "implicit.hpp" + +#include +#include +#include + +TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int stage) +{ + Flag("Generating default task collection"); + // Reminder that this list is created BEFORE any of the list contents are run! + // Prints or function calls here will likely not do what you want: instead, add to the list by calling tl.AddTask() + + TaskCollection tc; + TaskID t_none(0); + + // Which packages we've loaded affects which tasks we'll add to the list + auto& pkgs = blocks[0]->packages.AllPackages(); + auto& driver_pkg = pkgs.at("Driver")->AllParams(); + const bool use_electrons = pkgs.count("Electrons"); + const bool use_b_cleanup = pkgs.count("B_Cleanup"); + const bool use_implicit = pkgs.count("Implicit"); + const bool use_jcon = pkgs.count("Current"); + const bool use_linesearch = (use_implicit) ? pkgs.at("Implicit")->Param("linesearch") : false; + + // Allocate the fluid states ("containers") we need for each block + for (auto& pmb : blocks) { + // first make other useful containers + auto &base = pmb->meshblock_data.Get(); + if (stage == 1) { + pmb->meshblock_data.Add("dUdt", base); + for (int i = 1; i < integrator->nstages; i++) + pmb->meshblock_data.Add(integrator->stage_name[i], base); + + if (use_jcon) { + // At the end of the step, updating "mbd_sub_step_final" updates the base + // So we have to keep a copy at the beginning to calculate jcon + pmb->meshblock_data.Add("preserve", base); + } + + if (use_implicit) { + // When solving, we need a temporary copy with any explicit updates, + // but not overwriting the beginning- or mid-step values + pmb->meshblock_data.Add("solver", base); + if (use_linesearch) { + // Need an additional state for linesearch + pmb->meshblock_data.Add("linesearch", base); + } + } + } + } + + //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); + + // Big synchronous region: get & apply fluxes to advance the fluid state + // num_partitions is nearly always 1 + const int num_partitions = pmesh->DefaultNumPartitions(); + TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = single_tasklist_per_pack_region[i]; + // Container names: + // '_full_step_init' refers to the fluid state at the start of the full time step (Si in iharm3d) + // '_sub_step_init' refers to the fluid state at the start of the sub step (Ss in iharm3d) + // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) + // '_flux_src' refers to the mesh object corresponding to -divF + S + // '_solver' refers to the fluid state passed to the Implicit solver. At the end of the solve + // '_linesearch' refers to the fluid state updated while performing a linesearch in the solver + // copy P and U from solver state to sub_step_final state. + auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); + auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage - 1], i); + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); + // Normally we put explicit update in md_solver, then add implicitly-evolved variables and copy back. + // If we're not doing an implicit solve at all, just write straight to sub_step_final + std::shared_ptr> &md_solver = (use_implicit) ? pmesh->mesh_data.GetOrAdd("solver", i) : md_sub_step_final; + + // Start receiving flux corrections and ghost cells + namespace cb = parthenon::cell_centered_bvars; + auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_flux = t_start_recv_bound; + if (pmesh->multilevel) + t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + + // Calculate the flux of each variable through each face + // This reconstructs the primitives (P) at faces and uses them to calculate fluxes + // of the conserved variables (U) through each face. + const KReconstruction::Type& recon = driver_pkg.Get("recon"); + auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_bound, tl, recon, md_sub_step_init.get()); + + // If we're in AMR, correct fluxes from neighbors + auto t_flux_bounds = t_fluxes; + if (pmesh->multilevel) { + tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + } + + // Any package modifications to the fluxes. e.g.: + // 1. CT calculations for B field transport + // 2. Zero fluxes through poles + // etc + auto t_fix_flux = tl.AddTask(t_flux_bounds, Packages::FixFlux, md_sub_step_init.get()); + + // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" + auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); + + // Add any source terms: geometric \Gamma * T, wind, damping, etc etc + auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); + + // UPDATE VARIABLES + // This block is designed to intelligently update a set of variables partially marked "Implicit" + // and partially "Explicit," by first doing any explicit updates, then using them as elements + // of the "guess" for the implicit solve + + // Update the explicitly-evolved variables using the source term + // Add any proportion of the step start required by the integrator (e.g., RK2) + auto t_avg_data = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent}), + md_sub_step_init.get(), md_full_step_init.get(), + integrator->gam0[stage-1], integrator->gam1[stage-1], + md_solver.get()); + // apply du/dt to the result + auto t_update = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent}), + md_solver.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_solver.get()); + + // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init + auto t_copy_prims = t_none; + if (!pkgs.at("GRMHD")->Param("implicit")) { + t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + md_sub_step_init.get(), md_solver.get()); + } + + // Make sure the primitive values of *explicitly-evolved* variables are updated. + // Each package should have a guard which makes UtoP a no-op if it's implicitly evolved + auto t_explicit_UtoP = tl.AddTask(t_copy_prims, Packages::MeshUtoP, md_solver.get(), IndexDomain::interior, false); + + // Done with explicit update + auto t_explicit = t_explicit_UtoP; + + auto t_implicit = t_explicit; + if (use_implicit) { + // Extra containers for implicit solve + std::shared_ptr> &md_linesearch = (use_linesearch) ? pmesh->mesh_data.GetOrAdd("linesearch", i) : md_solver; + + // Copy the current state of any implicitly-evolved vars (at least the prims) in as a guess. + // This sets md_solver = md_sub_step_init + auto t_copy_guess = tl.AddTask(t_sources, Copy, std::vector({Metadata::GetUserFlag("Implicit")}), + md_sub_step_init.get(), md_solver.get()); + + auto t_guess_ready = t_explicit | t_copy_guess; + + // The `solver` MeshData object now has the implicit primitives corresponding to initial/half step and + // explicit variables have been updated to match the current step. + // Copy the primitives to the `linesearch` MeshData object if linesearch was enabled. + auto t_copy_linesearch = t_guess_ready; + if (use_linesearch) { + t_copy_linesearch = tl.AddTask(t_guess_ready, Copy, std::vector({Metadata::GetUserFlag("Primitive")}), + md_solver.get(), md_linesearch.get()); + } + + + // Time-step implicit variables by root-finding the residual. + // This calculates the primitive values after the substep for all "isImplicit" variables -- + // no need for separately adding the flux divergence or calling UtoP + auto t_implicit_step = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), + md_flux_src.get(), md_linesearch.get(), md_solver.get(), integrator->beta[stage-1] * integrator->dt); + + // Copy the entire solver state (everything defined on the grid, i.e. 'Cell') into the final state md_sub_step_final + // If we're entirely explicit, we just declare these equal + t_implicit = tl.AddTask(t_implicit_step, Copy, std::vector({Metadata::Cell}), + md_solver.get(), md_sub_step_final.get()); + + } + + // Apply all floors & limits (GRMHD,EMHD,etc), but do *not* immediately correct UtoP failures with FixUtoP -- + // rather, we will synchronize (including pflags!) first. + // With an extra ghost zone, this *should* still allow binary-similar evolution between numbers of mesh blocks, + // but hasn't been tested to do so yet. + auto t_floors = tl.AddTask(t_implicit, Packages::MeshApplyFloors, md_sub_step_final.get(), IndexDomain::interior); + + KHARMADriver::AddMPIBoundarySync(t_floors, tl, md_sub_step_final); + } + + // Async Region: Any post-sync tasks. Fixups, timestep & AMR tagging. + TaskRegion &async_region2 = tc.AddRegion(blocks.size()); + for (int i = 0; i < blocks.size(); i++) { + auto &pmb = blocks[i]; + auto &tl = async_region2[i]; + auto &mbd_sub_step_init = pmb->meshblock_data.Get(integrator->stage_name[stage-1]); + auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); + + // If we're evolving the GRMHD variables explicitly, we need to fix UtoP variable inversion failures + // Syncing bounds before calling this, and then running it over the whole domain, will make + // behavior for different mesh breakdowns much more similar (identical?), since bad zones in + // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk + auto t_fix_p = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); + + auto t_set_bc = tl.AddTask(t_fix_p, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + + // Any package- (likely, problem-) specific source terms which must be applied to primitive variables + // Apply these only after the final step so they're operator-split + auto t_prim_source = t_set_bc; + if (stage == integrator->nstages) { + t_prim_source = tl.AddTask(t_set_bc, Packages::BlockApplyPrimSource, mbd_sub_step_final.get()); + } + // Electron heating goes where it does in the KHARMA Driver, for the same reasons + auto t_heat_electrons = t_prim_source; + if (use_electrons) { + t_heat_electrons = tl.AddTask(t_prim_source, Electrons::ApplyElectronHeating, + mbd_sub_step_init.get(), mbd_sub_step_final.get()); + } + + // Make sure *all* conserved vars are synchronized at step end + auto t_ptou = tl.AddTask(t_heat_electrons, Flux::BlockPtoU, mbd_sub_step_final.get(), IndexDomain::entire, false); + + auto t_step_done = t_ptou; + + // Estimate next time step based on ctop + if (stage == integrator->nstages) { + auto t_new_dt = + tl.AddTask(t_step_done, Update::EstimateTimestep>, mbd_sub_step_final.get()); + + // Update refinement + if (pmesh->adaptive) { + auto tag_refine = tl.AddTask( + t_step_done, parthenon::Refinement::Tag>, mbd_sub_step_final.get()); + } + } + } + + // Second boundary sync: + // ensure that primitive variables in ghost zones are *exactly* + // identical to their physical counterparts, now that they have been + // modified on each rank. + const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); + if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + + + return tc; +} + diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp new file mode 100644 index 00000000..aead10ca --- /dev/null +++ b/kharma/driver/kharma_driver.cpp @@ -0,0 +1,275 @@ + +/* + * File: kharma_driver.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kharma_driver.hpp" + +#include "boundaries.hpp" +#include "flux.hpp" +// GetFlux +#include "get_flux.hpp" + +std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + Flag("Initializing KHARMA Driver"); + // This function builds and returns a "KHARMAPackage" object, which is a light + // superset of Parthenon's "StateDescriptor" class for packages. + // The most important part of this object is a member of type "Params", + // which acts more or less like a Python dictionary: + // it puts values into a map of names->objects, where "objects" are usually + // floats, strings, and ints, but can be arbitrary classes. + // This "dictionary" is mostly immutable, and should always be treated as immutable, + // except in the "Globals" package. + auto pkg = std::make_shared("Driver"); + Params ¶ms = pkg->AllParams(); + + // Driver options + // The two current drivers are "kharma" or "imex", with the former being the usual KHARMA + // driver, and the latter supporting implicit stepping of some or all variables + // Mostly, packages should react to the "sync_prims" option and any option they + bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); + std::string driver_type = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); + params.Add("type", driver_type); + + // Record whether we marked the prims or cons as "FillGhost." This also translates to whether we consider + // primitive or conserved state to be the ground truth when updating values in a step. + bool sync_prims = !(driver_type == "kharma" || driver_type == "harm"); + params.Add("sync_prims", sync_prims); + + // Synchronize boundary variables twice. Ensures KHARMA is agnostic to the breakdown + // of meshblocks, at the cost of twice the MPI overhead, for potentially worse strong scaling. + // On by default, disable only after testing that, e.g., divB meets your requirements + bool two_sync = pin->GetOrAddBoolean("driver", "two_sync", true); + params.Add("two_sync", two_sync); + + // Don't even error on this. Use LLF unless the user is very clear otherwise. + std::string flux = pin->GetOrAddString("driver", "flux", "llf"); + params.Add("use_hlle", (flux == "hlle")); + + // Reconstruction scheme: plm, weno5, ppm... + // Allow an old parameter location + std::string recon = pin->GetOrAddString("driver", "reconstruction", + pin->GetOrAddString("GRMHD", "reconstruction", "weno5")); + if (recon == "donor_cell") { + params.Add("recon", KReconstruction::Type::donor_cell); + } else if (recon == "linear_vl") { + params.Add("recon", KReconstruction::Type::linear_vl); + } else if (recon == "linear_mc") { + params.Add("recon", KReconstruction::Type::linear_mc); + } else if (recon == "weno5") { + params.Add("recon", KReconstruction::Type::weno5); + } else { + std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; + std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; + throw std::invalid_argument("Unsupported reconstruction algorithm!"); + } + + // Field flags related to driver operation are defined outside any particular driver + // When using the Implicit package we need to globally distinguish implicitly and explicitly-updated variables + // All independent variables should be marked one or the other, + // so we define the flags here to avoid loading order issues + Metadata::AddUserFlag("Implicit"); + Metadata::AddUserFlag("Explicit"); + + // Keep track of numbers of variables + params.Add("n_explicit_vars", 0, true); + params.Add("n_implicit_vars", 0, true); + + return pkg; +} + +void KHARMADriver::AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage) +{ + const TaskID t_none(0); + + // MPI boundary exchange, done over MeshData objects/partitions at once + const int num_partitions = pmesh->DefaultNumPartitions(); // Usually 1 + TaskRegion &bound_sync = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = bound_sync[i]; + // This is a member function of KHARMADriver, so it inherits 'integrator' + auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + AddMPIBoundarySync(t_none, tl, mbd_sub_step_final); + } + + // Parthenon's call for bounds is MeshBlock, it sucks + int nblocks = pmesh->block_list.size(); + TaskRegion &async_region2 = tc.AddRegion(nblocks); + for (int i = 0; i < nblocks; i++) { + auto &pmb = pmesh->block_list[i]; + auto &tl = async_region2[i]; + auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); + tl.AddTask(t_none, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + } + +} + +TaskID KHARMADriver::AddMPIBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1) +{ + // Readability + using parthenon::cell_centered_bvars::SendBoundBufs; + using parthenon::cell_centered_bvars::ReceiveBoundBufs; + using parthenon::cell_centered_bvars::SetBounds; + constexpr auto local = parthenon::BoundaryType::local; + constexpr auto nonlocal = parthenon::BoundaryType::nonlocal; + // Send all, receive/set local after sending + auto send = + tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); + + auto t_send_local = + tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); + auto t_recv_local = + tl.AddTask(t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); + auto t_set_local = + tl.AddTask(t_recv_local, parthenon::cell_centered_bvars::SetBounds, mc1); + + // Receive/set nonlocal + auto t_recv = tl.AddTask( + t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); + auto t_set = tl.AddTask(t_recv, parthenon::cell_centered_bvars::SetBounds, mc1); + + // TODO add AMR prolongate/restrict here (and/or maybe option not to?) + + return t_set | t_set_local; +} + +void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) +{ + Flag("Syncing all bounds"); + TaskID t_none(0); + + // If we're using the ImEx driver, where primitives are fundamental, AddMPIBoundarySync() + // will only sync those, and we can call PtoU over everything after. + // If "AddMPIBoundarySync" means syncing conserved variables, we have to call PtoU *before* + // the MPI sync operation, then recover the primitive vars *again* afterward. + auto pmesh = md->GetMeshPointer(); + bool sync_prims = pmesh->packages.Get("Driver")->Param("sync_prims"); + + // TODO clean this up when ApplyBoundaryConditions gets a MeshData version + auto &block_list = pmesh->block_list; + + if (sync_prims) { + // If we're syncing the primitive vars, we just sync once + TaskCollection tc; + auto tr = tc.AddRegion(1); + AddMPIBoundarySync(t_none, tr[0], md); + while (!tr.Execute()); + + // Then PtoU + for (auto &pmb : block_list) { + auto& rc = pmb->meshblock_data.Get(); + + Flag("Block fill Conserved"); + Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); + + if (apply_domain_bounds) { + Flag("Block physical bounds"); + // Physical boundary conditions + parthenon::ApplyBoundaryConditions(rc); + } + } + } else { + // If we're syncing the conserved vars... + // Honestly, the easiest way through this sync is: + // 1. PtoU everywhere + for (auto &pmb : block_list) { + auto& rc = pmb->meshblock_data.Get(); + Flag("Block fill conserved"); + Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); + } + + // 2. Sync MPI bounds like a normal step + TaskCollection tc; + auto tr = tc.AddRegion(1); + AddMPIBoundarySync(t_none, tr[0], md); + while (!tr.Execute()); + + // 3. UtoP everywhere + for (auto &pmb : block_list) { + auto& rc = pmb->meshblock_data.Get(); + + Flag("Block fill Derived"); + // Fill P again, including ghost zones + // But, sice we sync'd GRHD primitives already, + // leave those off + // (like we do in a normal boundary sync) + Packages::BlockUtoPExceptMHD(rc.get(), IndexDomain::entire); + + if (apply_domain_bounds) { + Flag("Block physical bounds"); + // Physical boundary conditions + parthenon::ApplyBoundaryConditions(rc); + } + } + } + + Flag("Sync'd"); +} + +TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md) +{ + // Calculate fluxes in each direction using given reconstruction + // Must be spelled out so as to generate each templated version of GetFlux<> to be available at runtime + // Details in flux/get_flux.hpp + using RType = KReconstruction::Type; + TaskID t_calculate_flux1, t_calculate_flux2, t_calculate_flux3; + switch (recon) { + case RType::donor_cell: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; + case RType::linear_mc: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; + case RType::linear_vl: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; + case RType::weno5: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; + case RType::ppm: + case RType::mp5: + case RType::weno5_lower_poles: + std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; + std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; + throw std::invalid_argument("Unsupported reconstruction algorithm!"); + } + return t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; +} \ No newline at end of file diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp new file mode 100644 index 00000000..bd131c4b --- /dev/null +++ b/kharma/driver/kharma_driver.hpp @@ -0,0 +1,144 @@ +/* + * File: kharma_driver.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "types.hpp" + +#include "reconstruction.hpp" + +using namespace parthenon; + +/** + * This is the "Driver" class for KHARMA. + * A Driver object orchestrates everything that has to be done to a mesh to constitute a step. + * This means handling RK2/4/predictor-corrector stepping + * + * Somewhat confusingly, but very conveniently, it is also a package; therefore, it defines + * a static member function Initialize(), which returns a StateDescriptor. + * Many things in that list are referenced by other packages dependent on this one. + * + */ +class KHARMADriver : public MultiStageDriver { + public: + KHARMADriver(ParameterInput *pin, ApplicationInput *app_in, Mesh *pm) : MultiStageDriver(pin, app_in, pm) {} + + static std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + + /** + * A Driver object orchestrates everything that has to be done to a mesh to take a step. + * The function MakeTaskCollection outlines everything to be done in one sub-step, + * so that the driver can repeat calls to create a predictor-corrector, RK2/4, etc. + * + * Unlike MHD, GRMHD must keep two forms of the variables: the conserved variables, and a set of + * "primitive" variables more amenable to reconstruction. To evolve the fluid, the code must: + * 1. Reconstruct the right- and left-going components at zone faces, given the primitive variables + * 2. Calculate the fluxes of conserved quantities through the faces + * 2a. Apply any fixes to fluxes (e.g., for the magnetic field) + * 3. Update conserved variables using their prior values the divergence of conserved fluxes + * 3a. Apply any source terms (e.g., the geometric term in GRMHD) + * 4. Recover primtive variables + * 4a. Apply any stability limits (floors) + * 4b. Fix any errors in recovering the primitives, re-apply floors + * 5. Apply any source terms (KEL), or calculate outputs (jcon) which require the change in primitive values + * + * This is before any synchronization between different blocks, etc, etc. + * Both task lists proceed roughly in this order, and you'll see the same broad outlines in both. + */ + TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); + TaskCollection MakeDefaultTaskCollection(BlockList_t &blocks, int stage); + + /** + * This "TaskCollection" (step) + * ImexDriver syncs primitive variables and treats them as fundamental, whereas HARMDriver syncs conserved variables. + * This allows ImexDriver to optionally use a semi-implicit step, adding a per-zone implicit solve via the 'Implicit' + * package, instead of just explicit RK2 time-stepping. This driver also allows explicit-only RK2 operation + */ + TaskCollection MakeImExTaskCollection(BlockList_t &blocks, int stage); + + /** + * A simple step for experimentation. Does NOT support MPI, + */ + TaskCollection MakeSimpleTaskCollection(BlockList_t &blocks, int stage); + + + static TaskID AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md); + + /** + * Add just the synchronization step to a task list tl, dependent upon taskID t_start, syncing mesh mc1 + * + * This sequence is used identically in several places, so it makes sense + * to define once and use elsewhere. + */ + void AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage); + + /** + * Add just the synchronization step to a task list tl, dependent upon taskID t_start, syncing mesh mc1 + * + * This sequence is used identically in several places, so it makes sense + * to define once and use elsewhere. + */ + static TaskID AddMPIBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1); + + /** + * Calculate the fluxes in each direction + */ + static TaskID AddFluxCalculation(TaskID& start, TaskList& tl, KReconstruction::Type recon, MeshData *md); + + /** + * Single call to sync all boundary conditions (MPI/internal and domain/physical boundaries) + * Used anytime boundary sync is needed outside the usual loop of steps. + */ + static void SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds=true); + + // TODO swapped versions of these + /** + * Copy variables matching 'flags' from 'source' to 'dest'. + * Mostly makes things easier to read. + */ + static TaskStatus Copy(std::vector flags, MeshData* source, MeshData* dest) + { + return Update::WeightedSumData, MeshData>(flags, source, source, 1., 0., dest); + } + + /** + * Scale a variable by 'norm'. + * Mostly makes things easier to read. + */ + static TaskStatus Scale(std::vector flags, MeshBlockData* source, Real norm) + { + return Update::WeightedSumData, MeshBlockData>(flags, source, source, norm, 0., source); + } + +}; \ No newline at end of file diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp new file mode 100644 index 00000000..3af0c72d --- /dev/null +++ b/kharma/driver/kharma_step.cpp @@ -0,0 +1,262 @@ +/* + * File: kharma_step.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kharma_driver.hpp" + +// TODO CLEAN +//Packages +#include "b_flux_ct.hpp" +#include "b_cd.hpp" +#include "b_cleanup.hpp" +#include "electrons.hpp" +#include "grmhd.hpp" +#include "wind.hpp" +// Other headers +#include "boundaries.hpp" +#include "debug.hpp" +#include "flux.hpp" +#include "resize_restart.hpp" +#include "implicit.hpp" + +#include +#include +#include + +TaskCollection KHARMADriver::MakeTaskCollection(BlockList_t &blocks, int stage) +{ + std::string driver_type = blocks[0]->packages.Get("Driver")->Param("type"); + if (driver_type == "imex") { + return MakeImExTaskCollection(blocks, stage); + } else if (driver_type == "simple") { + return MakeSimpleTaskCollection(blocks, stage); + } else { + return MakeDefaultTaskCollection(blocks, stage); + } +} + +TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int stage) +{ + Flag("Generating default task collection"); + // Reminder that this list is created BEFORE any of the list contents are run! + // Prints or function calls here will likely not do what you want: instead, add to the list by calling tl.AddTask() + + // TaskCollections are a collection of TaskRegions. + // Each TaskRegion can operate on eash meshblock separately, i.e. one MeshBlockData object (slower), + // or on a collection of MeshBlock objects called the MeshData + TaskCollection tc; + const TaskID t_none(0); + + // Which packages we load affects which tasks we'll add to the list + auto& pkgs = blocks[0]->packages.AllPackages(); + auto& driver_pkg = pkgs.at("Driver")->AllParams(); + const bool use_b_cleanup = pkgs.count("B_Cleanup"); + const bool use_electrons = pkgs.count("Electrons"); + const bool use_jcon = pkgs.count("Current"); + + // Allocate the fluid states ("containers") we need for each block + for (auto& pmb : blocks) { + // first make other useful containers + auto &base = pmb->meshblock_data.Get(); + if (stage == 1) { + pmb->meshblock_data.Add("dUdt", base); + for (int i = 1; i < integrator->nstages; i++) + pmb->meshblock_data.Add(integrator->stage_name[i], base); + + if (use_jcon) { + // At the end of the step, updating "mbd_sub_step_final" updates the base + // So we have to keep a copy at the beginning to calculate jcon + pmb->meshblock_data.Add("preserve", base); + } + } + } + + //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); + + // Big packed region: get and apply new fluxes on all the zones we control + const int num_partitions = pmesh->DefaultNumPartitions(); + TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = single_tasklist_per_pack_region[i]; + // Container names: + // '_full_step_init' refers to the fluid state at the start of the full time step (Si in iharm3d) + // '_sub_step_init' refers to the fluid state at the start of the sub step (Ss in iharm3d) + // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) + // '_flux_src' refers to the mesh object corresponding to -divF + S + auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); + auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage - 1], i); + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); + + // Start receiving flux corrections and ghost cells + namespace cb = parthenon::cell_centered_bvars; + auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_flux = t_start_recv_bound; + if (pmesh->multilevel) + t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + + // Calculate the flux of each variable through each face + // This reconstructs the primitives (P) at faces and uses them to calculate fluxes + // of the conserved variables (U) through each face. + const KReconstruction::Type& recon = driver_pkg.Get("recon"); + auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_bound, tl, recon, md_sub_step_init.get()); + + // If we're in AMR, correct fluxes from neighbors + auto t_flux_bounds = t_fluxes; + if (pmesh->multilevel) { + tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + } + + // Any package modifications to the fluxes. e.g.: + // 1. CT calculations for B field transport + // 2. Zero fluxes through poles + // etc + auto t_fix_flux = tl.AddTask(t_flux_bounds, Packages::FixFlux, md_sub_step_init.get()); + + // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" + auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); + + // Add any source terms: geometric \Gamma * T, wind, damping, etc etc + auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); + + // Perform the update using the source term + // Add any proportion of the step start required by the integrator (e.g., RK2) + auto t_avg_data = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent}), + md_sub_step_init.get(), md_full_step_init.get(), + integrator->gam0[stage-1], integrator->gam1[stage-1], + md_sub_step_final.get()); + // apply du/dt to the result + auto t_update = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent}), + md_sub_step_final.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_sub_step_final.get()); + + // UtoP needs a guess in order to converge, so we copy in sc0 + // (but only the fluid primitives!) Copying and syncing ensures that solves of the same zone + // on adjacent ranks are seeded with the same value, which keeps them (more) similar + auto t_copy_prims = t_update; + if (integrator->nstages > 1) { + t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + md_sub_step_init.get(), md_sub_step_final.get()); + } + + KHARMADriver::AddMPIBoundarySync(t_copy_prims, tl, md_sub_step_final); + } + + // Smaller meshblock region. This gets touchy because we want to keep ghost zones updated, + // so very commented + TaskRegion &async_region = tc.AddRegion(blocks.size()); + for (int i = 0; i < blocks.size(); i++) { + auto &pmb = blocks[i]; + auto &tl = async_region[i]; + //auto &base = pmb->meshblock_data.Get(); + auto &mbd_sub_step_init = pmb->meshblock_data.Get(integrator->stage_name[stage-1]); + auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); + + // At this point, we've sync'd all internal boundaries using the conserved + // variables. The physical boundaries (pole, inner/outer) are trickier, + // since they must be applied to the primitive variables rho,u,u1,u2,u3 + // but should apply to conserved forms of everything else. + + // This call fills the fluid primitive values in all physical zones, that is, including MPI boundaries but + // not the physical boundaries (which haven't been filled yet!) + // This relies on the primitives being calculated identically in MPI boundaries, vs their corresponding + // physical zones in the adjacent mesh block. To ensure this, we seed the solver with the same values + // in each case, by synchronizing them along with the conserved values above. + auto t_utop = tl.AddTask(t_none, Packages::BlockUtoP, mbd_sub_step_final.get(), IndexDomain::entire, false); + // As soon as we have primitive variables, apply floors + auto t_floors = tl.AddTask(t_utop, Packages::BlockApplyFloors, mbd_sub_step_final.get(), IndexDomain::entire); + + // Then, fix any inversions which failed. Fixups average the adjacent zones, so we want to work from + // post-floor data. Floors are re-applied after fixups. + auto t_fix_p = tl.AddTask(t_floors, Inverter::FixUtoP, mbd_sub_step_final.get()); + + // Domain (non-internal) boundary conditions: + // This is a parthenon call, but in spherical coordinates it will call the KHARMA functions in + // boundaries.cpp, which apply physical boundary conditions based on the primitive variables of GRHD, + // and based on the conserved forms for everything else. Note that because this is called *after* + // UtoP (since it needs bulk fluid primitives to apply GRMHD boundaries), this function + // must call UtoP *again* (for everything except the GRHD variables) to fill P in the ghost zones. + // This is why KHARMA packages need to implement their UtoP functions in the form + // UtoP(rc, domain, coarse): so that they can be run over just the boundary domains here. + auto t_set_bc = tl.AddTask(t_fix_p, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + + // Add primitive-variable source terms: + // In order to calculate dissipation, we must know the entropy at the beginning and end of the substep, + // and this must be calculated from the fluid primitive variables rho,u (and for stability, obey floors!). + // Only now do we have the end-of-step primitives in consistent, corrected forms. + // Luckily, ApplyElectronHeating should *not* need another synchronization of the ghost zones, as it is applied to + // all zones and has a stencil of only one zone. As with UtoP, this trusts that evaluations + // of the same zone match between MeshBlocks. + + // Any package- (likely, problem-) specific source terms which must be applied to primitive variables + // Apply these only after the final step so they're operator-split + auto t_prim_source = t_set_bc; + if (stage == integrator->nstages) { + t_prim_source = tl.AddTask(t_set_bc, Packages::BlockApplyPrimSource, mbd_sub_step_final.get()); + } + // Electron heating goes where it does in HARMDriver, for the same reasons + auto t_heat_electrons = t_prim_source; + if (use_electrons) { + t_heat_electrons = tl.AddTask(t_prim_source, Electrons::ApplyElectronHeating, + mbd_sub_step_init.get(), mbd_sub_step_final.get()); + } + + auto t_step_done = t_heat_electrons; + + // Estimate next time step based on ctop + if (stage == integrator->nstages) { + auto t_new_dt = + tl.AddTask(t_step_done, Update::EstimateTimestep>, mbd_sub_step_final.get()); + + // Update refinement + if (pmesh->adaptive) { + auto tag_refine = tl.AddTask( + t_step_done, parthenon::Refinement::Tag>, mbd_sub_step_final.get()); + } + } + } + + // Second boundary sync: + // ensure that primitive variables in ghost zones are *exactly* + // identical to their physical counterparts, now that they have been + // modified on each rank. + const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); + if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + + Flag("Generated"); + return tc; +} diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp new file mode 100644 index 00000000..3d86a819 --- /dev/null +++ b/kharma/driver/simple_step.cpp @@ -0,0 +1,167 @@ +/* + * File: simple_step.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kharma_driver.hpp" + +#include "inverter.hpp" +#include "flux.hpp" + +TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int stage) +{ + Flag("Generating non-MPI task collection"); + // This is probably incompatible with everything + + // TODO check for incompatibilities at some point: + // At least implicit, jcon output, various electrons tests, etc. + + TaskCollection tc; + TaskID t_none(0); + + // Which packages we've loaded affects which tasks we'll add to the list + auto& pkgs = blocks[0]->packages.AllPackages(); + auto& driver_pkg = pkgs.at("Driver")->AllParams(); + + // Allocate the fluid states ("containers") we need for each block + for (auto& pmb : blocks) { + auto &base = pmb->meshblock_data.Get(); + if (stage == 1) { + pmb->meshblock_data.Add("dUdt", base); + for (int i = 1; i < integrator->nstages; i++) + pmb->meshblock_data.Add(integrator->stage_name[i], base); + } + } + + //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); + + // Big synchronous region: get & apply fluxes to advance the fluid state + // num_partitions is nearly always 1 + const int num_partitions = pmesh->DefaultNumPartitions(); + TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = single_tasklist_per_pack_region[i]; + // Container names: + // '_full_step_init' refers to the fluid state at the start of the full time step (Si in iharm3d) + // '_sub_step_init' refers to the fluid state at the start of the sub step (Ss in iharm3d) + // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) + // '_flux_src' refers to the mesh object corresponding to -divF + S + auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); + auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage - 1], i); + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); + + // Calculate the flux of each variable through each face + // This reconstructs the primitives (P) at faces and uses them to calculate fluxes + // of the conserved variables (U) through each face. + const KReconstruction::Type& recon = driver_pkg.Get("recon"); + auto t_fluxes = KHARMADriver::AddFluxCalculations(t_none, tl, recon, md_sub_step_init.get()); + + // Any package modifications to the fluxes. e.g.: + // 1. CT calculations for B field transport + // 2. Zero fluxes through poles + // etc + auto t_fix_flux = tl.AddTask(t_fluxes, Packages::FixFlux, md_sub_step_init.get()); + + // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" + auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); + + // Add any source terms: geometric \Gamma * T, wind, damping, etc etc + auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); + + // Perform the update using the source term + // Add any proportion of the step start required by the integrator (e.g., RK2) + auto t_avg_data = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent}), + md_sub_step_init.get(), md_full_step_init.get(), + integrator->gam0[stage-1], integrator->gam1[stage-1], + md_sub_step_final.get()); + // apply du/dt to the result + auto t_update = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent}), + md_sub_step_final.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_sub_step_final.get()); + + // UtoP needs a guess in order to converge, so we copy in md_sub_step_init + auto t_copy_prims = t_update; + if (integrator->nstages > 1) { + t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + md_sub_step_init.get(), md_sub_step_final.get()); + } + + + // Make sure the primitive values are updated. + auto t_UtoP = tl.AddTask(t_copy_prims, Packages::MeshUtoP, md_sub_step_final.get(), IndexDomain::interior, false); + + // Apply any floors + auto t_floors = tl.AddTask(t_UtoP, Packages::MeshApplyFloors, md_sub_step_final.get(), IndexDomain::interior); + + // Boundary sync: neighbors must be available for FixUtoP below + KHARMADriver::AddMPIBoundarySync(t_floors, tl, md_sub_step_final); + } + + // Async Region: Any post-sync tasks. Fixups, timestep & AMR tagging. + TaskRegion &async_region2 = tc.AddRegion(blocks.size()); + for (int i = 0; i < blocks.size(); i++) { + auto &pmb = blocks[i]; + auto &tl = async_region2[i]; + auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); + + // If we're evolving the GRMHD variables explicitly, we need to fix UtoP variable inversion failures + // Syncing bounds before calling this, and then running it over the whole domain, will make + // behavior for different mesh breakdowns much more similar (identical?), since bad zones in + // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk + auto t_fix_p = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); + + auto t_set_bc = tl.AddTask(t_fix_p, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + + // Make sure *all* conserved vars are synchronized at step end + auto t_ptou = tl.AddTask(t_set_bc, Flux::BlockPtoU, mbd_sub_step_final.get(), IndexDomain::entire, false); + + auto t_step_done = t_ptou; + + // Estimate next time step based on ctop + if (stage == integrator->nstages) { + auto t_new_dt = + tl.AddTask(t_step_done, Update::EstimateTimestep>, mbd_sub_step_final.get()); + } + } + + // Second boundary sync: + // ensure that primitive variables in ghost zones are *exactly* + // identical to their physical counterparts, now that they have been + // modified on each rank. + const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); + if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + + return tc; +} \ No newline at end of file diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index c1d9b5eb..3eb294db 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -34,10 +34,15 @@ #include "electrons.hpp" #include "decs.hpp" +#include "flux.hpp" #include "grmhd.hpp" #include "kharma.hpp" +#include "gaussian.hpp" #include +#include + +#include using namespace parthenon; @@ -48,33 +53,34 @@ using namespace parthenon; namespace Electrons { -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { - auto pkg = std::make_shared("Electrons"); + auto pkg = std::make_shared("Electrons"); Params ¶ms = pkg->AllParams(); - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - // Evolution parameters Real gamma_e = pin->GetOrAddReal("electrons", "gamma_e", 4./3); params.Add("gamma_e", gamma_e); Real gamma_p = pin->GetOrAddReal("electrons", "gamma_p", 5./3); params.Add("gamma_p", gamma_p); - Real fel_0 = pin->GetOrAddReal("electrons", "fel_0", 0.01); - params.Add("fel_0", fel_0); + bool diss_sign = pin->GetOrAddBoolean("electrons", "diss_sign", true); + params.Add("diss_sign", diss_sign); + bool kel_lim = pin->GetOrAddBoolean("electrons", "kel_lim", true); + params.Add("kel_lim", kel_lim); // This is used only in constant model Real fel_const = pin->GetOrAddReal("electrons", "fel_constant", 0.1); params.Add("fel_constant", fel_const); + // This prevented spurious heating when heat_electrons used pre-floored dissipation bool suppress_highb_heat = pin->GetOrAddBoolean("electrons", "suppress_highb_heat", false); params.Add("suppress_highb_heat", suppress_highb_heat); + // Initialization + bool init_to_fel_0 = pin->GetOrAddBoolean("electrons", "init_to_fel_0", true); + params.Add("init_to_fel_0", init_to_fel_0); + Real fel_0 = pin->GetOrAddReal("electrons", "fel_0", 0.01); + params.Add("fel_0", fel_0); + // Floors Real tp_over_te_min = pin->GetOrAddReal("electrons", "tp_over_te_min", 0.001); params.Add("tp_over_te_min", tp_over_te_min); @@ -97,20 +103,53 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack bool do_sharma = pin->GetOrAddBoolean("electrons", "sharma", false); params.Add("do_sharma", do_sharma); - MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isElectrons = Metadata::AllocateNewFlag("Electrons"); - params.Add("ElectronsFlag", isElectrons); - - // General options for primitive and conserved variables in KHARMA - Metadata m_con = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, - Metadata::Restart, Metadata::Conserved, Metadata::WithFluxes, isElectrons}); - Metadata m_prim = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - isPrimitive, isElectrons}); + // Parse various mass and density units to set the different cooling rates + // These could maybe tie in with Parthenon::Units when we add radiation + // TODO pretty soon this can be a GetVector!!! + // std::vector masses = parse_list(pin->GetOrAddString("units", "MBH", "1.0")); + // if (masses != std::vector{1.0}) + // { + // std::vector> munits; + // for (int i=1; i <= masses.size(); ++i) { + // munits.push_back(parse_list(pin->GetString("units", "M_unit_" + std::to_string(i)))); + // } + + // if (MPIRank0() && packages->Get("Globals")->Param("verbose") > 0) { + // std::cout << "Using unit sets:" << std::endl; + // for (int i=0; i < masses.size(); ++i) { + // std::cout << std::endl << masses[i] << ":"; + // for (auto munit : munits[i]) { + // std::cout << " " << munit; + // } + // } + // std::cout << std::endl; + // } + // // This is a vector of Reals + // params.Add("masses", masses); + // // This is a vector of vectors of Reals + // params.Add("munits", munits); + // } + + // Default implicit iff GRMHD is done implicitly. TODO can we do explicit? + auto& driver = packages->Get("Driver")->AllParams(); + auto driver_type = driver.Get("type"); + bool grmhd_implicit = packages->Get("GRMHD")->Param("implicit"); // usually false + bool implicit_e = (driver_type == "imex" && pin->GetOrAddBoolean("electrons", "implicit", grmhd_implicit)); // so this false too + params.Add("implicit", implicit_e); + + Metadata::AddUserFlag("Electrons"); + MetadataFlag areWeImplicit = (implicit_e) ? Metadata::GetUserFlag("Implicit") + : Metadata::GetUserFlag("Explicit"); + + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + Metadata::WithFluxes, Metadata::FillGhost, areWeImplicit, Metadata::GetUserFlag("Electrons")}; + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + Metadata::Restart, areWeImplicit, Metadata::GetUserFlag("Electrons")}; // Total entropy, used to track changes int nKs = 1; - pkg->AddField("cons.Ktot", m_con); - pkg->AddField("prims.Ktot", m_prim); + pkg->AddField("cons.Ktot", flags_cons); + pkg->AddField("prims.Ktot", flags_prim); // Individual models // TO ADD A MODEL: @@ -120,50 +159,73 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // 4. Add heating model in ApplyElectronHeating, below if (do_constant) { nKs += 1; - pkg->AddField("cons.Kel_Constant", m_con); - pkg->AddField("prims.Kel_Constant", m_prim); + pkg->AddField("cons.Kel_Constant", flags_cons); + pkg->AddField("prims.Kel_Constant", flags_prim); } if (do_howes) { nKs += 1; - pkg->AddField("cons.Kel_Howes", m_con); - pkg->AddField("prims.Kel_Howes", m_prim); + pkg->AddField("cons.Kel_Howes", flags_cons); + pkg->AddField("prims.Kel_Howes", flags_prim); } if (do_kawazura) { nKs += 1; - pkg->AddField("cons.Kel_Kawazura", m_con); - pkg->AddField("prims.Kel_Kawazura", m_prim); + pkg->AddField("cons.Kel_Kawazura", flags_cons); + pkg->AddField("prims.Kel_Kawazura", flags_prim); } if (do_werner) { nKs += 1; - pkg->AddField("cons.Kel_Werner", m_con); - pkg->AddField("prims.Kel_Werner", m_prim); + pkg->AddField("cons.Kel_Werner", flags_cons); + pkg->AddField("prims.Kel_Werner", flags_prim); } if (do_rowan) { nKs += 1; - pkg->AddField("cons.Kel_Rowan", m_con); - pkg->AddField("prims.Kel_Rowan", m_prim); + pkg->AddField("cons.Kel_Rowan", flags_cons); + pkg->AddField("prims.Kel_Rowan", flags_prim); } if (do_sharma) { nKs += 1; - pkg->AddField("cons.Kel_Sharma", m_con); - pkg->AddField("prims.Kel_Sharma", m_prim); + pkg->AddField("cons.Kel_Sharma", flags_cons); + pkg->AddField("prims.Kel_Sharma", flags_prim); } // TODO if nKs == 1 then rename Kel_Whatever -> Kel? // TODO record nKs and find a nice way to loop/vector the device-side layout? - pkg->FillDerivedBlock = Electrons::FillDerivedBlock; + // Update variable numbers + if (implicit_e) { + int n_current = driver.Get("n_implicit_vars"); + driver.Update("n_implicit_vars", n_current+nKs); + } else { + int n_current = driver.Get("n_explicit_vars"); + driver.Update("n_explicit_vars", n_current+nKs); + } + + // Problem-specific fields + if (packages->Get("Globals")->Param("problem") == "driven_turbulence") { + std::vector s_vector({2}); + Metadata m_vector = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); + Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("grf_normalized", m_vector); + pkg->AddField("alfven_speed", m); + } + + pkg->BlockUtoP = Electrons::BlockUtoP; + return pkg; } -TaskStatus InitElectrons(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInput *pin) { + Flag("Initializing electron/fluid entropy values"); auto pmb = rc->GetBlockPointer(); - MetadataFlag isElectrons = pmb->packages.Get("Electrons")->Param("ElectronsFlag"); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); + // Don't initialize entropies if we've already done so e.g. in Hubble problem + if (!pmb->packages.Get("Electrons")->Param("init_to_fel_0")) { + return TaskStatus::complete; + } + // Need to distinguish KTOT from the other variables, so we record which it is PackIndexMap prims_map; - auto& e_P = rc->PackVariables({isElectrons, isPrimitive}, prims_map); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}, prims_map); const int ktot_index = prims_map["prims.Ktot"].first; // Just need these two from the rest of Prims GridScalar rho = rc->Get("prims.rho").data; @@ -178,7 +240,7 @@ TaskStatus InitElectrons(MeshBlockData *rc, ParameterInput *pin) int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); pmb->par_for("UtoP_electrons", 0, e_P.GetDim(4)-1, ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_VARS { + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { if (p == ktot_index) { // Initialize total entropy by definition, e_P(p, k, j, i) = (gam - 1.) * u(k, j, i) * m::pow(rho(k, j, i), -gam); @@ -189,20 +251,20 @@ TaskStatus InitElectrons(MeshBlockData *rc, ParameterInput *pin) } ); - // iharm3d syncs bounds here + // iharm3d syncs bounds here, but we do all that in PostInit + + Flag("Initialized electron/fluid entropy values"); return TaskStatus::complete; } -void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag(rc, "UtoP electrons"); auto pmb = rc->GetBlockPointer(); - MetadataFlag isElectrons = pmb->packages.Get("Electrons")->Param("ElectronsFlag"); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); // No need for a "map" here, we just want everything that fits these - auto& e_P = rc->PackVariables({isElectrons, isPrimitive}); - auto& e_U = rc->PackVariables({isElectrons, Metadata::Conserved}); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}); + auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::Conserved}); // And then the local density GridScalar rho_U = rc->Get("cons.rho").data; @@ -213,27 +275,49 @@ void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) int js = bounds.js(domain), je = bounds.je(domain); int ks = bounds.ks(domain), ke = bounds.ke(domain); pmb->par_for("UtoP_electrons", 0, e_P.GetDim(4)-1, ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_VARS { + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { e_P(p, k, j, i) = e_U(p, k, j, i) / rho_U(k, j, i); } ); +} + +void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "PtoU electrons"); + auto pmb = rc->GetBlockPointer(); + + PackIndexMap prims_map, cons_map; + auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + // And then the local density + GridScalar rho_P = rc->Get("cons.rho").data; + + const auto& G = pmb->coords; + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + int is = bounds.is(domain), ie = bounds.ie(domain); + int js = bounds.js(domain), je = bounds.je(domain); + int ks = bounds.ks(domain), ke = bounds.ke(domain); + pmb->par_for("PtoU_electrons", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Electrons::p_to_u(G, P, m_p, k, j, i, U, m_u); + } + ); } TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData *rc) -{ +{ // takes in '_sub_step_init' and '_sub_step_final' Flag(rc, "Applying electron heating"); auto pmb = rc->GetBlockPointer(); - MetadataFlag isElectrons = pmb->packages.Get("Electrons")->Param("ElectronsFlag"); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); // Need to distinguish different electron models // So far, Parthenon's maps of the same sets of variables are consistent, // so we only bother with one map of the primitives // TODO Parthenon can definitely build a pack from a map, though PackIndexMap prims_map, cons_map; - auto& P = rc_old->PackVariables({isPrimitive}, prims_map); - auto& P_new = rc->PackVariables({isPrimitive}, prims_map); + auto& P = rc_old->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto& P_new = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); auto& U_new = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); @@ -251,35 +335,41 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // This function (and any primitive-variable sources) needs to be run over the entire domain, // because the boundary zones have already been updated and so the same calculations must be applied // in order to keep them consistent. - // See harm_driver.cpp for the full picture of what gets updated when. + // See kharma_step.cpp for the full picture of what gets updated when. const IndexRange ib = rc->GetBoundsI(IndexDomain::entire); const IndexRange jb = rc->GetBoundsJ(IndexDomain::entire); const IndexRange kb = rc->GetBoundsK(IndexDomain::entire); pmb->par_for("heat_electrons", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); Real bsq = dot(Dtmp.bcon, Dtmp.bcov); - // Calculate the new total entropy in this cell - const Real kNew = (gam-1.) * P_new(m_p.UU, k, j, i) / m::pow(P_new(m_p.RHO, k, j, i) ,gam); + // Calculate the new total entropy in this cell considering heating + const Real k_energy_conserving = (gam-1.) * P_new(m_p.UU, k, j, i) / m::pow(P_new(m_p.RHO, k, j, i), gam); + + // Dissipation is the real entropy k_energy_conserving minus any advected entropy from the previous (sub-)step P_new(KTOT) + Real diss_tmp = (game-1.) / (gam-1.) * m::pow(P(m_p.RHO, k, j, i), gam - game) * (k_energy_conserving - P_new(m_p.KTOT, k, j, i)); + //this is eq27 ratio of heating: Qi/Qe advected entropy from prev step + // ^ denotes the solution corresponding to entropy conservation - // Dissipation is the real entropy kNew minus any advected entropy from the previous (sub-)step P_new(KTOT) - // Due to floors we can end up with diss==0 or even *slightly* <0, so we require it to be positive here // Under the flag "suppress_highb_heat", we set all dissipation to zero at sigma > 1. - const Real diss = (suppress_highb_heat && (bsq / P(m_p.RHO, k, j, i) > 1.)) ? 0.0 : - m::max((game-1.) / (gam-1.) * m::pow(P(m_p.RHO, k, j, i), gam - game) * (kNew - P_new(m_p.KTOT, k, j, i)), 0.0); + diss_tmp = (suppress_highb_heat && (bsq / P(m_p.RHO, k, j, i) > 1.)) ? 0.0 : diss_tmp; + + // Default is True diss_sign == Enforce nonnegative + // Due to floors we can end up with diss==0 or even *slightly* <0, so we require it to be positive here + const Real diss = pmb->packages.Get("Electrons")->Param("diss_sign") ? m::max(diss_tmp, 0.0) : diss_tmp; // Reset the entropy to measure next (sub-)step's dissipation - P_new(m_p.KTOT, k, j, i) = kNew; + P_new(m_p.KTOT, k, j, i) = k_energy_conserving; // We'll be applying floors inline as we heat electrons, so // we cache the floors as entropy limits so they'll be cheaper to apply. // Note tp_te_min -> kel_max & vice versa const Real kel_max = P(m_p.KTOT, k, j, i) * m::pow(P(m_p.RHO, k, j, i), gam - game) / - (tptemin * (gam - 1.) / (gamp-1.) + (gam-1.) / (game-1.)); + (tptemin * (gam - 1.) / (gamp-1.) + (gam-1.) / (game-1.)); //0.001 const Real kel_min = P(m_p.KTOT, k, j, i) * m::pow(P(m_p.RHO, k, j, i), gam - game) / - (tptemax * (gam - 1.) / (gamp-1.) + (gam-1.) / (game-1.)); + (tptemax * (gam - 1.) / (gamp-1.) + (gam-1.) / (game-1.)); //1000 // Note this differs a little from Ressler '15, who ensure u_e/u_g > 0.01 rather than use temperatures // The ion temperature is useful for a few models, cache it too. @@ -290,9 +380,17 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // Heat different electron passives based on different dissipation fraction models // Expressions here closely adapted (read: stolen) from implementation in iharm3d // courtesy of Cesar Diaz, see https://github.com/AFD-Illinois/iharm3d + + // In all of these the electron entropy stored value is the entropy conserving solution + // and then when updated it becomes the energy conserving solution if (m_p.K_CONSTANT >= 0) { const Real fel = fel_const; - P_new(m_p.K_CONSTANT, k, j, i) = clip(P_new(m_p.K_CONSTANT, k, j, i) + fel * diss, kel_min, kel_max); + // Default is true then enforce kel limits with clamp/clip, else no restrictions on kel + if (pmb->packages.Get("Electrons")->Param("kel_lim")) { + P_new(m_p.K_CONSTANT, k, j, i) = clip(P_new(m_p.K_CONSTANT, k, j, i) + fel * diss, kel_min, kel_max); + } else { + P_new(m_p.K_CONSTANT, k, j, i) += fel * diss; + } } if (m_p.K_HOWES >= 0) { const Real Tel = m::max(P(m_p.K_HOWES, k, j, i) * m::pow(P(m_p.RHO, k, j, i), game-1), SMALL); @@ -308,7 +406,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData const Real c3 = (Trat <= 1.) ? 18. + 5.*logTrat : 18.; const Real beta_pow = m::pow(beta, mbeta); - const Real qrat = 0.92 * (c2*c2 + beta_pow)/(c3*c3 + beta_pow) * exp(-1./beta) * m::sqrt(MP/ME * Trat); + const Real qrat = 0.92 * (c2*c2 + beta_pow)/(c3*c3 + beta_pow) * m::exp(-1./beta) * m::sqrt(MP/ME * Trat); const Real fel = 1./(1. + qrat); P_new(m_p.K_HOWES, k, j, i) = clip(P_new(m_p.K_HOWES, k, j, i) + fel * diss, kel_min, kel_max); } @@ -320,7 +418,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData const Real pres = P(m_p.RHO, k, j, i) * Tpr; // Proton pressure const Real beta = m::min(pres / bsq * 2, 1.e20);// If somebody enables electrons in a GRHD sim - const Real QiQe = 35. / (1. + m::pow(beta/15., -1.4) * exp(-0.1 / Trat)); + const Real QiQe = 35. / (1. + m::pow(beta/15., -1.4) * m::exp(-0.1 / Trat)); const Real fel = 1./(1. + QiQe); P_new(m_p.K_KAWAZURA, k, j, i) = clip(P_new(m_p.K_KAWAZURA, k, j, i) + fel * diss, kel_min, kel_max); } @@ -338,7 +436,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData const Real beta = pres / bsq * 2; const Real sigma = bsq / (P(m_p.RHO, k, j, i) + P(m_p.UU, k, j, i) + pg); const Real betamax = 0.25 / sigma; - const Real fel = 0.5 * exp(-m::pow(1 - beta/betamax, 3.3) / (1 + 1.2*m::pow(sigma, 0.7))); + const Real fel = 0.5 * m::exp(-m::pow(1 - beta/betamax, 3.3) / (1 + 1.2*m::pow(sigma, 0.7))); P_new(m_p.K_ROWAN, k, j, i) = clip(P_new(m_p.K_ROWAN, k, j, i) + fel * diss, kel_min, kel_max); } if (m_p.K_SHARMA >= 0) { @@ -350,54 +448,12 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData const Real fel = 1./(1.+1./QeQi); P_new(m_p.K_SHARMA, k, j, i) = clip(P_new(m_p.K_SHARMA, k, j, i) + fel * diss, kel_min, kel_max); } - - // Finally, make sure we update the conserved electron variables to keep them in sync - Electrons::p_to_u(G, P_new, m_p, k, j, i, U_new, m_u); + // Conserved variables are updated at the end of the step } ); - // A couple of the electron test problems add source terms - // TODO move this to dUdt with other source terms? - const std::string prob = pmb->packages.Get("GRMHD")->Param("problem"); - if (prob == "hubble") { - const Real v0 = pmb->packages.Get("GRMHD")->Param("v0"); - const Real ug0 = pmb->packages.Get("GRMHD")->Param("ug0"); - const Real t = pmb->packages.Get("Globals")->Param("time"); - const Real dt = pmb->packages.Get("Globals")->Param("dt_last"); // Close enough? - - pmb->par_for("hubble_Q_source_term", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - const Real Q = -(ug0 * v0 * (gam - 2) / m::pow(1 + v0 * t, 3)); - P_new(m_p.UU, k, j, i) += Q * dt; - // TODO all flux - GRMHD::p_to_u(G, P_new, m_p, gam, k, j, i, U_new, m_u); - } - ); - } else if (prob == "forced_MHD") { - // Gaussian random field: - // incompressible, sigma2 ~ k6 exp (-8k/kpeak), where kpeak = 4pi/L - - // - } - Flag(rc, "Applied"); return TaskStatus::complete; } -TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *rc) -{ - Flag(rc, "Printing electron diagnostics"); - - // Output any diagnostics after a step completes - - Flag(rc, "Printed"); - return TaskStatus::complete; -} - -void FillOutput(MeshBlock *pmb, ParameterInput *pin) -{ - // Any variables or diagnostics that should be computed especially for output to a file, - // but which are not otherwise updated. -} - } // namespace B_FluxCT diff --git a/kharma/electrons/electrons.hpp b/kharma/electrons/electrons.hpp index 63ca3fad..5d7cb64f 100644 --- a/kharma/electrons/electrons.hpp +++ b/kharma/electrons/electrons.hpp @@ -63,7 +63,7 @@ namespace Electrons { * For electrons, this means a total entropy Ktot to track dissipation, and electron entropies * for each model being run. */ -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * In addition to the standard functions, packages can include extras. This is called manually @@ -71,68 +71,45 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack * * Function in this package: Initialize electron temperatures when setting up the problem. Trivial. */ -TaskStatus InitElectrons(MeshBlockData *rc, ParameterInput *pin); +TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInput *pin); /** - * KHARMA requires two forms of the functions for obtaining and fixing the primitive values from - * conserved fluxes. - * KHARMA's version needs to take an IndexDomain enum and boundary "coarse" boolean, as it is called - * by KHARMA itself when updating boundary values (function UtoP below). The other version should take - * just the fluid state, to match Parthenon's calling convention for FillDerived functions. - * It's easiest to define them with these defaults in the header, register the FillDerived version as - * Parthenon's callback, and then add the UtoP version in kharma.cpp. + * Any implementation of UtoP needs to take an IndexDomain enum and boundary "coarse" boolean. + * This allows KHARMA to call it over the whole domain (IndexDomain::entire) or just on a boundary + * after conserved variables have been updated. * - * Defaults to entire domain, as the KHARMA algorithm relies on applying UtoP over ghost zones. + * Usually this should default to the entire domain, as the KHARMA algorithm relies on applying + * UtoP over ghost zones. * * Function in this package: Get the specific entropy primitive value, by dividing the total entropy K/(rho*u^0) */ -void UtoP(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); -inline void FillDerivedBlock(MeshBlockData *rc) { UtoP(rc); } +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); /** - * This heating step is custom for this package: - * it is added manually to the task list in harm_driver.cpp, just after the call to "FillDerived" - * a.k.a. "UtoP". For reasons mentioned there, it must update physical *and* boundary zones. - * - * It calculates how electrons should be heated and updates their entropy values, + * This heating step is custom for this package. It is added manually to any task list in the KHARMADriver, + * at the very end of the step. For reasons mentioned there & above, it must update *all* zones, incl. ghosts. + * + * The function calculates how electrons should be heated and updates their entropy values, * using each step's total dissipation (advected vs actual fluid entropy) * It applies any or all of several different esimates for this split, to each of the several different * primitive variables "prims.Kel_X" * Finally, it checks the results against a minimum and maximum temperature ratio T_protons/T_electrons * - * To recap re: floors: + * To recap re: floors: * This function expects two sets of values {rho0, u0, Ktot0} from rc_old and {rho1, u1} from rc, - * all of which obey all given floors + * all of which obey all given floors. * It produces end-of-substep values {Ktot1, Kel_X1, Kel_Y1, etc}, which are also guaranteed to obey floors * * TODO this function should update fflag to reflect temperature ratio floor hits */ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData *rc); -/** - * Diagnostics printed/computed after each step, called from kharma.cpp - * - * Function in this package: Currently nothing - */ -TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *rc); - -/** - * Fill fields which are calculated only for output to dump files, called from kharma.cpp - * - * Function in this package: Currently nothing - */ -void FillOutput(MeshBlock *pmb, ParameterInput *pin); - /** * KHARMA requires some method for getting conserved variables from primitives, as well. * * However, unlike UtoP, p_to_u is implemented device-side. That means that any - * package defining new primitive/conserved vars must add them to Flux::prim_to_flux - * in addition to providing a UtoP function. - * - * Some packages may wish to have their own local p_to_u functions as well, to avoid - * calling Flux::PtoU where not all conserved variables need to be calculated. This is - * an example. + * package defining new primitive/conserved vars must not only provide a prim_to_flux here, + * but add it to the list in Flux::prim_to_flux. */ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, const int& k, const int& j, const int& i, diff --git a/kharma/electrons/gaussian.hpp b/kharma/electrons/gaussian.hpp new file mode 100644 index 00000000..94a29135 --- /dev/null +++ b/kharma/electrons/gaussian.hpp @@ -0,0 +1,37 @@ +/* + * File: gaussian.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +float normalRand(); +void create_grf(int Nx1, int Nx2, double lx1, double lx2, double * dv1, double * dv2); diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 7f2f8833..4feffb19 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -33,9 +33,11 @@ */ #include "emhd.hpp" -#include "decs.hpp" +#include "emhd_limits.hpp" #include "emhd_sources.hpp" #include "emhd_utils.hpp" + +#include "decs.hpp" #include "grmhd.hpp" #include "kharma.hpp" @@ -46,36 +48,33 @@ using namespace parthenon; namespace EMHD { -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { - auto pkg = std::make_shared("EMHD"); + auto pkg = std::make_shared("EMHD"); Params ¶ms = pkg->AllParams(); - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - // EMHD Problem/Closure parameters // GRIM uses a callback to a problem-specific implementation which sets these // We share implementations in one function, controlled by these parameters // These are always necessary for performing EGRMHD. - bool higher_order_terms = pin->GetOrAddBoolean("emhd", "higher_order_terms", false); + bool higher_order_terms = pin->GetOrAddBoolean("emhd", "higher_order_terms", false); std::string closure_type = pin->GetOrAddString("emhd", "closure_type", "torus"); - Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); + // Should the EMHD sector feedback onto the ideal MHD variables? The default is 'yes'. + // So far it's just the viscous Bondi problem that doesn't require feedback + bool feedback = pin->GetOrAddBoolean("emhd", "feedback", true); + + Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); - Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); + Real viscosity_alpha = pin->GetOrAddReal("emhd", "viscosity_alpha", 1.0); Real kappa = pin->GetOrAddReal("emhd", "kappa", 1.0); Real eta = pin->GetOrAddReal("emhd", "eta", 1.0); EMHD_parameters emhd_params; emhd_params.higher_order_terms = higher_order_terms; + emhd_params.feedback = feedback; if (closure_type == "constant") { emhd_params.type = ClosureType::constant; } else if (closure_type == "sound_speed" || closure_type == "soundspeed") { @@ -95,25 +94,34 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack params.Add("emhd_params", emhd_params); // Slope reconstruction on faces. Always linear: default to MC unless we're using VL everywhere - if (packages.Get("GRMHD")->Param("recon") == ReconstructionType::linear_vl) { - params.Add("slope_recon", ReconstructionType::linear_mc); + if (packages->Get("Driver")->Param("recon") == KReconstruction::Type::linear_vl) { + params.Add("slope_recon", KReconstruction::Type::linear_vl); } else { - params.Add("slope_recon", ReconstructionType::linear_mc); + params.Add("slope_recon", KReconstruction::Type::linear_mc); } - // Floors specific to EMHD calculations? Currently only need to enforce bsq>0 in one denominator + // Apply limits on heat flux and pressure anisotropy from velocity space instabilities? + // We would want this for the torus runs but not for the test problems. + // For eg: we know that this affects the viscous bondi problem + bool enable_emhd_limits = pin->GetOrAddBoolean("floors", "emhd_limits", false) || + pin->GetOrAddBoolean("emhd", "limits", false); + // Only enable limits internally if we're actually doing EMHD + params.Add("enable_emhd_limits", enable_emhd_limits); + + // Update variable numbers + auto& driver = packages->Get("Driver")->AllParams(); + int n_current = driver.Get("n_implicit_vars"); + driver.Update("n_implicit_vars", n_current+2); - MetadataFlag isPrimitive = packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isEMHD = Metadata::AllocateNewFlag("EMHDFlag"); - params.Add("EMHDFlag", isEMHD); + Metadata::AddUserFlag("EMHD"); // General options for primitive and conserved scalar variables in ImEx driver // EMHD is supported only with imex driver and implicit evolution - MetadataFlag isImplicit = packages.Get("Implicit")->Param("ImplicitFlag"); + MetadataFlag isImplicit = packages->Get("Driver")->Param("ImplicitFlag"); Metadata m_con = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, isImplicit, - Metadata::Conserved, Metadata::WithFluxes, isEMHD}); + Metadata::Conserved, Metadata::WithFluxes, Metadata::GetUserFlag("EMHD")}); Metadata m_prim = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, isImplicit, - Metadata::FillGhost, Metadata::Restart, isPrimitive, isEMHD}); + Metadata::FillGhost, Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("EMHD")}); // Heat conduction pkg->AddField("cons.q", m_con); @@ -122,49 +130,66 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack pkg->AddField("cons.dP", m_con); pkg->AddField("prims.dP", m_prim); - // If we want to register an EMHD-specific UtoP for some reason? - // Likely we'll only use the post-step summary hook - //pkg->FillDerivedBlock = EMHD::FillDerived; - //pkg->PostFillDerivedBlock = EMHD::PostFillDerived; + // 4vel ucov and temperature Theta are needed as temporaries, but need to be grid-sized anyway. + // Allow keeping/saving them. + Metadata::AddUserFlag("EMHDTemporary"); + Metadata m_temp = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("EMHDTemporary")}); + pkg->AddField("Theta", m_temp); + std::vector fourv = {GR_DIM}; + Metadata m_temp_vec = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("EMHDTemporary")}, fourv); + pkg->AddField("ucov", m_temp_vec); + + // This works similarly to the fflag -- + // we register zones where limits on q and dP are hit + Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("eflag", m); + + pkg->AddSource = EMHD::AddSource; + + if (enable_emhd_limits) { + pkg->BlockApplyFloors = EMHD::ApplyEMHDLimits; + } + return pkg; } -TaskStatus AddSource(MeshData *md, MeshData *mdudt) +void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin) { + // Do we actually need anything here? +} +TaskStatus AddSource(MeshData *md, MeshData *mdudt) +{ Flag(mdudt, "Adding EMHD Explicit Sources"); // Pointers auto pmesh = mdudt->GetMeshPointer(); auto pmb0 = mdudt->GetBlockData(0)->GetBlockPointer(); - // Options + // Options: Global const auto& gpars = pmb0->packages.Get("GRMHD")->AllParams(); const Real gam = gpars.Get("gamma"); const int ndim = pmesh->ndim; - const MetadataFlag isPrimitive = gpars.Get("PrimitiveFlag"); - + // Options: Local const auto& pars = pmb0->packages.Get("EMHD")->AllParams(); const EMHD_parameters& emhd_params = pars.Get("emhd_params"); // Pack variables PackIndexMap prims_map, cons_map; - auto P = md->PackVariables(std::vector{isPrimitive}, prims_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); auto U = md->PackVariables(std::vector{Metadata::Conserved}, cons_map); auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}); const VarMap m_p(prims_map, false), m_u(cons_map, true); - // Get sizes, declare temporary ucov, Theta for gradients - const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); - const int n2 = pmb0->cellbounds.ncellsj(IndexDomain::entire); - const int n3 = pmb0->cellbounds.ncellsk(IndexDomain::entire); - const int nb = dUdt.GetDim(5); - GridVector ucov_s("ucov", nb, GR_DIM, n3, n2, n1); - GridScalar theta_s("Theta", nb, n3, n2, n1); + // Get temporary ucov, Theta for gradients + PackIndexMap temps_map; + auto Temps = md->PackVariables(std::vector{Metadata::GetUserFlag("EMHDTemp")}, temps_map); + int m_ucov = temps_map["ucov"].first; + int m_theta = temps_map["Theta"].first; // Get ranges const IndexRange ib = mdudt->GetBoundsI(IndexDomain::interior); const IndexRange jb = mdudt->GetBoundsJ(IndexDomain::interior); const IndexRange kb = mdudt->GetBoundsK(IndexDomain::interior); - const IndexRange block = IndexRange{0, nb - 1}; + const IndexRange block = IndexRange{0, dUdt.GetDim(5) - 1}; // 1-zone halo in nontrivial dimensions const IndexRange il = IndexRange{ib.s-1, ib.e+1}; const IndexRange jl = (ndim > 1) ? IndexRange{jb.s-1, jb.e+1} : jb; @@ -172,27 +197,26 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Calculate & apply source terms pmb0->par_for("emhd_sources_pre", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = dUdt.GetCoords(b); - const GReal gdet = G.gdet(Loci::center, j, i); // ucon Real ucon[GR_DIM], ucov[GR_DIM]; GRMHD::calc_ucon(G, P(b), m_p, k, j, i, Loci::center, ucon); G.lower(ucon, ucov, k, j, i, Loci::center); - DLOOP1 ucov_s(b, mu, k, j, i) = ucov[mu]; + DLOOP1 Temps(b, m_ucov + mu, k, j, i) = ucov[mu]; // theta - theta_s(b, k, j, i) = m::max((gam - 1) * P(b)(m_p.UU, k, j, i) / P(b)(m_p.RHO, k, j, i), SMALL); + Temps(b, m_theta, k, j, i) = m::max((gam - 1) * P(b)(m_p.UU, k, j, i) / P(b)(m_p.RHO, k, j, i), SMALL); } ); // Calculate & apply source terms pmb0->par_for("emhd_sources", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = dUdt.GetCoords(b); // Get the EGRMHD parameters Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P(b), m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e, "explicit_sources"); + EMHD::set_parameters(G, P(b), m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); // and the 4-vectors FourVectors D; @@ -201,7 +225,8 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Compute gradient of ucov and Theta Real grad_ucov[GR_DIM][GR_DIM], grad_Theta[GR_DIM]; - EMHD::gradient_calc(G, P(b), ucov_s, theta_s, b, k, j, i, (ndim > 2), (ndim > 1), grad_ucov, grad_Theta); + // TODO thread the limiter selection through to call + EMHD::gradient_calc(G, Temps(b), m_ucov, m_theta, b, k, j, i, (ndim > 2), (ndim > 1), grad_ucov, grad_Theta); // Compute div of ucon (all terms but the time-derivative ones are nonzero) Real div_ucon = 0; @@ -211,10 +236,11 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) const Real& rho = P(b)(m_p.RHO, k, j, i); const Real& qtilde = P(b)(m_p.Q, k, j, i); const Real& dPtilde = P(b)(m_p.DP, k, j, i); + const Real& Theta = Temps(b)(m_theta, k, j, i); Real q0 = 0; DLOOP1 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * grad_Theta[mu]; - DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * theta_s(b, k, j, i) * D.ucon[nu] * grad_ucov[nu][mu]; + DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / m::sqrt(bsq)) * Theta * D.ucon[nu] * grad_ucov[nu][mu]; Real dP0 = -rho * nu_e * div_ucon; DLOOP2 dP0 += 3. * rho * nu_e * (D.bcon[mu] * D.bcon[nu] / bsq) * grad_ucov[mu][nu]; @@ -222,8 +248,8 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) Real q0_tilde = q0; Real dP0_tilde = dP0; if (emhd_params.higher_order_terms) { - q0_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho * pow(theta_s(b, k, j, i), 2)) ) : 0.; - dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * theta_s(b, k, j, i)) ) : 0.; + q0_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho * Theta * Theta)) : 0.; + dP0_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho * Theta) ) : 0.; } dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 9743558e..463bbe95 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -55,6 +55,7 @@ class EMHD_parameters { public: bool higher_order_terms; + bool feedback; ClosureType type; Real tau; Real conduction_alpha; @@ -68,7 +69,21 @@ class EMHD_parameters { /** * Initialization: handle parameters, */ -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + +/** + * Get the EMHD parameters needed on the device side. + * This function exists to be able to easily return a null + * EMHD_parameters object even if the "EMHD" package is not loaded. + */ +inline EMHD_parameters GetEMHDParameters(Packages_t& packages) +{ + EMHD::EMHD_parameters emhd_params_tmp; + if (packages.AllPackages().count("EMHD")) { + emhd_params_tmp = packages.Get("EMHD")->Param("emhd_params"); + } + return emhd_params_tmp; +} /** * Add EGRMHD explicit source terms: anything which can be calculated once @@ -76,6 +91,12 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack */ TaskStatus AddSource(MeshData *md, MeshData *mdudt); +/** + * Set q and dP to sensible starting values if they are not initialized by the problem. + * Currently a no-op as sensible values are zeros. + */ +void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin); + /** * Set chi, nu, tau. Problem dependent * @@ -84,7 +105,7 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt); template KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, const EMHD_parameters& emhd_params, const Real& gam, - const int& k, const int& j, const int& i, + const int& j, const int& i, Real& tau, Real& chi_e, Real& nu_e) { if (emhd_params.type == ClosureType::constant) { @@ -102,7 +123,7 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& chi_e = emhd_params.conduction_alpha * cs2 * tau; nu_e = emhd_params.viscosity_alpha * cs2 * tau; - } else if (emhd_params.type == ClosureType::kappa_eta){ + } else if (emhd_params.type == ClosureType::kappa_eta) { // Set tau = const, chi = kappa / rho, nu = eta / rho tau = emhd_params.tau; @@ -112,20 +133,20 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& } else if (emhd_params.type == ClosureType::torus) { FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); + // TODO need this max() if we're correcting later? double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, Loci::center, Xembed); - GReal r = Xembed[1]; + G.coord_embed(0, j, i, Loci::center, Xembed); + const GReal r = Xembed[1]; // Compute dynamical time scale - Real tau_dyn = m::pow(r, 1.5); - tau = tau_dyn; + const Real tau_dyn = m::pow(r, 1.5); - Real pg = (gam - 1.) * P(m_p.UU); - Real Theta = pg / P(m_p.RHO); + const Real pg = (gam - 1.) * P(m_p.UU); + const Real Theta = pg / P(m_p.RHO); // Compute local sound speed - Real cs = m::sqrt(gam * pg / (P(m_p.RHO) + (gam * P(m_p.UU)))); + const Real cs = m::sqrt(gam * pg / (P(m_p.RHO) + (gam * P(m_p.UU)))); Real lambda = 0.01; Real inv_exp_g = 0.; @@ -134,27 +155,22 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& // Correction due to heat conduction Real q = P(m_p.Q); if (emhd_params.higher_order_terms) - q *= sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * m::pow(cs, 2.) * m::pow(Theta, 2.)); - Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * m::pow(cs, 3.); - Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); + q *= m::sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * cs * cs * Theta * Theta); + Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * cs * cs * cs; + Real q_ratio = m::abs(q) / q_max; + inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - tau = m::min(tau, f_fmin * tau_dyn); + tau = m::min(tau_dyn, f_fmin * tau_dyn); // Correction due to pressure anisotropy - Real dP = P(m_p.DP); - if (emhd_params.higher_order_terms) - dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * m::pow(cs, 2.) * Theta); - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); - Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); + const Real dP = (emhd_params.higher_order_terms) ? P(m_p.DP) * sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * cs * cs * Theta) : P(m_p.DP); + // TODO does this need first max()? + const Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); + const Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); + const Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; + const Real dP_max = (dP > 0.) ? dP_plus : dP_minus; Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); @@ -163,17 +179,16 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& tau = m::min(tau, f_fmin * tau_dyn); // Update thermal diffusivity and kinematic viscosity - Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + Real max_alpha = (1 - cs * cs) / (2 * cs * cs + 1.e-12); + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs * cs * tau; + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs * cs * tau; } // else yell? } -template -KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& P, const VarMap& m_p, +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, const EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, - Real& tau, Real& chi_e, Real& nu_e, const char* global_flag) + Real& tau, Real& chi_e, Real& nu_e) { if (emhd_params.type == ClosureType::constant) { // Set tau, nu, chi to constants @@ -212,7 +227,6 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& // Compute dynamical time scale Real tau_dyn = pow(r, 1.5); - tau = tau_dyn; Real pg = (gam - 1.) * uu; Real Theta = pg / rho; @@ -229,7 +243,7 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& q *= (rho * emhd_params.conduction_alpha * pow(cs, 2.) * pow(Theta, 2.)); Real q_max = emhd_params.conduction_alpha * rho * pow(cs, 3.); Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); + inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; tau = m::min(tau, f_fmin * tau_dyn); @@ -242,11 +256,7 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; + const Real dP_max = (dP > 0.) ? dP_plus : dP_minus; Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); @@ -255,14 +265,14 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Global& tau = m::min(tau, f_fmin * tau_dyn); // Update thermal diffusivity and kinematic viscosity - Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + Real max_alpha = (1 - cs * cs) / (2 * cs * cs + 1.e-12); + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs * cs * tau; + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs * cs * tau; } // else yell? } // ONLY FOR TEST PROBLEMS INITIALIZATION (local version) -KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Real& rho, const Real& u, +KOKKOS_INLINE_FUNCTION void set_parameters_init(const GRCoordinates& G, const Real& rho, const Real& u, const EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, Real& tau, Real& chi_e, Real& nu_e) @@ -297,22 +307,29 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Real& r * Entirely local! */ KOKKOS_INLINE_FUNCTION void calc_tensor(const Real& rho, const Real& u, const Real& pgas, + const EMHD::EMHD_parameters& emhd_params, const Real& q, const Real& dP, const FourVectors& D, const int& dir, Real emhd[GR_DIM]) { - const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); - const Real eta = pgas + rho + u + bsq; + const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); + const Real eta = pgas + rho + u + bsq; const Real ptot = pgas + 0.5 * bsq; - DLOOP1 { - emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] - + ptot * (dir == mu) - - D.bcon[dir] * D.bcov[mu] - + (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + - (D.bcon[dir] * D.ucov[mu])) - - dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); + if (!emhd_params.feedback) { + DLOOP1 { + emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + + ptot * (dir == mu) + - D.bcon[dir] * D.bcov[mu]; + } + } else { + DLOOP1 { + emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + + ptot * (dir == mu) + - D.bcon[dir] * D.bcov[mu] + + (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])) + - dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); + } } } @@ -326,8 +343,13 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea dP = dP_tilde; if (emhd_params.higher_order_terms) { - q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); - dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); + if (emhd_params.type == ClosureType::kappa_eta) { + q *= m::sqrt(emhd_params.kappa * m::pow(Theta, 2) / emhd_params.tau); + dP *= m::sqrt(emhd_params.eta * Theta / emhd_params.tau); + } else { + q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); + dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); + } } } diff --git a/kharma/emhd/emhd_limits.hpp b/kharma/emhd/emhd_limits.hpp new file mode 100644 index 00000000..1d5cff58 --- /dev/null +++ b/kharma/emhd/emhd_limits.hpp @@ -0,0 +1,154 @@ +/* + * File: emhd_limits.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "emhd.hpp" + +#include "flux_functions.hpp" + +// Flags for the extended MHD limits +#define HIT_Q_LIMIT 1 +#define HIT_DP_LIMIT 2 + +namespace EMHD { + + + +/** + * Apply limits on the Extended MHD variables + * + * @return elag, a bitflag indicating whether each particular limit was hit, allowing representation of arbitrary combinations + * See decs.h for bit names. + * + * The maximum heat flux is limited by the saturated value given by a hot cloud in cold gas. + * The bounds on the pressure anisotropy as due to the mirror and firehose instability limits. + * + * Although only q, dP are updated here, prim_to_flux updates all conserved. + * This shouldn't be an issue though since PtoU in analytic and will result in the same value for the ideal MHD variables. + */ +KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, + const Real& gam, const EMHD::EMHD_parameters& emhd_params, + const int& k, const int& j, const int& i, + const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) +{ + int eflag = 0; + + Real rho = P(m_p.RHO, k, j, i); + Real uu = P(m_p.UU, k, j, i); + Real qtilde = P(m_p.Q, k, j, i); + Real dPtilde = P(m_p.DP, k, j, i); + + Real pg = (gam - 1.) * uu; + Real Theta = pg / rho; + Real cs = m::sqrt(gam * pg / (rho + (gam * uu))); + + FourVectors D; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, D); + Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); + + Real tau, chi_e, nu_e; + EMHD::set_parameters(G, P, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + + Real q, dP; + EMHD::convert_prims_to_q_dP(qtilde, dPtilde, rho, Theta, cs*cs, emhd_params, q, dP); + + Real qmax = 1.07 * rho * cs*cs*cs; + Real max_frac = m::max(m::abs(q) / qmax, 1.); + if (m::abs(q) / qmax > 1.) + eflag |= HIT_Q_LIMIT; + + P(m_p.Q, k, j, i) = P(m_p.Q, k, j, i) / max_frac; + + Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); + Real dP_plus = m::min(1.07 * 0.5 * bsq * dP_comp_ratio, 1.49 * pg); + Real dP_minus = m::max(-1.07 * bsq, -2.99 * pg); + + if (dP > 0. && (dP / dP_plus > 1.)) + eflag |= HIT_DP_LIMIT; + else if (dP < 0. && (dP / dP_minus > 1.)) + eflag |= HIT_DP_LIMIT; + + if (dP > 0.) + P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_plus, 1.)); + else + P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_minus, 1.)); + + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + + return eflag; + +} + +/** + * Apply limits on the Extended MHD variables q & dP based on instabilities. + * + * LOCKSTEP: this function respects P and returns consistent P<->U + */ +inline void ApplyEMHDLimits(MeshBlockData *mbd, IndexDomain domain) +{ + Flag(mbd, "Applying EMHD limits"); + + auto pmb = mbd->GetBlockPointer(); + auto packages = pmb->packages; + + PackIndexMap prims_map, cons_map; + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + const auto& G = pmb->coords; + + GridScalar eflag = mbd->Get("eflag").data; + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(packages); + + const Real gam = packages.Get("GRMHD")->Param("gamma"); + + // Apply the EMHD instability limits in q, deltaP + // The user-specified limit values are in the FloorPrescription struct, + // but the EMHD closure parameters are in the emhd_params struct + const IndexRange ib = mbd->GetBoundsI(domain); + const IndexRange jb = mbd->GetBoundsJ(domain); + const IndexRange kb = mbd->GetBoundsK(domain); + pmb->par_for("apply_emhd_limits", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // Apply limits to the Extended MHD variables + eflag(k, j, i) = apply_instability_limits(G, P, m_p, gam, emhd_params, k, j, i, U, m_u); + } + ); + + Flag(mbd, "Applied"); +} + +} // EMHD diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index 9f6f8f37..d935147e 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -57,7 +57,7 @@ KOKKOS_INLINE_FUNCTION void implicit_sources(const GRCoordinates& G, const Local { // These are intentionally the tilde versions! Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P_tau, m_p, emhd_params_tau, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters(G, P_tau, m_p, emhd_params_tau, gam, j, i, tau, chi_e, nu_e); dUq = -G.gdet(Loci::center, j, i) * (P(m_p.Q) / tau); dUdP = -G.gdet(Loci::center, j, i) * (P(m_p.DP) / tau); } @@ -76,7 +76,7 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons { // Parameters Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters(G, P, m_p, emhd_params, gam, j, i, tau, chi_e, nu_e); FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); @@ -92,12 +92,12 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 dt_ucov[mu] = (ucov_new[mu] - ucov_old[mu]) / dt; // Compute div of ucon (only the temporal part is nonzero) - Real div_ucon = 0; + Real div_ucon = 0; DLOOP1 div_ucon += G.gcon(Loci::center, j, i, 0, mu) * dt_ucov[mu]; // dTheta/dt const Real Theta_new = m::max((gam-1) * P_new(m_p.UU) / P_new(m_p.RHO), SMALL); const Real Theta_old = m::max((gam-1) * P_old(m_p.UU) / P_old(m_p.RHO), SMALL); - const Real dt_Theta = (Theta_new - Theta_old) / dt; + const Real dt_Theta = (Theta_new - Theta_old) / dt; // TEMPORAL SOURCE TERMS const Real& rho = P(m_p.RHO); @@ -105,10 +105,10 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons const Real& dPtilde = P(m_p.DP); const Real& Theta = (gam-1) * P(m_p.UU) / P(m_p.RHO); - Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; + Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; - Real dP0 = -rho * nu_e * div_ucon; + Real dP0 = -rho * nu_e * div_ucon; DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; Real q0_tilde = q0; diff --git a/kharma/emhd/emhd_utils.hpp b/kharma/emhd/emhd_utils.hpp index 1d8597e1..deb42a73 100644 --- a/kharma/emhd/emhd_utils.hpp +++ b/kharma/emhd/emhd_utils.hpp @@ -35,6 +35,10 @@ #include "decs.hpp" +#include "reconstruction.hpp" + +using KReconstruction::slope_calc; + /** * Utilities for the EMHD source terms, things we might conceivably use somewhere else, * or use *from* somewhere else instead of here. @@ -46,88 +50,11 @@ namespace EMHD { -// Linear MC slope limiter -KOKKOS_INLINE_FUNCTION Real linear_monotonized_cd(Real x1, Real x2, Real x3, Real dx) -{ - const Real Dqm = 2 * (x2 - x1) / dx; - const Real Dqp = 2 * (x3 - x2) / dx; - const Real Dqc = 0.5 * (x3 - x1) / dx; - - if (Dqm * Dqp <= 0) { - return 0; - } else { - if ((m::abs(Dqm) < m::abs(Dqp)) && (fabs (Dqm) < m::abs(Dqc))) { - return Dqm; - } else if (m::abs(Dqp) < m::abs(Dqc)) { - return Dqp; - } else { - return Dqc; - } - } -} - -// Linear Van Leer slope limiter -KOKKOS_INLINE_FUNCTION Real linear_van_leer(Real x1, Real x2, Real x3, Real dx) -{ - const Real Dqm = (x2 - x1) / dx; - const Real Dqp = (x3 - x2) / dx; - - const Real extrema = Dqm * Dqp; - - if (extrema <= 0) { - return 0; - } else { - return (2 * extrema / (Dqm + Dqp)); - } -} - -/** - * Compute slope of scalars at faces - */ -template -KOKKOS_INLINE_FUNCTION Real slope_calc_scalar(const GRCoordinates& G, const Global& A, const int& dir, - const int& b, const int& k, const int& j, const int& i, - ReconstructionType recon=ReconstructionType::linear_mc) -{ - // TODO could generic-ize this, but with two options, screw it - if (recon != ReconstructionType::linear_vl) { - if (dir == 1) return linear_monotonized_cd(A(b, k, j, i-1), A(b, k, j, i), A(b, k, j, i+1), G.dx1v(i)); - if (dir == 2) return linear_monotonized_cd(A(b, k, j-1, i), A(b, k, j, i), A(b, k, j+1, i), G.dx2v(j)); - if (dir == 3) return linear_monotonized_cd(A(b, k-1, j, i), A(b, k, j, i), A(b, k+1, j, i), G.dx3v(k)); - } else { - if (dir == 1) return linear_van_leer(A(b, k, j, i-1), A(b, k, j, i), A(b, k, j, i+1), G.dx1v(i)); - if (dir == 2) return linear_van_leer(A(b, k, j-1, i), A(b, k, j, i), A(b, k, j+1, i), G.dx2v(j)); - if (dir == 3) return linear_van_leer(A(b, k-1, j, i), A(b, k, j, i), A(b, k+1, j, i), G.dx3v(k)); - } - return 0.; -} - -/** - * Compute slope of all vectors at faces - */ -template -KOKKOS_INLINE_FUNCTION Real slope_calc_vector(const GRCoordinates& G, const Global& A, const int& mu, - const int& dir, const int& b, const int& k, const int& j, const int& i, - ReconstructionType recon=ReconstructionType::linear_mc) -{ - // TODO could generic-ize this, but with two options, screw it - if (recon != ReconstructionType::linear_vl) { - if (dir == 1) return linear_monotonized_cd(A(b, mu, k, j, i-1), A(b, mu, k, j, i), A(b, mu, k, j, i+1), G.dx1v(i)); - if (dir == 2) return linear_monotonized_cd(A(b, mu, k, j-1, i), A(b, mu, k, j, i), A(b, mu, k, j+1, i), G.dx2v(j)); - if (dir == 3) return linear_monotonized_cd(A(b, mu, k-1, j, i), A(b, mu, k, j, i), A(b, mu, k+1, j, i), G.dx3v(k)); - } else { - if (dir == 1) return linear_van_leer(A(b, mu, k, j, i-1), A(b, mu, k, j, i), A(b, mu, k, j, i+1), G.dx1v(i)); - if (dir == 2) return linear_van_leer(A(b, mu, k, j-1, i), A(b, mu, k, j, i), A(b, mu, k, j+1, i), G.dx2v(j)); - if (dir == 3) return linear_van_leer(A(b, mu, k-1, j, i), A(b, mu, k, j, i), A(b, mu, k+1, j, i), G.dx3v(k)); - } - return 0.; -} - // Compute gradient of four velocities and temperature // Called by emhd_explicit_sources -template -KOKKOS_INLINE_FUNCTION void gradient_calc(const GRCoordinates& G, const Global& P, - const GridVector& ucov_s, const GridScalar& theta_s, +template +KOKKOS_INLINE_FUNCTION void gradient_calc(const GRCoordinates& G, const VariablePack& Temps, + const int& uvec_index, const int& theta_index, const int& b, const int& k, const int& j, const int& i, const bool& do_3d, const bool& do_2d, Real grad_ucov[GR_DIM][GR_DIM], Real grad_Theta[GR_DIM]) @@ -137,31 +64,32 @@ KOKKOS_INLINE_FUNCTION void gradient_calc(const GRCoordinates& G, const Global& grad_ucov[0][mu] = 0; // slope in direction nu of component mu - grad_ucov[1][mu] = slope_calc_vector(G, ucov_s, mu, 1, b, k, j, i); + grad_ucov[1][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); if (do_2d) { - grad_ucov[2][mu] = slope_calc_vector(G, ucov_s, mu, 2, b, k, j, i); + grad_ucov[2][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); } else { grad_ucov[2][mu] = 0.; } if (do_3d) { - grad_ucov[3][mu] = slope_calc_vector(G, ucov_s, mu, 3, b, k, j, i); + grad_ucov[3][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); } else { grad_ucov[3][mu] = 0.; } } - DLOOP3 grad_ucov[mu][nu] -= G.conn(j, i, lam, mu, nu) * ucov_s(lam, k, j, i); + // TODO skip this if flat space? + DLOOP3 grad_ucov[mu][nu] -= G.conn(j, i, lam, mu, nu) * Temps(uvec_index + lam, k, j, i); // Compute temperature gradient // Time derivative component is computed in time_derivative_sources grad_Theta[0] = 0; - grad_Theta[1] = slope_calc_scalar(G, theta_s, 1, b, k, j, i); + grad_Theta[1] = slope_calc(G, Temps, theta_index, k, j, i); if (do_2d) { - grad_Theta[2] = slope_calc_scalar(G, theta_s, 2, b, k, j, i); + grad_Theta[2] = slope_calc(G, Temps, theta_index, k, j, i); } else { grad_Theta[2] = 0.; } if (do_3d) { - grad_Theta[3] = slope_calc_scalar(G, theta_s, 3, b, k, j, i); + grad_Theta[3] = slope_calc(G, Temps, theta_index, k, j, i); } else { grad_Theta[3] = 0.; } diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 417500db..aff752bd 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -31,24 +31,25 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -// Floors. Apply limits to fluid values to maintain integrable state - #include "floors.hpp" +#include "floors_functions.hpp" #include "debug.hpp" #include "grmhd.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" -namespace Floors +// Floors. Apply limits to fluid values to maintain integrable state + +int CountFFlags(MeshData *md) { + return Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, 0, true); +} -std::shared_ptr Initialize(ParameterInput *pin) +std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - // TODO can I just build/add/use a Prescription here, rather than building one - // before each call? - auto pkg = std::make_shared("Floors"); + Flag("Initializing Floors"); + auto pkg = std::make_shared("Floors"); Params ¶ms = pkg->AllParams(); // Floor parameters @@ -94,7 +95,7 @@ std::shared_ptr Initialize(ParameterInput *pin) bool adjust_k = pin->GetOrAddBoolean("floors", "adjust_k", true); params.Add("adjust_k", adjust_k); - // Limit + // Limit the fluid Lorentz factor gamma double gamma_max = pin->GetOrAddReal("floors", "gamma_max", 50.); params.Add("gamma_max", gamma_max); @@ -105,6 +106,7 @@ std::shared_ptr Initialize(ParameterInput *pin) // Drift frame floors are now available and preferred when using // the implicit solver to avoid UtoP calls. std::string frame = pin->GetOrAddString("floors", "frame", "normal"); + // TODO TODO ENUM THIS params.Add("frame", frame); if (frame == "normal" || frame == "nof") { params.Add("fluid_frame", false); @@ -128,61 +130,105 @@ std::shared_ptr Initialize(ParameterInput *pin) // We initialize this even if not using mixed frame, for constructing Prescription objs Real frame_switch = pin->GetOrAddReal("floors", "frame_switch", 50.); params.Add("frame_switch", frame_switch); - // Disable all floors. It is obviously tremendously inadvisable to // set this option to true bool disable_floors = pin->GetOrAddBoolean("floors", "disable_floors", false); params.Add("disable_floors", disable_floors); - // Apply limits on heat flux and pressure anisotropy from velocity space instabilities? - // We would want this for the torus runs but not for the test problems. - // For eg: we know that this affects the viscous bondi problem - bool enable_emhd_limits = pin->GetOrAddBoolean("floors", "emhd_limits", false); - params.Add("enable_emhd_limits", enable_emhd_limits); - // Temporary fix just for being able to save field values // Should switch these to "Integer" fields when Parthenon supports it Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("fflag", m); - // Similar to fflag - will register zones where limits on q and dP are hit - // Enabled only if - pkg->AddField("eflag", m); - // bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); - // if (do_emhd && enable_emhd_limits) { - // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - // pkg->AddField("eflag", m); - // } - - // Floors should be applied to primitive ("Derived") variables just after they are calculated. - pkg->PostFillDerivedBlock = Floors::PostFillDerivedBlock; - // Could print floor flags using this package, but they're very similar to pflag - // so I'm leaving them together - //pkg->PostStepDiagnosticsMesh = GRMHD::PostStepDiagnostics; + pkg->BlockApplyFloors = Floors::ApplyGRMHDFloors; + pkg->PostStepDiagnosticsMesh = Floors::PostStepDiagnostics; + + // List (vector) of HistoryOutputVars that will all be enrolled as output variables + parthenon::HstVar_list hst_vars = {}; + // Count total floors as a history item + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, CountFFlags, "FFlags")); + // TODO Domain::entire version? + // TODO entries for each individual flag? + // add callbacks for HST output to the Params struct, identified by the `hist_param_key` + pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + Flag("Initialized"); return pkg; } -TaskStatus PostFillDerivedBlock(MeshBlockData *mbd) +TaskStatus Floors::ApplyInitialFloors(MeshBlockData *mbd, IndexDomain domain) { - if (mbd->GetBlockPointer()->packages.Get("Floors")->Param("disable_floors") - || !mbd->GetBlockPointer()->packages.Get("Globals")->Param("in_loop")) { - return TaskStatus::complete; + Flag(mbd, "Applying first floors"); + + auto pmb = mbd->GetBlockPointer(); + + PackIndexMap prims_map, cons_map; + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + const auto& G = pmb->coords; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + + // If we're going to apply floors through the run, apply the same ones at init + // Otherwise pick sensible defaults + Floors::Prescription floors_tmp; + if (pmb->packages.AllPackages().count("Floors")) { + floors_tmp = Floors::Prescription(pmb->packages.Get("Floors")->AllParams()); } else { - return ApplyFloors(mbd); + // JUST rho & u geometric + floors_tmp.rho_min_geom = 1e-6; + floors_tmp.u_min_geom = 1e-8; + floors_tmp.r_char = 10.; //unused + floors_tmp.frame_switch = 50.; //unused + + floors_tmp.bsq_over_rho_max = 1e20; + floors_tmp.bsq_over_u_max = 1e20; + floors_tmp.u_over_rho_max = 1e20; + floors_tmp.ktot_max = 1e20; + floors_tmp.gamma_max = 1e20; + + floors_tmp.use_r_char = false; + floors_tmp.temp_adjust_u = false; + floors_tmp.adjust_k = false; + + floors_tmp.fluid_frame = true; + floors_tmp.mixed_frame = false; + floors_tmp.drift_frame = false; } + const Floors::Prescription floors = floors_tmp; + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + // Apply floors over the same zones we just updated with UtoP + // This selects the entire domain, but we then require pflag >= 0, + // which keeps us from covering completely uninitialized zones + // (but still applies to failed UtoP!) + const IndexRange ib = mbd->GetBoundsI(domain); + const IndexRange jb = mbd->GetBoundsJ(domain); + const IndexRange kb = mbd->GetBoundsK(domain); + pmb->par_for("apply_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + apply_floors(G, P, m_p, gam, emhd_params, k, j, i, floors, U, m_u); + apply_ceilings(G, P, m_p, gam, k, j, i, floors, U, m_u); + } + ); + + Flag(mbd, "Applied"); + return TaskStatus::complete; } -TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) +TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain) { - Flag(mbd, "Apply floors"); + Flag(mbd, "Applying GRMHD floors"); auto pmb = mbd->GetBlockPointer(); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->AllParams().Get("PrimitiveFlag"); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({isPrimitive}, prims_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); @@ -190,18 +236,10 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) GridScalar pflag = mbd->Get("pflag").data; GridScalar fflag = mbd->Get("fflag").data; - GridScalar eflag = mbd->Get("eflag").data; - - const bool enable_emhd_limits = mbd->GetBlockPointer()->packages.Get("Floors")->Param("enable_emhd_limits"); - EMHD::EMHD_parameters emhd_params; - if (enable_emhd_limits) { - const auto& pars = pmb->packages.Get("EMHD")->AllParams(); - emhd_params = pars.Get("emhd_params"); - - } const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); // Apply floors over the same zones we just updated with UtoP // This selects the entire domain, but we then require pflag >= 0, @@ -211,23 +249,23 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) const IndexRange jb = mbd->GetBoundsJ(domain); const IndexRange kb = mbd->GetBoundsK(domain); pmb->par_for("apply_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - if (((int) pflag(k, j, i)) >= InversionStatus::success) { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (((int) pflag(k, j, i)) >= (int) Inverter::Status::success) { // apply_floors can involve another U_to_P call. Hide the pflag in bottom 5 bits and retrieve both - int comboflag = apply_floors(G, P, m_p, gam, k, j, i, floors, U, m_u); - fflag(k, j, i) = (comboflag / HIT_FLOOR_GEOM_RHO) * HIT_FLOOR_GEOM_RHO; + int comboflag = apply_floors(G, P, m_p, gam, emhd_params, k, j, i, floors, U, m_u); + fflag(k, j, i) = (comboflag / FFlag::MINIMUM) * FFlag::MINIMUM; // Record the pflag as well. KHARMA did not traditionally do this, // because floors were run over uninitialized zones, and thus wrote // garbage pflags. We now prevent this. // Note that the pflag is recorded only if inversion failed, // so that a zone is flagged if *either* the initial inversion or - // floor inversion failed. + // post-floor inversion failed. // Zones next to the sharp edge of the initial torus, for example, // can produce negative u when inverted, then magically stay invertible // after floors when they should be diffused. - if (comboflag % HIT_FLOOR_GEOM_RHO) { - pflag(k, j, i) = comboflag % HIT_FLOOR_GEOM_RHO; + if (comboflag % FFlag::MINIMUM) { + pflag(k, j, i) = comboflag % FFlag::MINIMUM; } #if !FUSE_FLOOR_KERNELS @@ -235,28 +273,35 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) } ); pmb->par_for("apply_ceilings", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - if (((int) pflag(k, j, i)) >= InversionStatus::success) { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (((int) pflag(k, j, i)) >= (int) Inverter::Status::success) { #endif // Apply ceilings *after* floors, to make the temperature ceiling better-behaved - // Ceilings never involve a U_to_P call + // Ceilings never involve a u_to_p call int addflag = fflag(k, j, i); addflag |= apply_ceilings(G, P, m_p, gam, k, j, i, floors, U, m_u); fflag(k, j, i) = addflag; } } ); - pmb->par_for("apply_ceilings", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - // Apply limits to the Extended MHD variables - if (enable_emhd_limits) - eflag(k, j, i) = apply_instability_limits(G, P, m_p, gam, emhd_params, k, j, i, floors, U, m_u); - - } - ); Flag(mbd, "Applied"); return TaskStatus::complete; } -} // namespace Floors +TaskStatus Floors::PostStepDiagnostics(const SimTime& tm, MeshData *md) +{ + Flag("Printing Floor diagnostics"); + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Options + const auto& pars = pmesh->packages.Get("Globals")->AllParams(); + const int flag_verbose = pars.Get("flag_verbose"); + + // Debugging/diagnostic info about floor and inversion flags + if (flag_verbose >= 1) { + Flag("Printing flags"); + Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, flag_verbose, true); + } + return TaskStatus::complete; +} diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index bff165ec..b6d88b34 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -1,5 +1,5 @@ /* - * File: floors.cpp + * File: floors.hpp * * BSD 3-Clause License * @@ -34,68 +34,61 @@ #pragma once #include "decs.hpp" - +#include "types.hpp" #include "b_flux_ct.hpp" #include "flux_functions.hpp" #include "grmhd_functions.hpp" -#include "U_to_P.hpp" +#include "inverter.hpp" #include "emhd.hpp" - -#include +#include "reductions.hpp" // Return which floors are hit post-reconstruction // Currently not recorded by the caller, so disabled #define RECORD_POST_RECON 0 +namespace FFlag { // Floor codes are non-exclusive, so it makes little sense to use an enum -// Instead, we use bitflags, starting high enough that we can stick the enum in the bottom 5 bits +// Instead, we use bitflags, starting high enough that we can stick the pflag in the bottom 5 bits // See floors.hpp for explanations of the flags -#define HIT_FLOOR_GEOM_RHO 32 -#define HIT_FLOOR_GEOM_U 64 -#define HIT_FLOOR_B_RHO 128 -#define HIT_FLOOR_B_U 256 -#define HIT_FLOOR_TEMP 512 -#define HIT_FLOOR_GAMMA 1024 -#define HIT_FLOOR_KTOT 2048 +// This is the namespaced, typed equivalent of #define +static constexpr int GEOM_RHO = 32; +static constexpr int GEOM_U = 64; +static constexpr int B_RHO = 128; +static constexpr int B_U = 256; +static constexpr int TEMP = 512; +static constexpr int GAMMA = 1024; +static constexpr int KTOT = 2048; // Separate flags for when the floors are applied after reconstruction. // Not yet used, as this will likely have some speed penalty paid even if // the flags aren't written -#define HIT_FLOOR_GEOM_RHO_FLUX 4096 -#define HIT_FLOOR_GEOM_U_FLUX 8192 - -// Flags for the extended MHD limits -#define HIT_Q_LIMIT 1 -#define HIT_DP_LIMIT 2 - -namespace Floors -{ - -/** - * Initialization. Set parameters. - */ -std::shared_ptr Initialize(ParameterInput *pin); - -/** - * Apply density and internal energy floors and ceilings - * - * This function definitely applies floors (regardless of "disable_floors") - * to the interior domain (not ghost zones). - * - * LOCKSTEP: this function respects P and returns consistent P<->U - */ -TaskStatus ApplyFloors(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire); +static constexpr int GEOM_RHO_FLUX = 4096; +static constexpr int GEOM_U_FLUX = 8192; +// Lowest flag value. Needed for combining floor and other return flags +static constexpr int MINIMUM = GEOM_RHO; + +// Other advantage of a namespace is including full lists for iterating over +// TODO +// 1. prettier names? +// 2. What deep majicks would allow this to be constexpr? +static const std::map flag_names = { + {GEOM_RHO, "GEOM_RHO"}, + {GEOM_U, "GEOM_U"}, + {B_RHO, "B_RHO"}, + {B_U, "B_U"}, + {GAMMA, "GAMMA"}, + {TEMP, "TEMPERATURE"}, + {KTOT, "ENTROPY"}, + {GEOM_RHO_FLUX, "GEOM_RHO_ON_RECON"}, + {GEOM_U_FLUX, "GEOM_U_ON_RECON"}}; +} -/** - * Parthenon call wrapper for ApplyFloors, called just after FillDerived == UtoP - * Decides whether to apply floors based on options, then does so - */ -TaskStatus PostFillDerivedBlock(MeshBlockData *rc); +namespace Floors { /** * Struct to hold floor values without cumbersome dictionary/string logistics. * Hopefully faster than dragging the full Params object device side, - * similar reasoning to VarMap above. + * similar reasoning to VarMap. */ class Prescription { public: @@ -111,9 +104,7 @@ class Prescription { bool fluid_frame, mixed_frame, drift_frame; bool use_r_char, temp_adjust_u, adjust_k; - // Instability limits - bool enable_emhd_limits; - + Prescription() {} Prescription(const parthenon::Params& params) { rho_min_geom = params.Get("rho_min_geom"); @@ -134,442 +125,37 @@ class Prescription { fluid_frame = params.Get("fluid_frame"); mixed_frame = params.Get("mixed_frame"); drift_frame = params.Get("drift_frame"); - - enable_emhd_limits = params.Get("enable_emhd_limits"); } }; /** - * Apply all ceilings together, currently at most one on velocity and two on internal energy - * - * @return fflag, a bitflag indicating whether each particular floor was hit, allowing representation of arbitrary combinations - * See decs.h for bit names. - * - * LOCKSTEP: this function respects P and returns consistent P<->U + * Initialization. Set parameters. */ -KOKKOS_INLINE_FUNCTION int apply_ceilings(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, - const Real& gam, const int& k, const int& j, const int& i, const Floors::Prescription& floors, - const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) -{ - int fflag = 0; - // First apply ceilings: - // 1. Limit gamma with respect to normal observer - Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, loc); - - if (gamma > floors.gamma_max) { - fflag |= HIT_FLOOR_GAMMA; - - Real f = m::sqrt((m::pow(floors.gamma_max, 2) - 1.)/(m::pow(gamma, 2) - 1.)); - VLOOP P(m_p.U1+v, k, j, i) *= f; - } - - // 2. Limit the entropy by controlling u, to avoid anomalous cooling from funnel wall - // Note this technically applies the condition *one step sooner* than legacy, since it operates on - // the entropy as calculated from current conditions, rather than the value kept from the previous - // step for calculating dissipation. - Real ktot = (gam - 1.) * P(m_p.UU, k, j, i) / m::pow(P(m_p.RHO, k, j, i), gam); - if (ktot > floors.ktot_max) { - fflag |= HIT_FLOOR_KTOT; - - P(m_p.UU, k, j, i) = floors.ktot_max / ktot * P(m_p.UU, k, j, i); - } - // Also apply the ceiling to the advected entropy KTOT, if we're keeping track of that - // (either for electrons, or robust primitive inversions in future) - // TODO make a separate flag for hitting this vs the "fake" version above - if (m_p.KTOT >= 0 && (P(m_p.KTOT, k, j, i) > floors.ktot_max)) { - fflag |= HIT_FLOOR_KTOT; - P(m_p.KTOT, k, j, i) = floors.ktot_max; - } - - // 3. Limit the temperature by controlling u. Can optionally add density instead, implemented in apply_floors - if (floors.temp_adjust_u && P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i) > floors.u_over_rho_max) { - fflag |= HIT_FLOOR_TEMP; - - P(m_p.UU, k, j, i) = floors.u_over_rho_max * P(m_p.RHO, k, j, i); - } - - if (fflag) { - // Keep lockstep! - // TODO all flux - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); - } - - return fflag; -} +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** - * Apply floors of several types in determining how to add mass and internal energy to preserve stability. - * All floors which might apply are recorded separately, then mass/energy are added *in normal observer frame* + * Apply density and internal energy floors and ceilings * - * @return fflag + pflag: fflag is a flagset starting at the sixth bit from the right. pflag is a number <32. - * This returns the sum, with the caller responsible for separating what's desired. + * This function definitely applies floors (regardless of "disable_floors") + * over the stated domain, by default the entire grid incl. ghost zones. * - * LOCKSTEP: this function respects P and ignores U in order to return consistent P<->U + * LOCKSTEP: this function respects P and returns consistent P<->U */ -KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, - const Real& gam, const int& k, const int& j, const int& i, const Floors::Prescription& floors, - const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) -{ - int fflag = 0; - InversionStatus pflag = InversionStatus::success; - // Then apply floors: - // 1. Geometric hard floors, not based on fluid relationships - Real rhoflr_geom, uflr_geom; - bool use_ff, use_df; - if(G.coords.spherical()) { - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, loc, Xembed); - GReal r = Xembed[1]; - // TODO measure whether this/if 1 is really faster - // GReal r = exp(G.x1v(i)); - - // Use the fluid frame if specified, or in outer domain - use_ff = floors.fluid_frame || (floors.mixed_frame && r > floors.frame_switch); - // Use the drift frame if specified - use_df = floors.drift_frame; - - if (floors.use_r_char) { - // Steeper floor from iharm3d - Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); - rhoflr_geom = floors.rho_min_geom * rhoscal; - uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); - } else { - // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d - } - } else { - rhoflr_geom = floors.rho_min_geom; - uflr_geom = floors.u_min_geom; - use_ff = floors.fluid_frame; - use_df = floors.drift_frame; - } - Real rho = P(m_p.RHO, k, j, i); - Real u = P(m_p.UU, k, j, i); - - // 2. Magnetization ceilings: impose maximum magnetization sigma = bsq/rho, and inverse beta prop. to bsq/U - FourVectors Dtmp; - // TODO is there a more efficient way to calculate just bsq? - GRMHD::calc_4vecs(G, P, m_p, k, j, i, loc, Dtmp); - double bsq = dot(Dtmp.bcon, Dtmp.bcov); - double rhoflr_b = bsq / floors.bsq_over_rho_max; - double uflr_b = bsq / floors.bsq_over_u_max; - - // Evaluate max U floor, needed for temp ceiling below - double uflr_max = m::max(uflr_geom, uflr_b); - - double rhoflr_max; - if (!floors.temp_adjust_u) { - // 3. Temperature ceiling: impose maximum temperature u/rho - // Take floors on U into account - double rhoflr_temp = m::max(u, uflr_max) / floors.u_over_rho_max; - // Record hitting temperature ceiling - fflag |= (rhoflr_temp > rho) * HIT_FLOOR_TEMP; // Misnomer for consistency - - // Evaluate max rho floor - rhoflr_max = m::max(m::max(rhoflr_geom, rhoflr_b), rhoflr_temp); - } else { - // Evaluate max rho floor - rhoflr_max = m::max(rhoflr_geom, rhoflr_b); - } - - // If we need to do anything... - if (rhoflr_max > rho || uflr_max > u) { - - // Record all the floors that were hit, using bitflags - // Record Geometric floor hits - fflag |= (rhoflr_geom > rho) * HIT_FLOOR_GEOM_RHO; - fflag |= (uflr_geom > u) * HIT_FLOOR_GEOM_U; - // Record Magnetic floor hits - fflag |= (rhoflr_b > rho) * HIT_FLOOR_B_RHO; - fflag |= (uflr_b > u) * HIT_FLOOR_B_U; - - if (use_ff) { - P(m_p.RHO, k, j, i) += m::max(0., rhoflr_max - rho); - P(m_p.UU, k, j, i) += m::max(0., uflr_max - u); - // TODO should be all Flux - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); - - } else if (use_df) { - // Drift frame floors. Refer to Appendix B3 in https://doi.org/10.1093/mnras/stx364 (hereafter R17) - const Real gdet = G.gdet(Loci::center, j, i); - const Real lapse = 1./m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); - double beta[GR_DIM] = {0}; - - beta[1] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 1); - beta[2] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 2); - beta[3] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 3); - - // Fluid quantities (four velocities have been computed above) - const Real rho = P(m_p.RHO, k, j, i); - const Real uu = P(m_p.UU, k, j, i); - const Real pg = (gam - 1.) * uu; - const Real w_old = m::max(rho + uu + pg, SMALL); - - // Normal observer magnetic field - Real Bcon[GR_DIM] = {0}; - Real Bcov[GR_DIM] = {0}; - Bcon[0] = 0; - Bcon[1] = P(m_p.B1, k, j, i); - Bcon[2] = P(m_p.B2, k, j, i); - Bcon[3] = P(m_p.B3, k, j, i); - DLOOP2 Bcov[mu] += G.gcov(Loci::center, j, i, mu, nu) * Bcon[nu]; - const Real Bsq = dot(Bcon, Bcov); - - // Normal observer fluid momentum - Real Qcov[GR_DIM] = {0}; - Qcov[0] = w_old * Dtmp.ucon[0] * Dtmp.ucov[0] + pg; - Qcov[1] = w_old * Dtmp.ucon[0] * Dtmp.ucov[1]; - Qcov[2] = w_old * Dtmp.ucon[0] * Dtmp.ucov[2]; - Qcov[3] = w_old * Dtmp.ucon[0] * Dtmp.ucov[3]; - - // Momentum along magnetic field lines (must be held constant) - double QdotB = dot(Bcon, Qcov); - - // Initial parallel velocity (refer R17 Eqn B10) - Real vpar = QdotB / (sqrt(Bsq) * w_old * pow(Dtmp.ucon[0], 2.)); - - Real ucon_dr[GR_DIM] = {0}; - // t-component of drift velocity (refer R17 Eqn B13) - ucon_dr[0] = 1. / sqrt(pow(Dtmp.ucon[0], -2.) + pow(vpar, 2.)); - // spatial components of drift velocity (refer R17 Eqn B11) - for (int mu = 1; mu < GR_DIM; mu++) { - ucon_dr[mu] = Dtmp.ucon[mu] * (ucon_dr[0] / Dtmp.ucon[0]) - (vpar * Bcon[mu] * ucon_dr[0] / sqrt(Bsq)); - } - - // Update rho, uu and compute new enthalpy - P(m_p.RHO, k, j, i) = m::max(rho, rhoflr_max); - P(m_p.UU, k, j, i) = m::max(uu, uflr_max); - const Real pg_new = (gam - 1.) * P(m_p.UU, k, j, i); - const Real w_new = P(m_p.RHO, k, j, i) + P(m_p.UU, k, j, i) + pg_new; - - // New parallel velocity (refer R17 Eqn B14) - const Real x = (2. * QdotB) / (sqrt(Bsq) * w_new * ucon_dr[0]); - vpar = x / (1 + sqrt(1 + x*x)) * (1. / ucon_dr[0]); - - // New fluid four velocity (refer R17 Eqns B13 and B11) - Dtmp.ucon[0] = 1. / sqrt(pow(ucon_dr[0], -2.) - pow(vpar, 2.)); - for (int mu = 1; mu < GR_DIM; mu++) { - Dtmp.ucon[mu] = ucon_dr[mu] * (Dtmp.ucon[0] / ucon_dr[0]) + (vpar * Bcon[mu] * Dtmp.ucon[0] / sqrt(Bsq)); - } - G.lower(Dtmp.ucon, Dtmp.ucov, k, j, i, Loci::center); - - // New Lorentz factor - const Real gamma = Dtmp.ucon[0] * lapse; - - // New velocity primitives - P(m_p.U1, k, j, i) = Dtmp.ucon[1] + (beta[1] * gamma/lapse); - P(m_p.U2, k, j, i) = Dtmp.ucon[2] + (beta[2] * gamma/lapse); - P(m_p.U3, k, j, i) = Dtmp.ucon[3] + (beta[3] * gamma/lapse); - - } else { - // Add the material in the normal observer frame, by: - // Adding the floors to the primitive variables - const Real rho_add = m::max(0., rhoflr_max - rho); - const Real u_add = m::max(0., uflr_max - u); - const Real uvec[NVEC] = {0}, B[NVEC] = {0}; - - // Calculating the corresponding conserved variables - Real rho_ut, T[GR_DIM]; - GRMHD::p_to_u_mhd(G, rho_add, u_add, uvec, B, gam, k, j, i, rho_ut, T, loc); - - // Add new conserved mass/energy to the current "conserved" state, - // and to the local primitives as a guess - P(m_p.RHO, k, j, i) += rho_add; - P(m_p.UU, k, j, i) += u_add; - // Add any velocity here - U(m_u.RHO, k, j, i) += rho_ut; - U(m_u.UU, k, j, i) += T[0]; // Note this shouldn't be a single loop: m_u.U1 != m_u.UU + 1 necessarily - U(m_u.U1, k, j, i) += T[1]; - U(m_u.U2, k, j, i) += T[2]; - U(m_u.U3, k, j, i) += T[3]; - - // Recover primitive variables from conserved versions - pflag = GRMHD::u_to_p(G, U, m_u, gam, k, j, i, loc, P, m_p); - // If that fails, we've effectively already applied the floors in fluid-frame to the prims, - // so we just formalize that - if (pflag) { - // TODO should be all Flux - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); - } - } - } - - // Ressler adjusts KTOT & KEL to conserve u whenever adjusting rho - // but does *not* recommend adjusting them when u hits floors/ceilings - // This is in contrast to ebhlight, which heats electrons before applying *any* floors, - // and resets KTOT during floor application without touching KEL - // TODO move to another loop/function, over electrons. Have to preserve rho/rho_old ratio tho - if (floors.adjust_k && (fflag & HIT_FLOOR_GEOM_RHO || fflag & HIT_FLOOR_B_RHO)) { - const Real reduce = m::pow(rho / P(m_p.RHO, k, j, i), gam); - const Real reduce_e = m::pow(rho / P(m_p.RHO, k, j, i), 4./3); // TODO pipe in real gam_e - if (m_p.KTOT >= 0) P(m_p.KTOT, k, j, i) *= reduce; - if (m_p.K_CONSTANT >= 0) P(m_p.K_CONSTANT, k, j, i) *= reduce_e; - if (m_p.K_HOWES >= 0) P(m_p.K_HOWES, k, j, i) *= reduce_e; - if (m_p.K_KAWAZURA >= 0) P(m_p.K_KAWAZURA, k, j, i) *= reduce_e; - if (m_p.K_WERNER >= 0) P(m_p.K_WERNER, k, j, i) *= reduce_e; - if (m_p.K_ROWAN >= 0) P(m_p.K_ROWAN, k, j, i) *= reduce_e; - if (m_p.K_SHARMA >= 0) P(m_p.K_SHARMA, k, j, i) *= reduce_e; - } - - // Return both flags - return fflag + pflag; -} +TaskStatus ApplyGRMHDFloors(MeshBlockData *rc, IndexDomain domain); /** - * Apply just the geometric floors to a set of local primitives. - * Specifically called after reconstruction when using non-TVD schemes, e.g. WENO5. - * Reimplemented to be fast and fit the general prim_to_flux calling convention. - * - * @return fflag: since no inversion is performed, this just returns a flag representing which geometric floors were hit - * - * NOT LOCKSTEP: Operates on and respects primitives *only* + * Apply the same floors as above, in the same way, except: + * 1. No ceilings + * 2. Don't record results to 'fflag' or 'pflag' + * Used for problems where some part of the domain is initialized to + * "whatever the floor value is." + * This function can be called even if the Floors package is not initialized. */ -template -KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, const VarMap& m, - const Real& gam, const int& j, const int& i, - const Floors::Prescription& floors, const Loci loc=Loci::center) -{ - // Apply only the geometric floors - Real rhoflr_geom, uflr_geom; - if(G.coords.spherical()) { - GReal Xembed[GR_DIM]; - G.coord_embed(0, j, i, loc, Xembed); - GReal r = Xembed[1]; - - if (floors.use_r_char) { - // Steeper floor from iharm3d - Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); - rhoflr_geom = floors.rho_min_geom * rhoscal; - uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); - } else { - // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d - } - } else { - rhoflr_geom = floors.rho_min_geom; - uflr_geom = floors.u_min_geom; - } - - int fflag = 0; -#if RECORD_POST_RECON - // Record all the floors that were hit, using bitflags - // Record Geometric floor hits - fflag |= (rhoflr_geom > P(m.RHO)) * HIT_FLOOR_GEOM_RHO_FLUX; - fflag |= (uflr_geom > P(m.UU)) * HIT_FLOOR_GEOM_U_FLUX; -#endif - - P(m.RHO) += m::max(0., rhoflr_geom - P(m.RHO)); - P(m.UU) += m::max(0., uflr_geom - P(m.UU)); - - return fflag; -} - -template -KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, const VarMap& m, - const Real& gam, const int& k, const int& j, const int& i, - const Floors::Prescription& floors, const Loci loc=Loci::center) -{ - // Apply only the geometric floors - Real rhoflr_geom, uflr_geom; - if(G.coords.spherical()) { - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, loc, Xembed); - GReal r = Xembed[1]; - - if (floors.use_r_char) { - // Steeper floor from iharm3d - Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); - rhoflr_geom = floors.rho_min_geom * rhoscal; - uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); - } else { - // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d - } - } else { - rhoflr_geom = floors.rho_min_geom; - uflr_geom = floors.u_min_geom; - } - - int fflag = 0; -#if RECORD_POST_RECON - // Record all the floors that were hit, using bitflags - // Record Geometric floor hits - fflag |= (rhoflr_geom > P(m.RHO, k, j, i)) * HIT_FLOOR_GEOM_RHO_FLUX; - fflag |= (uflr_geom > P(m.UU, k, j, i)) * HIT_FLOOR_GEOM_U_FLUX; -#endif - - P(m.RHO, k, j, i) += m::max(0., rhoflr_geom - P(m.RHO, k, j, i)); - P(m.UU, k, j, i) += m::max(0., uflr_geom - P(m.UU, k, j, i)); - - return fflag; -} +TaskStatus ApplyInitialFloors(MeshBlockData *rc, IndexDomain domain); /** - * Apply limits on the Extended MHD variables - * - * @return elag, a bitflag indicating whether each particular limit was hit, allowing representation of arbitrary combinations - * See decs.h for bit names. - * - * The maximum heat flux is limited by the saturated value given by a hot cloud in cold gas. - * The bounds on the pressure anisotropy as due to the mirror and firehose instability limits. - * - * Although only q, dP are updated here, prim_to_flux updates all conserved. - * This shouldn't be an issue though since PtoU in analytic and will result in the same value for the ideal MHD variables. + * Print a summary of floors hit */ -KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, - const Real& gam, const EMHD::EMHD_parameters& emhd_params, - const int& k, const int& j, const int& i, const Floors::Prescription& floors, - const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) -{ - int eflag = 0; - - Real rho = P(m_p.RHO, k, j, i); - Real uu = P(m_p.UU, k, j, i); - Real qtilde = P(m_p.Q, k, j, i); - Real dPtilde = P(m_p.DP, k, j, i); - - Real pg = (gam - 1.) * uu; - Real Theta = pg / rho; - Real cs = m::sqrt(gam * pg / (rho + (gam * uu))); - - FourVectors D; - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, D); - Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); - - Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e, "instability_limits"); - - Real q, dP; - EMHD::convert_prims_to_q_dP(qtilde, dPtilde, rho, Theta, cs*cs, emhd_params, q, dP); - - Real qmax = 1.07 * rho * m::pow(cs, 3.); - Real max_frac = m::max(m::abs(q) / qmax, 1.); - if (fabs(q) / qmax > 1.) - eflag |= HIT_Q_LIMIT; - - P(m_p.Q, k, j, i) = P(m_p.Q, k, j, i) / max_frac; - - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(1.07 * 0.5 * bsq * dP_comp_ratio, 1.49 * pg); - Real dP_minus = m::max(-1.07 * bsq, -2.99 * pg); - - if (dP > 0. && (dP / dP_plus > 1.)) - eflag |= HIT_DP_LIMIT; - else if (dP < 0. && (dP / dP_minus > 1.)) - eflag |= HIT_DP_LIMIT; - - if (dP > 0.) - P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_plus, 1.)); - else - P(m_p.DP, k, j, i) = P(m_p.DP, k, j, i) * (1. / m::max(dP / dP_minus, 1.)); - - Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); - - return eflag; - -} +TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md); } // namespace Floors diff --git a/kharma/floors/floors_functions.hpp b/kharma/floors/floors_functions.hpp new file mode 100644 index 00000000..de9b6a9d --- /dev/null +++ b/kharma/floors/floors_functions.hpp @@ -0,0 +1,414 @@ +/* + * File: floors_functions.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "floors.hpp" + +/** + * Device-side functions for applying GRMHD floors + */ +namespace Floors { + +/** + * Apply all ceilings together, currently at most one on velocity and two on internal energy + * + * @return fflag, a bitflag indicating whether each particular floor was hit, allowing representation of arbitrary combinations + * See decs.h for bit names. + * + * LOCKSTEP: this function respects P and returns consistent P<->U + */ +KOKKOS_INLINE_FUNCTION int apply_ceilings(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, + const Real& gam, const int& k, const int& j, const int& i, const Floors::Prescription& floors, + const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) +{ + int fflag = 0; + // First apply ceilings: + // 1. Limit gamma with respect to normal observer + Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, loc); + + if (gamma > floors.gamma_max) { + fflag |= FFlag::GAMMA; + + Real f = m::sqrt((m::pow(floors.gamma_max, 2) - 1.)/(m::pow(gamma, 2) - 1.)); + VLOOP P(m_p.U1+v, k, j, i) *= f; + } + + // 2. Limit the entropy by controlling u, to avoid anomalous cooling from funnel wall + // Note this technically applies the condition *one step sooner* than legacy, since it operates on + // the entropy as calculated from current conditions, rather than the value kept from the previous + // step for calculating dissipation. + Real ktot = (gam - 1.) * P(m_p.UU, k, j, i) / m::pow(P(m_p.RHO, k, j, i), gam); + if (ktot > floors.ktot_max) { + fflag |= FFlag::KTOT; + + P(m_p.UU, k, j, i) = floors.ktot_max / ktot * P(m_p.UU, k, j, i); + } + // Also apply the ceiling to the advected entropy KTOT, if we're keeping track of that + // (either for electrons, or robust primitive inversions in future) + // TODO TODO MOVE TO ELECTRONS PACKAGE (or Flux::p_to_u below!!) + if (m_p.KTOT >= 0 && (P(m_p.KTOT, k, j, i) > floors.ktot_max)) { + fflag |= FFlag::KTOT; + P(m_p.KTOT, k, j, i) = floors.ktot_max; + } + + // 3. Limit the temperature by controlling u. Can optionally add density instead, implemented in apply_floors + if (floors.temp_adjust_u && P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i) > floors.u_over_rho_max) { + fflag |= FFlag::TEMP; + + P(m_p.UU, k, j, i) = floors.u_over_rho_max * P(m_p.RHO, k, j, i); + } + + if (fflag) { + // Keep lockstep! + GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); + } + + return fflag; +} + +/** + * Apply floors of several types in determining how to add mass and internal energy to preserve stability. + * All floors which might apply are recorded separately, then mass/energy are added *in normal observer frame* + * + * @return fflag + pflag: fflag is a flagset starting at the sixth bit from the right. pflag is a number <32. + * This returns the sum, with the caller responsible for separating what's desired. + * + * LOCKSTEP: this function respects P and ignores U in order to return consistent P<->U + */ +KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, + const Real& gam, const EMHD::EMHD_parameters& emhd_params, + const int& k, const int& j, const int& i, const Floors::Prescription& floors, + const VariablePack& U, const VarMap& m_u, const Loci loc=Loci::center) +{ + int fflag = 0; + // Then apply floors: + // 1. Geometric hard floors, not based on fluid relationships + Real rhoflr_geom, uflr_geom; + bool use_ff, use_df; + if(G.coords.spherical()) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, loc, Xembed); + GReal r = Xembed[1]; + // TODO measure whether this/if 1 is really faster + // GReal r = m::exp(G.x1v(i)); + + // Use the fluid frame if specified, or in outer domain + use_ff = floors.fluid_frame || (floors.mixed_frame && r > floors.frame_switch); + // Use the drift frame if specified + use_df = floors.drift_frame; + + if (floors.use_r_char) { + // Steeper floor from iharm3d + const Real rhoscal = 1/(r * r * (1 + r / floors.r_char)); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); + } else { + // Original floors from iharm2d + rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); + uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + } + } else { + rhoflr_geom = floors.rho_min_geom; + uflr_geom = floors.u_min_geom; + use_ff = floors.fluid_frame; + use_df = floors.drift_frame; + } + Real rho = P(m_p.RHO, k, j, i); + Real u = P(m_p.UU, k, j, i); + + // 2. Magnetization ceilings: impose maximum magnetization sigma = bsq/rho, and inverse beta prop. to bsq/U + FourVectors Dtmp; + // TODO is there a more efficient way to calculate just bsq? + GRMHD::calc_4vecs(G, P, m_p, k, j, i, loc, Dtmp); + double bsq = dot(Dtmp.bcon, Dtmp.bcov); + double rhoflr_b = bsq / floors.bsq_over_rho_max; + double uflr_b = bsq / floors.bsq_over_u_max; + + // Evaluate max U floor, needed for temp ceiling below + double uflr_max = m::max(uflr_geom, uflr_b); + + double rhoflr_max; + if (!floors.temp_adjust_u) { + // 3. Temperature ceiling: impose maximum temperature u/rho + // Take floors on U into account + double rhoflr_temp = m::max(u, uflr_max) / floors.u_over_rho_max; + // Record hitting temperature ceiling + fflag |= (rhoflr_temp > rho) * FFlag::TEMP; // Misnomer for consistency + + // Evaluate max rho floor + rhoflr_max = m::max(m::max(rhoflr_geom, rhoflr_b), rhoflr_temp); + } else { + // Evaluate max rho floor + rhoflr_max = m::max(rhoflr_geom, rhoflr_b); + } + + // If we need to do anything... + if (rhoflr_max > rho || uflr_max > u) { + + // Record all the floors that were hit, using bitflags + // Record Geometric floor hits + fflag |= (rhoflr_geom > rho) * FFlag::GEOM_RHO; + fflag |= (uflr_geom > u) * FFlag::GEOM_U; + // Record Magnetic floor hits + fflag |= (rhoflr_b > rho) * FFlag::B_RHO; + fflag |= (uflr_b > u) * FFlag::B_U; + + if (use_ff) { + P(m_p.RHO, k, j, i) += m::max(0., rhoflr_max - rho); + P(m_p.UU, k, j, i) += m::max(0., uflr_max - u); + // TODO should be all Flux + GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); + + } else if (use_df) { + // Drift frame floors. Refer to Appendix B3 in https://doi.org/10.1093/mnras/stx364 (hereafter R17) + const Real gdet = G.gdet(Loci::center, j, i); + const Real lapse = 1./m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + double beta[GR_DIM] = {0}; + + beta[1] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 1); + beta[2] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 2); + beta[3] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 3); + + // Fluid quantities (four velocities have been computed above) + const Real rho = P(m_p.RHO, k, j, i); + const Real uu = P(m_p.UU, k, j, i); + const Real pg = (gam - 1.) * uu; + const Real w_old = m::max(rho + uu + pg, SMALL); + + // Normal observer magnetic field + Real Bcon[GR_DIM] = {0}; + Real Bcov[GR_DIM] = {0}; + Bcon[0] = 0; + Bcon[1] = P(m_p.B1, k, j, i); + Bcon[2] = P(m_p.B2, k, j, i); + Bcon[3] = P(m_p.B3, k, j, i); + DLOOP2 Bcov[mu] += G.gcov(Loci::center, j, i, mu, nu) * Bcon[nu]; + const Real Bsq = m::max(dot(Bcon, Bcov), SMALL); + + // Normal observer fluid momentum + Real Qcov[GR_DIM] = {0}; + Qcov[0] = w_old * Dtmp.ucon[0] * Dtmp.ucov[0] + pg; + Qcov[1] = w_old * Dtmp.ucon[0] * Dtmp.ucov[1]; + Qcov[2] = w_old * Dtmp.ucon[0] * Dtmp.ucov[2]; + Qcov[3] = w_old * Dtmp.ucon[0] * Dtmp.ucov[3]; + + // Momentum along magnetic field lines (must be held constant) + double QdotB = dot(Bcon, Qcov); + + // Initial parallel velocity (refer R17 Eqn B10) + Real vpar = QdotB / (sqrt(Bsq) * w_old * pow(Dtmp.ucon[0], 2.)); + + Real ucon_dr[GR_DIM] = {0}; + // t-component of drift velocity (refer R17 Eqn B13) + ucon_dr[0] = 1. / sqrt(pow(Dtmp.ucon[0], -2.) + pow(vpar, 2.)); + // spatial components of drift velocity (refer R17 Eqn B11) + for (int mu = 1; mu < GR_DIM; mu++) { + ucon_dr[mu] = Dtmp.ucon[mu] * (ucon_dr[0] / Dtmp.ucon[0]) - (vpar * Bcon[mu] * ucon_dr[0] / sqrt(Bsq)); + } + + // Update rho, uu and compute new enthalpy + P(m_p.RHO, k, j, i) = m::max(rho, rhoflr_max); + P(m_p.UU, k, j, i) = m::max(uu, uflr_max); + const Real pg_new = (gam - 1.) * P(m_p.UU, k, j, i); + const Real w_new = P(m_p.RHO, k, j, i) + P(m_p.UU, k, j, i) + pg_new; + + // New parallel velocity (refer R17 Eqn B14) + const Real x = (2. * QdotB) / (sqrt(Bsq) * w_new * ucon_dr[0]); + vpar = x / (1 + sqrt(1 + x*x)) * (1. / ucon_dr[0]); + + // New fluid four velocity (refer R17 Eqns B13 and B11) + Dtmp.ucon[0] = 1. / sqrt(pow(ucon_dr[0], -2.) - pow(vpar, 2.)); + for (int mu = 1; mu < GR_DIM; mu++) { + Dtmp.ucon[mu] = ucon_dr[mu] * (Dtmp.ucon[0] / ucon_dr[0]) + (vpar * Bcon[mu] * Dtmp.ucon[0] / sqrt(Bsq)); + } + G.lower(Dtmp.ucon, Dtmp.ucov, k, j, i, Loci::center); + + // New Lorentz factor + const Real gamma = Dtmp.ucon[0] * lapse; + + // New velocity primitives + P(m_p.U1, k, j, i) = Dtmp.ucon[1] + (beta[1] * gamma/lapse); + P(m_p.U2, k, j, i) = Dtmp.ucon[2] + (beta[2] * gamma/lapse); + P(m_p.U3, k, j, i) = Dtmp.ucon[3] + (beta[3] * gamma/lapse); + + // Update the conserved variables + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u, loc); + + } else { + // Add the material in the normal observer frame, by: + // Adding the floors to the primitive variables + const Real rho_add = m::max(0., rhoflr_max - rho); + const Real u_add = m::max(0., uflr_max - u); + const Real uvec[NVEC] = {0}, B[NVEC] = {0}; + + // Calculating the corresponding conserved variables + Real rho_ut, T[GR_DIM]; + GRMHD::p_to_u_mhd(G, rho_add, u_add, uvec, B, gam, k, j, i, rho_ut, T, loc); + + // Add new conserved mass/energy to the current "conserved" state, + // and to the local primitives as a guess + P(m_p.RHO, k, j, i) += rho_add; + P(m_p.UU, k, j, i) += u_add; + // Add any velocity here + U(m_u.RHO, k, j, i) += rho_ut; + U(m_u.UU, k, j, i) += T[0]; // Note this shouldn't be a single loop: m_u.U1 != m_u.UU + 1 necessarily + U(m_u.U1, k, j, i) += T[1]; + U(m_u.U2, k, j, i) += T[2]; + U(m_u.U3, k, j, i) += T[3]; + + // Recover primitive variables from conserved versions + // TODO selector here when we get more + Inverter::Status pflag = Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, loc); + // If that fails, we've effectively already applied the floors in fluid-frame to the prims, + // so we just formalize that + if (Inverter::failed(pflag)) { + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u, loc); + fflag += static_cast(pflag); + } + } + } + + // TODO separate electron floors! + // Ressler adjusts KTOT & KEL to conserve u whenever adjusting rho + // but does *not* recommend adjusting them when u hits floors/ceilings + // This is in contrast to ebhlight, which heats electrons before applying *any* floors, + // and resets KTOT during floor application without touching KEL + if (floors.adjust_k && (fflag & FFlag::GEOM_RHO || fflag & FFlag::B_RHO)) { + const Real reduce = m::pow(rho / P(m_p.RHO, k, j, i), gam); + const Real reduce_e = m::pow(rho / P(m_p.RHO, k, j, i), 4./3); // TODO pipe in real gam_e + if (m_p.KTOT >= 0) P(m_p.KTOT, k, j, i) *= reduce; + if (m_p.K_CONSTANT >= 0) P(m_p.K_CONSTANT, k, j, i) *= reduce_e; + if (m_p.K_HOWES >= 0) P(m_p.K_HOWES, k, j, i) *= reduce_e; + if (m_p.K_KAWAZURA >= 0) P(m_p.K_KAWAZURA, k, j, i) *= reduce_e; + if (m_p.K_WERNER >= 0) P(m_p.K_WERNER, k, j, i) *= reduce_e; + if (m_p.K_ROWAN >= 0) P(m_p.K_ROWAN, k, j, i) *= reduce_e; + if (m_p.K_SHARMA >= 0) P(m_p.K_SHARMA, k, j, i) *= reduce_e; + } + + // Return fflag (with pflag added if NOF floors were used!) + return fflag; +} + +/** + * Apply just the geometric floors to a set of local primitives. + * Specifically called after reconstruction when using non-TVD schemes, e.g. WENO5. + * Reimplemented to be fast and fit the general prim_to_flux calling convention. + * + * @return fflag: since no inversion is performed, this just returns a flag representing which geometric floors were hit + * + * NOT LOCKSTEP: Operates on and respects primitives *only* + */ +template +KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, const VarMap& m, + const Real& gam, const int& j, const int& i, + const Floors::Prescription& floors, const Loci loc=Loci::center) +{ + // Apply only the geometric floors + Real rhoflr_geom, uflr_geom; + if(G.coords.spherical()) { + GReal Xembed[GR_DIM]; + G.coord_embed(0, j, i, loc, Xembed); + GReal r = Xembed[1]; + + if (floors.use_r_char) { + // Steeper floor from iharm3d + Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); + } else { + // Original floors from iharm2d + rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); + uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + } + } else { + rhoflr_geom = floors.rho_min_geom; + uflr_geom = floors.u_min_geom; + } + + int fflag = 0; +#if RECORD_POST_RECON + // Record all the floors that were hit, using bitflags + // Record Geometric floor hits + fflag |= (rhoflr_geom > P(m.RHO)) * FFlag::GEOM_RHO_FLUX; + fflag |= (uflr_geom > P(m.UU)) * FFlag::GEOM_U_FLUX; +#endif + + P(m.RHO) += m::max(0., rhoflr_geom - P(m.RHO)); + P(m.UU) += m::max(0., uflr_geom - P(m.UU)); + + return fflag; +} + +template +KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, const VarMap& m, + const Real& gam, const int& k, const int& j, const int& i, + const Floors::Prescription& floors, const Loci loc=Loci::center) +{ + // Apply only the geometric floors + Real rhoflr_geom, uflr_geom; + if(G.coords.spherical()) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, loc, Xembed); + GReal r = Xembed[1]; + + if (floors.use_r_char) { + // Steeper floor from iharm3d + Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); + } else { + // Original floors from iharm2d + rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); + uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + } + } else { + rhoflr_geom = floors.rho_min_geom; + uflr_geom = floors.u_min_geom; + } + + int fflag = 0; +#if RECORD_POST_RECON + // Record all the floors that were hit, using bitflags + // Record Geometric floor hits + fflag |= (rhoflr_geom > P(m.RHO, k, j, i)) * FFlag::GEOM_RHO_FLUX; + fflag |= (uflr_geom > P(m.UU, k, j, i)) * FFlag::GEOM_U_FLUX; +#endif + + P(m.RHO, k, j, i) += m::max(0., rhoflr_geom - P(m.RHO, k, j, i)); + P(m.UU, k, j, i) += m::max(0., uflr_geom - P(m.UU, k, j, i)); + + return fflag; +} + +} // Floors \ No newline at end of file diff --git a/kharma/flux.cpp b/kharma/flux.cpp deleted file mode 100644 index b3ecb51a..00000000 --- a/kharma/flux.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * File: fluxes.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "flux.hpp" - -#include "grmhd.hpp" - -using namespace parthenon; - -// GetFlux is in the header, as it is templated on reconstruction scheme and flux direction -// That's also why we don't have any extra includes in here - -TaskStatus Flux::PtoU(MeshBlockData *rc, IndexDomain domain) -{ - Flag(rc, "Getting conserved variables"); - // Pointers - auto pmb = rc->GetBlockPointer(); - // Options - const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); - const Real gam = pars.Get("gamma"); - auto pkgs = pmb->packages.AllPackages(); - const bool flux_ct = pkgs.count("B_FluxCT"); - const bool b_cd = pkgs.count("B_CD"); - const bool use_electrons = pkgs.count("Electrons"); - const bool use_emhd = pkgs.count("EMHD"); - MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); - - EMHD::EMHD_parameters emhd_params_tmp; - if (use_emhd) { - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); - emhd_params_tmp = emhd_pars.Get("emhd_params"); - } - const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; - - // Pack variables - PackIndexMap prims_map, cons_map; - const auto& P_all = rc->PackVariables({isPrimitive}, prims_map); - const auto& U_all = rc->PackVariables({Metadata::Conserved}, cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - const int nvar = U_all.GetDim(4); - - const IndexRange ib = rc->GetBoundsI(domain); - const IndexRange jb = rc->GetBoundsJ(domain); - const IndexRange kb = rc->GetBoundsK(domain); - const int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); - - const auto& G = pmb->coords; - - // This is basically what all kernels look like if I want to stick to - // single, simple device side functions called over slices - // See fluxes.hpp or implicit.cpp for explanations of what everything here does - const int scratch_level = 1; - const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nvar, n1); - const size_t total_scratch_bytes = (2) * var_size_in_bytes; - - parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "PtoU", pmb->exec_space, - total_scratch_bytes, scratch_level, kb.s, kb.e, jb.s, jb.e, - KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& k, const int& j) { - ScratchPad2D P_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D U_s(member.team_scratch(scratch_level), nvar, n1); - - PLOOP parthenon::par_for_inner(member, ib.s, ib.e, - [&](const int& i) { - P_s(ip, i) = P_all(ip, k, j, i); - U_s(ip, i) = U_all(ip, k, j, i); - } - ); - - parthenon::par_for_inner(member, ib.s, ib.e, - [&](const int& i) { - auto P = Kokkos::subview(P_s, Kokkos::ALL(), i); - auto U = Kokkos::subview(U_s, Kokkos::ALL(), i); - Flux::p_to_u(G, P, m_p, emhd_params, gam, j, i, U, m_u); - } - ); - - PLOOP parthenon::par_for_inner(member, ib.s, ib.e, - [&](const int& i) { - P_all(ip, k, j, i) = P_s(ip, i); - U_all(ip, k, j, i) = U_s(ip, i); - } - ); - } - ); - - Flag(rc, "Got conserved variables"); - return TaskStatus::complete; -} diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp new file mode 100644 index 00000000..0d04cf83 --- /dev/null +++ b/kharma/flux/flux.cpp @@ -0,0 +1,178 @@ +/* + * File: flux.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "flux.hpp" +// Most includes are in the header TODO fix? + +#include "grmhd.hpp" + +using namespace parthenon; + +// GetFlux is in the header file get_flux.hpp, as it is templated on reconstruction scheme and flux direction + +TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Getting conserved GRMHD variables"); + // Pointers + auto pmb = rc->GetBlockPointer(); + // Options + const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + // Pack variables + PackIndexMap prims_map, cons_map; + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + const int nvar = U.GetDim(4); + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + + const auto& G = pmb->coords; + + pmb->par_for("p_to_u_mhd", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Flux::p_to_u_mhd(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + } + ); + + + Flag(rc, "Got conserved variables"); + return TaskStatus::complete; +} + +TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Getting conserved GRMHD variables"); + // Pointers + auto pmb = rc->GetBlockPointer(); + // Options + const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + // Pack variables + PackIndexMap prims_map, cons_map; + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + const int nvar = U.GetDim(4); + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + + const auto& G = pmb->coords; + + pmb->par_for("p_to_u", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + } + ); + + + Flag(rc, "Got conserved variables"); + return TaskStatus::complete; +} + +TaskStatus Flux::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) +{ + for (int i=0; i < md->NumBlocks(); ++i) + Flux::BlockPtoU(md->GetBlockData(i).get(), domain, coarse); + return TaskStatus::complete; +} + +void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) +{ + Flag(mdudt, "Adding GRMHD source term"); + // Pointers + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto pkgs = pmb0->packages; + // Options + const auto& pars = pkgs.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + + // All connection coefficients are zero in Cartesian Minkowski space + // TODO do we know this fully in init? + if (pmb0->coords.coords.is_cart_minkowski()) return; + + // Pack variables + PackIndexMap prims_map, cons_map; + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + + // EMHD params + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb0->packages); + + // Get sizes + IndexDomain domain = IndexDomain::interior; + auto ib = md->GetBoundsI(domain); + auto jb = md->GetBoundsJ(domain); + auto kb = md->GetBoundsK(domain); + auto block = IndexRange{0, P.GetDim(5)-1}; + + pmb0->par_for("tmunu_source", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { + const auto& G = dUdt.GetCoords(b); + FourVectors D; + GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, D); + // Call Flux::calc_tensor which will in turn call the right calc_tensor based on the number of primitives + Real Tmu[GR_DIM] = {0}; + Real new_du[GR_DIM] = {0}; + for (int mu = 0; mu < GR_DIM; ++mu) { + Flux::calc_tensor(G, P(b), m_p, D, emhd_params, gam, k, j, i, mu, Tmu); + for (int nu = 0; nu < GR_DIM; ++nu) { + // Contract mhd stress tensor with connection, and multiply by metric determinant + for (int lam = 0; lam < GR_DIM; ++lam) { + new_du[lam] += Tmu[nu] * G.gdet_conn(j, i, nu, lam, mu); + } + } + } + + dUdt(b, m_u.UU, k, j, i) += new_du[0]; + VLOOP dUdt(b, m_u.U1 + v, k, j, i) += new_du[1 + v]; + } + ); + + Flag(mdudt, "Added"); +} \ No newline at end of file diff --git a/kharma/flux/flux.hpp b/kharma/flux/flux.hpp new file mode 100644 index 00000000..9a35ec0d --- /dev/null +++ b/kharma/flux/flux.hpp @@ -0,0 +1,90 @@ +/* + * File: flux.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include + +#include "debug.hpp" +#include "floors.hpp" +#include "flux_functions.hpp" +#include "pack.hpp" +#include "reconstruction.hpp" +#include "types.hpp" + +namespace Flux { + +/** + * Add the geometric source term present in the covariant derivative of the stress-energy tensor, + * S_nu = sqrt(-g) T^kap_lam Gamma^lam_nu_kap + * This is defined in Flux:: rather than GRMHD:: because the stress-energy tensor may contain + * (E)GR(R)(M)HD terms. + */ +void AddGeoSource(MeshData *md, MeshData *mdudt); + +/** + * Likewise, the conversion P->U, even for just the GRMHD variables, requires (consists of) + * the stress-energy tensor. + */ +TaskStatus BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse=false); + +/** + * When calculating fluxes, we use Flux::prim_to_flux, which must generate conserved variables + * and fluxes for all loaded packages correctly. + * These calls just run that function over the grid. + */ +TaskStatus BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse=false); +TaskStatus MeshPtoU(MeshData *md, IndexDomain domain, bool coarse=false); + +// Fluxes a.k.a. "Approximate Riemann Solvers" +// More complex solvers require speed estimates not calculable completely from +// invariants, necessitating frame transformations and related madness. +// These have identical signatures, so that we could runtime relink w/variant like coordinate_embedding + +// Local Lax-Friedrichs flux (usual, more stable) +KOKKOS_INLINE_FUNCTION Real llf(const Real& fluxL, const Real& fluxR, const Real& cmax, + const Real& cmin, const Real& Ul, const Real& Ur) +{ + Real ctop = m::max(cmax, cmin); + return 0.5 * (fluxL + fluxR - ctop * (Ur - Ul)); +} +// Harten, Lax, van Leer, & Einfeldt flux (early problems but not extensively studied since) +KOKKOS_INLINE_FUNCTION Real hlle(const Real& fluxL, const Real& fluxR, const Real& cmax, + const Real& cmin, const Real& Ul, const Real& Ur) +{ + return (cmax*fluxL + cmin*fluxR - cmax*cmin*(Ur - Ul)) / (cmax + cmin); +} + +} diff --git a/kharma/flux_functions.hpp b/kharma/flux/flux_functions.hpp similarity index 85% rename from kharma/flux_functions.hpp rename to kharma/flux/flux_functions.hpp index a1c8052a..bb4073ae 100644 --- a/kharma/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -40,21 +40,22 @@ #include "grmhd_functions.hpp" #include "kharma_utils.hpp" #include "types.hpp" + /** - * Device-side functions prim_to_flux and vchar, which will depend on + * Device-side functions calc_tensor, prim_to_flux, and vchar, which will depend on * the set of enabled packages. */ namespace Flux { +// TODO Q > 0 != emhd_enabled. Store enablement in emhd_params since we need it anyway template KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& dir, Real T[GR_DIM]) { if (m_p.Q >= 0) { - // Apply higher-order terms conversion if necessary Real q, dP; const Real Theta = (gam - 1) * P(m_p.UU) / P(m_p.RHO); @@ -62,7 +63,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, EMHD::convert_prims_to_q_dP(P(m_p.Q), P(m_p.DP), P(m_p.RHO), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor - EMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), q, dP, D, dir, T); + EMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), emhd_params, q, dP, D, dir, T); } else if (m_p.B1 >= 0) { // GRMHD stress-energy tensor w/ first index up, second index down GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); @@ -70,30 +71,24 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, // GRHD stress-energy tensor w/ first index up, second index down GRHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); } - - // if (i == 11 && j == 11) printf("mhd: %6.5e %6.5e %6.5e %6.5e %6.5e\n", flux(m_u.RHO), T[0], T[1], T[2], T[3]); -} - -template -KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, - const Real& gam, const int& dir, - Real T[GR_DIM]) -{ - if (m_p.B1 >= 0) { - // GRMHD stress-energy tensor w/ first index up, second index down - GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); - } else { - // GRHD stress-energy tensor w/ first index up, second index down - GRHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); - } } template KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, - const Real& gam, const int& k, const int& j, const int& i, const int& dir, - Real T[GR_DIM]) + const EMHD::EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, const int& dir, + Real T[GR_DIM]) { - if (m_p.B1 >= 0) { + if (m_p.Q >= 0) { + // Apply higher-order terms conversion if necessary + Real q, dP; + const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); + const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); + EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); + + // Then calculate the tensor + EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), emhd_params, q, dP, D, dir, T); + } else if (m_p.B1 >= 0) { // GRMHD stress-energy tensor w/ first index up, second index down GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); } else { @@ -184,24 +179,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - if (m_p.Q >= 0) { - - // Apply higher-order terms conversion if necessary - Real q, dP; - const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); - const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); - EMHD::convert_prims_to_q_dP(P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); - - // Then calculate the tensor - EMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), q, dP, D, dir, T); - } else if (m_p.B1 >= 0) { - // GRMHD stress-energy tensor w/ first index up, second index down - GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); - } else { - // GRHD stress-energy tensor w/ first index up, second index down - GRHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); - } - // if (i == 11 && j == 11) printf("mhd: %6.5e %6.5e %6.5e %6.5e %6.5e\n", flux(m_u.RHO), T[0], T[1], T[2], T[3]); + calc_tensor(G, P, m_p, D, emhd_params, gam, k, j, i, dir, T); flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); flux(m_u.U1, k, j, i) = T[1] * gdet; flux(m_u.U2, k, j, i) = T[2] * gdet; @@ -252,7 +230,27 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P if (m_p.K_SHARMA >= 0) flux(m_u.K_SHARMA, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_SHARMA, k, j, i); } +} +/** + * P->U for just the GRMHD variables, but using the full tensor. Needed with floors and in a few places + */ +template +KOKKOS_INLINE_FUNCTION void prim_to_flux_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, + const EMHD::EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, const int dir, + const Global& flux, const VarMap& m_u, const Loci loc=Loci::center) +{ + const Real& gdet = G.gdet(loc, j, i); + // Particle number flux + flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; + + Real T[GR_DIM]; + calc_tensor(G, P, m_p, D, emhd_params, gam, k, j, i, dir, T); + flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); + flux(m_u.U1, k, j, i) = T[1] * gdet; + flux(m_u.U2, k, j, i) = T[2] * gdet; + flux(m_u.U3, k, j, i) = T[3] * gdet; } /** @@ -264,9 +262,8 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Local& P, const const Local& U, const VarMap& m_u, const Loci& loc=Loci::center) { FourVectors Dtmp; - GRMHD::calc_4vecs(G, P, m_p, j, i, loc, Dtmp); // TODO switch GRHD/GRMHD? + GRMHD::calc_4vecs(G, P, m_p, j, i, loc, Dtmp); prim_to_flux(G, P, m_p, Dtmp, emhd_params, gam, j, i, 0, U, m_u, loc); - // printf("%d %d %6.5e %6.5e\n", i, j, P(m_p.Q), P(m_p.DP)); } template @@ -280,6 +277,17 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Global& P, cons prim_to_flux(G, P, m_p, Dtmp, emhd_params, gam, k, j, i, 0, U, m_u, loc); } +template +KOKKOS_INLINE_FUNCTION void p_to_u_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, + const EMHD::EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, + const Global& U, const VarMap& m_u, const Loci& loc=Loci::center) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + prim_to_flux_mhd(G, P, m_p, Dtmp, emhd_params, gam, k, j, i, 0, U, m_u, loc); +} + /** * Calculate components of magnetosonic velocity from primitive variables * This is only called in GetFlux, so we only provide a ScratchPad form @@ -295,6 +303,10 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real cs2 = gam * (gam - 1) * P(m.UU) / ef; Real cms2; if (m.Q > 0) { + // Get the EGRMHD parameters + Real tau, chi_e, nu_e; + EMHD::set_parameters(G, P, m, emhd_params, gam, j, i, tau, chi_e, nu_e); + // Find fast magnetosonic speed const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); const Real ee = bsq + ef; diff --git a/kharma/flux.hpp b/kharma/flux/get_flux.hpp similarity index 71% rename from kharma/flux.hpp rename to kharma/flux/get_flux.hpp index 04527bd4..b00f0558 100644 --- a/kharma/flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -31,75 +31,22 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#pragma once +#include "flux.hpp" -#include "decs.hpp" - -#include - -#include "debug.hpp" -#include "floors.hpp" -#include "flux_functions.hpp" -#include "pack.hpp" -#include "reconstruction.hpp" -#include "types.hpp" - -// Package functions -#include "emhd.hpp" -#include "grmhd_functions.hpp" -#include "b_flux_ct.hpp" -#include "b_cd.hpp" -#include "electrons.hpp" +#include "floors_functions.hpp" namespace Flux { -/** - * Calculate dU/dt from a set of fluxes. - * This combines Parthenon's "FluxDivergence" operation with the GRMHD source term - * It also allows adding an arbitrary "wind" source term for stability - * - * @param rc is the current stage's container - * @param dudt is the base container containing the global dUdt term - */ -TaskStatus ApplyFluxes(MeshData *md, MeshData *mdudt); /** - * Fill all conserved variables (U) from primitive variables (P), over the whole grid. - * Second declaration is for Parthenon's benefit, similar to e.g. - * declaring UtoP vs FillDerived in GRMHD package. - */ -TaskStatus PtoU(MeshBlockData *rc, IndexDomain domain=IndexDomain::interior); -// The task version is generally used in the MeshBlock/end portion of a step *after* the boundary sync. -// Therefore it defaults to the entire domain, incl. ghost zones. -inline TaskStatus PtoUTask(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire) { return PtoU(rc, domain); } - -// Fluxes a.k.a. "Approximate Riemann Solvers" -// More complex solvers require speed estimates not calculable completely from -// invariants, necessitating frame transformations and related madness. -// These have identical signatures, so that we could runtime relink w/variant like coordinate_embedding - -// Local Lax-Friedrichs flux (usual, more stable) -KOKKOS_INLINE_FUNCTION Real llf(const Real& fluxL, const Real& fluxR, const Real& cmax, - const Real& cmin, const Real& Ul, const Real& Ur) -{ - Real ctop = m::max(cmax, cmin); - return 0.5 * (fluxL + fluxR - ctop * (Ur - Ul)); -} -// Harten, Lax, van Leer, & Einfeldt flux (early problems but not extensively studied since) -KOKKOS_INLINE_FUNCTION Real hlle(const Real& fluxL, const Real& fluxR, const Real& cmax, - const Real& cmin, const Real& Ul, const Real& Ur) -{ - return (cmax*fluxL + cmin*fluxR - cmax*cmin*(Ur - Ul)) / (cmax + cmin); -} - -/** - * Reconstruct the values of primitive variables at left and right zone faces, - * find the corresponding conserved variables and their fluxes through the zone faces + * @brief Reconstruct the values of primitive variables at left and right of each zone face, + * find the corresponding conserved variables and their fluxes through the face * - * @param rc the current stage container, holding pointers to all variable data - * - * Memory-wise, this fills the "flux" portions of the "conserved" fields. All fluxes are applied - * together "ApplyFluxes," and the final fields are calculated by Parthenon in - * Also fills the "ctop" vector with the signal speed mhd_vchar -- used to estimate timestep later. + * @param md the current stage MeshData container, holding pointers to all variable data + * + * Memory-wise, this fills the "flux" portions of the "conserved" fields. These will be used + * over the course of the step to calculate an update to the zone-centered values. + * This function also fills the "ctop" vector with the signal speed mhd_vchar, + * used to estimate the timestep later. * * This function is defined in the header because it is templated on the reconstruction scheme and * direction. Since there are only a few reconstruction schemes supported, and we will only ever @@ -107,58 +54,55 @@ KOKKOS_INLINE_FUNCTION Real hlle(const Real& fluxL, const Real& fluxR, const Rea * This allows some extra optimization from knowing that dir != 0 in parcticular, and inlining * the particular reconstruction call we need. */ -template +template inline TaskStatus GetFlux(MeshData *md) { Flag(md, "Recon and flux"); // Pointers auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Exit on trivial operations const int ndim = pmesh->ndim; if (ndim < 3 && dir == X3DIR) return TaskStatus::complete; if (ndim < 2 && dir == X2DIR) return TaskStatus::complete; // Options - const auto& pars = pmb0->packages.Get("GRMHD")->AllParams(); - const auto& globals = pmb0->packages.Get("Globals")->AllParams(); - const auto& floor_pars = pmb0->packages.Get("Floors")->AllParams(); - const bool use_hlle = pars.Get("use_hlle"); - // Apply post-reconstruction floors. - // Only enabled for WENO since it is not TVD, and only when other - // floors are enabled. - const bool reconstruction_floors = (Recon == ReconstructionType::weno5) - && !floor_pars.Get("disable_floors"); - // Pull out a struct of just the actual floor values for speed - const Floors::Prescription floors(floor_pars); - // Check presence of different packages - const auto& pkgs = pmb0->packages.AllPackages(); - const bool use_b_flux_ct = pkgs.count("B_FluxCT"); - const bool use_b_cd = pkgs.count("B_CD"); - const bool use_electrons = pkgs.count("Electrons"); - const bool use_emhd = pkgs.count("EMHD"); - // Pull flag indicating primitive variables - const MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); - - const Real gam = pars.Get("gamma"); - const double ctop_max = (use_b_cd) ? globals.Get("ctop_max_last") : 0.0; + const auto& pars = pmb0->packages.Get("Driver")->AllParams(); + const auto& mhd_pars = pmb0->packages.Get("GRMHD")->AllParams(); + const auto& globals = pmb0->packages.Get("Globals")->AllParams(); + const bool use_hlle = pars.Get("use_hlle"); - EMHD::EMHD_parameters emhd_params_tmp; - if (use_emhd) { - const auto& emhd_pars = pmb0->packages.Get("EMHD")->AllParams(); - emhd_params_tmp = emhd_pars.Get("emhd_params"); + const bool reconstruction_floors = pmb0->packages.AllPackages().count("Floors") && + (Recon == KReconstruction::Type::weno5); + Floors::Prescription floors_temp; + if (reconstruction_floors) { + // Apply post-reconstruction floors. + // Only enabled for WENO since it is not TVD, and only when other + // floors are enabled. + const auto& floor_pars = pmb0->packages.Get("Floors")->AllParams(); + // Pull out a struct of just the actual floor values for speed + floors_temp = Floors::Prescription(floor_pars); } - const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; + const Floors::Prescription& floors = floors_temp; + + const Real gam = mhd_pars.Get("gamma"); + + // Check whether we're using constraint-damping + // (which requires that a variable be propagated at ctop_max) + const bool use_b_cd = pmb0->packages.AllPackages().count("B_CD"); + const double ctop_max = (use_b_cd) ? pmb0->packages.Get("B_CD")->Param("ctop_max_last") : 0.0; + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb0->packages); const Loci loc = loc_of(dir); // Pack variables. Keep ctop separate PackIndexMap prims_map, cons_map; - const auto& ctop = md->PackVariables(std::vector{"ctop"}); - const auto& P_all = md->PackVariables(std::vector{isPrimitive}, prims_map); + const auto& ctop = md->PackVariables(std::vector{"ctop"}); + const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); - Flag(md, "Packed variables"); + //Flag(md, "Packed variables"); // Get sizes const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); @@ -170,10 +114,10 @@ inline TaskStatus GetFlux(MeshData *md) // 1-zone halo in nontrivial dimensions // We leave is/ie, js/je, ks/ke with their usual definitions for consistency, and define // the loop bounds separately to include the appropriate halo - int halo = 1; - const IndexRange il = IndexRange{ib.s - halo, ib.e + halo}; - const IndexRange jl = (ndim > 1) ? IndexRange{jb.s - halo, jb.e + halo} : jb; - const IndexRange kl = (ndim > 2) ? IndexRange{kb.s - halo, kb.e + halo} : kb; + // TODO halo 2 "shouldn't" crash but does. Artifact of switch to faces? + const IndexRange il = IndexRange{ib.s - 1, ib.e + 1}; + const IndexRange jl = (ndim > 1) ? IndexRange{jb.s - 1, jb.e + 1} : jb; + const IndexRange kl = (ndim > 2) ? IndexRange{kb.s - 1, kb.e + 1} : kb; // Allocate scratch space const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM @@ -182,8 +126,8 @@ inline TaskStatus GetFlux(MeshData *md) // Allocate enough to cache prims, conserved, and fluxes, for left and right faces, // plus temporaries inside reconstruction (most use 1, WENO5 uses none, linear_vl uses a bunch) // Then add cmax and cmin! - const size_t total_scratch_bytes = (6 + 1*(Recon != ReconstructionType::weno5) + - 4*(Recon == ReconstructionType::linear_vl)) * var_size_in_bytes + const size_t total_scratch_bytes = (6 + 1*(Recon != KReconstruction::Type::weno5) + + 4*(Recon == KReconstruction::Type::linear_vl)) * var_size_in_bytes + 2 * speed_size_in_bytes; Flag(md, "Flux kernel"); @@ -337,4 +281,5 @@ inline TaskStatus GetFlux(MeshData *md) Flag(md, "Finished recon and flux"); return TaskStatus::complete; } -} + +} // Flux diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index b36430e8..ac3ebc1e 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -34,27 +34,21 @@ #include "grmhd.hpp" -#include +#include "decs.hpp" -// Until Parthenon gets a reduce() +// TODO eliminate when Parthenon gets reduction types #include "Kokkos_Core.hpp" -#include - -#include "decs.hpp" - #include "boundaries.hpp" #include "current.hpp" #include "debug.hpp" #include "floors.hpp" #include "flux.hpp" #include "gr_coordinates.hpp" -#include "grmhd.hpp" -#include "kharma.hpp" #include "grmhd_functions.hpp" -#include "U_to_P.hpp" +#include "kharma.hpp" -using namespace parthenon; +#include /** @@ -63,24 +57,21 @@ using namespace parthenon; namespace GRMHD { -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages) +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { - // This function builds and returns a "StateDescriptor" or "Package" object. + Flag("Initializing GRMHD"); + // This function builds and returns a "KHARMAPackage" object, which is a light + // superset of Parthenon's "StateDescriptor" class for packages. // The most important part of this object is a member of type "Params", // which acts more or less like a Python dictionary: // it puts values into a map of names->objects, where "objects" are usually // floats, strings, and ints, but can be arbitrary classes. - // This "dictionary" is *not* totally immutable, but should be treated - // as such in every package except "Globals". - auto pkg = std::make_shared("GRMHD"); + // This "dictionary" is mostly immutable, and should always be treated as immutable, + // except in the "Globals" package. + auto pkg = std::make_shared("GRMHD"); Params ¶ms = pkg->AllParams(); - // =================================== PARAMETERS =================================== - - // Add the problem name, so we can be C++ noobs and special-case on string contents - std::string problem_name = pin->GetString("parthenon/job", "problem_id"); - params.Add("problem", problem_name); - + // GRMHD PARAMETERS // Fluid gamma for ideal EOS. Don't guess this. // Only ideal EOS are supported, though modifying gamma based on // local temperatures would be straightforward. @@ -91,15 +82,7 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack double cfl = pin->GetOrAddReal("GRMHD", "cfl", 0.9); params.Add("cfl", cfl); - // Don't even error on this. LLF or bust, baby - // TODO move this and recon options out of GRMHD package! - std::string flux = pin->GetOrAddString("GRMHD", "flux", "llf"); - if (flux == "hlle") { - params.Add("use_hlle", true); - } else { - params.Add("use_hlle", false); - } - + // TIME PARAMETERS // These parameters are put in "parthenon/time" to match others, but ultimately we should // override the parthenon timestep chooser // Minimum timestep, if something about the sound speed goes wonky. Probably won't save you :) @@ -121,62 +104,25 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack bool use_dt_light_phase_speed = pin->GetOrAddBoolean("parthenon/time", "use_dt_light_phase_speed", false); params.Add("use_dt_light_phase_speed", use_dt_light_phase_speed); - // Reconstruction scheme: plm, weno5, ppm... - std::string recon = pin->GetOrAddString("GRMHD", "reconstruction", "weno5"); - if (recon == "donor_cell") { - params.Add("recon", ReconstructionType::donor_cell); - } else if (recon == "linear_vl") { - params.Add("recon", ReconstructionType::linear_vl); - } else if (recon == "linear_mc") { - params.Add("recon", ReconstructionType::linear_mc); - } else if (recon == "weno5") { - params.Add("recon", ReconstructionType::weno5); - // } else if (recon == "weno5_lower_poles") { - // params.Add("recon", ReconstructionType::weno5_lower_poles); - } else { - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; - throw std::invalid_argument("Unsupported reconstruction algorithm!"); - } - - // Diagnostic data - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); - int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); - params.Add("flag_verbose", flag_verbose); - int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); - params.Add("extra_checks", extra_checks); - - // Option to disable checking the fluxes at boundaries: - // Prevent inflow at outer boundaries - bool check_inflow_inner = pin->GetOrAddBoolean("bounds", "check_inflow_inner", true); - params.Add("check_inflow_inner", check_inflow_inner); - bool check_inflow_outer = pin->GetOrAddBoolean("bounds", "check_inflow_outer", true); - params.Add("check_inflow_outer", check_inflow_outer); - // Ensure fluxes through the zero-size face at the pole are zero - bool fix_flux_pole = pin->GetOrAddBoolean("bounds", "fix_flux_pole", true); - params.Add("fix_flux_pole", fix_flux_pole); - // Ensure fluxes through the zero-size face at the x1 boundary are zero - bool fix_flux_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_x1", false); - params.Add("fix_flux_x1", fix_flux_x1); - - // Driver options - // The two current drivers are "harm" or "imex", with the former being the usual KHARMA - // driver, and the latter supporting implicit stepping of some or all variables - auto driver_type = pin->GetString("driver", "type"); // This is set in kharma.cpp - params.Add("driver_type", driver_type); + // IMPLICIT PARAMETERS // The ImEx driver is necessary to evolve implicitly, but doesn't require it. Using explicit // updates for GRMHD vars is useful for testing, or if adding just a couple of implicit variables // Doing EGRMHD requires implicit evolution of GRMHD variables, of course - auto implicit_grmhd = (driver_type == "imex") && + auto& driver = packages->Get("Driver")->AllParams(); + auto implicit_grmhd = (driver.Get("type") == "imex") && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); params.Add("implicit", implicit_grmhd); - // Synchronize boundary variables twice. Ensures KHARMA is agnostic to the breakdown - // of meshblocks, at the cost of twice the MPI overhead, for potentially much worse strong scaling. - bool two_sync = pin->GetOrAddBoolean("perf", "two_sync", false) || - pin->GetOrAddBoolean("driver", "two_sync", false); - params.Add("two_sync", two_sync); + // Update variable numbers + if (implicit_grmhd) { + int n_current = driver.Get("n_implicit_vars"); + driver.Update("n_implicit_vars", n_current+5); + } else { + int n_current = driver.Get("n_explicit_vars"); + driver.Update("n_explicit_vars", n_current+5); + } + + // AMR PARAMETERS // Adaptive mesh refinement options // Only active if "refinement" and "numlevel" parameters allow Real refine_tol = pin->GetOrAddReal("GRMHD", "refine_tol", 0.5); @@ -192,53 +138,41 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack // closely-related size (for "Face" and "Edge" fields) // Add flags to distinguish groups of fields. - // This is stretching what the "Params" object should really be carrying, - // but the flag values are necessary in many places, and this was the - // easiest way to ensure availability. // 1. One flag to mark the primitive variables specifically // (Parthenon has Metadata::Conserved already) - MetadataFlag isPrimitive = Metadata::AllocateNewFlag("Primitive"); - params.Add("PrimitiveFlag", isPrimitive); + Metadata::AddUserFlag("Primitive"); // 2. And one for hydrodynamics (everything we directly handle in this package) - MetadataFlag isHD = Metadata::AllocateNewFlag("HD"); - params.Add("HDFlag", isHD); + Metadata::AddUserFlag("HD"); // 3. And one for magnetohydrodynamics // (all HD fields plus B field, which we'll need to make use of) - MetadataFlag isMHD = Metadata::AllocateNewFlag("MHD"); - params.Add("MHDFlag", isMHD); - - std::vector flags_prim, flags_cons; - if (driver_type == "harm") { // Normal operation + Metadata::AddUserFlag("MHD"); + // Mark whether to evolve our variables via the explicit or implicit step inside the driver + MetadataFlag areWeImplicit = (implicit_grmhd) ? Metadata::GetUserFlag("Implicit") + : Metadata::GetUserFlag("Explicit"); + + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, + Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, + Metadata::WithFluxes, Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + + bool sync_prims = packages->Get("Driver")->Param("sync_prims"); + if (!sync_prims) { // Normal operation // As mentioned elsewhere, KHARMA treats the conserved variables as the independent ones, // and the primitives as "Derived" // Primitives are still used for reconstruction, physical boundaries, and output, and are // generally the easier to understand quantities - // Note especially their ghost zones are also filled! This is less efficient than syncing just - // one or the other, but allows the most flexibility for reasons that should be clearer in harm_driver.cpp - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::FillGhost, Metadata::Restart, - isPrimitive, isHD, isMHD}); - // Conserved variables are actually rho*u^0 & T^0_mu, but are named after the prims for consistency - // We will rarely need the conserved variables by name, we will mostly be treating them as a group - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, - Metadata::WithFluxes, Metadata::FillGhost, Metadata::Restart, - Metadata::Conserved, isHD, isMHD}); - } else if (driver_type == "imex") { // ImEx driver - // When evolving (E)GRMHD implicitly, we instead mark the primitive variables to be synchronized. + // TODO can we not sync prims if we're using two_sync? + flags_cons.push_back(Metadata::FillGhost); + flags_prim.push_back(Metadata::FillGhost); + } else { // Treat primitive vars as fundamental + // When evolving (E)GRMHD implicitly, we just mark the primitive variables to be synchronized. // This won't work for AMR, but it fits much better with the implicit solver, which expects // primitive variable inputs and produces primitive variable results. - - // Mark whether to evolve our variables via the explicit or implicit step inside the driver - MetadataFlag areWeImplicit = (implicit_grmhd) ? packages.Get("Implicit")->Param("ImplicitFlag") - : packages.Get("Implicit")->Param("ExplicitFlag"); - - flags_prim = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, - Metadata::FillGhost, Metadata::Restart, isPrimitive, isHD, isMHD}); - flags_cons = std::vector({Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, - Metadata::WithFluxes, Metadata::Conserved, isHD, isMHD}); + flags_prim.push_back(Metadata::FillGhost); } // With the flags sorted & explained, actually declaring fields is easy. + // These will be initialized & cleaned automatically for each meshblock auto m = Metadata(flags_prim); pkg->AddField("prims.rho", m); pkg->AddField("prims.u", m); @@ -257,91 +191,31 @@ std::shared_ptr Initialize(ParameterInput *pin, Packages_t pack m = Metadata(flags_cons_vec, s_vector); pkg->AddField("cons.uvec", m); - // No magnetic fields here. KHARMA should operate fine in GRHD without them, - // so they are allocated only by B field packages. - // Maximum signal speed (magnitude). // Needs to be cached from flux updates for calculating the timestep later m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); pkg->AddField("ctop", m); - // Flag denoting UtoP inversion failures - // Only needed if we're actually calling UtoP, but always allocated as it's retrieved often - // Needs boundary sync if treating primitive variables as fundamental - if (driver_type == "imex" && !implicit_grmhd) { - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); - } else { - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - } - pkg->AddField("pflag", m); + // No magnetic fields here. KHARMA should operate fine in GRHD without them, + // so they are allocated only by B field packages. - if (!implicit_grmhd) { - // If we're using a step that requires calling UtoP, register it - // Calling this messes up implicit stepping, so we only register it here - pkg->FillDerivedBlock = GRMHD::FillDerivedBlock; - } + // A KHARMAPackage also contains quite a few "callbacks," or functions called at + // specific points in a step if the package is loaded. + // Generally, see the headers for function descriptions. + + //pkg->BlockUtoP // Taken care of by the inverter package since it's hard to do + // There's no "Flux" package, so we register the geometric (\Gamma*T) source here. I think it makes sense. + pkg->AddSource = Flux::AddGeoSource; - // Finally, the StateDescriptor/Package object determines the Callbacks Parthenon makes to - // a particular package -- that is, some portion of the things that the package needs done - // at each step, which must be done at specific times. - // See the header files defining each of these functions for their purpose and call context. - pkg->CheckRefinementBlock = GRMHD::CheckRefinement; - pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; + // Parthenon general callbacks + pkg->CheckRefinementBlock = GRMHD::CheckRefinement; + pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; pkg->PostStepDiagnosticsMesh = GRMHD::PostStepDiagnostics; - return pkg; -} + // TODO TODO Reductions -void UtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - Flag(rc, "Filling Primitives"); - auto pmb = rc->GetBlockPointer(); - const auto& G = pmb->coords; - - PackIndexMap prims_map, cons_map; - auto U = GRMHD::PackMHDCons(rc, cons_map); - auto P = GRMHD::PackHDPrims(rc, prims_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - - GridScalar pflag = rc->Get("pflag").data; - - // KHARMA uses only one boundary exchange, in the conserved variables - // Except where FixUtoP has no neighbors, and must fix with bad zones, this is fully identical - // between #s of MPI ranks, because we sync 4 ghost zones and only require 3 for reconstruction. - // Thus as long as the last rank is not flagged, it will be inverted the same way on each process, and - // used in the same way for fixups. If it fails & thus might be different, it is ignored. - - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - // Get the primitives from our conserved versions - // Currently this returns *all* zones, including all ghosts, even - // uninitialized zones which are still zero. We select for initialized - // zones only in the loop below, to avoid failures to converge while - // calculating primtive vars over as much of the domain as possible - // We could (did formerly) save some time here by running over - // only zones with initialized conserved variables, but the domain - // of such values is not rectangular in the current handling - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const IndexRange ib = bounds.GetBoundsI(domain); - const IndexRange jb = bounds.GetBoundsJ(domain); - const IndexRange kb = bounds.GetBoundsK(domain); - const IndexRange ib_b = bounds.GetBoundsI(IndexDomain::interior); - const IndexRange jb_b = bounds.GetBoundsJ(IndexDomain::interior); - const IndexRange kb_b = bounds.GetBoundsK(IndexDomain::interior); - - pmb->par_for("U_to_P", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - if (inside(k, j, i, kb_b, jb_b, ib_b) || - m::abs(P(m_p.RHO, k, j, i)) > SMALL || m::abs(P(m_p.UU, k, j, i)) > SMALL) { - // Run over all interior zones and any initialized ghosts - pflag(k, j, i) = GRMHD::u_to_p(G, U, m_u, gam, k, j, i, Loci::center, P, m_p); - } else { - // Don't *use* un-initialized zones for fixes, but also don't *fix* them - pflag(k, j, i) = -1; - } - } - ); - Flag(rc, "Filled"); + Flag("Initialized"); + return pkg; } Real EstimateTimestep(MeshBlockData *rc) @@ -355,7 +229,7 @@ Real EstimateTimestep(MeshBlockData *rc) auto& ctop = rc->Get("ctop").data; // TODO: move timestep limiter into an override of SetGlobalTimestep - // TODO: move diagnostic printing to PostStepDiagnostics, now it's broken here + // TODO: keep location of the max, or be able to look it up in diagnostics auto& globals = pmb->packages.Get("Globals")->AllParams(); const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); @@ -391,11 +265,11 @@ Real EstimateTimestep(MeshBlockData *rc) pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int k, const int j, const int i, typename Kokkos::MinMax::value_type &lminmax) { - double ndt_zone = 1 / (1 / (G.dx1v(i) / ctop(0, k, j, i)) + - 1 / (G.dx2v(j) / ctop(1, k, j, i)) + - 1 / (G.dx3v(k) / ctop(2, k, j, i))); + double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / ctop(0, k, j, i)) + + 1 / (G.Dxc<2>(j) / ctop(1, k, j, i)) + + 1 / (G.Dxc<3>(k) / ctop(2, k, j, i))); // Effective "max speed" used for the timestep - double ctop_max_zone = m::min(G.dx1v(i), m::min(G.dx2v(j), G.dx3v(k))) / ndt_zone; + double ctop_max_zone = m::min(G.Dxc<1>(i), m::min(G.Dxc<2>(j), G.Dxc<3>(k))) / ndt_zone; if (!m::isnan(ndt_zone) && (ndt_zone < lminmax.min_val)) lminmax.min_val = ndt_zone; @@ -415,8 +289,11 @@ Real EstimateTimestep(MeshBlockData *rc) const double ndt = clip(min_ndt * cfl, dt_min, dt_max); // Record max ctop, for constraint damping - if (nctop > globals.Get("ctop_max")) { - globals.Update("ctop_max", nctop); + // TODO could probably use generic Max inside B_CD package + if (pmb->packages.AllPackages().count("B_CD")) { + auto& b_cd_params = pmb->packages.Get("B_CD")->AllParams(); + if (nctop > b_cd_params.Get("ctop_max")) + b_cd_params.Update("ctop_max", nctop); } Flag(rc, "Estimated"); @@ -435,13 +312,13 @@ Real EstimateRadiativeTimestep(MeshBlockData *rc) const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); const bool phase_speed = grmhd_pars.Get("use_dt_light_phase_speed"); - const Real dx[GR_DIM] = {0., G.dx1v(0), G.dx2v(0), G.dx3v(0)}; + const Real dx[GR_DIM] = {0., G.Dxc<1>(0), G.Dxc<2>(0), G.Dxc<3>(0)}; // Leaving minmax in case the max phase speed is useful typename Kokkos::MinMax::value_type minmax; pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA(const int k, const int j, const int i, - typename Kokkos::MinMax::value_type &lminmax) { + KOKKOS_LAMBDA(const int& k, const int& j, const int& i, + typename Kokkos::MinMax::value_type& lminmax) { double light_phase_speed = SMALL; double dt_light_local = 0.; @@ -511,7 +388,7 @@ AmrTag CheckRefinement(MeshBlockData *rc) , Kokkos::MinMax(minmax)); auto pkg = pmb->packages.Get("GRMHD"); - const auto &refine_tol = pkg->Param("refine_tol"); + const auto &refine_tol = pkg->Param("refine_tol"); const auto &derefine_tol = pkg->Param("derefine_tol"); if (minmax.max_val - minmax.min_val > refine_tol) return AmrTag::refine; @@ -525,21 +402,13 @@ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options - const auto& pars = pmesh->packages.Get("GRMHD")->AllParams(); - const int flag_verbose = pars.Get("flag_verbose"); + const auto& pars = pmesh->packages.Get("Globals")->AllParams(); const int extra_checks = pars.Get("extra_checks"); - - // Debugging/diagnostic info about floor and inversion flags - if (flag_verbose >= 1) { - Flag("Printing flags"); - CountPFlags(md, IndexDomain::interior, flag_verbose); - CountFFlags(md, IndexDomain::interior, flag_verbose); - } + Flag("Got pointers"); // Check for a soundspeed (ctop) of 0 or NaN // This functions as a "last resort" check to stop a // simulation on obviously bad data - // TODO also be able to print what zone dictated timestep if (extra_checks >= 1) { CheckNaN(md, X1DIR); if (pmesh->ndim > 1) CheckNaN(md, X2DIR); diff --git a/kharma/grmhd/grmhd.hpp b/kharma/grmhd/grmhd.hpp index 2b7e3e38..51736a2f 100644 --- a/kharma/grmhd/grmhd.hpp +++ b/kharma/grmhd/grmhd.hpp @@ -33,62 +33,19 @@ */ #pragma once -#include - -#include - -using namespace parthenon; +#include "decs.hpp" +#include "types.hpp" /** * This physics package implements General-Relativistic Magnetohydrodynamics * * Anything specific to GRMHD (but not relating to the particular *order* of operations) * is implemented in this namespace, in the files grmhd.cpp, source.cpp, and fixup.cpp. - * - * + * Many device-side functions related to GRMHD are implemented in grmhd_functions.hpp */ namespace GRMHD { -// For declaring meshes, as well as the full intermediates we need (right & left fluxes etc) -std::shared_ptr Initialize(ParameterInput *pin, Packages_t packages); - -/** - * Get the primitive variables - * This just computes P, and only for the fluid varaibles. - * Other packages must convert P->U by registering their version as "FillDerived" - * - * Defaults to entire domain, as the KHARMA algorithm relies on applying UtoP over ghost zones. - * - * input: U, whatever form - * output: U and P match down to inversion errors - */ -// void UtoP(MeshData *md, IndexDomain domain=IndexDomain::entire, bool coarse=false); -// inline void FillDerivedMesh(MeshData *md) { UtoP(md); } -void UtoP(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); -inline void FillDerivedBlock(MeshBlockData *rc) { UtoP(rc); } -inline TaskStatus FillDerivedBlockTask(MeshBlockData *rc) { UtoP(rc); return TaskStatus::complete; } - -/** - * Smooth over inversion failures by averaging values from each neighboring zone - * a.k.a. Diffusion? What diffusion? There is no diffusion here. - * - * LOCKSTEP: this function expects and should preserve P<->U - */ -TaskStatus FixUtoP(MeshBlockData *rc); -/** - * Fix the primitive variables - * Applies floors to the calculated primitives, and fixes up any failed inversions - * - * input: U & P, "matching" - * output: U and P match with inversion errors corrected, and obey floors - */ -void PostUtoP(MeshBlockData *rc); - -/** - * Function to apply the GRMHD source term over the entire grid. - * - * Note Flux::ApplyFluxes = parthenon::FluxDivergence + GRMHD::AddSource - */ -TaskStatus AddSource(MeshData *md, MeshData *mdudt); +// For declaring variables, as well as the full intermediates we need (right & left fluxes etc) +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Returns the minimum CFL timestep among all zones in the block, diff --git a/kharma/grmhd/grmhd_functions.hpp b/kharma/grmhd/grmhd_functions.hpp index 3aef0f2f..f62027e6 100644 --- a/kharma/grmhd/grmhd_functions.hpp +++ b/kharma/grmhd/grmhd_functions.hpp @@ -118,7 +118,7 @@ KOKKOS_INLINE_FUNCTION Real lorentz_calc(const GRCoordinates& G, const VariableP return m::sqrt(1. + qsq); } -template +template KOKKOS_INLINE_FUNCTION Real lorentz_calc(const GRCoordinates& G, const Local& P, const VarMap& m, const int& j, const int& i, const Loci& loc=Loci::center) { @@ -176,7 +176,7 @@ KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Real uvec[N // This fn is guaranteed to have B values D.bcon[0] = 0; - VLOOP D.bcon[0] += B_P[v] * D.ucov[v+1]; + VLOOP D.bcon[0] += B_P[v] * D.ucov[v+1]; VLOOP D.bcon[v+1] = (B_P[v] + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; G.lower(D.bcon, D.bcov, k, j, i, loc); @@ -201,46 +201,45 @@ KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const GridVector G.lower(D.bcon, D.bcov, k, j, i, loc); } // Primitive/VarMap versions of calc_4vecs for kernels that use "packed" primitives -template -KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Local& P, const VarMap& m, - const int& j, const int& i, const Loci loc, FourVectors& D) +KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const VariablePack& P, const VarMap& m, + const int& k, const int& j, const int& i, const Loci loc, FourVectors& D) { - const Real gamma = lorentz_calc(G, P, m, j, i, loc); + const Real gamma = lorentz_calc(G, P, m, k, j, i, loc); const Real alpha = 1. / m::sqrt(-G.gcon(loc, j, i, 0, 0)); D.ucon[0] = gamma / alpha; - VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); + VLOOP D.ucon[v+1] = P(m.U1 + v, k, j, i) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); - G.lower(D.ucon, D.ucov, 0, j, i, loc); + G.lower(D.ucon, D.ucov, k, j, i, loc); if (m.B1 >= 0) { D.bcon[0] = 0; - VLOOP D.bcon[0] += P(m.B1 + v) * D.ucov[v+1]; - VLOOP D.bcon[v+1] = (P(m.B1 + v) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; + VLOOP D.bcon[0] += P(m.B1 + v, k, j, i) * D.ucov[v+1]; + VLOOP D.bcon[v+1] = (P(m.B1 + v, k, j, i) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; - G.lower(D.bcon, D.bcov, 0, j, i, loc); + G.lower(D.bcon, D.bcov, k, j, i, loc); } else { DLOOP1 D.bcon[mu] = D.bcov[mu] = 0.; } } -template -KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Global& P, const VarMap& m, - const int& k, const int& j, const int& i, const Loci loc, FourVectors& D) +template +KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Local& P, const VarMap& m, + const int& j, const int& i, const Loci loc, FourVectors& D) { - const Real gamma = lorentz_calc(G, P, m, k, j, i, loc); + const Real gamma = lorentz_calc(G, P, m, j, i, loc); const Real alpha = 1. / m::sqrt(-G.gcon(loc, j, i, 0, 0)); D.ucon[0] = gamma / alpha; - VLOOP D.ucon[v+1] = P(m.U1 + v, k, j, i) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); + VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); - G.lower(D.ucon, D.ucov, k, j, i, loc); + G.lower(D.ucon, D.ucov, 0, j, i, loc); if (m.B1 >= 0) { D.bcon[0] = 0; - VLOOP D.bcon[0] += P(m.B1 + v, k, j, i) * D.ucov[v+1]; - VLOOP D.bcon[v+1] = (P(m.B1 + v, k, j, i) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; + VLOOP D.bcon[0] += P(m.B1 + v) * D.ucov[v+1]; + VLOOP D.bcon[v+1] = (P(m.B1 + v) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; - G.lower(D.bcon, D.bcov, k, j, i, loc); + G.lower(D.bcon, D.bcov, 0, j, i, loc); } else { DLOOP1 D.bcon[mu] = D.bcov[mu] = 0.; } diff --git a/kharma/grmhd/grmhd_reductions.hpp b/kharma/grmhd/grmhd_reductions.hpp new file mode 100644 index 00000000..169a06d2 --- /dev/null +++ b/kharma/grmhd/grmhd_reductions.hpp @@ -0,0 +1,134 @@ +/* + * File: grmhd_functions.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include "grmhd_functions.hpp" +#include "reductions.hpp" + +// GRMHD REDUCTIONS +// Each of these has an identical macro-defined argument list, designed +// to be used in the routines in reductions.cpp. +// You're free to use them elsewhere though, you do you + +namespace GRMHD { + +// Accretion rates: return a zone's contribution to the surface integral +// forming each rate measurement. +KOKKOS_INLINE_FUNCTION Real mdot(REDUCE_FUNCTION_ARGS_EH) +{ + Real ucon[GR_DIM]; + GRMHD::calc_ucon(G, P, m_p, j, i, Loci::center, ucon); + // \dot{M} == \int rho * u^1 * gdet * dx2 * dx3 + return -P(m_p.RHO, k, j, i) * ucon[X1DIR] * G.gdet(Loci::center, j, i); +} +KOKKOS_INLINE_FUNCTION Real edot(REDUCE_FUNCTION_ARGS_EH) +{ + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + // \dot{E} == \int - T^1_0 * gdet * dx2 * dx3 + return -T1[X0DIR] * G.gdet(Loci::center, j, i); +} +KOKKOS_INLINE_FUNCTION Real ldot(REDUCE_FUNCTION_ARGS_EH) +{ + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + // \dot{L} == \int T^1_3 * gdet * dx2 * dx3 + return T1[X3DIR] * G.gdet(Loci::center, j, i); +} + +// Then we can define the same with fluxes. +KOKKOS_INLINE_FUNCTION Real mdot_flux(REDUCE_FUNCTION_ARGS_EH) +{ + return -U.flux(X1DIR, m_u.RHO, k, j, i); +} +KOKKOS_INLINE_FUNCTION Real edot_flux(REDUCE_FUNCTION_ARGS_EH) +{ + return (U.flux(X1DIR, m_u.UU, k, j, i) - U.flux(X1DIR, m_u.RHO, k, j, i)); +} +KOKKOS_INLINE_FUNCTION Real ldot_flux(REDUCE_FUNCTION_ARGS_EH) +{ + return U.flux(X1DIR, m_u.U3, k, j, i); +} + +// Luminosity proxy from (for example) Porth et al 2019. +// Notice that this will be totaled for *all zones*, +// but one could define a variable which checks sigma, G.coord_embed(), etc +KOKKOS_INLINE_FUNCTION Real eht_lum(REDUCE_FUNCTION_ARGS_MESH) +{ + // Within radius... + GReal X[GR_DIM]; + G.coord_embed(k, j, i, Loci::face1, X); + if (X[1] > arg) { // If we are *outside* given radius + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, Dtmp); + Real rho = P(m_p.RHO, b, k, j, i); + Real Pg = (gam - 1.) * P(b, m_p.UU, k, j, i); + Real Bmag = m::sqrt(dot(Dtmp.bcon, Dtmp.bcov)); + Real j_eht = rho*rho*rho/Pg/Pg * m::exp(-0.2 * m::pow(rho * rho / (Bmag * Pg * Pg), 1./3.)); + return j_eht; + } else { + return 0.; + } +} + +// Example of checking extra conditions before adding local results: +// sums total jet power only at exactly r=radius, for areas with sig > 1 +// TODO version w/E&M power only. Needs "calc_tensor_EM" +KOKKOS_INLINE_FUNCTION Real jet_lum(REDUCE_FUNCTION_ARGS_MESH) +{ + // At r = radius, i.e. if our faces span acreoss it... + GReal X_f[GR_DIM]; GReal X_b[GR_DIM]; + G.coord_embed(k, j, i, Loci::face1, X_b); + G.coord_embed(k, j, i+1, Loci::face1, X_f); + if (X_f[1] > arg && X_b[1] < arg) { // If we are *at* given radius + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(G, P(b), m_p, Dtmp, gam, k, j, i, X1DIR, T1); + // If sigma > 1... + if ((dot(Dtmp.bcon, Dtmp.bcov) / P(b, m_p.RHO, k, j, i)) > 1.) { + // Energy flux, like at EH. 2D integral jacobian, so we have to take X1 off of auto-applied dV + return -T1[X0DIR] / G.Dxc<1>(i); + } + } + return 0.; +} + +} \ No newline at end of file diff --git a/kharma/grmhd/pack.hpp b/kharma/grmhd/pack.hpp index 106252b1..dac61a3d 100644 --- a/kharma/grmhd/pack.hpp +++ b/kharma/grmhd/pack.hpp @@ -50,45 +50,29 @@ namespace GRMHD { */ inline VariablePack PackMHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - auto pmb = rc->GetBlockPointer(); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isMHD = pmb->packages.Get("GRMHD")->Param("MHDFlag"); - return rc->PackVariables({isPrimitive, isMHD}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); } inline MeshBlockPack> PackMHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - auto pmb = md->GetBlockData(0)->GetBlockPointer(); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isMHD = pmb->packages.Get("GRMHD")->Param("MHDFlag"); - return md->PackVariables(std::vector{isPrimitive, isMHD}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); } inline VariablePack PackMHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { - auto pmb = rc->GetBlockPointer(); - MetadataFlag isMHD = pmb->packages.Get("GRMHD")->Param("MHDFlag"); - return rc->PackVariables({Metadata::Conserved, isMHD}, cons_map, coarse); + return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("MHD")}, cons_map, coarse); } inline MeshBlockPack> PackMHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - auto pmb = md->GetBlockData(0)->GetBlockPointer(); - MetadataFlag isMHD = pmb->packages.Get("GRMHD")->Param("MHDFlag"); - return md->PackVariables(std::vector{Metadata::Conserved, isMHD}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("MHD")}, cons_map, coarse); } inline VariablePack PackHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - auto pmb = rc->GetBlockPointer(); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isHD = pmb->packages.Get("GRMHD")->Param("HDFlag"); - return rc->PackVariables({isPrimitive, isHD}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); } inline MeshBlockPack> PackHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - auto pmb = md->GetBlockData(0)->GetBlockPointer(); - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isHD = pmb->packages.Get("GRMHD")->Param("HDFlag"); - return md->PackVariables(std::vector{isPrimitive, isHD}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); } // Version without template @@ -97,14 +81,11 @@ inline VariablePack PackHDPrims(T data) { PackIndexMap nop; return PackHDP inline VariablePack PackHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { auto pmb = rc->GetBlockPointer(); - MetadataFlag isHD = pmb->packages.Get("GRMHD")->Param("HDFlag"); - return rc->PackVariables({Metadata::Conserved, isHD}, cons_map, coarse); + return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("HD")}, cons_map, coarse); } inline MeshBlockPack> PackHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - auto pmb = md->GetBlockData(0)->GetBlockPointer(); - MetadataFlag isHD = pmb->packages.Get("GRMHD")->Param("HDFlag"); - return md->PackVariables(std::vector{Metadata::Conserved, isHD}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("HD")}, cons_map, coarse); } diff --git a/kharma/grmhd/source.cpp b/kharma/grmhd/source.cpp deleted file mode 100644 index a872f173..00000000 --- a/kharma/grmhd/source.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * File: source.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "grmhd.hpp" - -#include "grmhd_functions.hpp" -#include "pack.hpp" -#include "types.hpp" - -TaskStatus GRMHD::AddSource(MeshData *md, MeshData *mdudt) -{ - Flag(mdudt, "Adding GRMHD source"); - // Pointers - auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - // Options - const Real gam = pmb0->packages.Get("GRMHD")->Param("gamma"); - - // Pack variables - PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(md, prims_map); - auto dUdt = GRMHD::PackMHDCons(mdudt, cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - // Get sizes - IndexDomain domain = IndexDomain::interior; - auto ib = md->GetBoundsI(domain); - auto jb = md->GetBoundsJ(domain); - auto kb = md->GetBoundsK(domain); - auto block = IndexRange{0, P.GetDim(5)-1}; - - pmb0->par_for("grmhd_source", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { - const auto& G = dUdt.GetCoords(b); - FourVectors D; - GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, D); - // Get stuff we don't want to recalculate every loop iteration - // This is basically a manual version of GRMHD::calc_tensor but saves recalculating e.g. dot(bcon, bcov) 4 times - Real pgas = (gam - 1) * P(b, m_p.UU, k, j, i); - Real bsq = dot(D.bcon, D.bcov); - Real eta = pgas + P(b, m_p.RHO, k, j, i) + P(b, m_p.UU, k, j, i) + bsq; - Real ptot = pgas + 0.5 * bsq; - - // Contract mhd stress tensor with connection, and multiply by metric determinant - Real new_du[GR_DIM] = {0}; - DLOOP2 { - Real Tmunu = (eta * D.ucon[mu] * D.ucov[nu] + - ptot * (mu == nu) - - D.bcon[mu] * D.bcov[nu]); - - for (int lam = 0; lam < GR_DIM; ++lam) { - new_du[lam] += Tmunu * G.gdet_conn(j, i, nu, lam, mu); - } - } - - dUdt(b, m_u.UU, k, j, i) += new_du[0]; - VLOOP dUdt(b, m_u.U1 + v, k, j, i) += new_du[1 + v]; - } - ); - - Flag(mdudt, "Added"); - return TaskStatus::complete; -} diff --git a/kharma/harm_driver.cpp b/kharma/harm_driver.cpp deleted file mode 100644 index 5a16c81c..00000000 --- a/kharma/harm_driver.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - * File: harm_driver.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "harm_driver.hpp" - -#include - -#include -#include -#include - -#include "decs.hpp" - -#include "b_flux_ct.hpp" -#include "b_cd.hpp" -#include "electrons.hpp" -#include "grmhd.hpp" -#include "wind.hpp" - -#include "boundaries.hpp" -#include "debug.hpp" -#include "flux.hpp" -#include "resize_restart.hpp" - -TaskCollection HARMDriver::MakeTaskCollection(BlockList_t &blocks, int stage) -{ - // Reminder that NOTHING YOU CALL HERE WILL GET CALLED EVERY STEP - // this function is run *once*, and returns a list of what should be done every step. - // No prints or direct function calls here will do what you want, only calls to tl.AddTask() - - // TaskCollections are split into regions, each of which can be tackled by a specified number of independent threads. - // We take most of the splitting logic here from the advection example in Parthenon, - // except that we calculate the fluxes in a Mesh-wide section rather than for MeshBlocks independently - TaskCollection tc; - TaskID t_none(0); - - Real beta = integrator->beta[stage - 1]; - const Real dt = integrator->dt; - auto stage_name = integrator->stage_name; - - // Which packages we load affects which tasks we'll add to the list - auto& pkgs = blocks[0]->packages.AllPackages(); - bool use_b_cd = pkgs.count("B_CD"); - bool use_b_flux_ct = pkgs.count("B_FluxCT"); - bool use_electrons = pkgs.count("Electrons"); - bool use_wind = pkgs.count("Wind"); - - // Allocate the fields ("containers") we need block by block - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; - // first make other useful containers - auto &base = pmb->meshblock_data.Get(); - if (stage == 1) { - pmb->meshblock_data.Add("dUdt", base); - for (int i = 1; i < integrator->nstages; i++) - pmb->meshblock_data.Add(stage_name[i], base); - // At the end of the step, updating "sc1" updates the base - // So we have to keep a copy at the beginning to calculate jcon - pmb->meshblock_data.Add("preserve", base); - } - } - - // Big synchronous region: get & apply fluxes to advance the fluid state - // num_partitions is usually 1 - const int num_partitions = pmesh->DefaultNumPartitions(); - TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = single_tasklist_per_pack_region[i]; - auto &mbase = pmesh->mesh_data.GetOrAdd("base", i); - auto &mc0 = pmesh->mesh_data.GetOrAdd(stage_name[stage - 1], i); - auto &mc1 = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); - auto &mdudt = pmesh->mesh_data.GetOrAdd("dUdt", i); - - auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, mc1); - auto t_start_recv_flux = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveFluxCorrections, mc0); - auto t_start_recv = t_start_recv_bound | t_start_recv_flux; - - // Calculate the HLL fluxes in each direction - // This reconstructs the primitives (P) at faces and uses them to calculate fluxes - // of the conserved variables (U) - // All subsequent operations until FillDerived are applied only to U - const ReconstructionType& recon = blocks[0]->packages.Get("GRMHD")->Param("recon"); - TaskID t_calculate_flux1, t_calculate_flux2, t_calculate_flux3; - switch (recon) { - case ReconstructionType::donor_cell: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - break; - case ReconstructionType::linear_mc: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - break; - case ReconstructionType::linear_vl: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - break; - case ReconstructionType::weno5: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, mc0.get()); - break; - case ReconstructionType::ppm: - case ReconstructionType::mp5: - case ReconstructionType::weno5_lower_poles: - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; - throw std::invalid_argument("Unsupported reconstruction algorithm!"); - } - auto t_calculate_flux = t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; - - auto t_set_flux = t_calculate_flux; - if (pmesh->multilevel) { - tl.AddTask(t_calculate_flux, parthenon::cell_centered_bvars::LoadAndSendFluxCorrections, mc0); - auto t_recv_flux = tl.AddTask(t_calculate_flux, parthenon::cell_centered_bvars::ReceiveFluxCorrections, mc0); - t_set_flux = tl.AddTask(t_recv_flux, parthenon::cell_centered_bvars::SetFluxCorrections, mc0); - } - - // FIX FLUXES - // Zero any fluxes through the pole or inflow from outflow boundaries - auto t_fix_flux = tl.AddTask(t_set_flux, KBoundaries::FixFlux, mc0.get()); - - auto t_flux_ct = t_fix_flux; - if (use_b_flux_ct) { - // Fix the conserved fluxes (exclusively B1/2/3) so that they obey divB==0, - // and there is no B field flux through the pole - t_flux_ct = tl.AddTask(t_fix_flux, B_FluxCT::TransportB, mc0.get()); - } - auto t_flux_fixed = t_flux_ct; - - // APPLY FLUXES - auto t_flux_div = tl.AddTask(t_flux_fixed, Update::FluxDivergence>, mc0.get(), mdudt.get()); - - // ADD SOURCES TO CONSERVED VARIABLES - // Source term for GRMHD, \Gamma * T - - auto t_grmhd_source = tl.AddTask(t_flux_div, GRMHD::AddSource, mc0.get(), mdudt.get()); - // Source term for constraint-damping. Applied only to B - auto t_b_cd_source = t_grmhd_source; - if (use_b_cd) { - t_b_cd_source = tl.AddTask(t_grmhd_source, B_CD::AddSource, mc0.get(), mdudt.get()); - } - // Wind source. Applied to conserved variables similar to GR source term - auto t_wind_source = t_b_cd_source; - if (use_wind) { - t_wind_source = tl.AddTask(t_b_cd_source, Wind::AddSource, mdudt.get()); - } - // Done with source terms - auto t_sources = t_wind_source; - - // UPDATE BASE CONTAINER - auto t_avg_data = tl.AddTask(t_sources, Update::AverageIndependentData>, - mc0.get(), mbase.get(), beta); - // apply du/dt to all independent fields in the container - auto t_update = tl.AddTask(t_avg_data, Update::UpdateIndependentData>, mc0.get(), - mdudt.get(), beta * dt, mc1.get()); - - // U_to_P needs a guess in order to converge, so we copy in sc0 - // (but only the fluid primitives!) Copying and syncing ensures that solves of the same zone - // on adjacent ranks are seeded with the same value, which keeps them (more) similar - MetadataFlag isPrimitive = pkgs.at("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isHD = pkgs.at("GRMHD")->Param("HDFlag"); - auto t_copy_prims = tl.AddTask(t_none, Update::WeightedSumData>, - std::vector({isHD, isPrimitive}), - mc0.get(), mc0.get(), 1.0, 0.0, mc1.get()); - - KBoundaries::AddBoundarySync(t_copy_prims, tl, mc1); - // if (pmesh->multilevel) { - // auto t_restrict = tl.AddTask(t_bound_sync, parthenon::cell_centered_refinement::RestrictPhysicalBounds, mc1.get()); - // tl.AddTask(t_restrict, ProlongateBoundaries, mc1); - // } - } - - // Async Region: Fill primitive values, apply physical boundary conditions, - // add any source terms which require the full primitives->primitives step - // TODO this can be Meshified - TaskRegion &async_region = tc.AddRegion(blocks.size()); - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; - auto &tl = async_region[i]; - //auto &base = pmb->meshblock_data.Get(); - auto &sc0 = pmb->meshblock_data.Get(stage_name[stage-1]); - auto &sc1 = pmb->meshblock_data.Get(stage_name[stage]); - - // At this point, we've sync'd all internal boundaries using the conserved - // variables. The physical boundaries (pole, inner/outer) are trickier, - // since they must be applied to the primitive variables rho,u,u1,u2,u3 - // but should apply to conserved forms of everything else. - - // This call fills the fluid primitive values in all physical zones, that is, including MPI boundaries but - // not the physical boundaries (which haven't been filled yet!) - // This relies on the primitives being calculated identically in MPI boundaries, vs their corresponding - // physical zones in the adjacent mesh block. To ensure this, we seed the solver with the same values - // in each case, by synchronizing them along with the conserved values above. - auto t_fill_derived = tl.AddTask(t_none, Update::FillDerived>, sc1.get()); - // After this call, the floors are applied (with the hook 'PostFillDerived', see floors.cpp) - - // Immediately fix any inversions which failed. Floors have been applied already as a part of (Post)FillDerived, - // so fixups performed by averaging zones will return logical results. Floors are re-applied after fixups - // Someday this will not be necessary as guaranteed-convergent UtoP schemes exist - auto t_fix_derived = tl.AddTask(t_fill_derived, GRMHD::FixUtoP, sc1.get()); - - // This is a parthenon call, but in spherical coordinates it will call the KHARMA functions in - // boundaries.cpp, which apply physical boundary conditions based on the primitive variables of GRHD, - // and based on the conserved forms for everything else. Note that because this is called *after* - // FillDerived (since it needs bulk fluid primitives to apply GRMHD boundaries), this function - // must call FillDerived *again* (for everything except the GRHD variables) to fill P in the ghost zones. - // This is why KHARMA packages need to implement their "FillDerived" a.k.a. UtoP functions in the form - // UtoP(rc, domain, coarse): so that they can be run over just the boundary domains here. - auto t_set_bc = tl.AddTask(t_fix_derived, parthenon::ApplyBoundaryConditions, sc1); - - // ADD SOURCES TO PRIMITIVE VARIABLES - // In order to calculate dissipation, we must know the entropy at the beginning and end of the substep, - // and this must be calculated from the fluid primitive variables rho,u (and for stability, obey floors!). - // We only have these just now from FillDerived (and PostFillDerived, and the boundary consistency stuff) - // Luckily, ApplyElectronHeating does *not* need another synchronization of the ghost zones, as it is applied to - // all zones and has a stencil of only one zone. As with FillDerived, this trusts that evaluations - // on the same zone match between MeshBlocks. - auto t_heat_electrons = t_set_bc; - if (use_electrons) { - auto t_heat_electrons = tl.AddTask(t_set_bc, Electrons::ApplyElectronHeating, sc0.get(), sc1.get()); - } - - auto t_step_done = t_heat_electrons; - - // Estimate next time step based on ctop - if (stage == integrator->nstages) { - auto t_new_dt = - tl.AddTask(t_step_done, Update::EstimateTimestep>, sc1.get()); - - // Update refinement - if (pmesh->adaptive) { - auto tag_refine = tl.AddTask( - t_step_done, parthenon::Refinement::Tag>, sc1.get()); - } - } - } - - // Second boundary sync: - // ensure that primitive variables in ghost zones are *exactly* - // identical to their physical counterparts, now that they have been - // modified on each rank. - const auto &two_sync = pkgs.at("GRMHD")->Param("two_sync"); - if (two_sync) { - TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = single_tasklist_per_pack_region[i]; - auto &mc1 = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); - - auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, mc1); - auto t_bound_sync = KBoundaries::AddBoundarySync(t_start_recv_bound, tl, mc1); - } - } - - return tc; -} diff --git a/kharma/harm_driver.hpp b/kharma/harm_driver.hpp deleted file mode 100644 index 31980ebb..00000000 --- a/kharma/harm_driver.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * File: harm_driver.hpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include - -#include - -#include "types.hpp" - -using namespace parthenon; - -/** - * A Driver object orchestrates everything that has to be done to a mesh to constitute a step. - * For HARM, this means the predictor-corrector steps of fluid evolution. - * - * Unlike MHD, GRMHD has two independent sets of variables: the conserved variables, and a set of - * "primitive" variables more amenable to reconstruction. To evolve the fluid, the conserved - * variables must be: - * 1. Transformed to the primitives - * 2. Reconstruct the right- and left-going components at zone faces - * 3. Transform back to conserved quantities and calculate the fluxes at faces - * 4. Update conserved variables using the divergence of conserved fluxes - * - * (for higher-order schemes, this is more or less just repeated and added) - * - * iharm3d (and the ImEx driver) put step 1 at the bottom, and syncs/fixes primitive variables - * between each step. This driver runs through the steps as listed, applying floors after step - * 1 as iharm3d does, but syncing the conserved variables. - */ -class HARMDriver : public MultiStageDriver { - public: - /** - * Default constructor - */ - HARMDriver(ParameterInput *pin, ApplicationInput *papp, Mesh *pm) : MultiStageDriver(pin, papp, pm) {} - - /** - * All the tasks which constitute advancing the fluid in a mesh by one stage. - * This includes calculation of the primitives and reconstruction of their face values, - * calculation of conserved values and fluxes thereof at faces, - * application of fluxes and a source term in order to update zone values, - * and finally calculation of the next timestep based on the CFL condition. - * - * The function is heavily documented since order changes can introduce subtle bugs, - * usually w.r.t. fluid "state" being spread across the primitive and conserved quantities - */ - TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); -}; \ No newline at end of file diff --git a/kharma/imex_driver.cpp b/kharma/imex_driver.cpp deleted file mode 100644 index 095e0f48..00000000 --- a/kharma/imex_driver.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* - * File: imex_driver.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "imex_driver.hpp" - -#include - -#include -#include -#include - -#include "decs.hpp" - -//Packages -#include "b_flux_ct.hpp" -#include "b_cd.hpp" -#include "electrons.hpp" -#include "grmhd.hpp" -#include "wind.hpp" -// Other headers -#include "boundaries.hpp" -#include "debug.hpp" -#include "flux.hpp" -#include "harm_driver.hpp" -#include "resize_restart.hpp" -#include "implicit.hpp" - -TaskCollection ImexDriver::MakeTaskCollection(BlockList_t &blocks, int stage) -{ - // Reminder that NOTHING YOU CALL HERE WILL GET CALLED EVERY STEP - // this function is run *once*, and returns a list of what should be done every step. - // No prints or direct function calls here will do what you want, only calls to tl.AddTask() - - // This is *not* likely the task list you are looking for, and is not well commented yet. - // See harm_driver.cpp for KHARMA's main driver. - // This driver *requires* the "Implicit" package to be loaded, in order to read some flags - // it defines for - - // NOTE: Renamed state names to something more intuitive. - // '_full_step_init' refers to the fluid state at the start of the full time step (Si in iharm3d) - // '_sub_step_init' refers to the fluid state at the start of the sub step (Ss in iharm3d) - // '_sub_step_final' refers to the fluid state at the end of the sub step (Sf in iharm3d) - // '_flux_src' refers to the mesh object corresponding to -divF + S - // '_solver' refers to the fluid state passed to the Implicit solver. At the end of the solve - // copy P and U from solver state to sub_step_final state. - - TaskCollection tc; - TaskID t_none(0); - - Real beta = integrator->beta[stage - 1]; - const Real dt = integrator->dt; - auto stage_name = integrator->stage_name; - - // Which packages we've loaded affects which tasks we'll add to the list - auto& pkgs = blocks[0]->packages.AllPackages(); - bool use_b_cd = pkgs.count("B_CD"); - bool use_b_flux_ct = pkgs.count("B_FluxCT"); - bool use_electrons = pkgs.count("Electrons"); - bool use_wind = pkgs.count("Wind"); - bool use_emhd = pkgs.count("EMHD"); - - // Allocate the fluid states ("containers") we need for each block - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; - // first make other useful containers - auto &base = pmb->meshblock_data.Get(); - if (stage == 1) { - pmb->meshblock_data.Add("dUdt", base); - for (int i = 1; i < integrator->nstages; i++) - pmb->meshblock_data.Add(stage_name[i], base); - // At the end of the step, updating "mbd_sub_step_final" updates the base - // So we have to keep a copy at the beginning to calculate jcon - pmb->meshblock_data.Add("preserve", base); - // When solving, we need a temporary copy with any explicit updates, - // but not overwriting the beginning- or mid-step values - pmb->meshblock_data.Add("solver", base); - } - } - - // Big synchronous region: get & apply fluxes to advance the fluid state - // num_partitions is usually 1 - const int num_partitions = pmesh->DefaultNumPartitions(); - TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = single_tasklist_per_pack_region[i]; - auto &md_full_step_init = pmesh->mesh_data.GetOrAdd("base", i); - auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(stage_name[stage - 1], i); - auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); - auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); - auto &md_solver = pmesh->mesh_data.GetOrAdd("solver", i); - - auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, md_sub_step_final); - auto t_start_recv_flux = t_none; - if (pmesh->multilevel) - t_start_recv_flux = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveFluxCorrections, md_sub_step_init); - auto t_start_recv = t_start_recv_bound | t_start_recv_flux; - - // Calculate the HLL fluxes in each direction - // This reconstructs the primitives (P) at faces and uses them to calculate fluxes - // of the conserved variables (U) - const ReconstructionType& recon = pkgs.at("GRMHD")->Param("recon"); - TaskID t_calculate_flux1, t_calculate_flux2, t_calculate_flux3; - switch (recon) { - case ReconstructionType::donor_cell: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - break; - case ReconstructionType::linear_mc: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - break; - case ReconstructionType::linear_vl: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - break; - case ReconstructionType::weno5: - t_calculate_flux1 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux2 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - t_calculate_flux3 = tl.AddTask(t_start_recv, Flux::GetFlux, md_sub_step_init.get()); - break; - case ReconstructionType::ppm: - case ReconstructionType::mp5: - case ReconstructionType::weno5_lower_poles: - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; - throw std::invalid_argument("Unsupported reconstruction algorithm!"); - } - auto t_calculate_flux = t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; - - auto t_set_flux = t_calculate_flux; - if (pmesh->multilevel) { - tl.AddTask(t_calculate_flux, parthenon::cell_centered_bvars::LoadAndSendFluxCorrections, md_full_step_init); - auto t_recv_flux = tl.AddTask(t_calculate_flux, parthenon::cell_centered_bvars::ReceiveFluxCorrections, md_full_step_init); - t_set_flux = tl.AddTask(t_recv_flux, parthenon::cell_centered_bvars::SetFluxCorrections, md_full_step_init); - } - - // FIX FLUXES - // Zero any fluxes through the pole or inflow from outflow boundaries - auto t_fix_flux = tl.AddTask(t_set_flux, KBoundaries::FixFlux, md_sub_step_init.get()); - - auto t_flux_ct = t_fix_flux; - if (use_b_flux_ct) { - // Fix the conserved fluxes (exclusively B1/2/3) so that they obey divB==0, - // and there is no B field flux through the pole - auto t_flux_ct = tl.AddTask(t_fix_flux, B_FluxCT::TransportB, md_sub_step_init.get()); - } - auto t_flux_fixed = t_flux_ct; - - // APPLY FLUXES - auto t_flux_div = tl.AddTask(t_none, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); - - // ADD EXPLICIT SOURCES TO CONSERVED VARIABLES - // Source term for GRMHD, \Gamma * T - // TODO take this out in Minkowski space - auto t_grmhd_source = tl.AddTask(t_flux_div, GRMHD::AddSource, md_sub_step_init.get(), md_flux_src.get()); - // Source term for constraint-damping. Applied only to B - auto t_b_cd_source = t_grmhd_source; - if (use_b_cd) { - t_b_cd_source = tl.AddTask(t_grmhd_source, B_CD::AddSource, md_sub_step_init.get(), md_flux_src.get()); - } - // Wind source. Applied to conserved variables similar to GR source term - auto t_wind_source = t_b_cd_source; - if (use_wind) { - t_wind_source = tl.AddTask(t_b_cd_source, Wind::AddSource, md_flux_src.get()); - } - auto t_emhd_source = t_wind_source; - if (use_emhd) { - t_emhd_source = tl.AddTask(t_wind_source, EMHD::AddSource, md_sub_step_init.get(), md_flux_src.get()); - } - // Done with source terms - auto t_sources = t_emhd_source; - - // UPDATE VARIABLES - // This block is designed to intelligently update a set of variables partially marked "Implicit" - // and partially "Explicit," by first doing any explicit updates, then using them as elements - // of the "guess" for the implicit solve - - // Indicators for Explicit/Implicit variables to evolve - MetadataFlag isExplicit = pkgs.at("Implicit")->Param("ExplicitFlag"); - MetadataFlag isImplicit = pkgs.at("Implicit")->Param("ImplicitFlag"); - MetadataFlag isPrimitive = pkgs.at("GRMHD")->Param("PrimitiveFlag"); - // Substep timestep - const double beta_this = integrator->beta[stage % integrator->nstages]; - const double dt_this = dt * beta_this; - - // Update any variables for which we should take an explicit step. - // These calls are the equivalent of what's in HARMDriver - // auto t_average = tl.AddTask(t_sources, Update::WeightedSumData>, - // std::vector({isExplicit, Metadata::Independent}), - // md_sub_step_init.get(), md_full_step_init.get(), beta, (1.0 - beta), md_solver.get()); - // auto t_explicit_U = tl.AddTask(t_average, Update::WeightedSumData>, - // std::vector({isExplicit, Metadata::Independent}), - // md_solver.get(), md_flux_src.get(), 1.0, beta * dt, md_solver.get()); - // Version with half/whole step to match implicit solver - auto t_explicit_U = tl.AddTask(t_sources, Update::WeightedSumData>, - std::vector({isExplicit, Metadata::Independent}), - md_full_step_init.get(), md_flux_src.get(), 1.0, dt_this, md_solver.get()); - - // Make sure the primitive values of any explicit fields are filled - auto t_explicit_UtoP_B = t_explicit_U; - if (!pkgs.at("B_FluxCT")->Param("implicit")) - t_explicit_UtoP_B = tl.AddTask(t_explicit_U, B_FluxCT::FillDerivedMeshTask, md_solver.get()); - // If GRMHD is not implicit, but we're still going to be taking an implicit step, call its FillDerived function - // TODO Would be faster/more flexible if this supported MeshData. Also maybe race condition - auto t_explicit_UtoP_G = t_explicit_UtoP_B; - if (!pkgs.at("GRMHD")->Param("implicit") && use_b_cd) { - // Get flux corrections from AMR neighbors - for (auto &pmb : pmesh->block_list) { - auto& mbd = pmb->meshblock_data.Get(); - auto t_explicit_UtoP_G = tl.AddTask(t_explicit_UtoP_B, GRMHD::FillDerivedBlockTask, mbd.get()); - } - } - auto t_explicit = t_explicit_UtoP_G; - - // Copy the current implicit vars in as a guess. This needs at least the primitive vars - auto t_copy_guess = tl.AddTask(t_sources, Update::WeightedSumData>, - std::vector({isImplicit}), - md_sub_step_init.get(), md_sub_step_init.get(), 1.0, 0.0, md_solver.get()); - - // Time-step implicit variables by root-finding the residual - // This applies the functions of both the update above and FillDerived call below for "isImplicit" variables - // This takes dt for the *substep*, not the whole thing, so we multiply total dt by *this step's* beta - auto t_guess_ready = t_explicit | t_copy_guess; - auto t_implicit = tl.AddTask(t_guess_ready, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), - md_flux_src.get(), md_solver.get(), dt_this); - - // Copy the solver state into the final state md_sub_step_final - auto t_copy_result = tl.AddTask(t_implicit, Update::WeightedSumData>, - std::vector({}), md_solver.get(), md_solver.get(), - 1.0, 0.0, md_sub_step_final.get()); - - // If evolving GRMHD explicitly, U_to_P needs a guess in order to converge, so we copy in md_sub_step_init - auto t_copy_prims = t_none; - if (!pkgs.at("GRMHD")->Param("implicit")) { - MetadataFlag isPrimitive = pkgs.at("GRMHD")->Param("PrimitiveFlag"); - MetadataFlag isHD = pkgs.at("GRMHD")->Param("HDFlag"); - auto t_copy_prims = tl.AddTask(t_none, Update::WeightedSumData>, - std::vector({isHD, isPrimitive}), - md_sub_step_init.get(), md_sub_step_init.get(), 1.0, 0.0, md_sub_step_final.get()); - } - - } - - // Even though we filled some primitive vars - TaskRegion &async_region1 = tc.AddRegion(blocks.size()); - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; - auto &tl = async_region1[i]; - auto &mbd_sub_step_final = pmb->meshblock_data.Get(stage_name[stage]); - - // Note that floors are applied (to all variables!) immediately after this FillDerived call. - // However, inversion/floor inversion failures are *not* immediately corrected with FixUtoP, - // but synchronized (including pflags!) first. - // With an extra ghost zone, this *should* still allow binary-similar evolution between numbers of mesh blocks, - // but hasn't been tested to do so yet. - auto t_fill_derived = tl.AddTask(t_none, Update::FillDerived>, mbd_sub_step_final.get()); - } - - TaskRegion &sync_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = sync_region[i]; - auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); - // MPI/MeshBlock boundary exchange. - // Note that in this driver, this block syncs *primitive* variables, not conserved - KBoundaries::AddBoundarySync(t_none, tl, mbd_sub_step_final); - } - - // Async Region: Any post-sync tasks. Fixups, timestep & AMR things. - TaskRegion &async_region2 = tc.AddRegion(blocks.size()); - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; - auto &tl = async_region2[i]; - auto &mbd_sub_step_init = pmb->meshblock_data.Get(stage_name[stage-1]); - auto &mbd_sub_step_final = pmb->meshblock_data.Get(stage_name[stage]); - - // If we're evolving even the GRMHD variables explicitly, we need to fix UtoP variable inversion failures - // Syncing bounds before calling this, and then running it over the whole domain, will make - // behavior for different mesh breakdowns much more similar (identical?), since bad zones in - // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk - auto t_fix_derived = t_none; - if (!pkgs.at("GRMHD")->Param("implicit")) { - t_fix_derived = tl.AddTask(t_fix_derived, GRMHD::FixUtoP, mbd_sub_step_final.get()); - } - - auto t_set_bc = tl.AddTask(t_fix_derived, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); - - // Electron heating goes where it does in HARMDriver, for the same reasons - auto t_heat_electrons = t_set_bc; - if (use_electrons) { - t_heat_electrons = tl.AddTask(t_set_bc, Electrons::ApplyElectronHeating, - mbd_sub_step_init.get(), mbd_sub_step_final.get()); - } - - // Make sure conserved vars are synchronized at step end - auto t_ptou = tl.AddTask(t_heat_electrons, Flux::PtoUTask, mbd_sub_step_final.get(), IndexDomain::entire); - - auto t_step_done = t_ptou; - - // Estimate next time step based on ctop - if (stage == integrator->nstages) { - auto t_new_dt = - tl.AddTask(t_step_done, Update::EstimateTimestep>, mbd_sub_step_final.get()); - - // Update refinement - if (pmesh->adaptive) { - auto tag_refine = tl.AddTask( - t_step_done, parthenon::Refinement::Tag>, mbd_sub_step_final.get()); - } - } - } - - // Second boundary sync: - // ensure that primitive variables in ghost zones are *exactly* - // identical to their physical counterparts, now that they have been - // modified on each rank. - const auto &two_sync = pkgs.at("GRMHD")->Param("two_sync"); - if (two_sync) { - TaskRegion &single_tasklist_per_pack_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = single_tasklist_per_pack_region[i]; - auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(stage_name[stage], i); - - auto t_start_recv_bound = tl.AddTask(t_none, parthenon::cell_centered_bvars::StartReceiveBoundBufs, md_sub_step_final); - auto t_bound_sync = KBoundaries::AddBoundarySync(t_start_recv_bound, tl, md_sub_step_final); - } - } - - return tc; -} diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 8bd632f1..90695357 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -39,20 +39,38 @@ #include "grmhd_functions.hpp" #include "pack.hpp" +#if DISABLE_IMPLICIT + +// The package should never be loaded if there are not implicitly-evolved variables +// Therefore we yell at load time rather than waiting for the first solve +std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ throw std::runtime_error("KHARMA was compiled without implicit stepping support!"); } +// We still need a stub for Step() in order to compile, but it will never be called +TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, + MeshData *md_linesearch, MeshData *md_solver, const Real& dt) {} + +#else + +// Implicit nonlinear solve requires several linear solves per-zone +// Use Kokkos-kernels QR decomposition & triangular solve, they're fast. +#include +#include +#include +#include +#include + std::vector Implicit::get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit) { auto pmb0 = rc->GetBlockPointer(); - MetadataFlag isImplicit = pmb0->packages.Get("Implicit")->Param("ImplicitFlag"); - MetadataFlag isExplicit = pmb0->packages.Get("Implicit")->Param("ExplicitFlag"); std::vector out; - auto vars = rc->GetVariablesByFlag(std::vector({isImplicit, flag}), true).labels(); + auto vars = rc->GetVariablesByFlag(std::vector({Metadata::GetUserFlag("Implicit"), flag})).labels(); for (int i=0; i < vars.size(); ++i) { if (rc->Contains(vars[i])) { out.push_back(vars[i]); } } if (!only_implicit) { - vars = rc->GetVariablesByFlag(std::vector({isExplicit, flag}), true).labels(); + vars = rc->GetVariablesByFlag(std::vector({Metadata::GetUserFlag("Explicit"), flag})).labels(); for (int i=0; i < vars.size(); ++i) { if (rc->Contains(vars[i])) { out.push_back(vars[i]); @@ -62,10 +80,10 @@ std::vector Implicit::get_ordered_names(MeshBlockData *rc, co return out; } -std::shared_ptr Implicit::Initialize(ParameterInput *pin) +std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::shared_ptr& packages) { Flag("Initializing Implicit Package"); - auto pkg = std::make_shared("Implicit"); + auto pkg = std::make_shared("Implicit"); Params ¶ms = pkg->AllParams(); // Implicit solver parameters @@ -73,87 +91,116 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin) params.Add("jacobian_delta", jacobian_delta); Real rootfind_tol = pin->GetOrAddReal("implicit", "rootfind_tol", 1.e-12); params.Add("rootfind_tol", rootfind_tol); - Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); - params.Add("linesearch_lambda", linesearch_lambda); int min_nonlinear_iter = pin->GetOrAddInteger("implicit", "min_nonlinear_iter", 1); params.Add("min_nonlinear_iter", min_nonlinear_iter); int max_nonlinear_iter = pin->GetOrAddInteger("implicit", "max_nonlinear_iter", 3); params.Add("max_nonlinear_iter", max_nonlinear_iter); + // The QR decomposition bundled with KHARMA has column pivoting for stability. + // The alternative LU decomposition does not, and should mostly be used for debugging. bool use_qr = pin->GetOrAddBoolean("implicit", "use_qr", true); params.Add("use_qr", use_qr); - int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - params.Add("verbose", verbose); + bool linesearch = pin->GetOrAddBoolean("implicit", "linesearch", true); + params.Add("linesearch", linesearch); + int max_linesearch_iter = pin->GetOrAddInteger("implicit", "max_linesearch_iter", 3); + params.Add("max_linesearch_iter", max_linesearch_iter); + Real linesearch_eps = pin->GetOrAddReal("implicit", "linesearch_eps", 1.e-4); + params.Add("linesearch_eps", linesearch_eps); + Real linesearch_lambda = pin->GetOrAddReal("implicit", "linesearch_lambda", 1.0); + params.Add("linesearch_lambda", linesearch_lambda); // TODO some way to denote non-converged zones? impflag or something? - // When using this package we'll need to distinguish implicitly and explicitly-updated variables - // All independent variables should be marked one or the other when this package is in use - MetadataFlag isImplicit = Metadata::AllocateNewFlag("Implicit"); - params.Add("ImplicitFlag", isImplicit); - MetadataFlag isExplicit = Metadata::AllocateNewFlag("Explicit"); - params.Add("ExplicitFlag", isExplicit); + // Allocate additional fields that reflect the success of the solver + // L2 norm of the residual + Metadata m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("solve_norm", m_real); + // Integer field that saves where the solver fails (rho + drho < 0 || u + du < 0) + // Metadata m_int = Metadata({Metadata::Integer, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariabl + + // TODO: Find a way to save residuals based on a runtime parameter. We don't want to unnecessarily allocate + // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. + + // Should the solve save the residual vector field? Useful for debugging purposes. Default is NO. + // bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); + // params.Add("save_residual", save_residual); + + // Vector field to store residual components (only for those variables that are evolved implicitly) + // if (save_residual) { + // auto driver_type = pin->GetString("driver", "type"); + // bool grmhd_implicit = (driver_type == "imex") && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); + // bool implicit_b = (driver_type == "imex") && (pin->GetOrAddBoolean("b_field", "implicit", grmhd_implicit)); + // bool emhd_enabled = pin->GetOrAddBoolean("emhd", "on", false); + // int nvars_implicit = // Get this from "Driver" + + // // flags_vec = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + // // auto flags_vec(flags_vec); + // // flags_vec.push_back(Metadata::Vector); + // std::vector s_vector({nfvar}); + // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); + // pkg->AddField("residual", m); + // } // Anything we need to run from this package on callbacks // Maybe a post-step L2 or flag count or similar - // pkg->PostFillDerivedBlock = Implicit::PostFillDerivedBlock; - // pkg->PostStepDiagnosticsMesh = Implicit::PostStepDiagnostics; Flag("Initialized"); return pkg; } -#if ENABLE_IMPLICIT - -// Implicit nonlinear solve requires several linear solves per-zone -// Use Kokkos-kernels QR decomposition & triangular solve, they're fast. -#include -#include -#include -#include - TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, - MeshData *md_solver, const Real& dt) + MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { Flag(md_full_step_init, "Implicit Iteration start, full step"); Flag(md_sub_step_init, "Implicit Iteration start, sub step"); Flag(md_flux_src, "Implicit Iteration start, divF and sources"); + Flag(md_linesearch, "Linesearch"); auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); + auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); + auto pmb_linesearch = md_linesearch->GetBlockData(0)->GetBlockPointer(); // Parameters const auto& implicit_par = pmb_full_step_init->packages.Get("Implicit")->AllParams(); const int iter_min = implicit_par.Get("min_nonlinear_iter"); const int iter_max = implicit_par.Get("max_nonlinear_iter"); - const Real lambda = implicit_par.Get("linesearch_lambda"); const Real delta = implicit_par.Get("jacobian_delta"); const Real rootfind_tol = implicit_par.Get("rootfind_tol"); const bool use_qr = implicit_par.Get("use_qr"); - const int verbose = implicit_par.Get("verbose"); + const int verbose = pmb_full_step_init->packages.Get("Globals")->Param("verbose"); const Real gam = pmb_full_step_init->packages.Get("GRMHD")->Param("gamma"); + + const bool linesearch = implicit_par.Get("linesearch"); + const int max_linesearch_iter = implicit_par.Get("max_linesearch_iter"); + const Real linesearch_eps = implicit_par.Get("linesearch_eps"); + const Real linesearch_lambda = implicit_par.Get("linesearch_lambda"); + + // const bool save_residual = implicit_par.Get("save_residual"); + // Misc other constants for inside the kernel const bool am_rank0 = MPIRank0(); const Real tiny(SMALL), alpha(1.0); // We need two sets of emhd_params because we need the relaxation scale // at the same state in the implicit source terms - EMHD_parameters emhd_params_full_step_init, emhd_params_sub_step_init; + // Need an object of `EMHD_parameters` for the `linesearch` state + EMHD_parameters emhd_params_sub_step_init, emhd_params_solver, emhd_params_linesearch; if (pmb_sub_step_init->packages.AllPackages().count("EMHD")) { - const auto& pars_full_step_init = pmb_full_step_init->packages.Get("EMHD")->AllParams(); const auto& pars_sub_step_init = pmb_sub_step_init->packages.Get("EMHD")->AllParams(); - emhd_params_full_step_init = pars_full_step_init.Get("emhd_params"); + const auto& pars_solver = pmb_solver->packages.Get("EMHD")->AllParams(); + const auto& pars_linesearch = pmb_linesearch->packages.Get("EMHD")->AllParams(); emhd_params_sub_step_init = pars_sub_step_init.Get("emhd_params"); + emhd_params_solver = pars_solver.Get("emhd_params"); + emhd_params_linesearch = pars_linesearch.Get("emhd_params"); } // I don't normally do this, but we *really* care about variable ordering here. // The implicit variables need to be first, so we know how to iterate over just them to fill - // just the residual & Jacobian we care about, which makes the solve much faster. - // This strategy is ugly but potentially gives us complete control, - // in case Kokkos's un-pivoted LU proves problematic - MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); + // just the residual & Jacobian we care about, which makes the solve faster. auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), isPrimitive); - auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); + auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); + auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -168,30 +215,40 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Flux divergence plus explicit source terms. This is what we'd be adding. auto& flux_src_all = md_flux_src->PackVariables(ordered_cons); // Guess at initial state. We update only the implicit primitive vars - auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_solver_all = md_solver->PackVariables(ordered_prims); + auto& P_linesearch_all = md_linesearch->PackVariables(ordered_prims); // Sizes and scratchpads const int nblock = U_full_step_init_all.GetDim(5); const int nvar = U_full_step_init_all.GetDim(4); // Get number of implicit variables - auto implicit_vars = get_ordered_names(mbd_full_step_init.get(), isPrimitive, true); + auto implicit_vars = get_ordered_names(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive"), true); PackIndexMap implicit_prims_map; auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P_full_step_init_implicit.GetDim(4); + // Pull fields associated with the solver's performance + auto& solve_norm_all = md_solver->PackVariables(std::vector{"solve_norm"}); + auto& solve_fail_all = md_solver->PackVariables(std::vector{"solve_fail"}); + // auto& solve_fail_all = md_solver->GetBlockData(0)->Get("solve_fail").data; + + // if (save_residual) { + // auto& residual_all = md_solver->GetBlockData(0)->Get("residual").data; + // } + auto bounds = pmb_sub_step_init->cellbounds; const int n1 = bounds.ncellsi(IndexDomain::entire); const int n2 = bounds.ncellsj(IndexDomain::entire); const int n3 = bounds.ncellsk(IndexDomain::entire); // RETURN if there aren't any implicit variables to evolve - //std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; + // std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; if (nfvar == 0) return TaskStatus::complete; // The norm of the residual. We store this to avoid the main kernel // also being a 2-stage reduction, which is complex and sucks. // TODO keep this around as a field? - ParArray4D norm_all("norm_all", nblock, n3, n2, n1); + // ParArray4D norm_all("norm_all", nblock, n3, n2, n1); // EDIT // Get meshblock array bounds from Parthenon const IndexDomain domain = IndexDomain::interior; @@ -210,12 +267,19 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nvar, n1); const size_t fvar_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nfvar, n1); + const size_t fvar_int_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nfvar, n1); const size_t tensor_size_in_bytes = parthenon::ScratchPad3D::shmem_size(nfvar, nfvar, n1); + const size_t scalar_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); + const size_t int_size_in_bytes = parthenon::ScratchPad1D::shmem_size(n1); // Allocate enough to cache: // jacobian (2D) - // residual, deltaP (implicit only) - // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, divF_src, P_solver, dU_implicit, two temps (all vars) - const size_t total_scratch_bytes = tensor_size_in_bytes + (4) * fvar_size_in_bytes + (10) * var_size_in_bytes; + // residual, deltaP, trans, work (implicit only) + // P_full_step_init/U_full_step_init, P_sub_step_init/U_sub_step_init, flux_src, + // P_solver, P_linesearch, dU_implicit, three temps (all vars) + // solve_norm, solve_fail + const size_t total_scratch_bytes = tensor_size_in_bytes + (6) * fvar_size_in_bytes + fvar_int_size_in_bytes + + (10) * var_size_in_bytes + (2) * scalar_size_in_bytes; + // + int_size_in_bytes; // Iterate. This loop is outside the kokkos kernel in order to print max_norm // There are generally a low and similar number of iterations between @@ -232,12 +296,14 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad3D jacobian_s(member.team_scratch(scratch_level), nfvar, nfvar, n1); ScratchPad2D residual_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D delta_prim_s(member.team_scratch(scratch_level), nfvar, n1); + ScratchPad2D pivot_s(member.team_scratch(scratch_level), nfvar, n1); ScratchPad2D trans_s(member.team_scratch(scratch_level), nfvar, n1); - ScratchPad2D work_s(member.team_scratch(scratch_level), nfvar, n1); + ScratchPad2D work_s(member.team_scratch(scratch_level), 2*nfvar, n1); + // tmp2 holds a residual with only implicit variable errors + ScratchPad2D tmp2_s(member.team_scratch(scratch_level), nfvar, n1); // Scratchpads for all vars ScratchPad2D dU_implicit_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D tmp1_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D tmp2_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D tmp3_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_full_step_init_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D U_full_step_init_s(member.team_scratch(scratch_level), nvar, n1); @@ -245,6 +311,11 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad2D U_sub_step_init_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D flux_src_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D P_solver_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), nvar, n1); + // Scratchpads for solver performance diagnostics + ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); + // ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); + ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); // Copy some file contents to scratchpads, so we can slice them PLOOP { @@ -254,9 +325,30 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ U_full_step_init_s(ip, i) = U_full_step_init_all(b)(ip, k, j, i); P_sub_step_init_s(ip, i) = P_sub_step_init_all(b)(ip, k, j, i); U_sub_step_init_s(ip, i) = U_sub_step_init_all(b)(ip, k, j, i); - flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); - P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); - dU_implicit_s(ip, i) = 0.; + flux_src_s(ip, i) = flux_src_all(b)(ip, k, j, i); + P_solver_s(ip, i) = P_solver_all(b)(ip, k, j, i); + P_linesearch_s(ip, i) = P_linesearch_all(b)(ip, k, j, i); + dU_implicit_s(ip, i) = 0.; + + solve_norm_s(i) = 0.; + solve_fail_s(i) = 0; + } + ); + } + member.team_barrier(); + // For implicit variables only + for(int ip=0; ip < nfvar; ++ip) { + parthenon::par_for_inner(member, 0, n1-1, + [&](const int& i) { + for(int jp=0; jp < nfvar; ++jp) + jacobian_s(ip, jp, i) = 0.; + residual_s(ip, i) = 0.; + delta_prim_s(ip, i) = 0.; + pivot_s(ip, i) = 0; + trans_s(ip, i) = 0.; + work_s(ip, i) = 0.; + work_s(ip+nfvar, i) = 0.; + tmp2_s(ip, i) = 0.; } ); } @@ -283,32 +375,44 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ auto U_sub_step_init = Kokkos::subview(U_sub_step_init_s, Kokkos::ALL(), i); auto flux_src = Kokkos::subview(flux_src_s, Kokkos::ALL(), i); auto P_solver = Kokkos::subview(P_solver_s, Kokkos::ALL(), i); + auto P_linesearch = Kokkos::subview(P_linesearch_s, Kokkos::ALL(), i); // Solver variables auto residual = Kokkos::subview(residual_s, Kokkos::ALL(), i); auto jacobian = Kokkos::subview(jacobian_s, Kokkos::ALL(), Kokkos::ALL(), i); auto delta_prim = Kokkos::subview(delta_prim_s, Kokkos::ALL(), i); - auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); - auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); + auto pivot = Kokkos::subview(pivot_s, Kokkos::ALL(), i); + auto trans = Kokkos::subview(trans_s, Kokkos::ALL(), i); + auto work = Kokkos::subview(work_s, Kokkos::ALL(), i); // Temporaries auto tmp1 = Kokkos::subview(tmp1_s, Kokkos::ALL(), i); auto tmp2 = Kokkos::subview(tmp2_s, Kokkos::ALL(), i); auto tmp3 = Kokkos::subview(tmp3_s, Kokkos::ALL(), i); // Implicit sources at starting state auto dU_implicit = Kokkos::subview(dU_implicit_s, Kokkos::ALL(), i); + // Solver performance diagnostics + auto solve_norm = Kokkos::subview(solve_norm_s, i); + auto solve_fail = Kokkos::subview(solve_fail_s, i); + if (m_p.Q >= 0) { EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, emhd_params_sub_step_init, dU_implicit(m_u.Q), dU_implicit(m_u.DP)); } + // Copy `solver` prims to `linesearch`. This doesn't matter for the first step of the solver + // since we do a copy in imex_step just before, but it is required for the subsequent + // iterations of the solver. + PLOOP P_linesearch(ip) = P_solver(ip); + Real lambda = linesearch_lambda; + // Jacobian calculation // Requires calculating the residual anyway, so we grab it here calc_jacobian(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, - flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_full_step_init, + flux_src, dU_implicit, tmp1, tmp2, tmp3, m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); - // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { + // if (am_rank0 && b == 0 && i == 10 && j == 10 && k == kb.s) { // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", // m_p.RHO, m_p.UU, m_p.U1, m_p.B1, m_p.Q, m_p.DP); // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", @@ -319,14 +423,14 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // printf("Ps: "); PLOOP printf("%6.5e ", P_sub_step_init(ip)); printf("\n"); // printf("Us: "); PLOOP printf("%6.5e ", U_sub_step_init(ip)); printf("\n"); // printf("dUdt: "); PLOOP printf("%6.5e ", dU_implicit(ip)); printf("\n"); - // printf("Initial Jacobian:\n"); for (int jp=0; jp::invoke(jacobian, trans, work); + KokkosBatched::SerialQR::invoke(jacobian, trans, pivot, work); KokkosBatched::SerialApplyQ ::invoke(jacobian, trans, delta_prim, work); @@ -334,14 +438,66 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KokkosBatched::SerialLU::invoke(jacobian, tiny); } KokkosBatched::SerialTrsv + KokkosBatched::Diag::NonUnit, KokkosBatched::Algo::Trsv::Unblocked> ::invoke(alpha, jacobian, delta_prim); + if (use_qr) { + // Linear solve by QR decomposition + KokkosBatched::SerialApplyPivot + ::invoke(pivot, delta_prim); + } + + // Check for positive definite values of density and internal energy. + // Break from solve if manual backtracking is not sufficient. + // The primitives will be averaged over good neighbors. + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = 1; + lambda = 0.1; + } + if ((P_solver(m_p.RHO) + lambda*delta_prim(m_p.RHO) < 0.) || (P_solver(m_p.UU) + lambda*delta_prim(m_p.UU) < 0.)) { + solve_fail() = 2; + // break; // Doesn't break from the inner par_for. + // Let it continue for now, but we'll average over the zone later + } + + // Linesearch + if (linesearch) { + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); + + Real f0 = 0.5 * solve_norm(); + Real fprime0 = -2. * f0; + + for (int linesearch_iter = 0; linesearch_iter < max_linesearch_iter; linesearch_iter++) { + // Take step + FLOOP P_linesearch(ip) = P_solver(ip) + (lambda * delta_prim(ip)); + + // Compute solve_norm of the residual (loss function) + calc_residual(G, P_linesearch, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, + dU_implicit, tmp3, m_p, m_u, emhd_params_linesearch, emhd_params_solver, nfvar, + k, j, i, gam, dt, residual); + + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); + Real f1 = 0.5 * solve_norm(); + + // Compute new step length + int condition = f1 > (f0 * (1. - linesearch_eps * lambda) + SMALL); + Real denom = (f1 - f0 - (fprime0 * lambda)) * condition + (1 - condition); + Real lambda_new = -fprime0 * lambda * lambda / denom / 2.; + lambda = lambda * (1 - condition) + (condition * lambda_new); + + // Check if new solution has converged within required tolerance + if (condition == 0) break; + } + } - // Update the guess. For now lambda == 1, choose on the fly? + // Update the guess FLOOP P_solver(ip) += lambda * delta_prim(ip); calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // if (am_rank0 && b == 0 && i == 11 && j == 11 && k == kb.s) { // printf("Variable ordering: rho %d uu %d u1 %d B1 %d q %d dP %d\n", @@ -353,21 +509,31 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Store for maximum/output // I would be tempted to store the whole residual, but it's of variable size - norm_all(b, k , j, i) = 0; - FLOOP norm_all(b, k, j, i) += residual(ip)*residual(ip); - norm_all(b, k, j, i) = m::sqrt(norm_all(b, k, j, i)); // TODO faster to scratch cache & copy? + solve_norm() = 0; + FLOOP solve_norm() += residual(ip) * residual(ip); + solve_norm() = m::sqrt(solve_norm()); // TODO faster to scratch cache & copy? } ); member.team_barrier(); // Copy out (the good bits of) P_solver to the existing array + // And copy any other diagnostics that are relevant to analyze the solver's performance FLOOP { parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { P_solver_all(b)(ip, k, j, i) = P_solver_s(ip, i); + // if (save_residual) { + // residual_all(b, ip, k, j, i) = residual_s(ip, i); + // } } ); } + parthenon::par_for_inner(member, ib.s, ib.e, + [&](const int& i) { + solve_norm_all(b, 0, k, j, i) = solve_norm_s(i); + solve_fail_all(b, 0, k, j, i) = solve_fail_s(i); + } + ); } ); @@ -377,8 +543,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ static AllReduce max_norm; Kokkos::Max norm_max(max_norm.val); pmb_sub_step_init->par_reduce("max_norm", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D_REDUCE { - if (norm_all(b, k, j, i) > local_result) local_result = norm_all(b, k, j, i); + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { + if (solve_norm_all(b, 0, k, j, i) > local_result) local_result = solve_norm_all(b, 0, k, j, i); } , norm_max); // Then MPI AllReduce to copy the global max to every rank @@ -396,37 +562,4 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } -#else - -TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, - MeshData *md_solver, const Real& dt) -{ - Flag("Dummy implicit solve"); - auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); - - MetadataFlag isPrimitive = pmb_sub_step_init->packages.Get("GRMHD")->Param("PrimitiveFlag"); - auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - - // Get number of variables - auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); - PackIndexMap cons_map; - auto& U_full_step_init_all = md_full_step_init->PackVariables(ordered_cons, cons_map); - const int nvar = U_full_step_init_all.GetDim(4); - - // Get number of implicit variables - auto implicit_vars = get_ordered_names(mbd_full_step_init.get(), isPrimitive, true); - PackIndexMap implicit_prims_map; - auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); - const int nfvar = P_full_step_init_implicit.GetDim(4); - - // RETURN if there aren't any implicit variables to evolve - //std::cerr << "Solve size " << nfvar << " on prim size " << nvar << std::endl; - if (nfvar == 0) { - return TaskStatus::complete; - } else { - throw std::runtime_error("Cannot evolve variables implicitly: KHARMA was compiled without implicit solver!"); - } - Flag("End dummy implicit solve"); -} - #endif diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index 64de401a..d03c67ef 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -47,6 +47,11 @@ // implicit solver stuff using namespace EMHD; +// And an odd but useful loop for ex-iharm3d code +// This requires nvar to be defined in caller! +// It is not a const/global anymore. So, use this loop carefully +#define PLOOP for(int ip=0; ip < nvar; ++ip) + // Version of PLOOP for just implicit ("fluid") variables #define FLOOP for(int ip=0; ip < nfvar; ++ip) @@ -56,19 +61,20 @@ namespace Implicit /** * Initialization. Set parameters. */ -std::shared_ptr Initialize(ParameterInput *pin); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * @brief take the per-zone implicit portion of a semi-implicit scheme * - * @param mdi the fluid state at the beginning of the step - * @param md0 the initial fluid state for this substep - * @param dudt the negative flux divergence plus explicit source terms + * @param md_full_step_init the fluid state at the beginning of the step + * @param md_sub_step_init the initial fluid state for this substep + * @param md_flux_src the negative flux divergence plus explicit source terms * @param md_solver should contain initial guess on call, contains result on return + * @param md_linesearch should contain solver prims at start, updated in the linesearch * @param dt the timestep (current substep) */ -TaskStatus Step(MeshData *mdi, MeshData *md0, MeshData *dudt, - MeshData *mc_solver, const Real& dt); +TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, + MeshData *md_linesearch, MeshData *md_solver, const Real& dt); /** * Get the names of all variables matching 'flag' in a deterministic order, placing implicitly-evolved variables first. @@ -86,7 +92,7 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P const Local& Pi, const Local& Ui, const Local& Ps, const Local& dudt_explicit, const Local& dUi, const Local& tmp, const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params, - const EMHD_parameters& emhd_params_tau,const int& nfvar, + const EMHD_parameters& emhd_params_s,const int& nfvar, const int& k, const int& j, const int& i, const Real& gam, const double& dt, Local& residual) { @@ -100,14 +106,14 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P if (m_p.Q >= 0) { // Compute new implicit source terms and time derivative source terms Real dUq, dUdP; // Don't need full array for these - EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_tau, dUq, dUdP); // dU_new + EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_s, dUq, dUdP); // dU_new // ... - 0.5*(dU_new(ip) + dUi(ip)) ... residual(m_u.Q) -= 0.5*(dUq + dUi(m_u.Q)); residual(m_u.DP) -= 0.5*(dUdP + dUi(m_u.DP)); // if (i == 11 && j == 11) { // printf("Implicit sources: "); printf("%6.5e %6.5e", dUq - dUi(m_u.Q), dUdP - dUi(m_u.DP)); printf("\n"); // } - EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params, gam, dt, k, j, i, dUq, dUdP); // dU_time + EMHD::time_derivative_sources(G, P_test, Pi, Ps, m_p, emhd_params_s, gam, dt, k, j, i, dUq, dUdP); // dU_time // ... - dU_time(ip) residual(m_u.Q) -= dUq; residual(m_u.DP) -= dUdP; @@ -117,13 +123,13 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P // Normalize Real tau, chi_e, nu_e; - EMHD::set_parameters(G, P_test, m_p, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters(G, Ps, m_p, emhd_params_s, gam, j, i, tau, chi_e, nu_e); residual(m_u.Q) *= tau; residual(m_u.DP) *= tau; if (emhd_params.higher_order_terms){ - Real rho = P_test(m_p.RHO); - Real u = P_test(m_p.UU); - Real Theta = (gam - 1.) * u / rho; + Real rho = Ps(m_p.RHO); + Real uu = Ps(m_p.UU); + Real Theta = (gam - 1.) * uu / rho; residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; @@ -142,7 +148,7 @@ template KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P_solver, const Local& P_full_step_init, const Local& U_full_step_init, const Local& P_sub_step_init, const Local& flux_src, const Local& dU_implicit, Local& tmp1, Local& tmp2, Local& tmp3, - const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_full_step_init, + const VarMap& m_p, const VarMap& m_u, const EMHD_parameters& emhd_params_solver, const EMHD_parameters& emhd_params_sub_step_init, const int& nvar, const int& nfvar, const int& k, const int& j, const int& i, const Real& jac_delta, const Real& gam, const double& dt, @@ -150,7 +156,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P { // Calculate residual of P calc_residual(G, P_solver, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual); // Use one scratchpad as the incremented prims P_delta, // one as the new residual residual_delta @@ -170,7 +176,7 @@ KOKKOS_INLINE_FUNCTION void calc_jacobian(const GRCoordinates& G, const Local& P // Compute the residual for P_delta, residual_delta calc_residual(G, P_delta, P_full_step_init, U_full_step_init, P_sub_step_init, flux_src, dU_implicit, tmp3, - m_p, m_u, emhd_params_full_step_init, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); + m_p, m_u, emhd_params_solver, emhd_params_sub_step_init, nfvar, k, j, i, gam, dt, residual_delta); // Compute forward derivatives of each residual vs the primitive col for (int row = 0; row < nfvar; row++) { diff --git a/kharma/grmhd/fixup.cpp b/kharma/inverter/fixup.cpp similarity index 58% rename from kharma/grmhd/fixup.cpp rename to kharma/inverter/fixup.cpp index 1f0f6c17..0826df89 100644 --- a/kharma/grmhd/fixup.cpp +++ b/kharma/inverter/fixup.cpp @@ -32,55 +32,53 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "grmhd.hpp" +#include "inverter.hpp" #include "floors.hpp" +#include "floors_functions.hpp" #include "flux_functions.hpp" #include "pack.hpp" -// Version of PLOOP guaranteeing specifically the 5 GRMHD fixup-amenable primitive vars +// Version of "PLOOP" guaranteeing specifically the 5 GRMHD fixup-amenable primitive vars #define NPRIM 5 #define PRIMLOOP for(int p=0; p < NPRIM; ++p) -TaskStatus GRMHD::FixUtoP(MeshBlockData *rc) +TaskStatus Inverter::FixUtoP(MeshBlockData *rc) { // We expect primitives all the way out to 3 ghost zones on all sides. // But we can only fix primitives with their neighbors. // This may actually mean we require the 4 ghost zones Parthenon "wants" us to have, // if we need to use only fixed zones. - Flag(rc, "Fixing U to P inversions"); auto pmb = rc->GetBlockPointer(); - const auto& G = pmb->coords; + // Bail if we're not enabled + if (!pmb->packages.Get("Inverter")->Param("fix_average_neighbors")) { + return TaskStatus::complete; + } - // TODO what should be averaged on a fixup? Just these core 5 prims? - // Should there be a flag to do more? + Flag(rc, "Fixing U to P inversions"); + // Only fixup the core 5 prims auto P = GRMHD::PackHDPrims(rc); GridScalar pflag = rc->Get("pflag").data; - GridScalar fflag = rc->Get("fflag").data; const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); const Real gam = pars.Get("gamma"); - const int verbose = pars.Get("verbose"); - const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); - - // Just as UtoP needs to be applied over all zones, it needs to be fixed over all zones - // TODO probably shouldn't fix or use physical ghost zones... - const IndexRange ib = rc->GetBoundsI(IndexDomain::entire); - const IndexRange jb = rc->GetBoundsJ(IndexDomain::entire); - const IndexRange kb = rc->GetBoundsK(IndexDomain::entire); - - const IndexRange ib_b = rc->GetBoundsI(IndexDomain::interior); - const IndexRange jb_b = rc->GetBoundsJ(IndexDomain::interior); - const IndexRange kb_b = rc->GetBoundsK(IndexDomain::interior); - - // TODO attempt to recover from entropy here if it's present - - pmb->par_for("fix_U_to_P", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - // Negative flags mark physical corners, which shouldn't be fixed - if (((int) pflag(k, j, i)) > InversionStatus::success) { - // Luckily fixups are rare, so we don't have to worry about optimizing this too much + // Only yell about neighbors on extreme verbosity. + // + const int flag_verbose = pmb->packages.Get("Globals")->Param("flag_verbose"); + + // UtoP is applied and fixed over all "Physical" zones -- anything in the domain, + // OR in an MPI boundary. This is because it is applied *after* the MPI sync, + // but before physical boundary zones are computed (which it should never use anyway) + + const IndexRange3 b = GetPhysicalZones(pmb, pmb->cellbounds); + + const auto& G = pmb->coords; + + pmb->par_for("fix_U_to_P", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (failed(pflag(k, j, i))) { + // Luckily fixups are rare, so we don't have to worry about optimizing this *too* much double wsum = 0., wsum_x = 0.; double sum[NPRIM] = {0.}, sum_x[NPRIM] = {0.}; // For all neighboring cells... @@ -89,12 +87,12 @@ TaskStatus GRMHD::FixUtoP(MeshBlockData *rc) for (int l = -1; l <= 1; l++) { int ii = i + l, jj = j + m, kk = k + n; // If we haven't overstepped array bounds... - if (inside(kk, jj, ii, kb, jb, ib)) { + if (inside(kk, jj, ii, b.kb, b.jb, b.ib)) { // Weight by distance double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); - // Count only the good cells, if we can - if (((int) pflag(kk, jj, ii)) == InversionStatus::success) { + // Count only the good cells (not failed AND not corner), if we can + if (!failed(pflag(kk, jj, ii))) { // Weight by distance. Note interpolated "fixed" cells stay flagged wsum += w; PRIMLOOP sum[p] += w * P(p, kk, jj, ii); @@ -110,10 +108,11 @@ TaskStatus GRMHD::FixUtoP(MeshBlockData *rc) if(wsum < 1.e-10) { // TODO probably should crash here. #ifndef KOKKOS_ENABLE_SYCL - if (verbose >= 1 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... + if (flag_verbose >= 3) printf("No neighbors were available at %d %d %d!\n", i, j, k); #endif - //PRIMLOOP P(p, k, j, i) = sum_x[p]/wsum_x; + // TODO is there a situation in which this shadow is useful, or do we ditch it? + PRIMLOOP P(p, k, j, i) = sum_x[p]/wsum_x; } else { PRIMLOOP P(p, k, j, i) = sum[p]/wsum; } @@ -121,33 +120,37 @@ TaskStatus GRMHD::FixUtoP(MeshBlockData *rc) } ); - // We need the full packs of prims/cons for p_to_u - // Pack new variables - PackIndexMap prims_map, cons_map; - auto U = GRMHD::PackMHDCons(rc, cons_map); - P = GRMHD::PackMHDPrims(rc, prims_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - // Get new sizes - const int nvar = P.GetDim(4); - - pmb->par_for("fix_U_to_P_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - if (((int) pflag(k, j, i)) > InversionStatus::success) { - apply_geo_floors(G, P, m_p, gam, k, j, i, floors); - - // Make sure to keep lockstep - // This will only be run for GRMHD, so we can call its p_to_u - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u); - - // And make sure the fixed values still abide by floors (floors keep lockstep) - // TODO Fluid Frame instead of just geo? - // int fflag_local = 0; - // fflag_local |= Floors::apply_floors(G, P, m_p, gam, k, j, i, floors, U, m_u); - // fflag_local |= Floors::apply_ceilings(G, P, m_p, gam, k, j, i, floors, U, m_u); - // fflag(k, j, i) = fflag_local; + // Re-apply floors to fixed zones + if (pmb->packages.AllPackages().count("Floors")) { + // Floor prescription from the package + const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); + + // We need the full packs of prims/cons for p_to_u + // Pack new variables + PackIndexMap prims_map, cons_map; + auto U = GRMHD::PackMHDCons(rc, cons_map); + P = GRMHD::PackMHDPrims(rc, prims_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + // Get new sizes + const int nvar = P.GetDim(4); + + // Get floor flag + GridScalar fflag = rc->Get("fflag").data; + + pmb->par_for("fix_U_to_P_floors", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (failed(pflag(k, j, i))) { + // Make sure all fixed values still abide by floors (floors keep lockstep) + // TODO Full floors instead of just geo? + apply_geo_floors(G, P, m_p, gam, k, j, i, floors); + + // Make sure to keep lockstep + // This will only be run for GRMHD, so we can call its p_to_u + GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u); + } } - } - ); + ); + } Flag(rc, "Fixed U to P inversions"); return TaskStatus::complete; diff --git a/kharma/inverter/invert_template.hpp b/kharma/inverter/invert_template.hpp new file mode 100644 index 00000000..6f511dd4 --- /dev/null +++ b/kharma/inverter/invert_template.hpp @@ -0,0 +1,89 @@ +/* + * File: invert_template.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +// This houses only the template for u_to_p. +// It is included by each implementation, and implementations +// are *then* included by inverter.hpp, +// which is the only header which should be imported outside this package. + +#include "decs.hpp" +#include "types.hpp" + +namespace Inverter { + +// Denote inverter types. Currently just one +enum class Type{none=0, onedw}; + +// Denote inversion failures (pflags) +// This enum should grow to cover any inversion algorithm +// TODO is this better off in its own space like FFlag? +enum class Status{success=0, neg_input, max_iter, bad_ut, bad_gamma, neg_rho, neg_u, neg_rhou}; + +static const std::map status_names = { + {(int) Status::neg_input, "Negative input"}, + {(int) Status::max_iter, "Hit max iter"}, + {(int) Status::bad_ut, "Velocity invalid"}, + {(int) Status::bad_gamma, "Gamma invalid"}, + {(int) Status::neg_rho, "Negative rho"}, + {(int) Status::neg_u, "Negative U"}, + {(int) Status::neg_rhou, "Negative rho & U"}}; +template +KOKKOS_INLINE_FUNCTION bool failed(T status_flag) +{ + // Return only values >0, among the failure flags + return static_cast(status_flag) > static_cast(Status::success); + // TODO if in debug mode check flag < neg_rhou +} + +/** + * Recover local primitive variables, with a one-dimensional Newton-Raphson iterative solver. + * Iteration starts from the current primitive values, and otherwse may *fail to converge* + * + * Returns a code indicating whether the solver converged (success), failed (max_iter), or + * indicating that the converged solution was unphysical (bad_ut, neg_rhou, neg_rho, neg_u) + * + * On error, will not write replacement values, leaving the previous step's values in place + * These are fixed later, in FixUtoP + * + * This is the function template: implementations are filled in in their own headers. + * Be VERY CAREFUL to define any specializations by including those headers, + * BEFORE you instantiate the template. + */ +template +KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const VariablePack& U, const VarMap& m_u, + const Real& gam, const int& k, const int& j, const int& i, + const VariablePack& P, const VarMap& m_p, + const Loci loc); +} // namespace Inverter \ No newline at end of file diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp new file mode 100644 index 00000000..316bed09 --- /dev/null +++ b/kharma/inverter/inverter.cpp @@ -0,0 +1,165 @@ +/* + * File: inverter.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "inverter.hpp" + +// This will include headers in the correct order +#include "invert_template.hpp" + +#include "reductions.hpp" + +/** + * Internal inversion fn, templated on inverter type. Calls through to templated u_to_p + * This is called with the correct template argument from BlockUtoP + */ +template +inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Filling Primitives"); + auto pmb = rc->GetBlockPointer(); + const auto& G = pmb->coords; + + PackIndexMap prims_map, cons_map; + auto U = GRMHD::PackMHDCons(rc, cons_map); + auto P = GRMHD::PackHDPrims(rc, prims_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + GridScalar pflag = rc->Get("pflag").data; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + const Real err_tol = pmb->packages.Get("Inverter")->Param("err_tol"); + const int iter_max = pmb->packages.Get("Inverter")->Param("iter_max"); + const Real stepsize = pmb->packages.Get("Inverter")->Param("stepsize"); + + // Get the primitives from our conserved versions + // Currently this runs over *all* zones, including all ghosts, even + // uninitialized zones which are still zero. We select for initialized + // zones only in the loop below, to avoid failures to converge while + // calculating primtive vars over as much of the domain as possible + // We could (did formerly) save some time here by running over + // only zones with initialized conserved variables, but the domain + // of such values is not rectangular in the current handling + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange3 b = GetPhysicalZones(pmb, bounds); + + pmb->par_for("U_to_P", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (inside(k, j, i, b.kb, b.jb, b.ib)) { + // Run over all interior zones and any initialized ghosts + pflag(k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); + } + } + ); + Flag(rc, "Filled"); +} + +std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + auto pkg = std::make_shared("Inverter"); + Params ¶ms = pkg->AllParams(); + + // TODO TODO THESE ARE NO-OPS + Real err_tol = pin->GetOrAddReal("inverter", "err_tol", 1e-8); + params.Add("err_tol", err_tol); + int iter_max = pin->GetOrAddInteger("inverter", "iter_max", 8); + params.Add("iter_max", iter_max); + Real stepsize = pin->GetOrAddReal("inverter", "stepsize", 1e-5); + params.Add("stepsize", stepsize); + + std::string inverter_name = pin->GetOrAddString("inverter", "type", "onedw"); + if (inverter_name == "onedw") { + params.Add("inverter_type", Type::onedw); + } else if (inverter_name == "none") { + params.Add("inverter_type", Type::none); + } + + bool fix_average_neighbors = pin->GetOrAddBoolean("inverter", "fix_average_neighbors", true); + params.Add("fix_average_neighbors", fix_average_neighbors); + // TODO add version attempting to recover from entropy, stuff like that + + // Flag denoting UtoP inversion failures + // Only needed if we're actually calling UtoP, but always allocated as it's retrieved often + // Needs boundary sync if treating primitive variables as fundamental + bool sync_prims = packages->Get("Driver")->Param("sync_prims"); + bool implicit_grmhd = packages->Get("GRMHD")->Param("implicit"); + Metadata m; + if (sync_prims && !implicit_grmhd) { + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); + } else { + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + } + pkg->AddField("pflag", m); + + // Don't operate if GRMHD variables are being evolved implicitly + // This package is still loaded because fixes + if (!implicit_grmhd) { + pkg->BlockUtoP = Inverter::BlockUtoP; + } + + pkg->PostStepDiagnosticsMesh = Inverter::PostStepDiagnostics; + + return pkg; +} + +void Inverter::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + // This only chooses an implementation. See BlockPerformInversion and implementations e.g. onedw.hpp + auto& type = rc->GetBlockPointer()->packages.Get("Inverter")->Param("inverter_type"); + switch(type) { + case Type::onedw: + BlockPerformInversion(rc, domain, coarse); + break; + case Type::none: + break; + } +} + +TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) +{ + Flag("Printing Floor diagnostics"); + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Options + const auto& pars = pmesh->packages.Get("Globals")->AllParams(); + const int flag_verbose = pars.Get("flag_verbose"); + + // Debugging/diagnostic info about floor and inversion flags + if (flag_verbose >= 1) { + Flag("Printing flags"); + int nflags = Reductions::CountFlags(md, "pflag", Inverter::status_names, IndexDomain::interior, flag_verbose, false); + // TODO TODO yell here if there are too many flags + } + + return TaskStatus::complete; +} diff --git a/kharma/inverter/inverter.hpp b/kharma/inverter/inverter.hpp new file mode 100644 index 00000000..2b484cbb --- /dev/null +++ b/kharma/inverter/inverter.hpp @@ -0,0 +1,81 @@ +/* + * File: inverter.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "types.hpp" + +// Implementation of u_to_p must be defined before instantiation below +// Additionally, invert_template contains the Type and Status enums +#include "invert_template.hpp" +#include "onedw.hpp" + +#include "pack.hpp" + +using namespace parthenon; + +/** + * Recover primitive variables from conserved forms. + * Currently can only use the 1D_W scheme of Noble et al. (2006), + * but this is the spot for alternate implementations + */ +namespace Inverter { + +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + +/** + * Get the primitive variables + * This just computes P, and only for the GRHD fluid varaibles rho, u, uvec. + * + * Defaults to entire domain, as the KHARMA algorithm relies on applying UtoP over ghost zones. + * + * input: U, whatever form + * output: U and P match down to inversion errors + */ +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse); + +/** + * Smooth over inversion failures, usually by averaging values of the primitive variables from each neighboring zone + * a.k.a. Diffusion? What diffusion? There is no diffusion here. + * + * LOCKSTEP: this function expects and should preserve P<->U + */ +TaskStatus FixUtoP(MeshBlockData *rc); + +/** + * Print details of any inversion failures or fixed zones + */ +TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md); + +} \ No newline at end of file diff --git a/kharma/grmhd/U_to_P.hpp b/kharma/inverter/onedw.hpp similarity index 78% rename from kharma/grmhd/U_to_P.hpp rename to kharma/inverter/onedw.hpp index d0a082d9..dcdc9ee6 100644 --- a/kharma/grmhd/U_to_P.hpp +++ b/kharma/inverter/onedw.hpp @@ -1,5 +1,5 @@ /* - * File: U_to_P.hpp + * File: onedw.hpp * * BSD 3-Clause License * @@ -33,41 +33,76 @@ */ #pragma once -#include "decs.hpp" +// General template +// We define a specialization based on the Inverter::Type parameter +#include "invert_template.hpp" +#include "grmhd_functions.hpp" #include "kharma_utils.hpp" +namespace Inverter { + +// TODO TODO MOVE AWAY // Accuracy required for U to P -#define UTOP_ERRTOL 1.e-8 +static constexpr Real UTOP_ERRTOL = 1.e-8; // Maximum iterations when doing U to P inversion -#define UTOP_ITER_MAX 8 +static constexpr int UTOP_ITER_MAX = 8; // Heuristic step size -#define DELTA 1e-5 +static constexpr Real DELTA = 1e-5; + +// Could put support fns in their own namespace, but I'm lazy +/** + * Fluid relativistic factor gamma in terms of inversion state variables of the Noble 1D_W inverter + */ +KOKKOS_INLINE_FUNCTION Real lorentz_calc_w(const Real& Bsq, const Real& D, const Real& QdB, + const Real& Qtsq, const Real& Wp) +{ + const Real QdBsq = QdB * QdB; + const Real W = Wp + D; + const Real W2 = W * W; + const Real WB = W + Bsq; -namespace GRMHD { + // This is basically inversion of eq. A7 of Mignone & McKinney + const Real utsq = -((W + WB) * QdBsq + W2 * Qtsq) / (QdBsq * (W + WB) + W2 * (Qtsq - WB * WB)); + // Catch utsq < 0 and YELL + // TODO latter number should be ~1e3*GAMMAMAX^2 + if (utsq < -1.e-15 || utsq > 1.e7) { + return -1.; // This will trigger caller to return an error immediately + } else { + return m::sqrt(1. + m::abs(utsq)); + } +} + +/** + * Error metric for Newton-Raphson step in Noble 1D_W inverter + */ KOKKOS_INLINE_FUNCTION Real err_eqn(const Real& gam, const Real& Bsq, const Real& D, const Real& Ep, const Real& QdB, - const Real& Qtsq, const Real& Wp, InversionStatus& eflag); -KOKKOS_INLINE_FUNCTION Real lorentz_calc_w(const Real& Bsq, const Real& D, const Real& QdB, - const Real& Qtsq, const Real& Wp); + const Real& Qtsq, const Real& Wp, Status& eflag) +{ + const Real W = Wp + D; + const Real gamma = lorentz_calc_w(Bsq, D, QdB, Qtsq, Wp); + if (gamma < 1) eflag = Status::bad_ut; + const Real w = W / m::pow(gamma,2); + const Real rho = D / gamma; + const Real p = (w - rho) * (gam - 1) / gam; + + return -Ep + Wp - p + 0.5 * Bsq + 0.5 * (Bsq * Qtsq - QdB * QdB) / m::pow((Bsq + W), 2); + +} /** - * Recover local primitive variables, with a one-dimensional Newton-Raphson iterative solver. - * Iteration starts from the current primitive values, and otherwse may *fail to converge* - * - * Returns a code indicating whether the solver converged (success), failed (max_iter), or - * indicating that the converged solution was unphysical (bad_ut, neg_rhou, neg_rho, neg_u) - * - * On error, will not write replacement values, leaving the previous step's values in place - * These are fixed later, in FixUtoP + * 1D_W inverter from Ressler et al. 2006. */ -KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const VariablePack& U, const VarMap& m_u, - const Real& gam, const int& k, const int& j, const int& i, const Loci loc, - const VariablePack& P, const VarMap& m_p) +template <> +KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const VariablePack& U, const VarMap& m_u, + const Real& gam, const int& k, const int& j, const int& i, + const VariablePack& P, const VarMap& m_p, + const Loci loc) { // Catch negative density if (U(m_u.RHO, k, j, i) <= 0.) { - return InversionStatus::neg_input; + return Status::neg_input; } // Convert from conserved variables to four-vectors @@ -111,13 +146,13 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari // Numerical rootfinding // Accumulator for errors in err_eqn - InversionStatus eflag = InversionStatus::success; + Status eflag = Status::success; // Initial guess from primitives: Real Wp, err; { const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, loc); - if (gamma < 1) return InversionStatus::bad_ut; + if (gamma < 1) return Status::bad_ut; const Real rho = P(m_p.RHO, k, j, i), u = P(m_p.UU, k, j, i); Wp = (rho + u + (gam - 1) * u) * gamma * gamma - rho * gamma; @@ -136,7 +171,7 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari // Attempt a Halley/Muller/Bailey/Press step const Real dedW = (errp - errm) / (Wpp - Wpm); const Real dedW2 = (errp - 2. * err + errm) / m::pow(h,2); - // TODO look at this clip & the next vs iteration convergence %s + // TODO look into changing these clipped values? const Real f = clip(0.5 * err * dedW2 / m::pow(dedW,2), -0.3, 0.3); dW = clip(-err / dedW / (1. - f), -0.5*Wp, 2.0*Wp); @@ -150,8 +185,7 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari // Not good enough? apply secant method int iter = 0; - for (iter = 0; iter < UTOP_ITER_MAX; iter++) - { + for (iter = 0; iter < UTOP_ITER_MAX; iter++) { dW = clip((Wp1 - Wp) * err / (err - err1), (Real) -0.5*Wp, (Real) 2.0*Wp); Wp1 = Wp; @@ -169,11 +203,11 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari // Uncomment to error on any bad velocity. iharm2d/3d do not do this. //if (eflag) return eflag; // Return failure to converge - if (iter == UTOP_ITER_MAX) return InversionStatus::max_iter; + if (iter == UTOP_ITER_MAX) return Status::max_iter; // Find utsq, gamma, rho from Wp const Real gamma = lorentz_calc_w(Bsq, D, QdB, Qtsq, Wp); - if (gamma < 1) return InversionStatus::bad_ut; + if (gamma < 1) return Status::bad_ut; const Real rho = D / gamma; const Real W = Wp + D; @@ -182,9 +216,9 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari const Real u = w - (rho + p); // Return without updating non-B primitives - if (rho < 0 && u < 0) return InversionStatus::neg_rhou; - else if (rho < 0) return InversionStatus::neg_rho; - else if (u < 0) return InversionStatus::neg_u; + if (rho < 0 && u < 0) return Status::neg_rhou; + else if (rho < 0) return Status::neg_rho; + else if (u < 0) return Status::neg_u; // Set primitives P(m_p.RHO, k, j, i) = rho; @@ -196,45 +230,7 @@ KOKKOS_INLINE_FUNCTION InversionStatus u_to_p(const GRCoordinates &G, const Vari P(m_p.U2, k, j, i) = pre * (Qtcon[2] + QdB * Bcon[2] / W); P(m_p.U3, k, j, i) = pre * (Qtcon[3] + QdB * Bcon[3] / W); - return InversionStatus::success; -} - -// Document this -KOKKOS_INLINE_FUNCTION Real err_eqn(const Real& gam, const Real& Bsq, const Real& D, const Real& Ep, const Real& QdB, - const Real& Qtsq, const Real& Wp, InversionStatus& eflag) -{ - const Real W = Wp + D; - const Real gamma = lorentz_calc_w(Bsq, D, QdB, Qtsq, Wp); - if (gamma < 1) eflag = InversionStatus::bad_ut; - const Real w = W / m::pow(gamma,2); - const Real rho = D / gamma; - const Real p = (w - rho) * (gam - 1) / gam; - - return -Ep + Wp - p + 0.5 * Bsq + 0.5 * (Bsq * Qtsq - QdB * QdB) / m::pow((Bsq + W), 2); - -} - -/** - * Fluid relativistic factor gamma in terms of inversion state variables - */ -KOKKOS_INLINE_FUNCTION Real lorentz_calc_w(const Real& Bsq, const Real& D, const Real& QdB, - const Real& Qtsq, const Real& Wp) -{ - const Real QdBsq = QdB * QdB; - const Real W = Wp + D; - const Real W2 = W * W; - const Real WB = W + Bsq; - - // This is basically inversion of eq. A7 of Mignone & McKinney - const Real utsq = -((W + WB) * QdBsq + W2 * Qtsq) / (QdBsq * (W + WB) + W2 * (Qtsq - WB * WB)); - - // Catch utsq < 0 and YELL - // TODO latter number should be ~1e3*GAMMAMAX^2 - if (utsq < -1.e-15 || utsq > 1.e7) { - return -1.; // This will trigger caller to return an error immediately - } else { - return m::sqrt(1. + m::abs(utsq)); - } + return Status::success; } -} // namespace GRMHD +} // namespace Inverter \ No newline at end of file diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index b2b77257..eface98e 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -44,6 +44,7 @@ #include "b_cd.hpp" #include "b_cleanup.hpp" #include "current.hpp" +#include "kharma_driver.hpp" #include "electrons.hpp" #include "implicit.hpp" #include "floors.hpp" @@ -54,29 +55,43 @@ #include "bondi.hpp" #include "boundaries.hpp" -#include "harm_driver.hpp" #include "resize_restart.hpp" #include "resize_restart_kharma.hpp" -std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin) +std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, std::shared_ptr& packages) { Flag("Initializing Globals"); - // All global mutable state. All of these and only these parameters are "mutable" - auto pkg = std::make_shared("Globals"); + // All truly global state. Mostly mutable state in order to avoid scope creep + auto pkg = std::make_shared("Globals"); Params ¶ms = pkg->AllParams(); // Current time in the simulation. For ramping things up, ramping things down, // or preventing bad outcomes at known times params.Add("time", 0.0, true); // Last step's dt (Parthenon SimTime tm.dt), which must be preserved to output jcon params.Add("dt_last", 0.0, true); - // Accumulator for maximum ctop within an MPI process - // That is, this value does NOT generally reflect the actual maximum - params.Add("ctop_max", 0.0, true); - // Maximum between MPI processes, updated after each step; that is, always a maximum. - params.Add("ctop_max_last", 0.0, true); // Whether we are computing initial outputs/timestep, or versions in the execution loop params.Add("in_loop", false, true); + // Log levels, the other acceptable global + // Made mutable in case we want to bump global log level on certain events + // TODO allow a "go_verbose" file watch + int verbose = pin->GetOrAddInteger("debug", "verbose", 0); + params.Add("verbose", verbose, true); + int flag_verbose = pin->GetOrAddInteger("debug", "flag_verbose", 0); + params.Add("flag_verbose", flag_verbose, true); + int extra_checks = pin->GetOrAddInteger("debug", "extra_checks", 0); + params.Add("extra_checks", extra_checks, true); + + // Record the problem name, just in case we need to special-case for different problems. + // Please favor packages & options before using this, and modify problem-specific code + // to be more general as it matures. + std::string problem_name = pin->GetString("parthenon/job", "problem_id"); + params.Add("problem", problem_name); + + // Update the times with callbacks + pkg->MeshPreStepUserWorkInLoop = KHARMA::MeshPreStepUserWorkInLoop; + pkg->MeshPostStepUserWorkInLoop = KHARMA::MeshPostStepUserWorkInLoop; + Flag("Initialized"); return pkg; } @@ -94,6 +109,31 @@ void KHARMA::ResetGlobals(ParameterInput *pin, Mesh *pmesh) // to be restored by Parthenon } +void KHARMA::MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + Flag("KHARMA Pre-step"); + auto& globals = pmesh->packages.Get("Globals")->AllParams(); + if (!globals.Get("in_loop")) { + globals.Update("in_loop", true); + } + globals.Update("dt_last", tm.dt); + globals.Update("time", tm.time); +} + +void KHARMA::MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + Flag("KHARMA Post-step"); + // Knowing this works took a little digging into Parthenon's EvolutionDriver. + // The order of operations after calling Step() is: + // 1. Call PostStepUserWorkInLoop and PostStepDiagnostics (this function and following) + // 2. Set the timestep tm.dt to the minimum from the EstimateTimestep calls + // 3. Generate any outputs, e.g. jcon + // Thus we preserve tm.dt (which has not yet been reset) as dt_last for Current::FillOutput + auto& globals = pmesh->packages.Get("Globals")->AllParams(); + globals.Update("dt_last", tm.dt); + globals.Update("time", tm.time); +} + void KHARMA::FixParameters(std::unique_ptr& pin) { Flag("Fixing parameters"); @@ -140,7 +180,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) // Other systems must specify x1min/max directly in the mesh region if (!pin->DoesParameterExist("parthenon/mesh", "x1min") || !pin->DoesParameterExist("parthenon/mesh", "x1max")) { - // TODO ask our coordinates about this rather than assuming exp() + // TODO ask our coordinates about this rather than assuming m::exp() bool log_r = (coordinate_transform != "null"); // Outer radius is always specified @@ -159,14 +199,15 @@ void KHARMA::FixParameters(std::unique_ptr& pin) } } else { int nx1 = pin->GetInteger("parthenon/mesh", "nx1"); - Real a = pin->GetReal("coordinates", "a"); - GReal Rhor = 1 + sqrt(1 - a*a); - GReal x1hor = log_r ? log(Rhor) : Rhor; + const Real a = pin->GetReal("coordinates", "a"); + // Allow overriding Rhor for bondi_viscous problem + const GReal Rhor = pin->GetOrAddReal("coordinates", "Rhor", 1 + sqrt(1 - a*a)); + const GReal x1hor = log_r ? log(Rhor) : Rhor; // Set Rin such that we have 5 zones completely inside the event horizon // If xeh = log(Rhor), xin = log(Rin), and xout = log(Rout), // then we want xeh = xin + 5.5 * (xout - xin) / N1TOT: - GReal x1min = (nx1 * x1hor / 5.5 - x1max) / (-1. + nx1 / 5.5); + const GReal x1min = (nx1 * x1hor / 5.5 - x1max) / (-1. + nx1 / 5.5); if (x1min < 0.0) { throw std::invalid_argument("Not enough radial zones were specified to put 5 zones inside EH!"); } @@ -222,183 +263,109 @@ void KHARMA::FixParameters(std::unique_ptr& pin) Flag("Fixed"); } -Packages_t KHARMA::ProcessPackages(std::unique_ptr& pin) +TaskStatus KHARMA::AddPackage(std::shared_ptr& packages, + std::function(ParameterInput*, std::shared_ptr&)> package_init, + ParameterInput *pin) +{ + packages->Add(package_init(pin, packages)); + return TaskStatus::complete; +} + +Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) { // See above FixParameters(pin); Flag("Initializing packages"); - // Then put together what we're supposed to - Packages_t packages; - - // Read all package enablements first so we can set their defaults here, - // before any packages are initialized: thus they can know the full list - std::string b_field_solver = pin->GetOrAddString("b_field", "solver", "flux_ct"); - // Enable b_cleanup package if we want it explicitly - bool b_cleanup_package = pin->GetOrAddBoolean("b_cleanup", "on", false); - // OR if we need it for resizing a dump - bool is_resize = pin->GetString("parthenon/job", "problem_id") == "resize_restart"; - // OR if we want an initial cleanup pass for some other reason - bool initial_cleanup = pin->GetOrAddBoolean("b_field", "initial_cleanup", false); - // These were separated to make sure that the preference keys are initialized, - // since short-circuiting prevented that when they were listed below - bool b_cleanup = b_cleanup_package || is_resize || initial_cleanup; - - // TODO enable this iff jcon is in the list of outputs - bool add_jcon = pin->GetOrAddBoolean("GRMHD", "add_jcon", true); - bool do_electrons = pin->GetOrAddBoolean("electrons", "on", false); - bool do_reductions = pin->GetOrAddBoolean("reductions", "on", true); - bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); - bool do_wind = pin->GetOrAddBoolean("wind", "on", false); - - // Set the default driver all the way up here, so packages know how to flag - // prims vs cons (imex stepper syncs prims, but it's the packages' job to mark them) - std::string driver_type; - if (do_emhd) { - // Default to implicit step for EMHD - driver_type = pin->GetOrAddString("driver", "type", "imex"); - } else { - driver_type = pin->GetOrAddString("driver", "type", "harm"); - } - // Initialize the implicit timestepping package early so we can mark fields to be - // updated implicitly vs explicitly - if (driver_type == "imex") { - packages.Add(Implicit::Initialize(pin.get())); + // Allocate the packages list as a shared pointer, to be updated in various tasks + auto packages = std::make_shared(); + + Flag("Building task collection"); + TaskCollection tc; + auto& tr = tc.AddRegion(1); + auto& tl = tr[0]; + TaskID t_none(0); + // The globals package will never have dependencies + auto t_globals = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMA::InitializeGlobals, pin.get()); + // Driver package is the foundation + auto t_driver = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMADriver::Initialize, pin.get()); + // Floors package has no dependencies + if (!pin->GetOrAddBoolean("floors", "disable_floors", false)) { + auto t_floors = tl.AddTask(t_none, KHARMA::AddPackage, packages, Floors::Initialize, pin.get()); } - - // Global variables "package." Mutable global state Parthenon doesn't keep for us. - // Always enable. - packages.Add(KHARMA::InitializeGlobals(pin.get())); - - // Lots of common functions and variables are still in the GRMHD package, - // always initialize it first among physics stuff - packages.Add(GRMHD::Initialize(pin.get(), packages)); - - // We'll also always want the floors package, even if floors are disabled - packages.Add(Floors::Initialize(pin.get())); - - // B field solvers, to ensure divB == 0. - if (b_field_solver == "none") { + // GRMHD needs globals to mark packages + auto t_grmhd = tl.AddTask(t_globals | t_driver, KHARMA::AddPackage, packages, GRMHD::Initialize, pin.get()); + // Inverter (TODO: split out fixups, then don't load this when GRMHD isn't loaded) + auto t_inverter = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Inverter::Initialize, pin.get()); + + // B field solvers, to ensure divB ~= 0. + // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference + auto t_b_field = t_none; + std::string b_field_solver = pin->GetOrAddString("b_field", "solver", "flux_ct"); + if (b_field_solver == "none" || b_field_solver == "b_cleanup") { // Don't add a B field } else if (b_field_solver == "constraint_damping" || b_field_solver == "b_cd") { // Constraint damping, probably only useful for non-GR MHD systems - packages.Add(B_CD::Initialize(pin.get(), packages)); + t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_CD::Initialize, pin.get()); } else { // Don't even error on bad values. This is probably what you want - packages.Add(B_FluxCT::Initialize(pin.get(), packages)); - } - // Additional cleanup on B field. - // Can be enabled with or without a per-step solver, currently used for restart resizing - if (b_cleanup) { - packages.Add(B_Cleanup::Initialize(pin.get(), packages)); + t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_FluxCT::Initialize, pin.get()); } - // Unless both a field solver and cleanup routine are disabled, - // there is some form of B field present/declared. - bool b_field_exists = !(b_field_solver == "none" && !b_cleanup); - - // Add jcon, so long as there's a field to calculate it from - if (add_jcon && b_field_exists) { - packages.Add(Current::Initialize(pin.get())); - } - - // Electrons are boring but not impossible without a B field - if (do_electrons) { - packages.Add(Electrons::Initialize(pin.get(), packages)); + // Cleanup for the B field, using an elliptic solve for eliminating divB + // Almost always loaded explicitly in addition to another transport, just for cleaning at simulation start + // Enable b_cleanup package if we want it explicitly + bool b_cleanup_package = pin->GetOrAddBoolean("b_cleanup", "on", (b_field_solver == "b_cleanup")); + // OR if we need it for resizing a dump + bool is_resize = pin->GetString("parthenon/job", "problem_id") == "resize_restart" && + !pin->GetOrAddBoolean("resize_restart", "skip_b_cleanup", false); + // OR if we ordered an initial cleanup pass for some other reason + bool initial_cleanup = pin->GetOrAddBoolean("b_field", "initial_cleanup", false); + bool use_b_cleanup = b_cleanup_package || is_resize || initial_cleanup; + pin->SetBoolean("b_cleanup", "on", use_b_cleanup); + auto t_b_cleanup = t_none; + if (use_b_cleanup) { + t_b_cleanup = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_Cleanup::Initialize, pin.get()); + if (t_b_field == t_none) t_b_field = t_b_cleanup; } - if (do_reductions) { - packages.Add(Reductions::Initialize(pin.get())); + // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it) + // Since it is never required to restart, this is the only time we'd write (hence, need) it + // TODO use GetVector & == when available + if (FieldIsOutput(pin.get(), "jcon") && t_b_field != t_none) { + auto t_current = tl.AddTask(t_b_field, KHARMA::AddPackage, packages, Current::Initialize, pin.get()); } - - if (do_emhd) { - packages.Add(EMHD::Initialize(pin.get(), packages)); + // Electrons are usually boring but not impossible without a B field (TODO add a test?) + if (pin->GetOrAddBoolean("electrons", "on", false)) { + auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Electrons::Initialize, pin.get()); } - - if (do_wind) { - packages.Add(Wind::Initialize(pin.get())); + if (pin->GetOrAddBoolean("emhd", "on", false)) { + auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, EMHD::Initialize, pin.get()); } - - Flag("Finished initializing packages"); - return std::move(packages); -} - - -// TODO decide on a consistent implementation of foreach packages -> do X -void KHARMA::FillDerivedDomain(std::shared_ptr> &rc, IndexDomain domain, int coarse) -{ - Flag(rc.get(), "Filling derived variables on boundaries"); - // We need to re-fill the "derived" (primitive) variables on the physical boundaries, - // since we already called "FillDerived" before the ghost zones were initialized - // This does *not* apply to the GRMHD variables, as their primitive values are filled - // during the boundary call - auto pmb = rc->GetBlockPointer(); - // if (pmb->packages.AllPackages().count("GRMHD")) - // GRMHD::UtoP(rc.get(), domain, coarse); - if (pmb->packages.AllPackages().count("B_FluxCT")) - B_FluxCT::UtoP(rc.get(), domain, coarse); - if (pmb->packages.AllPackages().count("B_CD")) - B_CD::UtoP(rc.get(), domain, coarse); - if (pmb->packages.AllPackages().count("Electrons")) - Electrons::UtoP(rc.get(), domain, coarse); - - Flag(rc.get(), "Filled"); -} - -void KHARMA::PreStepMeshUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) -{ - if (!pmesh->packages.Get("Globals")->Param("in_loop")) { - pmesh->packages.Get("Globals")->UpdateParam("in_loop", true); + if (pin->GetOrAddBoolean("wind", "on", false)) { + auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Wind::Initialize, pin.get()); } -} -void KHARMA::PostStepMeshUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) -{ - // Knowing this works took a little digging into Parthenon's EvolutionDriver. - // The order of operations after calling Step() is: - // 1. Call PostStepUserWorkInLoop and PostStepDiagnostics (this function and following) - // 2. Set the timestep tm.dt to the minimum from the EstimateTimestep calls - // 3. Generate any outputs, e.g. jcon - // Thus we preserve tm.dt (which has not yet been reset) as dt_last for Current::FillOutput - pmesh->packages.Get("Globals")->UpdateParam("dt_last", tm.dt); - pmesh->packages.Get("Globals")->UpdateParam("time", tm.time); - - // ctop_max has fewer rules. It's just convenient to set here since we're assured of no MPI hangs - // Since it involves an MPI sync, we only keep track of this when we need it - if (pmesh->packages.AllPackages().count("B_CD")) { - static AllReduce ctop_max_last_r; - ctop_max_last_r.val = pmesh->packages.Get("Globals")->Param("ctop_max"); - ctop_max_last_r.StartReduce(MPI_MAX); - while (ctop_max_last_r.CheckReduce() == TaskStatus::incomplete); - pmesh->packages.Get("Globals")->UpdateParam("ctop_max_last", ctop_max_last_r.val); - pmesh->packages.Get("Globals")->UpdateParam("ctop_max", 0.0); + // Execute the whole collection (just in case we do something fancy?) + Flag("Running package loading tasks"); + while (!tr.Execute()); // TODO this will inf-loop on error + + // The boundaries package may need to know variable counts for allocating memory, + // so we initialize it after the main dependency tree + // TODO only init if at least one boundary is "user" + KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); + + // Load the implicit package *last*, if there are any variables which need implicit evolution + // TODO print what we're doing here & do some sanity checks, if verbose + int n_implicit = packages->Get("Driver")->Param("n_implicit_vars"); + if (n_implicit > 0) { + KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); + // Implicit evolution must use predictor-corrector i.e. "vl2" integrator + pin->SetString("parthenon/time", "integrator", "vl2"); } -} -void KHARMA::PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) -{ - // Parthenon's version of this has a bug, but I would probably subclass it anyway. - // very useful to have a single per-step spot to control any routine print statements - const auto& md = pmesh->mesh_data.GetOrAdd("base", 0).get(); - if (md->NumBlocks() > 0) { - for (auto &package : pmesh->packages.AllPackages()) { - package.second->PostStepDiagnostics(tm, md); - } - } -} + -void KHARMA::FillOutput(MeshBlock *pmb, ParameterInput *pin) -{ - Flag("Filling output"); - // Rewrite this and the above as a callback registration - if (pmb->packages.AllPackages().count("Current")) - Current::FillOutput(pmb, pin); - if (pmb->packages.AllPackages().count("B_FluxCT")) - B_FluxCT::FillOutput(pmb, pin); - if (pmb->packages.AllPackages().count("B_CD")) - B_CD::FillOutput(pmb, pin); - if (pmb->packages.AllPackages().count("Electrons")) - Electrons::FillOutput(pmb, pin); - Flag("Filled"); + Flag("Finished initializing all packages"); // TODO print full package list way up here? + return std::move(*packages); } - diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index ab6ec290..20afbadd 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -39,55 +39,68 @@ * General preferences for KHARMA. Anything semi-driver-independent, like loading packages, etc. */ namespace KHARMA { -/** - * This function messes with all Parthenon's parameters in-place before we hand them to the Mesh, - * so that KHARMA decks can omit/infer some things parthenon needs. - * This includes boundaries in spherical coordinates, coordinate system translations, etc. - * This function also handles setting parameters from restart files - */ -void FixParameters(std::unique_ptr& pin); /** - * Load any packages specified in the input parameters + * Initialize a "package" of global variables: quantities needed randomly in several places. + * Some are physical e.g. time, step times. Others track program state like initialization vs. stepping. */ -Packages_t ProcessPackages(std::unique_ptr& pin); +std::shared_ptr InitializeGlobals(ParameterInput *pin, std::shared_ptr& packages); /** - * Initialize a "package" (StateDescriptor) of global variables, quantities needed randomly in several places, like: - * dt_last, last step time - * ctop_max, maximum speed on the grid - * in_loop, whether one step has been completed (for e.g. EstimateTimestep) + * Version for restarts, called in PostInitialize if we're restarting from a Parthenon restart file + * Note this doesn't do very much -- Parthenon is good about restoring things the way we'd like */ -std::shared_ptr InitializeGlobals(ParameterInput *pin); -// Version for restarts, called in PostInitialize if we're restarting from a Parthenon restart file void ResetGlobals(ParameterInput *pin, Mesh *pmesh); /** - * Imitate Parthenon's FillDerived call, but on only a subset of zones defined by 'domain' - * Used for boundary calls, see boundaries.cpp + * Update variables in Globals package based on Parthenon state incl. SimTime struct + */ +void MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +/** + * Update variables in Globals package based on Parthenon state incl. SimTime struct */ -void FillDerivedDomain(std::shared_ptr> &rc, IndexDomain domain, int coarse); +void MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); /** - * Code-wide work before each step in the fluid evolution. Currently just updates globals. + * Task to add a package. Lets us queue up all the packages we want in a task list, *then* load them + * with correct dependencies and everything! */ -void PreStepMeshUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +TaskStatus AddPackage(std::shared_ptr& packages, + std::function(ParameterInput*, std::shared_ptr&)> package_init, + ParameterInput *pin); /** - * Code-wide work after each step in the fluid evolution. Currently just updates globals. + * This function messes with all Parthenon's parameters in-place before we hand them to the Mesh, + * so that KHARMA decks can omit/infer some things parthenon needs. + * This includes boundaries in spherical coordinates, coordinate system translations, etc. + * This function also handles setting parameters from restart files */ -void PostStepMeshUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void FixParameters(std::unique_ptr& pin); /** - * Calculate and print diagnostics after each step. Currently: - * GRMHD: pflags & fflags, negative values in rho,u, ctop of 0 or NaN - * B fields: MaxDivB + * Load any packages specified in the input parameters */ -void PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +Packages_t ProcessPackages(std::unique_ptr& pin); /** - * Fill any arrays that are calculated only for output, e.g. divB, jcon, etc. - * This calls the FillOutput function of each package + * Check whether a given field is anywhere in outputs. + * Used to avoid calculating expensive fields (jcon, divB) if they + * will not even be written. */ -void FillOutput(MeshBlock *pmb, ParameterInput *pin); +inline bool FieldIsOutput(ParameterInput *pin, std::string name) +{ + InputBlock *pib = pin->pfirst_block; + while (pib != nullptr) { + if (pib->block_name.compare(0, 16, "parthenon/output") == 0 && + pin->DoesParameterExist(pib->block_name, "variables")) { + std::string allvars = pin->GetString(pib->block_name, "variables"); + if (allvars.find(name) != std::string::npos) { + return true; + } + } + pib = pib->pnext; // move to next input block name + } + return false; +} + } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp new file mode 100644 index 00000000..587173b2 --- /dev/null +++ b/kharma/kharma_package.cpp @@ -0,0 +1,229 @@ +/* + * File: kharma_package.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kharma_package.hpp" + +#include "types.hpp" + +// PHYSICS-RELATED +// TODO take & accumulate TaskStatus? Useful for ::incomplete if we ever want to do that +// TODO Several of these are unused & commented, but will be used as I meshify different drivers. +// Then, I can work on meshifying packages by degrees + +TaskStatus Packages::FixFlux(MeshData *md) +{ + Flag("Fixing fluxes on mesh"); + for (auto &package : md->GetMeshPointer()->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->FixFlux != nullptr) + kpackage->FixFlux(md); + } + } + Flag("Fixed"); + return TaskStatus::complete; +} + +// TaskStatus Packages::BlockPtoU(MeshBlockData *mbd, IndexDomain domain, bool coarse) +// { +// Flag("Getting conserved variables on block"); +// for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { +// if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { +// if (kpackage->BlockPtoU != nullptr) +// kpackage->BlockPtoU(mbd, domain, coarse); +// } +// } +// Flag("Done"); +// return TaskStatus::complete; +// } +// TaskStatus Packages::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) +// { +// for (int i=0; i < md->NumBlocks(); ++i) +// PtoU(md->GetBlockData(i).get(), domain, coarse); +// return TaskStatus::complete; +// } + +TaskStatus Packages::BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse) +{ + Flag("Recovering primitive variables"); + for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->BlockUtoP != nullptr) + kpackage->BlockUtoP(mbd, domain, coarse); + } + } + Flag("Recovered"); + return TaskStatus::complete; +} +TaskStatus Packages::MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) +{ + for (int i=0; i < md->NumBlocks(); ++i) + BlockUtoP(md->GetBlockData(i).get(), domain, coarse); + return TaskStatus::complete; +} + +TaskStatus Packages::BlockUtoPExceptMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Recovering primitive variables on boundaries"); + // We need to re-fill the primitive variables on the physical boundaries, + // since the driver has already called UtoP for the step. + // However, this does *not* apply to the GRMHD variables, as the boundary call + // used/filled their primitive values. Instead, they will need a PtoU call + auto pmb = rc->GetBlockPointer(); + for (auto &package : pmb->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (package.first != "GRMHD" && package.first != "Inverter") { + if (kpackage->BlockUtoP != nullptr) + kpackage->BlockUtoP(rc, domain, coarse); + } + } + } + Flag(rc, "Recovered"); + return TaskStatus::complete; +} +TaskStatus Packages::MeshUtoPExceptMHD(MeshData *md, IndexDomain domain, bool coarse) +{ + for (int i=0; i < md->NumBlocks(); ++i) + BlockUtoPExceptMHD(md->GetBlockData(i).get(), domain, coarse); + return TaskStatus::complete; +} + +TaskStatus Packages::AddSource(MeshData *md, MeshData *mdudt) +{ + Flag("Adding source terms"); + for (auto &package : md->GetMeshPointer()->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->AddSource != nullptr) + kpackage->AddSource(md, mdudt); + } + } + Flag("Added"); + return TaskStatus::complete; +} + +TaskStatus Packages::BlockApplyPrimSource(MeshBlockData *rc) +{ + Flag("Applying primitive source terms"); + for (auto &package : rc->GetBlockPointer()->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->BlockApplyPrimSource != nullptr) + kpackage->BlockApplyPrimSource(rc); + } + } + Flag("Added"); + return TaskStatus::complete; +} + +// TODO will these need to be done on coarse versions? +TaskStatus Packages::BlockApplyFloors(MeshBlockData *mbd, IndexDomain domain) +{ + Flag("Applying floors"); + auto pmb = mbd->GetBlockPointer(); + auto pkgs = pmb->packages.AllPackages(); + + // Apply the version from "Floors" package first + if (pkgs.count("Floors")) { + KHARMAPackage *kpackage = dynamic_cast(pkgs.at("Floors").get()); + // We *want* to crash on null deref if this kpackage is null, something would be wrong + if (kpackage->BlockApplyFloors != nullptr) + kpackage->BlockApplyFloors(mbd, domain); + } + // Then anything else + for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { + if (package.first != "Floors") { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->BlockApplyFloors != nullptr) + kpackage->BlockApplyFloors(mbd, domain); + } + } + } + Flag("Applied"); + + return TaskStatus::complete; +} +TaskStatus Packages::MeshApplyFloors(MeshData *md, IndexDomain domain) +{ + for (int i=0; i < md->NumBlocks(); ++i) + BlockApplyFloors(md->GetBlockData(i).get(), domain); + return TaskStatus::complete; +} + +// GENERAL CALLBACKS +// TODO this will need to be mesh'd too +void Packages::UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin) +{ + Flag("Filling output arrays"); + for (auto &package : pmb->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->BlockUserWorkBeforeOutput != nullptr) + kpackage->BlockUserWorkBeforeOutput(pmb, pin); + } + } + Flag("Filled"); +} + +void Packages::PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + Flag("Pre-step package work"); + for (auto &package : pmesh->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->MeshPreStepUserWorkInLoop != nullptr) + kpackage->MeshPreStepUserWorkInLoop(pmesh, pin, tm); + } + } + Flag("Done pre-step package work"); +} + +void Packages::PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + Flag("Post-step package work"); + for (auto &package : pmesh->packages.AllPackages()) { + if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { + if (kpackage->MeshPostStepUserWorkInLoop != nullptr) + kpackage->MeshPostStepUserWorkInLoop(pmesh, pin, tm); + } + } +} + +void Packages::PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + // Parthenon's version of this has a bug, but I would probably subclass it anyway. + // very useful to have a single per-step spot to control any routine print statements + const auto& md = pmesh->mesh_data.GetOrAdd("base", 0).get(); + if (md->NumBlocks() > 0) { + for (auto &package : pmesh->packages.AllPackages()) { + if (package.second->PostStepDiagnosticsMesh != nullptr) + package.second->PostStepDiagnosticsMesh(tm, md); + } + } +} + diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp new file mode 100644 index 00000000..9748afad --- /dev/null +++ b/kharma/kharma_package.hpp @@ -0,0 +1,163 @@ +/* + * File: kharma_package.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include + +using namespace parthenon; + +/** + * Adds a number of useful callbacks which KHARMA packages might want to take advantage of, + * which may not make sense to add to the Parthenon StateDescriptor struct upstream + * (or which simply haven't been added yet, for whatever reason) + * + * KHARMA packages which handle variables evolved with Flux:: must additionally provide + * some device-side functions. + * 1. Package::prim_to_flux -- various calling conventions, see grmhd_functions.hpp + * + */ +class KHARMAPackage : public StateDescriptor { + public: + KHARMAPackage(std::string name) : StateDescriptor(name) {} + + // PHYSICS + // Recovery of primitive variables from conserved. + // These can be host-side functions because they are not called from the Uberkernel -- + // rather, they are called on zone center values once per step only. + // Called by various Flux::*UtoP* + std::function*, IndexDomain, bool)> BlockUtoP = nullptr; + std::function*, IndexDomain, bool)> MeshUtoP = nullptr; + + // Maybe at some point we'll have + // Since Flux::prim_to_flux must cover everything, it's not worth splitting now + //std::function*, IndexDomain, bool)> BlockPtoU = nullptr; + + // Source term to add to the conserved variables during each step + std::function*, MeshData*)> AddSource = nullptr; + + // Source term to apply to primitive variables, needed for some problems in order + // to control dissipation (Hubble, turbulence). + // Must be applied over entire domain! + std::function*)> BlockApplyPrimSource = nullptr; + + // Apply any fixes after the initial fluxes are calculated + std::function*)> FixFlux = nullptr; + + // Apply any floors or limiters specific to the package (that is, on the package's variables) + // Called by Floors::*ApplyFloors + std::function*, IndexDomain)> BlockApplyFloors = nullptr; + std::function*, IndexDomain)> MeshApplyFloors = nullptr; + + // CONVENIENCE + // Anything to be done before each step begins -- currently just updating global "in_loop" + std::function MeshPreStepUserWorkInLoop = nullptr; + // Anything to be done after every step is fully complete -- usually reductions or preservation of variables + std::function MeshPostStepUserWorkInLoop = nullptr; + + // Anything to be done just before any outputs (dump files, restarts, history files) are made + // Usually for filling output-only variables + // TODO Add MeshUserWorkBeforeOutput to Parthenon + std::function BlockUserWorkBeforeOutput = nullptr; + + // BOUNDARIES + // Currently only used by the "boundaries" package, or overridden during problem initialization + // Note these functions take the boundary domain as an argument, so you can assign the same function to multiple boundaries. + std::function>&, IndexDomain, bool)> KHARMAInnerX1Boundary = nullptr; + std::function>&, IndexDomain, bool)> KHARMAOuterX1Boundary = nullptr; + std::function>&, IndexDomain, bool)> KHARMAInnerX2Boundary = nullptr; + std::function>&, IndexDomain, bool)> KHARMAOuterX2Boundary = nullptr; +}; + +/** + * Implement the above callbacks + */ +namespace Packages { + +/** + * Any "fixes" to the fluxes through zone faces calculated by GetFlux. + * These are all package-defined, with boundary fluxes and magnetic field transport + * being the big cases. + */ +TaskStatus FixFlux(MeshData *md); + +/** + * + */ +TaskStatus BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); +TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); + +/** + * Fill the primitive variables P using the conserved U, for every package except "GRMHD." + * That is, currently, B fields and electrons. + * This is used for KHARMA's boundaries, which act on (e.g., reflect or outflow) the + * conserved variables where available (and thus must recover primitives), + * but act on primitive rho,u,uvec and must leave those alone. + */ +TaskStatus BlockUtoPExceptMHD(MeshBlockData *rc, IndexDomain domain, bool coarse=false); +TaskStatus MeshUtoPExceptMHD(MeshData *md, IndexDomain domain, bool coarse=false); + +/** + * Fill all conserved variables (U) from primitive variables (P), over a whole block + */ +// TaskStatus BlockPtoU(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); + +/** + * Add any source terms to the conserved variables. Applied over the interior/physical zones only, as these + * are the only ones well-defined in the only place this function is called. + */ +TaskStatus AddSource(MeshData *md, MeshData *mdudt); + +/** + * Add any source terms to the primitive variables. Applied directly rather than adding to a derivative. + */ +TaskStatus BlockApplyPrimSource(MeshBlockData *rc); + +/** + * Apply all floors, including any package-specific limiters. + * This function respects "disable_floors". + * + * LOCKSTEP: this function respects P and returns consistent P<->U + */ +TaskStatus BlockApplyFloors(MeshBlockData *mbd, IndexDomain domain); +TaskStatus MeshApplyFloors(MeshData *md, IndexDomain domain); + +// These are already Parthenon global callbacks -- see their documentation +// I define them here so I can pass them on to packages +void UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin); +void PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +} \ No newline at end of file diff --git a/kharma/kharma_utils.hpp b/kharma/kharma_utils.hpp index 10da57cb..f662454f 100644 --- a/kharma/kharma_utils.hpp +++ b/kharma/kharma_utils.hpp @@ -1,5 +1,35 @@ -/* - * Everything that doesn't fit somewhere else. General C/C++ convenience functions. +/* + * File: kharma_utils.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once @@ -9,6 +39,54 @@ #include #include +/* + * General C/C++ convenience functions, anything not specific to KHARMA's datatypes + */ + +/** + * String formatting in errors. + * Courtesy https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf + */ +template +std::string string_format( const std::string& format, Args ... args ) +{ + size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' + if( size <= 0 ){ throw std::runtime_error( "Error during formatting." ); } + std::unique_ptr buf( new char[ size ] ); + snprintf( buf.get(), size, format.c_str(), args ... ); + return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside +} +// If we need to disable it, set this version instead +// template +// std::string string_format( const std::string& format, Args ... args ) +// { return std::string(""); } + +/** + * Formatted printing functions for looking at vectors, tensors (in future, array areas?) + * Optionally kill the program if a NaN value is encountered. + */ +KOKKOS_INLINE_FUNCTION void print_matrix(const std::string name, const double g[GR_DIM][GR_DIM], bool kill_on_nan=false) +{ + // Print a name and a matrix + printf("%s:\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n%g\t%g\t%g\t%g\n", name.c_str(), + g[0][0], g[0][1], g[0][2], g[0][3], g[1][0], g[1][1], g[1][2], + g[1][3], g[2][0], g[2][1], g[2][2], g[2][3], g[3][0], g[3][1], + g[3][2], g[3][3]); + + if (kill_on_nan) { + // Additionally kill things if/when we hit NaNs + DLOOP2 if (m::isnan(g[mu][nu])) exit(-1); + } +} +KOKKOS_INLINE_FUNCTION void print_vector(const std::string name, const double v[GR_DIM], bool kill_on_nan=false) +{ + printf("%s: %g\t%g\t%g\t%g\n", name.c_str(), v[0], v[1], v[2], v[3]); + + if (kill_on_nan) { + DLOOP2 if (m::isnan(v[nu])) exit(-1); + } +} + /** * This takes a number n and clips it to lie on the real line between 'lower' and 'upper' * If n is NaN, it returns the *lower* bound, unless this is also NaN, in which case it returns the upper. @@ -27,7 +105,7 @@ KOKKOS_INLINE_FUNCTION T clip(const T& n, const T& lower, const T& upper) //if (n > upper) printf("Clip %g to %g\n", n, upper); //if (n < lower) printf("Clip %g to %g\n", n, lower); #endif - return m::min(m::max(lower, n), upper); + return m::min(m::max(lower, n), upper); } // Version which "bounces" any excess over the bounds, useful for the polar coordinate template @@ -42,10 +120,13 @@ KOKKOS_INLINE_FUNCTION T excise(const T& n, const T& center, const T& range) return (m::abs(n - center) > range) ? n : ( (n > center) ? center + range : center - range ); } +/** + * Every physics code ends up with something like this + */ template KOKKOS_INLINE_FUNCTION T close_to(const T& x, const T& y, const Real& rel_tol=1e-8, const Real& abs_tol=1e-8) { - return ((abs(x - y) / y) < rel_tol) || (abs(x) < abs_tol && abs(y) < abs_tol); + return ((m::abs(x - y) / y) < rel_tol) || (m::abs(x) < abs_tol && m::abs(y) < abs_tol); } // Quickly zero n elements of an array diff --git a/kharma/main.cpp b/kharma/main.cpp index 32a3f138..9430a0ac 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -36,10 +36,8 @@ #include "decs.hpp" #include "boundaries.hpp" -#include "imex_driver.hpp" -#include "harm_driver.hpp" +#include "kharma_driver.hpp" #include "kharma.hpp" -#include "mpi.hpp" #include "post_initialize.hpp" #include "problem.hpp" #include "emhd/conducting_atmosphere.hpp" @@ -100,20 +98,22 @@ int main(int argc, char *argv[]) { ParthenonManager pman; + // A couple of callbacks are KHARMA-wide single functions pman.app_input->ProcessPackages = KHARMA::ProcessPackages; pman.app_input->ProblemGenerator = KHARMA::ProblemGenerator; - pman.app_input->MeshBlockUserWorkBeforeOutput = KHARMA::FillOutput; - pman.app_input->PreStepMeshUserWorkInLoop = KHARMA::PreStepMeshUserWorkInLoop; - pman.app_input->PostStepMeshUserWorkInLoop = KHARMA::PostStepMeshUserWorkInLoop; - pman.app_input->PostStepDiagnosticsInLoop = KHARMA::PostStepDiagnostics; + // A few are passed on to be implemented by packages as they see fit + pman.app_input->MeshBlockUserWorkBeforeOutput = Packages::UserWorkBeforeOutput; + pman.app_input->PreStepMeshUserWorkInLoop = Packages::PreStepUserWorkInLoop; + pman.app_input->PostStepMeshUserWorkInLoop = Packages::PostStepUserWorkInLoop; + pman.app_input->PostStepDiagnosticsInLoop = Packages::PostStepDiagnostics; // Registering KHARMA's boundary functions here doesn't mean they will *always* run: - // all periodic boundary conditions are handled by Parthenon. - // KHARMA sets the correct options automatically for spherical coordinate systems. - pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x1] = KBoundaries::InnerX1; - pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x1] = KBoundaries::OuterX1; - pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x2] = KBoundaries::InnerX2; - pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x2] = KBoundaries::OuterX2; + // all periodic & internal boundary conditions are handled by Parthenon. + // KHARMA sets the correct boundaries automatically for spherical coordinate systems. + pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x1] = KBoundaries::ApplyBoundaryTemplate; + pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x1] = KBoundaries::ApplyBoundaryTemplate; + pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x2] = KBoundaries::ApplyBoundaryTemplate; + pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x2] = KBoundaries::ApplyBoundaryTemplate; // Parthenon init includes Kokkos, MPI, parses parameters & cmdline, // then calls ProcessPackages and ProcessProperties, then constructs the Mesh @@ -140,28 +140,29 @@ int main(int argc, char *argv[]) auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc auto papp = pman.app_input.get(); // The list of callback functions specified above - // Add magnetic field to the problem, initialize ghost zones. - // Implemented separately outside of MeshBlock since - // this usually involves global reductions for normalization - if(MPIRank0()) + if(MPIRank0()) { + // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation + if (pmesh->packages.Get("Globals")->Param("verbose") > 0) { + // Print a list of all loaded packages. Surprisingly useful for debugging init logic + std::cout << "Packages in use: " << std::endl; + for (auto package : pmesh->packages.AllPackages()) { + std::cout << package.first << std::endl; + } + std::cout << std::endl; + } std::cout << "Running post-initialization tasks..." << std::endl; + } + // PostInitialize: Add magnetic field to the problem, initialize ghost zones. + // Any init which may be run even when restarting, or requires all + // MeshBlocks to be initialized already auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_restart = (prob == "resize_restart") || pman.IsRestart(); - //bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); // Hyerin - bool is_resize = (prob == "resize_restart") && !pman.IsRestart(); - KHARMA::PostInitialize(pin, pmesh, is_restart, is_resize); + bool is_restart = (prob == "resize_restart") || (prob == "resize_restart") || pman.IsRestart(); + KHARMA::PostInitialize(pin, pmesh, is_restart); Flag("Post-initialization completed"); // Construct a temporary driver purely for parameter parsing - auto driver_type = pin->GetString("driver", "type"); - if (driver_type == "harm") { - HARMDriver driver(pin, papp, pmesh); - } else if (driver_type == "imex") { - ImexDriver driver(pin, papp, pmesh); - } else { - throw std::invalid_argument("Expected driver type to be harm or imex!"); - } + KHARMADriver driver(pin, papp, pmesh); // We could still have set parameters during driver initialization // Note the order here is *extremely important* as the first statement has a @@ -177,7 +178,7 @@ int main(int argc, char *argv[]) pars.close(); } // Also write parameters to console if we should be wordy - if ((pin->GetInteger("debug", "verbose") > 0) && MPIRank0()) { + if ((pmesh->packages.Get("Globals")->Param("verbose") > 0) && MPIRank0()) { // This dumps the full Kokkos config, useful for double-checking // that the compile did what we wanted ShowConfig(); @@ -188,22 +189,8 @@ int main(int argc, char *argv[]) // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion // of each step until a stop criterion is reached. Flag("Executing Driver"); + auto driver_status = driver.Execute(); - if (driver_type == "harm") { - std::cout << "Initializing and running KHARMA driver." << std::endl; - HARMDriver driver(pin, papp, pmesh); - auto driver_status = driver.Execute(); - } else if (driver_type == "imex") { - std::cout << "Initializing and running IMEX driver." << std::endl; - ImexDriver driver(pin, papp, pmesh); - auto driver_status = driver.Execute(); - } - -#ifndef KOKKOS_ENABLE_CUDA - // Cleanup our global NDArray - extern ParArrayND p_bound; - p_bound.~ParArrayND(); -#endif // Parthenon cleanup includes Kokkos, MPI Flag("Finalizing"); pman.ParthenonFinalize(); diff --git a/kharma/mpi.hpp b/kharma/mpi.hpp deleted file mode 100644 index 5ca31edc..00000000 --- a/kharma/mpi.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// MPI wrappers -// Some convenient MPI calls, for things like global reductions that Parthenon doesn't cover -// This file has two different versions, depending on MPI_PARALLEL macro -// This way, the rest of the code can assume MPI is available and should be used, -// but consistent results are generated without it for free -// Trust me it makes everything 1000x more readable -#pragma once - -#include - -#ifdef MPI_PARALLEL - -#include - -static auto comm = MPI_COMM_WORLD; - -// Wrappers to make Parthenon-scope MPI interface global, -// plus an easy barrier in case you need it for debugging -inline bool MPIRank() -{ - return parthenon::Globals::my_rank; -} -inline bool MPIRank0() -{ - return (parthenon::Globals::my_rank == 0 ? true : false); -} -inline void MPIBarrier() -{ - MPI_Barrier(comm); -} - -/** - * Perform a Parthenon MPI reduction. - * Now that Parthenon cleans up communicators, this is basically - * how all reductions should be done. - * However, Reduction objects should have a longer lifetime if - * possible, as they require some overhead to create/destroy. - */ -template -inline T MPIReduce_once(T f, MPI_Op O) -{ - // TODO Can this borrow from an object with global lifetime? - static parthenon::AllReduce reduction; - reduction.val = f; - reduction.StartReduce(O); - // Wait on results - while (reduction.CheckReduce() == parthenon::TaskStatus::incomplete); - return reduction.val; -} -#else -// Use Parthenon's MPI_Op workaround -//typedef MPI_Op parthenon::MPI_Op; - -// Dummy versions of calls -inline void MPIBarrier() {} -inline bool MPIRank() { return 0; } -inline bool MPIRank0() { return true; } - -template -inline T MPIReduce_once(T f, MPI_Op O) -{ - return f; -} - -#endif // MPI_PARALLEL diff --git a/kharma/prob/b_field_tools.cpp b/kharma/prob/b_field_tools.cpp deleted file mode 100644 index 49b71917..00000000 --- a/kharma/prob/b_field_tools.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * File: seed_B.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Seed a torus of some type with a magnetic field according to its density - -#include "b_field_tools.hpp" - -#include "grmhd_functions.hpp" - -// TODO KHARMA now has good reduction tooling, use that instead of these - -TaskStatus NormalizeBField(MeshBlockData *rc, Real norm) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::interior; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - GridVector B_P = rc->Get("prims.B").data; - const auto& G = pmb->coords; - - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - pmb->par_for("B_field_normalize", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { - VLOOP B_P(v, k, j, i) *= norm; - } - ); - - return TaskStatus::complete; -} - -Real GetLocalBetaMin(MeshBlockData *rc) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::interior; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - const auto& G = pmb->coords; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - Real beta_min; - Kokkos::Min min_reducer(beta_min); - pmb->par_reduce("B_field_betamin", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D_REDUCE { - FourVectors Dtmp; - GRMHD::calc_4vecs(G, uvec, B_P, k, j, i, Loci::center, Dtmp); - double bsq_ij = dot(Dtmp.bcon, Dtmp.bcov); - - Real beta_ij = ((gam - 1) * u(k, j, i))/(0.5*(bsq_ij + TINY_NUMBER)); - - if(beta_ij < local_result) local_result = beta_ij; - } - , min_reducer); - return beta_min; -} - -Real GetLocalBsqMax(MeshBlockData *rc) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::interior; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - const auto& G = pmb->coords; - - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - - Real bsq_max; - Kokkos::Max bsq_max_reducer(bsq_max); - pmb->par_reduce("B_field_bsqmax", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D_REDUCE { - FourVectors Dtmp; - GRMHD::calc_4vecs(G, uvec, B_P, k, j, i, Loci::center, Dtmp); - double bsq_ij = dot(Dtmp.bcon, Dtmp.bcov); - if(bsq_ij > local_result) local_result = bsq_ij; - } - , bsq_max_reducer); - return bsq_max; -} - -Real GetLocalBsqMin(MeshBlockData *rc) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::interior; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - const auto& G = pmb->coords; - - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - - Real bsq_min; - Kokkos::Min bsq_min_reducer(bsq_min); - pmb->par_reduce("B_field_bsqmax", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D_REDUCE { - FourVectors Dtmp; - GRMHD::calc_4vecs(G, uvec, B_P, k, j, i, Loci::center, Dtmp); - double bsq_ij = dot(Dtmp.bcon, Dtmp.bcov); - if(bsq_ij < local_result) local_result = bsq_ij; - } - , bsq_min_reducer); - return bsq_min; -} - -Real GetLocalPMax(MeshBlockData *rc) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::interior; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - const auto& G = pmb->coords; - GridScalar u = rc->Get("prims.u").data; - - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - Real p_max; - Kokkos::Max p_max_reducer(p_max); - pmb->par_reduce("B_field_pmax", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D_REDUCE { - Real p_ij = (gam - 1) * u(k, j, i); - if(p_ij > local_result) local_result = p_ij; - } - , p_max_reducer); - return p_max; -} diff --git a/kharma/prob/b_field_tools.hpp b/kharma/prob/b_field_tools.hpp index 89a3abfa..974c6f95 100644 --- a/kharma/prob/b_field_tools.hpp +++ b/kharma/prob/b_field_tools.hpp @@ -38,7 +38,7 @@ // Internal representation of the field initialization preference for quick switch // Avoids string comparsion in kernels -enum BSeedType{constant, monopole, sane, ryan, ryan_quadrupole, r3s3, steep, gaussian, bz_monopole, vertical}; +enum BSeedType{constant, monopole, monopole_cube, sane, ryan, ryan_quadrupole, r3s3, steep, gaussian, bz_monopole, vertical}; /** * Function to parse a string indicating desired field to a BSeedType @@ -49,6 +49,8 @@ inline BSeedType ParseBSeedType(std::string b_field_type) return BSeedType::constant; } else if (b_field_type == "monopole") { return BSeedType::monopole; + } else if (b_field_type == "monopole_cube") { + return BSeedType::monopole_cube; } else if (b_field_type == "sane") { return BSeedType::sane; } else if (b_field_type == "mad" || b_field_type == "ryan") { @@ -69,29 +71,3 @@ inline BSeedType ParseBSeedType(std::string b_field_type) throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); } } - -/** - * Get the minimum value of plasma beta on the (physical, non-ghost) domain - * - * Likely not actually what you want - */ -Real GetLocalBetaMin(parthenon::MeshBlockData *rc); - -/** - * Get the maximum/minimum value of b^2 (twice the magnetic field pressure) - * over the domain. Latter a good check for >0 & for constant-field init. - */ -Real GetLocalBsqMax(parthenon::MeshBlockData *rc); -Real GetLocalBsqMin(parthenon::MeshBlockData *rc); - -/** - * Get the maximum fluid pressure over the domain - */ -Real GetLocalPMax(parthenon::MeshBlockData *rc); - -/** - * Normalize the magnetic field by dividing by 'factor' - * - * LOCKSTEP: this function expects and preserves P==U - */ -TaskStatus NormalizeBField(parthenon::MeshBlockData *rc, Real factor); diff --git a/kharma/prob/blob.hpp b/kharma/prob/blob.hpp index 492d993a..61e25098 100644 --- a/kharma/prob/blob.hpp +++ b/kharma/prob/blob.hpp @@ -76,7 +76,7 @@ void InsertBlob(MeshBlockData *rc, ParameterInput *pin) IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("insert_blob", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); Real d = m::sqrt(blob_r*blob_r + X[1]*X[1] - 2*blob_r*X[1]* @@ -96,7 +96,7 @@ void InsertBlob(MeshBlockData *rc, ParameterInput *pin) // P(m_p.RHO, k, j, i) = rho_out + ramp * (rho_in - rho_out); Real lrho_factor_in = log(rho_factor); - P(m_p.RHO, k, j, i) *= exp(ramp * lrho_factor_in); + P(m_p.RHO, k, j, i) *= m::exp(ramp * lrho_factor_in); P(m_p.UU, k, j, i) = u_over_rho * P(m_p.RHO, k, j, i); } diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 094a4110..3319120d 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -34,49 +34,80 @@ #include "bondi.hpp" +#include "floors.hpp" +#include "flux_functions.hpp" + /** * Initialization of a Bondi problem with specified sonic point, BH mdot, and horizon radius * TODO mdot and rs are redundant and should be merged into one parameter. Uh, no. */ -TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing Bondi problem"); auto pmb = rc->GetBlockPointer(); const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); const Real rs = pin->GetOrAddReal("bondi", "rs", 8.0); - // r_shell : the radius of the shell where inside this radius is filled with vacuum. If 0, the simulation is initialized to Bondi everywhere - const Real r_shell = pin->GetOrAddReal("bondi", "r_shell", 0.); + + // Set the innermost radius to apply the Bondi problem initialization + // By default, stay away from the outer BL coordinate singularity + const Real a = pin->GetReal("coordinates", "a"); + const Real rin_bondi_default = 1 + m::sqrt(1 - a*a) + 0.1; + // TODO take r_shell + const Real rin_bondi = pin->GetOrAddReal("bondi", "r_in", rin_bondi_default); + // Add these to package properties, since they continue to be needed on boundaries - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot"))) + // TODO Problems need params + if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot")) pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rs"))) + if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("rs")) pmb->packages.Get("GRMHD")->AddParam("rs", rs); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("r_shell"))) - pmb->packages.Get("GRMHD")->AddParam("r_shell", r_shell); + if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("rin_bondi")) + pmb->packages.Get("GRMHD")->AddParam("rin_bondi", rin_bondi); - // Set the whole domain to the analytic solution to begin - SetBondi(rc); + // Set this problem to control the outer X1 boundary by default + // remember to disable inflow_check in parameter file! + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + if (pin->GetOrAddBoolean("bondi", "set_outer_bound", true)) { + bound_pkg->KHARMAOuterX1Boundary = SetBondi; + } + if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { + bound_pkg->KHARMAInnerX1Boundary = SetBondi; + } + + // Set the interior domain to the analytic solution to begin + // This tests that PostInitialize will correctly fill ghost zones with the boundary we set + SetBondi(rc, IndexDomain::interior); + + if (rin_bondi > pin->GetReal("coordinates", "r_in")) { + // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) + // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable. + Floors::ApplyInitialFloors(rc.get(), IndexDomain::interior); + } Flag(rc, "Initialized"); return TaskStatus::complete; } -TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { Flag(rc, "Setting Bondi zones"); auto pmb = rc->GetBlockPointer(); + //std::cerr << "Bondi on domain: " << BoundaryName(domain) << std::endl; + PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(rc, prims_map); - auto U = GRMHD::PackMHDCons(rc, cons_map); + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map); + auto U = GRMHD::PackMHDCons(rc.get(), cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - const Real r_shell = pmb->packages.Get("GRMHD")->Param("r_shell"); + const Real rin_bondi = pmb->packages.Get("GRMHD")->Param("rin_bondi"); + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); // Just the X1 right boundary GRCoordinates G = pmb->coords; @@ -84,28 +115,63 @@ TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain, bool coarse) SphBLCoords bl = SphBLCoords(ks.a); CoordinateEmbedding cs = G.coords; - // This function currently only handles "outer X1" and "entire" grid domains, - // but is the special-casing here necessary? - // Can we define outer_x1 w/priority more flexibly? + // Solution constants + // These don't depend on which zone we're calculating + const Real n = 1. / (gam - 1.); + const Real uc = m::sqrt(1. / (2. * rs)); + const Real Vc = m::sqrt(uc * uc / (1. - 3. * uc * uc)); + const Real Tc = -n * Vc * Vc / ((n + 1.) * (n * Vc * Vc - 1.)); + const Real C1 = uc * rs * rs * m::pow(Tc, n); + const Real A = 1. + (1. + n) * Tc; + const Real C2 = A * A * (1. - 2. / rs + uc * uc); + const Real K = m::pow(4 * M_PI * C1 / mdot, 1/n); + const Real Kn = m::pow(K, n); + + // Set the Bondi conditions wherever we're asked auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - int ibs, ibe; - if (domain == IndexDomain::outer_x1) { - ibs = bounds.GetBoundsI(IndexDomain::interior).e+1; - ibe = bounds.GetBoundsI(IndexDomain::entire).e; - } else if (domain == IndexDomain::inner_x1) { - ibs = bounds.GetBoundsI(IndexDomain::entire).s; - ibe = bounds.GetBoundsI(IndexDomain::interior).s-1; - } else { - ibs = bounds.GetBoundsI(domain).s; - ibe = bounds.GetBoundsI(domain).e; - } - IndexRange jb_e = bounds.GetBoundsJ(IndexDomain::entire); - IndexRange kb_e = bounds.GetBoundsK(IndexDomain::entire); - pmb->par_for("bondi_boundary", kb_e.s, kb_e.e, jb_e.s, jb_e.e, ibs, ibe, - KOKKOS_LAMBDA_3D { - get_prim_bondi(G, cs, P, m_p, gam, bl, ks, mdot, rs, r_shell, k, j, i); - // TODO all flux - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u); + + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + pmb->par_for("bondi_boundary", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + GReal Xnative[GR_DIM], Xembed[GR_DIM]; + G.coord(k, j, i, Loci::center, Xnative); + G.coord_embed(k, j, i, Loci::center, Xembed); + GReal r = Xembed[1]; + // Unless we're doing a Schwarzchild problem & comparing solutions, + // be a little cautious about initializing the Ergosphere zones + if (r < rin_bondi) return; + + const Real T = get_T(r, C1, C2, n, rs); + const Real Tn = m::pow(T, n); + const Real ur = -C1 / (Tn * r * r); + const Real rho = Tn / Kn; + const Real u = rho * T * n; + + // Set u^t to make u^r a 4-vector + Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; + Real gcov_bl[GR_DIM][GR_DIM]; + bl.gcov_embed(Xembed, gcov_bl); + set_ut(gcov_bl, ucon_bl); + + // Then transform that 4-vector to KS, then to native + Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; + ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); + cs.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + + // Convert native 4-vector to primitive u-twiddle, see Gammie '04 + Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; + G.gcon(Loci::center, j, i, gcon); + fourvel_to_prim(gcon, ucon_mks, u_prim); + + // This used to have NaN guards. No point, as for optimized builds they are ignored (!) + // Now we just avoid initializing near the EH + P(m_p.RHO, k, j, i) = rho; + P(m_p.UU, k, j, i) = u; + P(m_p.U1, k, j, i) = u_prim[0]; + P(m_p.U2, k, j, i) = u_prim[1]; + P(m_p.U3, k, j, i) = u_prim[2]; } ); diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 52a48567..e8975e6c 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -45,17 +45,16 @@ #include /** - * Initialization of a Bondi problem with specified sonic point and BH accretion rate mdot - * TODO mdot and rs are redundant and should be merged into one parameter + * Initialize a Bondi problem over the domain */ -TaskStatus InitializeBondi(MeshBlockData *rc, ParameterInput *pin); +TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterInput *pin); /** * Set all values on a given domain to the Bondi inflow analytic steady-state solution * * Used for initialization and boundary conditions */ -TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); +TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain, bool coarse=false); /** * Supporting functions for Bondi flow calculations @@ -65,26 +64,21 @@ TaskStatus SetBondi(MeshBlockData *rc, IndexDomain domain=IndexDomain::ent */ KOKKOS_INLINE_FUNCTION Real get_Tfunc(const Real T, const GReal r, const Real C1, const Real C2, const Real n) { - return m::pow(1. + (1. + n) * T, 2.) * (1. - 2. / r + m::pow(C1 / m::pow(r,2) / m::pow(T, n), 2.)) - C2; + const Real A = 1. + (1. + n) * T; + const Real B = C1 / (r * r * m::pow(T, n)); + return A * A * (1. - 2. / r + B * B) - C2; } KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n, const Real rs) { Real rtol = 1.e-12; Real ftol = 1.e-14; Real Tinf = (m::sqrt(C2) - 1.) / (n + 1); // temperature at infinity - Real Tnear = m::pow(C1 * m::sqrt(2. / m::pow(r,3)), 1. / n); // temperature near the BH - Real Tmin, Tmax; + Real Tnear = m::pow(C1 * m::sqrt(2. / (r*r*r)), 1. / n); // temperature near the BH // There are two branches of solutions (see Michel et al. 1971) and the two branches cross at rs. - // These bounds are set to only select the inflowing solution only. - if (r 0) return -1; + //if (f0 * f1 > 0) throw std::runtime_error("Cannot solve temperature!"); Th = (T0 + T1) / 2.; // a simple bisection method which is stable and fast fh = get_Tfunc(Th, r, C1, C2, n); @@ -113,72 +107,3 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c return Th; } - -/** - * Get the Bondi solution at a particular zone - * Note this assumes that there are ghost zones! - * - * TODO could put this back into SetBondi - */ -KOKKOS_INLINE_FUNCTION void get_prim_bondi(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, - const Real& gam, const SphBLCoords& bl, const SphKSCoords& ks, - const Real mdot, const Real rs, const Real r_shell, const int& k, const int& j, const int& i) -{ - // Solution constants - // Ideally these could be cached but preformance isn't an issue here - Real n = 1. / (gam - 1.); - Real uc = m::sqrt(mdot / (2. * rs)); - Real Vc = -m::sqrt(m::pow(uc, 2) / (1. - 3. * m::pow(uc, 2))); - Real Tc = -n * m::pow(Vc, 2) / ((n + 1.) * (n * m::pow(Vc, 2) - 1.)); - Real C1 = uc * m::pow(rs, 2) * m::pow(Tc, n); - Real C2 = m::pow(1. + (1. + n) * Tc, 2) * (1. - 2. * mdot / rs + m::pow(C1, 2) / (m::pow(rs, 4) * m::pow(Tc, 2 * n))); - - GReal Xnative[GR_DIM], Xembed[GR_DIM]; - G.coord(k, j, i, Loci::center, Xnative); - G.coord_embed(k, j, i, Loci::center, Xembed); - GReal r = Xembed[1]; - // Unless we're doing a Schwarzchild problem & comparing solutions, - // be a little cautious about initializing the Ergosphere zones - if (ks.a > 0.1 && r < 2) return; - - Real T = get_T(r, C1, C2, n, rs); - Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); - Real rho = m::pow(T, n); - Real u = rho * T * n; - - // Set u^t to make u^r a 4-vector - Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; - if (r #include "Kokkos_Random.hpp" -TaskStatus InitializeBZMonopole(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing BZ monopole problem"); @@ -63,12 +62,12 @@ TaskStatus InitializeBZMonopole(MeshBlockData *rc, ParameterInput *pin) const auto& G = pmb->coords; const GReal a = G.coords.get_a(); - if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Initializing BZ monopole." << std::endl; } pmb->par_for("fm_torus_init", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1]; @@ -87,6 +86,7 @@ TaskStatus InitializeBZMonopole(MeshBlockData *rc, ParameterInput *pin) } ); + Flag(rc, "Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/bz_monopole.hpp b/kharma/prob/bz_monopole.hpp index f7c1ff24..a0b69f72 100644 --- a/kharma/prob/bz_monopole.hpp +++ b/kharma/prob/bz_monopole.hpp @@ -7,5 +7,5 @@ /** * Initialize a Blandford-Znajek monopole setup */ -TaskStatus InitializeBZMonopole(MeshBlockData *rc, ParameterInput *pin); +TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, ParameterInput *pin); diff --git a/kharma/prob/elec/driven_turbulence.hpp b/kharma/prob/elec/driven_turbulence.hpp new file mode 100644 index 00000000..1d86f2f9 --- /dev/null +++ b/kharma/prob/elec/driven_turbulence.hpp @@ -0,0 +1,207 @@ +/* + * File: driven_turbulence.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "gaussian.hpp" +#include "types.hpp" + +#include + +using namespace parthenon; + +TaskStatus InitializeDrivenTurbulence(std::shared_ptr>& rc, ParameterInput *pin) +{ + Flag(rc, "Initializing Driven Turbulence problem"); + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const Real rho0 = pin->GetOrAddReal("driven_turbulence", "rho", 1.0); + const Real cs0 = pin->GetOrAddReal("driven_turbulence", "cs0", 8.6e-4); + const Real dt_kick = pin->GetOrAddReal("driven_turbulence", "dt_kick", 1); + const Real edot_frac = pin->GetOrAddReal("driven_turbulence", "edot_frac", 0.5); + const Real x1min = pin->GetOrAddReal("parthenon/mesh", "x1min", 0); + const Real x1max = pin->GetOrAddReal("parthenon/mesh", "x1max", 1); + const Real x2min = pin->GetOrAddReal("parthenon/mesh", "x2min", 0); + const Real x2max = pin->GetOrAddReal("parthenon/mesh", "x2max", 1); + const Real x3min = pin->GetOrAddReal("parthenon/mesh", "x3min", -1); + const Real x3max = pin->GetOrAddReal("parthenon/mesh", "x3max", 1); + + const Real edot = edot_frac * rho0 * pow(cs0, 3); const Real counter = 0.; + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("drive_edot"))) + pmb->packages.Get("GRMHD")->AddParam("drive_edot", edot); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("counter"))) + pmb->packages.Get("GRMHD")->AddParam("counter", counter, true); + const Real lx1 = x1max-x1min; const Real lx2 = x2max-x2min; + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("lx1"))) + pmb->packages.Get("GRMHD")->AddParam("lx1", lx1); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("lx2"))) + pmb->packages.Get("GRMHD")->AddParam("lx2", lx2); + //adding for later use in create_grf + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("dt_kick"))) + pmb->packages.Get("GRMHD")->AddParam("dt_kick", dt_kick); + + const Real u0 = cs0 * cs0 * rho0 / (gam - 1) / gam; //from flux_functions.hpp + IndexRange myib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); + IndexRange myjb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); + IndexRange mykb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); + pmb->par_for("driven_turb_rho_u_init", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + rho(k, j, i) = rho0; + u(k, j, i) = u0; + } + ); + + Flag(rc, "Initialized"); + return TaskStatus::complete; +} + +/** + * This applies a turbulent Gaussian random "kick" every dt_kick units of simulation time + * It is only called after the last sub-step, so this splits nicely with the fluid + * evolution operator. + */ +void ApplyDrivingTurbulence(MeshBlockData *rc) +{ + Flag("Applying Driven Turbulence kick"); + auto pmb = rc->GetBlockPointer(); + const IndexRange myib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); + const IndexRange myjb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); + const IndexRange mykb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); + + // Gaussian random field: + const auto& G = pmb->coords; + GridScalar rho = rc->Get("prims.rho").data; + GridVector uvec = rc->Get("prims.uvec").data; + GridVector B_P = rc->Get("prims.B").data; + GridVector grf_normalized = rc->Get("grf_normalized").data; + const Real t = pmb->packages.Get("Globals")->Param("time"); + Real counter = pmb->packages.Get("GRMHD")->Param("counter"); + const Real dt_kick= pmb->packages.Get("GRMHD")->Param("dt_kick"); + if (counter < t) { + counter += dt_kick; + pmb->packages.Get("GRMHD")->UpdateParam("counter", counter); + printf("Kick applied at time %.32f\n", t); + + const Real lx1= pmb->packages.Get("GRMHD")->Param("lx1"); + const Real lx2= pmb->packages.Get("GRMHD")->Param("lx2"); + const Real edot= pmb->packages.Get("GRMHD")->Param("drive_edot"); + GridScalar alfven_speed = rc->Get("alfven_speed").data; + + int Nx1 = pmb->cellbounds.ncellsi(IndexDomain::interior); + int Nx2 = pmb->cellbounds.ncellsj(IndexDomain::interior); + Real *dv0 = (Real*) malloc(sizeof(Real)*Nx1*Nx2); + Real *dv1 = (Real*) malloc(sizeof(Real)*Nx1*Nx2); + create_grf(Nx1, Nx2, lx1, lx2, dv0, dv1); + + Real mean_velocity_num0 = 0; Kokkos::Sum mean_velocity_num0_reducer(mean_velocity_num0); + Real mean_velocity_num1 = 0; Kokkos::Sum mean_velocity_num1_reducer(mean_velocity_num1); + Real tot_mass = 0; Kokkos::Sum tot_mass_reducer(tot_mass); + pmb->par_reduce("forced_mhd_normal_kick_centering_mean_vel0", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += cell_mass * dv0[(i-4)*Nx1+(j-4)]; + } + , mean_velocity_num0_reducer); + pmb->par_reduce("forced_mhd_normal_kick_centering_mean_vel1", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += cell_mass * dv1[(i-4)*Nx1+(j-4)]; + } + , mean_velocity_num1_reducer); + pmb->par_reduce("forced_mhd_normal_kick_centering_tot_mass", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + local_result += (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + } + , tot_mass_reducer); + Real mean_velocity0 = mean_velocity_num0/tot_mass; + Real mean_velocity1 = mean_velocity_num1/tot_mass; + #pragma omp parallel for simd collapse(2) + for (size_t i = 0; i < Nx1 ; i ++) { + for (size_t j = 0; j < Nx2 ; j ++) { + dv0[i*Nx1+j] -= mean_velocity0; + dv1[i*Nx1+j] -= mean_velocity1; + } + } + + Real Bhalf = 0; Real A = 0; Real init_e = 0; + Kokkos::Sum Bhalf_reducer(Bhalf); Kokkos::Sum A_reducer(A); Kokkos::Sum init_e_reducer(init_e); + pmb->par_reduce("forced_mhd_normal_kick_normalization_Bhalf", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += cell_mass * (dv0[(i-4)*Nx1+(j-4)]*uvec(0, k, j, i) + dv1[(i-4)*Nx1+(j-4)]*uvec(1, k, j, i)); + } + , Bhalf_reducer); + pmb->par_reduce("forced_mhd_normal_kick_normalization_A", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += cell_mass * (pow(dv0[(i-4)*Nx1+(j-4)], 2) + pow(dv1[(i-4)*Nx1+(j-4)], 2)); + } + , A_reducer); + pmb->par_reduce("forced_mhd_normal_kick_init_e", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += 0.5 * cell_mass * (pow(uvec(0, k, j, i), 2) + pow(uvec(1, k, j, i), 2)); + } + , init_e_reducer); + + Real norm_const = (-Bhalf + pow(pow(Bhalf,2) + A*2*dt_kick*edot, 0.5))/A; // going from k:(0, 0), j:(4, 515), i:(4, 515) inclusive + pmb->par_for("forced_mhd_normal_kick_setting", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + grf_normalized(0, k, j, i) = (dv0[(i-4)*Nx1+(j-4)]*norm_const); + grf_normalized(1, k, j, i) = (dv1[(i-4)*Nx1+(j-4)]*norm_const); + uvec(0, k, j, i) += grf_normalized(0, k, j, i); + uvec(1, k, j, i) += grf_normalized(1, k, j, i); + FourVectors Dtmp; + GRMHD::calc_4vecs(G, uvec, B_P, k, j, i, Loci::center, Dtmp); + Real bsq = dot(Dtmp.bcon, Dtmp.bcov); + alfven_speed(k,j,i) = bsq/rho(k, j, i); //saving alfven speed for analysis purposes + } + ); + + Real finl_e = 0; Kokkos::Sum finl_e_reducer(finl_e); + pmb->par_reduce("forced_mhd_normal_kick_finl_e", mykb.s, mykb.e, myjb.s, myjb.e, myib.s, myib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + Real cell_mass = (rho(k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i)); + local_result += 0.5 * cell_mass * (pow(uvec(0, k, j, i), 2) + pow(uvec(1, k, j, i), 2)); + } + , finl_e_reducer); + printf("%.32f\n", A); printf("%.32f\n", Bhalf); printf("%.32f\n", norm_const); + printf("%.32f\n", (finl_e-init_e)/dt_kick); + free(dv0); free(dv1); + } +} \ No newline at end of file diff --git a/kharma/prob/elec/gaussian.cpp b/kharma/prob/elec/gaussian.cpp new file mode 100644 index 00000000..b58527ce --- /dev/null +++ b/kharma/prob/elec/gaussian.cpp @@ -0,0 +1,122 @@ +/* + * File: gaussian.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gaussian.hpp" +#include "problem.hpp" + +#include +#include + +float normalRand() +{ + // TODO this can definitely be Kokkosified + std::random_device rd{}; + std::mt19937 gen{rd()}; + std::normal_distribution<> d{0,1}; + return d(gen); +} + +#if USE_FFTW + +#include "fftw3.h" + +void create_grf(int Nx1, int Nx2, double lx1, double lx2, + double * dv1, double * dv2) +{ + double dkx1 = 2*M_PI/lx1; + double dkx2 = 2*M_PI/lx2; + double Dx1 = lx1/Nx1; + double Dx2 = lx2/Nx2; + + double kx1max = 2*M_PI/(2*Dx1); + double kx2max = 2*M_PI/(2*Dx2); + double k_peak = 4*M_PI/lx1; + + fftw_complex *dvkx1, *dvkx2; + dvkx1 = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * Nx1 * Nx2); + dvkx2 = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * Nx1 * Nx2); +#pragma omp parallel for simd collapse(2) + for (size_t i = 0; i < Nx1 ; i ++) { + for (size_t j = 0; j < Nx2 ; j ++) { + double retx1 = i * dkx1; + double retx2 = j * dkx2; + if(retx1 > kx1max) retx1 = retx1 - 2*kx1max; + if(retx2 > kx2max) retx2 = retx2 - 2*kx2max; + double curr_k_magn = pow(pow(retx1, 2) + pow(retx2, 2), 0.5); + + double pwr_spct = pow(curr_k_magn, 6)*exp(-8*curr_k_magn/k_peak); + if (curr_k_magn != 0) { + retx1 /= curr_k_magn; + retx2 /= curr_k_magn; + } + + double noisy_dvkx1_real = pwr_spct*normalRand(); double noisy_dvkx1_imag = pwr_spct*normalRand(); + double noisy_dvkx2_real = pwr_spct*normalRand(); double noisy_dvkx2_imag = pwr_spct*normalRand(); + + //real part of kx, using real part of dot product, and the kx component. second line is imag part + double adj_dvkx1_real = (retx1*noisy_dvkx1_real + retx2*noisy_dvkx2_real)*retx1; + double adj_dvkx1_imag = (retx1*noisy_dvkx1_imag + retx2*noisy_dvkx2_imag)*retx1; + double adj_dvkx2_real = (retx1*noisy_dvkx1_real + retx2*noisy_dvkx2_real)*retx2; + double adj_dvkx2_imag = (retx1*noisy_dvkx1_imag + retx2*noisy_dvkx2_imag)*retx2; + + dvkx1[i*Nx1+j][0] = noisy_dvkx1_real - adj_dvkx1_real; dvkx1[i*Nx1+j][1] = noisy_dvkx1_imag - adj_dvkx1_imag; + dvkx2[i*Nx1+j][0] = noisy_dvkx2_real - adj_dvkx2_real; dvkx2[i*Nx1+j][1] = noisy_dvkx2_imag - adj_dvkx2_imag; + } + } + + fftw_plan p_x1, p_x2; + p_x1 = fftw_plan_dft_2d(Nx1, Nx2, dvkx1, dvkx1, FFTW_BACKWARD, FFTW_ESTIMATE); //in-place + p_x2 = fftw_plan_dft_2d(Nx1, Nx2, dvkx2, dvkx2, FFTW_BACKWARD, FFTW_ESTIMATE); + fftw_execute(p_x1); //look for threads documentation + fftw_execute(p_x2); + + fftw_destroy_plan(p_x1); fftw_destroy_plan(p_x2); +#pragma omp parallel for simd collapse(2) + for (size_t i = 0; i < Nx1 ; i ++) { + for (size_t j = 0; j < Nx2 ; j ++) { + dv1[i*Nx1+j] = dvkx1[i*Nx1+j][0]; + dv2[i*Nx1+j] = dvkx2[i*Nx1+j][0]; + } + } + fftw_free(dvkx1); fftw_free(dvkx2); +} + +#else + +void create_grf(int Nx1, int Nx2, double lx1, double lx2, + double * dv1, double * dv2) +{ + throw std::runtime_error("Attempted to use an FFT to generate a Gaussian random field, but KHARMA was compiled without FFT support!"); +} +#endif \ No newline at end of file diff --git a/kharma/prob/elec/hubble.cpp b/kharma/prob/elec/hubble.cpp new file mode 100644 index 00000000..3d75e7a5 --- /dev/null +++ b/kharma/prob/elec/hubble.cpp @@ -0,0 +1,215 @@ +/* + * File: hubble.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "hubble.hpp" + +#include "pack.hpp" +#include "types.hpp" + +TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterInput *pin) +{ + Flag("Initializing Hubble Flow Electron Heating problem"); + auto pmb = rc->GetBlockPointer(); + + const Real mach = pin->GetOrAddReal("hubble", "mach", 1.); + const Real v0 = pin->GetOrAddReal("hubble", "v0", 1.e-3); + const Real gam = pin->GetOrAddReal("GRMHD", "gamma", 1.666667); + // Whether to stop after "dyn_times" dynamical time L/max(v0*x) + bool set_tlim = pin->GetOrAddBoolean("hubble", "set_tlim", false); + bool cooling = pin->GetOrAddBoolean("hubble", "cooling", true); + bool context_boundaries = pin->GetOrAddBoolean("hubble", "context_boundaries", false); + Real dyntimes = pin->GetOrAddReal("hubble", "dyntimes", 1.0); + + // Add everything to package parameters, since they continue to be needed on boundaries + int counter = -5.0; + Params& g_params = pmb->packages.Get("GRMHD")->AllParams(); + if(!g_params.hasKey("counter")) g_params.Add("counter", counter, true); + Real rho0 = (mach/v0) * sqrt(gam*(gam-1)); + Real ug0 = (v0/mach) / sqrt(gam*(gam-1)); + if(!g_params.hasKey("rho0")) g_params.Add("rho0", rho0); + if(!g_params.hasKey("v0")) g_params.Add("v0", v0); + if(!g_params.hasKey("ug0")) g_params.Add("ug0", ug0); + if(!g_params.hasKey("cooling")) g_params.Add("cooling", cooling); + if(!g_params.hasKey("context_boundaries")) g_params.Add("context_boundaries", context_boundaries); + + // This is how we will initialize kel values later + if (pmb->packages.AllPackages().count("Electrons")) { + const Real fel0 = pmb->packages.Get("Electrons")->Param("fel_0"); + if(!g_params.hasKey("ue0")) g_params.Add("ue0", fel0 * ug0); + } + + // Override end time to be 1 dynamical time L/max(v@t=0) + if (set_tlim) { + pin->SetReal("parthenon/time", "tlim", dyntimes / v0); + } + + // Replace the boundary conditions + auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + bound_pkg->KHARMAInnerX1Boundary = SetHubble; + bound_pkg->KHARMAOuterX1Boundary = SetHubble; + bound_pkg->BlockApplyPrimSource = ApplyHubbleHeating; + + // Then call the general function to fill the grid + SetHubble(rc, IndexDomain::interior); + + Flag("Initialized"); + return TaskStatus::complete; +} + +TaskStatus SetHubble(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag("Setting zones to Hubble Flow"); + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const Real rho0 = pmb->packages.Get("GRMHD")->Param("rho0"); + const Real v0 = pmb->packages.Get("GRMHD")->Param("v0"); + const bool cooling = pmb->packages.Get("GRMHD")->Param("cooling"); + const bool context_boundaries = pmb->packages.Get("GRMHD")->Param("context_boundaries"); + const Real ug0 = pmb->packages.Get("GRMHD")->Param("ug0"); + // first time this is called in boundary conditions inside the time stepping cycle is when counter == 0 + int counter = pmb->packages.Get("GRMHD")->Param("counter"); + const Real tt = pmb->packages.Get("Globals")->Param("time"); + const Real dt = pmb->packages.Get("Globals")->Param("dt_last"); + + Real t = tt + 0.5*dt; + if ((counter%4) > 1) t = tt + dt; + + const auto& G = pmb->coords; + + IndexRange ib = pmb->cellbounds.GetBoundsI(domain); + IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); + IndexRange kb = pmb->cellbounds.GetBoundsK(domain); + + if (!context_boundaries || counter < 0) { + // Setting as in equation 37 + Real toberho = rho0 / (1. + v0*t); + Real tobeu = ug0 / pow(1 + v0*t, 2); + if (!cooling) tobeu = ug0 / pow(1 + v0*t, gam); + pmb->par_for("hubble_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real X[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, X); + rho(k, j, i) = toberho; + u(k, j, i) = tobeu; + uvec(0, k, j, i) = v0 * X[1] / (1 + v0*t); + uvec(1, k, j, i) = 0.0; + uvec(2, k, j, i) = 0.0; + } + ); + + if (pmb->packages.AllPackages().count("Electrons")) { + GridScalar ktot = rc->Get("prims.Ktot").data; + GridScalar kel_const = rc->Get("prims.Kel_Constant").data; + const Real game = pmb->packages.Get("Electrons")->Param("gamma_e"); + const Real ue0 = pmb->packages.Get("GRMHD")->Param("ue0"); + Real tobeke = (gam - 2) * (game - 1)/(game - 2) * ue0/pow(rho0, game) * pow(1 + v0*t, game-2); + // Without cooling, the entropy of electrons should stay the same, analytic solution. + if (!cooling) tobeke = (gam - 2) * (game - 1)/(game - 2) * ue0/pow(rho0, game); + pmb->par_for("hubble_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + ktot(k, j, i) = tobeke; + kel_const(k, j, i) = tobeke; //Since we are using fel = 1 + } + ); + } + } else { // We assume the fluid is following the solution so we set the boundaries from the real zones + // Left zone is first one to be called and counter starts at zero + bool left_zone = !(counter%2); + // struct IndexRange { + // int s = 0; /// Starting Index (inclusive) + // int e = 0; /// Ending Index (inclusive) + // }; + int context_index = 0; + if (left_zone) context_index = ib.e + 1; + else context_index = ib.s - 1; + + Real context_X[GR_DIM]; G.coord_embed(0, 0, context_index, Loci::center, context_X); + Real context_t = (v0*context_X[1] - uvec(0, 0, context_index))/(uvec(0, 0, context_index)*v0); + + pmb->par_for("hubble_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real X[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, X); + rho(k, j, i) = rho(k, j, context_index); + u(k, j, i) = u(k, j, context_index); + uvec(0, k, j, i) = v0 * X[1] / (1 + v0*context_t); + } + ); + if (pmb->packages.AllPackages().count("Electrons")) { + GridScalar kel_const = rc->Get("prims.Kel_Constant").data; + pmb->par_for("hubble_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + kel_const(k, j, i) = kel_const(k, j, context_index); + } + ); + } + } + pmb->packages.Get("GRMHD")->UpdateParam("counter", ++counter); + Flag("Set"); + return TaskStatus::complete; +} + +void ApplyHubbleHeating(MeshBlockData *mbase) +{ + Flag(mbase, "Applying heating"); + auto pmb0 = mbase->GetBlockPointer(); + + PackIndexMap prims_map; + auto P_mbase = GRMHD::PackHDPrims(mbase, prims_map); + const VarMap m_p(prims_map, false); + + Real Q = 0; + const Real dt = pmb0->packages.Get("Globals")->Param("dt_last"); // Close enough? + const Real t = pmb0->packages.Get("Globals")->Param("time") + 0.5*dt; + const Real v0 = pmb0->packages.Get("GRMHD")->Param("v0"); + const Real ug0 = pmb0->packages.Get("GRMHD")->Param("ug0"); + const Real gam = pmb0->packages.Get("GRMHD")->Param("gamma"); + Q = (ug0 * v0 * (gam - 2) / pow(1 + v0 * t, 3)); + IndexDomain domain = IndexDomain::interior; + auto ib = mbase->GetBoundsI(domain); + auto jb = mbase->GetBoundsJ(domain); + auto kb = mbase->GetBoundsK(domain); + auto block = IndexRange{0, P_mbase.GetDim(5)-1}; + + pmb0->par_for("heating_substep", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + P_mbase(m_p.UU, k, j, i) += Q*dt*0.5; + } + ); + + Flag(mbase, "Applied heating"); +} diff --git a/kharma/imex_driver.hpp b/kharma/prob/elec/hubble.hpp similarity index 53% rename from kharma/imex_driver.hpp rename to kharma/prob/elec/hubble.hpp index 6f3acfe3..0bf84d84 100644 --- a/kharma/imex_driver.hpp +++ b/kharma/prob/elec/hubble.hpp @@ -1,5 +1,5 @@ /* - * File: imex_driver.hpp + * File: hubble.hpp * * BSD 3-Clause License * @@ -33,35 +33,30 @@ */ #pragma once -#include +#include + +#include "decs.hpp" #include +using namespace std; using namespace parthenon; /** - * A Driver object orchestrates everything that has to be done to a mesh to constitute a step. - * This driver does pretty much the same thing as the HARMDriver, with one important difference: - * ImexDriver syncs primitive variables and treats them as fundamental, whereas HARMDriver syncs conserved variables. - * This allows ImexDriver to optionally use a semi-implicit step, adding a per-zone implicit solve via the 'Implicit' - * package, instead of just explicit RK2 time-stepping. This driver also allows explicit-only RK2 operation + * Test of electron entropy/temperature evolution in 1D Hubble-type flow + * Test of "Electrons" package + * See Ressler+ 2015 + */ +TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterInput *pin); + +/** + * Set all values on a given domain to the Hubble flow solution + * + * Used for initialization and boundary conditions */ -class ImexDriver : public MultiStageDriver { - public: - /** - * Default constructor - */ - ImexDriver(ParameterInput *pin, ApplicationInput *papp, Mesh *pm) : MultiStageDriver(pin, papp, pm) {} +TaskStatus SetHubble(std::shared_ptr>& rc,IndexDomain domain, bool coarse=false); - /** - * All the tasks which constitute advancing the fluid in a mesh by one stage. - * This includes calculation of the primitives and reconstruction of their face values, - * calculation of conserved values and fluxes thereof at faces, - * application of fluxes and a source term in order to update zone values, - * and finally calculation of the next timestep based on the CFL condition. - * - * The function is heavily documented since order changes can introduce subtle bugs, - * usually w.r.t. fluid "state" being spread across the primitive and conserved quantities - */ - TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); -}; +/** + * Apply the source term. Registered as ApplyPrimSource to run at end of step, once per step operator-split + */ +void ApplyHubbleHeating(MeshBlockData *mbase); \ No newline at end of file diff --git a/kharma/prob/noh.hpp b/kharma/prob/elec/noh.hpp similarity index 62% rename from kharma/prob/noh.hpp rename to kharma/prob/elec/noh.hpp index 1b783a45..511adaab 100644 --- a/kharma/prob/noh.hpp +++ b/kharma/prob/elec/noh.hpp @@ -40,63 +40,65 @@ using namespace parthenon; /** * Noh shock tube test. */ -TaskStatus InitializeNoh(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeNoh(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing 1D (Noh) Shock test"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridScalar ktot = rc->Get("prims.Ktot").data; - GridScalar kel_constant = rc->Get("prims.Kel_Constant").data; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - const Real game = pmb->packages.Get("Electrons")->Param("gamma_e"); - const Real fel0 = pmb->packages.Get("Electrons")->Param("fel_0"); - const Real fel_constant = pmb->packages.Get("Electrons")->Param("fel_constant"); - const Real mach = pin->GetOrAddReal("noh", "mach", 49); - const Real rhoL = pin->GetOrAddReal("noh", "rhoL", 1.0); - const Real rhoR = pin->GetOrAddReal("noh", "rhoR", 1.0); - const Real PL = pin->GetOrAddReal("noh", "PL", 0.1); - const Real PR = pin->GetOrAddReal("noh", "PR", 0.1); + const Real mach = pin->GetOrAddReal("noh", "mach", 49.); + const Real rho0 = pin->GetOrAddReal("noh", "rho", 1.0); + const Real v0 = pin->GetOrAddReal("noh", "v0", 1.e-3); + bool zero_ug = pin->GetOrAddBoolean("noh", "zero_ug", false); + bool centered = pin->GetOrAddBoolean("noh", "centered", true); bool set_tlim = pin->GetOrAddBoolean("noh", "set_tlim", false); - const auto& G = pmb->coords; + const GReal x1min = pin->GetReal("parthenon/mesh", "x1min"); + const GReal x1max = pin->GetReal("parthenon/mesh", "x1max"); + const GReal center = (x1min + x1max) / 2.; - IndexDomain domain = IndexDomain::interior; - IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - - const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); - const Real x1max = pin->GetReal("parthenon/mesh", "x1max"); - const Real center = (x1min + x1max) / 2.; - - // TODO relativistic sound speed - Real cs2 = (gam * (gam - 1) * PL) / rhoL; - Real v1 = mach * m::sqrt(cs2); + // Given Mach and knowing that v = 1e-3 and rho = 1, we calculate u + double cs2 = m::pow(v0, 2) / m::pow(mach, 2); + double gamma = 1. / m::sqrt(1. - m::pow(v0, 2)); // Since we are in flat space + const Real P = (zero_ug) ? 0. : rho0 * cs2 / (gam*(gam-1) - cs2*gam); if (set_tlim) { - pin->SetReal("parthenon/time", "tlim", 0.6*(x1max - x1min)/v1); + pin->SetReal("parthenon/time", "tlim", 0.6*(x1max - x1min)/v0); } - double gamma = 1. / m::sqrt(1. - v1 * v1); // Since we are in flat space - - + IndexDomain domain = IndexDomain::interior; + IndexRange ib = pmb->cellbounds.GetBoundsI(domain); + IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); + IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("noh_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - Real X[GR_DIM]; - G.coord_embed(k, j, i, Loci::center, X); - - const bool lhs = X[1] < center; - rho(k, j, i) = (lhs) ? rhoL : rhoR; - u(k, j, i) = ((lhs) ? PL : PR)/(gam - 1.); - uvec(0, k, j, i) = ((lhs) ? v1 : -v1) * gamma; + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + rho(k, j, i) = rho0; + u(k, j, i) = P/(gam - 1.); uvec(1, k, j, i) = 0.0; uvec(2, k, j, i) = 0.0; } ); + const auto& G = pmb->coords; + if (centered) { + pmb->par_for("noh_cent", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real X[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, X); + const bool lhs = X[1] < center; + uvec(0, k, j, i) = ((lhs) ? v0 : -v0) * gamma; + } + ); + } else { + pmb->par_for("noh_left", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + u(k, j, i) = P/(gam - 1.); + uvec(0, k, j, i) = -v0 * gamma; + } + ); + } Flag(rc, "Initialized 1D (Noh) Shock test"); return TaskStatus::complete; diff --git a/kharma/prob/emhd/anisotropic_conduction.hpp b/kharma/prob/emhd/anisotropic_conduction.hpp index e88b1ca5..ce83ddc7 100644 --- a/kharma/prob/emhd/anisotropic_conduction.hpp +++ b/kharma/prob/emhd/anisotropic_conduction.hpp @@ -42,7 +42,7 @@ using namespace parthenon; /** * Anisotropic heat conduction problem, see Chandra+ 2017 */ -TaskStatus InitializeAnisotropicConduction(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeAnisotropicConduction(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing EMHD Modes problem"); auto pmb = rc->GetBlockPointer(); @@ -67,13 +67,13 @@ TaskStatus InitializeAnisotropicConduction(MeshBlockData *rc, ParameterInp IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::entire); IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::entire); pmb->par_for("anisotropic_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); GReal r = m::sqrt(m::pow((X[1] - 0.5), 2) + m::pow((X[2] - 0.5), 2)); // Initialize primitives - rho(k, j, i) = 1 - (A * exp(-m::pow(r, 2) / m::pow(R, 2))); + rho(k, j, i) = 1 - (A * m::exp(-m::pow(r, 2) / m::pow(R, 2))); u(k, j, i) = 1.; uvec(0, k, j, i) = 0.; uvec(1, k, j, i) = 0.; diff --git a/kharma/prob/emhd/bondi_viscous.cpp b/kharma/prob/emhd/bondi_viscous.cpp deleted file mode 100644 index d6c1e697..00000000 --- a/kharma/prob/emhd/bondi_viscous.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * File: bondi.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "bondi_viscous.hpp" - -using namespace std; -using namespace parthenon; - -/** - * Initialization of a Bondi problem with specified sonic point, BH mdot, and horizon radius - * TODO mdot and rs are redundant and should be merged into one parameter. Uh, no. - */ -TaskStatus InitializeBondiViscous(MeshBlockData *rc, ParameterInput *pin) -{ - Flag(rc, "Initializing Viscous Bondi problem"); - auto pmb = rc->GetBlockPointer(); - - const Real mdot = pin->GetOrAddReal("bondi_viscous", "mdot", 1.0); - const Real rs = pin->GetOrAddReal("bondi_viscous", "rs", 8.0); - - // Add these to package properties, since they continue to be needed on boundaries - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot"))) - pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rs"))) - pmb->packages.Get("GRMHD")->AddParam("rs", rs); - - // Set the whole domain to the analytic solution to begin - SetBondiViscous(rc); - - Flag(rc, "Initialized"); - return TaskStatus::complete; -} - -TaskStatus SetBondiViscous(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - Flag(rc, "Setting Viscous Bondi zones"); - auto pmb = rc->GetBlockPointer(); - - PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(rc, prims_map); - auto U = GRMHD::PackMHDCons(rc, cons_map); - const VarMap m_p(prims_map, false), m_u(cons_map, true); - - const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); - const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - // Obtain EMHD params - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); - const EMHD::EMHD_parameters& emhd_params = emhd_pars.Get("emhd_params"); - - // Just the X1 right boundary - GRCoordinates G = pmb->coords; - SphKSCoords ks = mpark::get(G.coords.base); - SphBLCoords bl = SphBLCoords(ks.a); - CoordinateEmbedding cs = G.coords; - - // This function currently only handles "outer X1" and "entire" grid domains, - // but is the special-casing here necessary? - // Can we define outer_x1 w/priority more flexibly? - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - int ibs, ibe; - if (domain == IndexDomain::outer_x1) { - ibs = bounds.GetBoundsI(IndexDomain::interior).e+1; - ibe = bounds.GetBoundsI(IndexDomain::entire).e; - } else { - ibs = bounds.GetBoundsI(domain).s; - ibe = bounds.GetBoundsI(domain).e; - } - IndexRange jb_e = bounds.GetBoundsJ(IndexDomain::entire); - IndexRange kb_e = bounds.GetBoundsK(IndexDomain::entire); - - pmb->par_for("bondi_boundary", kb_e.s, kb_e.e, jb_e.s, jb_e.e, ibs, ibe, - KOKKOS_LAMBDA_3D { - get_prim_bondi_viscous(G, cs, P, m_p, emhd_params, gam, bl, ks, mdot, rs, k, j, i); - // GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u); - } - ); - - // for (int i=ibs; i<=ibe; i++) { - // for (int j=jb_e.s; j<=jb_e.e; j++) { - // cout << " " << i << " " << j << " " << "RHO" << " " << P(m_p.RHO, 0, j, i) << endl; - // cout << " " << i << " " << j << " " << "UU" << " " << P(m_p.UU, 0, j, i) << endl; - // cout << " " << i << " " << j << " " << "r" << " " << P(m_p.U1, 0, j, i) << endl; - // cout << " " << i << " " << j << " " << "th" << " " << P(m_p.U2, 0, j, i) << endl; - // } - // } - - Flag(rc, "Set"); - return TaskStatus::complete; -} diff --git a/kharma/prob/emhd/bondi_viscous.hpp b/kharma/prob/emhd/bondi_viscous.hpp deleted file mode 100644 index 3ee01446..00000000 --- a/kharma/prob/emhd/bondi_viscous.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * File: bondi.hpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include "decs.hpp" - -#include "gr_coordinates.hpp" -#include "flux_functions.hpp" -#include "grmhd_functions.hpp" -#include "pack.hpp" -#include "prob_common.hpp" -#include "types.hpp" -#include "emhd.hpp" - -#include - -/** - * Initialization of a Bondi problem with specified sonic point and BH accretion rate mdot - * TODO mdot and rs are redundant and should be merged into one parameter - */ -TaskStatus InitializeBondiViscous(MeshBlockData *rc, ParameterInput *pin); - -/** - * Set all values on a given domain to the Bondi inflow analytic steady-state solution - * - * Used for initialization and boundary conditions - */ -TaskStatus SetBondiViscous(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); - -/** - * Supporting functions for Bondi flow calculations - * - * Adapted from M. Chandra - */ -KOKKOS_INLINE_FUNCTION Real get_Tfunc_viscous(const Real T, const GReal r, const Real C4, const Real C3, const Real n) -{ - return pow(1. + (1. + n) * T, 2.) * (1. - 2. / r + pow(C4 / pow(r,2) / pow(T, n), 2.)) - C3; -} -KOKKOS_INLINE_FUNCTION Real get_T_viscous(const GReal r, const Real C4, const Real C3, const Real n) -{ - Real rtol = 1.e-12; - Real ftol = 1.e-14; - Real Tmin = 0.6 * (sqrt(C3) - 1.) / (n + 1); - Real Tmax = pow(C4 * sqrt(2. / pow(r,3)), 1. / n); - - Real f0, f1, fh; - Real T0, T1, Th; - T0 = Tmin; - f0 = get_Tfunc_viscous(T0, r, C4, C3, n); - T1 = Tmax; - f1 = get_Tfunc_viscous(T1, r, C4, C3, n); - if (f0 * f1 > 0) return -1; - - Th = (f1 * T0 - f0 * T1) / (f1 - f0); - fh = get_Tfunc_viscous(Th, r, C4, C3, n); - Real epsT = rtol * (Tmin + Tmax); - while (fabs(Th - T0) > epsT && fabs(Th - T1) > epsT && fabs(fh) > ftol) - { - if (fh * f0 < 0.) { - T0 = Th; - f0 = fh; - } else { - T1 = Th; - f1 = fh; - } - - Th = (f1 * T0 - f0 * T1) / (f1 - f0); - fh = get_Tfunc_viscous(Th, r, C4, C3, n); - } - - return Th; -} - -/** - * Get the Bondi solution at a particular zone - * Note this assumes that there are ghost zones! - * - * TODO could put this back into SetBondi - */ -KOKKOS_INLINE_FUNCTION void get_prim_bondi_viscous(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, - const EMHD::EMHD_parameters& emhd_params, const Real& gam, const SphBLCoords& bl, const SphKSCoords& ks, - const Real mdot, const Real rs, const int& k, const int& j, const int& i) -{ - // Solution constants - // Ideally these could be cached but preformance isn't an issue here - Real n = 1. / (gam - 1.); - Real uc = sqrt(1. / (2. * rs)); - Real Vc = sqrt(pow(uc, 2) / (1. - 3. * pow(uc, 2))); - Real Tc = -n * pow(Vc, 2) / ((n + 1.) * (n * pow(Vc, 2) - 1.)); - Real C4 = uc * pow(rs, 2) * pow(Tc, n); - Real C3 = pow(1. + (1. + n) * Tc, 2) * (1. - 2. / rs + pow(uc, 2)); - Real K = pow(4 * M_PI * C4 / mdot, 1/n); - - GReal Xnative[GR_DIM], Xembed[GR_DIM]; - G.coord(k, j, i, Loci::center, Xnative); - G.coord_embed(k, j, i, Loci::center, Xembed); - GReal r = Xembed[1]; - - Real T = get_T_viscous(r, C4, C3, n); - Real ur = -C4 / (pow(T, n) * pow(r, 2)); - Real rho = pow(K, -n) * pow(T, n); - Real u = rho * T / (gam - 1.); - - // Set u^t to make u^r a 4-vector - Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; - Real gcov_bl[GR_DIM][GR_DIM]; - bl.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS, then to native - Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; - ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); - coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); - - // Convert native 4-vector to primitive u-twiddle, see Gammie '04 - Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; - G.gcon(Loci::center, j, i, gcon); - fourvel_to_prim(gcon, ucon_mks, u_prim); - - P(m_p.RHO, k, j, i) = rho; - P(m_p.UU, k, j, i) = u; - P(m_p.U1, k, j, i) = u_prim[0]; - P(m_p.U2, k, j, i) = u_prim[1]; - P(m_p.U3, k, j, i) = u_prim[2]; - - // Additional initialization due to EMHD sector - P(m_p.B1, k, j, i) = 1. / pow(r, 3.); - -} diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 07bebb94..8fd39b3b 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -34,68 +34,50 @@ #include "emhd/conducting_atmosphere.hpp" -#ifdef KOKKOS_ENABLE_CUDA +#include "boundaries.hpp" +#include "prob_common.hpp" -TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) -{ - throw std::runtime_error("Conducting Atmosphere problem is not implemented for GPUs!!"); -} - -TaskStatus dirichlet_bc(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - throw std::runtime_error("Dirichlet BCs are not implemented for GPUs!!"); -} - -#else - -using namespace std; using namespace parthenon; #define STRLEN 2048 -/* +/** * Initialization of the hydrostatic conducting atmosphere test * * The ODE solution (kharma/prob/emhd/conducting_atmosphere_${RES}_default) is the input to the code. * Since the ODE solution is a steady-state solution of the EMHD equations, * the code should maintain the solution. - * */ - -ParArrayND p_bound; - -TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, ParameterInput *pin) { auto pmb = rc->GetBlockPointer(); - PackIndexMap prims_map; - auto P = GRMHD::PackMHDPrims(rc, prims_map); + // Obtain EMHD params const bool use_emhd = pmb->packages.AllPackages().count("EMHD"); bool higher_order_terms = false; EMHD::EMHD_parameters emhd_params_tmp; - - if (use_emhd) - { + if (use_emhd) { Flag(rc, "Initializing hydrostatic conducting atmosphere problem"); const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); emhd_params_tmp = emhd_pars.Get("emhd_params"); higher_order_terms = emhd_params_tmp.higher_order_terms; - - MetadataFlag isPrimitive = pmb->packages.Get("GRMHD")->Param("PrimitiveFlag"); - P = rc->PackVariables({isPrimitive}, prims_map); - } - else + } else { Flag(rc, "Initializing hydrostatic atmosphere problem"); - + } const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; // Obtain GRMHD params const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); const Real& gam = grmhd_pars.Get("gamma"); - int nvar = P.GetDim(4); + // Get all primitive variables (GRMHD+EMHD if in use) + PackIndexMap prims_map; + auto P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + VarMap m_p(prims_map, false); + + const int nvar = P.GetDim(4); const auto& G = pmb->coords; @@ -108,6 +90,7 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::entire); // Load file names into strings + // TODO store as single file table. HDF5? char fode_rCoords[STRLEN], fode_rho[STRLEN], fode_u[STRLEN], fode_q[STRLEN]; sprintf(fode_rCoords, "atmosphere_soln_rCoords.txt"); sprintf(fode_rho, "atmosphere_soln_rho.txt"); @@ -119,51 +102,62 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) fp_r = fopen(fode_rCoords, "r"); fp_rho = fopen(fode_rho, "r"); fp_u = fopen(fode_u, "r"); - if (use_emhd) + if (fp_r == NULL || fp_rho == NULL || fp_u == NULL) { + throw std::runtime_error("Could not open conducting atmosphere solution!"); + } + if (use_emhd) { fp_q = fopen(fode_q, "r"); + if (fp_q == NULL) { + throw std::runtime_error("Could not open conducting atmosphere solution!"); + } + } - + // Get primitives individually, so we can use GetHostMirror() + // TODO implement VariablePack::GetHostMirror, or mirror a temporary and dump into a pack device-side GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; GridVector B_P = rc->Get("prims.B").data; GridScalar q; GridScalar dP; - if (use_emhd) - { + if (use_emhd) { q = rc->Get("prims.q").data; dP = rc->Get("prims.dP").data; } - // Host side mirror of primitives - // TODO Better way to create mirrors using the primitive pack P instead of rc? auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); auto B_host = B_P.GetHostMirror(); - auto q_host = rho.GetHostMirror(); // TODO Temporary initialization necessary for auto type + // Temporary initializations are necessary for auto type + auto q_host = rho.GetHostMirror(); auto dP_host = rho.GetHostMirror(); - if (use_emhd) - { + if (use_emhd) { q_host = q.GetHostMirror(); dP_host = dP.GetHostMirror(); } + // Set dirichlet boundary conditions + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; + bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; // Define ParArrays to store radial boundary values + // TODO could probably standardize index use a bit here IndexRange ib_in = pmb->cellbounds.GetBoundsI(IndexDomain::interior); IndexRange jb_in = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); IndexRange kb_in = pmb->cellbounds.GetBoundsK(IndexDomain::interior); const int n1 = pmb->cellbounds.ncellsi(IndexDomain::interior); - const int ng = (int)(ib.e - ib_in.e); + const int ng = ib.e - ib_in.e; - p_bound = ParArrayND("Dirichlet boundary values", nvar, n1 + 2*ng); - auto p_bound_host = p_bound.GetHostMirror(); + auto p_bound_left = rc->Get("bound.inner_x1").data; + auto p_bound_left_host = p_bound_left.GetHostMirror(); + auto p_bound_right = rc->Get("bound.outer_x1").data; + auto p_bound_right_host = p_bound_right.GetHostMirror(); // Load coordinates 'r' and compare against grid values double rCoords[n1 + 2*ng]; double error = 0.; for (int i = ib.s; i <= ib.e; i++) { - fscanf(fp_r, "%lf", &(rCoords[i])); GReal Xnative[GR_DIM], Xembed[GR_DIM]; G.coord(0, ng, i, Loci::center, Xnative); // j and k don't matter since we need to compare only the radial coordinate @@ -171,9 +165,9 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) error = fabs(Xembed[1] - rCoords[i]); if (error > 1.e-10) { fprintf(stdout, "Error at radial zone i = %d, Error = %8.5e KHARMA: %8.7e, sage nb: %8.7e\n", i, error, Xembed[1], rCoords[i]); + exit(-1); } } - if (error > 1.e-10) exit(-1); // Initialize primitives double rho_temp, u_temp, q_temp; @@ -208,9 +202,11 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) if (use_emhd) dP_host(k, j, i) = 0.; - // Note that the velocity primitives defined up there isn't quite right. + // Note that the velocity primitives defined up there aren't quite right. // For a fluid at rest wrt. the normal observer, ucon = {-1/g_tt,0,0,0}. // We need to use this info to obtain the correct values for U1, U2 and U3 + // TODO is this just fourvel_to_prim? + Real ucon[GR_DIM] = {0}; Real gcov[GR_DIM][GR_DIM] = {0}; @@ -238,58 +234,58 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) uvec_host(V2, k, j, i) = ucon[2] + beta[2]*gamma/alpha; uvec_host(V3, k, j, i) = ucon[3] + beta[3]*gamma/alpha; - if (use_emhd) - if (higher_order_terms){ + if (use_emhd) { + // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde + Real q_tilde = q_host(k, j, i); + Real dP_tilde = dP_host(k, j, i); + if (emhd_params.higher_order_terms) { + Real tau, chi_e, nu_e; + EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); const Real Theta = (gam - 1.) * u_temp / rho_temp; - // Set EMHD parameters - Real tau, chi_e, nu_e; - EMHD::set_parameters(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); - - // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde - Real q_tilde = q_host(k, j, i); - Real dP_tilde = dP_host(k, j, i); - if (emhd_params.higher_order_terms) { - q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho_temp * pow(Theta, 2.))) : 0.; - dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; - } - q_host(k, j, i) = q_tilde; - dP_host(k, j, i) = dP_tilde; + q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho_temp * pow(Theta, 2.))) : 0.; + dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; } - } - } + q_host(k, j, i) = q_tilde; + dP_host(k, j, i) = dP_tilde; + } - // Save boundary values for Dirichlet boundary conditions - if (i < ng) { - p_bound_host(0, i) = rho_host(0, ng, i); - p_bound_host(1, i) = u_host(0, ng, i); - p_bound_host(2, i) = uvec_host(V1, 0, ng, i); - p_bound_host(3, i) = uvec_host(V2, 0, ng, i); - p_bound_host(4, i) = uvec_host(V3, 0, ng, i); - p_bound_host(5, i) = B_host(V1, 0, ng, i); - p_bound_host(6, i) = B_host(V2, 0, ng, i); - p_bound_host(7, i) = B_host(V3, 0, ng, i); - if (use_emhd) - p_bound_host(8, i) = q_host(0, ng, i); - p_bound_host(9, i) = dP_host(0, ng, i); - } - if (i > n1 + ng - 1) { - p_bound_host(0, i-n1) = rho_host(0, ng, i); - p_bound_host(1, i-n1) = u_host(0, ng, i); - p_bound_host(2, i-n1) = uvec_host(V1, 0, ng, i); - p_bound_host(3, i-n1) = uvec_host(V2, 0, ng, i); - p_bound_host(4, i-n1) = uvec_host(V3, 0, ng, i); - p_bound_host(5, i-n1) = B_host(V1, 0, ng, i); - p_bound_host(6, i-n1) = B_host(V2, 0, ng, i); - p_bound_host(7, i-n1) = B_host(V3, 0, ng, i); - if (use_emhd) - p_bound_host(8, i-n1) = q_host(0, ng, i); - p_bound_host(9, i-n1) = dP_host(0, ng, i); + // Save boundary values for Dirichlet boundary conditions + if (i < ng) { + p_bound_left_host(m_p.RHO, k, j, i) = rho_host(k, j, i); + p_bound_left_host(m_p.UU, k, j, i) = u_host(k, j, i); + p_bound_left_host(m_p.U1, k, j, i) = uvec_host(V1, k, j, i); + p_bound_left_host(m_p.U2, k, j, i) = uvec_host(V2, k, j, i); + p_bound_left_host(m_p.U3, k, j, i) = uvec_host(V3, k, j, i); + p_bound_left_host(m_p.B1, k, j, i) = B_host(V1, k, j, i); + p_bound_left_host(m_p.B2, k, j, i) = B_host(V2, k, j, i); + p_bound_left_host(m_p.B3, k, j, i) = B_host(V3, k, j, i); + if (use_emhd) { + p_bound_left_host(m_p.Q, k, j, i) = q_host(k, j, i); + p_bound_left_host(m_p.DP, k, j, i) = dP_host(k, j, i); + } + } else if (i >= n1 + ng) { + int ii = i - (n1 + ng); + p_bound_right_host(m_p.RHO, k, j, ii) = rho_host(k, j, i); + p_bound_right_host(m_p.UU, k, j, ii) = u_host(k, j, i); + p_bound_right_host(m_p.U1, k, j, ii) = uvec_host(V1, k, j, i); + p_bound_right_host(m_p.U2, k, j, ii) = uvec_host(V2, k, j, i); + p_bound_right_host(m_p.U3, k, j, ii) = uvec_host(V3, k, j, i); + p_bound_right_host(m_p.B1, k, j, ii) = B_host(V1, k, j, i); + p_bound_right_host(m_p.B2, k, j, ii) = B_host(V2, k, j, i); + p_bound_right_host(m_p.B3, k, j, ii) = B_host(V3, k, j, i); + if (use_emhd) { + p_bound_right_host(m_p.Q, k, j, ii) = q_host(k, j, i); + p_bound_right_host(m_p.DP, k, j, ii) = dP_host(k, j, i); + } + } + } } } // disassociate file pointer + fclose(fp_r); fclose(fp_rho); fclose(fp_u); if (use_emhd) @@ -300,83 +296,15 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin) u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); B_P.DeepCopy(B_host); - if (use_emhd) - { + if (use_emhd) { q.DeepCopy(q_host); dP.DeepCopy(dP_host); } - p_bound.DeepCopy(p_bound_host); + p_bound_left.DeepCopy(p_bound_left_host); + p_bound_right.DeepCopy(p_bound_right_host); Kokkos::fence(); + Flag("Initialized"); return TaskStatus::complete; } - -TaskStatus dirichlet_bc(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - - Flag(rc, "Applying Dirichlet boundary conditions along radial direction"); - - auto pmb = rc->GetBlockPointer(); - const bool use_emhd = pmb->packages.AllPackages().count("EMHD"); - - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - GridScalar q; - GridScalar dP; - if (use_emhd) - { - q = rc->Get("prims.q").data; - dP = rc->Get("prims.dP").data; - } - - const auto& G = pmb->coords; - - IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - - // Need number of physical zones to access outer boundary elements of p_bound - const int n1 = pmb->cellbounds.ncellsi(IndexDomain::interior); - - pmb->par_for("dirichlet_boundary", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { - if (domain == IndexDomain::inner_x1) { - rho(k, j, i) = p_bound(0, i); - u(k, j, i) = p_bound(1, i); - uvec(V1, k, j, i) = p_bound(2, i); - uvec(V2, k, j, i) = p_bound(3, i); - uvec(V3, k, j, i) = p_bound(4, i); - B_P(V1, k, j, i) = p_bound(5, i); - B_P(V2, k, j, i) = p_bound(6, i); - B_P(V3, k, j, i) = p_bound(7, i); - if (use_emhd) - { - q(k, j, i) = p_bound(8, i); - dP(k, j, i) = p_bound(9, i); - } - } - else { - rho(k, j, i) = p_bound(0, i - n1); - u(k, j, i) = p_bound(1, i - n1); - uvec(V1, k, j, i) = p_bound(2, i - n1); - uvec(V2, k, j, i) = p_bound(3, i - n1); - uvec(V3, k, j, i) = p_bound(4, i - n1); - B_P(V1, k, j, i) = p_bound(5, i - n1); - B_P(V2, k, j, i) = p_bound(6, i - n1); - B_P(V3, k, j, i) = p_bound(7, i - n1); - if (use_emhd) - { - q(k, j, i) = p_bound(8, i - n1); - dP(k, j, i) = p_bound(9, i - n1); - } - } - } - ); - - return TaskStatus::complete; -} - -#endif diff --git a/kharma/prob/emhd/conducting_atmosphere.hpp b/kharma/prob/emhd/conducting_atmosphere.hpp index 56844aba..c9a75d16 100644 --- a/kharma/prob/emhd/conducting_atmosphere.hpp +++ b/kharma/prob/emhd/conducting_atmosphere.hpp @@ -44,6 +44,4 @@ #include -TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin); - -TaskStatus dirichlet_bc(MeshBlockData *rc, IndexDomain domain, bool coarse); \ No newline at end of file +TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, ParameterInput *pin); \ No newline at end of file diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 7763866c..a220cc35 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -47,17 +47,17 @@ using namespace parthenon; * Note the end time is not set -- even after exactly 1 period, EMHD modes will * have lost amplitude due to having viscosity, which is kind of the point */ -TaskStatus InitializeEMHDModes(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing EMHD Modes problem"); auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; // It is well and good this problem should cry if B/EMHD are disabled. GridVector B_P = rc->Get("prims.B").data; - GridVector q = rc->Get("prims.q").data; - GridVector dP = rc->Get("prims.dP").data; + GridVector q = rc->Get("prims.q").data; + GridVector dP = rc->Get("prims.dP").data; const auto& G = pmb->coords; @@ -96,7 +96,7 @@ TaskStatus InitializeEMHDModes(MeshBlockData *rc, ParameterInput *pin) IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("emhdmodes_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); const Real cos_phi = cos(k1*X[1] + k2*X[2]); @@ -128,7 +128,7 @@ TaskStatus InitializeEMHDModes(MeshBlockData *rc, ParameterInput *pin) if (emhd_params.higher_order_terms) { Real tau, chi_e, nu_e; - EMHD::set_parameters(G, rho(k, j, i), u(k, j, i), emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters_init(G, rho(k, j, i), u(k, j, i), emhd_params, gam, k, j, i, tau, chi_e, nu_e); Real Theta = (gam - 1) * u(k, j, i) / rho(k, j, i); Real q_tilde = q(k, j, i); Real dP_tilde = dP(k, j, i); diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index 1c17836a..16c6a6c1 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -54,8 +54,7 @@ using namespace parthenon; * * Therefore, to quantitatively check the EMHD implementation, we prefer the BVP solution as the input. */ - -TaskStatus InitializeEMHDShock(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing EMHD shock problem"); auto pmb = rc->GetBlockPointer(); @@ -140,7 +139,7 @@ TaskStatus InitializeEMHDShock(MeshBlockData *rc, ParameterInput *pin) // Set EMHD parameters Real tau, chi_e, nu_e; - EMHD::set_parameters(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); // Update q and dP (which now are q_tilde and dP_tilde) Real q_tilde = q_host(k, j, i); @@ -192,7 +191,7 @@ TaskStatus InitializeEMHDShock(MeshBlockData *rc, ParameterInput *pin) double B3L = 0., B3R = 0.; pmb->par_for("emhdshock_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); diff --git a/kharma/prob/explosion.hpp b/kharma/prob/explosion.hpp index acaebc61..65102d4f 100644 --- a/kharma/prob/explosion.hpp +++ b/kharma/prob/explosion.hpp @@ -48,7 +48,7 @@ using namespace parthenon; * * Originally run on 2D Cartesian domain -6.0, 6.0 with a 200x200 grid, to tlim=4.0 */ -TaskStatus InitializeExplosion(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeExplosion(std::shared_ptr>& rc, ParameterInput *pin) { auto pmb = rc->GetBlockPointer(); @@ -81,7 +81,7 @@ TaskStatus InitializeExplosion(MeshBlockData *rc, ParameterInput *pin) IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("explosion_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); const GReal rx = X[1] - xoff; @@ -102,8 +102,8 @@ TaskStatus InitializeExplosion(MeshBlockData *rc, ParameterInput *pin) const Real lrho_in = log(rho_in); const Real lu_out = log(u_out); const Real lu_in = log(u_in); - rho(k, j, i) = exp(lrho_out + ramp * (lrho_in - lrho_out)); - u(k, j, i) = exp(lu_out + ramp * (lu_in - lu_out)); + rho(k, j, i) = m::exp(lrho_out + ramp * (lrho_in - lrho_out)); + u(k, j, i) = m::exp(lu_out + ramp * (lu_in - lu_out)); } } else { rho(k, j, i) = rho_out; diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index 97cda914..3df6c52a 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -34,29 +34,32 @@ #include "fm_torus.hpp" -#include "mpi.hpp" +#include "floors.hpp" #include "prob_common.hpp" #include "types.hpp" #include #include "Kokkos_Random.hpp" -TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing torus problem"); - auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; + auto pmb = rc->GetBlockPointer(); + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; + GridVector B_P = rc->Get("prims.B").data; + + // Have a look at InitializeFMTorusEMHD for the EMHD torus initialization + const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", false); - const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); - const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); - const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); + const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); + const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); + const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); const GReal tilt_deg = pin->GetOrAddReal("torus", "tilt", 0.0); - const GReal tilt = tilt_deg / 180. * M_PI; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const GReal tilt = tilt_deg / 180. * M_PI; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); IndexDomain domain = IndexDomain::interior; const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); @@ -64,13 +67,13 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); // Get coordinate systems - // G clearly holds a reference to an existing system G.coords.base, - // but we don't know if it's KS or BL coordinates - // Since we can't create a system and assign later, we just - // rebuild copies of both based on the BH spin "a" - const auto& G = pmb->coords; - const bool use_ks = G.coords.is_ks(); - const GReal a = G.coords.get_a(); + // Different coordinate systems do not inherit from a base + // class (see coordinate_systems.hpp, coordinate_embedding.hpp) + // so we can't cast or assign them like you'd expect. + // Instead we just create copies of each one we'll need. + const auto& G = pmb->coords; + const bool use_ks = G.coords.is_ks(); + const GReal a = G.coords.get_a(); const SphBLCoords blcoords = SphBLCoords(a); const SphKSCoords kscoords = SphKSCoords(a); @@ -78,14 +81,14 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) Real l = lfish_calc(a, rmax); pmb->par_for("fm_torus_init", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal Xnative[GR_DIM], Xembed[GR_DIM], Xmidplane[GR_DIM]; G.coord(k, j, i, Loci::center, Xnative); G.coord_embed(k, j, i, Loci::center, Xembed); // What are our corresponding "midplane" values for evaluating the function? rotate_polar(Xembed, tilt, Xmidplane); - GReal r = Xmidplane[1], th = Xmidplane[2]; + GReal r = Xmidplane[1], th = Xmidplane[2]; GReal sth = sin(th); GReal cth = cos(th); @@ -103,16 +106,15 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) Real SS = r2 + a2 * cth * cth; // Calculate rho and u - Real hm1 = exp(lnh) - 1.; - Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), - 1. / (gam - 1.)); - Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); + Real hm1 = m::exp(lnh) - 1.; + Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), 1. / (gam - 1.)); + Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); // Calculate u^phi Real expm2chi = SS * SS * DD / (AA * AA * sth * sth); - Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); - Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + - m::sqrt(SS / AA) * up1 / sth; + Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); + Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + + m::sqrt(SS / AA) * up1 / sth; const Real ucon_tilt[GR_DIM] = {0., 0., 0., up}; Real ucon_bl[GR_DIM]; @@ -162,7 +164,7 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) // If we print diagnostics, do so only from block 0 as the others do exactly the same thing // Since this is initialization, we are guaranteed to have a block 0 - if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Calculating maximum density:" << std::endl; std::cout << "a = " << a << std::endl; std::cout << "dx = " << dx << std::endl; @@ -172,10 +174,11 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) //cout << "nx2 = " << nx2 << std::endl; } + // TODO split this out Real rho_max = 0; Kokkos::Max max_reducer(rho_max); pmb->par_reduce("fm_torus_maxrho", 0, nx1, - KOKKOS_LAMBDA_1D_REDUCE { + KOKKOS_LAMBDA (const int &i, parthenon::Real &local_result) { GReal x1 = x1min + i*dx; //GReal x2 = x2min + j*dx; GReal Xnative[GR_DIM] = {0,x1,0,0}; @@ -195,22 +198,28 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin) // Record and print normalization factor if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rho_norm"))) pmb->packages.Get("GRMHD")->AllParams().Add("rho_norm", rho_max); - if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Initial maximum density is " << rho_max << std::endl; } pmb->par_for("fm_torus_normalize", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { rho(k, j, i) /= rho_max; u(k, j, i) /= rho_max; } ); + // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) + // Since the conserved vars U are not initialized, this is done in *fluid frame*, + // even if NOF frame is chosen (iharm3d does the same iiuc) + // This is probably not a huge issue, just good to state explicitly + Floors::ApplyInitialFloors(rc.get(), IndexDomain::interior); + return TaskStatus::complete; } // TODO move this to a different file -TaskStatus PerturbU(MeshBlockData *rc, ParameterInput *pin) +TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Applying U perturbation"); auto pmb = rc->GetBlockPointer(); @@ -219,12 +228,12 @@ TaskStatus PerturbU(MeshBlockData *rc, ParameterInput *pin) const Real u_jitter = pin->GetReal("perturbation", "u_jitter"); // Don't jitter values set by floors - const Real jitter_above_rho = pin->GetReal("floors", "rho_min_geom"); + const Real jitter_above_rho = pin->GetReal("floors", "rho_min_geom") + 1e-10; // Note we add the MeshBlock gid to this value when seeding RNG, // to get a new sequence for every block const int rng_seed = pin->GetOrAddInteger("perturbation", "rng_seed", 31337); // Print real seed used for all blocks, to ensure they're different - if (pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Seeding RNG in block " << pmb->gid << " with value " << rng_seed + pmb->gid << std::endl; } const bool serial = pin->GetOrAddInteger("perturbation", "serial", false); @@ -253,7 +262,7 @@ TaskStatus PerturbU(MeshBlockData *rc, ParameterInput *pin) RandPoolType rand_pool(rng_seed + pmb->gid); typedef typename RandPoolType::generator_type gen_type; pmb->par_for("perturb_u", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { if (rho(k, j, i) > jitter_above_rho) { gen_type rgen = rand_pool.get_state(); u(k, j, i) *= 1. + Kokkos::rand::draw(rgen, -u_jitter/2, u_jitter/2); diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index 3c1a8d02..0f87e949 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -10,7 +10,10 @@ * @param rin is the torus innermost radius, in r_g * @param rmax is the radius of maximum density of the F-M torus in r_g */ -TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin); +TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin); +/* Need a different initialization function since we have additional fields (q, dP) + * for the EMHD problem that are declared at runtime*/ +TaskStatus InitializeFMTorusEMHD(std::shared_ptr>& rc, ParameterInput *pin); /** * Perturb the internal energy by a uniform random proportion per cell. * Resulting internal energies will be between u \pm u*u_jitter/2 @@ -19,7 +22,7 @@ TaskStatus InitializeFMTorus(MeshBlockData *rc, ParameterInput *pin); * @param u_jitter see description * @param rng_seed is added to the MPI rank to seed the GSL RNG */ -TaskStatus PerturbU(MeshBlockData *rc, ParameterInput *pin); +TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin); /** * Torus solution for ln h, See Fishbone and Moncrief eqn. 3.6. @@ -106,7 +109,7 @@ KOKKOS_INLINE_FUNCTION Real fm_torus_rho(const GReal a, const GReal rin, const G Real lnh = lnh_calc(a, l, rin, r, th); if (lnh >= 0. && r >= rin) { // Calculate rho - Real hm1 = exp(lnh) - 1.; + Real hm1 = m::exp(lnh) - 1.; return m::pow(hm1 * (gam - 1.) / (kappa * gam), 1. / (gam - 1.)); } else { diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index cb4472eb..a183beb2 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -395,7 +395,14 @@ int hdf5_read_array(void *data, const char *name, size_t rank, strncpy(path, hdf5_cur_dir, STRLEN); strncat(path, name, STRLEN - strlen(path)); - if(DEBUG) fprintf(stderr,"Reading arr %s\n", path); + if(DEBUG) { + fprintf(stderr,"Reading arr %s:\n", path); + fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); + fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); + fprintf(stderr,"File read size: %llu %llu %llu %llu\n", fcount[0], fcount[1], fcount[2], fcount[3]); + fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); + } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index 234cd3e0..9827bf71 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -35,171 +35,89 @@ #include "decs.hpp" -// For using the ipole routines verbatim. -// Automatically wraps in k so we can avoid ghost zones -#define ind_sph(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (j) * n1 + (i)) -#define ind_periodic(i, j, k) ( (((k)+n3) % n3) * n2 * n1 + (((j)+n2) % n2) * n1 + (((i)+n1) % n1) ) - /** - * Routines for interpolating and initializing a KHARMA meshblock from the - * correct area of a global iharm3d restart file, used in resize_restart.cpp. - * Doesn't include "Elliptic maid" solver step for eliminating magnetic field - * divergence, see b_flux_ct for that (as it is divergence-rep dependent) + * Routines for interpolating on a grid, using values given in a flattened array. + * Mostly used in resize_restart.cpp, which must interpolate from old simulation + * data. + * + * Note that resizing or resampling of magnetic fields usually requires + * fixing a resulting divergence -- see b_cleanup/ for details. */ +namespace Interpolation { /** - * translates geodesic coordinates to a grid zone and returns offset - * for interpolation purposes. integer index corresponds to the zone - * center "below" the desired point and del[i] \in [0,1) returns the - * offset from that zone center. - * - * 0 0.5 1 - * [ | ] - * A B C DE F - * - * startx = 0. - * dx = 0.5 - * - * A -> (-1, 0.5) - * B -> ( 0, 0.0) - * C -> ( 0, 0.5) - * D -> ( 0, 0.9) - * E -> ( 1, 0.0) - * F -> ( 1, 0.5) + * Finds the closest grid zone index (i,j,k) with a center left of the given point. + * Additionally returns the point's proportional distance measured from the left + * zone center to the right (e.g., to (i+1, j, k) in X1) + * + * This proportion is useful in interpolation, since linear interpolation corresponds to + * del*var[i+1] + (1. - del)*var[i] */ -KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal XG[GR_DIM], +KOKKOS_INLINE_FUNCTION void Xtoijk(const GReal X[GR_DIM], const GReal startx[GR_DIM], const GReal dx[GR_DIM], - int& i, int& j, int& k, GReal del[GR_DIM], - bool nearest=false) -{ - // If we ever include ghosts in iharm3d-format restarts, we need to clip phi here - // GReal phi = fmod(XG[3], stopx[3]); - // if (phi < 0.0) // TODO adapt for startx3 != 0? - // phi += stopx[3]; - GReal phi = XG[3]; - - if (nearest) { - // get the index of the zone we are in: >= left corner? - i = (int) ((XG[1] - startx[1]) / dx[1] + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] + 1000) - 1000; - } else { - // Normal operation - // get provisional zone index. see note above function for details. note we - // shift to zone centers because that's where variables are most exact. - i = (int) ((XG[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; - j = (int) ((XG[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; - k = (int) ((phi - startx[3]) / dx[3] - 0.5 + 1000) - 1000; - } - - // now construct del - del[1] = (XG[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; - del[2] = (XG[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; - del[3] = (phi - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; -} - -KOKKOS_INLINE_FUNCTION void ijktoX(const GReal startx[GR_DIM], const GReal dx[GR_DIM], - const int& i, const int& j, const int& k, - GReal XG[GR_DIM]) + int& i, int& j, int& k, GReal del[GR_DIM]) { + // Normal operation // get provisional zone index. see note above function for details. note we // shift to zone centers because that's where variables are most exact. - XG[0] = 0.; - XG[1] = startx[1] + (i + 0.5) * dx[1]; - XG[2] = startx[2] + (j + 0.5) * dx[2]; - XG[3] = startx[3] + (k + 0.5) * dx[3]; + i = (int) ((X[1] - startx[1]) / dx[1] - 0.5 + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] - 0.5 + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] - 0.5 + 1000) - 1000; + + // Distance from closest zone center on the left + // i.e., portion of left zone to use vs right when interpolating + del[1] = (X[1] - ((i + 0.5) * dx[1] + startx[1])) / dx[1]; + del[2] = (X[2] - ((j + 0.5) * dx[2] + startx[2])) / dx[2]; + del[3] = (X[3] - ((k + 0.5) * dx[3] + startx[3])) / dx[3]; } /** - * This interpolates a single-array variable 'var' representing a grid of size 'startx' to 'stopx' in - * native coordinates, returning its value at location X - * NOTE: 'startx' must correspond to the grid you are interpolating *from* + * Return the grid zone index (i,j,k) corresponding which contains the point X. + * Note this is different from the above! */ -KOKKOS_INLINE_FUNCTION Real linear_interp(const GRCoordinates& G, const GReal X[GR_DIM], - const GReal startx[GR_DIM], - const GReal dx[GR_DIM], const bool& is_spherical, const bool& weight_by_gdet, - const int& n3, const int& n2, const int& n1, - const Real *var) +KOKKOS_INLINE_FUNCTION void Xtoijk_nearest(const GReal X[GR_DIM], + const GReal startx[GR_DIM], + const GReal dx[GR_DIM], + int& i, int& j, int& k) { - // zone and offset from X - // Obtain this in - GReal del[GR_DIM]; - int i, j, k; - Xtoijk(X, startx, dx, i, j, k, del); - - Real interp; - if (is_spherical) { - // For ghost zones, we treat each boundary differently: - // In X1, repeat first & last zones. - if (i < 0) { i = 0; del[1] = 0; } - if (i > n1-2) { i = n1 - 2; del[1] = 1; } - // In X2, stop completely at the last zone - // Left side of leftmost segment - if (j < 0) { j = 0; del[2] = 0; } - // Right side of rightmost segment. Phrased this way to not segfault - if (j > n2-2) { j = n2 - 2; del[2] = 1; } - // k auto-wraps. So do all indices for periodic boxes. - - if (weight_by_gdet) { - GReal Xtmp[GR_DIM]; - ijktoX(startx, dx, i, j, k, Xtmp); - GReal g_ij = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j, k, Xtmp); - GReal g_i1j = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i, j + 1, k, Xtmp); - GReal g_ij1 = G.coords.gdet_native(Xtmp); - ijktoX(startx, dx, i + 1, j + 1, k, Xtmp); - GReal g_i1j1 = G.coords.gdet_native(Xtmp); - - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*g_i1j1*del[1]*del[2]; - - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*g_ij*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*g_ij1*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*g_i1j*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*g_i1j1*del[1]*del[2]); - } - interp /= G.coords.gdet_native(X); - } else { - // interpolate in x1 and x2 - interp = var[ind_sph(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k)]*del[1]*del[2]; + // Get the index of the zone this point falls into. + // i.e., are we >= the left corner? + i = (int) ((X[1] - startx[1]) / dx[1] + 1000) - 1000; + j = (int) ((X[2] - startx[2]) / dx[2] + 1000) - 1000; + k = (int) ((X[3] - startx[3]) / dx[3] + 1000) - 1000; +} - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_sph(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_sph(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_sph(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_sph(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } - } - } else { - // interpolate in x1 and x2 - interp = var[ind_periodic(i , j , k)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k)]*del[1]*del[2]; +// For using the ipole routines in a recognizable form on a 1D array +#define ind(i, j, k) ( (k) * n2 * n1 + (j) * n1 + (i)) - // then interpolate in x3 if we need - if (n3 > 1) { - interp = (1. - del[3])*interp + - del[3]*(var[ind_periodic(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + - var[ind_periodic(i , j + 1, k + 1)]*(1. - del[1])*del[2] + - var[ind_periodic(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + - var[ind_periodic(i + 1, j + 1, k + 1)]*del[1]*del[2]); - } +/** + * Dumb linear interpolation: no special cases for boundaries. + * Takes indices i,j,k and a block size n1, n2, n3, + * as well as a flat array var. + * + * TODO version(s) with View(s) for real device-side operation + */ +KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, + const int& n1, const int& n2, const int& n3, + const double del[4], const double *var) +{ + // Interpolate in 1D at a time to avoid reading zones we don't have + Real interp = var[ind(i , j , k)]*(1. - del[1]) + + var[ind(i + 1, j , k)]*del[1]; + if (n2 > 1) { + interp = (1. - del[2])*interp + + del[2]*(var[ind(i , j + 1, k)]*(1. - del[1]) + + var[ind(i + 1, j + 1, k)]*del[1]); + } + if (n3 > 1) { + interp = (1. - del[3])*interp + + del[3]*(var[ind(i , j , k + 1)]*(1. - del[1])*(1. - del[2]) + + var[ind(i + 1, j , k + 1)]*del[1]*(1. - del[2]) + + var[ind(i , j + 1, k + 1)]*(1. - del[1])*del[2] + + var[ind(i + 1, j + 1, k + 1)]*del[1]*del[2]); } - return interp; } +} // Interpolation \ No newline at end of file diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 35f23ac1..1e2bdb34 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -43,7 +43,7 @@ * Follows initial conditions from Lecoanet et al. 2015, * MNRAS 455, 4274. */ -TaskStatus InitializeKelvinHelmholtz(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, ParameterInput *pin) { auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; @@ -71,7 +71,7 @@ TaskStatus InitializeKelvinHelmholtz(MeshBlockData *rc, ParameterInput *pi IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("kh_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); @@ -84,14 +84,14 @@ TaskStatus InitializeKelvinHelmholtz(MeshBlockData *rc, ParameterInput *pi u(k, j, i) = P0 / (gam - 1.); uvec(0, k, j, i) = uflow * (tanh((z - z1) / a) - tanh((z - z2) / a) - 1.); uvec(1, k, j, i) = A * sin(2. * M_PI * x) * - (exp(-(z - z1) * (z - z1) / (sigma * sigma)) + - exp(-(z - z2) * (z - z2) / (sigma * sigma))); + (m::exp(-(z - z1) * (z - z1) / (sigma * sigma)) + + m::exp(-(z - z2) * (z - z2) / (sigma * sigma))); uvec(2, k, j, i) = 0; } ); // Rescale primitive velocities by tscale, and internal energy by the square. pmb->par_for("kh_renorm", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { u(k, j, i) *= tscale * tscale; VLOOP uvec(v, k, j, i) *= tscale; } diff --git a/kharma/prob/mhdmodes.hpp b/kharma/prob/mhdmodes.hpp index 114277e2..e4b12bbb 100644 --- a/kharma/prob/mhdmodes.hpp +++ b/kharma/prob/mhdmodes.hpp @@ -57,7 +57,7 @@ using namespace parthenon; * Generally this is what we want for tests (run by 1 cycle and compare). * Modify function or reset tlim after to override. */ -TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeMHDModes(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing MHD Modes problem"); auto pmb = rc->GetBlockPointer(); @@ -72,6 +72,10 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) const int dir = pin->GetOrAddInteger("mhdmodes", "dir", 0); const bool one_period = pin->GetOrAddBoolean("mhdmodes", "one_period", true); + // if (pin->GetInteger("parthenon/mesh", "nx1")) { + // dir = 3; + // } + // START POSSIBLE ARGS: take all these as parameters in pin? // Mean state Real rho0 = 1.; @@ -107,16 +111,12 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) Real dB1 = 0, dB2 = 0, dB3 = 0; // Eigenmode definitions - if (dir == 0) - { + if (dir == 0) { // 3D (1,1,1) wave B10 = 1.; - if (nmode == 0) - { // Entropy + if (nmode == 0) { // Entropy drho = 1.; - } - else if (nmode == 1) - { // Slow + } else if (nmode == 1) { // Slow omega = 2.35896379113i; drho = 0.556500332363; du = 0.742000443151; @@ -126,17 +126,13 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) dB1 = -0.195509141461; dB2 = 0.0977545707307; dB3 = 0.0977545707307; - } - else if (nmode == 2) - { // Alfven + } else if (nmode == 2) { // Alfven omega = -3.44144232573i; du2 = -0.339683110243; du3 = 0.339683110243; dB2 = 0.620173672946; dB3 = -0.620173672946; - } - else - { // Fast + } else { // Fast omega = 6.92915162882i; drho = 0.481846076323; du = 0.642461435098; @@ -152,90 +148,63 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) { // 2D (1,1,0), (1,0,1), (0,1,1) wave // Constant field direction - if (dir == 1) - { + if (dir == 1) { B20 = 1.; - } - else if (dir == 2) - { + } else if (dir == 2) { B30 = 1.; - } - else if (dir == 3) - { + } else if (dir == 3) { B10 = 1.; } - if (nmode == 0) - { // Entropy + if (nmode == 0) { // Entropy drho = 1.; - } - else if (nmode == 1) - { // Slow + } else if (nmode == 1) { // Slow omega = 2.41024185339i; drho = 0.558104461559; du = 0.744139282078; - if (dir == 1) - { + if (dir == 1) { du2 = -0.277124827421; du3 = 0.0630348927707; dB2 = -0.164323721928; dB3 = 0.164323721928; - } - else if (dir == 2) - { + } else if (dir == 2) { du3 = -0.277124827421; du1 = 0.0630348927707; dB3 = -0.164323721928; dB1 = 0.164323721928; - } - else if (dir == 3) - { + } else if (dir == 3) { du1 = -0.277124827421; du2 = 0.0630348927707; dB1 = -0.164323721928; dB2 = 0.164323721928; } - } - else if (nmode == 2) - { // Alfven + } else if (nmode == 2) { // Alfven omega = 3.44144232573i; - if (dir == 1) - { + if (dir == 1) { du1 = 0.480384461415; dB1 = 0.877058019307; - } - else if (dir == 2) - { + } else if (dir == 2) { du2 = 0.480384461415; dB2 = 0.877058019307; - } - else if (dir == 3) - { + } else if (dir == 3) { du3 = 0.480384461415; dB3 = 0.877058019307; } - } - else - { // Fast + } else { // Fast omega = 5.53726217331i; drho = 0.476395427447; du = 0.635193903263; - if (dir == 1) - { + if (dir == 1) { du2 = -0.102965815319; du3 = -0.316873207561; dB2 = 0.359559114174; dB3 = -0.359559114174; - } - else if (dir == 2) - { + } else if (dir == 2) { du3 = -0.102965815319; du1 = -0.316873207561; dB3 = 0.359559114174; dB1 = -0.359559114174; - } - else if (dir == 3) - { + } else if (dir == 3) { du1 = -0.102965815319; du2 = -0.316873207561; dB1 = 0.359559114174; @@ -249,7 +218,7 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("mhdmodes_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); @@ -270,5 +239,6 @@ TaskStatus InitializeMHDModes(MeshBlockData *rc, ParameterInput *pin) pin->SetReal("parthenon/time", "tlim", 2. * M_PI / m::abs(omega.imag())); } + Flag(rc, "Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/orszag_tang.hpp b/kharma/prob/orszag_tang.hpp index 144c0085..74ea6500 100644 --- a/kharma/prob/orszag_tang.hpp +++ b/kharma/prob/orszag_tang.hpp @@ -17,7 +17,7 @@ using namespace parthenon; * * Stolen directly from iharm2d_v3 */ -TaskStatus InitializeOrszagTang(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing Orszag-Tang problem"); auto pmb = rc->GetBlockPointer(); @@ -38,7 +38,7 @@ TaskStatus InitializeOrszagTang(MeshBlockData *rc, ParameterInput *pin) IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); IndexRange kb = pmb->cellbounds.GetBoundsK(domain); pmb->par_for("ot_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord(k, j, i, Loci::center, X); rho(k, j, i) = 25./9.; @@ -53,7 +53,7 @@ TaskStatus InitializeOrszagTang(MeshBlockData *rc, ParameterInput *pin) ); // Rescale primitive velocities & B field by tscale, and internal energy by the square. pmb->par_for("ot_renorm", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { u(k, j, i) *= tscale * tscale; VLOOP uvec(v, k, j, i) *= tscale; VLOOP B_P(v, k, j, i) *= tscale; diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 18f342b2..9041477f 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -44,38 +44,86 @@ #include "gr_coordinates.hpp" #include "grmhd.hpp" #include "kharma.hpp" -#include "mpi.hpp" +#include "kharma_driver.hpp" +#include "reductions.hpp" #include "types.hpp" #include "seed_B_ct.hpp" #include "seed_B_cd.hpp" +/** + * Perform a Parthenon MPI reduction. + * Should only be used in initialization code, as the + * reducer object & MPI comm are created on entry & + * cleaned on exit + */ +template +inline T MPIReduce_once(T f, MPI_Op O) +{ + parthenon::AllReduce reduction; + reduction.val = f; + reduction.StartReduce(O); + // Wait on results + while (reduction.CheckReduce() == parthenon::TaskStatus::incomplete); + // TODO catch errors? + return reduction.val; +} + +// Define reductions we need just for PostInitialize code. +// TODO namespace... +KOKKOS_INLINE_FUNCTION Real bsq(REDUCE_FUNCTION_ARGS_MESH) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + return dot(Dtmp.bcon, Dtmp.bcov); +} +KOKKOS_INLINE_FUNCTION Real gas_pres(REDUCE_FUNCTION_ARGS_MESH) +{ + return (gam - 1) * P(m_p.UU, k, j, i); +} +KOKKOS_INLINE_FUNCTION Real gas_beta(REDUCE_FUNCTION_ARGS_MESH) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + return ((gam - 1) * P(m_p.UU, k, j, i))/(0.5*(dot(Dtmp.bcon, Dtmp.bcov) + SMALL)); +} +Real MaxBsq(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::max, bsq, 0.0); +} +Real MaxPressure(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::max, gas_pres, 0.0); +} +Real MinBeta(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::min, gas_beta, 0.0); +} + void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr> md) { // Check which solver we'll be using auto pmesh = md->GetMeshPointer(); const bool use_b_flux_ct = pmesh->packages.AllPackages().count("B_FluxCT"); const bool use_b_cd = pmesh->packages.AllPackages().count("B_CD"); + const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); // Add the field for torus problems as a second pass // Preserves P==U and ends with all physical zones fully defined if (pin->GetOrAddString("b_field", "type", "none") != "none") { - // Calculating B has a stencil outside physical zones - Flag("Extra boundary sync for B"); - KBoundaries::SyncAllBounds(md); - // "Legacy" is the much more common normalization: // It's the ratio of max values over the domain i.e. max(P) / max(P_B), // not necessarily a local min(beta) Real beta_calc_legacy = pin->GetOrAddBoolean("b_field", "legacy", true); Flag("Seeding magnetic field"); - // Seed the magnetic field and find the minimum beta + // Seed the magnetic field on each block Real beta_min = 1.e100, p_max = 0., bsq_max = 0., bsq_min = 0.; for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); // This initializes B_P & B_U + // TODO callback, also what about B_Cleanup? if (use_b_flux_ct) { B_FluxCT::SeedBField(rc.get(), pin); } else if (use_b_cd) { @@ -92,18 +140,6 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr bsq_max) bsq_max = bsq_local; - bsq_local = GetLocalBsqMin(rc.get()); - if(bsq_local < bsq_min) bsq_min = bsq_local; - Real p_local = GetLocalPMax(rc.get()); - if(p_local > p_max) p_max = p_local; - } else { - Real beta_local = GetLocalBetaMin(rc.get()); - if(beta_local < beta_min) beta_min = beta_local; - } } // Then, if we're in a torus problem or explicitly ask for it, @@ -115,21 +151,21 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrGetOrAddReal("b_field", "beta_min", 100.); // Calculate current beta_min value + Real bsq_min, bsq_max, p_max, beta_min; if (beta_calc_legacy) { - bsq_max = MPIReduce_once(bsq_max, MPI_MAX); - bsq_min = MPIReduce_once(bsq_min, MPI_MIN); - p_max = MPIReduce_once(p_max, MPI_MAX); + bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); beta_min = p_max / (0.5 * bsq_max); } else { - beta_min = MPIReduce_once(beta_min, MPI_MIN); + beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); } - if (pin->GetInteger("debug", "verbose") > 0) { - if (MPIRank0()) { - std::cerr << "bsq_max pre-norm: " << bsq_max << std::endl; - std::cerr << "bsq_min pre-norm: " << bsq_min << std::endl; - std::cerr << "Beta min pre-norm: " << beta_min << std::endl; + if (MPIRank0() && verbose > 0) { + if (beta_calc_legacy) { + std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; + std::cout << "Pressure max pre-norm: " << p_max << std::endl; } + std::cout << "Beta min pre-norm: " << beta_min << std::endl; } // Then normalize B by sqrt(beta/beta_min) @@ -138,101 +174,105 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrblock_list) { auto& rc = pmb->meshblock_data.Get(); - NormalizeBField(rc.get(), norm); + KHARMADriver::Scale(std::vector{"prims.B"}, rc.get(), norm); } } } - if (pin->GetInteger("debug", "verbose") > 0) { - // Measure again to check, and add divB for good measure - beta_min = 1e100; p_max = 0.; bsq_max = 0.; - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - - if (beta_calc_legacy) { - Real bsq_local = GetLocalBsqMax(rc.get()); - if(bsq_local > bsq_max) bsq_max = bsq_local; - Real p_local = GetLocalPMax(rc.get()); - if(p_local > p_max) p_max = p_local; - } else { - Real beta_local = GetLocalBetaMin(rc.get()); - if(beta_local < beta_min) beta_min = beta_local; - } - } + if (verbose > 0) { + // Measure again to check. We'll add divB too, later + Real bsq_min, bsq_max, p_max, beta_min; if (beta_calc_legacy) { - bsq_max = MPIReduce_once(bsq_max, MPI_MAX); - p_max = MPIReduce_once(p_max, MPI_MAX); + bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); beta_min = p_max / (0.5 * bsq_max); } else { - beta_min = MPIReduce_once(beta_min, MPI_MIN); + beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); } if (MPIRank0()) { - std::cerr << "bsq_max post-norm: " << bsq_max << std::endl; - std::cerr << "Beta min post-norm: " << beta_min << std::endl; + if (beta_calc_legacy) { + std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; + std::cout << "Pressure max pre-norm: " << p_max << std::endl; + } + std::cout << "Beta min pre-norm: " << beta_min << std::endl; } } } + // We've been initializing/manipulating P + Flux::MeshPtoU(md.get(), IndexDomain::entire); + // Synchronize after + KHARMADriver::SyncAllBounds(md); + Flag("Added B Field"); } -void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart, bool is_resize) +void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) { Flag("Post-initialization started"); + // This call: + // 1. Initializes any magnetic fields which are "seeded," i.e., defined with a magnetic field implementation + // rather than assuming an implementation and setting the field with problem initialization. + // 2. Renormalizes magnetic fields based on a desired ratio of maximum magnetic/gas pressures + // 3. Adds any extra material which might be superimposed when restarting, e.g. "hotspot" regions a.k.a. "blobs" + // 4. Resets a couple of incidental flags, if Parthenon read them from a restart file + // 5. If necessary, cleans up any magnetic field divergence present on the grid + + // Coming into this function, the *interior* regions should be initialized with a problem: + // that is, at least rho, u, uvec on each physical zone. + // If your problem requires custom boundary conditions, these should be implemented + // with the problem and assigned to the relevant functions in the "Boundaries" package. // Make sure we've built the MeshData object we'll be synchronizing/updating auto &md = pmesh->mesh_data.GetOrAdd("base", 0); - if (!is_restart) - KHARMA::SeedAndNormalizeB(pin, md); + auto& pkgs = pmesh->packages.AllPackages(); - if (pin->GetString("b_field", "solver") != "none") { - // Synchronize our seeded or initialized field (incl. primitives) before we print out what divB it has - KBoundaries::SyncAllBounds(md); + // First, make sure any data from the per-block init is synchronized + Flag("Initial boundary sync"); + KHARMADriver::SyncAllBounds(md); - const bool use_b_flux_ct = pmesh->packages.AllPackages().count("B_FluxCT"); - const bool use_b_cd = pmesh->packages.AllPackages().count("B_CD"); + // Then, add/modify any magnetic field left until this step + // (since B field initialization can depend on global maxima, + // & is handled by the B field transport package, it's sometimes done here) + if (!is_restart) { + KHARMA::SeedAndNormalizeB(pin, md); + } - // Still print divB, even if we're not initializing/normalizing field here - if (use_b_flux_ct) { + // Print divB + if (pin->GetString("b_field", "solver") != "none") { + // If a B field exists, print divB here + if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); - } // TODO B_CD version + } else if (pkgs.count("B_CD")) { + //B_CD::PrintGlobalMaxDivB(md.get()); + } } + // Add any hotspots. + // Note any other modifications made when restarting should be made around here if (pin->GetOrAddBoolean("blob", "add_blob", false)) { for (auto &pmb : pmesh->block_list) { auto rc = pmb->meshblock_data.Get(); // This inserts only in vicinity of some global r,th,phi InsertBlob(rc.get(), pin); } + KHARMADriver::SyncAllBounds(md); } - // Sync to fill the ghost zones: prims for ImExDriver, everything for HARMDriver - Flag("Boundary sync"); - KBoundaries::SyncAllBounds(md); - - // Extra cleanup & init to do if restarting + // Any extra cleanup & init especially when restarting if (is_restart) { - // Parthenon restored our global data for us, but we don't always want that + // Parthenon restores all parameters (global vars) when restarting, + // but KHARMA needs a few (currently one) reset instead KHARMA::ResetGlobals(pin, pmesh); } - // If we resized the array, cleanup any field divergence we created - // Let the user specify to do this, too - if ((is_restart && is_resize && !pin->GetOrAddBoolean("resize_restart", "skip_b_cleanup", false)) - || pin->GetBoolean("b_field", "initial_cleanup")) { - // Clean field divergence across the whole grid - // Includes boundary syncs + // Clean the B field if we've introduced a divergence somewhere + // Call this any time the package is loaded, all the + // logic about parsing whether to clean is there + if (pkgs.count("B_Cleanup")) { B_Cleanup::CleanupDivergence(md); } - if (MPIRank0()) { - std::cout << "Packages in use: " << std::endl; - for (auto pkg : pmesh->packages.AllPackages()) { - std::cout << pkg.first << std::endl; - } - std::cout << std::endl; - } - Flag("Post-initialization finished"); } diff --git a/kharma/prob/post_initialize.hpp b/kharma/prob/post_initialize.hpp index e520bd2c..9da817e4 100644 --- a/kharma/prob/post_initialize.hpp +++ b/kharma/prob/post_initialize.hpp @@ -51,11 +51,12 @@ void SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr> md); /** * Functions run over the entire mesh after per-block initialization: - * 1. Initialize magnetic field, which must be normalized globally to respect beta_min parameter - * 2. Any ad-hoc additions to fluid state, e.g. add hotspots etc. - * 3. Initial boundary sync to populate ghost zones - * 4. On restarts, reset any per-run parameters & clean up B field divergence if resizing the grid + * 1. Initial boundary sync to populate ghost zones + * 2. Initialize magnetic field, which must be normalized globally to respect beta_min parameter + * 3. Any ad-hoc additions to fluid state, e.g. add hotspots etc. + * 4. On restarts, reset any per-run parameters + * 5. Clean up B field divergence if resizing the grid */ -void PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart, bool is_resize); +void PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart); } diff --git a/kharma/prob/prob_common.hpp b/kharma/prob/prob_common.hpp index 099135ed..ce7a883a 100644 --- a/kharma/prob/prob_common.hpp +++ b/kharma/prob/prob_common.hpp @@ -163,6 +163,28 @@ KOKKOS_INLINE_FUNCTION void rotate_polar_vec(const GReal Xin[GR_DIM], const GRea } } +/** + * + */ +// KOKKOS_INLINE_FUNCTION void bl_fourv_to_native_prim(const Real Xembed[GR_DIM], const Real ucon_bl[GR_DIM], +// Real u_prim[GR_DIM]) +// { + +// Real gcov_bl[GR_DIM][GR_DIM]; +// bl.gcov_embed(Xembed, gcov_bl); +// set_ut(gcov_bl, ucon_bl); + +// // Then transform that 4-vector to KS, then to native +// Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; +// ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); +// cs.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + +// // Convert native 4-vector to primitive u-twiddle, see Gammie '04 +// Real gcon[GR_DIM][GR_DIM]; +// G.gcon(Loci::center, j, i, gcon); +// fourvel_to_prim(gcon, ucon_mks, u_prim); +// } + /** * Set time component for a consistent 4-velocity given a 3-velocity */ diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 507ca53e..f72b5faa 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -37,10 +37,12 @@ #include "b_field_tools.hpp" #include "boundaries.hpp" #include "debug.hpp" +#include "electrons.hpp" #include "floors.hpp" #include "flux.hpp" #include "gr_coordinates.hpp" #include "grmhd.hpp" +#include "grmhd_functions.hpp" #include "types.hpp" // Problem initialization headers @@ -54,28 +56,24 @@ #include "mhdmodes.hpp" #include "orszag_tang.hpp" #include "shock_tube.hpp" -#include "noh.hpp" - +#include "hubble.hpp" +// EMHD problem headers #include "emhd/anisotropic_conduction.hpp" #include "emhd/emhdmodes.hpp" #include "emhd/emhdshock.hpp" #include "emhd/conducting_atmosphere.hpp" -#include "emhd/bondi_viscous.hpp" - -#include "b_field_tools.hpp" - -// Package headers -#include "grmhd_functions.hpp" +// Electron problem headers +#include "elec/driven_turbulence.hpp" +#include "elec/hubble.hpp" +#include "elec/noh.hpp" -#include "bvals/boundary_conditions.hpp" -#include "mesh/mesh.hpp" using namespace parthenon; void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) { auto rc = pmb->meshblock_data.Get(); - Flag(rc.get(), "Initializing Block"); + Flag(rc, "Initializing Block"); // Breakout to call the appropriate initialization function, // defined in accompanying headers. @@ -97,42 +95,47 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) std::cout << "Initializing problem: " << prob << std::endl; } TaskStatus status = TaskStatus::fail; - // GRMHD + // MHD if (prob == "mhdmodes") { - status = InitializeMHDModes(rc.get(), pin); + status = InitializeMHDModes(rc, pin); } else if (prob == "orszag_tang") { - status = InitializeOrszagTang(rc.get(), pin); + status = InitializeOrszagTang(rc, pin); } else if (prob == "explosion") { - status = InitializeExplosion(rc.get(), pin); + status = InitializeExplosion(rc, pin); } else if (prob == "kelvin_helmholtz") { - status = InitializeKelvinHelmholtz(rc.get(), pin); + status = InitializeKelvinHelmholtz(rc, pin); } else if (prob == "shock") { - status = InitializeShockTube(rc.get(), pin); + status = InitializeShockTube(rc, pin); + // GRMHD } else if (prob == "bondi") { - status = InitializeBondi(rc.get(), pin); + status = InitializeBondi(rc, pin); } else if (prob == "bz_monopole") { - status = InitializeBZMonopole(rc.get(), pin); + status = InitializeBZMonopole(rc, pin); // Electrons } else if (prob == "noh") { - status = InitializeNoh(rc.get(), pin); + status = InitializeNoh(rc, pin); + } else if (prob == "hubble") { + status = InitializeHubble(rc, pin); + } else if (prob == "driven_turbulence") { + status = InitializeDrivenTurbulence(rc, pin); // Extended GRMHD } else if (prob == "emhdmodes") { - status = InitializeEMHDModes(rc.get(), pin); + status = InitializeEMHDModes(rc, pin); } else if (prob == "anisotropic_conduction") { - status = InitializeAnisotropicConduction(rc.get(), pin); + status = InitializeAnisotropicConduction(rc, pin); } else if (prob == "emhdshock") { - status = InitializeEMHDShock(rc.get(), pin); + status = InitializeEMHDShock(rc, pin); } else if (prob == "conducting_atmosphere") { - status = InitializeAtmosphere(rc.get(), pin); + status = InitializeAtmosphere(rc, pin); } else if (prob == "bondi_viscous") { - status = InitializeBondiViscous(rc.get(), pin); + status = InitializeBondi(rc, pin); // Everything } else if (prob == "torus") { - status = InitializeFMTorus(rc.get(), pin); + status = InitializeFMTorus(rc, pin); } else if (prob == "resize_restart") { - status = ReadIharmRestart(rc.get(), pin); + status = ReadIharmRestart(rc, pin); } else if (prob == "resize_restart_kharma") { // Hyerin - status = ReadKharmaRestart(rc.get(), pin); + status = ReadKharmaRestart(rc, pin); } // If we didn't initialize a problem, yell @@ -145,31 +148,25 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // Perturb the internal energy a bit to encourage accretion // Note this defaults to zero & is basically turned on only for torii if (pin->GetOrAddReal("perturbation", "u_jitter", 0.0) > 0.0) { - PerturbU(rc.get(), pin); + PerturbU(rc, pin); } // Initialize electron entropies to defaults if enabled if (pmb->packages.AllPackages().count("Electrons")) { - Electrons::InitElectrons(rc.get(), pin); + Electrons::InitElectrons(rc, pin); + } + + if (pmb->packages.AllPackages().count("EMHD")) { + EMHD::InitEMHDVariables(rc, pin); } } // Fill the conserved variables U, - // which we'll treat as the independent/fundamental state. - // P is filled again from this later on - // Note this is needed *after* P is finalized, but - // *before* the floor call: normal-observer floors need U populated - Flux::PtoU(rc.get(), IndexDomain::interior); + // which we'll usually treat as the independent/fundamental state. + // This will need to be repeated once magnetic field is seeded + Flux::BlockPtoU(rc.get(), IndexDomain::interior); - // If we're not restarting, apply the floors - if ((prob != "resize_restart") && (prob != "resize_restart_kharma")) { - // This is purposefully done even if floors are disabled, - // as it is required for consistent initialization - // Note however we do *not* preserve any inversion flags in this call. - // There will be subsequent renormalization and re-inversion that will - // initialize those flags. - Floors::ApplyFloors(rc.get(), IndexDomain::interior); - } + // Floors are NOT automatically applied at this point anymore. - Flag(rc.get(), "Initialized Block"); + Flag(rc, "Initialized Block"); } diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 5f60e3e9..95ffcca7 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -38,26 +38,26 @@ #include "debug.hpp" #include "hdf5_utils.h" #include "kharma_utils.hpp" -#include "mpi.hpp" #include "interpolation.hpp" #include "types.hpp" #include #include -// This is gross, but everything else is grosser -// What's a little leaked host mem between friends? -static Real *ptmp = NULL; -static int blocks_initialized = 0; - // TODO: The iharm3d restart format fails to record several things we must guess: // 1. Sometimes, even precise domain boundaries in native coordinates // 2. Which coordinate system was used // 3. Any coordinate system parameters // Better to either: // a. read KHARMA restart files so we can re-grid -// b. use the IL dump format, but in double -// Either are useful capabilities. +// b. use the IL dump format, but in double precision (or even in single w/cleanup) +// Either would be very useful independently + +// This exists to simplify some initializer lists below +// This indicates I know that moving from signed->unsigned is dangerous, +// and sign off that these results are positive (they are) +hsize_t static_max(int i, int n) { return static_cast(m::max(i, n)); } +hsize_t static_min(int i, int n) { return static_cast(m::min(i, n)); } void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin) { @@ -74,7 +74,6 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& std::cout << "Initialized from " << fname << ", file version " << version << std::endl << std::endl; } - // Read what we need from the file, regardless of where we're putting it int n1file, n2file, n3file; hdf5_read_single_val(&n1file, "n1", H5T_STD_I32LE); @@ -180,8 +179,8 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& // Going from domain->coords values is better to match everything if (file_in_spherical) { - pin->SetReal("coordinates", "r_in", exp(x1min)); - pin->SetReal("coordinates", "r_out", exp(x1max)); + pin->SetReal("coordinates", "r_in", m::exp(x1min)); + pin->SetReal("coordinates", "r_out", m::exp(x1max)); } } else { std::cout << "Guessing geometry when restarting! This is potentially very bad to do!" << std::endl; @@ -232,16 +231,11 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& } } -TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) +TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Restarting from iharm3d checkpoint file"); - // TODO pack? Probably not worth it auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess const bool regrid_only = pin->GetOrAddBoolean("resize_restart", "regrid_only", false); @@ -271,24 +265,12 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetInteger("parthenon/mesh", "nx2") != n2tot || pin->GetInteger("parthenon/mesh", "nx3") != n3tot) { printf("Mesh size does not match!\n"); - printf("[%d %d %d] vs [%d %d %d]", + printf("[%d %d %d] vs [%llu %llu %llu]", pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), pin->GetInteger("parthenon/mesh", "nx3"), n1tot, n2tot, n3tot); } - - if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), - m::log(pin->GetReal("coordinates", "r_in"))) || - !close_to(pin->GetReal("parthenon/mesh", "x1max"), - m::log(pin->GetReal("coordinates", "r_out")))) { - printf("Mesh shape does not match!"); - printf("Rin %g vs %g, Rout %g vs %g", - m::exp(pin->GetReal("parthenon/mesh", "x1min")), - pin->GetReal("coordinates", "r_in"), - m::exp(pin->GetReal("parthenon/mesh", "x1max")), - pin->GetReal("coordinates", "r_out")); - } if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), startx[1]) || !close_to(pin->GetReal("parthenon/mesh", "x1max"), stopx[1]) || @@ -305,76 +287,217 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) pin->GetReal("parthenon/mesh", "x3min"), startx[3], pin->GetReal("parthenon/mesh", "x3max"), stopx[3]); } - } - - // TODO there must be a better way to cache this. InitUserData and make it a big variable or something? - if (ptmp == NULL) { - std::cout << "Reading mesh from file to cache..." << std::endl; - // Declare known sizes for inputting/outputting primitives - // We'll only ever read the full block, so this is the size we want - hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; - hsize_t fstart[] = {0, 0, 0, 0}; - ptmp = new double[nfprim*n3tot*n2tot*n1tot]; // These will include B & thus be double or upconverted to it - - hdf5_open(fname.c_str()); - hdf5_set_directory("/"); - hdf5_read_array(ptmp, "p", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_close(); + if (is_spherical) { + // Check that the coordinate parameters r_{in,out} match the mesh + if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), + m::log(pin->GetReal("coordinates", "r_in"))) || + !close_to(pin->GetReal("parthenon/mesh", "x1max"), + m::log(pin->GetReal("coordinates", "r_out")))) { + printf("Mesh shape does not match!"); + printf("Rin %g vs %g, Rout %g vs %g", + m::exp(pin->GetReal("parthenon/mesh", "x1min")), + pin->GetReal("coordinates", "r_in"), + m::exp(pin->GetReal("parthenon/mesh", "x1max")), + pin->GetReal("coordinates", "r_out")); + } + } - std::cout << "Read!" << std::endl; } - // If we are going to keep a static pointer, keep count so the last guy can kill it - blocks_initialized += 1; - auto rho_host = rho.GetHostMirror(); - auto u_host = u.GetHostMirror(); - auto uvec_host = uvec.GetHostMirror(); - auto B_host = B_P.GetHostMirror(); + if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; + + // In this section we're dealing with two different meshes: the one we're interpolating *from* (the "file" grid) + // and the one we're interpolating *to* -- the "meshblock." + // Additionally, in the "file" mesh we must deail with global file locations (no ghost zones, global index, prefixed "g") + // as well as local file locations (locations in a cache we read to host memory, prefixed "m") // Size/domain of the MeshBlock we're reading *to*. - // Note that we only read physical zones. + // Note that we only fill the block's physical zones -- + // PostInitialize will take care of ghosts with MPI syncs and calls to the domain boundary conditions IndexDomain domain = IndexDomain::interior; const IndexRange ib = pmb->cellbounds.GetBoundsI(domain); const IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); const IndexRange kb = pmb->cellbounds.GetBoundsK(domain); + const auto& G = pmb->coords; + + // Total file size + // TODO separate nmprim to stop at 8 prims if we don't need e- + hsize_t fdims[] = {nfprim, n3tot, n2tot, n1tot}; + + // Figure out the subset in global space corresponding to our memory cache + int gis, gjs, gks, gie, gje, gke; + if (regrid_only) { + // For nearest neighbor "interpolation," we don't need any ghost zones + // Global location of first zone of our new grid + double X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + Interpolation::Xtoijk_nearest(X, startx, dx, gis, gjs, gks); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gie, gje, gke); + } else { + // Linear interpolation case: we need ghost zones + // Global location of first zone of our new grid + double tmp[GR_DIM], X[GR_DIM]; + G.coord(kb.s, jb.s, ib.s, Loci::center, X); + // Global file coordinate corresponding to that location + // Note this will be the *left* side already, so we'll never read below this. + // The values gis,gjs,gks can/will be -1 sometimes + Interpolation::Xtoijk(X, startx, dx, gis, gjs, gks, tmp); + // Same for the end + G.coord(kb.e, jb.e, ib.e, Loci::center, X); + Interpolation::Xtoijk(X, startx, dx, gie, gje, gke, tmp); + // Include one extra zone in each direction, for right side of linear interp + gke += 1; gje += 1; gie += 1; + } + + // Truncate the file read sizes so we don't overrun the file data + hsize_t fstart[4] = {0, static_max(gks, 0), static_max(gjs, 0), static_max(gis, 0)}; + // Test gXe against last valid index, i.e. nXtot-1 + hsize_t fstop[4] = {nfprim-1, static_min(gke, n3tot-1), static_min(gje, n2tot-1), static_min(gie, n1tot-1)}; + // We add one here to get sizes from indices + hsize_t fcount[4] = {fstop[0] - fstart[0] + 1, + fstop[1] - fstart[1] + 1, + fstop[2] - fstart[2] + 1, + fstop[3] - fstart[3] + 1}; + // If we overran an index on the left, we need to leave a blank row (i.e., start at 1 == true) to reflect this + hsize_t mstart[4] = {0, (gks < 0), (gjs < 0), (gis < 0)}; + // Total memory size is never truncated + // This calculation produces XxYx2 arrays for 2D sims w/linear interp but that's fine + hsize_t nmk = gke-gks+1, nmj = gje-gjs+1, nmi = gie-gis+1; + hsize_t mdims[4] = {nfprim, nmk, nmj, nmi}; + // TODO these should be const but hdf5_read_array yells about it, fix that + // TODO should yell if any of these fired for nearest-neighbor + + // Allocate the array we'll need + hsize_t nmblock = nmk * nmj * nmi; + // TODO this may be float[] if we ever want to read dump files as restarts + double *ptmp = new double[nfprim*nmblock]; + + // Open the file + hdf5_open(fname.c_str()); + hdf5_set_directory("/"); - auto& G = pmb->coords; + // Read the main array + hdf5_read_array(ptmp, "p", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + + // Do some special reads from elsewhere in the file to fill periodic bounds + // Note we do NOT fill outflow/reflecting bounds here -- instead, we treat them specially below + // TODO this could probably be a lot cleaner + hsize_t fstart_tmp[4], fcount_tmp[4], mstart_tmp[4]; +#define RESET_COUNTS DLOOP1 {fstart_tmp[mu] = fstart[mu]; fcount_tmp[mu] = fcount[mu]; mstart_tmp[mu] = mstart[mu];} + if (gks < 0 && pmb->boundary_flag[BoundaryFace::inner_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally LAST rank in X3 + fstart_tmp[1] = n3tot-1; + fcount_tmp[1] = 1; + // Read it to the FIRST rank of our array + mstart_tmp[1] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gke > n3tot-1 && pmb->boundary_flag[BoundaryFace::outer_x3] == BoundaryFlag::periodic) { + RESET_COUNTS + // same X1/X2, but take only the globally FIRST rank in X3 + fstart_tmp[1] = 0; + fcount_tmp[1] = 1; + // Read it to the LAST rank of our array + mstart_tmp[1] = mdims[1]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gjs < 0 && pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = n2tot-1; + fcount_tmp[2] = 1; + mstart_tmp[2] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gje > n2tot-1 && pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[2] = 0; + fcount_tmp[2] = 1; + mstart_tmp[2] = mdims[2]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gis < 0 && pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = n1tot-1; + fcount_tmp[3] = 1; + mstart_tmp[3] = 0; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } + if (gie > n1tot-1 && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::periodic) { + RESET_COUNTS + fstart_tmp[3] = 0; + fcount_tmp[3] = 1; + mstart_tmp[3] = mdims[3]-1; + hdf5_read_array(ptmp, "p", 4, fdims, fstart_tmp, fcount_tmp, mdims, mstart_tmp, H5T_IEEE_F64LE); + } - Flag("Reordering meshblock..."); - // Host-side interpolate & copy into the mirror array - // TODO Support restart native coordinates != new native coordinates + hdf5_close(); + + if (MPIRank0()) std::cout << "Read!" << std::endl; + + // Get the arrays we'll be writing to + // TODO this is probably easier AND more flexible if we pack them + GridScalar rho = rc->Get("prims.rho").data; + GridScalar u = rc->Get("prims.u").data; + GridVector uvec = rc->Get("prims.uvec").data; + GridVector B_P = rc->Get("prims.B").data; + auto rho_host = rho.GetHostMirror(); + auto u_host = u.GetHostMirror(); + auto uvec_host = uvec.GetHostMirror(); + auto B_host = B_P.GetHostMirror(); + + Flag("Interpolating meshblock..."); + // Interpolate on the host side & copy into the mirror Views + // Nearest-neighbor interpolation is currently only used when grids exactly correspond -- otherwise, linear interpolation is used + // to minimize the resulting B field divergence. // NOTE: KOKKOS USES < not <=!! Therefore the RangePolicy below will seem like it is too big if (regrid_only) { - // Kokkos::parallel_for("copy_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO Kokkos calls here had problems with CUDA, reintroduce/fix + // OpenMP here conflicts with Kokkos parallel in some cases, so we're stuck for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); double tmp[GR_DIM]; - int gk,gj,gi; Xtoijk(X, startx, dx, gi, gj, gk, tmp, true); - // Fill block cells with global equivalents - rho_host(k, j, i) = ptmp[0*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - u_host(k, j, i) = ptmp[1*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - VLOOP B_host(v, k, j, i) = ptmp[(5+v)*n3tot*n2tot*n1tot + gk*n2tot*n1tot + gj*n1tot + gi]; - } - // ); + GReal X[GR_DIM]; int gk, gj, gi; + G.coord(k, j, i, Loci::center, X); + Interpolation::Xtoijk_nearest(X, startx, dx, gi, gj, gk); + // TODO verify this never reads zones outside the cache + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Fill cells of the new block with equivalents in the cached block + rho_host(k, j, i) = ptmp[0*nmblock + mk*nmj*nmi + mj*nmi + mi]; + u_host(k, j, i) = ptmp[1*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP uvec_host(v, k, j, i) = ptmp[(2+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + VLOOP B_host(v, k, j, i) = ptmp[(5+v)*nmblock + mk*nmj*nmi + mj*nmi + mi]; + } } else { - // Kokkos::parallel_for("interp_restart_state", - // Kokkos::MDRangePolicy>({kb.s, jb.s, ib.s}, {kb.e+1, jb.e+1, ib.e+1}), - // KOKKOS_LAMBDA_3D { + // TODO real boundary flags. Repeat on any outflow/reflecting bounds + const bool repeat_x1i = is_spherical; + const bool repeat_x1o = is_spherical; + const bool repeat_x2i = is_spherical; + const bool repeat_x2o = is_spherical; + for (int k=kb.s; k <= kb.e; ++k) for (int j=jb.s; j <= jb.e; ++j) for (int i=ib.s; i <= ib.e; ++i) { - // Get the zone center location - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - // Interpolate the value at this location from the global grid - rho_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[0*n3tot*n2tot*n1tot])); - u_host(k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[1*n3tot*n2tot*n1tot])); - VLOOP uvec_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(2+v)*n3tot*n2tot*n1tot])); - VLOOP B_host(v, k, j, i) = linear_interp(G, X, startx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(ptmp[(5+v)*n3tot*n2tot*n1tot])); - } - // ); + GReal X[GR_DIM], del[GR_DIM]; int gk, gj, gi; + // Get the zone center location + G.coord(k, j, i, Loci::center, X); + // Get global indices + Interpolation::Xtoijk(X, startx, dx, gi, gj, gk, del); + // Make any corrections due to global boundaries + // Currently just repeats the last zone, equivalent to falling back to nearest-neighbor + if (repeat_x1i && gi < 0) { gi = 0; del[1] = 0; } + if (repeat_x1o && gi > n1tot-2) { gi = n1tot - 2; del[1] = 1; } + if (repeat_x2i && gj < 0) { gj = 0; del[2] = 0; } + if (repeat_x2o && gj > n2tot-2) { gj = n2tot - 2; del[2] = 1; } + // Calculate indices inside our cached block + int mk = gk - gks, mj = gj - gjs, mi = gi - gis; + // Interpolate the value at this location from the cached grid + rho_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[0*nmblock])); + u_host(k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[1*nmblock])); + VLOOP uvec_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(2+v)*nmblock])); + VLOOP B_host(v, k, j, i) = Interpolation::linear(mi, mj, mk, nmi, nmj, nmk, del, &(ptmp[(5+v)*nmblock])); + } } // Deep copy to device @@ -385,11 +508,9 @@ TaskStatus ReadIharmRestart(MeshBlockData *rc, ParameterInput *pin) B_P.DeepCopy(B_host); Kokkos::fence(); - // Close the door on our way out - if (blocks_initialized == pmb->pmy_mesh->GetNumMeshBlocksThisRank()) { - std::cout << "Deleting cached mesh" << std::endl; - delete[] ptmp; - } + // Delete our cache. Only we ever used it, so we're safe here. + Flag("Deleting cached interpolation values"); + delete[] ptmp; return TaskStatus::complete; } diff --git a/kharma/prob/resize_restart.hpp b/kharma/prob/resize_restart.hpp index b5940270..a62dc062 100644 --- a/kharma/prob/resize_restart.hpp +++ b/kharma/prob/resize_restart.hpp @@ -8,11 +8,11 @@ * Read the header of an iharm3d HDF5 restart file, and set appropriate parameters * Call this before mesh creation! */ -void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin); +void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin); /** * Read data from an iharm3d restart file. Does not support >1 meshblock in Parthenon * * Returns stop time tf of the original simulation, for e.g. replicating regression tests */ -TaskStatus ReadIharmRestart(MeshBlockData *rc, parthenon::ParameterInput *pin); +TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterInput *pin); diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 2d315f72..c41af3dc 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -65,8 +65,7 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& // Read input from restart file // (from external/parthenon/src/parthenon_manager.cpp) - std::unique_ptr restartReader; - restartReader = std::make_unique(fname.c_str()); + auto restartReader = std::make_unique(fname.c_str()); // Load input stream std::unique_ptr fpinput; @@ -133,11 +132,10 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& hslope = fpinput->GetReal("coordinates", "hslope"); pin->SetReal("coordinates", "hslope", hslope); - // close hdf5 file to prevent HDF5 hangs and corrupted files - restartReader = nullptr; + // File closed here when restartReader falls out of scope } -TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin) +TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterInput *pin) { Flag(rc, "Restarting from KHARMA checkpoint file"); @@ -197,13 +195,18 @@ TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin) if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("b_field_type"))) pmb->packages.Get("GRMHD")->AddParam("b_field_type", fBfield); + // Register SetKharmaRestart as the X1 boundary condition + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + bound_pkg->KHARMAInnerX1Boundary = SetKharmaRestart; + bound_pkg->KHARMAOuterX1Boundary = SetKharmaRestart; + // Set the whole domain - SetKharmaRestart(rc); + SetKharmaRestart(rc, IndexDomain::entire, false); return TaskStatus::complete; } -TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain domain, bool coarse) { Flag(rc, "Setting KHARMA restart zones"); auto pmb = rc->GetBlockPointer(); @@ -248,8 +251,8 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co int fnghost = pmb->packages.Get("GRMHD")->Param("rnghost"); const Real fx1min_ghost = fx1min - fnghost*dx1; PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(rc, prims_map); - auto U = GRMHD::PackMHDCons(rc, cons_map); + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map); + auto U = GRMHD::PackMHDCons(rc.get(), cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); if ((domain != IndexDomain::outer_x1) && (domain != IndexDomain::inner_x1)) { @@ -426,7 +429,7 @@ TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain, bool co // Host-side interpolate & copy into the mirror array pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { get_prim_restart_kharma(G, coords, P, m_p, blcoord, kscoord, fx1min, fx1max, fnghost, should_fill, is_spherical, include_B, gam, rs, mdot, length, x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 8dcb9cb1..7b8c99f3 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -20,10 +20,10 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& * * Returns stop time tf of the original simulation, for e.g. replicating regression tests */ -TaskStatus ReadKharmaRestart(MeshBlockData *rc, ParameterInput *pin); +TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterInput *pin); // newly added by Hyerin (09/06/22) -TaskStatus SetKharmaRestart(MeshBlockData *rc, IndexDomain domain=IndexDomain::entire, bool coarse=false); +TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain domain, bool coarse); // Hint form resize.hpp // TODO: (Hyerin) should I do const for x1, x2, x3, var? diff --git a/kharma/prob/shock_tube.hpp b/kharma/prob/shock_tube.hpp index b599a6e5..191ede1e 100644 --- a/kharma/prob/shock_tube.hpp +++ b/kharma/prob/shock_tube.hpp @@ -10,7 +10,7 @@ using namespace parthenon; * * Stolen directly from iharm3D */ -TaskStatus InitializeShockTube(MeshBlockData *rc, ParameterInput *pin) +TaskStatus InitializeShockTube(std::shared_ptr>& rc, ParameterInput *pin) { Flag(rc, "Initializing Shock Tube problem"); auto pmb = rc->GetBlockPointer(); @@ -50,7 +50,7 @@ TaskStatus InitializeShockTube(MeshBlockData *rc, ParameterInput *pin) const Real center = (x1min + x1max) / 2.; pmb->par_for("ot_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord(k, j, i, Loci::center, X); diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 0d292535..15d25215 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -40,14 +40,17 @@ using namespace parthenon; -#define EPS 1.e-26 - /** * This namespace covers custom new reconstructions for KHARMA, and a function which * automatically chooses the inner loop based on an enum from decs.hpp */ namespace KReconstruction { +constexpr Real EPS = 1.e-26; + +// Enum for types. +enum class Type{donor_cell=0, linear_mc, linear_vl, ppm, mp5, weno5, weno5_lower_poles}; + // BUILD UP (a) LINEAR MC RECONSTRUCTION // Single-item implementation @@ -69,7 +72,7 @@ KOKKOS_INLINE_FUNCTION void PiecewiseLinearX1(parthenon::team_mbr_t const &membe const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real dql = q(p, k, j, i) - q(p, k, j, i - 1); Real dqr = q(p, k, j, i + 1) - q(p, k, j, i); Real dq = mc(dql, dqr)*dqr; @@ -87,7 +90,7 @@ KOKKOS_INLINE_FUNCTION void PiecewiseLinearX2(parthenon::team_mbr_t const &membe const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real dql = q(p, k, j, i) - q(p, k, j - 1, i); Real dqr = q(p, k, j + 1, i) - q(p, k, j, i); Real dq = mc(dql, dqr)*dqr; @@ -105,7 +108,7 @@ KOKKOS_INLINE_FUNCTION void PiecewiseLinearX3(parthenon::team_mbr_t const &membe const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real dql = q(p, k, j, i) - q(p, k - 1, j, i); Real dqr = q(p, k + 1, j, i) - q(p, k, j, i); Real dq = mc(dql, dqr)*dqr; @@ -117,14 +120,15 @@ KOKKOS_INLINE_FUNCTION void PiecewiseLinearX3(parthenon::team_mbr_t const &membe } // BUILD UP WENO5 RECONSTRUCTION +// Adapted from implementation in iharm3d originally by Monika Moscibrodzka +// References: Tchekhovskoy et al. 2007 (T07), Shu 2011 (S11) // Single-element implementation: "left" and "right" here are relative to zone centers, so the combo calls will switch them later. -// WENO interpolation. See Tchekhovskoy et al. 2007 (T07), Shu 2011 (S11) -// Implemented by Monika Moscibrodzka KOKKOS_INLINE_FUNCTION void weno5(const Real& x1, const Real& x2, const Real& x3, const Real& x4, const Real& x5, Real &lout, Real &rout) { // Smoothness indicators, T07 A18 or S11 8 + // TODO are small arrays really the play here? Should I further reduce cache by increasing flops? Real beta[3], c1, c2; c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; @@ -134,14 +138,13 @@ KOKKOS_INLINE_FUNCTION void weno5(const Real& x1, const Real& x2, const Real& x3 beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; - den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; - Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; - Real Wr = wtr[0] + wtr[1] + wtr[2]; + const Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; + const Real Wr = wtr[0] + wtr[1] + wtr[2]; - Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; - Real Wl = wtl[0] + wtl[1] + wtl[2]; + const Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; + const Real Wl = wtl[0] + wtl[1] + wtl[2]; // S11 1, 2, 3 lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + @@ -164,11 +167,10 @@ KOKKOS_INLINE_FUNCTION void weno5l(const Real x1, const Real& x2, const Real& x3 beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; - den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; - Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; - Real Wl = wtl[0] + wtl[1] + wtl[2]; + const Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; + const Real Wl = wtl[0] + wtl[1] + wtl[2]; // S11 1, 2, 3 lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + @@ -188,12 +190,12 @@ KOKKOS_INLINE_FUNCTION void weno5r(const Real& x1, const Real& x2, const Real& x beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; - den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; - Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; - Real Wr = wtr[0] + wtr[1] + wtr[2]; + const Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; + const Real Wr = wtr[0] + wtr[1] + wtr[2]; + // S11 1, 2, 3 rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr[0] / Wr) + ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr[1] / Wr) + ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr[2] / Wr); @@ -212,7 +214,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X1(parthenon::team_mbr_t const &member, const i const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real lout, rout; weno5(q(p, k, j, i - 2), q(p, k, j, i - 1), @@ -233,7 +235,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X2(parthenon::team_mbr_t const &member, const i const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real lout, rout; weno5(q(p, k, j - 2, i), q(p, k, j - 1, i), @@ -253,7 +255,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X2l(parthenon::team_mbr_t const &member, const const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real rout; weno5r(q(p, k, j - 2, i), q(p, k, j - 1, i), @@ -272,7 +274,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X2r(parthenon::team_mbr_t const &member, const const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real lout; weno5l(q(p, k, j - 2, i), q(p, k, j - 1, i), @@ -292,7 +294,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X3(parthenon::team_mbr_t const &member, const i const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real lout, rout; weno5(q(p, k - 2, j, i), q(p, k - 1, j, i), @@ -312,7 +314,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X3l(parthenon::team_mbr_t const &member, const const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real rout; weno5r(q(p, k - 2, j, i), q(p, k - 1, j, i), @@ -331,7 +333,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X3r(parthenon::team_mbr_t const &member, const const int nu = q.GetDim(4) - 1; for (int p = 0; p <= nu; ++p) { parthenon::par_for_inner(member, il, iu, - KOKKOS_LAMBDA_1D { + KOKKOS_LAMBDA (const int& i) { Real lout; weno5l(q(p, k - 2, j, i), q(p, k - 1, j, i), @@ -347,18 +349,18 @@ KOKKOS_INLINE_FUNCTION void WENO5X3r(parthenon::team_mbr_t const &member, const /** * Templated calls to different reconstruction algorithms * This is basically a compile-time 'if' or 'switch' statement, where all the options get generated - * at compile-time (see harm_driver.cpp where they're spelled out explicitly) + * at compile-time (see driver.cpp for the different instantiations) * - * We could temlate these directly on the function if Parthenon could agree on what argument list to use + * We could template these directly on the function if Parthenon could agree on what argument list to use * Better than a runtime decision per outer loop I think */ -template +template KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) {} // DONOR CELL template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -366,7 +368,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(p DonorCellX1(member, k, j, is_l, ie_l, P, ql, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -376,7 +378,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(p DonorCellX2(member, k, j, is_l, ie_l, P, q_u, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -387,7 +389,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(p } // LINEAR W/VAN LEER template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -400,7 +402,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa PiecewiseLinearX1(member, k, j, is_l, ie_l, G, P, ql, qr, qc, dql, dqr, dqm); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -415,7 +417,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa PiecewiseLinearX2(member, k, j, is_l, ie_l, G, P, q_u, qr, qc, dql, dqr, dqm); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -431,7 +433,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa } // LINEAR WITH MC template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -439,7 +441,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa KReconstruction::PiecewiseLinearX1(member, k, j, is_l, ie_l, P, ql, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -449,7 +451,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa KReconstruction::PiecewiseLinearX2(member, k, j, is_l, ie_l, P, q_u, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -460,7 +462,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(pa } // WENO5 template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -468,7 +470,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthe KReconstruction::WENO5X1(member, k, j, is_l, ie_l, P, ql, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -477,7 +479,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthe KReconstruction::WENO5X2r(member, k, j, is_l, ie_l, P, qr); } template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) @@ -486,4 +488,90 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthe KReconstruction::WENO5X3r(member, k, j, is_l, ie_l, P, qr); } +/** + * Versions computing just the (limited) slope, for linear reconstructions. + * Used for gradient calculations needed to implement Extended GRMHD. + */ +template +KOKKOS_INLINE_FUNCTION Real slope_limit(Real x1, Real x2, Real x3, Real dx); +// Linear MC slope limiter +template <> +KOKKOS_INLINE_FUNCTION Real slope_limit(Real x1, Real x2, Real x3, Real dx) +{ + const Real Dqm = 2 * (x2 - x1) / dx; + const Real Dqp = 2 * (x3 - x2) / dx; + const Real Dqc = 0.5 * (x3 - x1) / dx; + + if (Dqm * Dqp <= 0) { + return 0; + } else { + if ((m::abs(Dqm) < m::abs(Dqp)) && (m::abs(Dqm) < m::abs(Dqc))) { + return Dqm; + } else if (m::abs(Dqp) < m::abs(Dqc)) { + return Dqp; + } else { + return Dqc; + } + } +} +// Linear Van Leer slope limiter +template <> +KOKKOS_INLINE_FUNCTION Real slope_limit(Real x1, Real x2, Real x3, Real dx) +{ + const Real Dqm = (x2 - x1) / dx; + const Real Dqp = (x3 - x2) / dx; + + const Real extrema = Dqm * Dqp; + + if (extrema <= 0) { + return 0; + } else { + return (2 * extrema / (Dqm + Dqp)); + } +} + +/** + * Run slope_limit in direction 'dir' using limiter 'recon' + */ +template +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i); +// And six implementations. Why can't you partial-specialize functions? Why? +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc<1>(i)); +} +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc<2>(j)); +} +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc<3>(k)); +} +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc<1>(i)); +} +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc<2>(j)); +} +template <> +KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, + const int& p, const int& k, const int& j, const int& i) +{ + return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc<3>(k)); +} + } // namespace KReconstruction diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index cb157c9b..011e249f 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -36,74 +36,257 @@ #include -std::shared_ptr Reductions::Initialize(ParameterInput *pin) + + +Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone) { - auto pkg = std::make_shared("Reductions"); - Params ¶ms = pkg->AllParams(); - - bool add_zones = pin->GetOrAddBoolean("reductions", "add_zones_accretion", false); - params.Add("add_zones", add_zones); - bool add_fluxes = pin->GetOrAddBoolean("reductions", "add_fluxes_accretion", true); - params.Add("add_fluxes", add_fluxes); - bool add_totals = pin->GetOrAddBoolean("reductions", "add_totals", true); - params.Add("add_totals", add_totals); - bool add_flags = pin->GetOrAddBoolean("reductions", "add_flags", true); - params.Add("add_flags", add_flags); - - // List (vector) of HistoryOutputVar that will all be enrolled as output variables - parthenon::HstVar_list hst_vars = {}; - // Accretion reductions only apply in spherical coordinates - if (pin->GetBoolean("coordinates", "spherical")) { - // Zone-based sums - if (add_zones) { - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, MdotBound, "Mdot")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, MdotEH, "Mdot_EH")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, EdotBound, "Edot")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, EdotEH, "Edot_EH")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, LdotBound, "Ldot")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, LdotEH, "Ldot_EH")); - } + Flag("Performing accretion reduction"); + auto pmesh = md->GetMeshPointer(); + + Real result = 0.; + for (auto &pmb : pmesh->block_list) { + // If we're on the inner edge + if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { + const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + + auto& rc = pmb->meshblock_data.Get(); + PackIndexMap prims_map, cons_map; + const auto& P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); - // EH magnetization parameter - // TODO option? Or just record this always? - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, PhiBound, "Phi")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, PhiEH, "Phi_EH")); - - // Count accretion more accurately, as total flux through a spherical shell - if (add_fluxes) { - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, MdotBoundFlux, "Mdot_Flux")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, MdotEHFlux, "Mdot_EH_Flux")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, EdotBoundFlux, "Edot_Flux")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, EdotEHFlux, "Edot_EH_Flux")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, LdotBoundFlux, "Ldot_Flux")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, LdotEHFlux, "Ldot_EH_Flux")); + IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); + IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); + IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); + const auto& G = pmb->coords; + + Real block_result; + switch(op) { + case UserHistoryOperation::sum: { + Kokkos::Sum sum_reducer(block_result); + pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + local_result += fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); + } + , sum_reducer); + result += block_result; + break; + } + case UserHistoryOperation::max: { + Kokkos::Max max_reducer(block_result); + pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + const Real val = fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); + if (val > local_result) local_result = val; + } + , max_reducer); + if (block_result > result) result = block_result; + break; + } + case UserHistoryOperation::min: { + Kokkos::Min min_reducer(block_result); + pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + const Real val = fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); + if (val < local_result) local_result = val; + } + , min_reducer); + if (block_result < result) result = block_result; + break; + } + } } } - // Grid totals of various quantities potentially of interest - if (add_totals) { - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, TotalM, "Mass")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, TotalE, "Egas")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, TotalL, "Ang_Mom")); + Flag("Reduced"); + return result; +} + +Real Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg) +{ + Flag("Performing domain reduction"); + auto pmesh = md->GetMeshPointer(); + + // TODO TODO MESHDATA THIS + Real result = 0.; + const auto& pars = pmesh->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, TotalEHTLum, "EHT_Lum_Proxy")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, JetLum_50, "Jet_Lum")); + PackIndexMap prims_map, cons_map; + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + IndexRange ib = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); + IndexRange jb = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); + IndexRange kb = pmb0->cellbounds.GetBoundsK(IndexDomain::interior); + IndexRange block = IndexRange{0, U.GetDim(5) - 1}; + + switch(op) { + case UserHistoryOperation::sum: { + Kokkos::Sum sum_reducer(result); + pmb0->par_reduce("domain_sum", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { + const auto& G = U.GetCoords(b); + local_result += fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); + } + , sum_reducer); + break; + } + case UserHistoryOperation::max: { + Kokkos::Max max_reducer(result); + pmb0->par_reduce("domain_max", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { + const auto& G = U.GetCoords(b); + const Real val = fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); + if (val > local_result) local_result = val; + } + , max_reducer); + break; + } + case UserHistoryOperation::min: { + Kokkos::Min min_reducer(result); + pmb0->par_reduce("domain_min", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { + const auto& G = U.GetCoords(b); + const Real val = fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); + if (val < local_result) local_result = val; + } + , min_reducer); + break; } - // Keep a slightly more granular log of flags than the usual dump cadence - if (add_flags) { - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, NPFlags, "Num_PFlags")); - hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, NFFlags, "Num_FFlags")); } - // Possible additions: - // 0. option for "verbose" logs of particular flags hit, using new functions - // 1. total 3- and 4-current numbers (best to add in "current" package) - // 2. Luminosity proxy sums over smaller areas, e.g. just disk, just disk 3-10M, etc - // 3. Total output power, using betagamma and/or just T^0_1 > 0 - // 4+ basically anything with MI correlated to final image MI... + Flag("Reduced"); + return result; +} + +/** + * Counts occurrences of a particular flag value + * + */ +int Reductions::CountFlag(MeshData *md, std::string field_name, const int& flag_val, IndexDomain domain, bool is_bitflag) +{ + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Pack variables + std::vector flag_vec = {field_name}; + auto& flag = md->PackVariables(flag_vec); + + // Get sizes + IndexRange ib = md->GetBoundsI(domain); + IndexRange jb = md->GetBoundsJ(domain); + IndexRange kb = md->GetBoundsK(domain); + IndexRange block = IndexRange{0, flag.GetDim(5) - 1}; + + int n_flag; + Kokkos::Sum flag_ct(n_flag); + pmb0->par_reduce("count_flag", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { + if ((is_bitflag && static_cast(flag(b, 0, k, j, i)) & flag_val) || + (!is_bitflag && static_cast(flag(b, 0, k, j, i)) == flag_val)) + ++local_result; + } + , flag_ct); + return n_flag; +} + +int Reductions::CountFlags(MeshData *md, std::string field_name, std::map flag_values, IndexDomain domain, int verbose, bool is_bitflag) +{ + Flag("Counting inversion failures"); + int nflags = 0; + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + + // Pack variables + std::vector flag_vec = {field_name}; + auto& flag = md->PackVariables(flag_vec); + + // Get sizes + IndexRange ib = md->GetBoundsI(domain); + IndexRange jb = md->GetBoundsJ(domain); + IndexRange kb = md->GetBoundsK(domain); + IndexRange block = IndexRange{0, flag.GetDim(5) - 1}; + + // Count all nonzero (technically, >0) values + // This works for pflags or fflags, so long as they're separate + // We don't count negative pflags as they denote zones that shouldn't be fixed + Kokkos::Sum sum_reducer(nflags); + pmb0->par_reduce("count_flags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { + if ((int) flag(b, 0, k, j, i) > 0) ++local_result; + } + , sum_reducer); + + // TODO TODO REPLACE ABOVE WITH SOMETHING LIKE: + // array_sum::array_type res; + // parthenon::par_reduce(parthenon::loop_pattern_mdrange_tag, "RadiationResidual1", + // DevExecSpace(), 0, mout->NumBlocks()-1, + // 0, nang1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + // KOKKOS_LAMBDA(const int b, const int n, const int k, const int j, const int i, + // array_sum::array_type& dsum) { + // dsum.my_array[0] += fabs(iiter(b,n,k,j,i) - iout(b,n,k,j,i)); + // dsum.my_array[1] += iout(b,n,k,j,i); + // }, array_sum::GlobalSum(res)); + + // Need the total on all ranks to evaluate the if statement below + static AllReduce n_tot; + n_tot.val = nflags; + n_tot.StartReduce(MPI_SUM); + while (n_tot.CheckReduce() == TaskStatus::incomplete); + nflags = n_tot.val; - // Finally, add the whole list of callbacks to the package Params struct, using a special key - pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + // If necessary, count each flag + // This is slow, but it can be slow: it's not called for normal operation + if (verbose > 0 && nflags > 0) { + // Overlap reductions to save time + // ...at the cost of considerable complexity... + + // TODO TODO eliminate static reducers, they crash the program after it finishes + static Reduce n_cells_r; + n_cells_r.val = (block.e - block.s + 1) * (kb.e - kb.s + 1) * (jb.e - jb.s + 1) * (ib.e - ib.s + 1); + n_cells_r.StartReduce(0, MPI_SUM); + + static std::vector>> reducers; + // Initialize reducers if they haven't been + if (reducers.size() == 0) { + for (auto& status : flag_values) { + std::shared_ptr> reducer = std::make_shared>(); + reducers.push_back(reducer); + } + } + // Count occurrences of each flag value, assign to a reducer in order + int i = 0; + for (auto& status : flag_values) { + reducers[i]->val = CountFlag(md, field_name, (int) status.first, domain, is_bitflag); + reducers[i]->StartReduce(0, MPI_SUM); + ++i; + } + while (n_cells_r.CheckReduce() == TaskStatus::incomplete); + const int n_cells = n_cells_r.val; + // Check each reducer in order, add to a vector + std::vector n_status_present; + for (std::shared_ptr> reducer : reducers) { + while (reducer->CheckReduce() == TaskStatus::incomplete); + n_status_present.push_back(reducer->val); + } + + if (MPIRank0()) { + std::cout << field_name << ": " << nflags << " (" << (int)(((double) nflags )/n_cells * 100) << "% of all cells)" << std::endl; + if (verbose > 1) { + // Print nonzero vector contents against flag names in order + int i = 0; + for (auto& status : flag_values) { + if (n_status_present[i] > 0) std::cout << status.second << ": " << n_status_present[i] << std::endl; + ++i; + } + std::cout << std::endl; + } + } + + // TODO Print zone locations of bad inversions + } - return pkg; + Flag("Counted"); + return nflags; } diff --git a/kharma/reductions/reductions.hpp b/kharma/reductions/reductions.hpp index 2c2dc4b8..11dd709f 100644 --- a/kharma/reductions/reductions.hpp +++ b/kharma/reductions/reductions.hpp @@ -39,306 +39,50 @@ #include "grmhd_functions.hpp" #include "types.hpp" -namespace Reductions { - -std::shared_ptr Initialize(ParameterInput *pin); - -// Remaining TODO: -// 1. Run over MeshData since I bundle anyway -// 2. Add MaxP, MaxBsq, MinBeta, etc. -// 3. More flexibility with operations? -// Check blocks for relevance (within/contain some radius) -// Or, at least avoid calculating all T in all zones each time - -// Okay so this requires a little explaining. -// The point is to share all the code we can between calculations of -// all of the EH or inner bound fluxes: mdot, edot, ldot, etc, etc, etc - -// We start with a template, which will be used for all reductions -// The "typename" here is basically a flag to distinguish implementations -template -Real AccretionRate(MeshData *md, const int& i); -template -Real DomainSum(MeshData *md, const Real& radius); - -// Then we define the macro which will generate all of our accretion rate calculations. -// This is a general (dangerous) macro which will generate an implementation of -// AccretionRate, given the arguments -// "Something" and "Function", which together specify a variable name, and the function -// to run inside the reduction - -// And no, this can't just be a template: "Function" must be first defined within "AccretionRate", -// so that it can inherit the variable names (U, P, etc.) from the function context. -// That is, if we try to define "Function" outside and pass it as a template argument, -// the compiler has no idea what "U" means -#define MAKE_SUM2D_FN(name, fn) template<> inline Real AccretionRate(MeshData *md, const int& i) { \ - Flag("Performing accretion reduction"); \ - auto pmesh = md->GetMeshPointer(); \ -\ - Real result = 0.; \ - for (auto &pmb : pmesh->block_list) { \ - auto& rc = pmb->meshblock_data.Get(); \ - if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { \ - const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); \ - const MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); \ - PackIndexMap prims_map, cons_map; \ - const auto& P = rc->PackVariables(std::vector{isPrimitive}, prims_map); \ - const auto& U = rc->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); \ - const VarMap m_u(cons_map, true), m_p(prims_map, false); \ -\ - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); \ -\ - IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); \ - IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); \ - IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); \ - const auto& G = pmb->coords; \ -\ - Real block_result; \ - Kokkos::Sum sum_reducer(block_result); \ - pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+i, ib.s+i, \ - KOKKOS_LAMBDA_3D_REDUCE { \ - FourVectors Dtmp; \ - Real T[GR_DIM][GR_DIM]; \ - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); \ - DLOOP1 Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, mu, T[mu]); \ - GReal gdA = G.dx3v(k) * G.dx2v(j) * G.gdet(Loci::center, j, i); \ - GReal dA = G.dx3v(k) * G.dx2v(j); \ - fn \ - } \ - , sum_reducer); \ - result += block_result; \ - } \ - } \ -\ - Flag("Reduced"); \ -\ - return result; \ -} - -// Now we need some valid type names to use in distinguishing functions. -// The 'enum class' lines just serve to define an arbitrary name as some valid type, -// so that it can be used to distinguish between implementations of AccretionRate. -// We could also have used different int values here, but type names seemed more elegant. - -// We also provide some implementations. -// Each of the MAKE_ETC "calls" expands into an implementation of -// AccretionRate using the macro we just defined above. -enum class Mdot : int; -MAKE_SUM2D_FN(Mdot, - // \dot{M} == \int rho * u^1 * gdet * dx2 * dx3 - local_result += -P(m_p.RHO, k, j, i) * Dtmp.ucon[1] * gdA; -) -enum class Edot : int; -MAKE_SUM2D_FN(Edot, - // \dot{E} == \int - T^1_0 * gdet * dx2 * dx3 - local_result += -T[X1DIR][X0DIR] * gdA; -) -enum class Ldot : int; -MAKE_SUM2D_FN(Ldot, - // \dot{L} == \int T^1_3 * gdet * dx2 * dx3 - local_result += T[X1DIR][X3DIR] * gdA; -) -enum class Phi : int; -MAKE_SUM2D_FN(Phi, - // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 - // Can also sum the hemispheres independently to be fancy (TODO?) - // This if statement prevents running if no B field is present in the VariablePack - if (m_u.B1 >= 0) { - local_result += 0.5 * m::abs(U(m_u.B1, k, j, i)) * dA; // gdet is included in cons.B - } -) - -// Then we can define the same with fluxes. -// The MAKE_SUM2D_FN macro pulls out pretty much any variable we could need here -enum class Mdot_Flux : int; -MAKE_SUM2D_FN(Mdot_Flux, local_result += -U.flux(X1DIR, m_u.RHO, k, j, i) * dA;) -enum class Edot_Flux : int; -MAKE_SUM2D_FN(Edot_Flux, local_result += (U.flux(X1DIR, m_u.UU, k, j, i) - U.flux(X1DIR, m_u.RHO, k, j, i)) * dA;) -enum class Ldot_Flux : int; -MAKE_SUM2D_FN(Ldot_Flux, local_result += U.flux(X1DIR, m_u.U3, k, j, i) * dA;) - -// Finally, we define the reductions in the form Parthenon needs, picking particular -// variables and zones so that the resulting functions take only MeshData as an argument -inline Real MdotBound(MeshData *md) {return AccretionRate(md, 0);} -inline Real MdotEH(MeshData *md) {return AccretionRate(md, 5);} -inline Real EdotBound(MeshData *md) {return AccretionRate(md, 0);} -inline Real EdotEH(MeshData *md) {return AccretionRate(md, 5);} -inline Real LdotBound(MeshData *md) {return AccretionRate(md, 0);} -inline Real LdotEH(MeshData *md) {return AccretionRate(md, 5);} -inline Real PhiBound(MeshData *md) {return AccretionRate(md, 0);} -inline Real PhiEH(MeshData *md) {return AccretionRate(md, 5);} +// This is for flux/accretion rate +#define REDUCE_FUNCTION_ARGS_EH const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, \ + const VariableFluxPack& U, const VarMap& m_u, const Real& gam, \ + const int& k, const int& j, const int& i + +// Notice this list also includes a generic Real-type argument: this is for denoting a radius or placement. +// Provided as argument in case reductions at/within/etc multiple places are desired +// (e.g., disk and jet, inner & outer, multiple radii) +// TODO take off 'b' from arg list and pass block contents? +#define REDUCE_FUNCTION_ARGS_MESH const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, \ + const VariableFluxPack& U, const VarMap& m_u, const Real& gam, \ + const int& k, const int& j, const int& i, const Real& arg -inline Real MdotBoundFlux(MeshData *md) {return AccretionRate(md, 0);} -inline Real MdotEHFlux(MeshData *md) {return AccretionRate(md, 5);} -inline Real EdotBoundFlux(MeshData *md) {return AccretionRate(md, 0);} -inline Real EdotEHFlux(MeshData *md) {return AccretionRate(md, 5);} -inline Real LdotBoundFlux(MeshData *md) {return AccretionRate(md, 0);} -inline Real LdotEHFlux(MeshData *md) {return AccretionRate(md, 5);} - -// Now we repeat the whole process for reductions across the entire domain - -#define MAKE_SUM3D_FN(name, fn) template<> inline Real DomainSum(MeshData *md, const Real& radius) { \ - Flag("Performing domain reduction"); \ - auto pmesh = md->GetMeshPointer(); \ -\ - Real result = 0.; \ - for (auto &pmb : pmesh->block_list) { \ - auto& rc = pmb->meshblock_data.Get(); \ - if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { \ - const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); \ - const MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); \ - PackIndexMap prims_map, cons_map; \ - const auto& P = rc->PackVariables(std::vector{isPrimitive}, prims_map); \ - const auto& U = rc->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); \ - const VarMap m_u(cons_map, true), m_p(prims_map, false); \ -\ - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); \ -\ - IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); \ - IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); \ - IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); \ - const auto& G = pmb->coords; \ -\ - Real block_result; \ - Kokkos::Sum sum_reducer(block_result); \ - pmb->par_reduce("domain_sum", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, \ - KOKKOS_LAMBDA_3D_REDUCE { \ - FourVectors Dtmp; \ - Real T[GR_DIM][GR_DIM]; \ - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); \ - DLOOP1 Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, mu, T[mu]); \ - GReal gdV = G.dx3v(k) * G.dx2v(j) * G.dx1v(i) * G.gdet(Loci::center, j, i); \ - GReal dV = G.dx3v(k) * G.dx2v(j) * G.dx1v(i); \ - fn \ - } \ - , sum_reducer); \ - result += block_result; \ - } \ - } \ -\ - Flag("Reduced"); \ -\ - return result; \ -} -enum class Mtot : int; -MAKE_SUM3D_FN(Mtot, - // Within radius... - GReal X[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, X); - if (X[1] < radius) { - local_result += U(m_u.RHO, k, j, i) * dV; - } -) -enum class Ltot : int; -MAKE_SUM3D_FN(Ltot, - GReal X[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, X); - if (X[1] < radius) { - local_result += U(m_u.U3, k, j, i) * dV; - } -) -enum class Etot : int; -MAKE_SUM3D_FN(Etot, - GReal X[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, X); - if (X[1] < radius) { - local_result += U(m_u.UU, k, j, i) * dV; - } -) - -// Luminosity proxy from (for example) Porth et al 2019. -// Notice that this will be totaled for *all zones*, -// but one could define a variable which checks sigma, G.coord_embed(), etc -enum class EHTLum : int; -MAKE_SUM3D_FN(EHTLum, - // Within radius... - GReal X[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, X); - if (X[1] > radius) { - Real rho = P(m_p.RHO, k, j, i); - Real Pg = (gam - 1.) * P(m_p.UU, k, j, i); - Real Bmag = m::sqrt(dot(Dtmp.bcon, Dtmp.bcov)); - Real j_eht = m::pow(rho, 3.) * m::pow(Pg, -2.) * exp(-0.2 * m::pow(rho * rho / (Bmag * Pg * Pg), 1./3.)); - local_result += j_eht * gdV; - } -) - -// Example of checking extra conditions before adding local results: -// sums total jet power only at exactly r=radius, for areas with sig > 1 -// Split versions for e.g. E&M power only should calculate T manually for their case -enum class JetLum : int; -MAKE_SUM3D_FN(JetLum, - // At r = radius, i.e. if our faces span acreoss it... - GReal X_f[GR_DIM]; GReal X_b[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, X_b); - G.coord_embed(k, j, i+1, Loci::face1, X_f); - if (X_f[1] > radius && X_b[1] < radius) { - // If sigma > 1... - if ((dot(Dtmp.bcon, Dtmp.bcov) / P(m_p.RHO, k, j, i)) > 1.) { - // Energy flux, like at EH. 2D integral jacobian. - local_result += -T[X1DIR][X0DIR] * G.dx3v(k) * G.dx2v(j) * G.gdet(Loci::center, j, i);; - } - } -) - -inline Real TotalM(MeshData *md) {return DomainSum(md, 50.);} -inline Real TotalE(MeshData *md) {return DomainSum(md, 50.);} -inline Real TotalL(MeshData *md) {return DomainSum(md, 50.);} +namespace Reductions { -inline Real TotalEHTLum(MeshData *md) {return DomainSum(md, 50.);} -inline Real JetLum_50(MeshData *md) {return DomainSum(md, 50.);} // Recall this is *at* not *within* +/** + * Perform a reduction using operation 'op' over a spherical shell at the given zone, measured from left side of + * innermost block in radius. + * As this only runs on innermost blocks, this is intended for accretion/event horizon + * measurements in black hole simulations. + */ +Real EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone); -// #define MAKE_MAX_FN(name, fn) template<> inline Real DomainSum(MeshData *md, const Real& radius) { \ -// Flag("Performing domain reduction"); \ -// auto pmesh = md->GetMeshPointer(); \ -// \ -// Real result = 0.; \ -// for (auto &pmb : pmesh->block_list) { \ -// auto& rc = pmb->meshblock_data.Get(); \ -// if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { \ -// const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); \ -// const MetadataFlag isPrimitive = pars.Get("PrimitiveFlag"); \ -// PackIndexMap prims_map, cons_map; \ -// const auto& P = rc->PackVariables(std::vector{isPrimitive}, prims_map); \ -// const auto& U = rc->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); \ -// const VarMap m_u(cons_map, true), m_p(prims_map, false); \ -// \ -// const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); \ -// \ -// IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); \ -// IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); \ -// IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); \ -// const auto& G = pmb->coords; \ -// \ -// Real block_result; \ -// Kokkos::Sum sum_reducer(block_result); \ -// pmb->par_reduce("domain_sum", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, \ -// KOKKOS_LAMBDA_3D_REDUCE { \ -// FourVectors Dtmp; \ -// Real T[GR_DIM][GR_DIM]; \ -// GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); \ -// DLOOP1 Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, mu, T[mu]); \ -// GReal gdV = G.dx3v(k) * G.dx2v(j) * G.dx1v(i) * G.gdet(Loci::center, j, i); \ -// GReal dV = G.dx3v(k) * G.dx2v(j) * G.dx1v(i); \ -// fn \ -// } \ -// , sum_reducer); \ -// result += block_result; \ -// } \ -// } \ -// \ -// Flag("Reduced"); \ -// \ -// return result; \ -// } +/** + * Perform a reduction using operation 'op' over all zones. + * The extra 'arg' is passed as the last argument to the device-side function. + * It is generally used to denote a radius inside, outside, or at which the reduction should be taken. + * This should be used for 2D shell sums not at the EH: just divide the function result by the zone spacing dx1. + */ +Real DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg); -// enum class MaxP -// enum class Max -// enum class MaxBeta -// enum class MinBeta -// enum class +/** + * Count instances of a particular flag value in the named field. + * is_bitflag specifies whether multiple flags may be present and will be orthogonal (e.g. FFlag), + * or whether flags receive consecutive integer values. + */ +int CountFlag(MeshData *md, std::string field_name, const int& flag_val, IndexDomain domain, bool is_bitflag); -inline int NPFlags(MeshData *md) {return CountPFlags(md, IndexDomain::interior, 0);} -inline int NFFlags(MeshData *md) {return CountFFlags(md, IndexDomain::interior, 0);} +/** + * Count instances of a particular flag value in the named field. + * is_bitflag specifies whether multiple flags may be present and will be orthogonal (e.g. FFlag), + * or whether flags receive consecutive integer values. + * TODO could return numbers for all flags instead of just printing + */ +int CountFlags(MeshData *md, std::string field_name, std::map flag_values, IndexDomain domain, int verbose, bool is_bitflag); } // namespace Reductions diff --git a/kharma/types.hpp b/kharma/types.hpp index fd92d2cf..1155be29 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -34,7 +34,8 @@ #pragma once #include "decs.hpp" -#include "mpi.hpp" + +#include "kharma_package.hpp" #include @@ -51,18 +52,11 @@ using parthenon::MeshBlockData; // This provides a way of addressing vectors that matches // directions, to make derivatives etc more readable +// TODO Spammy to namespace. Keep? #define V1 0 #define V2 1 #define V3 2 -// Denote reconstruction algorithms -// See reconstruction.hpp for implementations -enum ReconstructionType{donor_cell=0, linear_mc, linear_vl, ppm, mp5, weno5, weno5_lower_poles}; - -// Denote inversion failures (pflags). See U_to_P for status explanations -// Only thrown from function in U_to_P.hpp, see that file for meanings -enum InversionStatus{success=0, neg_input, max_iter, bad_ut, bad_gamma, neg_rho, neg_u, neg_rhou}; - // Struct for derived 4-vectors at a point, usually calculated and needed together typedef struct { Real ucon[GR_DIM]; @@ -71,6 +65,12 @@ typedef struct { Real bcov[GR_DIM]; } FourVectors; +typedef struct { + IndexRange ib; + IndexRange jb; + IndexRange kb; +} IndexRange3; + /** * Map of the locations of particular variables in a VariablePack * Used for operations conducted over all vars which must still @@ -153,26 +153,105 @@ class VarMap { }; /** - * Functions for checking boundaries in 3D + * Functions for checking boundaries in 3D. + * Uses IndexRange objects, or this would be in kharma_utils.hpp */ -KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, - const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) -{ - return (i >= ib.s) && (i <= ib.e) && (j >= jb.s) && (j <= jb.e) && (k >= kb.s) && (k <= kb.e); -} KOKKOS_INLINE_FUNCTION bool outside(const int& k, const int& j, const int& i, const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) { return (i < ib.s) || (i > ib.e) || (j < jb.s) || (j > jb.e) || (k < kb.s) || (k > kb.e); } +KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, + const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) +{ + // This is faster in the case that the point is outside + return !outside(k, j, i, kb, jb, ib); +} /** * Function for checking boundary flags: is this a domain or internal bound? */ -inline bool IsDomainBound(MeshBlock *pmb, BoundaryFace face) +inline bool IsDomainBound(std::shared_ptr pmb, BoundaryFace face) +{ + return !(pmb->boundary_flag[face] == BoundaryFlag::block || + pmb->boundary_flag[face] == BoundaryFlag::periodic); +} + +inline bool BoundaryIsInner(IndexDomain domain) +{ + return domain == IndexDomain::inner_x1 || + domain == IndexDomain::inner_x2 || + domain == IndexDomain::inner_x3; +} + +inline int BoundarySide(IndexDomain domain) +{ + switch (domain) { + case IndexDomain::inner_x1: + case IndexDomain::outer_x1: + return 1; + case IndexDomain::inner_x2: + case IndexDomain::outer_x2: + return 2; + case IndexDomain::inner_x3: + case IndexDomain::outer_x3: + return 3; + default: + return 0; + } +} + +inline std::string BoundaryName(IndexDomain domain) +{ + switch (domain) { + case IndexDomain::inner_x1: + return "inner_x1"; + case IndexDomain::outer_x1: + return "outer_x1"; + case IndexDomain::inner_x2: + return "inner_x2"; + case IndexDomain::outer_x2: + return "outer_x2"; + case IndexDomain::inner_x3: + return "inner_x3"; + case IndexDomain::outer_x3: + return "outer_x3"; + case IndexDomain::interior: + return "interior"; + case IndexDomain::entire: + return "entire"; + default: + return "unknown"; + } +} + +/** + * Get zones in the domain interior + */ + +/** + * Get the + */ +inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& bounds) { - return (pmb->boundary_flag[face] != BoundaryFlag::block && - pmb->boundary_flag[face] != BoundaryFlag::periodic); + return IndexRange3{IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x1) + ? bounds.is(IndexDomain::interior) + : bounds.is(IndexDomain::entire), + IsDomainBound(pmb, BoundaryFace::outer_x1) + ? bounds.ie(IndexDomain::interior) + : bounds.ie(IndexDomain::entire)}, + IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x2) + ? bounds.js(IndexDomain::interior) + : bounds.js(IndexDomain::entire), + IsDomainBound(pmb, BoundaryFace::outer_x2) + ? bounds.je(IndexDomain::interior) + : bounds.je(IndexDomain::entire)}, + IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x3) + ? bounds.ks(IndexDomain::interior) + : bounds.ks(IndexDomain::entire), + IsDomainBound(pmb, BoundaryFace::outer_x3) + ? bounds.ke(IndexDomain::interior) + : bounds.ke(IndexDomain::entire)}}; } /** @@ -224,12 +303,26 @@ inline void PrintZone(MeshBlockData *rc) auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - std::cerr << "RHO: " << rhop(0,0,100) - << " UU: " << up(0,0,100) - << " U: " << uvecp(0, 0,0,100) << " " << uvecp(1, 0,0,100)<< " " << uvecp(2, 0,0,100) - << " B: " << Bp(0, 0,0,100) << " " << Bp(1, 0,0,100) << " " << Bp(2, 0,0,100) - << " q: " << q(0,0,100) - << " dP: " << dP(0,0,100) << std::endl; + + auto rhoU = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); + auto uU = rc->Get("cons.u").data.GetHostMirrorAndCopy(); + auto uvecU = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); + auto BU = rc->Get("cons.B").data.GetHostMirrorAndCopy(); + auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); + auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); + + std::cerr << "RHO: " << rhop(0,108,63) + << " UU: " << up(0,108,63) + << " U: " << uvecp(0,0,108,63) << " " << uvecp(1,0,108,63)<< " " << uvecp(2,0,108,63) + << " B: " << Bp(0,0,108,63) << " " << Bp(1,0,108,63) << " " << Bp(2,0,108,63) + << " q: " << q(0,108,63) + << " dP: " << dP(0,108,63) << std::endl; + std::cerr << "RHO: " << rhoU(0,108,63) + << " UU: " << uU(0,108,63) + << " U: " << uvecU(0,0,108,63) << " " << uvecU(1,0,108,63)<< " " << uvecU(2,0,108,63) + << " B: " << BU(0,0,108,63) << " " << BU(1,0,108,63) << " " << BU(2,0,108,63) + << " q: " << qU(0,108,63) + << " dP: " << dPU(0,108,63) << std::endl; } inline void Flag(std::string label) @@ -263,3 +356,10 @@ inline void Flag(std::string label) {} inline void Flag(MeshBlockData *rc, std::string label) {} inline void Flag(MeshData *md, std::string label) {} #endif +/** + * Versions of Flag() that take shared_ptr objects and call through with get() + * Avoids having to pay attention to shared_ptr vs * pointers in adding Flag() calls + * when diagnosing a problem. + */ +inline void Flag(std::shared_ptr>& rc, std::string label) { Flag(rc.get(), label); } +inline void Flag(std::shared_ptr>& md, std::string label) { Flag(md.get(), label); } diff --git a/kharma/wind/wind.cpp b/kharma/wind/wind.cpp index e2107192..3c0483fe 100644 --- a/kharma/wind/wind.cpp +++ b/kharma/wind/wind.cpp @@ -31,12 +31,11 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "wind.hpp" -std::shared_ptr Wind::Initialize(ParameterInput *pin) +std::shared_ptr Wind::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - auto pkg = std::make_shared("Wind"); + auto pkg = std::make_shared("Wind"); Params ¶ms = pkg->AllParams(); // Wind term in funnel @@ -53,10 +52,14 @@ std::shared_ptr Wind::Initialize(ParameterInput *pin) Real ramp_end = pin->GetOrAddReal("wind", "ramp_end", 0.0); params.Add("ramp_end", ramp_end); + pkg->AddSource = Wind::AddSource; + + // TODO track additions? + return pkg; } -TaskStatus Wind::AddSource(MeshData *mdudt) +TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) { Flag(mdudt, "Adding wind"); // Pointers @@ -89,7 +92,7 @@ TaskStatus Wind::AddSource(MeshData *mdudt) const Real current_n = (ramp_end > 0.0) ? m::min(m::max(time - ramp_start, 0.0) / (ramp_end - ramp_start), 1.0) * n : n; pmb0->par_for("add_wind", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA_MESH_3D { + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = dUdt.GetCoords(b); // Need coordinates to evaluate particle addtn rate // Note that makes the wind spherical-only, TODO ensure this @@ -97,9 +100,8 @@ TaskStatus Wind::AddSource(MeshData *mdudt) G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1], th = Xembed[2]; - // Particle addition rate: concentrate at poles & center - // TODO poles only w/e.g. cos2? - Real drhopdt = current_n * m::pow(cos(th), power) / m::pow(1. + r * r, 2); + // Particle addition rate: concentrate at poles + Real drhopdt = current_n * m::pow(m::cos(th), power) / m::pow(1. + r * r, 2); // Insert fluid moving in positive U1, without B field // Ramp up like density, since we're not at a set proportion diff --git a/kharma/wind/wind.hpp b/kharma/wind/wind.hpp index f6cede09..b7ad86df 100644 --- a/kharma/wind/wind.hpp +++ b/kharma/wind/wind.hpp @@ -42,11 +42,11 @@ namespace Wind { /** * Initialize the wind package with several options from the input deck */ -std::shared_ptr Initialize(ParameterInput *pin); +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** - * Add the wind source term. Applied just after the FluxDivergence/ApplyFluxes calculation + * Add the wind source term. Applied in Flux::AddSource, just after the FluxDivergence calculation */ -TaskStatus AddSource(MeshData *mdudt); +TaskStatus AddSource(MeshData *md, MeshData *mdudt); } diff --git a/machines/bp.sh b/machines/bp.sh index a4f457de..9eed4109 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -14,7 +14,7 @@ if [[ $HOST == "cheshire"* ]]; then module load compiler mpi/2021 fi - NPROC=24 + NPROC=16 MPI_EXE=mpirun fi @@ -27,46 +27,6 @@ if [[ $METAL_HOSTNAME == "fermium" ]]; then DEVICE_ARCH="TURING75" # Nvidia MPI hangs unless I do this MPI_EXE=mpirun - - if [[ "$ARGS" == *"cuda"* ]]; then - module purge - module load nvhpc - PREFIX_PATH="$HOME/libs/hdf5-nvhpc" - MPI_NUM_PROCS=1 - - if [[ "$ARGS" == *"gcc"* ]]; then - C_NATIVE=gcc - CXX_NATIVE=g++ - else - C_NATIVE=nvc - CXX_NATIVE=nvc++ - export CFLAGS="-mp" - export CXXFLAGS="-mp" - fi - else - # To experiment with AMD NUMA - #MPI_EXTRA_ARGS="--map-by ppr:2:socket:pe=12" - #MPI_NUM_PROCS=2 - if [[ "$ARGS" == *"gcc"* ]]; then - module purge - #module load mpi/mpich-x86_64 - C_NATIVE=gcc - CXX_NATIVE=g++ - elif [[ "$ARGS" == *"clang"* ]]; then - module purge - module load mpi/mpich-x86_64 - C_NATIVE=clang - CXX_NATIVE=clang++ - PREFIX_PATH="$HOME/libs/hdf5-clang14" - else - module purge - module load mpi/mpich-x86_64 - source /opt/AMD/aocc-compiler-3.2.0/setenv_AOCC.sh - PREFIX_PATH="$HOME/libs/hdf5-aocc" - C_NATIVE=clang - CXX_NATIVE=clang++ - fi - fi fi if [[ $METAL_HOSTNAME == "ferrum" ]]; then @@ -103,7 +63,7 @@ if [[ $HOST == "cinnabar"* ]]; then if [[ "$ARGS" == *"cuda"* ]]; then # Use NVHPC libraries (GPU-aware OpenMPI!) DEVICE_ARCH="KEPLER35" - MPI_NUM_PROCS=2 + MPI_NUM_PROCS=1 MPI_EXTRA_ARGS="--map-by ppr:1:numa:pe=14" # Quash warning about my old gpus diff --git a/machines/illinois.sh b/machines/illinois.sh index 2a18c18c..7f32f0ac 100644 --- a/machines/illinois.sh +++ b/machines/illinois.sh @@ -43,7 +43,7 @@ elif [[ $HOST == *".astro.illinois.edu" ]]; then # Older GCC has no flag for ZEN2 HOST_ARCH="ZEN" fi - module load gnu mpich phdf5 + module load gnu hdf5 fftw3 # System HDF5 location PREFIX_PATH="$MPI_DIR" fi diff --git a/machines/incite.sh b/machines/incite.sh index 2dfd8bb4..1268469a 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -11,8 +11,8 @@ if [[ $HOST == *".summit.olcf.ornl.gov" ]]; then KOKKOS_NUM_DEVICES=1 MPI_NUM_PROCS=6 - # ONLY GCC WORKS: There are C++17 compile issues with most other combos/stacks - # Tested with Spectrum MPI 10.4.0.3 + # Summit *hates* C++17. + # Use GCC with 14 module load cmake if [[ "$ARGS" == *"xl"* ]]; then # xlC: OpenMP CXX problems @@ -32,7 +32,7 @@ if [[ $HOST == *".summit.olcf.ornl.gov" ]]; then PREFIX_PATH="/gpfs/alpine/proj-shared/ast171/libs/hdf5-nvhpc-21.9" else # Use default GCC - module load gcc/11.1.0 hdf5/1.10.7 cuda/11.5.2 + module load gcc cuda hdf5 C_NATIVE='gcc' CXX_NATIVE='g++' fi diff --git a/make.sh b/make.sh index 3be92a4a..58fd7179 100755 --- a/make.sh +++ b/make.sh @@ -43,7 +43,6 @@ # Less common options: # PREFIX_PATH= -# EXTRA_FLAGS= HOST=$(hostname -f) if [ -z $HOST ]; then @@ -82,6 +81,9 @@ fi if [[ "$ARGS" == *"noimplicit"* ]]; then EXTRA_FLAGS="-DKHARMA_DISABLE_IMPLICIT=1 $EXTRA_FLAGS" fi +if [[ "$ARGS" == *"nocleanup"* ]]; then + EXTRA_FLAGS="-DKHARMA_DISABLE_CLEANUP=1 $EXTRA_FLAGS" +fi ### Enivoronment Prep ### if [[ "$(which python3 2>/dev/null)" == *"conda"* ]]; then @@ -125,7 +127,6 @@ if [[ -z "$CXX_NATIVE" ]]; then elif which icpc >/dev/null 2>&1; then CXX_NATIVE=icpc C_NATIVE=icc - # Prefer NVHPC over generic compilers elif which nvc++ >/dev/null 2>&1; then CXX_NATIVE=nvc++ @@ -218,12 +219,16 @@ fi if [[ $CXX == "icpc" ]]; then export CXXFLAGS="-Wno-unknown-pragmas $CXXFLAGS" fi +# Avoid icpx's astonishing DEFAULT -ffast-math +if [[ $CXX == "icpx" ]]; then + export CXXFLAGS="-fno-fast-math $CXXFLAGS" +fi ### Build HDF5 ### # If we're building HDF5, do it after we set *all flags* if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then - H5VER=1.12.0 - H5VERU=1_12_0 + H5VER=1.12.2 + H5VERU=1_12_2 cd external if [ ! -d hdf5-${H5VER}/ ]; then curl https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${H5VERU}/source/hdf5-${H5VER}.tar.gz -o hdf5-${H5VER}.tar.gz @@ -264,8 +269,13 @@ if [[ "$ARGS" == *"hdf5"* ]]; then fi ### Build KHARMA ### -# Optionally delete build/ to wipe the slate +# If we're doing a clean build, prep the source and +# delete the build directory if [[ "$ARGS" == *"clean"* ]]; then + cd external/parthenon + git apply ../patches/parthenon-*.patch + cd - + rm -rf build fi mkdir -p build diff --git a/pars/bondi.par b/pars/bondi.par index 744ee84d..e7de780a 100644 --- a/pars/bondi.par +++ b/pars/bondi.par @@ -17,16 +17,17 @@ nx3 = 1 # Split into blocks mesh # Don't bother with xN boundaries for spherical coordinate systems # KHARMA will automatically place ~5 zones inside the EH -nx1 = 32 -nx2 = 32 +nx1 = 128 +nx2 = 128 nx3 = 1 base = ks -transform = fmks +transform = mks a = 0.0 hslope = 0.3 -r_out = 30 +r_in = 3.0 +r_out = 30.0 tlim = 50.0 @@ -45,7 +46,7 @@ rs = 8.0 disable_floors = true # We'll be adding material, and that's okay - + check_inflow_outer = false @@ -58,10 +59,8 @@ solver = none verbose = 0 -flag_verbose = 2 - - -type = harm +flag_verbose = 0 +extra_checks = 1 file_type = hdf5 diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index c6ab6825..1c3d15ac 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -13,7 +13,7 @@ nx3 = 1 nx1 = 128 -nx2 = 128 +nx2 = 64 nx3 = 1 @@ -21,9 +21,8 @@ base = ks transform = mks a = 0.0 hslope = 1.0 -# Override usual 5 zones in EH by specifying inner radius -r_in = 3.0 r_out = 20 +Rhor = 3 tlim = 400.0 @@ -36,26 +35,30 @@ implicit = true implicit = true -initial_cleanup = false +type = monopole_cube + + +type = imex + + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 # IMPORTANT: This block must be present and values filled in all EGRMHD simulations on = true higher_order_terms = true +feedback = false closure_type = kappa_eta tau = 30. kappa = 0.0 eta = 0.01 - -type = imex - - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 - mdot = 1.0 rs = 8.0 @@ -68,12 +71,13 @@ check_inflow_outer = false verbose = 1 +flag_verbose = 2 file_type = hdf5 -dt = 20.0 +dt = 100.0 single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail file_type = hst diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index d8bd396c..445c01fc 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -26,25 +26,36 @@ base = ks transform = mks a = 0.0 hslope = 1.0 -r_in = 100. +r_in = 200. r_out = 300. +use_dirichlet = true check_inflow_inner = false check_inflow_outer = false - tlim = 150. + +type = imex + + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 + -cfl = 0.5 +implicit = true +cfl = 0.9 gamma = 1.333333 reconstruction = weno5 -implicit = true -implicit = true +implicit = false initial_cleanup = false @@ -52,34 +63,29 @@ initial_cleanup = false on = true higher_order_terms = true +feedback = true closure_type = kappa_eta tau = 10. kappa = 0.1 eta = 0.0 - -type = imex - - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 - input = ODE -disable_floors = false +disable_floors = true +emhd_limits = false + + +use_dirichlet = true -verbose = 1 -flag_verbose = 2 -extra_checks = 1 +verbose = 1 file_type = hdf5 -dt = 20 +dt = 10 single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP diff --git a/pars/driven_turbulence.par b/pars/driven_turbulence.par new file mode 100644 index 00000000..6032d0c4 --- /dev/null +++ b/pars/driven_turbulence.par @@ -0,0 +1,87 @@ +# Driven turbulence electron heating +# Perturb 2D state + + +problem_id = driven_turbulence + + +refinement = none +numlevel = 1 + +nx1 = 64 +x1min = 0 +x1max = 1 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 64 +x2min = 0 +x2max = 1 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -1 +x3max = 1 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 +add_jcon = false + + +type = constant +b10 = 1 +norm = true +beta_min = 10 + + +cs0 = 8.6e-4 +edot_frac = 0.5 +dt_kick = 2. + + +tlim = 31396 +integrator = rk2 +use_dt_light = true + + +type = imex +two_sync = true + + +on = true +constant = true +kawazura = true +sharma = true +fel_constant = 0.5 +gamma_e = 1.333333 + + +verbose = 1 +flag_verbose = 0 +extra_checks = 1 + + +file_type = hdf5 +dt = 5. +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, & + prims.Ktot, prims.Kel_Constant, prims.Kel_Sharma, prims.Kel_Kawazura, & + grf_normalized, alfven_speed, ctop, fflag, pflag + + +file_type = hst +dt = 0.5 diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index 81d9c94f..8f34e899 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -61,9 +61,14 @@ disable_floors = true enable_emhd_limits = false -min_nonlinear_iter = 3 -max_nonlinear_iter = 3 -use_qr = false +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-20 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true # General verbosity level: @@ -79,12 +84,14 @@ flag_verbose = 0 # This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = false -closure_type = sound_speed -tau = 1.0 +feedback = true + +closure_type = sound_speed +tau = 1.0 conduction_alpha = 1.0 -viscosity_alpha = 1.0 +viscosity_alpha = 1.0 file_type = hdf5 @@ -92,7 +99,7 @@ file_type = hdf5 dt = 100.0 # Output in double due to low amplitude single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail file_type = hst diff --git a/pars/hubble.par b/pars/hubble.par new file mode 100644 index 00000000..17d6c53c --- /dev/null +++ b/pars/hubble.par @@ -0,0 +1,86 @@ +# GRMHD Modes problem +# Try to propagate several analytically-amenable linear modes of the MHD equations + + +problem_id = hubble + + +refinement = none +numlevel = 1 + +nx1 = 128 +x1min = -0.5 +x1max = 1.5 +ix1_bc = user +ox1_bc = user + +nx2 = 1 +x2min = -0.01 +x2max = 0.01 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 128 +nx2 = 1 +nx3 = 1 + + +check_inflow_inner = false +check_inflow_outer = false + + +base = cartesian_minkowski +transform = null + + +tlim = 3e-05 +integrator = rk2 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +verbose = 2 +flag_verbose = 1 +extra_checks = 1 + + +solver = none + + +mach = 1. +# Optimal number to avoid relativistic effects and keep the ratio between density/internal energy +v0 = 1e-3 +set_tlim = true +dyntimes = 1. + + +on = true +constant = true +gamma_e = 1.333333 +# This can be anything; we care about evolution of ue +fel_constant = 1.0 +# To match the ratio eq40 +fel_0 = 0.5 +diss_sign = false +kel_lim = false + + +type = imex + + +file_type = hdf5 +# Once at the end +ghost_zones = true +dt = 1 +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant, fflag diff --git a/pars/mhdmodes.par b/pars/mhdmodes.par index 629adba4..0703cb42 100644 --- a/pars/mhdmodes.par +++ b/pars/mhdmodes.par @@ -16,7 +16,7 @@ nmode = 1 # 1: propagate in X2/X3 plane # 2: propagate in X1/X3 plane # 3: propagate in X1/X2 plane -dir = 0 +dir = 3 # Size and parameters of the full mesh # KHARMA does not yet support AMR, @@ -26,19 +26,19 @@ dir = 0 refinement = none numlevel = 1 -nx1 = 64 +nx1 = 1024 x1min = 0.0 x1max = 1.0 ix1_bc = periodic ox1_bc = periodic -nx2 = 64 +nx2 = 1024 x2min = 0.0 x2max = 1.0 ix2_bc = periodic ox2_bc = periodic -nx3 = 64 +nx3 = 1 x3min = 0.0 x3max = 1.0 ix3_bc = periodic @@ -48,9 +48,9 @@ ox3_bc = periodic # # of meshblocks must be >= the number of MPI ranks, # however there may be multiple blocks per rank -nx1 = 32 -nx2 = 32 -nx3 = 32 +nx1 = 1024 +nx2 = 1024 +nx3 = 1 # Set boring box coordinates. Explanations in bondi.par @@ -60,14 +60,13 @@ transform = null # tlim will be overridden depending on the problem tlim = 5.0 -integrator = rk2 +integrator = vl2 # Minimum is also the starting timestep dt_min = 0.0001 cfl = 0.9 gamma = 1.333333 -reconstruction = weno5 # Whether to evolve these variables with an # implicit solver similar to GRIM implicit = false @@ -85,25 +84,26 @@ disable_floors = true # General verbosity level: # 1: general archival info # 2: specific debugging logs -verbose = 1 +verbose = 0 # Set to 1 to check each step for wavespeed of zero/NaN & exit -extra_checks = 0 +extra_checks = 1 # Print summary of all flags hit during each step: # 1: Number of flags total # 2: Number of flags of each type flag_verbose = 0 -# Driver, of type "harm" or "imex". The former +# Driver, of type "kharma" or "imex". The former # Synchronizes mostly the conserved variables, # the latter synchronizes primitives. -type = harm +type = simple +reconstruction = weno5 # Primary HDF5 output enabled in most problems file_type = hdf5 # This is so as to output only the final state -dt = 100.0 +dt = 0.01 single_precision_output = true variables = prims.rho, prims.u, prims.uvec, prims.B diff --git a/pars/noh.par b/pars/noh.par index 70f4245c..3361cdb7 100644 --- a/pars/noh.par +++ b/pars/noh.par @@ -13,7 +13,7 @@ numlevel = 1 nx1 = 2000 x1min = 0.0 x1max = 1.0 -ix1_bc = outflow +ix1_bc = reflecting ox1_bc = outflow nx2 = 1 @@ -49,32 +49,33 @@ solver = none on = true constant = true -kawazura = false -sharma = false -werner = false -rowan = false -gamma_e = 1.666667 -fel_0 = 0.0 +gamma_e = 1.333333 +fel_0 = 0. fel_constant = 0.5 +diss_sign = false +kel_min = false mach = 49. -rhoL = 1.0 -rhoR = 1.0 -PL = 1.e-6 -PR = 1.e-6 +rho = 1. +v0 = 1.e-3 +zero_ug = false set_tlim = true +centered = false disable_floors = true + +type = imex + verbose = 0 file_type = hdf5 dt = 0.1 -single_precision_output = true +single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index be4869d7..c3340a98 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -45,9 +45,9 @@ gamma = 1.666667 reconstruction = weno5 -verbose = 0 -flag_verbose = 0 -extra_checks = 0 +verbose = 1 +flag_verbose = 2 +extra_checks = 1 file_type = hdf5 @@ -58,3 +58,9 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 + +# This problem is generally much too short to need +# checkpointing. However, we have a test which uses it. +# +#file_type = rst +#dt = 10.0 diff --git a/pars/rest_conserve.par b/pars/rest_conserve.par new file mode 100644 index 00000000..327b9b5c --- /dev/null +++ b/pars/rest_conserve.par @@ -0,0 +1,80 @@ +# GRMHD Modes problem +# Try to propagate several analytically-amenable linear modes of the MHD equations + + +problem_id = rest_conserve + + +refinement = none +numlevel = 1 + +nx1 = 128 +x1min = -2 +x1max = 2 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 1 +x2min = -0.01 +x2max = 0.01 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 128 +nx2 = 1 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 10. +integrator = rk2 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +flag_verbose = 1 +extra_checks = 1 + + +solver = none + + +u0 = 1e3 +rho0 = 1e3 +q = 1 +set_tlim = false + + +on = true +constant = true +gamma_e = 1.333333 +# This can be anything; we care about evolution of ue +fel_constant = 1.0 +# To match the ratio eq40 +fel_0 = 1. +diss_sign = false +kel_lim = false + + +type = imex + + +file_type = hdf5 +# Once at the end +ghost_zones = true +dt = 1 +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant, fflag diff --git a/pars/sane.par b/pars/sane.par index a9fd9c04..ff324c5c 100644 --- a/pars/sane.par +++ b/pars/sane.par @@ -13,8 +13,8 @@ nx2 = 64 nx3 = 64 -nx1 = 32 -nx2 = 32 +nx1 = 128 +nx2 = 64 nx3 = 32 @@ -34,11 +34,11 @@ nlim = -1 cfl = 0.9 gamma = 1.666667 -reconstruction = weno5 -type = imex +type = kharma two_sync = true +reconstruction = weno5 rin = 6.0 @@ -50,7 +50,8 @@ u_jitter = 0.04 type = sane beta_min = 100. -initial_cleanup = true +fix_eh_flux = false +fix_exterior_flux = false rho_min_geom = 1e-6 @@ -59,10 +60,9 @@ bsq_over_rho_max = 100 u_over_rho_max = 2 -archive_parameters = true verbose = 1 extra_checks = 1 -flag_verbose = 0 +flag_verbose = 2 on = false @@ -73,7 +73,7 @@ Tp = 10 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, divB file_type = rst diff --git a/pars/sane2d.par b/pars/sane2d.par index df90ce85..5d555836 100644 --- a/pars/sane2d.par +++ b/pars/sane2d.par @@ -15,7 +15,7 @@ nx3 = 1 nx1 = 128 -nx2 = 128 +nx2 = 64 nx3 = 1 diff --git a/pars/sane2d_cooling.par b/pars/sane2d_cooling.par new file mode 100644 index 00000000..34c16996 --- /dev/null +++ b/pars/sane2d_cooling.par @@ -0,0 +1,97 @@ +# SANE model mirroring the simulation library +# Overall simulation size 50M, to allow +# running at small scale on e.g. a laptop +# Uses MKS coordinates, not Funky variant + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 128 +nx3 = 1 + + +nx1 = 128 +nx2 = 64 +nx3 = 1 + + +base = spherical_ks +transform = mks +r_out = 50 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 3000.0 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +type = sane +beta_min = 100. + + +rho_min_geom = 1e-5 +u_min_geom = 1e-7 +ktot_max = 1500 +u_over_rho_max = 100 +bsq_over_rho_max = 100 + + +MBH = 3.9e6, 4.3e6, 6.5e9 +M_unit_1 = 1e17, 1e18, 1e19 +M_unit_2 = 1e17, 1e18, 1e19 +M_unit_3 = 1e20, 1e21, 1e22 + + +on = true +howes = false +kawazura = true +werner = true +rowan = true +sharma = true + + +on = false + + +file_type = hdf5 +dt = 10.0 +single_precision_output = true +# Any fields listed here which are not present (e.g. electrons if disabled) +# will be silently skipped. '&' character is a line continuation, like '\' +# Remember that the commas are still necessary, and unknown fields will silently fail! +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.Ktot, & + prims.Kel_Howes, prims.Kel_Kawazura, prims.Kel_Werner, prims.Kel_Rowan, prims.Kel_Sharma, & + pflag, fflag + + +file_type = rst +dt = 100.0 +ghost_zones = true + + +file_type = hst +dt = 0.1 diff --git a/pars/sane_divb_2d.par b/pars/sane_divb_2d.par index 542ba8d6..6157ed55 100644 --- a/pars/sane_divb_2d.par +++ b/pars/sane_divb_2d.par @@ -31,9 +31,6 @@ poly_alpha = 14.0 tlim = 3000.0 nlim = -1 - -type = harm - cfl = 0.9 gamma = 1.666667 diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par new file mode 100644 index 00000000..df41df12 --- /dev/null +++ b/pars/sane_emhd.par @@ -0,0 +1,108 @@ +# Extended SANE model mirroring the simulation library +# Quite small to run for more than 10kM, 6M/12M F-M torus, +# Overall simulation size 1000M + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +nx1 = 32 +nx2 = 32 +nx3 = 32 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 4000.0 +nlim = -1 + + +type = imex +two_sync = true + + +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-3 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true + + +implicit = true +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +implicit = false +type = sane +beta_min = 100. +initial_cleanup = true + +# This block must be present and values filled in all EGRMHD simulations + +on = true +higher_order_terms = true +feedback = true + +closure_type = torus +conduction_alpha = 1.0 +viscosity_alpha = 1.0 + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +frame = drift +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 +enable_emhd_limits = true + + +archive_parameters = true +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +on = false +ne = 1.e-4 +Tp = 10 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, q, dP, jcon, fflag, pflag, solve_norm, solve_fail, eflag + + +file_type = rst +dt = 100.0 + + +file_type = hst +dt = 0.1 diff --git a/pars/sane_imex.par b/pars/sane_imex.par new file mode 100644 index 00000000..80af3404 --- /dev/null +++ b/pars/sane_imex.par @@ -0,0 +1,98 @@ +# SANE model mirroring the simulation library +# Quite small to run for more than 10kM, 6M/12M F-M torus, +# Overall simulation size 1000M +# Uses the IMEX solver + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +nx1 = 32 +nx2 = 32 +nx3 = 32 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 4000.0 +nlim = -1 + + +type = imex +two_sync = true + + +min_nonlinear_iter = 1 +max_nonlinear_iter = 3 +jacobian_delta = 4.e-8 +rootfind_tol = 1.e-3 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 +use_qr = true + + +implicit = true +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +implicit = false +type = sane +beta_min = 100. +initial_cleanup = true + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +frame = drift +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +archive_parameters = true +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +on = false +ne = 1.e-4 +Tp = 10 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag, solve_norm, solve_fail + + +file_type = rst +dt = 100.0 + + +file_type = hst +dt = 0.1 diff --git a/pars/sane_perf.par b/pars/sane_perf.par index 24e7d660..7a398911 100644 --- a/pars/sane_perf.par +++ b/pars/sane_perf.par @@ -39,7 +39,7 @@ gamma = 1.666667 reconstruction = weno5 -type = harm +type = kharma two_sync = true diff --git a/pars/sane_tilt.par b/pars/sane_tilt.par index 547abaca..37a72f1e 100644 --- a/pars/sane_tilt.par +++ b/pars/sane_tilt.par @@ -33,7 +33,7 @@ gamma = 1.666667 reconstruction = weno5 -type = harm +type = kharma rin = 8.0 diff --git a/pars/shocks/noh_43.par b/pars/shocks/noh_43.par new file mode 100644 index 00000000..a3535079 --- /dev/null +++ b/pars/shocks/noh_43.par @@ -0,0 +1,76 @@ +# 1D Noh shock test for electrons +# Should reproduce electron energies behind the shock +# as in Ressler+ 2015 eqn. 41 + + +problem_id = shock + + +refinement = none +numlevel = 1 + +nx1 = 400 +x1min = 0.0 +x1max = 1.0 +ix1_bc = outflow +ox1_bc = outflow + +nx2 = 1 +x2min = 0.0 +x2max = 1.0 + +nx3 = 1 +x3min = 0.0 +x3max = 1.0 + + +nx1 = 400 +nx2 = 1 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 1.0 +integrator = rk2 +dt_min = 0.0001 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +solver = none + + +on = true +constant = true +gamma_e = 1.333333 +fel_0 = 0.0 +fel_constant = 0.5 + + +rhoL = 1. +PL = 0.01 +u1L = 0.5 + +rhoR = 1. +PR = 0.01 +u1R = -0.5 + + +disable_floors = true + + +file_type = hdf5 +dt = 0.1 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant + + +file_type = hst +dt = 0.1 + diff --git a/pars/shocks/noh_53.par b/pars/shocks/noh_53.par new file mode 100644 index 00000000..5701bfe2 --- /dev/null +++ b/pars/shocks/noh_53.par @@ -0,0 +1,76 @@ +# 1D Noh shock test for electrons +# Should reproduce electron energies behind the shock +# as in Ressler+ 2015 eqn. 41 + + +problem_id = shock + + +refinement = none +numlevel = 1 + +nx1 = 400 +x1min = 0.0 +x1max = 1.0 +ix1_bc = outflow +ox1_bc = outflow + +nx2 = 1 +x2min = 0.0 +x2max = 1.0 + +nx3 = 1 +x3min = 0.0 +x3max = 1.0 + + +nx1 = 400 +nx2 = 1 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 1.0 +integrator = rk2 +dt_min = 0.0001 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +solver = none + + +on = true +constant = true +gamma_e = 1.666667 +fel_0 = 0.0 +fel_constant = 0.5 + + +rhoL = 1. +PL = 0.01 +u1L = 0.5 + +rhoR = 1. +PR = 0.01 +u1R = -0.5 + + +disable_floors = true + + +file_type = hdf5 +dt = 0.1 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant + + +file_type = hst +dt = 0.1 + diff --git a/scripts/batch/delta.sb b/scripts/batch/delta.sb index 2dfdeb95..d97210b9 100755 --- a/scripts/batch/delta.sb +++ b/scripts/batch/delta.sb @@ -35,7 +35,7 @@ export CUDA_LAUNCH_BLOCKING=0 #export KOKKOS_DEVICE_ID=0 # Choose the kharma from compiled options in order of preference -KHARMA_DIR="$HOME/kharma" +KHARMA_DIR=${KHARMA_DIR:-"$HOME/kharma"} # Optionally use the Kokkos tools to profile kernels #export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so diff --git a/tests/README.md b/tests/README.md index 425b9cba..d803e34e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,9 +3,11 @@ Since all KHARMA parameters are determined at runtime, testing KHARMA is relatively easy, and many different tests are defined changing different options -Tests are housed in folders, each containing a bash script `run.sh` to be run by CI, and a +Tests are housed in folders, each containing a bash script `run.sh` to perform any runs for +the test, and another, `check.sh` to veryify the results. `check.sh` usually calls a python script `check.py` to produce any relevant plots and check that the output matches -expectations. +expectations. Note that while `run.sh` will exit on the first failed run, `check.sh` runs +all checks, accumulating a single return value `0` for success or `1` if any check fails. While tests sometimes use many meshblocks, they do not by default use more than 1 MPI process. This may change if MPI-related issues crop up requiring KHARMA-specific tests @@ -19,11 +21,21 @@ Current and near-future planned tests are outlined below. * Unmagnetized static Bondi accretion `bondi` * MHD linear modes `mhdmodes` * Komissarov shock tube tests `komissarov_shocks` -* BZ monopole stability test `bz_monopole` -See pretty much any GRMHD code paper, but notably Gammie+ [2003](https://doi.org/10.1086/374594). -Several variants of each test are run, using different coordinate systems, reconstruction, etc -to catch regressions in particular features. +Tests outlined in many code papers, notably Gammie+ [2003](https://doi.org/10.1086/374594). + +## Electron transport convergence tests + +* Hubble flow with energy source term `hubble` +* Noh shock heating `noh_shocks` + +Tests outlined in Ressler+ [2015](https://doi.org/10.1093/mnras/stv2084). + +## Regression tests + +* State at 1M after initialization vs restarting a problem `init_vs_restart` +* Stability stress test `bz_monopole` for polar boundary conditions, high-B operation +* Restart from mid-run of a MAD simulation `get_mad` Note that the BZ monopole test has 2 parts: a stability test running through to 100M, a test outputting state after a single step. Currently both are imaged in the same way, with the @@ -31,18 +43,13 @@ first two images showing initial condition and single-step state, and the rest s full 100M run at normal dump cadence. Plots for this test show the primitive radial velocity U1 since this in particular shows erratic behavior near the polar bound. -## Identity regression tests - -* Near-identical output of the same problem evolved with different block geometry -* MHD linear modes convergence using tiny (8x8x8) meshblocks -* State at 1M of a problem run from initialization, vs state at 1M of a problem initialized - from its first restart file +## Performance tests -These are basic regression tests in MPI operation, catching smaller differences which wouldn't -necessarily show up in conversion +* torus_scaling.par input with single block and 8 blocks, cycle=100 +* Same with orszag_tang, mhdmodes ## Testing wishlist -* Record `torus_scaling.par` stepwise performance at step=100, due to lower systematics - than early step average -* Linear modes in cylindrical and spherical coordinates, to test polar boundary effects efficiently +* Linear modes in cylindrical or spherical coordinates, to test polar boundary effects efficiently +* Driven turbulence in 2D, for testing electrons in more realistic scenarios e.g. with floors +* Test for unique random perturbations in all blocks diff --git a/tests/bondi/check.py b/tests/bondi/check.py index 8eb30382..898359ae 100644 --- a/tests/bondi/check.py +++ b/tests/bondi/check.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # Bondi problem convergence plots +# TODO could use the analytic solution here for extra rigor import os,sys import numpy as np @@ -11,8 +12,9 @@ RES = [int(x) for x in sys.argv[1].split(",")] LONG = sys.argv[2] SHORT = sys.argv[3] +VARS = ('RHO', 'UU', 'U1') -L1 = [] +L1 = {} # 2d for res in RES: @@ -20,46 +22,51 @@ end = pyharm.load_dump("bondi_2d_{}_end_{}.phdf".format(res, SHORT)) params = start.params - r = start['r'][:,start['n2']//2] - + # Start from at least outside the outer BL coord singularity + # Usually the test itself will start from r=3M and avoid this imin = 0 - while r[imin] < params['r_eh']: + while start['r1d'][imin] < (1 + np.sqrt(1 + start['a']**2) + 0.2): imin += 1 - r = r[imin:] - - rho0 = np.mean(start['RHO'][imin:,:], axis=1) - rho1 = np.mean(end['RHO'][imin:,:], axis=1) + for var in VARS: + if not var in L1: + L1[var] = [] - fig = plt.figure(figsize=(5,5)) - ax = fig.add_subplot(1,1,1) - ax.plot(r, rho0, label='Initial') - ax.plot(r, rho1, label='Final') - plt.xlabel('r'); plt.ylabel('rho') - plt.title("Bondi test stability, {}".format(LONG)) - plt.legend() - plt.savefig("bondi_compare_{}_{}.png".format(res, SHORT)) + var0 = np.mean(start[var][imin:,:,:], axis=1) + var1 = np.mean(end[var][imin:,:,:], axis=1) + L1[var].append(np.mean(np.fabs(var1 - var0))) - L1.append(np.mean(np.fabs(rho1 - rho0))) + if var == 'RHO': + r = start['r1d'][imin:] + fig = plt.figure(figsize=(5,5)) + plt.loglog(r, var0, label='Initial') + plt.loglog(r, var1, label='Final') + plt.xlabel('r'); plt.ylabel('rho') + plt.title("Bondi test stability, {}".format(LONG)) + plt.legend() + plt.savefig("bondi_compare_{}_{}.png".format(res, SHORT)) # MEASURE CONVERGENCE -L1 = np.array(L1) -powerfit = np.polyfit(np.log(RES), np.log(L1), 1)[0] -print("Powerfit: {} L1: {}".format(powerfit, L1)) - fail = 0 -if powerfit < -2.2 or powerfit > -1.9: - fail = 1 +for var in VARS: + L1[var] = np.array(L1[var]) + powerfit = np.polyfit(np.log(RES), np.log(L1[var]), 1)[0] + print("Powerfit: {} L1: {}".format(powerfit, L1[var])) + if powerfit < -2.2 or powerfit > -1.9: + fail = 1 # MAKE PLOTS fig = plt.figure(figsize=(5,5)) -ax = fig.add_subplot(1,1,1) -ax.plot(RES, L1, marker='s', label='RHO') -amp = L1[0]*RES[0]*RES[0] +for var in VARS: + plt.plot(RES, L1[var], marker='s', label=var) + +# Guideline at N^-2 +# Key the guideline from the middle point +amp = L1['RHO'][len(RES)//2]*RES[len(RES)//2]**2 xmin = RES[0]/2. xmax = RES[-1]*2. -ax.plot([xmin, xmax], amp*np.asarray([xmin, xmax])**-2., color='k', linestyle='--', label='N^-2') +plt.plot([xmin, xmax], amp*np.asarray([xmin, xmax])**-2., color='k', linestyle='--', label='N^-2') plt.xscale('log', base=2); plt.yscale('log') plt.xlim([RES[0]/np.sqrt(2.), RES[-1]*np.sqrt(2.)]) diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index ee540f35..ee929895 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -5,12 +5,13 @@ BASE=../.. exit_code=0 conv_2d() { - ALL_RES="32,48,64,96,128" - for res in 32 48 64 96 128 + ALL_RES="16,32,48,64" + for res in 16 32 48 64 do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/bondi.par parthenon/output0/dt=1000 debug/verbose=1 \ + $BASE/run.sh -i $BASE/pars/bondi.par debug/verbose=1 debug/flag_verbose=2 parthenon/time/tlim=50 \ + parthenon/output0/dt=1000 parthenon/output0/single_precision_output=false \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ $2 >log_${1}_${res}.txt 2>&1 @@ -27,19 +28,19 @@ conv_2d() { fi } -# Test coordinates (raw ks?) +# Test coordinates conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" conv_2d mks coordinates/transform=mks "in 2D, MKS coordinates" -# TODO fix this: converges at 2.3!! -#conv_2d eks coordinates/transform=eks "in 2D, EKS coordinates" +conv_2d eks coordinates/transform=eks "in 2D, EKS coordinates" +# TODO broken +#conv_2d ks coordinates/transform=null "in 2D, KS coordinates" # Recon conv_2d linear_mc GRMHD/reconstruction=linear_mc "in 2D, linear recon with MC limiter" conv_2d linear_vl GRMHD/reconstruction=linear_vl "in 2D, linear recon with VL limiter" # And the GRIM/classic driver -# TODO these crash, likely an implicit w/o B field thing -#conv_2d imex driver/type=imex "in 2D, with Imex driver" -#conv_2d imex_im "driver/type=imex GRMHD/implicit=true" "in 2D, semi-implicit stepping" +conv_2d imex driver/type=imex "in 2D, with Imex driver" +conv_2d imex_im "driver/type=imex GRMHD/implicit=true" "in 2D, semi-implicit stepping" exit $exit_code diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index 1f084926..69d6223d 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -5,59 +5,67 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import pyharm + if __name__=='__main__': - outputdir = os.getcwd() - kharmadir = '/data/bh29-home/vdhruv2/kharma' - RES = [int(r) for r in sys.argv[1].split(",")] - VISCOSITY = 1 - if VISCOSITY: - PRIMS = ['rho','u','dP'] - else: - PRIMS = ['rho','u'] - L1_norm = np.zeros([len(RES), len(PRIMS)]) + outputdir = './' + kharmadir = '../../' + + NVAR = 3 + VARS = ['rho', 'u', 'dP'] + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] + + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) for r, res in enumerate(RES): # load analytic result - if VISCOSITY: - rho_analytic, u_analytic, dP_analytic = np.loadtxt(os.path.join(kharmadir, \ - 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ - usecols=(0,1,3), unpack=True) - else: - rho_analytic, u_analytic, = np.loadtxt(os.path.join(kharmadir, \ - 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ - usecols=(0,1), unpack=True) + rho_analytic, uu_analytic, dP_analytic = np.loadtxt(os.path.join(kharmadir, \ + 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ + usecols=(0,1,3), unpack=True) # load code data - dfile = h5py.File('emhd_2d_{}_end.h5'.format(res), 'r') + dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - u = np.squeeze(dfile['prims'][Ellipsis,1][()]) - if VISCOSITY: - dP_tilde = np.squeeze(dfile['prims'][Ellipsis,9][()]) - + uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) + dP_tilde = np.squeeze(dfile['prims'][Ellipsis,9][()]) + t = dfile['t'][()] gam = dfile['header/gam'][()] higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') - # compute dP - if VISCOSITY: - if higher_order_terms=="TRUE": - tau = 30. - eta = 0.01 - P = (gam - 1.) * u - Theta = P / rho - nu_emhd = eta / rho - dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) - else: - dP = dP_tilde + # compute dP + if higher_order_terms=="TRUE": + print("Res: "+str(res)+"; higher order terms enabled") + tau = 30. + eta = 0.01 + P = (gam - 1.) * uu + Theta = P / rho + nu_emhd = eta / rho + dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) + else: + dP = dP_tilde # compute L1 norm - L1_norm[r,0] = np.mean(np.fabs(rho-rho_analytic[:,None])) - L1_norm[r,1] = np.mean(np.fabs(u-u_analytic[:,None])) - if VISCOSITY: - L1_norm[r,2] = np.mean(np.fabs(dP-dP_analytic[:,None])[1:-1]) + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(dP - dP_analytic[:,None])[1:-1]) + + # MEASURE CONVERGENCE + L1 = np.array(L1) + print(L1) + powerfits = [0.,]*NVAR + fail = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -2 or powerfits[k] < -2.7: + fail = 1 # plotting parameters @@ -81,14 +89,17 @@ # loop over prims tracker = 0 - for n in range(len(PRIMS)): - color = colors[tracker] - ax.loglog(RES, L1_norm[:,n], color=color, marker='o', label=PRIMS[n]) - tracker+=1 + for n in range(len(VARS)): + color = colors[tracker] + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) + tracker+=1 ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + # ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') plt.xscale('log', base=2) ax.set_xlabel('Resolution') ax.set_ylabel('L1 norm') ax.legend() - plt.savefig(os.path.join(outputdir, 'bondi_viscous_convergence.png'), dpi=300) + plt.savefig(os.path.join(outputdir, "bondi_viscous_convergence_"+SHORT+".png"), dpi=300) + + exit(fail) diff --git a/tests/bondi_viscous/check.sh b/tests/bondi_viscous/check.sh deleted file mode 100755 index 402306da..00000000 --- a/tests/bondi_viscous/check.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Run checks against analytic result for specified tests - -. /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - -RES2D="32,64,128,256" - -conda activate base - -fail=0 - -python3 check.py $RES2D "Bondi viscous" emhd2d || fail=1 - -exit $fail diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index 07e365f8..c87cfb6b 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -1,32 +1,38 @@ #!/bin/bash -#set -euo pipefail +set -euo pipefail -BASE=~/kharma +BASE=../.. + +exit_code=0 # Viscous bondi inflow convergence to exercise all terms in the evolution equation of dP conv_2d() { - for res in 32 64 128 256 - do - $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ - parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ - b_field/implicit=false - if [[ -d $res ]]; then - echo -e "Resolution directory exists. Clearing existing files in there and copying new files\n" - rm -r ${res} - else - mkdir $res - fi - . /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - conda activate pyharm - pyharm-convert --double *.phdf - conda deactivate - cp -r ./bondi_viscous.out0*.h5 $res - mv bondi_viscous.out0.00000.h5 emhd_2d_${res}_start.h5 - mv bondi_viscous.out0.final.h5 emhd_2d_${res}_end.h5 - rm -r ./bondi_viscous* - done + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" + do + half=$(( $res / 2 )) + $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ + parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ + parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + + mv bondi_viscous.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv bondi_viscous.out0.final.phdf emhd_2d_${res}_end_${1}.phdf + done + check_code=0 + pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + rm -r *.phdf *.xdmf *.out0* + if [[ $check_code != 0 ]]; then + echo Viscous Bondi test $3 FAIL: $check_code + exit_code=1 + else + echo Viscous Bondi test $3 success + fi } -conv_2d +ALL_RES="32,64,128,256" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Viscous Bondi in 2D, WENO5" + +exit $exit_code diff --git a/tests/bz_monopole/run.sh b/tests/bz_monopole/run.sh index 5757c79b..8d31c601 100755 --- a/tests/bz_monopole/run.sh +++ b/tests/bz_monopole/run.sh @@ -7,7 +7,7 @@ BASE=../.. $BASE/run.sh -i $BASE/pars/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 # At *least* check divB -pyharm-analysis print_divb bz_monopole.out0.final.phdf +pyharm-check-basics bz_monopole.out0.final.phdf # Take 1 step to look for early signs of non-fatal instabilities $BASE/run.sh -i $BASE/pars/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 diff --git a/tests/clean_tests.sh b/tests/clean_tests.sh index e0ba4a67..62249051 100755 --- a/tests/clean_tests.sh +++ b/tests/clean_tests.sh @@ -2,4 +2,4 @@ # Cleans all temporary/gitignore files from tests TEST_DIR=$(dirname "$(readlink -f "$0")") -rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* +rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index 05bd5f51..c73cd5e6 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -5,53 +5,68 @@ import matplotlib as mpl import matplotlib.pyplot as plt +import pyharm + if __name__=='__main__': - outputdir = os.getcwd() - kharmadir = '/home/vdhruv2/kharma' - RES = [int(r) for r in sys.argv[1].split(",")] - NG = 4 - CONDUCTION = 1 - if CONDUCTION: - PRIMS = ['rho','u','q'] - else: - PRIMS = ['rho','u'] - L1_norm = np.zeros([len(RES), len(PRIMS)]) + outputdir = './' + kharmadir = '../../' + + NVAR = 3 + VARS = ['rho', 'u', 'q'] + NG = 4 + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] + + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) for r, res in enumerate(RES): # load analytic result rho_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_rho.txt'))[NG:-NG] - u_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_u.txt'))[NG:-NG] - if CONDUCTION: - q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_phi.txt'))[NG:-NG] + uu_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_u.txt'))[NG:-NG] + q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_phi.txt'))[NG:-NG] # load code data - dfile = h5py.File('emhd_2d_{}_end.h5'.format(res), 'r') + dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - u = np.squeeze(dfile['prims'][Ellipsis,1][()]) - if CONDUCTION: - q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) + uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) + q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) t = dfile['t'][()] gam = dfile['header/gam'][()] + higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') # compute q - if CONDUCTION: + if higher_order_terms=="TRUE": + print("Res: "+str(res)+"; higher order terms enabled") tau = 10. kappa = 0.1 - P = (gam - 1.) * u + P = (gam - 1.) * uu Theta = P / rho - cs2 = (gam * P) / (rho + (gam * u)) chi_emhd = kappa / rho q = q_tilde * np.sqrt(chi_emhd * rho * Theta**2 / tau) + else: + q = q_tilde # compute L1 norm - L1_norm[r,0] = np.mean(np.fabs(rho-rho_analytic[:,None])) - L1_norm[r,1] = np.mean(np.fabs(u-u_analytic[:,None])) - if CONDUCTION: - L1_norm[r,2] = np.mean(np.fabs(q-q_analytic[:,None])[1:-1]) + # compute L1 norm + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(q - q_analytic[:,None])[1:-1]) + + # MEASURE CONVERGENCE + L1 = np.array(L1) + powerfits = [0.,]*NVAR + fail = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -1.6 or powerfits[k] < -2.2: + fail = 1 # plotting parameters @@ -75,9 +90,9 @@ # loop over prims tracker = 0 - for n in range(len(PRIMS)): + for n in range(len(VARS)): color = colors[tracker] - ax.loglog(RES, L1_norm[:,n], color=color, marker='o', label=PRIMS[n]) + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) tracker+=1 ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') @@ -86,4 +101,6 @@ ax.set_xlabel('Resolution') ax.set_ylabel('L1 norm') ax.legend() - plt.savefig(os.path.join(outputdir, 'conducting_atmosphere_convergence.png'), dpi=300) + plt.savefig(os.path.join(outputdir, "conducting_atmosphere_convergence_"+SHORT+".png"), dpi=300) + + exit(fail) diff --git a/tests/conducting_atmosphere/check.sh b/tests/conducting_atmosphere/check.sh deleted file mode 100755 index 00132ccf..00000000 --- a/tests/conducting_atmosphere/check.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Run checks against analytic result for specified tests - -. /home/vdhruv2/anaconda3/etc/profile.d/conda.sh -conda activate pyharm - -# Very small amplitude by default, preserve double precision -~/pyHARM/scripts/pyharm-convert --double *.phdf - -RES2D="64,128,256,512" - -conda activate base - -fail=0 - -python3 check.py $RES2D "Conducting atmosphere" emhd2d || fail=1 - -exit $fail diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 764086ae..47f30046 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -1,34 +1,40 @@ #!/bin/bash -#set -euo pipefail +# set -euo pipefail -BASE=~/kharma +BASE=../.. + +exit_code=0 # Extended MHD atmosphere test convergence to exercise geometrical terms # We'll use just 1 MPI rank to circumvent the somewhat annoying ODE initialization conv_2d() { - for res in 64 128 256 512 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do cp -r ${BASE}/kharma/prob/emhd/conducting_atmosphere_${res}_default/*txt ./ $BASE/run.sh -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 - if [[ -d $res ]]; then - echo -e "Resolution directory exists. Clearing existing files in there and copying new files\n" - rm ${res}/* - else - mkdir $res - fi - . /home/vdhruv2/anaconda3/etc/profile.d/conda.sh - conda activate pyharm - ~/pyHARM/scripts/pyharm-convert --double *.phdf - conda deactivate - cp -r ./conducting_atmosphere.out0*.h5 $res - mv conducting_atmosphere.out0.00000.h5 emhd_2d_${res}_start.h5 - mv conducting_atmosphere.out0.final.h5 emhd_2d_${res}_end.h5 - rm -r ./conducting_atmosphere* - rm ./atmosphere*.txt + parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + + mv conducting_atmosphere.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv conducting_atmosphere.out0.final.phdf emhd_2d_${res}_end_${1}.phdf done + check_code=0 + pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + rm -r *.phdf + rm -r *.xdmf + rm -r *.out0* + rm -r ./*.txt + if [[ $check_code != 0 ]]; then + echo Conducting atmosphere test $3 FAIL: $check_code + exit_code=1 + else + echo Conducting atmosphere test $3 success + fi } -conv_2d +ALL_RES="64,128,256,512" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Conducting atmosphere in 2D, WENO5" diff --git a/tests/hubble/make_plots.py b/tests/hubble/make_plots.py new file mode 100644 index 00000000..f0ab919e --- /dev/null +++ b/tests/hubble/make_plots.py @@ -0,0 +1,40 @@ + +import numpy as np +import h5py +import matplotlib.pyplot as plt + +f = h5py.File("hubble.out0.final.phdf", "r") + +gam = 5/3 +game = 4/3 + +rho0 = 1 +v0 = 1e-3 +ug0 = 1e-3 +fel0 = 1.0 +u0 = fel0 * ug0 +t = 1000 + +x = np.linspace(0.0, 1.0, 128) +kap = (gam - 2) * (game - 1) / (game - 2) * u0 / rho0**game * (1 + v0 * t)**(game - 2) +kap_dump = f['prims.Kel_Constant'][0,0,0,:,0] + +fig, ax = plt.subplots(2,2, figsize=(10,10)) +ax[0, 0].plot(x,f['prims.uvec'][0,0,0,:,0]) +ax[0, 0].plot(x, v0*x / (1 + v0 * t)) +ax[0, 0].set_title("vx") + +ax[0, 1].plot(x,f['prims.rho'][0,0,0,:,0]) +ax[0, 1].plot(x, rho0 / (1 + v0 * t) * np.ones_like(x)) +ax[0, 1].set_title("rho") + +ax[1, 0].plot(x,f['prims.u'][0,0,0,:,0]) +ax[1, 0].plot(x, ug0 / (1 + v0 * t)**2 * np.ones_like(x)) +ax[1, 0].set_title("u") + +kap = (gam - 2) * (game - 1) / (game - 2) * u0 / rho0**game * (1 + v0 * t)**(game - 2) +ax[1, 1].plot(x, f['prims.Kel_Constant'][0,0,0,:,0]) +ax[1, 1].plot(x, kap*np.ones_like(x)) +ax[1, 1].set_title("kappa_e") + +plt.savefig("hubble.png") \ No newline at end of file diff --git a/tests/mhdmodes/check.py b/tests/mhdmodes/check.py index 104aa1e4..5b7ec02d 100644 --- a/tests/mhdmodes/check.py +++ b/tests/mhdmodes/check.py @@ -16,6 +16,12 @@ DIM = sys.argv[4] else: DIM = "3d" +if len(sys.argv) > 5: + DIR = int(sys.argv[5]) +else: + DIR = 0 + +print(DIR) NVAR = 8 VARS = ['rho', 'u', 'u1', 'u2', 'u3', 'B1', 'B2', 'B3'] @@ -23,7 +29,7 @@ amp = 1.e-4 k1 = 2.*np.pi k2 = 2.*np.pi -if DIM == "3d": +if DIM == "3d" and DIR == 0: k3 = 2.*np.pi else: k3 = 0 @@ -41,7 +47,7 @@ # EIGENMODES: 3D dvar = np.zeros(NVAR) -if DIM == "3d": +if DIM == "3d" and DIR == 0: if "entropy" in SHORT: dvar[0] = 1. if "slow" in SHORT: @@ -95,8 +101,7 @@ # USE DUMPS IN FOLDERS OF GIVEN FORMAT for m, res in enumerate(RES): - #print(DIM, res, SHORT) - dump = pyharm.load_dump("mhd_{}_{}_end_{}.phdf".format(DIM, res, SHORT)) + dump = pyharm.load_dump("mhd_{}_{}_{}_end.phdf".format(DIM, SHORT, res)) X1 = dump['X1'] X2 = dump['X2'] @@ -125,7 +130,9 @@ for k in range(NVAR): if abs(dvar[k]) != 0.: powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + # These bounds were chosen heuristically: fast u2/u3 converge fast if powerfits[k] > -1.9 or ("entropy" not in SHORT and powerfits[k] < -2.1): # Allow entropy wave to converge fast, otherwise everything is ~2 fail = 1 @@ -150,6 +157,6 @@ plt.xlabel('N'); plt.ylabel('L1') plt.title("MHD mode test convergence, {}".format(LONG)) plt.legend(loc=1) -plt.savefig("convergence_modes_{}.png".format(SHORT)) +plt.savefig("convergence_modes_{}_{}.png".format(DIM,SHORT)) exit(fail) diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index 2dae5fb3..4c5ea46e 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -8,20 +8,21 @@ BASE=../.. exit_code=0 conv_3d() { - ALL_RES="16,24,32,48" - for res in 16 24 32 48 + ALL_RES="8,16,24,32,48,64" + for res in 8 16 24 32 48 64 do # Eight blocks half=$(( $res / 2 )) $BASE/run.sh -i $BASE/pars/mhdmodes.par debug/verbose=2 \ + parthenon/output0/single_precision_output=false parthenon/output0/dt=100. \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=$res \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=$half \ - $2 >log_${1}_${res}.txt 2>&1 - mv mhdmodes.out0.00000.phdf mhd_3d_${res}_start_${1}.phdf - mv mhdmodes.out0.final.phdf mhd_3d_${res}_end_${1}.phdf + $2 >log_3d_${1}_${res}.txt 2>&1 + mv mhdmodes.out0.00000.phdf mhd_3d_${1}_${res}_start.phdf + mv mhdmodes.out0.final.phdf mhd_3d_${1}_${res}_end.phdf done check_code=0 - python check.py $ALL_RES "$3" $1 || check_code=$? + python check.py $ALL_RES "$3" $1 3d 3 || check_code=$? if [[ $check_code != 0 ]]; then echo MHD modes test \"$3\" FAIL: $check_code exit_code=1 @@ -30,17 +31,18 @@ conv_3d() { fi } conv_2d() { - ALL_RES="32,64,128,256" - for res in 32 64 128 256 + ALL_RES="16,24,32,48,64,96,128,256,512" + for res in 16 24 32 48 64 96 128 256 512 do # Four blocks half=$(( $res / 2 )) $BASE/run.sh -i $BASE/pars/mhdmodes.par debug/verbose=1 mhdmodes/dir=3 \ + parthenon/output0/single_precision_output=false parthenon/output0/dt=100. \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=16 parthenon/meshblock/nx2=16 parthenon/meshblock/nx3=1 \ - $2 >log_${1}_${res}.txt 2>&1 - mv mhdmodes.out0.00000.phdf mhd_2d_${res}_start_${1}.phdf - mv mhdmodes.out0.final.phdf mhd_2d_${res}_end_${1}.phdf + parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ + $2 >log_2d_${1}_${res}.txt 2>&1 + mv mhdmodes.out0.00000.phdf mhd_2d_${1}_${res}_start.phdf + mv mhdmodes.out0.final.phdf mhd_2d_${1}_${res}_end.phdf done check_code=0 python check.py $ALL_RES "$3" $1 2d || check_code=$? @@ -51,41 +53,32 @@ conv_2d() { echo MHD modes test \"$3\" success fi } -conv_1d() { - ALL_RES="64 128 256 512" - for res in 64 128 256 512 - do - # Eight blocks - eighth=$(( $res / 8 )) - $BASE/run.sh -i $BASE/pars/mhdmodes.par debug/verbose=1 mhdmodes/dir=3 \ - parthenon/mesh/nx1=$res parthenon/mesh/nx2=1 parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$eighth parthenon/meshblock/nx2=1 parthenon/meshblock/nx3=1 \ - $2 >log_${1}_${res}.txt 2>&1 - mv mhdmodes.out0.00000.phdf mhd_1d_${res}_start_${1}.phdf - mv mhdmodes.out0.final.phdf mhd_1d_${res}_end_${1}.phdf - done -} + +#conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D" +conv_2d slow mhdmodes/nmode=1 "slow mode in 2D" +#conv_2d alfven mhdmodes/nmode=2 "Alfven mode in 2D" +conv_2d fast mhdmodes/nmode=3 "fast mode in 2D" # These 3 double as a demo of why WENO is great -conv_3d entropy mhdmodes/nmode=0 "entropy mode in 3D" -conv_3d entropy_mc "mhdmodes/nmode=0 GRMHD/reconstruction=linear_mc" "entropy mode in 3D, linear/MC reconstruction" -conv_3d entropy_vl "mhdmodes/nmode=0 GRMHD/reconstruction=linear_vl" "entropy mode in 3D, linear/VL reconstruction" +#conv_3d entropy mhdmodes/nmode=0 "entropy mode in 3D" +#conv_3d entropy_mc "mhdmodes/nmode=0 GRMHD/reconstruction=linear_mc" "entropy mode in 3D, linear/MC reconstruction" +#conv_3d entropy_vl "mhdmodes/nmode=0 GRMHD/reconstruction=linear_vl" "entropy mode in 3D, linear/VL reconstruction" # Other modes don't benefit, exercise WENO most since we use it -conv_3d slow mhdmodes/nmode=1 "slow mode in 3D" -conv_3d alfven mhdmodes/nmode=2 "Alfven mode in 3D" -conv_3d fast mhdmodes/nmode=3 "fast mode in 3D" +#conv_3d slow "mhdmodes/nmode=1 mhdmodes/dir=3" "slow mode in 3D" +#conv_3d alfven "mhdmodes/nmode=2 mhdmodes/dir=3" "Alfven mode in 3D" +#conv_3d fast "mhdmodes/nmode=3 mhdmodes/dir=3" "fast mode in 3D" # And we've got to test classic/GRIM stepping -conv_3d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 3D, ImEx explicit" -conv_3d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 3D, ImEx explicit" -conv_3d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 3D, ImEx explicit" +#conv_3d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 3D, ImEx explicit" +#conv_3d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 3D, ImEx explicit" +#conv_3d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 3D, ImEx explicit" # B field totally explicit -conv_3d slow_imex_semi "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true" "slow mode 3D, ImEx semi-implicit" -conv_3d alfven_imex_semi "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true" "Alfven mode 3D, ImEx semi-implicit" -conv_3d fast_imex_semi "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true" "fast mode 3D, ImEx semi-implicit" +#conv_3d slow_imex_semi "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "slow mode 3D, ImEx semi-implicit" +#conv_3d alfven_imex_semi "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "Alfven mode 3D, ImEx semi-implicit" +#conv_3d fast_imex_semi "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "fast mode 3D, ImEx semi-implicit" # All variables semi-implicit -conv_3d slow_imex_im "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=true implicit/use_qr=false" "slow mode 3D, ImEx implicit" -conv_3d alfven_imex_im "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=true implicit/use_qr=false" "Alfven mode 3D, ImEx implicit" -conv_3d fast_imex_im "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=true implicit/use_qr=false" "fast mode 3D, ImEx implicit" +#conv_3d slow_imex_im "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "slow mode 3D, ImEx implicit" +#conv_3d alfven_imex_im "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "Alfven mode 3D, ImEx implicit" +#conv_3d fast_imex_im "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "fast mode 3D, ImEx implicit" # 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 # Currently very slow, plus modes are incorrect diff --git a/tests/noh/check.py b/tests/noh/check.py index 226048d1..bf2021d9 100644 --- a/tests/noh/check.py +++ b/tests/noh/check.py @@ -10,6 +10,7 @@ resolutions = sys.argv[3].split(',') for r, resolution in enumerate(resolutions): resolutions[r] = int(resolution) + resolutions = np.array(resolutions) gamma_e = float(sys.argv[4]) l1_norm = [] @@ -33,7 +34,9 @@ x1[i] = startx1 + i*dx1 u_e = (kel * rho**gam_e)/(gam_e - 1.) - ratio_analytical = np.where(rho != 1., fel/2. * (((gam + 1.)/(gam - 1.))**gam_e * (1. - gam/gam_e) + 1. + gam/gam_e) * ((gam**2 - 1.)/(gam_e**2 - 1.)), 0.) + ratio_analytical = np.where(rho > 1.5, \ + fel/2. * (((gam + 1.)/(gam - 1.))**gam_e * (1. - gam/gam_e) + 1. + gam/gam_e) * ((gam**2 - 1.)/(gam_e**2 - 1.)), \ + 0.) plt.figure(figsize=(6,6)) plt.plot(x1, u_e/uu, label="Computed") @@ -43,7 +46,15 @@ l1_norm.append(np.mean(abs(u_e/uu - ratio_analytical))) - print(resolutions, l1_norm) + l1_norm = np.array(l1_norm) + powerfit = np.polyfit(np.log(resolutions), np.log(l1_norm), 1)[0] + print("Power fit: {} {}".format(powerfit, l1_norm)) + # These bounds were chosen heuristically + if powerfit < -1.9 and powerfit > -2.1: + fail = 0 + else: + fail = 1 + # plot fig, ax = plt.subplots(1,1,figsize=(8,8)) ax.plot(resolutions, l1_norm, color='darkblue', marker='^', markersize=8, label='$\\gamma_{{e}}$={:.2f}'.format(gamma_e)) @@ -56,3 +67,5 @@ plt.legend() plt.savefig(os.path.join(plotsdir, 'noh_convergence_{:.2f}.png'.format(gamma_e)), dpi=200) plt.close() + + exit(fail) diff --git a/tests/noh/run.sh b/tests/noh/run.sh index 881a5d3e..6e10b03b 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -8,8 +8,8 @@ KHARMADIR=../.. exit_code=0 noh_test() { - ALL_RES="64,128,256,512,1024,2048,4096" - for res in 64 128 256 512 1024 2048 4096 + ALL_RES="64,128,256,512,1024,2048" + for res in 64 128 256 512 1024 2048 do eighth=$(($res / 8)) $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par parthenon/output0/dt=1000 debug/verbose=1 \ @@ -32,4 +32,4 @@ noh_test() { noh_test -exit $exit_code \ No newline at end of file +exit $exit_code diff --git a/tests/regrid/orszag_tang_with_restarts.par b/tests/regrid/orszag_tang_with_restarts.par new file mode 100644 index 00000000..c732e718 --- /dev/null +++ b/tests/regrid/orszag_tang_with_restarts.par @@ -0,0 +1,67 @@ +# Orszag-Tang Vortex problem: +# Generate current sheets on short timescales +# Adds a restart file output at 50 time units +# Also uses ImEx driver, so that the restart +# file contains all the primitive variables. +# Also omits history file + + +problem_id = orszag_tang + + +refinement = none +numlevel = 1 + +nx1 = 256 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 256 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 256 +nx2 = 128 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 100.0 +integrator = rk2 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +type = imex + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + + +file_type = hdf5 +dt = 1000.0 # Only output final dump +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon + + +file_type = rst +dt = 10.0 diff --git a/tests/regrid/regrid_orszag_tang.par b/tests/regrid/regrid_orszag_tang.par new file mode 100644 index 00000000..3ac4870a --- /dev/null +++ b/tests/regrid/regrid_orszag_tang.par @@ -0,0 +1,53 @@ +# Regrid an OT vortex, keeping all properties but the block size + + +problem_id = resize_restart + + +# Set by restart file + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 100 +integrator = rk2 + + +cfl = 0.9 + + +type = imex + + +fname = orszag_tang.out1.00005.h5 +use_tf = true +use_dt = false # TODO this is borked somehow +skip_b_cleanup = true +regrid_only = true + + +disable_floors = true + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + +# Have to compare last output file + +file_type = hdf5 +dt = 1000.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + +# Don't check the restart if the last dump matches +# +#file_type = rst +#dt = 1000.0 diff --git a/tests/regrid/resize_orszag_tang.par b/tests/regrid/resize_orszag_tang.par new file mode 100644 index 00000000..467090f4 --- /dev/null +++ b/tests/regrid/resize_orszag_tang.par @@ -0,0 +1,73 @@ +# Resize an OT vortex, keeping most properties + + +problem_id = resize_restart + + +refinement = none +numlevel = 1 + +nx1 = 512 +x1min = -3.141592653589793 +x1max = 3.141592653589793 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 512 +x2min = -3.141592653589793 +x2max = 3.141592653589793 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 512 +nx2 = 256 +nx3 = 1 + + +base = cartesian_minkowski +transform = none + + +tlim = 100 +integrator = rk2 + + +cfl = 0.9 + + +type = imex + + +fname = orszag_tang.out1.00009.h5 +use_tf = false +use_dt = false +skip_b_cleanup = false + + +rel_tolerance = 1.e-11 + + +disable_floors = true + + +verbose = 0 +flag_verbose = 0 +extra_checks = 0 + +# +#file_type = hdf5 +#dt = 1000.0 +#single_precision_output = true +#variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag + +# We only need to check the last restart file, specifically divB + +file_type = rst +dt = 1000.0 diff --git a/tests/regrid/run.sh b/tests/regrid/run.sh new file mode 100755 index 00000000..4748290d --- /dev/null +++ b/tests/regrid/run.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Bash script testing a fresh Orszag-Tang vortex vs a version +# re-gridded to 64^2 tiles in the middle of the run, +# and then a version resized to twice the resolution + +# TODO the first comparison should really be binary-identical + +exit_code=0 + +# Set paths +KHARMADIR=../.. + +$KHARMADIR/run.sh -i ./orszag_tang_with_restarts.par >log_orig.txt 2>&1 + +mv orszag_tang.out0.final.phdf orszag_tang.out0.final.orig.phdf + +sleep 1 + +pyharm-convert --to_restart orszag_tang.out1.00005.rhdf orszag_tang.out1.00009.rhdf + +sleep 1 + +$KHARMADIR/run.sh -i ./regrid_orszag_tang.par >log_regrid.txt 2>&1 + +mv resize_restart.out0.final.phdf resize_restart.out0.final.regrid.phdf + +# compare.py allows for small (5e-10) difference +check_code=0 +pyharm-diff orszag_tang.out0.final.orig.phdf resize_restart.out0.final.regrid.phdf -o compare_regrid --rel_tol=0.002 || check_code=$? +if [[ $check_code != 0 ]]; then + echo Regrid test FAIL: $check_code + exit_code=1 +else + echo Regrid test success +fi + +# Finally, test that we can sanely resize the dump, too +# This won't output .phdf files, only restarts (.rhdf) +$KHARMADIR/run.sh -i ./resize_orszag_tang.par >log_resize.txt 2>&1 + +# Check the final .rhdf file for sanity (i.e., divB small) +check_code=0 +pyharm-check-basics resize_restart.out1.final.rhdf || check_code=$? +if [[ $check_code != 0 ]]; then + echo Resize test FAIL: $check_code + exit_code=1 +else + echo Resize test success +fi + +exit $exit_code From 4cd816d00023cdc32a3f231ea0180c208a16002e Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 28 Feb 2023 10:46:16 -0500 Subject: [PATCH 045/219] Forward-port GIZMO initialization --- kharma/coordinates/coordinate_embedding.hpp | 10 ++ kharma/coordinates/coordinate_systems.hpp | 50 +++++- kharma/coordinates/gr_coordinates.cpp | 6 +- kharma/prob/bondi.cpp | 53 ++++-- kharma/prob/bondi.hpp | 2 + kharma/prob/fm_torus.cpp | 19 ++- kharma/prob/gizmo.cpp | 177 ++++++++++++++++++++ kharma/prob/gizmo.hpp | 151 +++++++++++++++++ kharma/prob/prob_common.hpp | 23 +++ kharma/prob/problem.cpp | 4 +- kharma/prob/resize_restart_kharma.cpp | 4 +- kharma/prob/resize_restart_kharma.hpp | 27 +-- 12 files changed, 468 insertions(+), 58 deletions(-) create mode 100644 kharma/prob/gizmo.cpp create mode 100644 kharma/prob/gizmo.hpp diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index 22d8118c..9a716a5b 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -145,6 +145,16 @@ class CoordinateEmbedding { return 0.0; //throw std::invalid_argument("BH Spin is not defined for selected coordinate system!"); } } + KOKKOS_INLINE_FUNCTION bool is_ext_g() const + { + if (mpark::holds_alternative(base)) { + return mpark::get(base).ext_g; + } else if (mpark::holds_alternative(base)) { + return mpark::get(base).ext_g; + } else { + return 0.0; //throw std::invalid_argument("Ext_g is not defined for selected coordinate system!"); + } + } KOKKOS_INLINE_FUNCTION bool is_ks() const { if (mpark::holds_alternative(base)) { diff --git a/kharma/coordinates/coordinate_systems.hpp b/kharma/coordinates/coordinate_systems.hpp index 6030228e..a7ccae18 100644 --- a/kharma/coordinates/coordinate_systems.hpp +++ b/kharma/coordinates/coordinate_systems.hpp @@ -111,8 +111,9 @@ class SphKSCoords { // BH Spin is a property of KS const GReal a; const bool spherical = true; + const bool ext_g; // added by Hyerin (02/27/23) - KOKKOS_FUNCTION SphKSCoords(GReal spin): a(spin) {}; + KOKKOS_FUNCTION SphKSCoords(GReal spin, bool external_gravity): a(spin), ext_g(external_gravity) {}; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { @@ -122,6 +123,11 @@ class SphKSCoords { const GReal cos2 = m::pow(cos(th), 2); const GReal sin2 = m::pow(sin(th), 2); const GReal rho2 = r*r + a*a*cos2; + + // (Hyerin 11/13/22) test + const GReal A = 1.46797639*m::pow(10.,-8); + const GReal B = 1.29411117; + const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); gcov[0][0] = -1. + 2.*r/rho2; gcov[0][1] = 2.*r/rho2; @@ -142,6 +148,15 @@ class SphKSCoords { gcov[3][1] = -a*sin2*(1. + 2.*r/rho2); gcov[3][2] = 0.; gcov[3][3] = sin2*(rho2 + a*a*sin2*(1. + 2.*r/rho2)); + + // Hyerin TODO: add an error when spin != 0 + if (ext_g) { + if (a>0) printf("WARNING: External gravity is not compatible with nonzero spin! \n"); + gcov[0][0] -= 2. * Phi_g; + gcov[0][1] -= 2. * Phi_g; + gcov[1][0] -= 2. * Phi_g; + gcov[1][1] -= 2. * Phi_g; + } } // For converting from BL @@ -154,6 +169,15 @@ class SphKSCoords { trans[0][1] = 2.*r/(r*r - 2.*r + a*a); trans[3][1] = a/(r*r - 2.*r + a*a); + // external gravity from GIZMO + const GReal A = 1.46797639*m::pow(10.,-8); + const GReal B = 1.29411117; + const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); + + if (ext_g) { + trans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); + } + gzero(vcon); DLOOP2 vcon[mu] += trans[mu][nu]*vcon_bl[nu]; } @@ -165,6 +189,15 @@ class SphKSCoords { DLOOP2 rtrans[mu][nu] = (mu == nu); rtrans[0][1] = 2.*r/(r*r - 2.*r + a*a); rtrans[3][1] = a/(r*r - 2.*r + a*a); + + // external gravity from GIZMO + const GReal A = 1.46797639*m::pow(10.,-8); + const GReal B = 1.29411117; + const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); + + if (ext_g) { + rtrans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); + } invert(&rtrans[0][0], &trans[0][0]); gzero(vcon); @@ -186,8 +219,9 @@ class SphBLCoords { // BH Spin is a property of BL const GReal a; const bool spherical = true; + const bool ext_g; // added by Hyerin (11/13/22) - KOKKOS_FUNCTION SphBLCoords(GReal spin): a(spin) {} + KOKKOS_FUNCTION SphBLCoords(GReal spin, bool external_gravity): a(spin), ext_g(external_gravity) {} KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { @@ -201,6 +235,11 @@ class SphBLCoords { // TODO this and gcov_embed for KS should look more similar... const GReal mmu = 1. + a2*cth*cth/r2; // mu is taken as an index + // (Hyerin 11/13/22) test + const GReal A = 1.46797639*m::pow(10.,-8); + const GReal B = 1.29411117; + const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); + gzero2(gcov); gcov[0][0] = -(1. - 2./(r*mmu)); gcov[0][3] = -2.*a*s2/(r*mmu); @@ -208,6 +247,13 @@ class SphBLCoords { gcov[2][2] = r2*mmu; gcov[3][0] = -2.*a*s2/(r*mmu); gcov[3][3] = s2*(r2 + a2 + 2.*a2*s2/(r*mmu)); + + // Hyerin TODO: add an error when spin != 0 + if (ext_g) { + if (a>0) printf("WARNING: External gravity is not compatible with nonzero spin! \n"); + gcov[0][0] -= 2. * Phi_g; + gcov[1][1] *= (1. - 2./r + a2/r2) / (1. - 2./r + 2.*Phi_g); + } } // TODO vec to/from ks, put guaranteed ks/bl fns into embedding diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index 7a0f45d9..1859c28e 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -82,10 +82,12 @@ GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): Uniform base.emplace(CartMinkowskiCoords()); } else if (base_str == "spherical_ks" || base_str == "ks") { GReal a = pin->GetReal("coordinates", "a"); - base.emplace(SphKSCoords(a)); + bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); //added by Hyerin + base.emplace(SphKSCoords(a, ext_g)); } else if (base_str == "spherical_bl" || base_str == "bl") { GReal a = pin->GetReal("coordinates", "a"); - base.emplace(SphBLCoords(a)); + bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); //added by Hyerin + base.emplace(SphBLCoords(a, ext_g)); } else { throw std::invalid_argument("Unsupported base coordinates!"); } diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 3319120d..93e7c4ea 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -38,8 +38,7 @@ #include "flux_functions.hpp" /** - * Initialization of a Bondi problem with specified sonic point, BH mdot, and horizon radius - * TODO mdot and rs are redundant and should be merged into one parameter. Uh, no. + * Initialization of a Bondi problem with specified sonic point & accretion rate */ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterInput *pin) { @@ -56,6 +55,8 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // TODO take r_shell const Real rin_bondi = pin->GetOrAddReal("bondi", "r_in", rin_bondi_default); + const bool fill_interior = pin->GetOrAddBoolean("bondi", "fill_interior", false); + const bool zero_velocity = pin->GetOrAddBoolean("bondi", "zero_velocity", false); // Add these to package properties, since they continue to be needed on boundaries // TODO Problems need params @@ -65,6 +66,10 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn pmb->packages.Get("GRMHD")->AddParam("rs", rs); if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("rin_bondi")) pmb->packages.Get("GRMHD")->AddParam("rin_bondi", rin_bondi); + if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("fill_interior_bondi")) + pmb->packages.Get("GRMHD")->AddParam("fill_interior_bondi", fill_interior); + if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("zero_velocity_bondi")) + pmb->packages.Get("GRMHD")->AddParam("zero_velocity_bondi", zero_velocity); // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! @@ -80,7 +85,7 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // This tests that PostInitialize will correctly fill ghost zones with the boundary we set SetBondi(rc, IndexDomain::interior); - if (rin_bondi > pin->GetReal("coordinates", "r_in")) { + if (rin_bondi > pin->GetReal("coordinates", "r_in") && !(fill_interior)) { // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable. Floors::ApplyInitialFloors(rc.get(), IndexDomain::interior); @@ -106,13 +111,15 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); const Real rin_bondi = pmb->packages.Get("GRMHD")->Param("rin_bondi"); + const bool fill_interior = pmb->packages.Get("GRMHD")->Param("fill_interior_bondi"); + const bool zero_velocity = pmb->packages.Get("GRMHD")->Param("zero_velocity_bondi"); const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); // Just the X1 right boundary GRCoordinates G = pmb->coords; SphKSCoords ks = mpark::get(G.coords.base); - SphBLCoords bl = SphBLCoords(ks.a); + SphBLCoords bl = SphBLCoords(ks.a, ks.ext_g); // modified CoordinateEmbedding cs = G.coords; // Solution constants @@ -133,22 +140,38 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain const IndexRange ib = bounds.GetBoundsI(domain); const IndexRange jb = bounds.GetBoundsJ(domain); const IndexRange kb = bounds.GetBoundsK(domain); + pmb->par_for("bondi_boundary", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal Xnative[GR_DIM], Xembed[GR_DIM]; G.coord(k, j, i, Loci::center, Xnative); G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1]; - // Unless we're doing a Schwarzchild problem & comparing solutions, - // be a little cautious about initializing the Ergosphere zones - if (r < rin_bondi) return; + + // Either fill the interior region with the innermost analytically computed value, + // or let it be filled with floor values later + if (r < rin_bondi) { + if (fill_interior) { + // values at infinity; would need modifications below + /* + Real Tinf = (m::sqrt(C2) - 1.) / (n + 1); // temperature at infinity + rho = m::pow(Tinf,n); + u = rho * Tinf * n; + */ + // just match at the rin_bondi value + r = rin_bondi; + } else { + return; + } + } const Real T = get_T(r, C1, C2, n, rs); const Real Tn = m::pow(T, n); - const Real ur = -C1 / (Tn * r * r); const Real rho = Tn / Kn; const Real u = rho * T * n; + const Real ur = (zero_velocity) ? 0. : -C1 / (Tn * r * r); + // Set u^t to make u^r a 4-vector Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; Real gcov_bl[GR_DIM][GR_DIM]; @@ -165,13 +188,13 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain G.gcon(Loci::center, j, i, gcon); fourvel_to_prim(gcon, ucon_mks, u_prim); - // This used to have NaN guards. No point, as for optimized builds they are ignored (!) - // Now we just avoid initializing near the EH - P(m_p.RHO, k, j, i) = rho; - P(m_p.UU, k, j, i) = u; - P(m_p.U1, k, j, i) = u_prim[0]; - P(m_p.U2, k, j, i) = u_prim[1]; - P(m_p.U3, k, j, i) = u_prim[2]; + // Note that NaN guards, including these, are ignored (!) under -ffast-math flag. + // Thus we stay away from initializing at EH where this could happen + if(!isnan(rho)) P(m_p.RHO, k, j, i) = rho; + if(!isnan(u)) P(m_p.UU, k, j, i) = u; + if(!isnan(u_prim[0])) P(m_p.U1, k, j, i) = u_prim[0]; + if(!isnan(u_prim[1])) P(m_p.U2, k, j, i) = u_prim[1]; + if(!isnan(u_prim[2])) P(m_p.U3, k, j, i) = u_prim[2]; } ); diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index e8975e6c..443c281c 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -68,6 +68,7 @@ KOKKOS_INLINE_FUNCTION Real get_Tfunc(const Real T, const GReal r, const Real C1 const Real B = C1 / (r * r * m::pow(T, n)); return A * A * (1. - 2. / r + B * B) - C2; } + KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, const Real n, const Real rs) { Real rtol = 1.e-12; @@ -86,6 +87,7 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c f0 = get_Tfunc(T0, r, C1, C2, n); T1 = Tmax; f1 = get_Tfunc(T1, r, C1, C2, n); + // TODO(BSP) find a way to throw/communicate this //if (f0 * f1 > 0) throw std::runtime_error("Cannot solve temperature!"); Th = (T0 + T1) / 2.; // a simple bisection method which is stable and fast diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index 3df6c52a..5d845b85 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -67,15 +67,16 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); // Get coordinate systems - // Different coordinate systems do not inherit from a base - // class (see coordinate_systems.hpp, coordinate_embedding.hpp) - // so we can't cast or assign them like you'd expect. - // Instead we just create copies of each one we'll need. - const auto& G = pmb->coords; - const bool use_ks = G.coords.is_ks(); - const GReal a = G.coords.get_a(); - const SphBLCoords blcoords = SphBLCoords(a); - const SphKSCoords kscoords = SphKSCoords(a); + // G clearly holds a reference to an existing system G.coords.base, + // but we don't know if it's KS or BL coordinates + // Since we can't create a system and assign later, we just + // rebuild copies of both based on the BH spin "a" + const auto& G = pmb->coords; + const bool use_ks = G.coords.is_ks(); + const GReal a = G.coords.get_a(); + const bool ext_g = G.coords.is_ext_g(); + const SphBLCoords blcoords = SphBLCoords(a, ext_g); + const SphKSCoords kscoords = SphKSCoords(a, ext_g); // Fishbone-Moncrief parameters Real l = lfish_calc(a, rmax); diff --git a/kharma/prob/gizmo.cpp b/kharma/prob/gizmo.cpp new file mode 100644 index 00000000..3796acd8 --- /dev/null +++ b/kharma/prob/gizmo.cpp @@ -0,0 +1,177 @@ +/* + * File: bondi.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "gizmo.hpp" + +#include "floors.hpp" +#include "flux_functions.hpp" + +/** + * Initialization of domain from output of cosmological simulation code GIZMO + * Note this requires + */ +TaskStatus InitializeGIZMO(std::shared_ptr>& rc, ParameterInput *pin) +{ + Flag(rc, "Initializing GIZMO problem"); + auto pmb = rc->GetBlockPointer(); + + const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); + const Real rs = pin->GetOrAddReal("bondi", "rs", 8.0); + + // Set the innermost radius to apply the initialization + const Real a = pin->GetReal("coordinates", "a"); + const Real rin_default = 1 + m::sqrt(1 - a*a) + 0.1; + const Real rin_init = pin->GetOrAddReal("gizmo", "r_in", rin_default); + + auto datfn = pin->GetOrAddString("gizmo", "datfn", "none"); + + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("gizmo_dat"))) + pmb->packages.Get("GRMHD")->AddParam("gizmo_dat", datfn); + if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rin_init"))) + pmb->packages.Get("GRMHD")->AddParam("rin_init", rin_init); + + // Set the interior domain to the analytic solution to begin + // This tests that PostInitialize will correctly fill ghost zones with the boundary we set + SetGIZMO(rc, IndexDomain::interior); + + Flag(rc, "Initialized"); + return TaskStatus::complete; +} + +TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Setting zones from GIZMO output"); + auto pmb = rc->GetBlockPointer(); + + //std::cerr << "GIZMO on domain: " << BoundaryName(domain) << std::endl; + // Don't apply GIZMO initialization to X1 boundaries + if (domain == IndexDomain::outer_x1 || domain == IndexDomain::inner_x1) { + return; + } + + PackIndexMap prims_map, cons_map; + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map); + auto U = GRMHD::PackMHDCons(rc.get(), cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); + const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + auto datfn = pmb->packages.Get("GRMHD")->Param("gizmo_dat"); + auto rin_init = pmb->packages.Get("GRMHD")->Param("rin_init"); + + // Just the X1 right boundary + GRCoordinates G = pmb->coords; + SphKSCoords ks = mpark::get(G.coords.base); + SphBLCoords bl = SphBLCoords(ks.a, ks.ext_g); // modified + CoordinateEmbedding cs = G.coords; + + // Solution constants + // These don't depend on which zone we're calculating + const Real n = 1. / (gam - 1.); + const Real uc = m::sqrt(1. / (2. * rs)); + const Real Vc = m::sqrt(uc * uc / (1. - 3. * uc * uc)); + const Real Tc = -n * Vc * Vc / ((n + 1.) * (n * Vc * Vc - 1.)); + const Real C1 = uc * rs * rs * m::pow(Tc, n); + const Real A = 1. + (1. + n) * Tc; + const Real C2 = A * A * (1. - 2. / rs + uc * uc); + const Real K = m::pow(4 * M_PI * C1 / mdot, 1/n); + const Real Kn = m::pow(K, n); + + // Set the Bondi conditions wherever we're asked + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + + // GIZMO shell + // Read the gizmo data file + FILE *fptr = fopen(datfn.c_str(),"r"); + const int datlen = 100000; + Real *rarr = new double[datlen]; + Real *rhoarr = new double[datlen]; + Real *Tarr = new double[datlen]; + Real *vrarr = new double[datlen]; + Real *Mencarr = new double[datlen]; + int length=0, itemp=0; + while (fscanf(fptr,"%lf %lf %lf %lf %lf\n", &(rarr[itemp]), &(rhoarr[itemp]), &(Tarr[itemp]), &(vrarr[itemp]), &(Mencarr[itemp])) == 5) { // assign the read value to variable, and enter it in array + itemp++; + } + fclose(fptr); + length = itemp; + + GridVector r_device("r_device", length); + GridVector rho_device("rho_device", length); + GridVector T_device("T_device", length); + GridVector vr_device("vr_device", length); + auto r_host = r_device.GetHostMirror(); + auto rho_host = rho_device.GetHostMirror(); + auto T_host = T_device.GetHostMirror(); + auto vr_host = vr_device.GetHostMirror(); + for (itemp = 0; itemp < length; itemp++) { + r_host(itemp) = rarr[itemp]; + rho_host(itemp) = rhoarr[itemp]; + T_host(itemp) = Tarr[itemp]; + vr_host(itemp) = vrarr[itemp]; + } + r_device.DeepCopy(r_host); + rho_device.DeepCopy(rho_host); + T_device.DeepCopy(T_host); + vr_device.DeepCopy(vr_host); + + Kokkos::fence(); + + pmb->par_for("gizmo_shell", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // same vacuum conditions at rin_init + GReal Xshell[GR_DIM] = {0, rin_init, 0, 0}; + int i_sh; + GReal del_sh; + XtoindexGIZMO(Xshell, r_device, length, i_sh, del_sh); + Real vacuum_rho, vacuum_u_over_rho, vacuum_logrho, vacuum_log_u_over_rho; + vacuum_rho = rho_device(i_sh)*(1.-del_sh)+rho_device(i_sh+1)*del_sh; + vacuum_u_over_rho = (T_device(i_sh)*(1.-del_sh)+T_device(i_sh+1)*del_sh)/(gam-1.); + + get_prim_gizmo_shell(G, cs, P, m_p, gam, bl, ks, rin_init, rs, vacuum_rho, vacuum_u_over_rho, + r_device, rho_device, T_device, vr_device, length, k, j, i); + } + ); + + Flag(rc, "Set"); + return TaskStatus::complete; +} diff --git a/kharma/prob/gizmo.hpp b/kharma/prob/gizmo.hpp new file mode 100644 index 00000000..f51a7739 --- /dev/null +++ b/kharma/prob/gizmo.hpp @@ -0,0 +1,151 @@ +/* + * File: bondi.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include "bondi.hpp" +#include "gr_coordinates.hpp" +#include "flux_functions.hpp" +#include "grmhd_functions.hpp" +#include "pack.hpp" +#include "prob_common.hpp" +#include "types.hpp" + +#include + +/** + * Initialize a Bondi problem over the domain + */ +TaskStatus InitializeGIZMO(std::shared_ptr>& rc, ParameterInput *pin); + +/** + * Set all values on a given domain to the Bondi inflow analytic steady-state solution + * + * Used for initialization and boundary conditions + */ +TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain, bool coarse=false); + +KOKKOS_INLINE_FUNCTION void XtoindexGIZMO(const GReal XG[GR_DIM], + const GridScalar& rarr, const int length, int& i, GReal& del) +{ + Real dx2, dx2_min; + dx2_min = m::pow(XG[1]-rarr(0),2); //100000.; //arbitrarily large number + + i = 0; // initialize + + for (int itemp = 0; itemp < length; itemp++) { + if (rarr(itemp) < XG[1]) { // only look for smaller side + dx2 = m::pow(XG[1] - rarr(itemp), 2); + + // simplest interpolation (Hyerin 07/26/22) + if (dx2 < dx2_min){ + dx2_min = dx2; + i = itemp; + } + } + } + + // interpolation (11/14/2022) TODO: write a case where indices hit the boundaries of the data file + del = (XG[1]-rarr(i))/(rarr(i+1)-rarr(i)); +} +/** + * Get the GIZMO output values at a particular zone + * Note this assumes that there are ghost zones! + */ +KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, + const Real& gam, const SphBLCoords& bl, const SphKSCoords& ks, + const Real rin_init, const Real rs, Real vacuum_rho, Real vacuum_u_over_rho, + const GridScalar& rarr, const GridScalar& rhoarr, const GridScalar& Tarr, const GridScalar& vrarr, const int length, + const int& k, const int& j, const int& i) +{ + // Solution constants for velocity prescriptions + // Ideally these could be cached but preformance isn't an issue here + Real mdot = 1.; // mdot and rs defined arbitrarily + Real n = 1. / (gam - 1.); + Real uc = sqrt(mdot / (2. * rs)); + Real Vc = -sqrt(pow(uc, 2) / (1. - 3. * pow(uc, 2))); + Real Tc = -n * pow(Vc, 2) / ((n + 1.) * (n * pow(Vc, 2) - 1.)); + Real C1 = uc * pow(rs, 2) * pow(Tc, n); + Real C2 = pow(1. + (1. + n) * Tc, 2) * (1. - 2. * mdot / rs + pow(C1, 2) / (pow(rs, 4) * pow(Tc, 2 * n))); + + //Real rs = 1./sqrt(T); //1000.; + GReal Xnative[GR_DIM], Xembed[GR_DIM]; + G.coord(k, j, i, Loci::center, Xnative); + G.coord_embed(k, j, i, Loci::center, Xembed); + GReal r = Xembed[1]; + + // Use Bondi infall velocity + Real rho, u; + Real T = get_T(r, C1, C2, n, rs); + Real ur = -C1 / (pow(T, n) * pow(r, 2)); + Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; + if (r < rin_init * 0.9){ + // Vacuum values for interior + rho = vacuum_rho; + u = vacuum_rho * vacuum_u_over_rho; + ucon_bl[1] = ur; + } else { + // linear interpolation + int itemp; GReal del; + XtoindexGIZMO(Xembed, rarr, length, itemp, del); + if (del < 0 ) { // when r is smaller than GIZMO's range + del = 0; // just copy over the smallest r values + } + rho = rhoarr(itemp) * (1.-del) + rhoarr(itemp+1) * del; + u = rho * (Tarr(itemp) * (1.-del) + Tarr(itemp+1) * del)*n; + ucon_bl[1] = 0.; + } + + // Set u^t to make u^r a 4-vector + Real gcov_bl[GR_DIM][GR_DIM]; + bl.gcov_embed(Xembed, gcov_bl); + set_ut(gcov_bl, ucon_bl); + + // Then transform that 4-vector to KS, then to native + Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; + ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); + coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + + // Convert native 4-vector to primitive u-twiddle, see Gammie '04 + Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; + G.gcon(Loci::center, j, i, gcon); + fourvel_to_prim(gcon, ucon_mks, u_prim); + + P(m_p.RHO, k, j, i) = rho; + P(m_p.UU, k, j, i) = u; + P(m_p.U1, k, j, i) = u_prim[0]; + P(m_p.U2, k, j, i) = u_prim[1]; + P(m_p.U3, k, j, i) = u_prim[2]; +} diff --git a/kharma/prob/prob_common.hpp b/kharma/prob/prob_common.hpp index ce7a883a..57f895aa 100644 --- a/kharma/prob/prob_common.hpp +++ b/kharma/prob/prob_common.hpp @@ -221,3 +221,26 @@ KOKKOS_INLINE_FUNCTION void fourvel_to_prim(const Real gcon[GR_DIM][GR_DIM], con u_prim[1] = ucon[2] + ucon[0] * alpha2 * gcon[0][2]; u_prim[2] = ucon[3] + ucon[0] * alpha2 * gcon[0][3]; } + +KOKKOS_INLINE_FUNCTION void bl_fourvel_to_prim(const GRCoordinates& G, const CoordinateEmbedding& coords, + const SphBLCoords& bl, const SphKSCoords& ks, + const int& k, const int& j, const int& i, Real ucon_bl[GR_DIM], Real u_prim[NVEC]) +{ + GReal Xnative[GR_DIM], Xembed[GR_DIM]; // + G.coord(k, j, i, Loci::center, Xnative); + G.coord_embed(k, j, i, Loci::center, Xembed); + + // Set u^t to make u^r a 4-vector + Real gcov_bl[GR_DIM][GR_DIM]; + bl.gcov_embed(Xembed, gcov_bl); + set_ut(gcov_bl, ucon_bl); + + // Then transform that 4-vector to KS, then to native + Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; + ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); + coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + + Real gcon[GR_DIM][GR_DIM]; + G.gcon(Loci::center, j, i, gcon); //TODO: this causes the memory issue!! + fourvel_to_prim(gcon, ucon_mks, u_prim); +} \ No newline at end of file diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index f72b5faa..6083b11f 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -56,7 +56,7 @@ #include "mhdmodes.hpp" #include "orszag_tang.hpp" #include "shock_tube.hpp" -#include "hubble.hpp" +#include "gizmo.hpp" // EMHD problem headers #include "emhd/anisotropic_conduction.hpp" #include "emhd/emhdmodes.hpp" @@ -136,6 +136,8 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) status = ReadIharmRestart(rc, pin); } else if (prob == "resize_restart_kharma") { // Hyerin status = ReadKharmaRestart(rc, pin); + } else if (prob == "gizmo") { + status = InitializeGIZMO(rc, pin); } // If we didn't initialize a problem, yell diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index c41af3dc..1a41b516 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -401,7 +401,7 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); SphKSCoords kscoord = mpark::get(G.coords.base); - SphBLCoords blcoord = SphBLCoords(kscoord.a); //, kscoord.ext_g); // modified (11/15/22) + SphBLCoords blcoord = SphBLCoords(kscoord.a, kscoord.ext_g); // modified (11/15/22) CoordinateEmbedding coords = G.coords; @@ -435,7 +435,6 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, k, j, i); - //GRMHD::p_to_u(G,P,m_p,gam,k,j,i,U,m_u); //TODO: is this needed? I don't see it in resize_restart.cpp //if (pin->GetOrAddString("b_field", "type", "none") != "none") { // VLOOP B_host(v, k, j, i) = interp_scalar(G, X, startx, stopx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(B_file[v*block_sz])); //} @@ -447,7 +446,6 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain k, j, i); } ); - //if (include_B) B_FluxCT::PtoU(rc,domain); // added for B fields } return TaskStatus::complete; diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 7b8c99f3..52bf8883 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -71,31 +71,6 @@ KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], del[3] = 0.;// (phi - ((k) * dx[3] + startx[3])) / dx[3]; } - -KOKKOS_INLINE_FUNCTION void convert_to_utwiddle(const GRCoordinates& G, const CoordinateEmbedding& coords, - const SphBLCoords& bl, const SphKSCoords& ks, - const int& k, const int& j, const int& i, Real ucon_bl[GR_DIM], Real u_prim[NVEC]) -{ - GReal Xnative[GR_DIM], Xembed[GR_DIM]; // - G.coord(k, j, i, Loci::center, Xnative); - G.coord_embed(k, j, i, Loci::center, Xembed); - - // Set u^t to make u^r a 4-vector - Real gcov_bl[GR_DIM][GR_DIM]; - bl.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS, then to native - Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; - ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); - coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); - - Real gcon[GR_DIM][GR_DIM]; - G.gcon(Loci::center, j, i, gcon); //TODO: this causes the memory issue!! - fourvel_to_prim(gcon, ucon_mks, u_prim); - -} - KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, const SphBLCoords& bl, const SphKSCoords& ks, const Real fx1min, const Real fx1max, const Real fnghost, const bool should_fill, const bool is_spherical, const bool include_B, @@ -142,7 +117,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; - convert_to_utwiddle(G,coords,bl,ks,k,j,i,ucon_bl,u_prim); + bl_fourvel_to_prim(G,coords,bl,ks,k,j,i,ucon_bl,u_prim); } // HyerinTODO: if fname_fill exists and smaller. From e4a57890cfc91f8e830ebc18c247e272b7418610 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Mon, 6 Mar 2023 09:04:49 -0500 Subject: [PATCH 046/219] updated bclean test code --- tests/bclean/run.sh | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/tests/bclean/run.sh b/tests/bclean/run.sh index 84a619ff..e7d7ee74 100755 --- a/tests/bclean/run.sh +++ b/tests/bclean/run.sh @@ -5,19 +5,20 @@ # User specified values here KERR=false +bz=5e-3 DIM=3 NZONES=2 #7 BASE=8 -NRUNS=2 -START_RUN=0 -DRTAG="bondi_multizone_021823_sane_b_clean" +NRUNS=100 +START_RUN=0 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from +DRTAG="bondi_multizone_030423_bclean_${bz}_flr" # Set paths KHARMADIR=../.. PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory DR="${PDR}data/${DRTAG}" -#parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file -parfilename="${PDR}/sane.par" # parameter file +parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file +#parfilename="${PDR}/sane_save.par" # parameter file # other values determined automatically turn_around=$(($NZONES-1)) @@ -39,7 +40,7 @@ fi for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) do args=() - echo "iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" + echo "${DRTAG}: iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO runtime=`echo "scale=0; e($logruntime)+1" | bc -l` log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) @@ -49,7 +50,7 @@ do # set problem type and cleanup if [ $VAR -eq 0 ]; then - prob="torus" #"bondi" + prob="bondi" #"torus" # init_c=0 else prob="resize_restart_kharma" @@ -65,7 +66,8 @@ do # output time steps output0_dt=$((${runtime}/100*10)) - output1_dt=$((${runtime}/20*10)) + #output1_dt=$((${runtime}/20*10)) + output1_dt=$((${runtime}/50*10)) output2_dt=$((${runtime}/1000*10)) # dt, fname, fname_fill @@ -100,21 +102,26 @@ do out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - srun --mpi=pmix ${PDR}/kharma_fork/kharma.cuda -i ${parfilename} \ + srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ + parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ + parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=32 \ parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time} \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} \ + parthenon/time/tlim=${start_time}\ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/hslope=1 coordinates/transform=mks \ bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + floors/disable_floors=false floors/bsq_over_rho_max=100 floors/u_over_rho_max=2 \ + b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ + b_cleanup/rel_tolerance=1.e-8 \ + resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ parthenon/output0/dt=$output0_dt \ parthenon/output1/dt=$output1_dt \ parthenon/output2/dt=$output2_dt \ ${args[@]} \ -d ${data_dir} 1> ${out_fn} 2>${err_fn} - #parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ - #parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=32 parthenon/meshblock/nx3=64 \ - #coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin \ - #b_field/type=vertical b_field/solver=flux_ct \ + # nlim=10000 for 1e-3 parthenon/time/nlim=$((10000*($VAR+1))) + #b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ + #coordinates/transform=mks coordinates/hslope=1 \ this, for some reason does not work for b cleaning? if [ $VAR -ne 0 ]; then if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then From b21d9253a477b69a92d5d4a656d8b5590f29b122 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Mon, 20 Mar 2023 15:34:09 -0400 Subject: [PATCH 047/219] Port forward multizone debugging/fixes --- kharma/prob/gizmo.hpp | 2 ++ kharma/prob/post_initialize.cpp | 5 +++++ kharma/prob/resize_restart_kharma.hpp | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kharma/prob/gizmo.hpp b/kharma/prob/gizmo.hpp index f51a7739..6e4aef1e 100644 --- a/kharma/prob/gizmo.hpp +++ b/kharma/prob/gizmo.hpp @@ -79,6 +79,8 @@ KOKKOS_INLINE_FUNCTION void XtoindexGIZMO(const GReal XG[GR_DIM], // interpolation (11/14/2022) TODO: write a case where indices hit the boundaries of the data file del = (XG[1]-rarr(i))/(rarr(i+1)-rarr(i)); + + if (m::abs(dx2_min/m::pow(XG[1],2))>1.e-8) printf("XtoindexGizmo: dx2 pretty large = %g at r= %g \n",dx2_min, XG[1]); } /** * Get the GIZMO output values at a particular zone diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 9041477f..52c29b50 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -272,6 +272,11 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // logic about parsing whether to clean is there if (pkgs.count("B_Cleanup")) { B_Cleanup::CleanupDivergence(md); + // Hyerin (03/02/23) after cleaning, floors should be applied again + for (auto &pmb : pmesh->block_list) { + auto rc = pmb->meshblock_data.Get(); + Floors::ApplyFloors(rc.get(), IndexDomain::entire); + } } Flag("Post-initialization finished"); diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 52bf8883..35161297 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -69,6 +69,7 @@ KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], del[1] = 0.; //(XG[1] - ((i) * dx[1] + startx[1])) / dx[1]; del[2] = 0.;//(XG[2] - ((j) * dx[2] + startx[2])) / dx[2]; del[3] = 0.;// (phi - ((k) * dx[3] + startx[3])) / dx[3]; + if (m::abs(dx2_min/m::pow(XG[1],2.))>1.e-8) printf("Xtoindex: dx2 pretty large = %g at r= %g \n",dx2_min, XG[1]); } KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, @@ -100,7 +101,8 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons GReal r = Xembed[1]; // copy over smallest radius states - Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); + //Xtoindex(X, x1, x2, x3, length, iblocktemp, itemp, jtemp, ktemp, del); + iblocktemp=0; // assuming always this block contains smallest radii? itemp = fnghost; // in order to copy over the physical region, not the ghost region // (02/08/23) instead in order to set the vacuum homogeneous instead of having theta phi dependence, set j and k values jtemp = fnghost; From 11403dbb241d6bf8973d30b69b65524db7151b9d Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 21 Mar 2023 09:59:52 -0400 Subject: [PATCH 048/219] updated openmpi and gcc --- machines/cannon_ramesh.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/machines/cannon_ramesh.sh b/machines/cannon_ramesh.sh index 42b59dd6..0a23a057 100755 --- a/machines/cannon_ramesh.sh +++ b/machines/cannon_ramesh.sh @@ -19,8 +19,10 @@ if [[ $(hostname -f) == *"rc.fas.harvard.edu" ]]; then if [[ "$ARGS" == *"cuda"* ]]; then #DEVICE_ARCH=VOLTA70 ## test, (old GPUs) DEVICE_ARCH=AMPERE80 ## blackhole_gpu, itc_gpu - module load gcc/9.3.0-fasrc01 - module load openmpi/4.0.5-fasrc01 + #module load gcc/9.3.0-fasrc01 + module load gcc/10.2.0-fasrc01 # test + #module load openmpi/4.0.5-fasrc01 + module load openmpi/4.1.3-fasrc03 # test #module load cuda/11.1.0-fasrc01 module load cuda/11.6.2-fasrc01 export PATH=/n/home09/hyerincho/packages/hdf5-openmpi4.1.1:$PATH From 5d7dc3251138493ad058dc64fe519e76611fe5bc Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 21 Mar 2023 10:03:35 -0400 Subject: [PATCH 049/219] updated the test batch scripts --- .gitignore | 3 + tests/bclean/run.sh | 13 ++-- tests/bflux/run.sh | 33 ++++---- tests/bondi_multizone/run.sh | 143 +++++++++++++++++++++++++++++++++++ tests/gizmo_shell/run.sh | 133 ++++++++++++++++++++++++++++++++ 5 files changed, 306 insertions(+), 19 deletions(-) create mode 100755 tests/bondi_multizone/run.sh create mode 100755 tests/gizmo_shell/run.sh diff --git a/.gitignore b/.gitignore index eb9e862e..858fd075 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ make_args # Python files __pycache__/ *.pyc + +# added by Hyerin +*.swp diff --git a/tests/bclean/run.sh b/tests/bclean/run.sh index e7d7ee74..2c2f9fc6 100755 --- a/tests/bclean/run.sh +++ b/tests/bclean/run.sh @@ -11,10 +11,9 @@ NZONES=2 #7 BASE=8 NRUNS=100 START_RUN=0 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from -DRTAG="bondi_multizone_030423_bclean_${bz}_flr" +DRTAG="bondi_multizone_031123_bclean_${bz}_flr_test" # Set paths -KHARMADIR=../.. PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory DR="${PDR}data/${DRTAG}" parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file @@ -104,12 +103,13 @@ do srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ - parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=32 \ + parthenon/meshblock/nx1=16 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time}\ + parthenon/time/tlim=${start_time} \ coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/hslope=1 coordinates/transform=mks \ bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - floors/disable_floors=false floors/bsq_over_rho_max=100 floors/u_over_rho_max=2 \ + floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ + floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 \ b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ b_cleanup/rel_tolerance=1.e-8 \ @@ -119,7 +119,8 @@ do parthenon/output2/dt=$output2_dt \ ${args[@]} \ -d ${data_dir} 1> ${out_fn} 2>${err_fn} - # nlim=10000 for 1e-3 parthenon/time/nlim=$((10000*($VAR+1))) + # nlim=10000 for 1e-3 + # floors/u_over_rho_max=2 #b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ #coordinates/transform=mks coordinates/hslope=1 \ this, for some reason does not work for b cleaning? diff --git a/tests/bflux/run.sh b/tests/bflux/run.sh index 683deff3..2494f1a7 100755 --- a/tests/bflux/run.sh +++ b/tests/bflux/run.sh @@ -5,12 +5,13 @@ # User specified values here KERR=false +bz=1e-4 DIM=3 -NZONES=2 #7 +NZONES=7 BASE=8 -NRUNS=14 +NRUNS=100 START_RUN=0 -DRTAG="bondi_multizone_022223_n2b8_bondi_1e-3_flows" +DRTAG="bondi_multizone_032023_fixfluxx1_${bz}_n7b8" # Set paths KHARMADIR=../.. @@ -20,11 +21,11 @@ parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # par # other values determined automatically turn_around=$(($NZONES-1)) -start_time=0 +start_time=0 #83964 # out_to_in=1 -iteration=1 -r_out=$((${BASE}**($turn_around+2))) -r_in=$((${BASE}**$turn_around)) +iteration=1 #13 # +r_out=$((${BASE}**($turn_around+2))) #64 # +r_in=$((${BASE}**$turn_around)) #1 # # if the directories are not present, make them. if [ ! -d "${DR}" ]; then @@ -38,7 +39,7 @@ fi for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) do args=() - echo "iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" + echo "${DRTAG}: iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO runtime=`echo "scale=0; e($logruntime)+1" | bc -l` log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) @@ -98,20 +99,26 @@ do out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - srun --mpi=pmix ${PDR}/kharma_fork/kharma.cuda -i ${parfilename} \ + srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ + parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ + parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=32 \ parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time} parthenon/time/nlim=$((5000*($VAR+1))) \ - parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ - parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ + parthenon/time/tlim=${start_time} \ coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=false\ + coordinates/transform=mks coordinates/hslope=1 \ bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - b_field/type=vertical b_field/solver=flux_ct b_field/bz=1e-3 \ + floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ + floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 floors/u_over_rho_max=100 floors/gamma_max=5 \ + b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ + resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ parthenon/output0/dt=$output0_dt \ parthenon/output1/dt=$output1_dt \ parthenon/output2/dt=$output2_dt \ ${args[@]} \ -d ${data_dir} 1> ${out_fn} 2>${err_fn} + # parthenon/time/nlim=$((10000*($VAR+1))) + #floors/bsq_over_rho_max=100 floors/u_over_rho_max=2 \ if [ $VAR -ne 0 ]; then if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then diff --git a/tests/bondi_multizone/run.sh b/tests/bondi_multizone/run.sh new file mode 100755 index 00000000..395a7e64 --- /dev/null +++ b/tests/bondi_multizone/run.sh @@ -0,0 +1,143 @@ +#!/bin/bash +# Hyerin (02/17/23) copied from Ben's code + +# Bash script testing HD bondi + +# User specified values here +KERR=false +JITTER=false #true # +DIM=3 +NZONES=7 +BASE=8 +NRUNS=300 +START_RUN=53 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from +DRTAG="bondi_multizone_030723_bondi_128^3" + +# Set paths +KHARMADIR=../.. +PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory +DR="${PDR}data/${DRTAG}" +parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file + +# other values determined automatically +turn_around=$(($NZONES-1)) +start_time=32963095169 #0 # +out_to_in=1 # -1 # +iteration=9 # eq : (iteration-1)*(NZONES-1) 0.00001" |bc -l) )); then + dt_new=$dt_new + else + dt_new=0.00001 + fi + fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" + fname=$(find ${fname_dir} -type f -iname "*final.rhdf") + if [ $VAR -ge $NZONES ]; then + fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) + fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" + fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") + else + fname_fill="none" + fi + args+=(" resize_restart/fname=$fname resize_restart/use_dt=false parthenon/time/dt_min=$dt_new") + args+=(" resize_restart/fname_fill=$fname_fill ") + else + r_shell=$((${r_out}/2)) + args+=(" bondi/r_shell=$r_shell ") + if [[ $JITTER == "true" ]]; then + args+=(" perturbation/u_jitter=0.3 ") + else + args+=(" perturbation/u_jitter=0.0 ") + fi + fi + + + + # data_dir, logfiles + data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" + out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" + err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" + + srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ + parthenon/job/problem_id=$prob \ + parthenon/time/tlim=${start_time} parthenon/time/nlim=-1 \ + parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ + parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=128 \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=false \ + coordinates/transform=mks coordinates/hslope=1 \ + bounds/fix_flux_pole=1 \ + bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} bondi/use_gizmo=false \ + b_field/type=none b_field/solver=none b_field/bz=1e-3 \ + b_field/fix_flux_x1=0 b_field/initial_cleanup=0 \ + resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ + parthenon/output0/dt=$output0_dt \ + parthenon/output1/dt=$output1_dt \ + parthenon/output2/dt=$output2_dt \ + ${args[@]} \ + -d ${data_dir} 1> ${out_fn} 2>${err_fn} + + if [ $VAR -ne 0 ]; then + if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then + out_to_in=$(($out_to_in*(-1))) + iteration=$(($iteration+1)) + fi + fi + + if [ $out_to_in -gt 0 ]; then + # half the radii + r_out=$((${r_out}/$BASE)) + r_in=$((${r_in}/$BASE)) + else + # double the radii + r_out=$((${r_out}*$BASE)) + r_in=$((${r_in}*$BASE)) + fi +done diff --git a/tests/gizmo_shell/run.sh b/tests/gizmo_shell/run.sh new file mode 100755 index 00000000..64ee3796 --- /dev/null +++ b/tests/gizmo_shell/run.sh @@ -0,0 +1,133 @@ +#!/bin/bash +# Hyerin (02/18/23) copied from Ben's code + +# Bash script testing gizmo shell run (no b field) + +# User specified values here +KERR=false +EXT_G=false #true # +DIM=3 +NZONES=7 +BASE=8 +NRUNS=300 +START_RUN=8 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from +DRTAG="bondi_multizone_030723_gizmo_no_ext_g_128^3" + +# Set paths +KHARMADIR=../.. +PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory +DR="${PDR}data/${DRTAG}" +parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file + +# other values determined automatically +turn_around=$(($NZONES-1)) +start_time=6013548357 #0 # +out_to_in=-1 # 1 # +iteration=2 # 1 #eq : (iteration-1)*(NZONES-1) 0.00001" |bc -l) )); then + dt_new=$dt_new + else + dt_new=0.00001 + fi + fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" + fname=$(find ${fname_dir} -type f -iname "*final.rhdf") + if [ $VAR -ge $NZONES ]; then + fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) + fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" + fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") + else + fname_fill="none" + fi + args+=(" resize_restart/fname=$fname resize_restart/use_dt=false parthenon/time/dt_min=$dt_new") + args+=(" resize_restart/fname_fill=$fname_fill ") + else + r_shell=$r_in + args+=(" bondi/r_shell=$r_shell ") + fi + + # data_dir, logfiles + data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" + out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" + err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" + + srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ + parthenon/job/problem_id=$prob \ + parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ + parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=128 \ + parthenon/time/tlim=${start_time} \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=$EXT_G \ + coordinates/transform=mks coordinates/hslope=1 \ + bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + bondi/use_gizmo=true \ + b_field/type=none b_field/solver=none \ + b_field/fix_flux_x1=0 b_field/initial_cleanup=0 \ + resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration \ + parthenon/output0/dt=$output0_dt \ + parthenon/output1/dt=$output1_dt \ + parthenon/output2/dt=$output2_dt \ + ${args[@]} \ + -d ${data_dir} 1> ${out_fn} 2>${err_fn} + + if [ $VAR -ne 0 ]; then + if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then + out_to_in=$(($out_to_in*(-1))) + iteration=$(($iteration+1)) + fi + fi + + if [ $out_to_in -gt 0 ]; then + # half the radii + r_out=$((${r_out}/$BASE)) + r_in=$((${r_in}/$BASE)) + else + # double the radii + r_out=$((${r_out}*$BASE)) + r_in=$((${r_in}*$BASE)) + fi +done From b8c9714f852e0f9ba21cebdcb209e407379fd157 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 21 Mar 2023 10:14:12 -0400 Subject: [PATCH 050/219] Port forward flux-fix improvements --- kharma/b_flux_ct/b_flux_ct.cpp | 146 ++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index af801fbc..c1ac6829 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -462,12 +462,31 @@ TaskStatus FixX1Flux(MeshData *md) Real x1min = pmb0->packages.Get("GRMHD")->Param("x1min"); //Hyerin (01/31/23) + // (03/08/23) places to store + //const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); + //const int n2 = pmb0->cellbounds.ncellsj(IndexDomain::entire); + //const int n3 = pmb0->cellbounds.ncellsk(IndexDomain::entire); + //GridScalar B_F_X2_V1("B_F_X2_V1", n3, n2, n1); // for B_F.flux(X2DIR,V1,k,j,i) + //GridScalar B_F_X3_V1("B_F_X3_V1", n3, n2, n1); // for B_F.flux(X3DIR,V1,k,j,i) + //auto B_F_X2_V1_host = B_F_X2_V1.GetHostMirror(); + //auto B_F_X3_V1_host = B_F_X3_V1.GetHostMirror(); + //auto B_F_host = x2_fill_device.GetHostMirror(); + GridVector F1, F2, F3; + // Assuming the fluxes through the pole are 0, // make sure the polar EMFs are 0 when performing fluxCT // TODO only invoke one kernel? We avoid invocation except on boundaries anyway for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); + + // (03/08/23) + F1 = rc->Get("cons.B").flux[X1DIR]; // B_F.flux(X1DIR,v,k,j,i) + F2 = rc->Get("cons.B").flux[X2DIR]; // B_F.flux(X2DIR,v,k,j,i) + F3 = rc->Get("cons.B").flux[X3DIR]; // B_F.flux(X3DIR,v,k,j,i) + auto F1_host=F1.GetHostMirrorAndCopy(); + auto F2_host=F2.GetHostMirrorAndCopy(); + auto F3_host=F3.GetHostMirrorAndCopy(); // update the j and k bounds (Hyerin 02/21/23) js_new = js+1; //js-1; @@ -480,18 +499,25 @@ TaskStatus FixX1Flux(MeshData *md) } if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { out_x2 = true; - je_new = je_e; + je_new = je; //_e; } + //printf("HYERIN: test F1V2 %g\n",F1_host(V2,30,30,is)); + //pmb->par_for("test", 30,30,30,30,is,is, + // KOKKOS_LAMBDA_3D { + // printf("HYERIN: test B_F(X1DIR,V2) %g, F1V2 %g \n",B_F.flux(X1DIR,V2,k,j,i),F1(V2,k,j,i)); + // } + //); + //added by Hyerin (12/23/22) TODO: it has to ask if x2 boundary is inner_x2 or outer_x2 and update the jj bounds if ((pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) && (x1min>1) ) // only apply fix flux for inner bc when it is far from the EH { for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { for (int jtemp = js_new; jtemp <= je_new; jtemp++) { - pmb->par_for("fix_flux_b_l", ktemp, ktemp, jtemp, jtemp, is, is, // Hyerin (02/20/23) for 3rd prescription, sequential + //pmb->par_for("fix_flux_b_l", ktemp, ktemp, jtemp, jtemp, is, is, // Hyerin (02/20/23) for 3rd prescription, sequential //pmb->par_for("fix_flux_b_l", ks_all+2, ke_all, js_new, je_new, is, is, // Hyerin (02/20/23) for 3rd prescription //pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // Hyerin (12/28/22) for 1st & 2nd prescription - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; @@ -503,61 +529,48 @@ TaskStatus FixX1Flux(MeshData *md) // // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux if (ndim > 1) { - B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) - B_F.flux(X1DIR, V2, k, j-2, is) + B_F.flux(X2DIR, V1, k, j-1, is) + B_F.flux(X2DIR, V1, k, j-1, is-1); + //B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) - B_F.flux(X1DIR, V2, k, j-2, is) + B_F.flux(X2DIR, V1, k, j-1, is) + B_F.flux(X2DIR, V1, k, j-1, is-1); + F2_host(V1, ktemp, jtemp, is-1) = -F2_host(V1, ktemp, jtemp, is) + F1_host(V2, ktemp, jtemp, is) - F1_host(V2, ktemp, jtemp-2, is) + F2_host(V1, ktemp, jtemp-1, is) + F2_host(V1, ktemp, jtemp-1, is-1); } if (ndim > 2) { - B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) - B_F.flux(X1DIR, V3, k-2, j, is) + B_F.flux(X3DIR, V1, k-1, j, is) + B_F.flux(X3DIR, V1, k-1, j, is-1); + //B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) - B_F.flux(X1DIR, V3, k-2, j, is) + B_F.flux(X3DIR, V1, k-1, j, is) + B_F.flux(X3DIR, V1, k-1, j, is-1); + F3_host(V1, ktemp, jtemp, is-1) = -F3_host(V1, ktemp, jtemp, is) + F1_host(V3, ktemp, jtemp, is) - F1_host(V3, ktemp-2, jtemp, is) + F3_host(V1, ktemp-1, jtemp, is) + F3_host(V1, ktemp-1, jtemp, is-1); } - if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) - B_F.flux(X2DIR, V1,k,j,i-1) = -B_F.flux(X1DIR,V2,k,j,i+1) -B_F.flux(X1DIR,V2,k,j-1,i+1); - B_F.flux(X2DIR, V1,k,j,i) = -0.5*B_F.flux(X2DIR,V1,k,j,i-1); + //if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) + if (in_x2 && (jtemp==js)) {// (corners are tricky so let's just initialize) + //B_F.flux(X2DIR, V1,k,j,i-1) = -B_F.flux(X1DIR,V2,k,j,i+1) -B_F.flux(X1DIR,V2,k,j-1,i+1); + F2_host(V1,ktemp,jtemp,is-1) = -F1_host(V2,ktemp,jtemp,is+1) -F1_host(V2,ktemp,jtemp-1,is+1); + //B_F.flux(X2DIR, V1,k,j,i) = -0.5*B_F.flux(X2DIR,V1,k,j,i-1); + F2_host(V1,ktemp,jtemp,is) = -0.5*F2_host(V1,ktemp,jtemp,is-1); } - if (out_x2 && (j==je_e)) {// (corners are tricky) + //if (out_x2 && (j==je_e)) {// (corners are tricky) + if (out_x2 && (jtemp==je_e)) {// (corners are tricky) ( so maybe just don't touch it...? (03/12/23) //B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, je, is) - B_F.flux(X2DIR, V1, k, je, is-1) // +B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je-1, is); //B_F.flux(X2DIR, V1, k, j, i-1) = -2.*B_F.flux(X1DIR, V2, k, je-1, is) -B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je+1, is) // +2.*B_F.flux(X2DIR, V1, k, je, is) + 2.*B_F.flux(X2DIR, V1, k, je, is-1); - B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); - B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + //B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); + F1_host(V2,ktemp,jtemp-1,is) = -F1_host(V2,ktemp,je-1,is)+F2_host(V1,ktemp,je,is)+F2_host(V1,ktemp,je,is-1); + //B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + F1_host(V2,ktemp,jtemp,is) = -F1_host(V2,ktemp,je,is); } - /* - if (k == ke_all-5 && j>js-1 && jboundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { for (int jtemp = js_new; jtemp <= je_new; jtemp++) { - pmb->par_for("fix_flux_b_r", ktemp, ktemp, jtemp, jtemp, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription, sequential + //pmb->par_for("fix_flux_b_r", ktemp, ktemp, jtemp, jtemp, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription, sequential //pmb->par_for("fix_flux_b_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription //pmb->par_for("fix_flux_b_r", ks_all+1, ke_all+1, js_all+1, je_all+1, ie+1, ie+1, // Hyerin (12/28/22) for 1st & 2nd prescription - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { /* 1st prescription to make the X1DIR flux = 0 B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie); if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; @@ -568,28 +581,61 @@ TaskStatus FixX1Flux(MeshData *md) //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); // // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux - if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, ie+1) - - B_F.flux(X1DIR, V2, k, j-2, ie+1) + B_F.flux(X2DIR, V1, k, j-1, ie) + B_F.flux(X2DIR, V1, k, j-1, ie+1); - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, ie+1) - - B_F.flux(X1DIR, V3, k-2, j, ie+1) + B_F.flux(X3DIR, V1, k-1, j, ie) + B_F.flux(X3DIR, V1, k-1, j, ie+1); - - if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) - B_F.flux(X2DIR, V1,k,j,i) = -B_F.flux(X1DIR,V2,k,j,ie) -B_F.flux(X1DIR,V2,k,j-1,ie); - B_F.flux(X2DIR, V1,k,j,i-1) = -0.5*B_F.flux(X2DIR,V1,k,j,i); + //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, ie+1) + // - B_F.flux(X1DIR, V2, k, j-2, ie+1) + B_F.flux(X2DIR, V1, k, j-1, ie) + B_F.flux(X2DIR, V1, k, j-1, ie+1); + if (ndim > 1) F2_host(V1, ktemp, jtemp, ie+1) = -F2_host(V1, ktemp, jtemp, ie) + F1_host(V2, ktemp, jtemp, ie+1) + - F1_host(V2, ktemp, jtemp-2, ie+1) + F2_host(V1, ktemp, jtemp-1, ie) + F2_host(V1, ktemp, jtemp-1, ie+1); + //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, ie+1) + // - B_F.flux(X1DIR, V3, k-2, j, ie+1) + B_F.flux(X3DIR, V1, k-1, j, ie) + B_F.flux(X3DIR, V1, k-1, j, ie+1); + if (ndim > 2) F3_host(V1, ktemp, jtemp, ie+1) = -F3_host(V1, ktemp, jtemp, ie) + F1_host(V3, ktemp, jtemp, ie+1) + - F1_host(V3, ktemp-2, jtemp, ie+1) + F3_host(V1, ktemp-1, jtemp, ie) + F3_host(V1, ktemp-1, jtemp, ie+1); + + //if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) + if (in_x2 && (jtemp==js)) {// (corners are tricky so let's just initialize) + //B_F.flux(X2DIR, V1,k,j,i) = -B_F.flux(X1DIR,V2,k,j,ie) -B_F.flux(X1DIR,V2,k,j-1,ie); + F2_host(V1,ktemp,jtemp,ie+1) = -F1_host(V2,ktemp,jtemp,ie) -F1_host(V2,ktemp,jtemp-1,ie); + //B_F.flux(X2DIR, V1,k,j,i-1) = -0.5*B_F.flux(X2DIR,V1,k,j,i); + F2_host(V1,ktemp,jtemp,ie) = -0.5*F2_host(V1,ktemp,jtemp,ie+1); } - if (out_x2 && (j==je_e)) {// (corners are tricky) + //if (out_x2 && (j==je_e)) {// (corners are tricky) + if (out_x2 && (jtemp==je_e)) {// (corners are tricky) //B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, je, ie) - B_F.flux(X2DIR, V1, k, je, ie+1) // +B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je-1, ie+1); //B_F.flux(X2DIR, V1, k, j, i) = -2.*B_F.flux(X1DIR, V2, k, je-1, ie+1) -B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je+1, ie+1) // +2.*B_F.flux(X2DIR, V1, k, je, ie) + 2.*B_F.flux(X2DIR, V1, k, je, ie+1); - B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); - B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + //B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); + F1_host(V2,ktemp,jtemp-1,ie+1) = -F1_host(V2,ktemp,je-1,ie+1)+F2_host(V1,ktemp,je,ie+1)+F2_host(V1,ktemp,je,ie); + //B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); + F1_host(V2,ktemp,jtemp,ie+1) = -F1_host(V2,ktemp,je,ie+1); } - } - ); + //} + //); } } } + // Deep copy to device + F1.DeepCopy(F1_host); + F2.DeepCopy(F2_host); + F3.DeepCopy(F3_host); + Kokkos::fence(); + + // put it back to B_F.flux. is this even needed? + //pmb->par_for("copy_to_B_F_l", ks_all+2, ke_all, js_new, je_new, is, is, + // KOKKOS_LAMBDA_3D { + // VLOOP B_F.flux(X1DIR,v,k,j,i) = F1(v,k,j,i); + // VLOOP B_F.flux(X2DIR,v,k,j,i) = F2(v,k,j,i); + // VLOOP B_F.flux(X3DIR,v,k,j,i) = F3(v,k,j,i); + // } + //); + //pmb->par_for("copy_to_B_F_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, + // KOKKOS_LAMBDA_3D { + // VLOOP B_F.flux(X1DIR,v,k,j,i) = F1(v,k,j,i); + // VLOOP B_F.flux(X2DIR,v,k,j,i) = F2(v,k,j,i); + // VLOOP B_F.flux(X3DIR,v,k,j,i) = F3(v,k,j,i); + // } + //); + + } Flag(md, "Fixed X1 B"); From e942addc781df5bece749626dbc3aec27551fd16 Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 21 Mar 2023 10:16:32 -0400 Subject: [PATCH 051/219] Port forward fn rearranging and prototype vector potential solver --- kharma/b_flux_ct/seed_B_ct.cpp | 140 ++++++++++++++++++++------------- kharma/b_flux_ct/seed_B_ct.hpp | 58 ++++++++++++++ 2 files changed, 144 insertions(+), 54 deletions(-) diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 5a5f38a9..33db70a0 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -118,6 +118,10 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); + //domain = IndexDomain::entire; // Hyerin: also do it everywhere if it is resize_restart_kharma + //int is_all = pmb->cellbounds.is(domain), ie_all = pmb->cellbounds.ie(domain); + //int js_all = pmb->cellbounds.js(domain), je_all = pmb->cellbounds.je(domain); + //int ks_all = pmb->cellbounds.ks(domain), ke_all = pmb->cellbounds.ke(domain); int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); int n2 = pmb->cellbounds.ncellsj(IndexDomain::entire); int n3 = pmb->cellbounds.ncellsk(IndexDomain::entire); @@ -273,68 +277,20 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Calculate B-field if (ndim > 2) { pmb->par_for("B_field_B_3D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Take a flux-ct step from the corner potentials. - // This needs to be 3D because post-tilt A may not point in the phi direction only - - // A3,2 derivative - const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1) + - A(V3, k + 1, j + 1, i) + A(V3, k + 1, j + 1, i + 1)) / 4; - const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1) + - A(V3, k + 1, j, i) + A(V3, k + 1, j, i + 1)) / 4; - // A2,3 derivative - const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j, i + 1) + - A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; - const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + - A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); - - // A1,3 derivative - const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + - A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; - const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + - A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 4; - // A3,1 derivative - const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1) + - A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; - const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + - A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; - B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); - - // A2,1 derivative - const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + - A(V2, k + 1, j, i + 1) + A(V2, k + 1, j + 1, i + 1)) / 4; - const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i) + - A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 4; - // A1,2 derivative - const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1) + - A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; - const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + - A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; - B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); + KOKKOS_LAMBDA_3D { + get_B_from_A_3D(G, A, B_U, k, j, i); } ); } else { pmb->par_for("B_field_B_2D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // A3,2 derivative - const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; - const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j); - - // A3,1 derivative - const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; - const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; - B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.Dxc<1>(i); - - B_U(V3, k, j, i) = 0; + KOKKOS_LAMBDA_3D { + get_B_from_A_2D(G, A, B_U, k, j, i); } ); } - if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { // Hyerin (12/19/22) copy over data after initialization - + pmb->par_for("copy_B_restart_resize_kharma", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal X[GR_DIM]; @@ -344,11 +300,87 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // do nothing. just use the initialization from SeedBField } else { // overwrite with the saved values - //VLOOP B_P(v, k, j, i) = B_Save(v, k, j, i); VLOOP B_U(v, k, j, i) = B_Save(v, k, j, i); } } ); + + /* + if (ndim > 2) { + printf("WARNING: 3D not supported for resize_restart_kharma!!\n"); + } else{ + // Hyerin (02/28/23) this needs testing!! + // getting A vector by solving vector Poisson eq \Del^2\vec{A}= - \Del \cross \vec{b} + GridVector B_interp("B_interp", NVEC, n3, n2, n1); // \vec{b} in rhs + int idx, ntot; + ntot=n3*n2*n1; + if (ndim > 2) ntot *= NVEC; + GReal coeffs[ntot][ntot], curl_B[ntot], inv_coeffs[ntot][ntot], A_out[ntot]; + // curl_B : -\Del \cross \vec{b} in rhs + // coeffs : \Del^2 in lhs + + // initialize + for (int mu_ = 0; mu_ < ntot; mu_++) { + curl_B[mu_] = 0.; + A_out[ntot] = 0.; + for (int nu_ = 0; nu_ < ntot; nu_++) { + coeffs[mu_][nu_] = 0.; + } + } + pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, + KOKKOS_LAMBDA_3D { + + idx=n1*(n2*k+j)+i; + B_interp(V3,k,j,i) = 0; //(B_U(2,k,j,i) + B_U(2,k-1,j,i))/2; + + if (i==is_all || j==js_all || k== ks_all) { // think + B_interp(V1,k,j,i) = 0.; + B_interp(V2,k,j,i) = 0.; + curl_B[idx] = 0.; + } else { + B_interp(V1,k,j,i) = (B_U(V1,k,j,i) + B_U(V2,k,j,i-1))/2; + B_interp(V2,k,j,i) = (B_U(V2,k,j,i) + B_U(V2,k,j-1,i))/2; + if (ndim > 2) B_interp(V3,k,j,i) = (B_U(V3,k,j,i) + B_U(V3,k-1,j,i))/2; + curl_B[idx] = -(B_interp(V2,k,j,i)-B_interp(V2,k,j,i-1))/G.dx1v(i) + (B_interp(V1,k,j,i)-B_interp(V1,k,j-1,i))/G.dx2v(j); + } + + coeffs[idx,idx] = -2.*m::pow(G.dx1v(i),-2.)-2.*m::pow(G.dx2v(j),-2.); + coeffs[idx,idx-1] = m::pow(G.dx1v(i)) ; + coeffs[idx,idx+1] = m::pow(G.dx1v(i)) ; + coeffs[idx,idx-n2] = m::pow(G.dx2v(j)) ; + coeffs[idx,idx+n2] = m::pow(G.dx2v(j)) ; + } + ); + invert(&coeffs[0][0], &inv_coeffs[0][0]); // TODO: make my own fxn to write up an inverse (numerical recipes in C) + // get A from B + for (int mu_ = 0; mu_ < ntot; mu_++) { + for (int nu_ = 0; nu_ < ntot; nu_++) { + A_out[mu] += inv_coeffs[mu_][nu_]*curl_B[nu_]; + } + } + + // store into GridVector + pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, // think about ranges + KOKKOS_LAMBDA_3D { + idx=n1*(n2*k+j)+i; + A(V3, k, j, i) = A_out[idx]; + } + ); + + // put it back to B_U + pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, + KOKKOS_LAMBDA_3D { + get_B_from_A_2D(G, A, B_U, k, j, i); + } + ); + + + } + */ + + // update conserved values + //B_FluxCT::PtoU(rc,IndexDomain::entire); + B_FluxCT::UtoP(rc,IndexDomain::entire); } // Then make sure the primitive versions are updated, too diff --git a/kharma/b_flux_ct/seed_B_ct.hpp b/kharma/b_flux_ct/seed_B_ct.hpp index 58b87f7d..9b098459 100644 --- a/kharma/b_flux_ct/seed_B_ct.hpp +++ b/kharma/b_flux_ct/seed_B_ct.hpp @@ -26,3 +26,61 @@ TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); //void SeedBHFlux(MeshBlockData *rc, Real BHflux); } // namespace B_FluxCT + + +KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +{ + // Take a flux-ct step from the corner potentials. + // This needs to be 3D because post-tilt A may not point in the phi direction only + + // A3,2 derivative + const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1) + + A(V3, k + 1, j + 1, i) + A(V3, k + 1, j + 1, i + 1)) / 4; + const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1) + + A(V3, k + 1, j, i) + A(V3, k + 1, j, i + 1)) / 4; + // A2,3 derivative + const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j, i + 1) + + A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; + const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + + A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j) - (A2c3f - A2c3b) / G.dx3v(k); + + // A1,3 derivative + const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + + A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; + const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + + A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 4; + // A3,1 derivative + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1) + + A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + + A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; + B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.dx3v(k) - (A3c1f - A3c1b) / G.dx1v(i); + + // A2,1 derivative + const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + + A(V2, k + 1, j, i + 1) + A(V2, k + 1, j + 1, i + 1)) / 4; + const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i) + + A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 4; + // A1,2 derivative + const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1) + + A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; + const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + + A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; + B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.dx1v(i) - (A1c2f - A1c2b) / G.dx2v(j); +} + +KOKKOS_INLINE_FUNCTION void get_B_from_A_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +{ + // A3,2 derivative + const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; + const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j); + + // A3,1 derivative + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; + B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.dx1v(i); + + B_U(V3, k, j, i) = 0; +} From e9869e8f4265861481279e9f83c230a2d1975661 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 22 Mar 2023 16:22:08 -0600 Subject: [PATCH 052/219] Update some interface usage for new KHARMA/Parthenon --- kharma/b_flux_ct/b_flux_ct.cpp | 4 +--- kharma/b_flux_ct/seed_B_ct.cpp | 8 ++------ kharma/b_flux_ct/seed_B_ct.hpp | 10 +++++----- kharma/prob/post_initialize.cpp | 5 ----- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index c1ac6829..3ec65e76 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -473,9 +473,7 @@ TaskStatus FixX1Flux(MeshData *md) //auto B_F_host = x2_fill_device.GetHostMirror(); GridVector F1, F2, F3; - // Assuming the fluxes through the pole are 0, - // make sure the polar EMFs are 0 when performing fluxCT - // TODO only invoke one kernel? We avoid invocation except on boundaries anyway + // TODO(BSP) try to eliminate full-array copies. Host-parallel applications to inner/outer? for (auto &pmb : pmesh->block_list) { auto& rc = pmb->meshblock_data.Get(); auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 33db70a0..bec5431e 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -277,13 +277,13 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Calculate B-field if (ndim > 2) { pmb->par_for("B_field_B_3D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { get_B_from_A_3D(G, A, B_U, k, j, i); } ); } else { pmb->par_for("B_field_B_2D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA_3D { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { get_B_from_A_2D(G, A, B_U, k, j, i); } ); @@ -377,10 +377,6 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } */ - - // update conserved values - //B_FluxCT::PtoU(rc,IndexDomain::entire); - B_FluxCT::UtoP(rc,IndexDomain::entire); } // Then make sure the primitive versions are updated, too diff --git a/kharma/b_flux_ct/seed_B_ct.hpp b/kharma/b_flux_ct/seed_B_ct.hpp index 9b098459..063ab6e9 100644 --- a/kharma/b_flux_ct/seed_B_ct.hpp +++ b/kharma/b_flux_ct/seed_B_ct.hpp @@ -43,7 +43,7 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVe A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j) - (A2c3f - A2c3b) / G.dx3v(k); + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); // A1,3 derivative const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + @@ -55,7 +55,7 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVe A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; - B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.dx3v(k) - (A3c1f - A3c1b) / G.dx1v(i); + B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); // A2,1 derivative const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + @@ -67,7 +67,7 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVe A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; - B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.dx1v(i) - (A1c2f - A1c2b) / G.dx2v(j); + B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } KOKKOS_INLINE_FUNCTION void get_B_from_A_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) @@ -75,12 +75,12 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_2D(const GRCoordinates& G, const GridVe // A3,2 derivative const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.dx2v(j); + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j); // A3,1 derivative const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; - B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.dx1v(i); + B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.Dxc<1>(i); B_U(V3, k, j, i) = 0; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 52c29b50..9041477f 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -272,11 +272,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // logic about parsing whether to clean is there if (pkgs.count("B_Cleanup")) { B_Cleanup::CleanupDivergence(md); - // Hyerin (03/02/23) after cleaning, floors should be applied again - for (auto &pmb : pmesh->block_list) { - auto rc = pmb->meshblock_data.Get(); - Floors::ApplyFloors(rc.get(), IndexDomain::entire); - } } Flag("Post-initialization finished"); From 10b9cbf755d0aeddf7b9f0e2494cc2c59989d23f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 4 Apr 2023 19:21:51 -0600 Subject: [PATCH 053/219] Coordinates * Add SuperExponential coordinates as in Tchekhovskoy+'11/HARMPI * Split out external gravity coordinate systems, handle multiple systems which are ~KS or ~BL * Add one-step BL->KS->Native function in CoordinateEmbedding, use in init * More fixes for porting forward multizone stuff --- .../parthenon-use-gr-coordinates.patch | 17 +- kharma/b_flux_ct/b_flux_ct.cpp | 7 +- kharma/b_flux_ct/seed_B_ct.cpp | 2 +- kharma/boundaries/boundaries.cpp | 9 +- kharma/coordinates/coordinate_embedding.hpp | 196 ++++++++--- kharma/coordinates/coordinate_systems.hpp | 319 +++++++++++++----- .../coordinate_utils.hpp} | 47 +-- kharma/coordinates/gr_coordinates.cpp | 97 ++---- kharma/coordinates/gr_coordinates.hpp | 32 +- kharma/coordinates/root_find.hpp | 34 ++ kharma/decs.hpp | 2 +- kharma/driver/kharma_driver.cpp | 6 +- kharma/floors/floors_functions.hpp | 6 +- kharma/kharma.cpp | 143 ++++---- kharma/prob/bondi.cpp | 19 +- kharma/prob/bondi.hpp | 2 +- kharma/prob/bz_monopole.cpp | 2 +- kharma/prob/emhd/conducting_atmosphere.cpp | 2 +- kharma/prob/emhd/conducting_atmosphere.hpp | 2 +- kharma/prob/fm_torus.cpp | 20 +- kharma/prob/gizmo.cpp | 4 +- kharma/prob/gizmo.hpp | 23 +- kharma/prob/post_initialize.cpp | 6 + kharma/prob/problem.cpp | 11 - kharma/prob/resize_restart_kharma.cpp | 42 ++- kharma/prob/resize_restart_kharma.hpp | 15 +- machines/darwin.sh | 42 +-- tests/bclean/bondi_multizone_00000.par | 120 +++++++ tests/bclean/run.sh | 82 ++--- 29 files changed, 800 insertions(+), 509 deletions(-) rename kharma/{prob/prob_common.hpp => coordinates/coordinate_utils.hpp} (80%) create mode 100755 tests/bclean/bondi_multizone_00000.par diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index 36ada3a1..60abf16d 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 81a8a1bd..65ba74f8 100644 +index 67e5d082..0e6d2a7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,7 +11,7 @@ index 81a8a1bd..65ba74f8 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -279,6 +279,8 @@ lint_target(parthenon) +@@ -285,6 +285,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ @@ -32,3 +32,16 @@ index d1290dee..50bfc840 100644 namespace parthenon { +diff --git a/src/interface/meshblock_data.cpp b/src/interface/meshblock_data.cpp +index 720d708d..de31a71b 100644 +--- a/src/interface/meshblock_data.cpp ++++ b/src/interface/meshblock_data.cpp +@@ -432,7 +432,7 @@ MeshBlockData::GetVariablesByFlag(const Metadata::FlagCollection &flags, + + template + void MeshBlockData::Remove(const std::string &label) { +- throw std::runtime_error("MeshBlockData::Remove not yet implemented"); ++ varMap_.erase(label); + } + + template diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 3ec65e76..dfbadc84 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -130,9 +130,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); // Hyerin (12/19/22) - // TODO declare this only on "resize_kharma_restart" - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); - pkg->AddField("B_Save", m); + if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); + pkg->AddField("B_Save", m); + } // We exist basically to do this pkg->FixFlux = B_FluxCT::FixFlux; diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index bec5431e..342005f0 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -38,9 +38,9 @@ #include "b_field_tools.hpp" #include "b_flux_ct.hpp" +#include "coordinate_utils.hpp" #include "fm_torus.hpp" #include "grmhd_functions.hpp" -#include "prob_common.hpp" using namespace parthenon; diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 52a541dc..bff8a7a0 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -70,9 +70,12 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: // Fix the X1/X2 corner by replacing the reflecting condition with the inflow // Only needed if x1min is inside BH event horizon, otherwise a nuisance for divB on corners - bool inside_eh = spherical && pin->GetBoolean("coordinates", "r_min") < pin->GetBoolean("coordinates", "Rhor"); - bool fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", inside_eh); - params.Add("fix_corner", fix_corner); + if (spherical) { + const Real a = pin->GetReal("coordinates", "a"); + bool inside_eh = pin->GetBoolean("coordinates", "r_in") < 1 + sqrt(1 - a*a); + bool fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", inside_eh); + params.Add("fix_corner", fix_corner); + } // Allocate space for Dirichlet boundaries if they'll be used // We have to trust the user here since the problem will set the function pointers later diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index 9a716a5b..a5a4ba5c 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -33,20 +33,22 @@ */ #pragma once +#include "decs.hpp" + +#include "coordinate_systems.hpp" +#include "coordinate_utils.hpp" +#include "matrix.hpp" + // std::variant requires C++ exceptions, // so it will never be SYCL-ready. // Instead we use mpark's reimplementation, // patched to never throw exceptions. // Because who needs those? +// TODO(BSP) try to switch to std:: unless using SYCL #include //#include //namespace mpark = std; -#include "decs.hpp" - -#include "coordinate_systems.hpp" -#include "matrix.hpp" - /** * Coordinates in HARM are logically Cartesian -- that is, in some coordinate system, here dubbed "native" * coordinates, each cell is a rectangular prism of exactly the same shape as all the others. @@ -98,6 +100,8 @@ class CoordinateEmbedding { transform.emplace(mpark::get(transform_in)); } else if (mpark::holds_alternative(transform_in)) { transform.emplace(mpark::get(transform_in)); + } else if (mpark::holds_alternative(transform_in)) { + transform.emplace(mpark::get(transform_in)); } else if (mpark::holds_alternative(transform_in)) { transform.emplace(mpark::get(transform_in)); } else if (mpark::holds_alternative(transform_in)) { @@ -108,13 +112,73 @@ class CoordinateEmbedding { // Constructors #pragma hd_warning_disable CoordinateEmbedding() = default; +#pragma hd_warning_disable + CoordinateEmbedding(parthenon::ParameterInput* pin) { + const std::string base_str = pin->GetString("coordinates", "base"); + const std::string transform_str = pin->GetOrAddString("coordinates", "transform", "null"); + + // Parse names. See coordinate_systems.hpp for details + if (base_str == "spherical_minkowski") { + base.emplace(SphMinkowskiCoords()); + } else if (base_str == "cartesian_minkowski" || base_str == "minkowski") { + base.emplace(CartMinkowskiCoords()); + } else if (base_str == "spherical_ks" || base_str == "ks" || + base_str == "spherical_ks_extg" || base_str == "ks_extg") { + GReal a = pin->GetReal("coordinates", "a"); + bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); + if (ext_g || base_str == "spherical_ks_extg" || base_str == "ks_extg") { + if (a > 0) throw std::invalid_argument("Transform is for spherical coordinates!"); + base.emplace(SphKSExtG(a)); + } else { + base.emplace(SphKSCoords(a)); + } + } else if (base_str == "spherical_bl" || base_str == "bl" || + base_str == "spherical_bl_extg" || base_str == "bl_extg") { + GReal a = pin->GetReal("coordinates", "a"); + bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); + if (ext_g || base_str == "spherical_bl_extg" || base_str == "bl_extg") { + if (a > 0) throw std::invalid_argument("Transform is for spherical coordinates!"); + base.emplace(SphBLExtG(a)); + } else { + base.emplace(SphBLCoords(a)); + } + } else { + throw std::invalid_argument("Unsupported base coordinates!"); + } + + bool spherical = is_spherical(); + + if (transform_str == "null" || transform_str == "none") { + transform.emplace(NullTransform()); + } else if (transform_str == "exponential" || transform_str == "exp" || transform_str == "eks") { + if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); + transform.emplace(ExponentialTransform()); + } else if (transform_str == "superexponential" || transform_str == "superexp") { + if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); + GReal r_br = pin->GetOrAddReal("coordinates", "r_br", 1000.); + GReal npow = pin->GetOrAddReal("coordinates", "npow", 1.0); + GReal cpow = pin->GetOrAddReal("coordinates", "cpow", 4.0); + transform.emplace(SuperExponentialTransform(r_br, npow, cpow)); + } else if (transform_str == "modified" || transform_str == "mks") { + if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); + GReal hslope = pin->GetOrAddReal("coordinates", "hslope", 0.3); + transform.emplace(ModifyTransform(hslope)); + } else if (transform_str == "funky" || transform_str == "fmks") { + if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); + GReal hslope = pin->GetOrAddReal("coordinates", "hslope", 0.3); + GReal startx1 = pin->GetReal("parthenon/mesh", "x1min"); + GReal mks_smooth = pin->GetOrAddReal("coordinates", "mks_smooth", 0.5); + GReal poly_xt = pin->GetOrAddReal("coordinates", "poly_xt", 0.82); + GReal poly_alpha = pin->GetOrAddReal("coordinates", "poly_alpha", 14.0); + transform.emplace(FunkyTransform(startx1, hslope, mks_smooth, poly_xt, poly_alpha)); + } else { + throw std::invalid_argument("Unsupported coordinate transform!"); + } + } #pragma hd_warning_disable KOKKOS_FUNCTION CoordinateEmbedding(SomeBaseCoords& base_in, SomeTransform& transform_in): base(base_in), transform(transform_in) {} #pragma hd_warning_disable - KOKKOS_FUNCTION CoordinateEmbedding(const CoordinateEmbedding& src) - { - EmplaceSystems(src.base, src.transform); - } + KOKKOS_FUNCTION CoordinateEmbedding(const CoordinateEmbedding& src): base(src.base), transform(src.transform) {} #pragma hd_warning_disable KOKKOS_FUNCTION const CoordinateEmbedding& operator=(const CoordinateEmbedding& src) { @@ -123,53 +187,48 @@ class CoordinateEmbedding { } // Convenience functions to get common things - KOKKOS_INLINE_FUNCTION bool spherical() const + KOKKOS_INLINE_FUNCTION bool is_spherical() const { return mpark::visit( [&](const auto& self) { return self.spherical; }, base); } - // KOKKOS_INLINE_FUNCTION GReal rhor() const - // { - // return mpark::visit( [&](const auto& self) { - // self.rhor(); - // }, base); - // } - KOKKOS_INLINE_FUNCTION GReal get_a() const + KOKKOS_INLINE_FUNCTION GReal get_horizon() const { - if (mpark::holds_alternative(base)) { - return mpark::get(base).a; - } else if (mpark::holds_alternative(base)) { - return mpark::get(base).a; + if (mpark::holds_alternative(base) || + mpark::holds_alternative(base)) { + const GReal a = get_a(); + return 1 + m::sqrt(1 - a * a); } else { - return 0.0; //throw std::invalid_argument("BH Spin is not defined for selected coordinate system!"); + return 0.0; } } - KOKKOS_INLINE_FUNCTION bool is_ext_g() const + KOKKOS_INLINE_FUNCTION GReal get_a() const { - if (mpark::holds_alternative(base)) { - return mpark::get(base).ext_g; - } else if (mpark::holds_alternative(base)) { - return mpark::get(base).ext_g; - } else { - return 0.0; //throw std::invalid_argument("Ext_g is not defined for selected coordinate system!"); - } + return mpark::visit( [&](const auto& self) { + return self.a; + }, base); } + GReal startx(int dir) const + { + return mpark::visit( [&](const auto& self) { + return self.startx[dir - 1]; + }, transform); + } + GReal stopx(int dir) const + { + return mpark::visit( [&](const auto& self) { + return self.stopx[dir - 1]; + }, transform); + } + KOKKOS_INLINE_FUNCTION bool is_ks() const { - if (mpark::holds_alternative(base)) { - return true; - } else { - return false; - } + return mpark::holds_alternative(base); } KOKKOS_INLINE_FUNCTION bool is_cart_minkowski() const { - if (mpark::holds_alternative(base) && mpark::holds_alternative(transform)) { - return true; - } else { - return false; - } + return mpark::holds_alternative(base) && mpark::holds_alternative(transform); } // Spell out the interface we take from BaseCoords @@ -206,6 +265,26 @@ class CoordinateEmbedding { }, transform); } + // Convenience functions: only radial coordinate as others might be cylinderized + KOKKOS_INLINE_FUNCTION GReal r_to_native(const GReal r) const + { + const GReal Xembed[GR_DIM] = {0., r, 0., 0.}; + GReal Xnative[GR_DIM]; + mpark::visit( [&Xembed, &Xnative](const auto& self) { + self.coord_to_native(Xembed, Xnative); + }, transform); + return Xnative[1]; + } + KOKKOS_INLINE_FUNCTION GReal X1_to_embed(const GReal X1) const + { + const GReal Xnative[GR_DIM] = {0., X1, 0., 0.}; + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + return Xembed[1]; + } + // VECTOR TRANSFORMS // Contravariant vectors: KOKKOS_INLINE_FUNCTION void con_vec_to_embed(const GReal Xnative[GR_DIM], const GReal vcon_native[GR_DIM], GReal vcon_embed[GR_DIM]) const @@ -350,4 +429,41 @@ class CoordinateEmbedding { } } } + + /** + * Takes a velocity in Boyer-Lindquist coordinates (optionally without time component) and converts it + * to KS, and then to native coordinates. + * Not guaranteed to be fast. + */ + KOKKOS_INLINE_FUNCTION void bl_fourvel_to_native(const Real Xnative[GR_DIM], const Real ucon_bl[GR_DIM], Real ucon_native[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + coord_to_embed(Xnative, Xembed); + + // Set u^t to make u a velocity 4-vector in BL + Real gcov_bl[GR_DIM][GR_DIM]; + if (mpark::holds_alternative(base) || + mpark::holds_alternative(base)) { + SphBLCoords(get_a()).gcov_embed(Xembed, gcov_bl); + } else if (mpark::holds_alternative(base) || + mpark::holds_alternative(base)) { + SphBLExtG(get_a()).gcov_embed(Xembed, gcov_bl); + } + GReal ucon_bl_fourv[GR_DIM]; + DLOOP1 ucon_bl_fourv[mu] = ucon_bl[mu]; + set_ut(gcov_bl, ucon_bl_fourv); + + // Then transform that 4-vector to KS (or not, if we're using BL base coords) + Real ucon_base[GR_DIM]; + if (mpark::holds_alternative(base)) { + mpark::get(base).vec_from_bl(Xembed, ucon_bl_fourv, ucon_base); + } else if (mpark::holds_alternative(base)) { + mpark::get(base).vec_from_bl(Xembed, ucon_bl_fourv, ucon_base); + } else if (mpark::holds_alternative(base) || + mpark::holds_alternative(base)) { + DLOOP1 ucon_base[mu] = ucon_bl_fourv[mu]; + } + // Finally, apply any transform to native coordinates + con_vec_to_native(Xnative, ucon_base, ucon_native); + } }; diff --git a/kharma/coordinates/coordinate_systems.hpp b/kharma/coordinates/coordinate_systems.hpp index a7ccae18..216c0f94 100644 --- a/kharma/coordinates/coordinate_systems.hpp +++ b/kharma/coordinates/coordinate_systems.hpp @@ -58,7 +58,7 @@ * * TODO Cartesian KS base * TODO snake coordinate transform for Cartesian Minkowski - * TODO CMKS, MKS3 transforms, proper Cartesian<->Spherical functions stolen from e.g. prob_common.hpp + * TODO CMKS, MKS3 transforms, proper Cartesian<->Spherical functions stolen from e.g. coordinate_utils.hpp * TODO overhaul the LEGACY_TH stuff * TODO currently avoids returning gcov which might be singular, * is this the correct play vs handling in inversions? @@ -76,7 +76,8 @@ */ class CartMinkowskiCoords { public: - const bool spherical = false; + static constexpr bool spherical = false; + static constexpr GReal a = 0.0; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { DLOOP2 gcov[mu][nu] = (mu == nu) - 2*(mu == 0 && nu == 0); @@ -88,18 +89,19 @@ class CartMinkowskiCoords { */ class SphMinkowskiCoords { public: - const bool spherical = true; + static constexpr bool spherical = true; + static constexpr GReal a = 0.0; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { const GReal r = m::max(Xembed[1], SMALL); const GReal th = excise(excise(Xembed[2], 0.0, SMALL), M_PI, SMALL); - const GReal sth = sin(th); + const GReal sth = m::sin(th); gzero2(gcov); gcov[0][0] = 1.; gcov[1][1] = 1.; gcov[2][2] = r*r; - gcov[3][3] = m::pow(sth*r, 2); + gcov[3][3] = sth*sth*r*r; } }; @@ -110,24 +112,19 @@ class SphKSCoords { public: // BH Spin is a property of KS const GReal a; - const bool spherical = true; - const bool ext_g; // added by Hyerin (02/27/23) + static constexpr bool spherical = true; - KOKKOS_FUNCTION SphKSCoords(GReal spin, bool external_gravity): a(spin), ext_g(external_gravity) {}; + KOKKOS_FUNCTION SphKSCoords(GReal spin): a(spin) {}; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { const GReal r = Xembed[1]; const GReal th = excise(excise(Xembed[2], 0.0, SMALL), M_PI, SMALL); - const GReal cos2 = m::pow(cos(th), 2); - const GReal sin2 = m::pow(sin(th), 2); - const GReal rho2 = r*r + a*a*cos2; - - // (Hyerin 11/13/22) test - const GReal A = 1.46797639*m::pow(10.,-8); - const GReal B = 1.29411117; - const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); + const GReal cth = m::cos(th); + const GReal sth = m::sin(th); + const GReal sin2 = sth*sth; + const GReal rho2 = r*r + a*a*cth*cth; gcov[0][0] = -1. + 2.*r/rho2; gcov[0][1] = 2.*r/rho2; @@ -148,19 +145,9 @@ class SphKSCoords { gcov[3][1] = -a*sin2*(1. + 2.*r/rho2); gcov[3][2] = 0.; gcov[3][3] = sin2*(rho2 + a*a*sin2*(1. + 2.*r/rho2)); - - // Hyerin TODO: add an error when spin != 0 - if (ext_g) { - if (a>0) printf("WARNING: External gravity is not compatible with nonzero spin! \n"); - gcov[0][0] -= 2. * Phi_g; - gcov[0][1] -= 2. * Phi_g; - gcov[1][0] -= 2. * Phi_g; - gcov[1][1] -= 2. * Phi_g; - } } // For converting from BL - // TODO will we ever need a from_ks? KOKKOS_INLINE_FUNCTION void vec_from_bl(const GReal Xembed[GR_DIM], const Real vcon_bl[GR_DIM], Real vcon[GR_DIM]) const { GReal r = Xembed[1]; @@ -169,15 +156,6 @@ class SphKSCoords { trans[0][1] = 2.*r/(r*r - 2.*r + a*a); trans[3][1] = a/(r*r - 2.*r + a*a); - // external gravity from GIZMO - const GReal A = 1.46797639*m::pow(10.,-8); - const GReal B = 1.29411117; - const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); - - if (ext_g) { - trans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); - } - gzero(vcon); DLOOP2 vcon[mu] += trans[mu][nu]*vcon_bl[nu]; } @@ -190,24 +168,93 @@ class SphKSCoords { rtrans[0][1] = 2.*r/(r*r - 2.*r + a*a); rtrans[3][1] = a/(r*r - 2.*r + a*a); + invert(&rtrans[0][0], &trans[0][0]); + + gzero(vcon); + DLOOP2 vcon[mu] += trans[mu][nu]*vcon_bl[nu]; + } +}; + +/** + * Spherical Kerr-Schild coordinates w/ external gravity term + */ +class SphKSExtG { + public: + // BH Spin is a property of KS + const GReal a; + static constexpr bool spherical = true; + + static constexpr GReal A = 1.46797639e-8; + static constexpr GReal B = 1.29411117; + + KOKKOS_FUNCTION SphKSExtG(GReal spin): a(spin) {}; + + KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const + { + const GReal r = Xembed[1]; + const GReal th = excise(excise(Xembed[2], 0.0, SMALL), M_PI, SMALL); + + const GReal cth = m::cos(th); + const GReal sth = m::sin(th); + const GReal sin2 = sth*sth; + const GReal rho2 = r*r + a*a*cth*cth; + + const GReal Phi_g = (A / (B - 1.)) * (m::pow(r, B-1.) - m::pow(2, B-1.)); + + gcov[0][0] = -1. + 2.*r/rho2 - 2. * Phi_g; + gcov[0][1] = 2.*r/rho2 - 2. * Phi_g; + gcov[0][2] = 0.; + gcov[0][3] = -2.*a*r*sin2/rho2; + + gcov[1][0] = 2.*r/rho2 - 2. * Phi_g; + gcov[1][1] = 1. + 2.*r/rho2 - 2. * Phi_g; + gcov[1][2] = 0.; + gcov[1][3] = -a*sin2*(1. + 2.*r/rho2); + + gcov[2][0] = 0.; + gcov[2][1] = 0.; + gcov[2][2] = rho2; + gcov[2][3] = 0.; + + gcov[3][0] = -2.*a*r*sin2/rho2; + gcov[3][1] = -a*sin2*(1. + 2.*r/rho2); + gcov[3][2] = 0.; + gcov[3][3] = sin2*(rho2 + a*a*sin2*(1. + 2.*r/rho2)); + } + + // For converting from BL + // TODO will we ever need a from_ks? + KOKKOS_INLINE_FUNCTION void vec_from_bl(const GReal Xembed[GR_DIM], const Real vcon_bl[GR_DIM], Real vcon[GR_DIM]) const + { + GReal r = Xembed[1]; + Real trans[GR_DIM][GR_DIM]; + DLOOP2 trans[mu][nu] = (mu == nu); + // external gravity from GIZMO - const GReal A = 1.46797639*m::pow(10.,-8); - const GReal B = 1.29411117; const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); - - if (ext_g) { - rtrans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); - } - invert(&rtrans[0][0], &trans[0][0]); + + trans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); + trans[3][1] = a/(r*r - 2.*r + a*a); gzero(vcon); DLOOP2 vcon[mu] += trans[mu][nu]*vcon_bl[nu]; } - // TODO more: isco etc? - KOKKOS_INLINE_FUNCTION GReal rhor() const + KOKKOS_INLINE_FUNCTION void vec_to_bl(const GReal Xembed[GR_DIM], const Real vcon_bl[GR_DIM], Real vcon[GR_DIM]) const { - return (1. + m::sqrt(1. - a*a)); + GReal r = Xembed[1]; + GReal rtrans[GR_DIM][GR_DIM], trans[GR_DIM][GR_DIM]; + DLOOP2 rtrans[mu][nu] = (mu == nu); + + const GReal Phi_g = (A / (B-1.)) * (m::pow(r, B-1.) - m::pow(2, B-1.)); + + rtrans[0][1] = (2./r - 2.*Phi_g)/(1. - 2./r + 2.*Phi_g); + rtrans[3][1] = a/(r*r - 2.*r + a*a); + + invert(&rtrans[0][0], &trans[0][0]); + + gzero(vcon); + DLOOP2 vcon[mu] += trans[mu][nu]*vcon_bl[nu]; } }; @@ -218,49 +265,69 @@ class SphBLCoords { public: // BH Spin is a property of BL const GReal a; - const bool spherical = true; - const bool ext_g; // added by Hyerin (11/13/22) + static constexpr bool spherical = true; - KOKKOS_FUNCTION SphBLCoords(GReal spin, bool external_gravity): a(spin), ext_g(external_gravity) {} + KOKKOS_FUNCTION SphBLCoords(GReal spin): a(spin) {} KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { const GReal r = Xembed[1]; const GReal th = excise(excise(Xembed[2], 0.0, SMALL), M_PI, SMALL); - const GReal cth = cos(th), sth = sin(th); + const GReal cth = m::cos(th), sth = m::sin(th); - const GReal s2 = sth*sth; + const GReal sin2 = sth*sth; const GReal a2 = a*a; const GReal r2 = r*r; - // TODO this and gcov_embed for KS should look more similar... + // TODO(BSP) this and gcov_embed for KS should look more similar... const GReal mmu = 1. + a2*cth*cth/r2; // mu is taken as an index - // (Hyerin 11/13/22) test - const GReal A = 1.46797639*m::pow(10.,-8); - const GReal B = 1.29411117; - const GReal Phi_g = (A/(B-1.)) * (m::pow(r,B-1.)-m::pow(2,B-1.)); - gzero2(gcov); gcov[0][0] = -(1. - 2./(r*mmu)); - gcov[0][3] = -2.*a*s2/(r*mmu); + gcov[0][3] = -2.*a*sin2/(r*mmu); gcov[1][1] = mmu/(1. - 2./r + a2/r2); gcov[2][2] = r2*mmu; - gcov[3][0] = -2.*a*s2/(r*mmu); - gcov[3][3] = s2*(r2 + a2 + 2.*a2*s2/(r*mmu)); - - // Hyerin TODO: add an error when spin != 0 - if (ext_g) { - if (a>0) printf("WARNING: External gravity is not compatible with nonzero spin! \n"); - gcov[0][0] -= 2. * Phi_g; - gcov[1][1] *= (1. - 2./r + a2/r2) / (1. - 2./r + 2.*Phi_g); - } + gcov[3][0] = -2.*a*sin2/(r*mmu); + gcov[3][3] = sin2*(r2 + a2 + 2.*a2*sin2/(r*mmu)); } - // TODO vec to/from ks, put guaranteed ks/bl fns into embedding + // TODO(BSP) vec to/from ks, put guaranteed ks/bl fns into embedding - KOKKOS_INLINE_FUNCTION GReal rhor() const +}; + +/** + * Boyer-Lindquist coordinates as an embedding system + */ +class SphBLExtG { + public: + // BH Spin is a property of BL + const GReal a; + static constexpr bool spherical = true; + + static constexpr GReal A = 1.46797639e-8; + static constexpr GReal B = 1.29411117; + + KOKKOS_FUNCTION SphBLExtG(GReal spin): a(spin) {} + + KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const { - return (1. + m::sqrt(1. - a*a)); + const GReal r = Xembed[1]; + const GReal th = excise(excise(Xembed[2], 0.0, SMALL), M_PI, SMALL); + const GReal cth = m::cos(th), sth = m::sin(th); + + const GReal sin2 = sth*sth; + const GReal a2 = a*a; + const GReal r2 = r*r; + const GReal mmu = 1. + a2*cth*cth/r2; // mu is taken as an index + + const GReal Phi_g = (A / (B-1.)) * (m::pow(r, B-1.) - m::pow(2, B-1.)); + + gzero2(gcov); + gcov[0][0] = -(1. - 2./(r*mmu)) - 2. * Phi_g;; + gcov[0][3] = -2.*a*sin2/(r*mmu); + gcov[1][1] = mmu / (1. - 2./r + 2.*Phi_g); + gcov[2][2] = r2*mmu; + gcov[3][0] = -2.*a*sin2/(r*mmu); + gcov[3][3] = sin2*(r2 + a2 + 2.*a2*sin2/(r*mmu)); } }; @@ -277,6 +344,8 @@ class SphBLCoords { */ class NullTransform { public: + static constexpr GReal startx[3] = {-1, -1, -1}; + static constexpr GReal stopx[3] = {-1, -1, -1}; // Coordinate transformations // Any coordinate value protections (th < 0, th > pi, phi > 2pi) should be in the base system KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const @@ -304,6 +373,9 @@ class NullTransform { */ class ExponentialTransform { public: + static constexpr GReal startx[3] = {-1, 0., 0.}; + static constexpr GReal stopx[3] = {-1, M_PI, 2*M_PI}; + // Coordinate transformations KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const { @@ -319,7 +391,7 @@ class ExponentialTransform { KOKKOS_INLINE_FUNCTION void coord_to_native(const GReal Xembed[GR_DIM], GReal Xnative[GR_DIM]) const { Xnative[0] = Xembed[0]; - Xnative[1] = log(Xembed[1]); + Xnative[1] = m::log(Xembed[1]); Xnative[2] = Xembed[2]; Xnative[3] = Xembed[3]; } @@ -347,12 +419,80 @@ class ExponentialTransform { } }; +/** + * SuperExponential coordinates, for super simulations + * Implementation follows HARMPI described in Tchekhovskoy+ + */ +class SuperExponentialTransform { + public: + static constexpr GReal startx[3] = {-1, 0., 0.}; + static constexpr GReal stopx[3] = {-1, M_PI, 2*M_PI}; + + const GReal xe1br, xn1br; + const double npow2, cpow2; + + // Constructor + KOKKOS_FUNCTION SuperExponentialTransform(GReal xe1br_in, double npow2_in, double cpow2_in): + xe1br(xe1br_in), npow2(npow2_in), cpow2(cpow2_in), xn1br(m::log(xe1br_in)) {} + + // Coordinate transformations + KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const + { + Xembed[0] = Xnative[0]; + const GReal super_dist = Xnative[1] - xn1br; + Xembed[1] = m::exp(Xnative[1] + (super_dist > 0) * cpow2 * m::pow(super_dist, npow2)); +#if LEGACY_TH + Xembed[2] = excise(excise(Xnative[2], 0.0, SMALL), M_PI, SMALL); +#else + Xembed[2] = Xnative[2]; +#endif + Xembed[3] = Xnative[3]; + } + KOKKOS_INLINE_FUNCTION void coord_to_native(const GReal Xembed[GR_DIM], GReal Xnative[GR_DIM]) const + { + Xnative[0] = Xembed[0]; + Xnative[2] = Xembed[2]; + Xnative[3] = Xembed[3]; + // TODO can just take log for x1 < xe1br + ROOT_FIND_1 + } + /** + * Transformation matrix for contravariant vectors to embedding, or covariant vectors to native + */ + KOKKOS_INLINE_FUNCTION void dxdX(const GReal Xnative[GR_DIM], Real dxdX[GR_DIM][GR_DIM]) const + { + gzero2(dxdX); + dxdX[0][0] = 1.; + const GReal super_dist = Xnative[1] - xn1br; + dxdX[1][1] = m::exp(Xnative[1] + (super_dist > 0) * cpow2 * m::pow(super_dist, npow2)) + * (1 + cpow2 * npow2 * m::pow(super_dist, npow2-1)); + dxdX[2][2] = 1.; + dxdX[3][3] = 1.; + } + /** + * Transformation matrix for contravariant vectors to native, or covariant vectors to embedding + */ + KOKKOS_INLINE_FUNCTION void dXdx(const GReal Xnative[GR_DIM], Real dXdx[GR_DIM][GR_DIM]) const + { + gzero2(dXdx); + dXdx[0][0] = 1.; + const GReal super_dist = Xnative[1] - xn1br; + dXdx[1][1] = 1 / (m::exp(Xnative[1] + (super_dist > 0) * cpow2 * m::pow(super_dist, npow2)) + * (1 + cpow2 * npow2 * m::pow(super_dist, npow2-1))); + dXdx[2][2] = 1.; + dXdx[3][3] = 1.; + } +}; + /** * Modified Kerr-Schild coordinates "MKS" * Makes sense only for spherical base systems! */ class ModifyTransform { public: + static constexpr GReal startx[3] = {-1, 0., 0.}; + static constexpr GReal stopx[3] = {-1, 1., 2*M_PI}; + const GReal hslope; // Constructor @@ -364,17 +504,17 @@ class ModifyTransform { Xembed[0] = Xnative[0]; Xembed[1] = m::exp(Xnative[1]); #if LEGACY_TH - const GReal th = M_PI*Xnative[2] + ((1. - hslope)/2.)*sin(2.*M_PI*Xnative[2]); + const GReal th = M_PI*Xnative[2] + ((1. - hslope)/2.)*m::sin(2.*M_PI*Xnative[2]); Xembed[2] = excise(excise(th, 0.0, SMALL), M_PI, SMALL); #else - Xembed[2] = M_PI*Xnative[2] + ((1. - hslope)/2.)*sin(2.*M_PI*Xnative[2]); + Xembed[2] = M_PI*Xnative[2] + ((1. - hslope)/2.)*m::sin(2.*M_PI*Xnative[2]); #endif Xembed[3] = Xnative[3]; } KOKKOS_INLINE_FUNCTION void coord_to_native(const GReal Xembed[GR_DIM], GReal Xnative[GR_DIM]) const { Xnative[0] = Xembed[0]; - Xnative[1] = log(Xembed[1]); + Xnative[1] = m::log(Xembed[1]); Xnative[3] = Xembed[3]; // Treat the special case with a large macro ROOT_FIND @@ -387,7 +527,7 @@ class ModifyTransform { gzero2(dxdX); dxdX[0][0] = 1.; dxdX[1][1] = m::exp(Xnative[1]); - dxdX[2][2] = M_PI - (hslope - 1.)*M_PI*cos(2.*M_PI*Xnative[2]); + dxdX[2][2] = M_PI - (hslope - 1.)*M_PI*m::cos(2.*M_PI*Xnative[2]); dxdX[3][3] = 1.; } /** @@ -398,7 +538,7 @@ class ModifyTransform { gzero2(dXdx); dXdx[0][0] = 1.; dXdx[1][1] = 1 / m::exp(Xnative[1]); - dXdx[2][2] = 1 / (M_PI - (hslope - 1.)*M_PI*cos(2.*M_PI*Xnative[2])); + dXdx[2][2] = 1 / (M_PI - (hslope - 1.)*M_PI*m::cos(2.*M_PI*Xnative[2])); dXdx[3][3] = 1.; } }; @@ -409,16 +549,18 @@ class ModifyTransform { */ class FunkyTransform { public: + static constexpr GReal startx[3] = {-1, 0., 0.}; + static constexpr GReal stopx[3] = {-1, 1., 2*M_PI}; + const GReal startx1; const GReal hslope, poly_xt, poly_alpha, mks_smooth; - GReal poly_norm; // TODO make this const and use a wrapper/factory to make these things? + // Must be *defined* afterward to use constructor below + const GReal poly_norm; // Constructor KOKKOS_FUNCTION FunkyTransform(GReal startx1_in, GReal hslope_in, GReal mks_smooth_in, GReal poly_xt_in, GReal poly_alpha_in): - startx1(startx1_in), hslope(hslope_in), mks_smooth(mks_smooth_in), poly_xt(poly_xt_in), poly_alpha(poly_alpha_in) - { - poly_norm = 0.5 * M_PI * 1./(1. + 1./(poly_alpha + 1.) * 1./m::pow(poly_xt, poly_alpha)); - } + startx1(startx1_in), hslope(hslope_in), mks_smooth(mks_smooth_in), poly_xt(poly_xt_in), poly_alpha(poly_alpha_in), + poly_norm(0.5 * M_PI * 1./(1. + 1./(poly_alpha + 1.) * 1./m::pow(poly_xt, poly_alpha))) {} // Coordinate transformations KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const @@ -426,7 +568,7 @@ class FunkyTransform { Xembed[0] = Xnative[0]; Xembed[1] = m::exp(Xnative[1]); - const GReal thG = M_PI*Xnative[2] + ((1. - hslope)/2.)*sin(2.*M_PI*Xnative[2]); + const GReal thG = M_PI*Xnative[2] + ((1. - hslope)/2.)*m::sin(2.*M_PI*Xnative[2]); const GReal y = 2*Xnative[2] - 1.; const GReal thJ = poly_norm * y * (1. + m::pow(y/poly_xt,poly_alpha) / (poly_alpha + 1.)) + 0.5 * M_PI; #if LEGACY_TH @@ -440,7 +582,7 @@ class FunkyTransform { KOKKOS_INLINE_FUNCTION void coord_to_native(const GReal Xembed[GR_DIM], GReal Xnative[GR_DIM]) const { Xnative[0] = Xembed[0]; - Xnative[1] = log(Xembed[1]); + Xnative[1] = m::log(Xembed[1]); Xnative[3] = Xembed[3]; // Treat the special case with a macro ROOT_FIND @@ -461,8 +603,8 @@ class FunkyTransform { * (1 + (m::pow((-1. + 2 * Xnative[2]) / poly_xt, poly_alpha)) / (1 + poly_alpha)) - - 1. / 2. * (1. - hslope) * sin(2. * M_PI * Xnative[2])); - dxdX[2][2] = M_PI + (1. - hslope) * M_PI * cos(2. * M_PI * Xnative[2]) + - 1. / 2. * (1. - hslope) * m::sin(2. * M_PI * Xnative[2])); + dxdX[2][2] = M_PI + (1. - hslope) * M_PI * m::cos(2. * M_PI * Xnative[2]) + m::exp(mks_smooth * (startx1 - Xnative[1])) * (-M_PI + 2. * poly_norm @@ -472,7 +614,7 @@ class FunkyTransform { + (2. * poly_alpha * poly_norm * (2. * Xnative[2] - 1.) * m::pow((2. * Xnative[2] - 1.) / poly_xt, poly_alpha - 1.)) / ((1. + poly_alpha) * poly_xt) - - (1. - hslope) * M_PI * cos(2. * M_PI * Xnative[2])); + - (1. - hslope) * M_PI * m::cos(2. * M_PI * Xnative[2])); dxdX[3][3] = 1.; } /** @@ -488,6 +630,7 @@ class FunkyTransform { }; // Bundle coordinates and transforms into umbrella variant types -// Note nesting isn't allowed -- do it yourself by calling the steps if that's really important... -using SomeBaseCoords = mpark::variant; -using SomeTransform = mpark::variant; +// These act as a wannabe "interface" or "parent class" with the exception that access requires "mpark::visit" +// See coordinate_embedding.hpp +using SomeBaseCoords = mpark::variant; +using SomeTransform = mpark::variant; diff --git a/kharma/prob/prob_common.hpp b/kharma/coordinates/coordinate_utils.hpp similarity index 80% rename from kharma/prob/prob_common.hpp rename to kharma/coordinates/coordinate_utils.hpp index 57f895aa..98e6a54e 100644 --- a/kharma/prob/prob_common.hpp +++ b/kharma/coordinates/coordinate_utils.hpp @@ -1,5 +1,5 @@ /* - * File: prob_common.hpp + * File: coordinate_utils.hpp * * BSD 3-Clause License * @@ -163,28 +163,6 @@ KOKKOS_INLINE_FUNCTION void rotate_polar_vec(const GReal Xin[GR_DIM], const GRea } } -/** - * - */ -// KOKKOS_INLINE_FUNCTION void bl_fourv_to_native_prim(const Real Xembed[GR_DIM], const Real ucon_bl[GR_DIM], -// Real u_prim[GR_DIM]) -// { - -// Real gcov_bl[GR_DIM][GR_DIM]; -// bl.gcov_embed(Xembed, gcov_bl); -// set_ut(gcov_bl, ucon_bl); - -// // Then transform that 4-vector to KS, then to native -// Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; -// ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); -// cs.con_vec_to_native(Xnative, ucon_ks, ucon_mks); - -// // Convert native 4-vector to primitive u-twiddle, see Gammie '04 -// Real gcon[GR_DIM][GR_DIM]; -// G.gcon(Loci::center, j, i, gcon); -// fourvel_to_prim(gcon, ucon_mks, u_prim); -// } - /** * Set time component for a consistent 4-velocity given a 3-velocity */ @@ -221,26 +199,3 @@ KOKKOS_INLINE_FUNCTION void fourvel_to_prim(const Real gcon[GR_DIM][GR_DIM], con u_prim[1] = ucon[2] + ucon[0] * alpha2 * gcon[0][2]; u_prim[2] = ucon[3] + ucon[0] * alpha2 * gcon[0][3]; } - -KOKKOS_INLINE_FUNCTION void bl_fourvel_to_prim(const GRCoordinates& G, const CoordinateEmbedding& coords, - const SphBLCoords& bl, const SphKSCoords& ks, - const int& k, const int& j, const int& i, Real ucon_bl[GR_DIM], Real u_prim[NVEC]) -{ - GReal Xnative[GR_DIM], Xembed[GR_DIM]; // - G.coord(k, j, i, Loci::center, Xnative); - G.coord_embed(k, j, i, Loci::center, Xembed); - - // Set u^t to make u^r a 4-vector - Real gcov_bl[GR_DIM][GR_DIM]; - bl.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS, then to native - Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; - ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); - coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); - - Real gcon[GR_DIM][GR_DIM]; - G.gcon(Loci::center, j, i, gcon); //TODO: this causes the memory issue!! - fourvel_to_prim(gcon, ucon_mks, u_prim); -} \ No newline at end of file diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index 1859c28e..b1359873 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -48,13 +48,6 @@ using Kokkos::Rank; // Stepsize for numerical derivatives of the metric #define DELTA 1.e-8 -// Points to average (one side of a square, odd) when calculating the connections, -// and metric determinants on faces -#define CONN_AVG_POINTS 1 -// Whether to make corrections to some metric quantities to match -// metric determinant derivatives -#define CONN_CORRECTIONS 0 - #if FAST_CARTESIAN /** * Fast Cartesian GRCoordinates objects just use the underlying UniformCartesian object for everything @@ -63,80 +56,30 @@ GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): Uniform GRCoordinates::GRCoordinates(const GRCoordinates &src, int coarsen): UniformCartesian(src, coarsen) {} #else // Internal function for initializing cache -void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3); +void init_GRCoordinates(GRCoordinates& G); /** * Construct a GRCoordinates object with a transformation according to preferences set in the package */ -GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): UniformCartesian(rs, pin) +GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): UniformCartesian(rs, pin), + coords(pin) { - // TODO This is effectively a constructor for the CoordinateEmbedding object - // We should move it there so we can handle system names, synonyms & categories in one place - std::string base_str = pin->GetString("coordinates", "base"); // Require every problem to specify very basic geometry - std::string transform_str = pin->GetString("coordinates", "transform"); // This is guessed in kharma.cpp - - SomeBaseCoords base; - if (base_str == "spherical_minkowski") { - base.emplace(SphMinkowskiCoords()); - } else if (base_str == "cartesian_minkowski" || base_str == "minkowski") { - base.emplace(CartMinkowskiCoords()); - } else if (base_str == "spherical_ks" || base_str == "ks") { - GReal a = pin->GetReal("coordinates", "a"); - bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); //added by Hyerin - base.emplace(SphKSCoords(a, ext_g)); - } else if (base_str == "spherical_bl" || base_str == "bl") { - GReal a = pin->GetReal("coordinates", "a"); - bool ext_g = pin->GetOrAddBoolean("coordinates", "ext_g", false); //added by Hyerin - base.emplace(SphBLCoords(a, ext_g)); - } else { - throw std::invalid_argument("Unsupported base coordinates!"); - } - - bool spherical = mpark::visit( [&](const auto& self) { - return self.spherical; - }, base); - - SomeTransform transform; - if (transform_str == "null") { - transform.emplace(NullTransform()); - } else if (transform_str == "exponential" || transform_str == "exp" || transform_str == "eks") { - if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); - transform.emplace(ExponentialTransform()); - } else if (transform_str == "modified" || transform_str == "mks") { - if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); - GReal hslope = pin->GetOrAddReal("coordinates", "hslope", 0.3); - transform.emplace(ModifyTransform(hslope)); - } else if (transform_str == "funky" || transform_str == "fmks") { - if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); - GReal hslope = pin->GetOrAddReal("coordinates", "hslope", 0.3); - GReal startx1 = pin->GetReal("parthenon/mesh", "x1min"); - GReal mks_smooth = pin->GetOrAddReal("coordinates", "mks_smooth", 0.5); - GReal poly_xt = pin->GetOrAddReal("coordinates", "poly_xt", 0.82); - GReal poly_alpha = pin->GetOrAddReal("coordinates", "poly_alpha", 14.0); - transform.emplace(FunkyTransform(startx1, hslope, mks_smooth, poly_xt, poly_alpha)); - } else { - throw std::invalid_argument("Unsupported coordinate transform!"); - } - - coords = CoordinateEmbedding(base, transform); - n1 = rs.nx1 + 2*Globals::nghost; n2 = rs.nx2 > 1 ? rs.nx2 + 2*Globals::nghost : 1; n3 = rs.nx3 > 1 ? rs.nx3 + 2*Globals::nghost : 1; //cout << "Initialized coordinates with nghost " << Globals::nghost << std::endl; - init_GRCoordinates(*this, n1, n2, n3); + // TODO TODO set averaging/correcting prefs here + + init_GRCoordinates(*this); } -GRCoordinates::GRCoordinates(const GRCoordinates &src, int coarsen): UniformCartesian(src, coarsen) +GRCoordinates::GRCoordinates(const GRCoordinates &src, int coarsen): UniformCartesian(src, coarsen), + coords(src.coords), n1(src.n1/coarsen), n2(src.n2/coarsen), n3(src.n3/coarsen) { //std::cerr << "Calling coarsen constructor" << std::endl; - coords = src.coords; - n1 = src.n1/coarsen; - n2 = src.n2/coarsen; - n3 = src.n3/coarsen; - init_GRCoordinates(*this, n1, n2, n3); + init_GRCoordinates(*this); } /** @@ -147,7 +90,13 @@ GRCoordinates::GRCoordinates(const GRCoordinates &src, int coarsen): UniformCart * This needs to be defined *outside* of the GRCoordinates object, because of some * fun issues with C++ Lambda capture, which Kokkos brings to the fore */ -void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { +void init_GRCoordinates(GRCoordinates& G) { + const int n1 = G.n1; + const int n2 = G.n2; + const int n3 = G.n3; + const bool correct_connections = G.correct_connections; + const int connection_average_points = G.connection_average_points; + //cerr << "Creating GRCoordinate cache size " << n1 << " " << n2 << std::endl; // Cache geometry. May be faster than re-computing. May not be. G.gcon_direct = GeomTensor2("gcon", NLOC, n2+1, n1+1, GR_DIM, GR_DIM); @@ -172,9 +121,9 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { for (int iloc =0; iloc < NLOC; iloc++) { Loci loc = (Loci) iloc; // radius of points to sample, floor(npoints/2) - const int radius = CONN_AVG_POINTS / 2; - const int diameter = CONN_AVG_POINTS; - const int square = CONN_AVG_POINTS*CONN_AVG_POINTS; + const int radius = connection_average_points / 2; + const int diameter = connection_average_points; + const int square = connection_average_points*connection_average_points; if (loc == Loci::center || loc == Loci::face3) { // This prevents overstepping conn's bounds by halting in the last zone if (i >= n1 || j >= n2) continue; @@ -188,8 +137,8 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { GReal Xn1[GR_DIM], Xn2[GR_DIM]; G.coord(0, j, i+1, loc, Xn1); G.coord(0, j+1, i, loc, Xn2); - X[1] += (Xn1[1] - X[1])/CONN_AVG_POINTS * k; - X[2] += (Xn2[2] - X[2])/CONN_AVG_POINTS * l; + X[1] += (Xn1[1] - X[1])/connection_average_points * k; + X[2] += (Xn2[2] - X[2])/connection_average_points * l; // Get geometry at points GReal gcov_loc[GR_DIM][GR_DIM], gcon_loc[GR_DIM][GR_DIM]; G.coords.gcov_native(X, gcov_loc); @@ -233,7 +182,7 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { gcon_local(loc, j, i, mu, nu) += gcon_loc[mu][nu] / diameter; } } - } else { + } else { // corner // Just one point GReal X[GR_DIM]; G.coord(0, j, i, loc, X); @@ -251,7 +200,7 @@ void init_GRCoordinates(GRCoordinates& G, int n1, int n2, int n3) { } } ); - if (CONN_CORRECTIONS) { + if (correct_connections) { Kokkos::parallel_for("geom_corrections", MDRangePolicy>({0,0}, {n2, n1}), KOKKOS_LAMBDA (const int& j, const int& i) { // In the two directions the grid changes, make sure that we *exactly* diff --git a/kharma/coordinates/gr_coordinates.hpp b/kharma/coordinates/gr_coordinates.hpp index 4d1a6f4b..28ad6955 100644 --- a/kharma/coordinates/gr_coordinates.hpp +++ b/kharma/coordinates/gr_coordinates.hpp @@ -66,14 +66,23 @@ class GRCoordinates : public parthenon::UniformCartesian { public: - // Host-side coordinates object pointer + // Coordinate geometry & transform object: metric functions, to/from KS coordinates, etc. // Note we keep the actual object in GRCoordinates. This is a royal pain to implement, // but ensures it will get copied device-side by C++14 Lambdas, circumventing *so many* bugs CoordinateEmbedding coords; - // TODO try again to get these from parent always, e.g. with the RegionSize or len() + // Store the block size, since UniformCartesian doesn't int n1, n2, n3; - // And optionally some caches + + // Points to average (one side of a square, odd) when calculating the connections, + // and metric determinants on faces + int connection_average_points = 1; + + // Whether to "correct" the connection coefficients in order to satisfy + // metric determinant derivatives discretized at faces + bool correct_connections = false; + + // Caches for geometry values at zone centers/faces/etc #if !FAST_CARTESIAN && !NO_CACHE GeomTensor2 gcon_direct, gcov_direct; GeomScalar gdet_direct; @@ -90,21 +99,20 @@ class GRCoordinates : public parthenon::UniformCartesian // Interim & copy constructors so that Parthenon can use us like a UniformCartesian object, // that is, host- & device-side indiscriminately KOKKOS_FUNCTION GRCoordinates(): UniformCartesian() {}; - KOKKOS_FUNCTION GRCoordinates(const GRCoordinates &src): parthenon::UniformCartesian(src) + KOKKOS_FUNCTION GRCoordinates(const GRCoordinates &src): UniformCartesian(src), + n1(src.n1), n2(src.n2), n3(src.n3), coords(src.coords) { //std::cerr << "Calling copy constructor size " << src.n1 << " " << src.n2 << std::endl; - coords = src.coords; - n1 = src.n1; - n2 = src.n2; - n3 = src.n3; - #if !FAST_CARTESIAN && !NO_CACHE +#if !FAST_CARTESIAN && !NO_CACHE gcon_direct = src.gcon_direct; gcov_direct = src.gcov_direct; gdet_direct = src.gdet_direct; conn_direct = src.conn_direct; gdet_conn_direct = src.gdet_conn_direct; - #endif +#endif }; + + // TODO(BSP) eliminate calls to this from Parthenon, grid should be const KOKKOS_FUNCTION GRCoordinates operator=(const GRCoordinates& src) { //std::cerr << "Calling assignment operator size " << src.n1 << " " << src.n2 << std::endl; @@ -113,13 +121,13 @@ class GRCoordinates : public parthenon::UniformCartesian n1 = src.n1; n2 = src.n2; n3 = src.n3; - #if !FAST_CARTESIAN && !NO_CACHE +#if !FAST_CARTESIAN && !NO_CACHE gcon_direct = src.gcon_direct; gcov_direct = src.gcov_direct; gdet_direct = src.gdet_direct; conn_direct = src.conn_direct; gdet_conn_direct = src.gdet_conn_direct; - #endif +#endif return *this; }; diff --git a/kharma/coordinates/root_find.hpp b/kharma/coordinates/root_find.hpp index 06d75e39..63d9d94e 100644 --- a/kharma/coordinates/root_find.hpp +++ b/kharma/coordinates/root_find.hpp @@ -85,3 +85,37 @@ else Xb[2] = Xc[2];\ }\ Xnative[2] = Xc[2]; + +#define ROOT_FIND_1 \ + double r = Xembed[1];\ + double ra, rb, rc;\ +\ + double Xa[GR_DIM], Xb[GR_DIM], Xc[GR_DIM], Xtmp[GR_DIM];\ + Xa[2] = Xnative[2];\ + Xa[3] = Xnative[3];\ +\ + Xb[2] = Xa[2];\ + Xb[3] = Xa[3];\ + Xc[2] = Xa[2];\ + Xc[3] = Xa[3];\ +\ + Xa[1] = 0.;\ + Xb[1] = 100.;\ +\ + coord_to_embed(Xa, Xtmp); ra = Xtmp[1];\ + coord_to_embed(Xb, Xtmp); rb = Xtmp[1];\ +\ + if (m::abs(ra-r) < ROOTFIND_TOL) {\ + Xnative[1] = Xa[1]; return;\ + } else if (m::abs(rb-r) < ROOTFIND_TOL) {\ + Xnative[1] = Xb[1]; return;\ + }\ + for (int i = 0; i < 1000; i++) {\ + Xc[1] = 0.5 * (Xa[1] + Xb[1]);\ + coord_to_embed(Xc, Xtmp); rc = Xtmp[1];\ +\ + if (m::abs(rc - r) < ROOTFIND_TOL) break;\ + else if ((rc - r) * (rb - r) < 0.) Xa[1] = Xc[1];\ + else Xb[1] = Xc[1];\ + }\ + Xnative[1] = Xc[1]; \ No newline at end of file diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 87bcaee5..991b9067 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -64,7 +64,7 @@ namespace m = std; // Bare Parthenon defs // Anything more leads to circular deps from gr_coordinates.hpp -// TODO update (carefully), this was from very early Parthenon +#include "parameter_input.hpp" #include "parthenon_arrays.hpp" #include "parthenon_mpi.hpp" #include "globals.hpp" diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index aead10ca..a50bfba3 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -189,14 +189,14 @@ void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_ for (auto &pmb : block_list) { auto& rc = pmb->meshblock_data.Get(); - Flag("Block fill Conserved"); - Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); - if (apply_domain_bounds) { Flag("Block physical bounds"); // Physical boundary conditions parthenon::ApplyBoundaryConditions(rc); } + + Flag("Block fill Conserved"); + Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); } } else { // If we're syncing the conserved vars... diff --git a/kharma/floors/floors_functions.hpp b/kharma/floors/floors_functions.hpp index de9b6a9d..0ad4fde8 100644 --- a/kharma/floors/floors_functions.hpp +++ b/kharma/floors/floors_functions.hpp @@ -116,7 +116,7 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa // 1. Geometric hard floors, not based on fluid relationships Real rhoflr_geom, uflr_geom; bool use_ff, use_df; - if(G.coords.spherical()) { + if(G.coords.is_spherical()) { GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, loc, Xembed); GReal r = Xembed[1]; @@ -336,7 +336,7 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, co { // Apply only the geometric floors Real rhoflr_geom, uflr_geom; - if(G.coords.spherical()) { + if(G.coords.is_spherical()) { GReal Xembed[GR_DIM]; G.coord_embed(0, j, i, loc, Xembed); GReal r = Xembed[1]; @@ -377,7 +377,7 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, c { // Apply only the geometric floors Real rhoflr_geom, uflr_geom; - if(G.coords.spherical()) { + if(G.coords.is_spherical()) { GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, loc, Xembed); GReal r = Xembed[1]; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index eface98e..29134d62 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -157,109 +157,88 @@ void KHARMA::FixParameters(std::unique_ptr& pin) ReadKharmaRestartHeader(pin->GetString("resize_restart", "fname"), pin); } - // Then handle coordinate systems and boundaries! - std::string coordinate_base = pin->GetString("coordinates", "base"); - if (coordinate_base == "ks") coordinate_base = "spherical_ks"; - if (coordinate_base == "bl") coordinate_base = "spherical_bl"; - if (coordinate_base == "minkowski") coordinate_base = "cartesian_minkowski"; - std::string coordinate_transform = pin->GetOrAddString("coordinates", "transform", "null"); - if (coordinate_transform == "none") coordinate_transform = "null"; - if (coordinate_transform == "fmks") coordinate_transform = "funky"; - if (coordinate_transform == "mks") coordinate_transform = "modified"; - if (coordinate_transform == "exponential") coordinate_transform = "exp"; - if (coordinate_transform == "eks") coordinate_transform = "exp"; - // TODO any other synonyms - if (coordinate_base == "spherical_ks" || coordinate_base == "spherical_bl" || coordinate_base == "spherical_minkowski") { - pin->SetBoolean("coordinates", "spherical", true); - } else { - pin->SetBoolean("coordinates", "spherical", false); - } - - // Spherical systems can specify r_out and optionally r_in, - // instead of xNmin/max. - // Other systems must specify x1min/max directly in the mesh region - if (!pin->DoesParameterExist("parthenon/mesh", "x1min") || - !pin->DoesParameterExist("parthenon/mesh", "x1max")) { - // TODO ask our coordinates about this rather than assuming m::exp() - bool log_r = (coordinate_transform != "null"); - - // Outer radius is always specified - GReal Rout = pin->GetReal("coordinates", "r_out"); - GReal x1max = log_r ? log(Rout) : Rout; - pin->GetOrAddReal("parthenon/mesh", "x1max", x1max); - - if (coordinate_base == "spherical_ks" || coordinate_base == "spherical_bl") { - // Set inner radius if not specified - if (pin->DoesParameterExist("coordinates", "r_in")) { + // Construct a CoordinateEmbedding object. See coordinate_embedding.hpp for supported systems/tags + CoordinateEmbedding tmp_coords(pin.get()); + // Record whether we're in spherical as we'll need that + pin->SetBoolean("coordinates", "spherical", tmp_coords.is_spherical()); + + // Do a bunch of autodetection/setting in spherical coordinates + if (tmp_coords.is_spherical()) { + // Spherical systems can specify r_out and optionally r_in, + // instead of xNmin/max. + if (!pin->DoesParameterExist("parthenon/mesh", "x1min") || + !pin->DoesParameterExist("parthenon/mesh", "x1max")) { + // Outer radius is always specified + GReal Rout = pin->GetReal("coordinates", "r_out"); + GReal x1max = tmp_coords.r_to_native(Rout); + pin->GetOrAddReal("parthenon/mesh", "x1max", x1max); + + if (mpark::holds_alternative(tmp_coords.base)) { + // In Minkowski coordinates, require Rin so the singularity is at user option GReal Rin = pin->GetReal("coordinates", "r_in"); - GReal x1min = log_r ? log(Rin) : Rin; + GReal x1min = tmp_coords.r_to_native(Rin); pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); - if (Rin < 2.5){ // warn to check if there are 5 zones inside the event horizon - std::cout << "Hyerin: Rin = " << Rin << ". Check if there are 5 zones inside the EH." << std::endl; + } else { // Any spherical BH metric: KS, BL, and derivatives + // Set inner radius if not specified + if (pin->DoesParameterExist("coordinates", "r_in")) { + GReal Rin = pin->GetReal("coordinates", "r_in"); + GReal x1min = tmp_coords.r_to_native(Rin); + pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); + if (Rin < 2.5){ // warn if there are fewer than 5 zones inside the event horizon + GReal dx = (x1max - x1min) / pin->GetInteger("parthenon/mesh", "nx1"); + if (tmp_coords.X1_to_embed(x1min + 5*dx) > tmp_coords.get_horizon()) { + std::cerr << "WARNING: inner radius is near/in the EH, but does not allow 5 zones inside!" << std::endl; + } + } + } else { + int nx1 = pin->GetInteger("parthenon/mesh", "nx1"); + // Allow overriding Rhor for bondi_viscous problem + const GReal Rhor = pin->GetOrAddReal("coordinates", "Rhor", tmp_coords.get_horizon()); + const GReal x1hor = tmp_coords.r_to_native(Rhor); + + // Set Rin such that we have 5 zones completely inside the event horizon + // If xeh = log(Rhor), xin = log(Rin), and xout = log(Rout), + // then we want xeh = xin + 5.5 * (xout - xin) / N1TOT: + const GReal x1min = (nx1 * x1hor / 5.5 - x1max) / (-1. + nx1 / 5.5); + if (x1min < 0.0) { + throw std::invalid_argument("Not enough radial zones were specified to put 5 zones inside EH!"); + } + pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); } - } else { - int nx1 = pin->GetInteger("parthenon/mesh", "nx1"); - const Real a = pin->GetReal("coordinates", "a"); - // Allow overriding Rhor for bondi_viscous problem - const GReal Rhor = pin->GetOrAddReal("coordinates", "Rhor", 1 + sqrt(1 - a*a)); - const GReal x1hor = log_r ? log(Rhor) : Rhor; - - // Set Rin such that we have 5 zones completely inside the event horizon - // If xeh = log(Rhor), xin = log(Rin), and xout = log(Rout), - // then we want xeh = xin + 5.5 * (xout - xin) / N1TOT: - const GReal x1min = (nx1 * x1hor / 5.5 - x1max) / (-1. + nx1 / 5.5); - if (x1min < 0.0) { - throw std::invalid_argument("Not enough radial zones were specified to put 5 zones inside EH!"); - } - pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); - } - //cout << "Setting x1min: " << x1min << " x1max " << x1max << " based on BH with a=" << a << endl; + //cout << "Setting x1min: " << x1min << " x1max " << x1max << " based on BH with a=" << a << endl; - } else if (coordinate_base == "spherical_minkowski") { - // In Minkowski coordinates, require Rin so the singularity is at user option - GReal Rin = pin->GetReal("coordinates", "r_in"); - GReal x1min = log_r ? log(Rin) : Rin; - pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); + } } - } - // Assumption: if we're in a spherical system... - if (coordinate_base == "spherical_ks" || coordinate_base == "spherical_bl" || coordinate_base == "spherical_minkowski") { - // ...then we definitely want KHARMA's spherical boundary conditions - // These are inflow in x1 and reflecting in x2, but applied to *primitives* in - // a custom operation, see boundaries.cpp + // Spherical systems will also want KHARMA's spherical boundary conditions. + // By default, this means inflow in x1 and reflecting in x2, but can be chosen + // by *KHARMA* options (not here, since we certainly don't want periodic pole/radial bounds) pin->GetOrAddString("parthenon/mesh", "ix1_bc", "user"); pin->GetOrAddString("parthenon/mesh", "ox1_bc", "user"); pin->GetOrAddString("parthenon/mesh", "ix2_bc", "user"); pin->GetOrAddString("parthenon/mesh", "ox2_bc", "user"); pin->GetOrAddString("parthenon/mesh", "ix3_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ox3_bc", "periodic"); - - // We also know the bounds for most transforms in spherical coords - // Note we *only* set them here if they were not previously set/read! - if (coordinate_transform == "null" || coordinate_transform == "exp") { - pin->GetOrAddReal("parthenon/mesh", "x2min", 0.0); - pin->GetOrAddReal("parthenon/mesh", "x2max", M_PI); - pin->GetOrAddReal("parthenon/mesh", "x3min", 0.0); - pin->GetOrAddReal("parthenon/mesh", "x3max", 2*M_PI); - } else if (coordinate_transform == "modified" || coordinate_transform == "funky") { - pin->GetOrAddReal("parthenon/mesh", "x2min", 0.0); - pin->GetOrAddReal("parthenon/mesh", "x2max", 1.0); - pin->GetOrAddReal("parthenon/mesh", "x3min", 0.0); - pin->GetOrAddReal("parthenon/mesh", "x3max", 2*M_PI); - } // TODO any other transforms/systems } else { - // Most likely, Cartesian simulations will specify boundary conditions, - // but we set defaults here. + // We can set reasonable default boundary conditions for Cartesian sims, + // but not default domain bounds pin->GetOrAddString("parthenon/mesh", "ix1_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ox1_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ix2_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ox2_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ix3_bc", "periodic"); pin->GetOrAddString("parthenon/mesh", "ox3_bc", "periodic"); - // Cartesian sims must specify the domain! } + + // Set default bounds covering our coordinates/transform + for (int i = X1DIR; i <= X3DIR; i++) { + if (tmp_coords.startx(i) > 0) + pin->GetOrAddReal("parthenon/mesh", "x1min", tmp_coords.startx(i)); + if (tmp_coords.stopx(i) > 0) + pin->GetOrAddReal("parthenon/mesh", "x1max", tmp_coords.stopx(i)); + } + Flag("Fixed"); } diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 93e7c4ea..4da15f74 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -118,9 +118,6 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain // Just the X1 right boundary GRCoordinates G = pmb->coords; - SphKSCoords ks = mpark::get(G.coords.base); - SphBLCoords bl = SphBLCoords(ks.a, ks.ext_g); // modified - CoordinateEmbedding cs = G.coords; // Solution constants // These don't depend on which zone we're calculating @@ -172,21 +169,15 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain const Real ur = (zero_velocity) ? 0. : -C1 / (Tn * r * r); - // Set u^t to make u^r a 4-vector - Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; - Real gcov_bl[GR_DIM][GR_DIM]; - bl.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS, then to native - Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; - ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); - cs.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + // Get the native-coordinate 4-vector corresponding to ur + const Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; + Real ucon_native[GR_DIM]; + G.coords.bl_fourvel_to_native(Xnative, ucon_bl, ucon_native); // Convert native 4-vector to primitive u-twiddle, see Gammie '04 Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; G.gcon(Loci::center, j, i, gcon); - fourvel_to_prim(gcon, ucon_mks, u_prim); + fourvel_to_prim(gcon, ucon_native, u_prim); // Note that NaN guards, including these, are ignored (!) under -ffast-math flag. // Thus we stay away from initializing at EH where this could happen diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 443c281c..72107f0b 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -39,7 +39,7 @@ #include "flux_functions.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" #include "types.hpp" #include diff --git a/kharma/prob/bz_monopole.cpp b/kharma/prob/bz_monopole.cpp index 9e8a8a70..8d737866 100644 --- a/kharma/prob/bz_monopole.cpp +++ b/kharma/prob/bz_monopole.cpp @@ -34,7 +34,7 @@ #include "bz_monopole.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" #include "types.hpp" #include diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 8fd39b3b..094ffb91 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -35,7 +35,7 @@ #include "emhd/conducting_atmosphere.hpp" #include "boundaries.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" using namespace parthenon; diff --git a/kharma/prob/emhd/conducting_atmosphere.hpp b/kharma/prob/emhd/conducting_atmosphere.hpp index c9a75d16..f18265be 100644 --- a/kharma/prob/emhd/conducting_atmosphere.hpp +++ b/kharma/prob/emhd/conducting_atmosphere.hpp @@ -39,7 +39,7 @@ #include "flux_functions.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" #include "types.hpp" #include diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index 5d845b85..e7814294 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -35,7 +35,7 @@ #include "fm_torus.hpp" #include "floors.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" #include "types.hpp" #include @@ -72,11 +72,7 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter // Since we can't create a system and assign later, we just // rebuild copies of both based on the BH spin "a" const auto& G = pmb->coords; - const bool use_ks = G.coords.is_ks(); const GReal a = G.coords.get_a(); - const bool ext_g = G.coords.is_ext_g(); - const SphBLCoords blcoords = SphBLCoords(a, ext_g); - const SphKSCoords kscoords = SphKSCoords(a, ext_g); // Fishbone-Moncrief parameters Real l = lfish_calc(a, rmax); @@ -121,20 +117,10 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter Real ucon_bl[GR_DIM]; rotate_polar_vec(Xmidplane, ucon_tilt, -tilt, Xembed, ucon_bl); - Real gcov_bl[GR_DIM][GR_DIM]; - blcoords.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS if necessary, + // Then set u^t and transform the 4-vector to KS if necessary, // and then to native coordinates Real ucon_native[GR_DIM]; - if (use_ks) { - Real ucon_ks[GR_DIM]; - kscoords.vec_from_bl(Xembed, ucon_bl, ucon_ks); - G.coords.con_vec_to_native(Xnative, ucon_ks, ucon_native); - } else { - G.coords.con_vec_to_native(Xnative, ucon_bl, ucon_native); - } + G.coords.bl_fourvel_to_native(Xnative, ucon_bl, ucon_native); // Convert native 4-vector to primitive u-twiddle, see Gammie '04 Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; diff --git a/kharma/prob/gizmo.cpp b/kharma/prob/gizmo.cpp index 3796acd8..f83860fd 100644 --- a/kharma/prob/gizmo.cpp +++ b/kharma/prob/gizmo.cpp @@ -96,8 +96,6 @@ TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain // Just the X1 right boundary GRCoordinates G = pmb->coords; - SphKSCoords ks = mpark::get(G.coords.base); - SphBLCoords bl = SphBLCoords(ks.a, ks.ext_g); // modified CoordinateEmbedding cs = G.coords; // Solution constants @@ -167,7 +165,7 @@ TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain vacuum_rho = rho_device(i_sh)*(1.-del_sh)+rho_device(i_sh+1)*del_sh; vacuum_u_over_rho = (T_device(i_sh)*(1.-del_sh)+T_device(i_sh+1)*del_sh)/(gam-1.); - get_prim_gizmo_shell(G, cs, P, m_p, gam, bl, ks, rin_init, rs, vacuum_rho, vacuum_u_over_rho, + get_prim_gizmo_shell(G, cs, P, m_p, gam, rin_init, rs, vacuum_rho, vacuum_u_over_rho, r_device, rho_device, T_device, vr_device, length, k, j, i); } ); diff --git a/kharma/prob/gizmo.hpp b/kharma/prob/gizmo.hpp index 6e4aef1e..6f1cfb27 100644 --- a/kharma/prob/gizmo.hpp +++ b/kharma/prob/gizmo.hpp @@ -40,7 +40,7 @@ #include "flux_functions.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" -#include "prob_common.hpp" +#include "coordinate_utils.hpp" #include "types.hpp" #include @@ -87,7 +87,7 @@ KOKKOS_INLINE_FUNCTION void XtoindexGIZMO(const GReal XG[GR_DIM], * Note this assumes that there are ghost zones! */ KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, - const Real& gam, const SphBLCoords& bl, const SphKSCoords& ks, + const Real& gam, const Real rin_init, const Real rs, Real vacuum_rho, Real vacuum_u_over_rho, const GridScalar& rarr, const GridScalar& rhoarr, const GridScalar& Tarr, const GridScalar& vrarr, const int length, const int& k, const int& j, const int& i) @@ -111,13 +111,12 @@ KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const C // Use Bondi infall velocity Real rho, u; Real T = get_T(r, C1, C2, n, rs); - Real ur = -C1 / (pow(T, n) * pow(r, 2)); - Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; + Real ucon_bl[GR_DIM] = {0}; if (r < rin_init * 0.9){ // Vacuum values for interior rho = vacuum_rho; u = vacuum_rho * vacuum_u_over_rho; - ucon_bl[1] = ur; + ucon_bl[1] = -C1 / (pow(T, n) * pow(r, 2)); } else { // linear interpolation int itemp; GReal del; @@ -130,20 +129,14 @@ KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const C ucon_bl[1] = 0.; } - // Set u^t to make u^r a 4-vector - Real gcov_bl[GR_DIM][GR_DIM]; - bl.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS, then to native - Real ucon_ks[GR_DIM], ucon_mks[GR_DIM]; - ks.vec_from_bl(Xembed, ucon_bl, ucon_ks); - coords.con_vec_to_native(Xnative, ucon_ks, ucon_mks); + // Set u^t and transform to native coordinates + GReal ucon_native[GR_DIM]; + G.coords.bl_fourvel_to_native(Xnative, ucon_bl, ucon_native); // Convert native 4-vector to primitive u-twiddle, see Gammie '04 Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; G.gcon(Loci::center, j, i, gcon); - fourvel_to_prim(gcon, ucon_mks, u_prim); + fourvel_to_prim(gcon, ucon_native, u_prim); P(m_p.RHO, k, j, i) = rho; P(m_p.UU, k, j, i) = u; diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 9041477f..a94d8e64 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -267,6 +267,12 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMA::ResetGlobals(pin, pmesh); } + KHARMADriver::SyncAllBounds(md); + + auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); + auto pouts = std::make_unique(pmesh, pin, &tm); + pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); + // Clean the B field if we've introduced a divergence somewhere // Call this any time the package is loaded, all the // logic about parsing whether to clean is there diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 6083b11f..d75ffaa7 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -77,17 +77,6 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // Breakout to call the appropriate initialization function, // defined in accompanying headers. - - - // Hyerin - // save x1min, x_EH for boundary conditions in boundaries.cpp - const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); - const Real a = pin->GetReal("coordinates", "a"); - const GReal x_EH = log(1 + m::sqrt(1 - a*a)); // EH radius - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x1min"))) - pmb->packages.Get("GRMHD")->AddParam("x1min", x1min); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x_EH"))) - pmb->packages.Get("GRMHD")->AddParam("x_EH", x_EH); auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 1a41b516..706d1a0e 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -217,6 +217,7 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain if (include_B) B_Save = rc->Get("B_Save").data; auto& G = pmb->coords; + CoordinateEmbedding coords = G.coords; // Size/domain of the MeshBlock we're reading to int is, ie; @@ -232,7 +233,6 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain } int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - //IndexRange block = IndexRange{0, nb - 1}; const int n1tot = pmb->packages.Get("GRMHD")->Param("rnx1"); const int n2tot = pmb->packages.Get("GRMHD")->Param("rnx2"); @@ -297,20 +297,21 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain Real *x1_file = new double[length[0]*length[1]]; Real *x2_file = new double[length[0]*length[2]]; Real *x3_file = new double[length[0]*length[3]]; - //static hsize_t fdims[] = {length[0], length[3], length[2], length[1],1}; //outdated - static hsize_t fdims[] = {length[0], 1, length[3], length[2], length[1]}; + //static hsize_t fdims[] = {length[0], 1, length[3], length[2], length[1],1}; //outdated + static hsize_t fdims[] = {length[0], length[3], length[2], length[1]}; //static hsize_t fdims_vec[] = {length[0], length[3], length[2], length[1],3}; //outdated static hsize_t fdims_vec[] = {length[0], 3, length[3], length[2], length[1]}; static hsize_t fdims_x1[] = {length[0], length[1]}; static hsize_t fdims_x2[] = {length[0], length[2]}; static hsize_t fdims_x3[] = {length[0], length[3]}; - hsize_t fstart[] = {0, 0, 0, 0, 0}; + hsize_t fstart[] = {0, 0, 0, 0}; + hsize_t fstart_vec[] = {0, 0, 0, 0, 0}; hsize_t fstart_x[] = {0, 0}; - hdf5_read_array(rho_file, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); - hdf5_read_array(u_file, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); - hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - //if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_file, "cons.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + hdf5_read_array(rho_file, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(u_file, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_file, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); @@ -340,11 +341,11 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain if (fname_fill != "none") { // TODO: here I'm assuming fname and fname_fill has same dimensions, which is not always the case. hdf5_open(fname_fill.c_str()); hdf5_set_directory("/"); - hdf5_read_array(rho_filefill, "prims.rho", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); - hdf5_read_array(u_filefill, "prims.u", 5, fdims, fstart,fdims,fdims,fstart,H5T_IEEE_F64LE); - hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - //if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_filefill, "cons.B", 5, fdims_vec, fstart,fdims_vec,fdims_vec,fstart,H5T_IEEE_F64LE); + hdf5_read_array(rho_filefill, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(u_filefill, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_filefill, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec,H5T_IEEE_F64LE); hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); @@ -400,10 +401,6 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - SphKSCoords kscoord = mpark::get(G.coords.base); - SphBLCoords blcoord = SphBLCoords(kscoord.a, kscoord.ext_g); // modified (11/15/22) - CoordinateEmbedding coords = G.coords; - // Deep copy to device x1_f_device.DeepCopy(x1_f_host); @@ -427,10 +424,10 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain //} Kokkos::fence(); - // Host-side interpolate & copy into the mirror array + // Device-side interpolate & copy into the mirror array pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - get_prim_restart_kharma(G, coords, P, m_p, blcoord, kscoord, + get_prim_restart_kharma(G, coords, P, m_p, fx1min, fx1max, fnghost, should_fill, is_spherical, include_B, gam, rs, mdot, length, x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, @@ -438,12 +435,13 @@ TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain //if (pin->GetOrAddString("b_field", "type", "none") != "none") { // VLOOP B_host(v, k, j, i) = interp_scalar(G, X, startx, stopx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(B_file[v*block_sz])); //} - if (include_B) - get_B_restart_kharma(G, coords, P, m_p, blcoord, kscoord, + if (include_B) { + get_B_restart_kharma(G, P, m_p, fx1min, fx1max, should_fill, length, x1_f_device, x2_f_device, x3_f_device, B_f_device, x1_fill_device, x2_fill_device, x3_fill_device, B_fill_device, B_Save, k, j, i); + } } ); } diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 35161297..034eca75 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -73,7 +73,6 @@ KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], } KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, - const SphBLCoords& bl, const SphKSCoords& ks, const Real fx1min, const Real fx1max, const Real fnghost, const bool should_fill, const bool is_spherical, const bool include_B, const Real gam, const Real rs, const Real mdot, const hsize_t length[GR_DIM], const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, const GridVector& B, @@ -96,7 +95,8 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons Real C1 = uc * m::pow(rs, 2) * m::pow(Tc, n); Real C2 = m::pow(1. + (1. + n) * Tc, 2) * (1. - 2. * mdot / rs + m::pow(C1, 2) / (m::pow(rs, 4) * m::pow(Tc, 2 * n))); - GReal Xembed[GR_DIM]; + GReal Xnative[GR_DIM], Xembed[GR_DIM]; + G.coord(k, j, i, Loci::center, Xnative); G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1]; @@ -119,7 +119,13 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons Real ur = -C1 / (m::pow(T, n) * m::pow(r, 2)); Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; - bl_fourvel_to_prim(G,coords,bl,ks,k,j,i,ucon_bl,u_prim); + Real ucon_native[GR_DIM]; + coords.bl_fourvel_to_native(Xnative, ucon_bl, ucon_native); + + // Convert native 4-vector to primitive u-twiddle, see Gammie '04 + Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; + G.gcon(Loci::center, j, i, gcon); + fourvel_to_prim(gcon, ucon_native, u_prim); } // HyerinTODO: if fname_fill exists and smaller. @@ -146,8 +152,7 @@ KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, cons } -KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, - const SphBLCoords& bl, const SphKSCoords& ks, +KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, const Real fx1min, const Real fx1max, const bool should_fill, const hsize_t length[GR_DIM], const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridVector& B, diff --git a/machines/darwin.sh b/machines/darwin.sh index e6c0d8b9..74d882c4 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -14,34 +14,38 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then # Run ""./make.sh hdf5" to build it PREFIX_PATH="$SOURCE_DIR/external/hdf5" - # These are - if [[ "$ARGS" == *"arm-nv"* ]]; then - HOST_ARCH="ARMV81" - DEVICE_ARCH="AMPERE80" - module load nvhpc/22.7 cuda/11.7.0 + if [[ "$ARGS" == *"gcc12"* ]]; then + module load cuda/12.0.0 openmpi gcc/12.1.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + elif [[ "$ARGS" == *"gcc"* ]]; then + module load cuda openmpi gcc/10.2.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + else + module load nvhpc/23.3 cuda/11.7.0 C_NATIVE="nvc" CXX_NATIVE="nvc++" # New NVHPC doesn't like CUDA_HOME export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME + unset CUDA_HOME + fi + + # These are + if [[ "$ARGS" == *"arm-nv"* ]]; then + HOST_ARCH="ARMV81" + DEVICE_ARCH="AMPERE80" + MPI_NUM_PROCS=2 + MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=40" elif [[ "$ARGS" == *"ampere"* ]]; then HOST_ARCH="ZEN3" DEVICE_ARCH="AMPERE80" - module load nvhpc/22.7 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME + MPI_NUM_PROCS=2 + MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=4" elif [[ "$ARGS" == *"volta"* ]]; then HOST_ARCH="HSW" DEVICE_ARCH="VOLTA70" - module load nvhpc/22.7 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME + MPI_NUM_PROCS=1 else echo "No target arch specified: must list a target arch for Darwin" exit @@ -49,6 +53,4 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then # Runtime MPI_EXE="mpirun" - MPI_NUM_PROCS=2 - MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=8" fi diff --git a/tests/bclean/bondi_multizone_00000.par b/tests/bclean/bondi_multizone_00000.par new file mode 100755 index 00000000..4829e892 --- /dev/null +++ b/tests/bclean/bondi_multizone_00000.par @@ -0,0 +1,120 @@ +# Bondi flow problem +# Model a spherically symmetric, unmagnetized inflow +# Uses more MeshBlocks than necessary, for debugging + + +problem_id = bondi #gizmo_shell #bondi_shell #_multizone + + +# Full mesh size, no refinement +refinement = none +numlevel = 1 +nx1 = 64 #128 # +nx2 = 64 #128 # +nx3 = 64 #128 # nx3_mesh updated from run_kharma.sh + + +# Split into blocks mesh +# Don't bother with xN boundaries for spherical coordinate systems +# KHARMA will automatically place ~5 zones inside the EH +nx1 = 32 #64 # nx1_meshblock updated from run_kharma.sh +nx2 = 32 #64 # nx2_meshblock updated from run_kharma.sh +nx3 = 64 #128 #64 # nx3_meshblock updated from run_kharma.sh + + +base = ks +transform = fmks +mks_smooth = 0 +a = 0.0 # spin updated from run_kharma.sh +ext_g = true # updated from run_kharma.sh +hslope = 0.3 +r_out = 16777216 # updated from run_kharma.sh +r_in = 262144 # updated from run_kharma.sh +#nghost = 6 # test Hyerin (12/28/22) + + +tlim = 5289680481 # updated from run_kharma.sh +nlim = -1 # updated from run_kharma.sh +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 +implicit = false + + +mdot = 1.0 +rs = 316.22776601683796 # (1e2.5)#1000.0 #8.0 #300.0 # +vacuum_logrho= -8.2014518 #-9.6983 #-10 #-5 +vacuum_log_u_over_rho = -5.2915149 # updated from run_kharma.sh +r_shell = 8388608 # updated from run_kharma.sh +use_gizmo = false + + +datfn = /n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/data/gizmo/first_test/dat.txt + + +fname = /n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/data/bondi_multizone_021623_bondi_b_clean/bondi_multizone_00000/bondi.out1.final.rhdf # updated from run_kharma.sh +fname_fill = none #, updated from run_kharma.sh +use_dt = false +base = 8 +nzone = 7 + +# Disable floors + +disable_floors = true +rho_min_geom = 1.0e-12 +u_min_geom = 1.0e-15 + +# We'll be adding material, and that's okay + +check_inflow_outer = false +check_inflow_inner = false # Hyerin test (12/22/22) +#fix_flux_pole = 0 # Hyerin test (12/22/22) + + +u_jitter=0 + + +type = vertical # b_field_type updated from run_kharma.sh +solver = none # b_field_solver updated from run_kharma.sh +#norm = true # Hyerin (12/29/22) this increases divB in the boundaries, so won't use here +#beta_min=1000 # Hyerin (12/29/22) +bz = 1e-4 #1e-6 +#fix_polar_flux = 0 # Hyerin test (12/22/22) +fix_flux_x1 = 0 #1 # Hyerin test (02/16/23) +initial_cleanup = true # updated from run_kharma.sh + + +rel_tolerance = 1.e-8 +always_solve = true + + +verbose = 1 + + +type = imex ## +two_sync = 1 + + +max_nonlinear_iter = 3 + + +file_type = hdf5 +dt = 528968040 # output0_dt updated from run_kharma.sh +single_precision_output = true #false +variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag #G.gcov +ghost_zones = true + + +file_type = rst +dt = 2644840240 # output1_dt updated from run_kharma.sh +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.B #, prims.B #, cons.rho, cons.u, cons.uvec +ghost_zones = true + + +file_type = hst +dt = 52896800 # output2_dt updated from run_kharma.sh + diff --git a/tests/bclean/run.sh b/tests/bclean/run.sh index 2c2f9fc6..ce2ebdcb 100755 --- a/tests/bclean/run.sh +++ b/tests/bclean/run.sh @@ -9,15 +9,15 @@ bz=5e-3 DIM=3 NZONES=2 #7 BASE=8 -NRUNS=100 +NRUNS=2 START_RUN=0 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from -DRTAG="bondi_multizone_031123_bclean_${bz}_flr_test" +DRTAG="." # Set paths -PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory -DR="${PDR}data/${DRTAG}" -parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file -#parfilename="${PDR}/sane_save.par" # parameter file +PDR="." ## parent directory +DR="." +parfilename="./bondi_multizone_00000.par" # parameter file +KHARMA_DIR=../.. # other values determined automatically turn_around=$(($NZONES-1)) @@ -28,20 +28,18 @@ r_out=$((${BASE}**($turn_around+2))) r_in=$((${BASE}**$turn_around)) # if the directories are not present, make them. -if [ ! -d "${DR}" ]; then - mkdir "${DR}" -fi -if [ ! -d "${PDR}logs/${DRTAG}" ]; then - mkdir "${PDR}logs/${DRTAG}" -fi +mkdir -p "${DR}" +mkdir -p "${PDR}/logs/${DRTAG}" ### Start running zone by zone for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) do args=() echo "${DRTAG}: iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" - logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO - runtime=`echo "scale=0; e($logruntime)+1" | bc -l` + #logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO + #runtime=`echo "scale=0; e($logruntime)+1" | bc -l` + runtime=10 + echo "Running for: " $runtime log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) start_time=$(($start_time+$runtime)) @@ -50,7 +48,7 @@ do # set problem type and cleanup if [ $VAR -eq 0 ]; then prob="bondi" #"torus" # - init_c=0 + init_c=1 else prob="resize_restart_kharma" init_c=1 @@ -64,10 +62,10 @@ do fi # output time steps - output0_dt=$((${runtime}/100*10)) + output0_dt=$((${runtime}/10)) #output1_dt=$((${runtime}/20*10)) - output1_dt=$((${runtime}/50*10)) - output2_dt=$((${runtime}/1000*10)) + output1_dt=$((${runtime}/5)) + output2_dt=$((${runtime}/10)) # dt, fname, fname_fill if [ $VAR -ne 0 ]; then @@ -75,12 +73,14 @@ do tag=($( tail -n 10 ${PDR}/logs/${DRTAG}/log_multizone$(printf %05d $((${VAR}-1)))_out )) dt=$(printf "%.18g" "${tag[2]:3}") # previous dt dt_new=$(echo "scale=14; $dt*sqrt($BASE^(-3*$out_to_in))/4" | bc -l) # new dt ## TODO: r^3/2 + echo "dt: $dt dt_new: $dt_new" if (( $(echo "$dt_new > 0.00001" |bc -l) )); then dt_new=$dt_new else dt_new=0.00001 fi fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" + echo "Restarting from directory $fname_dir" fname=$(find ${fname_dir} -type f -iname "*final.rhdf") if [ $VAR -ge $NZONES ]; then fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) @@ -89,6 +89,7 @@ do else fname_fill="none" fi + echo "Restarting with $fname, filling using $fname_fill" args+=(" resize_restart/fname=$fname parthenon/time/dt_min=$dt_new") args+=(" resize_restart/fname_fill=$fname_fill ") else @@ -101,28 +102,29 @@ do out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ - parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ - parthenon/meshblock/nx1=16 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ - parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time} \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/hslope=1 coordinates/transform=mks \ - bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ - floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 \ - b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ - b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ - b_cleanup/rel_tolerance=1.e-8 \ - resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ - parthenon/output0/dt=$output0_dt \ - parthenon/output1/dt=$output1_dt \ - parthenon/output2/dt=$output2_dt \ - ${args[@]} \ - -d ${data_dir} 1> ${out_fn} 2>${err_fn} - # nlim=10000 for 1e-3 - # floors/u_over_rho_max=2 - #b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ - #coordinates/transform=mks coordinates/hslope=1 \ this, for some reason does not work for b cleaning? + $KHARMA_DIR/run.sh -n 1 -i ${parfilename} \ + parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ + parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ + parthenon/job/problem_id=$prob \ + parthenon/time/tlim=${start_time} \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/hslope=1 coordinates/transform=mks \ + bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ + floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 \ + b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ + b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ + b_cleanup/rel_tolerance=1.e-8 \ + resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ + parthenon/output0/dt=$output0_dt \ + parthenon/output1/dt=$output1_dt \ + parthenon/output2/dt=$output2_dt \ + ${args[@]} \ + -d ${data_dir} 1> ${out_fn} 2>${err_fn} + # kharma/b_flux_ct/seed_B_ct.cpp + # nlim=10000 for 1e-3 + # floors/u_over_rho_max=2 + #b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ + #coordinates/transform=mks coordinates/hslope=1 \ this, for some reason does not work for b cleaning? if [ $VAR -ne 0 ]; then if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then From 44e4b98fa2f1b9e2607fb1ddfb05b4369b4e82b2 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 12 Apr 2023 13:20:17 -0600 Subject: [PATCH 054/219] Integrate Multizone better Read B directly when restarting from KHARMA files, w/o B_Save Use integrated Dirichlet bounds w/ new setter from existing ghosts Single function for Bondi soln properties Fix different init bugs introduced: Boundaries in B cleaning Coordinate systems not emplaced Also: Primary work for B clean every N steps with/without other transport Warn on tradition FMKS using domain startx1 as this is not consistent Try harder to remove extra FillGhosts fields in runs w/cleaning --- .gitignore | 1 + external/parthenon | 2 +- .../parthenon-use-gr-coordinates.patch | 21 +- kharma/b_cleanup/b_cleanup.cpp | 96 ++- kharma/b_cleanup/b_cleanup.hpp | 8 +- kharma/b_flux_ct/b_flux_ct.cpp | 37 +- kharma/b_flux_ct/b_flux_ct.hpp | 29 +- kharma/b_flux_ct/seed_B_ct.cpp | 108 +--- kharma/boundaries/boundaries.cpp | 150 +++-- kharma/boundaries/boundaries.hpp | 5 + kharma/boundaries/boundaries_forked_cpp.txt | 580 ------------------ kharma/coordinates/coordinate_embedding.hpp | 39 +- kharma/coordinates/coordinate_systems.hpp | 15 +- kharma/coordinates/gr_coordinates.cpp | 8 +- kharma/coordinates/gr_coordinates.hpp | 6 +- kharma/coordinates/root_find.hpp | 2 +- kharma/driver/imex_step.cpp | 23 +- kharma/driver/kharma_driver.cpp | 178 +++--- kharma/driver/kharma_driver.hpp | 2 +- kharma/driver/kharma_step.cpp | 22 +- kharma/flux/flux.cpp | 62 ++ kharma/flux/flux.hpp | 6 + kharma/implicit/implicit.cpp | 8 +- kharma/implicit/implicit.hpp | 2 +- kharma/kharma.cpp | 27 +- kharma/kharma_utils.hpp | 6 + kharma/main.cpp | 2 +- kharma/prob/bondi.cpp | 65 +- kharma/prob/bondi.hpp | 23 + kharma/prob/emhd/conducting_atmosphere.cpp | 71 ++- kharma/prob/gizmo.cpp | 14 +- kharma/prob/gizmo.hpp | 25 +- kharma/prob/post_initialize.cpp | 66 +- kharma/prob/resize_restart.cpp | 16 - kharma/prob/resize_restart_kharma.cpp | 509 +++++++-------- kharma/prob/resize_restart_kharma.hpp | 81 ++- kharma/types.hpp | 40 +- machines/darwin.sh | 56 +- pars/conducting_atmosphere.par | 7 +- pars/sane.par | 2 +- tests/bclean/bondi_multizone.par | 115 ++++ tests/bclean/bondi_multizone_00000.par | 120 ---- tests/bclean/run.sh | 22 +- 43 files changed, 1114 insertions(+), 1563 deletions(-) delete mode 100644 kharma/boundaries/boundaries_forked_cpp.txt create mode 100755 tests/bclean/bondi_multizone.par delete mode 100755 tests/bclean/bondi_multizone_00000.par diff --git a/.gitignore b/.gitignore index 858fd075..80ec56b0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ convergence.txt *.webm core.* frames_*/ +logs/ # KHARMA/Parthenon outputs *.phdf diff --git a/external/parthenon b/external/parthenon index fd7d58e7..6e4d9ea9 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit fd7d58e759df72f403f78611553b4dcfc2320514 +Subproject commit 6e4d9ea9b3961b5d0129cb5b1254256f5f2331be diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index 60abf16d..c29c1a3b 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 67e5d082..0e6d2a7e 100644 +index f45cc979..23fb0f45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,7 +11,7 @@ index 67e5d082..0e6d2a7e 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -285,6 +285,8 @@ lint_target(parthenon) +@@ -301,6 +301,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ @@ -32,11 +32,24 @@ index d1290dee..50bfc840 100644 namespace parthenon { +diff --git a/src/interface/data_collection.cpp b/src/interface/data_collection.cpp +index 6a1d72c9..b5ba609b 100644 +--- a/src/interface/data_collection.cpp ++++ b/src/interface/data_collection.cpp +@@ -48,7 +48,7 @@ std::shared_ptr DataCollection::Add(const std::string &name, + if (it != containers_.end()) { + // check to make sure they are the same + if (!(*src == *(it->second))) { +- PARTHENON_THROW("Error attempting to add a Container to a Collection"); ++ //PARTHENON_THROW("Error attempting to add a Container to a Collection"); + } + return it->second; + } diff --git a/src/interface/meshblock_data.cpp b/src/interface/meshblock_data.cpp -index 720d708d..de31a71b 100644 +index ca4aa5fb..d7cc33ec 100644 --- a/src/interface/meshblock_data.cpp +++ b/src/interface/meshblock_data.cpp -@@ -432,7 +432,7 @@ MeshBlockData::GetVariablesByFlag(const Metadata::FlagCollection &flags, +@@ -430,7 +430,7 @@ MeshBlockData::GetVariablesByFlag(const Metadata::FlagCollection &flags, template void MeshBlockData::Remove(const std::string &label) { diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index dfc1bc13..45ae55df 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -68,6 +68,9 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s auto pkg = std::make_shared("B_Cleanup"); Params ¶ms = pkg->AllParams(); + // The solver needs this flag + Metadata::AddUserFlag("B_Cleanup"); + // Solver options // Allow setting tolerance relative to starting value. Off by default Real rel_tolerance = pin->GetOrAddReal("b_cleanup", "rel_tolerance", 1.); @@ -101,7 +104,7 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // RHS. Must not just be "divB" as that field does not sync boundaries pkg->AddParam("rhs_name", "divB_RHS"); // Construct a solver. We don't need the template parameter, so we use 'int' - BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor()); + BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}, {Metadata::GetUserFlag("B_Cleanup")}); // Set callback solver.user_MatVec = B_Cleanup::CornerLaplacian; @@ -109,37 +112,90 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // FIELDS std::vector s_vector({NVEC}); - Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost}); - // Scalar potential, solution to div^2 p = div B - pkg->AddField("p", m); + std::vector cleanup_flags({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("B_Cleanup")}); + auto cleanup_flags_ghost = cleanup_flags; + cleanup_flags_ghost.push_back(Metadata::FillGhost); + // Scalar potential, solution to del^2 p = div B + pkg->AddField("p", Metadata(cleanup_flags_ghost)); // Gradient of potential; temporary for gradient calc - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); - pkg->AddField("dB", m); + pkg->AddField("dB", Metadata(cleanup_flags, s_vector)); // Field divergence as RHS, i.e. including boundary sync - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); - pkg->AddField("divB_RHS", m); + pkg->AddField("divB_RHS", Metadata(cleanup_flags_ghost)); // Optionally take care of B field transport ourselves. Inadvisable. // We've already set a default, so only do this if we're *explicitly* asked - // TODO there's a long list of stuff to enable this if someone really wants it bool manage_field = pin->GetString("b_field", "solver") == "b_cleanup"; params.Add("manage_field", manage_field); + // Set an interval to clean during the run *can be run in addition to a normal solver*! + // You might want to do this if, e.g., you care about divergence on faces with outflow/constant conditions int cleanup_interval = pin->GetOrAddInteger("b_cleanup", "cleanup_interval", manage_field ? 10 : -1); params.Add("cleanup_interval", cleanup_interval); // Declare fields if we're doing that if (manage_field) { - throw std::runtime_error("B Cleanup package as transport not implemented!"); + // Stolen verbatim from FluxCT, except we don't register the FixFlux step obvs + // TODO preserve an easier form of divB in this case? + + // Mark if we're evolving implicitly + bool implicit_b = pin->GetOrAddBoolean("b_field", "implicit", false); + params.Add("implicit", implicit_b); + MetadataFlag areWeImplicit = (implicit_b) ? Metadata::GetUserFlag("Implicit") + : Metadata::GetUserFlag("Explicit"); + + // Flags for B fields. "Primitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + + auto m = Metadata(flags_prim, s_vector); + pkg->AddField("prims.B", m); + m = Metadata(flags_cons, s_vector); + pkg->AddField("cons.B", m); + + // Also ensure that prims get filled, *if* we're evolved explicitly + if (!implicit_b) { + pkg->MeshUtoP = B_FluxCT::MeshUtoP; + pkg->BlockUtoP = B_FluxCT::BlockUtoP; + } + + // Register the other callbacks + pkg->PostStepDiagnosticsMesh = B_FluxCT::PostStepDiagnostics; + + // The definition of MaxDivB we care about actually changes per-transport, + // so calculating it is handled by the transport package + // We'd only ever need to declare or calculate divB for output (getting the max is independent) + if (KHARMA::FieldIsOutput(pin, "divB")) { + pkg->BlockUserWorkBeforeOutput = B_FluxCT::FillOutput; + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("divB", m); + } + + // List (vector) of HistoryOutputVars that will all be enrolled as output variables + parthenon::HstVar_list hst_vars = {}; + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_FluxCT::MaxDivB, "MaxDivB")); + // Event horizon magnetization. Might be the same or different for different representations? + if (pin->GetBoolean("coordinates", "spherical")) { + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, B_FluxCT::ReducePhi0, "Phi_0")); + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, B_FluxCT::ReducePhi5, "Phi_EH")); + } + // add callbacks for HST output to the Params struct, identified by the `hist_param_key` + pkg->AddParam<>(parthenon::hist_param_key, hst_vars); } return pkg; } -void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) +bool B_Cleanup::CleanupThisStep(Mesh* pmesh, int nstep) { - Flag(md, "Cleaning up divB"); + auto pkg = pmesh->packages.Get("B_Cleanup"); + return (pkg->Param("cleanup_interval") > 0) && (nstep % pkg->Param("cleanup_interval") == 0); +} +// TODO(BSP) Make this add to a TaskCollection rather than operating synchronously +TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) +{ auto pmesh = md->GetMeshPointer(); auto pkg = pmesh->packages.Get("B_Cleanup"); auto max_iters = pkg->Param("max_iterations"); @@ -164,7 +220,7 @@ void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) // If divB is "pretty good" and we allow not solving... if (MPIRank0()) std::cout << "Magnetic field divergence of " << divb_start << " is below tolerance. Skipping B field cleanup." << std::endl; - return; + return TaskStatus::complete; } else { if(MPIRank0()) std::cout << "Starting magnetic field divergence: " << divb_start << std::endl; @@ -173,6 +229,7 @@ void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) // Initialize the divB variable, which we'll be solving against. // This gets signed divB on all physical corners (total (N+1)^3) // and syncs ghost zones + KHARMADriver::SyncAllBounds(md); B_FluxCT::CalcDivB(md.get(), "divB_RHS"); KHARMADriver::SyncAllBounds(md); @@ -208,22 +265,24 @@ void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) if (MPIRank0() && verbose > 0) { std::cout << "Applying magnetic field correction" << std::endl; } - // Update the magnetic field on physical zones using our solution + // Update the (conserved) magnetic field on physical zones using our solution B_Cleanup::ApplyP(msolve.get(), md.get()); // Synchronize to update ghost zones KHARMADriver::SyncAllBounds(md); + // Make sure all primitive vars reflect the solution + Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + // Recalculate divB max for one last check const double divb_end = B_FluxCT::GlobalMaxDivB(md.get()); if (MPIRank0()) { std::cout << "Magnetic field divergence after cleanup: " << divb_end << std::endl; } - Flag(md, "Cleaned"); + return TaskStatus::complete; } -// TODO TODO NEEDED? Can we remove the package instead? TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) { // If we aren't needed to clean anything... @@ -234,7 +293,7 @@ TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) for (auto& pmb : blocks) { auto rc_s = pmb->meshblock_data.Get(); //auto varlabels = rc_s->GetVariablesByName({"pk0", "res0", "divB_RHS", "p"}).labels(); - for (auto varlabel : {"pk0", "res0", "divB_RHS", "p"}) { + for (auto varlabel : {"pk0", "res0", "temp0", "divB_RHS", "p"}) { if (rc_s->HasCellVariable(varlabel)) rc_s->Remove(varlabel); } @@ -262,6 +321,7 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); double b1, b2, b3; + B_FluxCT::MeshUtoP(md, IndexDomain::interior); B_FluxCT::center_grad(G, P, b, k, j, i, ndim > 2, b1, b2, b3); B(b, V1, k, j, i) -= b1; B(b, V2, k, j, i) -= b2; @@ -269,8 +329,6 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) } ); - B_FluxCT::MeshUtoP(md, IndexDomain::entire); - return TaskStatus::complete; } diff --git a/kharma/b_cleanup/b_cleanup.hpp b/kharma/b_cleanup/b_cleanup.hpp index 05f74373..ebb4037f 100644 --- a/kharma/b_cleanup/b_cleanup.hpp +++ b/kharma/b_cleanup/b_cleanup.hpp @@ -58,13 +58,13 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

>& md); +TaskStatus CleanupDivergence(std::shared_ptr>& md); /** - * Add the iterative tasks required for B field cleanup to the tasklist - * Likely faster than above if we want to clean periodically + * Whether the parameters say to perform cleanup this step, during execution + * Takes the mesh pointer to find our package parameters */ -//void AddBCleanupTasks(TaskList tl, TaskID t_dep); +bool CleanupThisStep(Mesh* pmesh, int nstep); /** * Remove the extra solver fields which B_Cleanup added during initialization. diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index dfbadc84..5b372af0 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -39,30 +39,12 @@ #include "decs.hpp" #include "grmhd.hpp" #include "kharma.hpp" -#include "reductions.hpp" using namespace parthenon; namespace B_FluxCT { -// Reductions: phi uses global machinery, but divB is too -// Can also sum the hemispheres independently to be fancy (TODO?) -KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) -{ - // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 - return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B -} - -Real ReducePhi0(MeshData *md) -{ - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); -} -Real ReducePhi5(MeshData *md) -{ - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); -} - std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { auto pkg = std::make_shared("B_FluxCT"); @@ -93,6 +75,11 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("b_field", "disable_flux_ct", false); params.Add("disable_flux_ct", disable_flux_ct); + bool kill_on_large_divb = pin->GetOrAddBoolean("b_field", "kill_on_large_divb", true); + params.Add("kill_on_large_divb", kill_on_large_divb); + Real kill_on_divb_over = pin->GetOrAddReal("b_field", "kill_on_divb_over", 1.e-3); + params.Add("kill_on_divb_over", kill_on_divb_over); + // Driver type & implicit marker // By default, solve B explicitly auto& driver = packages->Get("Driver")->AllParams(); @@ -129,12 +116,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); - // Hyerin (12/19/22) - if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::FillGhost, Metadata::Vector}); - pkg->AddField("B_Save", m); - } - // We exist basically to do this pkg->FixFlux = B_FluxCT::FixFlux; @@ -706,7 +687,7 @@ double GlobalMaxDivB(MeshData *md) return max_divb.val; } -TaskStatus PrintGlobalMaxDivB(MeshData *md) +TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) { Flag(md, "Printing B field diagnostics"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -718,9 +699,13 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md) // Calculate the maximum from/on all nodes const double divb_max = B_FluxCT::GlobalMaxDivB(md); // Print on rank zero - if(MPIRank0()) { + if (MPIRank0()) { std::cout << "Max DivB: " << divb_max << std::endl; } + if (kill_on_large_divb) { + if (divb_max > pmb0->packages.Get("B_FluxCT")->Param("kill_on_divb_over")) + throw std::runtime_error("DivB exceeds maximum! Quitting..."); + } } Flag(md, "Printed B field diagnostics"); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 8f021b61..6b1800f0 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -35,6 +35,7 @@ #include "decs.hpp" #include "grmhd_functions.hpp" +#include "reductions.hpp" #include "types.hpp" #include @@ -103,9 +104,16 @@ double GlobalMaxDivB(MeshData *md); * Diagnostics printed/computed after each step * Currently just max divB */ -TaskStatus PrintGlobalMaxDivB(MeshData *md); +TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb=false); + +/** + * Diagnostics function should print divB, and optionally stop execution if it's large + */ inline TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) - { return PrintGlobalMaxDivB(md); } +{ + auto& params = md->GetMeshPointer()->block_list[0]->packages.Get("B_FluxCT")->AllParams(); + return PrintGlobalMaxDivB(md, params.Get("kill_on_large_divb")); +} /** * Fill fields which are calculated only for output to file, i.e., divB @@ -202,4 +210,21 @@ KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, B3 = norm*term3/G.Dxc<3>(k); } +// Reductions: phi uses global machinery, but divB is too +// Can also sum the hemispheres independently to be fancy (TODO?) +KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) +{ + // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 + return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B +} + +inline Real ReducePhi0(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); +} +inline Real ReducePhi5(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); +} + } diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 342005f0..caae709c 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -52,16 +52,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) GridScalar rho = rc->Get("prims.rho").data; GridVector B_P = rc->Get("prims.B").data; GridVector B_U = rc->Get("cons.B").data; - GridVector B_Save = rc->Get("B_Save").data; Real fx1min, fx1max, dx1, fx1min_ghost; - int n1tot; - if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { - fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); - fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); - n1tot = pmb->packages.Get("GRMHD")->Param("rnx1"); - dx1 = (fx1max - fx1min) / n1tot; - fx1min_ghost = fx1min - 4*dx1; - } auto fname_fill = pin->GetOrAddString("resize_restart", "fname_fill", "none"); const bool should_fill = !(fname_fill == "none"); @@ -114,14 +105,10 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) break; } - IndexDomain domain = IndexDomain::entire; //Hyerin: why interior? + IndexDomain domain = IndexDomain::interior; int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - //domain = IndexDomain::entire; // Hyerin: also do it everywhere if it is resize_restart_kharma - //int is_all = pmb->cellbounds.is(domain), ie_all = pmb->cellbounds.ie(domain); - //int js_all = pmb->cellbounds.js(domain), je_all = pmb->cellbounds.je(domain); - //int ks_all = pmb->cellbounds.ks(domain), ke_all = pmb->cellbounds.ke(domain); int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); int n2 = pmb->cellbounds.ncellsj(IndexDomain::entire); int n3 = pmb->cellbounds.ncellsk(IndexDomain::entire); @@ -288,99 +275,6 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } ); } - if (pin->GetString("parthenon/job", "problem_id") == "resize_restart_kharma") { - // Hyerin (12/19/22) copy over data after initialization - - pmb->par_for("copy_B_restart_resize_kharma", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - GReal X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - - if ((!should_fill) && (X[1] 2) { - printf("WARNING: 3D not supported for resize_restart_kharma!!\n"); - } else{ - // Hyerin (02/28/23) this needs testing!! - // getting A vector by solving vector Poisson eq \Del^2\vec{A}= - \Del \cross \vec{b} - GridVector B_interp("B_interp", NVEC, n3, n2, n1); // \vec{b} in rhs - int idx, ntot; - ntot=n3*n2*n1; - if (ndim > 2) ntot *= NVEC; - GReal coeffs[ntot][ntot], curl_B[ntot], inv_coeffs[ntot][ntot], A_out[ntot]; - // curl_B : -\Del \cross \vec{b} in rhs - // coeffs : \Del^2 in lhs - - // initialize - for (int mu_ = 0; mu_ < ntot; mu_++) { - curl_B[mu_] = 0.; - A_out[ntot] = 0.; - for (int nu_ = 0; nu_ < ntot; nu_++) { - coeffs[mu_][nu_] = 0.; - } - } - pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, - KOKKOS_LAMBDA_3D { - - idx=n1*(n2*k+j)+i; - B_interp(V3,k,j,i) = 0; //(B_U(2,k,j,i) + B_U(2,k-1,j,i))/2; - - if (i==is_all || j==js_all || k== ks_all) { // think - B_interp(V1,k,j,i) = 0.; - B_interp(V2,k,j,i) = 0.; - curl_B[idx] = 0.; - } else { - B_interp(V1,k,j,i) = (B_U(V1,k,j,i) + B_U(V2,k,j,i-1))/2; - B_interp(V2,k,j,i) = (B_U(V2,k,j,i) + B_U(V2,k,j-1,i))/2; - if (ndim > 2) B_interp(V3,k,j,i) = (B_U(V3,k,j,i) + B_U(V3,k-1,j,i))/2; - curl_B[idx] = -(B_interp(V2,k,j,i)-B_interp(V2,k,j,i-1))/G.dx1v(i) + (B_interp(V1,k,j,i)-B_interp(V1,k,j-1,i))/G.dx2v(j); - } - - coeffs[idx,idx] = -2.*m::pow(G.dx1v(i),-2.)-2.*m::pow(G.dx2v(j),-2.); - coeffs[idx,idx-1] = m::pow(G.dx1v(i)) ; - coeffs[idx,idx+1] = m::pow(G.dx1v(i)) ; - coeffs[idx,idx-n2] = m::pow(G.dx2v(j)) ; - coeffs[idx,idx+n2] = m::pow(G.dx2v(j)) ; - } - ); - invert(&coeffs[0][0], &inv_coeffs[0][0]); // TODO: make my own fxn to write up an inverse (numerical recipes in C) - // get A from B - for (int mu_ = 0; mu_ < ntot; mu_++) { - for (int nu_ = 0; nu_ < ntot; nu_++) { - A_out[mu] += inv_coeffs[mu_][nu_]*curl_B[nu_]; - } - } - - // store into GridVector - pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, // think about ranges - KOKKOS_LAMBDA_3D { - idx=n1*(n2*k+j)+i; - A(V3, k, j, i) = A_out[idx]; - } - ); - - // put it back to B_U - pmb->par_for("poisson_eq", ks_all, ke_all, js_all, je_all, is_all, ie_all, - KOKKOS_LAMBDA_3D { - get_B_from_A_2D(G, A, B_U, k, j, i); - } - ); - - - } - */ - } - - // Then make sure the primitive versions are updated, too - B_FluxCT::BlockUtoP(rc, IndexDomain::interior); return TaskStatus::complete; } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index bff8a7a0..05e635ef 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -72,7 +72,7 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: // Only needed if x1min is inside BH event horizon, otherwise a nuisance for divB on corners if (spherical) { const Real a = pin->GetReal("coordinates", "a"); - bool inside_eh = pin->GetBoolean("coordinates", "r_in") < 1 + sqrt(1 - a*a); + bool inside_eh = pin->GetBoolean("coordinates", "r_in") < (1 + sqrt(1 - a*a)); bool fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", inside_eh); params.Add("fix_corner", fix_corner); } @@ -80,24 +80,50 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: // Allocate space for Dirichlet boundaries if they'll be used // We have to trust the user here since the problem will set the function pointers later // TODO specify which boundaries individually for cleanliness? - bool use_dirichlet = pin->GetOrAddBoolean("boundaries", "use_dirichlet", false); + bool use_dirichlet = pin->GetOrAddBoolean("boundaries", "prob_uses_dirichlet", false); + params.Add("use_dirichlet", use_dirichlet); if (use_dirichlet) { auto& driver = packages->Get("Driver")->AllParams(); - int nvar = driver.Get("n_explicit_vars") + driver.Get("n_implicit_vars"); - std::cout << "Allocating Dirichlet boundaries for " << nvar << " variables." << std::endl; - // TODO We also don't know the mesh size, since it's not constructed. Infer. - int ng = pin->GetInteger("parthenon/mesh", "nghost"); - int nx1 = pin->GetInteger("parthenon/meshblock", "nx1"); - int n1 = nx1 + 2*ng; - int nx2 = pin->GetInteger("parthenon/meshblock", "nx2"); - int n2 = (nx2 == 1) ? nx2 : nx2 + 2*ng; - int nx3 = pin->GetInteger("parthenon/meshblock", "nx3"); - int n3 = (nx3 == 1) ? nx3 : nx3 + 2*ng; + + // We can't use GetVariablesByFlag yet, so walk through and count manually + int nvar = 0; + for (auto pkg : packages->AllPackages()) { + //std::cerr << pkg.first << ": "; + for (auto field : pkg.second->AllFields()) { + //std::cerr << field.first.label() << " "; + // Specifically ignore the B_Cleanup variables, we don't handle their boundary conditions + if (field.second.IsSet(Metadata::FillGhost) && !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup"))) { + if (field.second.Shape().size() < 1) { + nvar += 1; + } else { + nvar += field.second.Shape()[0]; + } + } + } + //std::cerr << std::endl; + } + + // We also don't know the mesh size, since it's not constructed. We infer. + const int ng = pin->GetInteger("parthenon/mesh", "nghost"); + const int nx1 = pin->GetInteger("parthenon/meshblock", "nx1"); + const int n1 = nx1 + 2*ng; + const int nx2 = pin->GetInteger("parthenon/meshblock", "nx2"); + const int n2 = (nx2 == 1) ? nx2 : nx2 + 2*ng; + const int nx3 = pin->GetInteger("parthenon/meshblock", "nx3"); + const int n3 = (nx3 == 1) ? nx3 : nx3 + 2*ng; + + if (pin->GetInteger("debug", "verbose") > 0) { + std::cout << "Allocating Dirichlet boundaries for " << nvar << " variables." << std::endl; + if (pin->GetInteger("debug", "verbose") > 1) { + std::cout << "Initializing Dirichlet bounds with dimensions nvar,n1,n2,n3: " << nvar << " " << n1 << " " << n2 << " " << n3 << " and ng: " << ng << std::endl; + } + } // These are declared *backward* from how they will be indexed std::vector s_x1({ng, n2, n3, nvar}); std::vector s_x2({n1, ng, n3, nvar}); std::vector s_x3({n1, n2, ng, nvar}); + // Dirichlet conditions must be restored when restarting! Needs Metadata::Restart when this works! Metadata m_x1 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x1); Metadata m_x2 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x2); Metadata m_x3 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x3); @@ -204,34 +230,34 @@ void KBoundaries::FixCorner(std::shared_ptr>& rc, IndexDomai Flag(rc, "Fixed"); } -void KBoundaries::CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -{ - Flag(rc, "Correcting the B field w/metric"); - std::shared_ptr pmb = rc->GetBlockPointer(); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); +// void KBoundaries::CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +// { +// Flag(rc, "Correcting the B field w/metric"); +// std::shared_ptr pmb = rc->GetBlockPointer(); +// const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - auto B_P = rc->PackVariables(std::vector{"prims.B"}); - // Return if no field to correct - if (B_P.GetDim(4) == 0) return; +// auto B_P = rc->PackVariables(std::vector{"prims.B"}); +// // Return if no field to correct +// if (B_P.GetDim(4) == 0) return; - const auto& G = pmb->coords; +// const auto& G = pmb->coords; - const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundarySide(domain); - const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) - : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) - : bounds.GetBoundsK(IndexDomain::interior)); - const int ref = BoundaryIsInner(domain) ? range.s : range.e; - - pmb->par_for_bndry("Correct_B_P", IndexRange{0,NVEC-1}, domain, coarse, - KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { - B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) - / G.gdet(Loci::center, j, i); - } - ); +// const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; +// const int dir = BoundarySide(domain); +// const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) +// : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) +// : bounds.GetBoundsK(IndexDomain::interior)); +// const int ref = BoundaryIsInner(domain) ? range.s : range.e; - Flag(rc, "Corrected"); -} +// pmb->par_for_bndry("Correct_B_P", IndexRange{0,NVEC-1}, domain, coarse, +// KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { +// B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) +// / G.gdet(Loci::center, j, i); +// } +// ); + +// Flag(rc, "Corrected"); +// } void KBoundaries::DefaultBoundary(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { @@ -256,20 +282,60 @@ void KBoundaries::DefaultBoundary(std::shared_ptr>& rc, Inde } } +void KBoundaries::SetDomainDirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { + Flag("Setting Dirichlet bound"); + + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + using FC = Metadata::FlagCollection; + auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); + auto bound = rc->Get("bound."+BoundaryName(domain)).data; + + if (q.GetDim(4) != bound.GetDim(4)) { + std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + } + + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; + const bool right = !BoundaryIsInner(domain); + + // Subtract off the starting index if we're on the right + const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundarySide(domain); + const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior)+1 : 0; + const int je = (dir == 2) ? bounds.je(IndexDomain::interior)+1 : 0; + const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior)+1 : 0; + + const auto& G = pmb->coords; + + pmb->par_for_bndry("dirichlet_boundary", vars, domain, coarse, + KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + if (right) { + bound(p, k-ke, j-je, i-ie) = q(p, k, j, i); + } else { + bound(p, k, j, i) = q(p, k, j, i); + } + } + ); + + Flag("Set"); +} + void KBoundaries::Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { - Flag(rc, "Applying dirichlet bound"); + Flag(rc, "Applying Dirichlet bound"); std::shared_ptr pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - auto q = rc->PackVariables({Metadata::FillGhost}, coarse); + using FC = Metadata::FlagCollection; + auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); auto bound = rc->Get("bound."+BoundaryName(domain)).data; - PackIndexMap prims_map; - auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); - const VarMap m_p(prims_map, false); // In case we need it - + if (q.GetDim(4) != bound.GetDim(4)) { + std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + } + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; const bool right = !BoundaryIsInner(domain); diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index 764095ce..8994bc33 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -82,6 +82,11 @@ void DefaultBoundary(std::shared_ptr> &rc, IndexDomain domai */ void Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse); +/** + * Set the current contents of a domain to be the Dirichlet boundary conditions. + */ +void SetDomainDirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse); + /** * Fix fluxes on physical boundaries. * 1. Ensure no inflow of density onto the domain diff --git a/kharma/boundaries/boundaries_forked_cpp.txt b/kharma/boundaries/boundaries_forked_cpp.txt deleted file mode 100644 index 34e5fde6..00000000 --- a/kharma/boundaries/boundaries_forked_cpp.txt +++ /dev/null @@ -1,580 +0,0 @@ -/* - * File: boundaries.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "decs.hpp" - -#include "boundaries.hpp" - -#include "kharma.hpp" -#include "flux.hpp" -#include "flux_functions.hpp" -#include "grmhd_functions.hpp" -#include "pack.hpp" -#include "types.hpp" - -// Problem-specific boundaries -#include "bondi.hpp" -#include "emhd/conducting_atmosphere.hpp" -#include "emhd/bondi_viscous.hpp" -#include "resize_restart_kharma.hpp" // Hyerin -//#include "hubble.hpp" - -// Going to need all modules' headers here -#include "b_flux_ct.hpp" -#include "b_cd.hpp" - -#include "basic_types.hpp" -#include "mesh/domain.hpp" -#include "mesh/mesh.hpp" - -// Single outflow boundary function for inner and outer bounds -// Lots of shared code and only a few indices different -void OutflowX1(std::shared_ptr> &rc, IndexDomain domain, bool coarse) -{ - Flag(rc.get(), "Applying KHARMA outflow X1 bound"); - std::shared_ptr pmb = rc->GetBlockPointer(); - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const auto& G = pmb->coords; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - bool check_inner = pmb->packages.Get("GRMHD")->Param("check_inflow_inner"); - bool check_outer = pmb->packages.Get("GRMHD")->Param("check_inflow_outer"); - const bool check_inflow = ((check_inner && domain == IndexDomain::inner_x1) - || (check_outer && domain == IndexDomain::outer_x1)); - - // q will actually have *both* cons & prims (unless using imex driver) - // We'll only need cons.B specifically tho - PackIndexMap prims_map, ghosts_map; - auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); - auto q = rc->PackVariables({Metadata::FillGhost}, ghosts_map, coarse); - const VarMap m_u(ghosts_map, true), m_p(prims_map, false); - // If we're running imex, q is just the *primitive* variables - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - - // KHARMA is very particular about corner boundaries. - // In particular, we apply the outflow boundary over ALL X2, X3, - // Then the polar bound only where outflow is not applied, - // and periodic bounds only where neither other bound applies. - // The latter is accomplished regardless of Parthenon's definitions, - // since these functions are run after Parthenon's MPI boundary syncs & - // replace whatever they've done. - IndexDomain ldomain = IndexDomain::interior; - int is = bounds.is(ldomain), ie = bounds.ie(ldomain); - int js = bounds.js(ldomain), je = bounds.je(ldomain); - int ks = bounds.ks(ldomain), ke = bounds.ke(ldomain); - ldomain = IndexDomain::entire; - int is_e = bounds.is(ldomain), ie_e = bounds.ie(ldomain); - int js_e = bounds.js(ldomain), je_e = bounds.je(ldomain); - int ks_e = bounds.ks(ldomain), ke_e = bounds.ke(ldomain); - - int ref_tmp, ibs, ibe; - if (domain == IndexDomain::inner_x1) { - ref_tmp = is; - ibs = is_e; - ibe = is - 1; - } else if (domain == IndexDomain::outer_x1) { - ref_tmp = ie; - ibs = ie + 1; - ibe = ie_e; - } else { - throw std::invalid_argument("KHARMA Outflow boundaries only implemented in X1!"); - } - const int ref = ref_tmp; - - // This first loop copies all variables with the "FillGhost" tag into the outer zones - // This includes some we may replace below - pmb->par_for("OutflowX1", 0, q.GetDim(4) - 1, ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_VARS { - q(p, k, j, i) = q(p, k, j, ref); - } - ); - // Inflow check - if (check_inflow) { - pmb->par_for("OutflowX1_check", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); - } - ); - } - if (!prim_ghosts) { - // Normal operation: We copied both both prim & con GRMHD variables, but we want to apply - // the boundaries based on just the former, so we run P->U - pmb->par_for("OutflowX1_PtoU", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // TODO move these steps into FillDerivedDomain, make a GRMHD::PtoU call the last in that series - // Correct primitive B - if (m_p.B1 >= 0) - VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); - // Recover conserved vars. Must be only GRMHD. - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); - } - ); - } - - Flag(rc.get(), "Applied"); -} - -// Single reflecting boundary function for inner and outer bounds -// See above for comments -void ReflectX2(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag(rc.get(), "Applying KHARMA reflecting X2 bound"); - std::shared_ptr pmb = rc->GetBlockPointer(); - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const auto& G = pmb->coords; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin - Real x_EH = pmb->packages.Get("GRMHD")->Param("x_EH"); //Hyerin - - // q will actually have *both* cons & prims (unless using imex driver) - // We'll only need cons.B specifically tho - PackIndexMap prims_map, ghosts_map; - auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); - auto q = rc->PackVariables({Metadata::FillGhost}, ghosts_map, coarse); - const VarMap m_u(ghosts_map, true), m_p(prims_map, false); - // If we're running imex, q is the *primitive* variables - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - - // KHARMA is very particular about corner boundaries, see above - IndexDomain ldomain = IndexDomain::interior; - int is = bounds.is(ldomain), ie = bounds.ie(ldomain); - int js = bounds.js(ldomain), je = bounds.je(ldomain); - int ks = bounds.ks(ldomain), ke = bounds.ke(ldomain); - ldomain = IndexDomain::entire; - int is_e = bounds.is(ldomain), ie_e = bounds.ie(ldomain); - int js_e = bounds.js(ldomain), je_e = bounds.je(ldomain); - int ks_e = bounds.ks(ldomain), ke_e = bounds.ke(ldomain); - - // So. Parthenon wants us to do our thing over is_e to ie_e - // BUT if we're at the interior bound on X1, that's gonna blow things up - // (for reasons unknown, inflow bounds must take precedence) - // so we have to be smart. - // Side note: this *lags* the X1/X2 corner zones by one step, since X1 is applied first. - // this is potentially bad - int ics = (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) ? is : is_e; - //int ice = (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) ? ie : ie_e; - //int ics = is_e; - int ice = ie_e; - if (x1min > x_EH){ - ics = is_e; // overwrite the starting index such that - //ice = ie_e; // the reflectx2 bc is also applied to outermost and innermost boundary - } - - int ref_tmp, add_tmp, jbs, jbe; - if (domain == IndexDomain::inner_x2) { - add_tmp = -1; - ref_tmp = bounds.GetBoundsJ(IndexDomain::interior).s; - jbs = js_e; - jbe = js - 1; - } else if (domain == IndexDomain::outer_x2) { - add_tmp = 1; - ref_tmp = bounds.GetBoundsJ(IndexDomain::interior).e; - jbs = je + 1; - jbe = je_e; - } else { - throw std::invalid_argument("KHARMA Reflecting boundaries only implemented in X2!"); - } - const int ref = ref_tmp; - const int add = add_tmp; - - // This first loop copies all variables with the "FillGhost" tag into the outer zones - // This includes some we may replace below - pmb->par_for("ReflectX2", 0, q.GetDim(4) - 1, ks_e, ke_e, jbs, jbe, ics, ice, - KOKKOS_LAMBDA_VARS { - Real reflect = q.VectorComponent(p) == X2DIR ? -1.0 : 1.0; - q(p, k, j, i) = reflect * q(p, k, (ref + add) + (ref - j), i); - } - ); - if (!prim_ghosts) { - // Normal operation: see above - pmb->par_for("ReflectX2_PtoU", ks_e, ke_e, jbs, jbe, ics, ice, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - if (m_p.B1 >= 0) - VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); - } - ); - } -} - -// Single reflecting boundary function for inner and outer bounds -// copied from ReflectX2 -void ReflectX1(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag(rc.get(), "Applying KHARMA reflecting X1 bound"); - std::shared_ptr pmb = rc->GetBlockPointer(); - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const auto& G = pmb->coords; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin - Real x_EH = pmb->packages.Get("GRMHD")->Param("x_EH"); //Hyerin - - // q will actually have *both* cons & prims (unless using imex driver) - // We'll only need cons.B specifically tho - PackIndexMap prims_map, ghosts_map; - auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); - auto q = rc->PackVariables({Metadata::FillGhost}, ghosts_map, coarse); - //auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); // instead, just directly alter flux to being 0 consistent with B field (check if the flux calculation is called later though) - const VarMap m_u(ghosts_map, true), m_p(prims_map, false); - // If we're running imex, q is the *primitive* variables - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - - // KHARMA is very particular about corner boundaries, see above - IndexDomain ldomain = IndexDomain::interior; - int is = bounds.is(ldomain), ie = bounds.ie(ldomain); - int js = bounds.js(ldomain), je = bounds.je(ldomain); - int ks = bounds.ks(ldomain), ke = bounds.ke(ldomain); - ldomain = IndexDomain::entire; - int is_e = bounds.is(ldomain), ie_e = bounds.ie(ldomain); - int js_e = bounds.js(ldomain), je_e = bounds.je(ldomain); - int ks_e = bounds.ks(ldomain), ke_e = bounds.ke(ldomain); - - int ref_tmp, add_tmp, ibs, ibe; - if (domain == IndexDomain::inner_x1) { - add_tmp = -1; - ref_tmp = bounds.GetBoundsI(IndexDomain::interior).s; - ibs = is_e; - ibe = is - 1; - } else if (domain == IndexDomain::outer_x1) { - add_tmp = 1; - ref_tmp = bounds.GetBoundsI(IndexDomain::interior).e; - ibs = ie + 1; - ibe = ie_e; - } else { - throw std::invalid_argument("KHARMA Reflecting boundaries only implemented in X1!"); - } - const int ref = ref_tmp; - const int add = add_tmp; - - // This first loop copies all variables with the "FillGhost" tag into the outer zones - // This includes some we may replace below - /* - pmb->par_for("ReflectX1", 0, q.GetDim(4) - 1, ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA_VARS { - if (k == ks_e && j == js_e && i == ibs) printf("Hyerin: p = %i, m_u.U1 = %i, ghosts_map[prims.U1] =%i \n",p, m_u.U1, ghosts_map["prims.uvec"].first); - //Real reflect = q.VectorComponent(p) == X1DIR ? -1.0 : 1.0; - //if (p != m_u.B1 && p != m_p.B2 && p != m_p.B3) { // Hyerin (02/12/23) don't change the B fields because this is done in b_flux_ct's FixX1Flux routine - //q(p, k, j, i) = reflect * q(p, k, j, (ref + add) + (ref - i)); - //} - } - ); - */ - int idx = ghosts_map["prims.uvec"].first; - pmb->par_for("ReflectX1", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Hyerin (02/13/23) only do for velocities - q(idx, k, j, i) = (-1.) * q(idx, k, j, (ref + add) + (ref - i)); - q(idx+1, k, j, i) = q(idx+1, k, j, (ref + add) + (ref - i)); - q(idx+2, k, j, i) = q(idx+2, k, j, (ref + add) + (ref - i)); - } - ); - if (!prim_ghosts) { - // Normal operation: see above - pmb->par_for("ReflectX1_PtoU", ks_e, ke_e, js_e, je_e, ibs, ibe, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - //if (m_p.B1 >= 0) - //VLOOP P(m_p.B1 + v, k, j, i) = q(m_u.B1 + v, k, j, i) / G.gdet(Loci::center, j, i); - GRMHD::p_to_u(G, P, m_p, gam, k, j, i, q, m_u); - } - ); - } -} - -// Interface calls into the preceding functions -void KBoundaries::InnerX1(std::shared_ptr> &rc, bool coarse) -{ - // TODO implement as named callback, give combo start/bound problems their own "packages" - auto pmb = rc->GetBlockPointer(); - std::string prob = pmb->packages.Get("GRMHD")->Param("problem"); - Real x1min = pmb->packages.Get("GRMHD")->Param("x1min"); //Hyerin - if (prob == "hubble") { - //SetHubble(rc.get(), IndexDomain::inner_x1, coarse); - } else if (prob == "conducting_atmosphere"){ - dirichlet_bc(rc.get(), IndexDomain::inner_x1, coarse); - } else if ((prob == "resize_restart_kharma")&& (x1min>1)){ - // Hyerin (if the inner x1 bound is far from BH, constant bc) - SetKharmaRestart(rc.get(), IndexDomain::inner_x1,coarse); - //ReflectX1(rc, IndexDomain::inner_x1, coarse); // Hyerin (02/12/23) reflecting bc instead of porous bc - } else if ((prob == "bondi") && (x1min>1)){ // Hyerin - SetBondi(rc.get(), IndexDomain::inner_x1,coarse); - //ReflectX1(rc, IndexDomain::inner_x1, coarse); - //} else if ((prob == "gizmo_shell") && (x1min>1)){ // Hyerin - // SetGizmoShell(rc.get(), IndexDomain::inner_x1,coarse); - } else { - OutflowX1(rc, IndexDomain::inner_x1, coarse); - } - // If we're in KHARMA/HARM driver, we need primitive versions of all the - // non-GRMHD vars - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - if (!prim_ghosts) KHARMA::FillDerivedDomain(rc, IndexDomain::inner_x1, coarse); -} -void KBoundaries::OuterX1(std::shared_ptr> &rc, bool coarse) -{ - auto pmb = rc->GetBlockPointer(); - std::string prob = pmb->packages.Get("GRMHD")->Param("problem"); - if (prob == "hubble") { - //SetHubble(rc.get(), IndexDomain::outer_x1, coarse); - } else if (prob == "bondi") { - SetBondi(rc.get(), IndexDomain::outer_x1, coarse); - //ReflectX1(rc, IndexDomain::outer_x1, coarse); - } else if (prob == "conducting_atmosphere"){ - dirichlet_bc(rc.get(), IndexDomain::outer_x1, coarse); - } else if (prob == "bondi_viscous") { - SetBondiViscous(rc.get(), IndexDomain::outer_x1, coarse); - } else if (prob == "resize_restart_kharma") { // Hyerin, constant boundary condition - SetKharmaRestart(rc.get(),IndexDomain::outer_x1, coarse); - //ReflectX1(rc, IndexDomain::outer_x1, coarse); - } else { - OutflowX1(rc, IndexDomain::outer_x1, coarse); - } - // If we're in KHARMA/HARM driver, we need primitive versions of all the - // non-GRMHD vars - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - if (!prim_ghosts) KHARMA::FillDerivedDomain(rc, IndexDomain::outer_x1, coarse); -} -void KBoundaries::InnerX2(std::shared_ptr> &rc, bool coarse) -{ - auto pmb = rc->GetBlockPointer(); - ReflectX2(rc, IndexDomain::inner_x2, coarse); - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - if (!prim_ghosts) KHARMA::FillDerivedDomain(rc, IndexDomain::inner_x2, coarse); -} -void KBoundaries::OuterX2(std::shared_ptr> &rc, bool coarse) -{ - auto pmb = rc->GetBlockPointer(); - ReflectX2(rc, IndexDomain::outer_x2, coarse); - bool prim_ghosts = pmb->packages.Get("GRMHD")->Param("driver_type") == "imex"; - if (!prim_ghosts) KHARMA::FillDerivedDomain(rc, IndexDomain::outer_x2, coarse); -} - -/** - * Zero flux of mass through inner and outer boundaries, and everything through the pole - * TODO Both may be unnecessary... - */ -TaskStatus KBoundaries::FixFlux(MeshData *md) -{ - Flag("Fixing fluxes"); - auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - bool check_inflow_inner = pmb0->packages.Get("GRMHD")->Param("check_inflow_inner"); - bool check_inflow_outer = pmb0->packages.Get("GRMHD")->Param("check_inflow_outer"); - bool fix_flux_pole = pmb0->packages.Get("GRMHD")->Param("fix_flux_pole"); - bool fix_flux_x1 = pmb0->packages.Get("GRMHD")->Param("fix_flux_x1"); - - IndexDomain domain = IndexDomain::interior; - const int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); - const int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); - const int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); - const int ndim = pmesh->ndim; - - // Fluxes are defined at faces, so there is one more valid flux than - // valid cell in the face direction. That is, e.g. F1 is valid on - // an (N1+1)xN2xN3 grid, F2 on N1x(N2+1)xN3, etc - const int ie_l = ie + 1; - const int je_l = (ndim > 1) ? je + 1 : je; - //const int ke_l = (ndim > 2) ? ke + 1 : ke; - - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - - PackIndexMap cons_map; - auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); - const int m_rho = cons_map["cons.rho"].first; - const int m_B = cons_map["cons.B"].first; // Hyerin (12/22/22) - - if (check_inflow_inner) { - if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); - } - ); - } - } - if (check_inflow_outer) { - if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - F.flux(X1DIR, m_rho, k, j, i) = m::max(F.flux(X1DIR, m_rho, k, j, i), 0.); - } - ); - } - } - - // This is a lot of zero fluxes! - if (fix_flux_pole) { - //printf("HYERIN: m_B=%i m_rho=%i dim = (%i %i %i %i %i %i)\n",m_B, m_rho,F.GetDim(1),F.GetDim(2), F.GetDim(3), F.GetDim(4), F.GetDim(5),F.GetDim(6)); - if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { - // This loop covers every flux we need - pmb->par_for("fix_flux_pole_l", 0, F.GetDim(4) - 1, ks-1, ke+1, js, js, is-1, ie+1, // Hyerin: expanded i and k ranges. see FluxCT. they care about these - KOKKOS_LAMBDA_VARS { - F.flux(X2DIR, p, k, j, i) = 0.; - //if (p==7 && k==15 && i==is-1){ - // printf("HYERIN: BC B flux %i %i %i = (%g %g %g)\n",i,j,k,F.flux(X2DIR,m_B,ks,js,i),F.flux(X2DIR,m_B+1,ks,js,i),F.flux(X2DIR,m_B+2,ks,js,i)); - //} - } - ); - } - - if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { - pmb->par_for("fix_flux_pole_r", 0, F.GetDim(4) - 1, ks-1, ke+1, je_l, je_l, is-1, ie+1, - KOKKOS_LAMBDA_VARS { - F.flux(X2DIR, p, k, j, i) = 0.; - } - ); - } - } - - /* Hyerin (01/03/23) I don't think this is needed. Same thing is applied on FixX1Flux - if (fix_flux_x1) { - // Hyerin (12/22/22) ensure no ghost zone B field change - if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones - } - ); - } - if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - VLOOP F.flux(X1DIR, m_B + v, k, j, i) = 0.; // Hyerin (12/22/22) no flux into ghost zones - } - ); - } - } - */ - } - - Flag("Fixed fluxes"); - return TaskStatus::complete; -} - -TaskID KBoundaries::AddBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1) -{ - // Readability - const auto local = parthenon::BoundaryType::local; - const auto nonlocal = parthenon::BoundaryType::nonlocal; - // Send all, receive/set local after sending - auto send = - tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); - - auto t_send_local = - tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); - auto t_recv_local = - tl.AddTask(t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); - auto t_set_local = - tl.AddTask(t_recv_local, parthenon::cell_centered_bvars::SetBounds, mc1); - - // Receive/set nonlocal - auto t_recv = tl.AddTask( - t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); - auto t_set = tl.AddTask(t_recv, parthenon::cell_centered_bvars::SetBounds, mc1); - - // TODO add AMR prolongate/restrict here (and/or maybe option not to?) - - return t_set | t_set_local; -} - -void KBoundaries::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) -{ - Flag("Syncing all bounds"); - TaskID t_none(0); - - // If we're using the ImEx driver, where primitives are fundamental, "AddBoundarySync" - // will only sync those, and we can call PtoU over everything after. - // If "AddBoundarySync" means syncing conserved variables, we have to call PtoU *before* - // the MPI sync operation, then recover the primitive vars *again* afterward. - auto pmesh = md->GetMeshPointer(); - bool sync_prims = pmesh->packages.Get("GRMHD")->Param("driver_type") == "imex"; - - // TODO un-meshblock the rest of this - auto &block_list = md.get()->GetMeshPointer()->block_list; - - if (sync_prims) { - // If we're syncing the primitive vars, we just sync once - TaskCollection tc; - auto tr = tc.AddRegion(1); - AddBoundarySync(t_none, tr[0], md); - while (!tr.Execute()); - - // Then PtoU - for (auto &pmb : block_list) { - auto& rc = pmb->meshblock_data.Get(); - - Flag("Block fill Conserved"); - Flux::PtoU(rc.get(), IndexDomain::entire); - - if (apply_domain_bounds) { - Flag("Block physical bounds"); - // Physical boundary conditions - parthenon::ApplyBoundaryConditions(rc); - } - } - } else { - // If we're syncing the conserved vars... - // Honestly, the easiest way through this sync is: - // 1. PtoU everywhere - for (auto &pmb : block_list) { - auto& rc = pmb->meshblock_data.Get(); - Flag("Block fill conserved"); - Flux::PtoU(rc.get(), IndexDomain::entire); - } - - // 2. Sync MPI bounds like a normal step - TaskCollection tc; - auto tr = tc.AddRegion(1); - AddBoundarySync(t_none, tr[0], md); - while (!tr.Execute()); - - // 3. UtoP everywhere - for (auto &pmb : block_list) { - auto& rc = pmb->meshblock_data.Get(); - - Flag("Block fill Derived"); - // Fill P again, including ghost zones - // But, sice we sync'd GRHD primitives already, - // leave those off by calling *Domain - // (like we do in a normal boundary sync) - KHARMA::FillDerivedDomain(rc, IndexDomain::entire, false); - - if (apply_domain_bounds) { - Flag("Block physical bounds"); - // Physical boundary conditions - parthenon::ApplyBoundaryConditions(rc); - } - } - } - - Kokkos::fence(); - Flag("Sync'd"); -} diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index a5a4ba5c..465f4b17 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -94,6 +94,10 @@ class CoordinateEmbedding { base.emplace(mpark::get(base_in)); } else if (mpark::holds_alternative(base_in)) { base.emplace(mpark::get(base_in)); + } else if (mpark::holds_alternative(base_in)) { + base.emplace(mpark::get(base_in)); + } else if (mpark::holds_alternative(base_in)) { + base.emplace(mpark::get(base_in)); } if (mpark::holds_alternative(transform_in)) { @@ -166,10 +170,18 @@ class CoordinateEmbedding { } else if (transform_str == "funky" || transform_str == "fmks") { if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); GReal hslope = pin->GetOrAddReal("coordinates", "hslope", 0.3); - GReal startx1 = pin->GetReal("parthenon/mesh", "x1min"); GReal mks_smooth = pin->GetOrAddReal("coordinates", "mks_smooth", 0.5); GReal poly_xt = pin->GetOrAddReal("coordinates", "poly_xt", 0.82); GReal poly_alpha = pin->GetOrAddReal("coordinates", "poly_alpha", 14.0); + // Set fmks to use x1min from our system for compatibility. Note THIS WILL CHANGE + GReal startx1 = 0.; // Default for temporary coordinate construction before mesh, future general default + if (pin->DoesParameterExist("coordinates", "fmks_zero_point")) { + startx1 = pin->GetReal("coordinates", "fmks_zero_point"); + } else if (pin->DoesParameterExist("parthenon/mesh", "x1min")) { + std::cout << "KHARMA WARNING: Constructing FMKS coordinates using mesh x1min is deprecated." << std::endl + << "Set coordinates/fmks_zero_point for consistent behavior." << std::endl; + startx1 = pin->GetReal("parthenon/mesh", "x1min"); + } transform.emplace(FunkyTransform(startx1, hslope, mks_smooth, poly_xt, poly_alpha)); } else { throw std::invalid_argument("Unsupported coordinate transform!"); @@ -182,6 +194,9 @@ class CoordinateEmbedding { #pragma hd_warning_disable KOKKOS_FUNCTION const CoordinateEmbedding& operator=(const CoordinateEmbedding& src) { + //CoordinateEmbedding copy(src); + //base.swap(copy.base); + //transform.swap(copy.transform); EmplaceSystems(src.base, src.transform); return *this; } @@ -231,6 +246,23 @@ class CoordinateEmbedding { return mpark::holds_alternative(base) && mpark::holds_alternative(transform); } + KOKKOS_INLINE_FUNCTION std::string variant_names() const + { + std::string basename( + mpark::visit( [&](const auto& self) { + return self.name; + }, base) + ); + + std::string transformname( + mpark::visit( [&](const auto& self) { + return self.name; + }, transform) + ); + + return basename + " " + transformname; + } + // Spell out the interface we take from BaseCoords // TODO add a gcon_embed, gdet_embed KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const @@ -441,7 +473,7 @@ class CoordinateEmbedding { coord_to_embed(Xnative, Xembed); // Set u^t to make u a velocity 4-vector in BL - Real gcov_bl[GR_DIM][GR_DIM]; + GReal gcov_bl[GR_DIM][GR_DIM]; if (mpark::holds_alternative(base) || mpark::holds_alternative(base)) { SphBLCoords(get_a()).gcov_embed(Xembed, gcov_bl); @@ -449,7 +481,8 @@ class CoordinateEmbedding { mpark::holds_alternative(base)) { SphBLExtG(get_a()).gcov_embed(Xembed, gcov_bl); } - GReal ucon_bl_fourv[GR_DIM]; + + Real ucon_bl_fourv[GR_DIM]; DLOOP1 ucon_bl_fourv[mu] = ucon_bl[mu]; set_ut(gcov_bl, ucon_bl_fourv); diff --git a/kharma/coordinates/coordinate_systems.hpp b/kharma/coordinates/coordinate_systems.hpp index 216c0f94..b0361618 100644 --- a/kharma/coordinates/coordinate_systems.hpp +++ b/kharma/coordinates/coordinate_systems.hpp @@ -76,6 +76,7 @@ */ class CartMinkowskiCoords { public: + static constexpr char name[] = "CartMinkowskiCoords"; static constexpr bool spherical = false; static constexpr GReal a = 0.0; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const @@ -89,6 +90,7 @@ class CartMinkowskiCoords { */ class SphMinkowskiCoords { public: + static constexpr char name[] = "SphMinkowskiCoords"; static constexpr bool spherical = true; static constexpr GReal a = 0.0; KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const @@ -110,6 +112,7 @@ class SphMinkowskiCoords { */ class SphKSCoords { public: + static constexpr char name[] = "SphKSCoords"; // BH Spin is a property of KS const GReal a; static constexpr bool spherical = true; @@ -180,6 +183,7 @@ class SphKSCoords { */ class SphKSExtG { public: + static constexpr char name[] = "SphKSExtG"; // BH Spin is a property of KS const GReal a; static constexpr bool spherical = true; @@ -263,6 +267,7 @@ class SphKSExtG { */ class SphBLCoords { public: + static constexpr char name[] = "SphBLCoords"; // BH Spin is a property of BL const GReal a; static constexpr bool spherical = true; @@ -299,6 +304,7 @@ class SphBLCoords { */ class SphBLExtG { public: + static constexpr char name[] = "SphBLExtG"; // BH Spin is a property of BL const GReal a; static constexpr bool spherical = true; @@ -344,6 +350,7 @@ class SphBLExtG { */ class NullTransform { public: + static constexpr char name[] = "NullTransform"; static constexpr GReal startx[3] = {-1, -1, -1}; static constexpr GReal stopx[3] = {-1, -1, -1}; // Coordinate transformations @@ -373,6 +380,7 @@ class NullTransform { */ class ExponentialTransform { public: + static constexpr char name[] = "ExponentialTransform"; static constexpr GReal startx[3] = {-1, 0., 0.}; static constexpr GReal stopx[3] = {-1, M_PI, 2*M_PI}; @@ -425,6 +433,7 @@ class ExponentialTransform { */ class SuperExponentialTransform { public: + static constexpr char name[] = "SuperExponentialTransform"; static constexpr GReal startx[3] = {-1, 0., 0.}; static constexpr GReal stopx[3] = {-1, M_PI, 2*M_PI}; @@ -465,7 +474,7 @@ class SuperExponentialTransform { dxdX[0][0] = 1.; const GReal super_dist = Xnative[1] - xn1br; dxdX[1][1] = m::exp(Xnative[1] + (super_dist > 0) * cpow2 * m::pow(super_dist, npow2)) - * (1 + cpow2 * npow2 * m::pow(super_dist, npow2-1)); + * (1 + (super_dist > 0) * cpow2 * npow2 * m::pow(super_dist, npow2-1)); dxdX[2][2] = 1.; dxdX[3][3] = 1.; } @@ -478,7 +487,7 @@ class SuperExponentialTransform { dXdx[0][0] = 1.; const GReal super_dist = Xnative[1] - xn1br; dXdx[1][1] = 1 / (m::exp(Xnative[1] + (super_dist > 0) * cpow2 * m::pow(super_dist, npow2)) - * (1 + cpow2 * npow2 * m::pow(super_dist, npow2-1))); + * (1 + (super_dist > 0) * cpow2 * npow2 * m::pow(super_dist, npow2-1))); dXdx[2][2] = 1.; dXdx[3][3] = 1.; } @@ -490,6 +499,7 @@ class SuperExponentialTransform { */ class ModifyTransform { public: + static constexpr char name[] = "ModifyTransform"; static constexpr GReal startx[3] = {-1, 0., 0.}; static constexpr GReal stopx[3] = {-1, 1., 2*M_PI}; @@ -549,6 +559,7 @@ class ModifyTransform { */ class FunkyTransform { public: + static constexpr char name[] = "FunkyTransform"; static constexpr GReal startx[3] = {-1, 0., 0.}; static constexpr GReal stopx[3] = {-1, 1., 2*M_PI}; diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index b1359873..a94a3491 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -69,14 +69,16 @@ GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): Uniform n3 = rs.nx3 > 1 ? rs.nx3 + 2*Globals::nghost : 1; //cout << "Initialized coordinates with nghost " << Globals::nghost << std::endl; - // TODO TODO set averaging/correcting prefs here + connection_average_points = pin->GetOrAddInteger("coordinates", "connection_average_points", 1); + correct_connections = pin->GetOrAddBoolean("coordinates", "correct_connections", false); init_GRCoordinates(*this); } - GRCoordinates::GRCoordinates(const GRCoordinates &src, int coarsen): UniformCartesian(src, coarsen), - coords(src.coords), n1(src.n1/coarsen), n2(src.n2/coarsen), n3(src.n3/coarsen) + coords(src.coords), n1(src.n1/coarsen), n2(src.n2/coarsen), n3(src.n3/coarsen), + connection_average_points(src.connection_average_points), + correct_connections(src.correct_connections) { //std::cerr << "Calling coarsen constructor" << std::endl; init_GRCoordinates(*this); diff --git a/kharma/coordinates/gr_coordinates.hpp b/kharma/coordinates/gr_coordinates.hpp index 28ad6955..8a48ffd9 100644 --- a/kharma/coordinates/gr_coordinates.hpp +++ b/kharma/coordinates/gr_coordinates.hpp @@ -100,7 +100,9 @@ class GRCoordinates : public parthenon::UniformCartesian // that is, host- & device-side indiscriminately KOKKOS_FUNCTION GRCoordinates(): UniformCartesian() {}; KOKKOS_FUNCTION GRCoordinates(const GRCoordinates &src): UniformCartesian(src), - n1(src.n1), n2(src.n2), n3(src.n3), coords(src.coords) + n1(src.n1), n2(src.n2), n3(src.n3), coords(src.coords), + connection_average_points(src.connection_average_points), + correct_connections(src.correct_connections) { //std::cerr << "Calling copy constructor size " << src.n1 << " " << src.n2 << std::endl; #if !FAST_CARTESIAN && !NO_CACHE @@ -121,6 +123,8 @@ class GRCoordinates : public parthenon::UniformCartesian n1 = src.n1; n2 = src.n2; n3 = src.n3; + connection_average_points = src.connection_average_points; + correct_connections = src.correct_connections; #if !FAST_CARTESIAN && !NO_CACHE gcon_direct = src.gcon_direct; gcov_direct = src.gcov_direct; diff --git a/kharma/coordinates/root_find.hpp b/kharma/coordinates/root_find.hpp index 63d9d94e..57e4bf37 100644 --- a/kharma/coordinates/root_find.hpp +++ b/kharma/coordinates/root_find.hpp @@ -118,4 +118,4 @@ else if ((rc - r) * (rb - r) < 0.) Xa[1] = Xc[1];\ else Xb[1] = Xc[1];\ }\ - Xnative[1] = Xc[1]; \ No newline at end of file + Xnative[1] = Xc[1]; diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index da489215..35652ad7 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -71,6 +71,12 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta const bool use_jcon = pkgs.count("Current"); const bool use_linesearch = (use_implicit) ? pkgs.at("Implicit")->Param("linesearch") : false; + // If we cleaned up, this added other fields marked FillDerived + // Remove them before we allocate the space + if (use_b_cleanup) { + B_Cleanup::RemoveExtraFields(blocks); + } + // Allocate the fluid states ("containers") we need for each block for (auto& pmb : blocks) { // first make other useful containers @@ -98,8 +104,6 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta } } - //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); - // Big synchronous region: get & apply fluxes to advance the fluid state // num_partitions is nearly always 1 const int num_partitions = pmesh->DefaultNumPartitions(); @@ -177,8 +181,8 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), - md_sub_step_init.get(), md_solver.get()); + t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + md_sub_step_init.get(), md_solver.get()); } // Make sure the primitive values of *explicitly-evolved* variables are updated. @@ -279,6 +283,17 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta } } + // B Field cleanup: this is a separate solve so it's split out + // It's also really slow when enabled so we don't care too much about limiting regions, etc. + if (use_b_cleanup && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { + TaskRegion &cleanup_region = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = cleanup_region[i]; + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + tl.AddTask(t_none, B_Cleanup::CleanupDivergence, md_sub_step_final); + } + } + // Second boundary sync: // ensure that primitive variables in ghost zones are *exactly* // identical to their physical counterparts, now that they have been diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index a50bfba3..45b864c1 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -55,15 +55,17 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // Driver options // The two current drivers are "kharma" or "imex", with the former being the usual KHARMA - // driver, and the latter supporting implicit stepping of some or all variables - // Mostly, packages should react to the "sync_prims" option and any option they + // driver (formerly HARM driver), and the latter supporting implicit stepping of some or all variables + // Mostly, packages should react to e.g. the "sync_prims" option rather than the driver name bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); std::string driver_type = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); + if (driver_type == "harm") driver_type = "kharma"; // TODO enum rather than strings? params.Add("type", driver_type); // Record whether we marked the prims or cons as "FillGhost." This also translates to whether we consider // primitive or conserved state to be the ground truth when updating values in a step. - bool sync_prims = !(driver_type == "kharma" || driver_type == "harm"); + // Currently "imex" and "simple" drivers both sync primitive vars + bool sync_prims = !(driver_type == "kharma"); params.Add("sync_prims", sync_prims); // Synchronize boundary variables twice. Ensures KHARMA is agnostic to the breakdown @@ -112,16 +114,6 @@ void KHARMADriver::AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage) { const TaskID t_none(0); - // MPI boundary exchange, done over MeshData objects/partitions at once - const int num_partitions = pmesh->DefaultNumPartitions(); // Usually 1 - TaskRegion &bound_sync = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = bound_sync[i]; - // This is a member function of KHARMADriver, so it inherits 'integrator' - auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); - AddMPIBoundarySync(t_none, tl, mbd_sub_step_final); - } - // Parthenon's call for bounds is MeshBlock, it sucks int nblocks = pmesh->block_list.size(); TaskRegion &async_region2 = tc.AddRegion(nblocks); @@ -132,35 +124,65 @@ void KHARMADriver::AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage) tl.AddTask(t_none, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); } + // MPI boundary exchange, done over MeshData objects/partitions at once + const int num_partitions = pmesh->DefaultNumPartitions(); // Usually 1 + TaskRegion &bound_sync = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = bound_sync[i]; + // This is a member function of KHARMADriver, so it inherits 'integrator' + auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + AddMPIBoundarySync(t_none, tl, mbd_sub_step_final); + } } -TaskID KHARMADriver::AddMPIBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1) +TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> mc1) { - // Readability - using parthenon::cell_centered_bvars::SendBoundBufs; - using parthenon::cell_centered_bvars::ReceiveBoundBufs; - using parthenon::cell_centered_bvars::SetBounds; - constexpr auto local = parthenon::BoundaryType::local; - constexpr auto nonlocal = parthenon::BoundaryType::nonlocal; - // Send all, receive/set local after sending - auto send = - tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); - - auto t_send_local = - tl.AddTask(t_start, parthenon::cell_centered_bvars::SendBoundBufs, mc1); - auto t_recv_local = - tl.AddTask(t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); - auto t_set_local = - tl.AddTask(t_recv_local, parthenon::cell_centered_bvars::SetBounds, mc1); - - // Receive/set nonlocal - auto t_recv = tl.AddTask( - t_start, parthenon::cell_centered_bvars::ReceiveBoundBufs, mc1); - auto t_set = tl.AddTask(t_recv, parthenon::cell_centered_bvars::SetBounds, mc1); + auto t_start_sync = t_start; + + if (0) { //(mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { + TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; + TaskID t_ptou_final(0); + int i_task = 0; + for (int i_block = 0; i_block < mc1->NumBlocks(); i_block++) { + auto &rc = mc1->GetBlockData(i_block); + for (int i_bnd = 0; i_bnd < BOUNDARY_NFACES; i_bnd++) { + if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || + rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { + t_all_ptou[i_task] = tl.AddTask(t_start, Flux::BlockPtoU_Send, rc.get(), BoundaryDomain((BoundaryFace) i_bnd), false); + t_ptou_final = t_ptou_final | t_all_ptou[i_task]; + i_task++; + } + } + } + t_start_sync = t_ptou_final; + } - // TODO add AMR prolongate/restrict here (and/or maybe option not to?) + auto t_sync_done = parthenon::cell_centered_bvars::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); + auto t_bounds = t_sync_done; + + // TODO(BSP) careful about how AMR interacts with below + Kokkos::fence(); + + // If we're "syncing primitive variables" but just exchanged cons.B, we need to recover the prims + if (mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { + TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; + TaskID t_utop_final(0); + int i_task = 0; + for (int i_block = 0; i_block < mc1->NumBlocks(); i_block++) { + auto &rc = mc1->GetBlockData(i_block); + for (int i_bnd = 0; i_bnd < BOUNDARY_NFACES; i_bnd++) { + if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || + rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { + t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BlockUtoPExceptMHD, rc.get(), BoundaryDomain((BoundaryFace) i_bnd), false); + t_utop_final = t_utop_final | t_all_utop[i_task]; + i_task++; + } + } + } + t_bounds = t_utop_final; + } - return t_set | t_set_local; + return t_bounds; } void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) @@ -168,68 +190,24 @@ void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_ Flag("Syncing all bounds"); TaskID t_none(0); - // If we're using the ImEx driver, where primitives are fundamental, AddMPIBoundarySync() - // will only sync those, and we can call PtoU over everything after. - // If "AddMPIBoundarySync" means syncing conserved variables, we have to call PtoU *before* - // the MPI sync operation, then recover the primitive vars *again* afterward. - auto pmesh = md->GetMeshPointer(); - bool sync_prims = pmesh->packages.Get("Driver")->Param("sync_prims"); - - // TODO clean this up when ApplyBoundaryConditions gets a MeshData version - auto &block_list = pmesh->block_list; - - if (sync_prims) { - // If we're syncing the primitive vars, we just sync once - TaskCollection tc; - auto tr = tc.AddRegion(1); - AddMPIBoundarySync(t_none, tr[0], md); - while (!tr.Execute()); - - // Then PtoU - for (auto &pmb : block_list) { - auto& rc = pmb->meshblock_data.Get(); - - if (apply_domain_bounds) { - Flag("Block physical bounds"); - // Physical boundary conditions - parthenon::ApplyBoundaryConditions(rc); - } - - Flag("Block fill Conserved"); - Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); - } - } else { - // If we're syncing the conserved vars... - // Honestly, the easiest way through this sync is: - // 1. PtoU everywhere - for (auto &pmb : block_list) { + // 1. PtoU on the interior to ensure we're up-to-date + Flux::MeshPtoU(md.get(), IndexDomain::interior, false); + + // 2. Sync MPI bounds + // This call syncs the primitive variables when using the ImEx driver, and cons + // + TaskCollection tc; + auto tr = tc.AddRegion(1); + AddMPIBoundarySync(t_none, tr[0], md); + while (!tr.Execute()); + + if (apply_domain_bounds) { + // 3. Apply physical bounds block-by-block + // TODO clean this up when ApplyBoundaryConditions gets a MeshData version + for (auto &pmb : md->GetMeshPointer()->block_list) { auto& rc = pmb->meshblock_data.Get(); - Flag("Block fill conserved"); - Flux::BlockPtoU(rc.get(), IndexDomain::entire, false); - } - - // 2. Sync MPI bounds like a normal step - TaskCollection tc; - auto tr = tc.AddRegion(1); - AddMPIBoundarySync(t_none, tr[0], md); - while (!tr.Execute()); - - // 3. UtoP everywhere - for (auto &pmb : block_list) { - auto& rc = pmb->meshblock_data.Get(); - - Flag("Block fill Derived"); - // Fill P again, including ghost zones - // But, sice we sync'd GRHD primitives already, - // leave those off - // (like we do in a normal boundary sync) - Packages::BlockUtoPExceptMHD(rc.get(), IndexDomain::entire); - - if (apply_domain_bounds) { - Flag("Block physical bounds"); - // Physical boundary conditions - parthenon::ApplyBoundaryConditions(rc); - } + // Physical boundary conditions + parthenon::ApplyBoundaryConditions(rc); } } @@ -267,8 +245,8 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst case RType::ppm: case RType::mp5: case RType::weno5_lower_poles: - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; + std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl + << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } return t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index bd131c4b..f1336669 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -109,7 +109,7 @@ class KHARMADriver : public MultiStageDriver { * This sequence is used identically in several places, so it makes sense * to define once and use elsewhere. */ - static TaskID AddMPIBoundarySync(TaskID t_start, TaskList &tl, std::shared_ptr> mc1); + static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> mc1); /** * Calculate the fluxes in each direction diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 3af0c72d..6423a2ba 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -83,6 +83,12 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const bool use_electrons = pkgs.count("Electrons"); const bool use_jcon = pkgs.count("Current"); + // If we cleaned up, this added other fields marked FillDerived + // Remove them before we allocate the space + if (use_b_cleanup) { + B_Cleanup::RemoveExtraFields(blocks); + } + // Allocate the fluid states ("containers") we need for each block for (auto& pmb : blocks) { // first make other useful containers @@ -235,7 +241,10 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int mbd_sub_step_init.get(), mbd_sub_step_final.get()); } - auto t_step_done = t_heat_electrons; + // Make sure *all* conserved vars are synchronized at step end + auto t_ptou = tl.AddTask(t_heat_electrons, Flux::BlockPtoU, mbd_sub_step_final.get(), IndexDomain::entire, false); + + auto t_step_done = t_ptou; // Estimate next time step based on ctop if (stage == integrator->nstages) { @@ -250,6 +259,17 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int } } + // B Field cleanup: this is a separate solve so it's split out + // It's also really slow when enabled so we don't care too much about limiting regions, etc. + if (use_b_cleanup && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { + TaskRegion &cleanup_region = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &tl = cleanup_region[i]; + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + tl.AddTask(t_none, B_Cleanup::CleanupDivergence, md_sub_step_final); + } + } + // Second boundary sync: // ensure that primitive variables in ghost zones are *exactly* // identical to their physical counterparts, now that they have been diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 0d04cf83..a019825c 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -120,6 +120,68 @@ TaskStatus Flux::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) return TaskStatus::complete; } +TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "Getting conserved GRMHD variables"); + // Pointers + auto pmb = rc->GetBlockPointer(); + const int ndim = pmb->pmy_mesh->ndim; + // Options + const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + // Pack variables + PackIndexMap prims_map, cons_map; + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + IndexRange ib = bounds.GetBoundsI(domain); + IndexRange jb = bounds.GetBoundsJ(domain); + IndexRange kb = bounds.GetBoundsK(domain); + + // Modify the bounds to reflect zones we're sending, rather than actual ghosts + int ng = Globals::nghost; + if (domain == IndexDomain::inner_x1) { + ib.s += ng; + ib.e += ng; + } else if (domain == IndexDomain::outer_x1) { + ib.s -= ng; + ib.e -= ng; + } else if (domain == IndexDomain::inner_x2) { + if (ndim < 2) return TaskStatus::complete; + jb.s += ng; + jb.e += ng; + } else if (domain == IndexDomain::outer_x2) { + if (ndim < 2) return TaskStatus::complete; + jb.s -= ng; + jb.e -= ng; + } else if (domain == IndexDomain::inner_x3) { + if (ndim < 3) return TaskStatus::complete; + kb.s += ng; + kb.e += ng; + } else if (domain == IndexDomain::outer_x3) { + if (ndim < 3) return TaskStatus::complete; + kb.s -= ng; + kb.e -= ng; + } + + const auto& G = pmb->coords; + + pmb->par_for("p_to_u", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); + } + ); + + + Flag(rc, "Got conserved variables"); + return TaskStatus::complete; +} + void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) { Flag(mdudt, "Adding GRMHD source term"); diff --git a/kharma/flux/flux.hpp b/kharma/flux/flux.hpp index 9a35ec0d..1760a75d 100644 --- a/kharma/flux/flux.hpp +++ b/kharma/flux/flux.hpp @@ -68,6 +68,12 @@ TaskStatus BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse TaskStatus BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse=false); TaskStatus MeshPtoU(MeshData *md, IndexDomain domain, bool coarse=false); +/** + * As above, except that IndexDomains of ghost cells are taken to cover + * cells *sent* (that is, part of the domain) rather than received + */ +TaskStatus BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, bool coarse); + // Fluxes a.k.a. "Approximate Riemann Solvers" // More complex solvers require speed estimates not calculable completely from // invariants, necessitating frame transformations and related madness. diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 90695357..fe10f981 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -59,7 +59,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ #include #include -std::vector Implicit::get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit) +std::vector Implicit::GetOrderedNames(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit) { auto pmb0 = rc->GetBlockPointer(); std::vector out; @@ -199,8 +199,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // The implicit variables need to be first, so we know how to iterate over just them to fill // just the residual & Jacobian we care about, which makes the solve faster. auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = get_ordered_names(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); - auto ordered_cons = get_ordered_names(mbd_full_step_init.get(), Metadata::Conserved); + auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); + auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -222,7 +222,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const int nblock = U_full_step_init_all.GetDim(5); const int nvar = U_full_step_init_all.GetDim(4); // Get number of implicit variables - auto implicit_vars = get_ordered_names(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive"), true); + auto implicit_vars = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive"), true); PackIndexMap implicit_prims_map; auto& P_full_step_init_implicit = md_full_step_init->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P_full_step_init_implicit.GetDim(4); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index d03c67ef..c04854ba 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -79,7 +79,7 @@ TaskStatus Step(MeshData *md_full_step_init, MeshData *md_sub_step_i /** * Get the names of all variables matching 'flag' in a deterministic order, placing implicitly-evolved variables first. */ -std::vector get_ordered_names(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit=false); +std::vector GetOrderedNames(MeshBlockData *rc, const MetadataFlag& flag, bool only_implicit=false); /** * Calculate the residual generated by the trial primitives P_test diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 29134d62..76ce1494 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -204,6 +204,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) throw std::invalid_argument("Not enough radial zones were specified to put 5 zones inside EH!"); } pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); + pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(Rhor)); } //cout << "Setting x1min: " << x1min << " x1max " << x1max << " based on BH with a=" << a << endl; @@ -232,12 +233,26 @@ void KHARMA::FixParameters(std::unique_ptr& pin) } // Set default bounds covering our coordinates/transform - for (int i = X1DIR; i <= X3DIR; i++) { - if (tmp_coords.startx(i) > 0) - pin->GetOrAddReal("parthenon/mesh", "x1min", tmp_coords.startx(i)); - if (tmp_coords.stopx(i) > 0) - pin->GetOrAddReal("parthenon/mesh", "x1max", tmp_coords.stopx(i)); - } + std::cout << "Coordinate transform has boundaries: " + << tmp_coords.startx(1) << " " + << tmp_coords.startx(2) << " " + << tmp_coords.startx(3) << " to " + << tmp_coords.stopx(1) << " " + << tmp_coords.stopx(2) << " " + << tmp_coords.stopx(3) << std::endl; + // TODO(BSP) is this worth looping? I say probably no. + if (tmp_coords.startx(1) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x1min", tmp_coords.startx(1)); + if (tmp_coords.stopx(1) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x1max", tmp_coords.stopx(1)); + if (tmp_coords.startx(2) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x2min", tmp_coords.startx(2)); + if (tmp_coords.stopx(2) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x2max", tmp_coords.stopx(2)); + if (tmp_coords.startx(3) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x3min", tmp_coords.startx(3)); + if (tmp_coords.stopx(3) >= 0) + pin->GetOrAddReal("parthenon/mesh", "x3max", tmp_coords.stopx(3)); Flag("Fixed"); } diff --git a/kharma/kharma_utils.hpp b/kharma/kharma_utils.hpp index f662454f..80e0308e 100644 --- a/kharma/kharma_utils.hpp +++ b/kharma/kharma_utils.hpp @@ -35,6 +35,7 @@ #include "decs.hpp" +#include #include #include #include @@ -132,23 +133,28 @@ KOKKOS_INLINE_FUNCTION T close_to(const T& x, const T& y, const Real& rel_tol=1e // Quickly zero n elements of an array // Types can fail to resolve if gzeroN() calls zeroN(), // so we duplicate code a bit +// TODO forceinline template KOKKOS_INLINE_FUNCTION void zero(T* a, const int& n) { memset(a, 0, n*sizeof(T)); + //for(int i = 0; i < n; i++) a[i] = 0.; } template KOKKOS_INLINE_FUNCTION void gzero(T a[GR_DIM]) { memset(a, 0, GR_DIM*sizeof(T)); + //for(int i = 0; i < GR_DIM; i++) a[i] = 0.; } template KOKKOS_INLINE_FUNCTION void zero2(T* a[], const int& n) { memset(&(a[0][0]), 0, n*sizeof(T)); + //for(int i = 0; i < n; i++) (&(a[0][0]))[i] = 0.; } template KOKKOS_INLINE_FUNCTION void gzero2(T a[GR_DIM][GR_DIM]) { memset(&(a[0][0]), 0, GR_DIM*GR_DIM*sizeof(T)); + //for(int i = 0; i < GR_DIM*GR_DIM; i++) (&(a[0][0]))[i] = 0.; } diff --git a/kharma/main.cpp b/kharma/main.cpp index 9430a0ac..78c6d3dc 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -157,7 +157,7 @@ int main(int argc, char *argv[]) // Any init which may be run even when restarting, or requires all // MeshBlocks to be initialized already auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_restart = (prob == "resize_restart") || (prob == "resize_restart") || pman.IsRestart(); + bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); KHARMA::PostInitialize(pin, pmesh, is_restart); Flag("Post-initialization completed"); diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 4da15f74..513314a8 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -34,6 +34,7 @@ #include "bondi.hpp" +#include "boundaries.hpp" #include "floors.hpp" #include "flux_functions.hpp" @@ -52,8 +53,14 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // By default, stay away from the outer BL coordinate singularity const Real a = pin->GetReal("coordinates", "a"); const Real rin_bondi_default = 1 + m::sqrt(1 - a*a) + 0.1; - // TODO take r_shell - const Real rin_bondi = pin->GetOrAddReal("bondi", "r_in", rin_bondi_default); + // Prefer parameter bondi/r_in vs bondi/r_shell + Real rin_bondi_tmp; + if (pin->DoesParameterExist("bondi", "r_in")) { + rin_bondi_tmp = pin->GetReal("bondi", "r_in"); + } else { + rin_bondi_tmp = pin->GetOrAddReal("bondi", "r_shell", rin_bondi_default); + } + const Real rin_bondi = rin_bondi_tmp; const bool fill_interior = pin->GetOrAddBoolean("bondi", "fill_interior", false); const bool zero_velocity = pin->GetOrAddBoolean("bondi", "zero_velocity", false); @@ -74,17 +81,26 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - if (pin->GetOrAddBoolean("bondi", "set_outer_bound", true)) { - bound_pkg->KHARMAOuterX1Boundary = SetBondi; - } - if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { - bound_pkg->KHARMAInnerX1Boundary = SetBondi; + if (pin->GetOrAddBoolean("bondi", "use_dirichlet", false)) { + SetBondi(rc, IndexDomain::entire); + // Register a Dirichlet boundary condition + bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; + bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; + // Fill the Dirichlet caches based on the current ghost zone contents + KBoundaries::SetDomainDirichlet(rc, IndexDomain::inner_x1, false); + KBoundaries::SetDomainDirichlet(rc, IndexDomain::outer_x1, false); + } else { + if (pin->GetOrAddBoolean("bondi", "set_outer_bound", true)) { + bound_pkg->KHARMAOuterX1Boundary = SetBondi; + } + if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { + bound_pkg->KHARMAInnerX1Boundary = SetBondi; + } + // Set the interior domain to the analytic solution to begin + // This tests that PostInitialize will correctly fill ghost zones with the boundary we set + SetBondi(rc, IndexDomain::interior); } - // Set the interior domain to the analytic solution to begin - // This tests that PostInitialize will correctly fill ghost zones with the boundary we set - SetBondi(rc, IndexDomain::interior); - if (rin_bondi > pin->GetReal("coordinates", "r_in") && !(fill_interior)) { // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable. @@ -119,18 +135,6 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain // Just the X1 right boundary GRCoordinates G = pmb->coords; - // Solution constants - // These don't depend on which zone we're calculating - const Real n = 1. / (gam - 1.); - const Real uc = m::sqrt(1. / (2. * rs)); - const Real Vc = m::sqrt(uc * uc / (1. - 3. * uc * uc)); - const Real Tc = -n * Vc * Vc / ((n + 1.) * (n * Vc * Vc - 1.)); - const Real C1 = uc * rs * rs * m::pow(Tc, n); - const Real A = 1. + (1. + n) * Tc; - const Real C2 = A * A * (1. - 2. / rs + uc * uc); - const Real K = m::pow(4 * M_PI * C1 / mdot, 1/n); - const Real Kn = m::pow(K, n); - // Set the Bondi conditions wherever we're asked auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; @@ -149,25 +153,16 @@ TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain // or let it be filled with floor values later if (r < rin_bondi) { if (fill_interior) { - // values at infinity; would need modifications below - /* - Real Tinf = (m::sqrt(C2) - 1.) / (n + 1); // temperature at infinity - rho = m::pow(Tinf,n); - u = rho * Tinf * n; - */ // just match at the rin_bondi value r = rin_bondi; + // TODO(BSP) could also do values at inf, restore that? } else { return; } } - const Real T = get_T(r, C1, C2, n, rs); - const Real Tn = m::pow(T, n); - const Real rho = Tn / Kn; - const Real u = rho * T * n; - - const Real ur = (zero_velocity) ? 0. : -C1 / (Tn * r * r); + Real rho, u, ur; + get_bondi_soln(r, rs, mdot, gam, rho, u, ur); // Get the native-coordinate 4-vector corresponding to ur const Real ucon_bl[GR_DIM] = {0, ur, 0, 0}; diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 72107f0b..fc1732d5 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -109,3 +109,26 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c return Th; } + +KOKKOS_INLINE_FUNCTION void get_bondi_soln(const Real &r, const Real &rs, const Real &mdot, const Real &gam, + Real &rho, Real &u, Real &ur) +{ + // Solution constants + // These don't depend on which zone we're calculating + const Real n = 1. / (gam - 1.); + const Real uc = m::sqrt(1. / (2. * rs)); + const Real Vc = m::sqrt(uc * uc / (1. - 3. * uc * uc)); + const Real Tc = -n * Vc * Vc / ((n + 1.) * (n * Vc * Vc - 1.)); + const Real C1 = uc * rs * rs * m::pow(Tc, n); + const Real A = 1. + (1. + n) * Tc; + const Real C2 = A * A * (1. - 2. / rs + uc * uc); + const Real K = m::pow(4 * M_PI * C1 / mdot, 1/n); + const Real Kn = m::pow(K, n); + + const Real T = get_T(r, C1, C2, n, rs); + const Real Tn = m::pow(T, n); + + rho = Tn / Kn; + u = rho * T * n; + ur = -C1 / (Tn * r * r); +} diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 094ffb91..95c3763a 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -149,10 +149,10 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame const int n1 = pmb->cellbounds.ncellsi(IndexDomain::interior); const int ng = ib.e - ib_in.e; - auto p_bound_left = rc->Get("bound.inner_x1").data; - auto p_bound_left_host = p_bound_left.GetHostMirror(); - auto p_bound_right = rc->Get("bound.outer_x1").data; - auto p_bound_right_host = p_bound_right.GetHostMirror(); + // auto p_bound_left = rc->Get("bound.inner_x1").data; + // auto p_bound_left_host = p_bound_left.GetHostMirror(); + // auto p_bound_right = rc->Get("bound.outer_x1").data; + // auto p_bound_right_host = p_bound_right.GetHostMirror(); // Load coordinates 'r' and compare against grid values double rCoords[n1 + 2*ng]; @@ -252,34 +252,34 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame } // Save boundary values for Dirichlet boundary conditions - if (i < ng) { - p_bound_left_host(m_p.RHO, k, j, i) = rho_host(k, j, i); - p_bound_left_host(m_p.UU, k, j, i) = u_host(k, j, i); - p_bound_left_host(m_p.U1, k, j, i) = uvec_host(V1, k, j, i); - p_bound_left_host(m_p.U2, k, j, i) = uvec_host(V2, k, j, i); - p_bound_left_host(m_p.U3, k, j, i) = uvec_host(V3, k, j, i); - p_bound_left_host(m_p.B1, k, j, i) = B_host(V1, k, j, i); - p_bound_left_host(m_p.B2, k, j, i) = B_host(V2, k, j, i); - p_bound_left_host(m_p.B3, k, j, i) = B_host(V3, k, j, i); - if (use_emhd) { - p_bound_left_host(m_p.Q, k, j, i) = q_host(k, j, i); - p_bound_left_host(m_p.DP, k, j, i) = dP_host(k, j, i); - } - } else if (i >= n1 + ng) { - int ii = i - (n1 + ng); - p_bound_right_host(m_p.RHO, k, j, ii) = rho_host(k, j, i); - p_bound_right_host(m_p.UU, k, j, ii) = u_host(k, j, i); - p_bound_right_host(m_p.U1, k, j, ii) = uvec_host(V1, k, j, i); - p_bound_right_host(m_p.U2, k, j, ii) = uvec_host(V2, k, j, i); - p_bound_right_host(m_p.U3, k, j, ii) = uvec_host(V3, k, j, i); - p_bound_right_host(m_p.B1, k, j, ii) = B_host(V1, k, j, i); - p_bound_right_host(m_p.B2, k, j, ii) = B_host(V2, k, j, i); - p_bound_right_host(m_p.B3, k, j, ii) = B_host(V3, k, j, i); - if (use_emhd) { - p_bound_right_host(m_p.Q, k, j, ii) = q_host(k, j, i); - p_bound_right_host(m_p.DP, k, j, ii) = dP_host(k, j, i); - } - } + // if (i < ng) { + // p_bound_left_host(m_p.RHO, k, j, i) = rho_host(k, j, i); + // p_bound_left_host(m_p.UU, k, j, i) = u_host(k, j, i); + // p_bound_left_host(m_p.U1, k, j, i) = uvec_host(V1, k, j, i); + // p_bound_left_host(m_p.U2, k, j, i) = uvec_host(V2, k, j, i); + // p_bound_left_host(m_p.U3, k, j, i) = uvec_host(V3, k, j, i); + // p_bound_left_host(m_p.B1, k, j, i) = B_host(V1, k, j, i); + // p_bound_left_host(m_p.B2, k, j, i) = B_host(V2, k, j, i); + // p_bound_left_host(m_p.B3, k, j, i) = B_host(V3, k, j, i); + // if (use_emhd) { + // p_bound_left_host(m_p.Q, k, j, i) = q_host(k, j, i); + // p_bound_left_host(m_p.DP, k, j, i) = dP_host(k, j, i); + // } + // } else if (i >= n1 + ng) { + // int ii = i - (n1 + ng); + // p_bound_right_host(m_p.RHO, k, j, ii) = rho_host(k, j, i); + // p_bound_right_host(m_p.UU, k, j, ii) = u_host(k, j, i); + // p_bound_right_host(m_p.U1, k, j, ii) = uvec_host(V1, k, j, i); + // p_bound_right_host(m_p.U2, k, j, ii) = uvec_host(V2, k, j, i); + // p_bound_right_host(m_p.U3, k, j, ii) = uvec_host(V3, k, j, i); + // p_bound_right_host(m_p.B1, k, j, ii) = B_host(V1, k, j, i); + // p_bound_right_host(m_p.B2, k, j, ii) = B_host(V2, k, j, i); + // p_bound_right_host(m_p.B3, k, j, ii) = B_host(V3, k, j, i); + // if (use_emhd) { + // p_bound_right_host(m_p.Q, k, j, ii) = q_host(k, j, i); + // p_bound_right_host(m_p.DP, k, j, ii) = dP_host(k, j, i); + // } + // } } } } @@ -300,10 +300,13 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame q.DeepCopy(q_host); dP.DeepCopy(dP_host); } - p_bound_left.DeepCopy(p_bound_left_host); - p_bound_right.DeepCopy(p_bound_right_host); + // p_bound_left.DeepCopy(p_bound_left_host); + // p_bound_right.DeepCopy(p_bound_right_host); Kokkos::fence(); + KBoundaries::SetDomainDirichlet(rc, IndexDomain::inner_x1, false); + KBoundaries::SetDomainDirichlet(rc, IndexDomain::outer_x1, false); + Flag("Initialized"); return TaskStatus::complete; diff --git a/kharma/prob/gizmo.cpp b/kharma/prob/gizmo.cpp index f83860fd..2ce4602c 100644 --- a/kharma/prob/gizmo.cpp +++ b/kharma/prob/gizmo.cpp @@ -77,7 +77,7 @@ TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain //std::cerr << "GIZMO on domain: " << BoundaryName(domain) << std::endl; // Don't apply GIZMO initialization to X1 boundaries if (domain == IndexDomain::outer_x1 || domain == IndexDomain::inner_x1) { - return; + return TaskStatus::complete; } PackIndexMap prims_map, cons_map; @@ -98,18 +98,6 @@ TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain GRCoordinates G = pmb->coords; CoordinateEmbedding cs = G.coords; - // Solution constants - // These don't depend on which zone we're calculating - const Real n = 1. / (gam - 1.); - const Real uc = m::sqrt(1. / (2. * rs)); - const Real Vc = m::sqrt(uc * uc / (1. - 3. * uc * uc)); - const Real Tc = -n * Vc * Vc / ((n + 1.) * (n * Vc * Vc - 1.)); - const Real C1 = uc * rs * rs * m::pow(Tc, n); - const Real A = 1. + (1. + n) * Tc; - const Real C2 = A * A * (1. - 2. / rs + uc * uc); - const Real K = m::pow(4 * M_PI * C1 / mdot, 1/n); - const Real Kn = m::pow(K, n); - // Set the Bondi conditions wherever we're asked auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; diff --git a/kharma/prob/gizmo.hpp b/kharma/prob/gizmo.hpp index 6f1cfb27..77093efb 100644 --- a/kharma/prob/gizmo.hpp +++ b/kharma/prob/gizmo.hpp @@ -94,29 +94,23 @@ KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const C { // Solution constants for velocity prescriptions // Ideally these could be cached but preformance isn't an issue here - Real mdot = 1.; // mdot and rs defined arbitrarily - Real n = 1. / (gam - 1.); - Real uc = sqrt(mdot / (2. * rs)); - Real Vc = -sqrt(pow(uc, 2) / (1. - 3. * pow(uc, 2))); - Real Tc = -n * pow(Vc, 2) / ((n + 1.) * (n * pow(Vc, 2) - 1.)); - Real C1 = uc * pow(rs, 2) * pow(Tc, n); - Real C2 = pow(1. + (1. + n) * Tc, 2) * (1. - 2. * mdot / rs + pow(C1, 2) / (pow(rs, 4) * pow(Tc, 2 * n))); - + Real mdot = 1.; // mdot defined arbitrarily //Real rs = 1./sqrt(T); //1000.; + GReal Xnative[GR_DIM], Xembed[GR_DIM]; G.coord(k, j, i, Loci::center, Xnative); G.coord_embed(k, j, i, Loci::center, Xembed); GReal r = Xembed[1]; - // Use Bondi infall velocity - Real rho, u; - Real T = get_T(r, C1, C2, n, rs); - Real ucon_bl[GR_DIM] = {0}; + // Get GIZMO or vacuum/Bondi data + Real rho, u, ur; if (r < rin_init * 0.9){ // Vacuum values for interior rho = vacuum_rho; u = vacuum_rho * vacuum_u_over_rho; - ucon_bl[1] = -C1 / (pow(T, n) * pow(r, 2)); + // Radial velocity from Bondi solution + Real rho_tmp, u_tmp; + get_bondi_soln(r, rs, mdot, gam, rho_tmp, u_tmp, ur); } else { // linear interpolation int itemp; GReal del; @@ -125,9 +119,10 @@ KOKKOS_INLINE_FUNCTION void get_prim_gizmo_shell(const GRCoordinates& G, const C del = 0; // just copy over the smallest r values } rho = rhoarr(itemp) * (1.-del) + rhoarr(itemp+1) * del; - u = rho * (Tarr(itemp) * (1.-del) + Tarr(itemp+1) * del)*n; - ucon_bl[1] = 0.; + u = rho * (Tarr(itemp) * (1.-del) + Tarr(itemp+1) * del) / (gam - 1.); + ur = 0.; } + Real ucon_bl[GR_DIM] = {0., ur, 0., 0.}; // Set u^t and transform to native coordinates GReal ucon_native[GR_DIM]; diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index a94d8e64..bd32c501 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -104,7 +104,8 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrGetMeshPointer(); - const bool use_b_flux_ct = pmesh->packages.AllPackages().count("B_FluxCT"); + const bool use_b_flux_ct = pmesh->packages.AllPackages().count("B_FluxCT") + || pmesh->packages.AllPackages().count("B_Cleanup"); const bool use_b_cd = pmesh->packages.AllPackages().count("B_CD"); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); @@ -177,32 +178,30 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr{"prims.B"}, rc.get(), norm); } } - } - if (verbose > 0) { // Measure again to check. We'll add divB too, later - Real bsq_min, bsq_max, p_max, beta_min; - if (beta_calc_legacy) { - bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); - p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); - beta_min = p_max / (0.5 * bsq_max); - } else { - beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); - } - if (MPIRank0()) { + if (verbose > 0) { + Real bsq_min, bsq_max, p_max, beta_min; if (beta_calc_legacy) { - std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; - std::cout << "Pressure max pre-norm: " << p_max << std::endl; + bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); + beta_min = p_max / (0.5 * bsq_max); + } else { + beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); + } + if (MPIRank0()) { + if (beta_calc_legacy) { + std::cout << "B^2 max post-norm: " << bsq_max << std::endl; + std::cout << "Pressure max post-norm: " << p_max << std::endl; + } + std::cout << "Beta min post-norm: " << beta_min << std::endl; } - std::cout << "Beta min pre-norm: " << beta_min << std::endl; } } } // We've been initializing/manipulating P - Flux::MeshPtoU(md.get(), IndexDomain::entire); - // Synchronize after - KHARMADriver::SyncAllBounds(md); + Flux::MeshPtoU(md.get(), IndexDomain::interior); Flag("Added B Field"); } @@ -228,19 +227,20 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) auto& pkgs = pmesh->packages.AllPackages(); - // First, make sure any data from the per-block init is synchronized - Flag("Initial boundary sync"); - KHARMADriver::SyncAllBounds(md); - // Then, add/modify any magnetic field left until this step // (since B field initialization can depend on global maxima, // & is handled by the B field transport package, it's sometimes done here) if (!is_restart) { + // B field init is not stencil-1, needs boundaries sync'd + KHARMADriver::SyncAllBounds(md); + // Then init B field on each block KHARMA::SeedAndNormalizeB(pin, md); } // Print divB if (pin->GetString("b_field", "solver") != "none") { + // Another sync to update B fields + KHARMA::SeedAndNormalizeB(pin, md); // If a B field exists, print divB here if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); @@ -257,7 +257,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // This inserts only in vicinity of some global r,th,phi InsertBlob(rc.get(), pin); } - KHARMADriver::SyncAllBounds(md); } // Any extra cleanup & init especially when restarting @@ -267,18 +266,27 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMA::ResetGlobals(pin, pmesh); } - KHARMADriver::SyncAllBounds(md); - - auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); - auto pouts = std::make_unique(pmesh, pin, &tm); - pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); - // Clean the B field if we've introduced a divergence somewhere // Call this any time the package is loaded, all the // logic about parsing whether to clean is there if (pkgs.count("B_Cleanup")) { + if (pin->GetOrAddBoolean("b_cleanup", "output_before_cleanup", false)) { + auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); + auto pouts = std::make_unique(pmesh, pin, &tm); + pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); + } + B_Cleanup::CleanupDivergence(md); + + B_Cleanup::RemoveExtraFields(pmesh->block_list); } + // Finally, synchronize boundary values. + // This should be the first sync if there is no B field + KHARMADriver::SyncAllBounds(md); + // And make sure the (trivial) primitive values are up-to-date + Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + + Flag("Post-initialization finished"); } diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 95ffcca7..2e2b277a 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -287,22 +287,6 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI pin->GetReal("parthenon/mesh", "x3min"), startx[3], pin->GetReal("parthenon/mesh", "x3max"), stopx[3]); } - - if (is_spherical) { - // Check that the coordinate parameters r_{in,out} match the mesh - if (!close_to(pin->GetReal("parthenon/mesh", "x1min"), - m::log(pin->GetReal("coordinates", "r_in"))) || - !close_to(pin->GetReal("parthenon/mesh", "x1max"), - m::log(pin->GetReal("coordinates", "r_out")))) { - printf("Mesh shape does not match!"); - printf("Rin %g vs %g, Rout %g vs %g", - m::exp(pin->GetReal("parthenon/mesh", "x1min")), - pin->GetReal("coordinates", "r_in"), - m::exp(pin->GetReal("parthenon/mesh", "x1max")), - pin->GetReal("coordinates", "r_out")); - } - } - } if(MPIRank0()) std::cout << "Reading mesh from file to cache..." << std::endl; diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 706d1a0e..cbae5318 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -34,30 +34,15 @@ #include "resize_restart_kharma.hpp" +#include "boundaries.hpp" #include "hdf5_utils.h" #include "types.hpp" #include #include -//using namespace Kokkos; // Hyerin: 10/07/22 comment this out, use par_for instead - - -// TODO -// Record & read: -// 1. startx/stopx/dx -// 2. coordinate name FMKS/MKS/etc -// 3. all coordinate params in play -// 4. Electron MODEL bitflag param -// 5. nprim for sanity check? -// 6. Indication of EMHD vs MHD - -// TODO this code is very specific to spherical systems/boundares or entirely periodic boxes. -// No other boundaries/geometries are really supported. -// // Reads in KHARMA restart file but at a different simulation size - void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin) { bool use_dt = pin->GetOrAddBoolean("resize_restart", "use_dt", true); @@ -74,6 +59,7 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& std::istringstream is(inputString); fpinput->LoadFromStream(is); + // TODO(BSP) is there a way to copy all parameters finput->pin and fine-tune later? int fnx1, fnx2, fnx3, fmbnx1, fmbnx2, fmbnx3; fnx1 = fpinput->GetInteger("parthenon/mesh", "nx1"); fnx2 = fpinput->GetInteger("parthenon/mesh", "nx2"); @@ -126,11 +112,12 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin->SetInteger("parthenon/time", "ncycle", ncycle); // TODO NSTEP, next tdump/tlog, etc? - Real a, hslope;//, Rout; - a = fpinput->GetReal("coordinates", "a"); + GReal a = fpinput->GetReal("coordinates", "a"); pin->SetReal("coordinates", "a", a); - hslope = fpinput->GetReal("coordinates", "hslope"); - pin->SetReal("coordinates", "hslope", hslope); + if (fpinput->DoesParameterExist("coordinates", "hslope")) { + GReal hslope = fpinput->GetReal("coordinates", "hslope"); + pin->SetReal("coordinates", "hslope", hslope); + } // File closed here when restartReader falls out of scope } @@ -141,12 +128,12 @@ TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterI auto pmb = rc->GetBlockPointer(); - const int n1tot = pin->GetInteger("parthenon/mesh", "restart_nx1"); - const int n2tot = pin->GetInteger("parthenon/mesh", "restart_nx2"); - const int n3tot = pin->GetInteger("parthenon/mesh", "restart_nx3"); - const int n1mb = pin->GetInteger("parthenon/meshblock", "restart_nx1"); - const int n2mb = pin->GetInteger("parthenon/meshblock", "restart_nx2"); - const int n3mb = pin->GetInteger("parthenon/meshblock", "restart_nx3"); + const hsize_t n1tot = pin->GetInteger("parthenon/mesh", "restart_nx1"); + const hsize_t n2tot = pin->GetInteger("parthenon/mesh", "restart_nx2"); + const hsize_t n3tot = pin->GetInteger("parthenon/mesh", "restart_nx3"); + const hsize_t n1mb = pin->GetInteger("parthenon/meshblock", "restart_nx1"); + const hsize_t n2mb = pin->GetInteger("parthenon/meshblock", "restart_nx2"); + const hsize_t n3mb = pin->GetInteger("parthenon/meshblock", "restart_nx3"); auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess auto fname_fill = pin->GetOrAddString("resize_restart", "fname_fill", "none"); const bool is_spherical = pin->GetBoolean("coordinates", "spherical"); @@ -155,296 +142,230 @@ TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterI const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); const Real rs = pin->GetOrAddReal("bondi", "rs", 8.0); const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); - const int nghost = pin->GetReal("parthenon/mesh", "restart_nghost"); - const bool ghost_zones = pin->GetBoolean("parthenon/mesh", "restart_ghostzones"); - auto fBfield = pin->GetOrAddString("b_field", "type", "none"); - - // Add these to package properties, since they continue to be needed on boundaries - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx1"))) - pmb->packages.Get("GRMHD")->AddParam("rnx1", n1tot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx2"))) - pmb->packages.Get("GRMHD")->AddParam("rnx2", n2tot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnx3"))) - pmb->packages.Get("GRMHD")->AddParam("rnx3", n3tot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx1"))) - pmb->packages.Get("GRMHD")->AddParam("rmbnx1", n1mb); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx2"))) - pmb->packages.Get("GRMHD")->AddParam("rmbnx2", n2mb); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rmbnx3"))) - pmb->packages.Get("GRMHD")->AddParam("rmbnx3", n3mb); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("fname"))) - pmb->packages.Get("GRMHD")->AddParam("fname", fname); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("fname_fill"))) - pmb->packages.Get("GRMHD")->AddParam("fname_fill", fname_fill); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("spherical"))) - pmb->packages.Get("GRMHD")->AddParam("spherical", is_spherical); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rx1min"))) - pmb->packages.Get("GRMHD")->AddParam("rx1min", fx1min); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rx1max"))) - pmb->packages.Get("GRMHD")->AddParam("rx1max", fx1max); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot"))) - pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rs"))) - pmb->packages.Get("GRMHD")->AddParam("rs", rs); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("x1min"))) - pmb->packages.Get("GRMHD")->AddParam("x1min", x1min); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rnghost"))) - pmb->packages.Get("GRMHD")->AddParam("rnghost", nghost); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rghostzones"))) - pmb->packages.Get("GRMHD")->AddParam("rghostzones", ghost_zones); - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("b_field_type"))) - pmb->packages.Get("GRMHD")->AddParam("b_field_type", fBfield); - - // Register SetKharmaRestart as the X1 boundary condition - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - bound_pkg->KHARMAInnerX1Boundary = SetKharmaRestart; - bound_pkg->KHARMAOuterX1Boundary = SetKharmaRestart; + const bool fghostzones = pin->GetBoolean("parthenon/mesh", "restart_ghostzones"); + auto b_field_type = pin->GetOrAddString("b_field", "type", "none"); + int verbose = pin->GetOrAddInteger("debug", "verbose", 0); - // Set the whole domain - SetKharmaRestart(rc, IndexDomain::entire, false); - - return TaskStatus::complete; -} - -TaskStatus SetKharmaRestart(std::shared_ptr> rc, IndexDomain domain, bool coarse) -{ - Flag(rc, "Setting KHARMA restart zones"); - auto pmb = rc->GetBlockPointer(); - auto b_field_type = pmb->packages.Get("GRMHD")->Param("b_field_type"); + // Derived parameters + hsize_t nBlocks = (int) (n1tot*n2tot*n3tot)/(n1mb*n2mb*n3mb); + const bool should_fill = !(fname_fill == "none"); + const Real dx1 = (fx1max - fx1min) / n1tot; + int fnghost = pin->GetReal("parthenon/mesh", "restart_nghost"); + const Real fx1min_ghost = fx1min - fnghost*dx1; const bool include_B = (b_field_type != "none"); - // A placeholder to save the B fields for SeedBField - GridVector B_Save; - if (include_B) B_Save = rc->Get("B_Save").data; auto& G = pmb->coords; CoordinateEmbedding coords = G.coords; - - // Size/domain of the MeshBlock we're reading to - int is, ie; - if (domain == IndexDomain::outer_x1) {// copying from bondi - is = pmb->cellbounds.GetBoundsI(IndexDomain::interior).e+1; - ie = pmb->cellbounds.GetBoundsI(IndexDomain::entire).e; - } else if (domain == IndexDomain::inner_x1) { - is = pmb->cellbounds.GetBoundsI(IndexDomain::entire).s; - ie = pmb->cellbounds.GetBoundsI(IndexDomain::interior).s-1; - } else { - is = pmb->cellbounds.is(domain); - ie = pmb->cellbounds.ie(domain); + + // read from a restart file and save it to static GridScalar + + if (!fghostzones) fnghost=0; // reset to 0 + int x3factor=1; + if (n3tot <= 1) x3factor=0; // if less than 3D, do not add ghosts in x3 + hsize_t length[GR_DIM] = {nBlocks, + n1mb+2*fnghost, + n2mb+2*fnghost, + n3mb+2*fnghost*x3factor}; + const int block_sz = length[0]*length[1]*length[2]*length[3]; + + if (MPIRank0() && verbose > 0) { + std::cout << "Reading mesh size " << n1tot << "x" << n2tot << "x" << n3tot << + " block size " << n1mb << "x" << n2mb << "x" << n3mb << std::endl; + std::cout << "Reading " << length[0] << " meshblocks of total size " << + length[1] << "x" << length[2]<< "x" << length[3] << std::endl; } - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - const int n1tot = pmb->packages.Get("GRMHD")->Param("rnx1"); - const int n2tot = pmb->packages.Get("GRMHD")->Param("rnx2"); - const int n3tot = pmb->packages.Get("GRMHD")->Param("rnx3"); - hsize_t n1mb = pmb->packages.Get("GRMHD")->Param("rmbnx1"); - hsize_t n2mb = pmb->packages.Get("GRMHD")->Param("rmbnx2"); - hsize_t n3mb = pmb->packages.Get("GRMHD")->Param("rmbnx3"); - hsize_t nBlocks = (int) (n1tot*n2tot*n3tot)/(n1mb*n2mb*n3mb); - auto fname = pmb->packages.Get("GRMHD")->Param("fname"); - auto fname_fill = pmb->packages.Get("GRMHD")->Param("fname_fill"); - const bool should_fill = !(fname_fill == "none"); - const Real fx1min = pmb->packages.Get("GRMHD")->Param("rx1min"); - const Real fx1max = pmb->packages.Get("GRMHD")->Param("rx1max"); - const Real dx1 = (fx1max - fx1min) / n1tot; - const bool fghostzones = pmb->packages.Get("GRMHD")->Param("rghostzones"); - int fnghost = pmb->packages.Get("GRMHD")->Param("rnghost"); - const Real fx1min_ghost = fx1min - fnghost*dx1; - PackIndexMap prims_map, cons_map; - auto P = GRMHD::PackMHDPrims(rc.get(), prims_map); - auto U = GRMHD::PackMHDCons(rc.get(), cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - if ((domain != IndexDomain::outer_x1) && (domain != IndexDomain::inner_x1)) { - // read from a restart file and save it to static GridScalar - //cout << "Hyerin: reading files" << endl; - - - if (! fghostzones) fnghost=0; // reset to 0 - int x3factor=1; - if (n3tot <= 1) x3factor=0; // if less than 3D, do not add ghosts in x3 - hsize_t length[GR_DIM] = {nBlocks, - n1mb+2*fnghost, - n2mb+2*fnghost, - n3mb+2*fnghost*x3factor}; - const int block_sz = length[0]*length[1]*length[2]*length[3]; - //std::cout << "lengths " << length[0] << " " << length[1] <<" " << length[2]<<" " << length[3] << std::endl; - //printf("lengths %i %i %i %i \n", length[0], length[1], length[2], length[3]); - - - // read from file and stored in device Hyerin (10/18/2022) - GridScalar x1_f_device("x1_f_device", length[0], length[1]); - GridScalar x2_f_device("x2_f_device", length[0], length[2]); - GridScalar x3_f_device("x3_f_device", length[0], length[3]); - GridScalar rho_f_device("rho_f_device", length[0], length[3], length[2], length[1]); - GridScalar u_f_device("u_f_device", length[0], length[3], length[2], length[1]); - GridVector uvec_f_device("uvec_f_device", NVEC, length[0], length[3], length[2], length[1]); - GridVector B_f_device("B_f_device", NVEC, length[0], length[3], length[2], length[1]); - auto x1_f_host = x1_f_device.GetHostMirror(); - auto x2_f_host = x2_f_device.GetHostMirror(); - auto x3_f_host = x3_f_device.GetHostMirror(); - auto rho_f_host = rho_f_device.GetHostMirror(); - auto u_f_host = u_f_device.GetHostMirror(); - auto uvec_f_host = uvec_f_device.GetHostMirror(); - auto B_f_host = B_f_device.GetHostMirror(); - // Hyerin (09/19/2022) : new attempt to read the file - hdf5_open(fname.c_str()); + // read from file and stored in device Hyerin (10/18/2022) + GridScalar x1_f_device("x1_f_device", length[0], length[1]); + GridScalar x2_f_device("x2_f_device", length[0], length[2]); + GridScalar x3_f_device("x3_f_device", length[0], length[3]); + GridScalar rho_f_device("rho_f_device", length[0], length[3], length[2], length[1]); + GridScalar u_f_device("u_f_device", length[0], length[3], length[2], length[1]); + GridVector uvec_f_device("uvec_f_device", NVEC, length[0], length[3], length[2], length[1]); + GridVector B_f_device("B_f_device", NVEC, length[0], length[3], length[2], length[1]); + auto x1_f_host = x1_f_device.GetHostMirror(); + auto x2_f_host = x2_f_device.GetHostMirror(); + auto x3_f_host = x3_f_device.GetHostMirror(); + auto rho_f_host = rho_f_device.GetHostMirror(); + auto u_f_host = u_f_device.GetHostMirror(); + auto uvec_f_host = uvec_f_device.GetHostMirror(); + auto B_f_host = B_f_device.GetHostMirror(); + // Hyerin (09/19/2022) : new attempt to read the file + hdf5_open(fname.c_str()); + hdf5_set_directory("/"); + Real *rho_file = new double[block_sz]; + Real *u_file = new double[block_sz]; + Real *uvec_file = new double[NVEC*block_sz]; + Real *B_file = new double[NVEC*block_sz]; + Real *x1_file = new double[length[0]*length[1]]; + Real *x2_file = new double[length[0]*length[2]]; + Real *x3_file = new double[length[0]*length[3]]; + //static hsize_t fdims[] = {length[0], 1, length[3], length[2], length[1],1}; //outdated + static hsize_t fdims[] = {length[0], length[3], length[2], length[1]}; + //static hsize_t fdims_vec[] = {length[0], length[3], length[2], length[1],3}; //outdated + static hsize_t fdims_vec[] = {length[0], NVEC, length[3], length[2], length[1]}; + static hsize_t fdims_x1[] = {length[0], length[1]}; + static hsize_t fdims_x2[] = {length[0], length[2]}; + static hsize_t fdims_x3[] = {length[0], length[3]}; + hsize_t fstart[] = {0, 0, 0, 0}; + hsize_t fstart_vec[] = {0, 0, 0, 0, 0}; + hsize_t fstart_x[] = {0, 0}; + hdf5_read_array(rho_file, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(u_file, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_file, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x, fdims_x1, fdims_x1, fstart_x, H5T_IEEE_F64LE); + hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x, fdims_x2, fdims_x2, fstart_x, H5T_IEEE_F64LE); + hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x, fdims_x3, fdims_x3, fstart_x, H5T_IEEE_F64LE); + hdf5_close(); + + GridScalar x1_fill_device("x1_fill_device", length[0], length[1]); + GridScalar x2_fill_device("x2_fill_device", length[0], length[2]); + GridScalar x3_fill_device("x3_fill_device", length[0], length[3]); + GridScalar rho_fill_device("rho_fill_device", length[0], length[3], length[2], length[1]); + GridScalar u_fill_device("u_fill_device", length[0], length[3], length[2], length[1]); + GridVector uvec_fill_device("uvec_fill_device", NVEC, length[0], length[3], length[2], length[1]); + GridVector B_fill_device("B_fill_device", NVEC, length[0], length[3], length[2], length[1]); + auto x1_fill_host = x1_fill_device.GetHostMirror(); + auto x2_fill_host = x2_fill_device.GetHostMirror(); + auto x3_fill_host = x3_fill_device.GetHostMirror(); + auto rho_fill_host = rho_fill_device.GetHostMirror(); + auto u_fill_host = u_fill_device.GetHostMirror(); + auto uvec_fill_host = uvec_fill_device.GetHostMirror(); + auto B_fill_host = B_fill_device.GetHostMirror(); + Real *rho_filefill = new double[block_sz]; + Real *u_filefill = new double[block_sz]; + Real *uvec_filefill = new double[block_sz*NVEC]; + Real *B_filefill = new double[block_sz*NVEC]; + Real *x1_filefill = new double[length[0]*length[1]]; + Real *x2_filefill = new double[length[0]*length[2]]; + Real *x3_filefill = new double[length[0]*length[3]]; + if (fname_fill != "none") { // TODO: here I'm assuming fname and fname_fill has same dimensions, which is not always the case. + hdf5_open(fname_fill.c_str()); hdf5_set_directory("/"); - Real *rho_file = new double[block_sz]; - Real *u_file = new double[block_sz]; - Real *uvec_file = new double[block_sz*3]; - Real *B_file = new double[block_sz*3]; - Real *x1_file = new double[length[0]*length[1]]; - Real *x2_file = new double[length[0]*length[2]]; - Real *x3_file = new double[length[0]*length[3]]; - //static hsize_t fdims[] = {length[0], 1, length[3], length[2], length[1],1}; //outdated - static hsize_t fdims[] = {length[0], length[3], length[2], length[1]}; - //static hsize_t fdims_vec[] = {length[0], length[3], length[2], length[1],3}; //outdated - static hsize_t fdims_vec[] = {length[0], 3, length[3], length[2], length[1]}; - static hsize_t fdims_x1[] = {length[0], length[1]}; - static hsize_t fdims_x2[] = {length[0], length[2]}; - static hsize_t fdims_x3[] = {length[0], length[3]}; - hsize_t fstart[] = {0, 0, 0, 0}; - hsize_t fstart_vec[] = {0, 0, 0, 0, 0}; - hsize_t fstart_x[] = {0, 0}; - hdf5_read_array(rho_file, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_read_array(u_file, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_read_array(uvec_file, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); - //if (include_B) hdf5_read_array(B_file, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_file, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); - hdf5_read_array(x1_file, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); - hdf5_read_array(x2_file, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); - hdf5_read_array(x3_file, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); + hdf5_read_array(rho_filefill, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(u_filefill, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); + hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + //if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); + if (include_B) hdf5_read_array(B_filefill, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec,H5T_IEEE_F64LE); + hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x, fdims_x1, fdims_x1, fstart_x, H5T_IEEE_F64LE); + hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x, fdims_x2, fdims_x2, fstart_x, H5T_IEEE_F64LE); + hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x, fdims_x3, fdims_x3, fstart_x, H5T_IEEE_F64LE); hdf5_close(); - - GridScalar x1_fill_device("x1_fill_device", length[0], length[1]); - GridScalar x2_fill_device("x2_fill_device", length[0], length[2]); - GridScalar x3_fill_device("x2_fill_device", length[0], length[3]); - GridScalar rho_fill_device("rho_fill_device", length[0], length[3], length[2], length[1]); - GridScalar u_fill_device("u_fill_device", length[0], length[3], length[2], length[1]); - GridVector uvec_fill_device("uvec_fill_device", NVEC, length[0], length[3], length[2], length[1]); - GridVector B_fill_device("B_fill_device", NVEC, length[0], length[3], length[2], length[1]); - auto x1_fill_host = x1_fill_device.GetHostMirror(); - auto x2_fill_host = x2_fill_device.GetHostMirror(); - auto x3_fill_host = x3_fill_device.GetHostMirror(); - auto rho_fill_host = rho_fill_device.GetHostMirror(); - auto u_fill_host = u_fill_device.GetHostMirror(); - auto uvec_fill_host = uvec_fill_device.GetHostMirror(); - auto B_fill_host = B_fill_device.GetHostMirror(); - Real *rho_filefill = new double[block_sz]; - Real *u_filefill = new double[block_sz]; - Real *uvec_filefill = new double[block_sz*3]; - Real *B_filefill = new double[block_sz*3]; - Real *x1_filefill = new double[length[0]*length[1]]; - Real *x2_filefill = new double[length[0]*length[2]]; - Real *x3_filefill = new double[length[0]*length[3]]; - if (fname_fill != "none") { // TODO: here I'm assuming fname and fname_fill has same dimensions, which is not always the case. - hdf5_open(fname_fill.c_str()); - hdf5_set_directory("/"); - hdf5_read_array(rho_filefill, "prims.rho", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_read_array(u_filefill, "prims.u", 4, fdims, fstart, fdims, fdims, fstart, H5T_IEEE_F64LE); - hdf5_read_array(uvec_filefill, "prims.uvec", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); - //if (include_B) hdf5_read_array(B_filefill, "prims.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec, H5T_IEEE_F64LE); - if (include_B) hdf5_read_array(B_filefill, "cons.B", 5, fdims_vec, fstart_vec, fdims_vec, fdims_vec, fstart_vec,H5T_IEEE_F64LE); - hdf5_read_array(x1_filefill, "VolumeLocations/x", 2, fdims_x1, fstart_x,fdims_x1,fdims_x1,fstart_x,H5T_IEEE_F64LE); - hdf5_read_array(x2_filefill, "VolumeLocations/y", 2, fdims_x2, fstart_x,fdims_x2,fdims_x2,fstart_x,H5T_IEEE_F64LE); - hdf5_read_array(x3_filefill, "VolumeLocations/z", 2, fdims_x3, fstart_x,fdims_x3,fdims_x3,fstart_x,H5T_IEEE_F64LE); - hdf5_close(); - } + } - // save the grid coordinate values to host array - for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { - for (int itemp = 0; itemp < length[1]; itemp++) { - x1_f_host(iblocktemp,itemp) = x1_file[length[1]*iblocktemp+itemp]; - if (fname_fill != "none") x1_fill_host(iblocktemp,itemp) = x1_filefill[length[1]*iblocktemp+itemp]; - } for (int jtemp = 0; jtemp < length[2]; jtemp++) { - x2_f_host(iblocktemp,jtemp) = x2_file[length[2]*iblocktemp+jtemp]; - if (fname_fill != "none") x2_fill_host(iblocktemp,jtemp) = x2_filefill[length[2]*iblocktemp+jtemp]; - } for (int ktemp = 0; ktemp < length[3]; ktemp++) { - x3_f_host(iblocktemp,ktemp) = x3_file[length[3]*iblocktemp+ktemp]; - if (fname_fill != "none") x3_fill_host(iblocktemp,ktemp) = x3_filefill[length[3]*iblocktemp+ktemp]; - } + // save the grid coordinate values to host array + for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { + for (int itemp = 0; itemp < length[1]; itemp++) { + x1_f_host(iblocktemp,itemp) = x1_file[length[1]*iblocktemp+itemp]; + if (fname_fill != "none") x1_fill_host(iblocktemp,itemp) = x1_filefill[length[1]*iblocktemp+itemp]; } - // re-arrange uvec such that it can be read in the VLOOP - int vector_file_index, scalar_file_index; - for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { - for (int itemp = 0; itemp < length[1]; itemp++) { - for (int jtemp = 0; jtemp < length[2]; jtemp++) { - for (int ktemp = 0; ktemp < length[3]; ktemp++) { - scalar_file_index = length[1]*(length[2]*(length[3]*iblocktemp+ktemp)+jtemp)+itemp; - - rho_f_host(iblocktemp,ktemp,jtemp,itemp) = rho_file[scalar_file_index]; - u_f_host(iblocktemp,ktemp,jtemp,itemp) = u_file[scalar_file_index]; + for (int jtemp = 0; jtemp < length[2]; jtemp++) { + x2_f_host(iblocktemp,jtemp) = x2_file[length[2]*iblocktemp+jtemp]; + if (fname_fill != "none") x2_fill_host(iblocktemp,jtemp) = x2_filefill[length[2]*iblocktemp+jtemp]; + } + for (int ktemp = 0; ktemp < length[3]; ktemp++) { + x3_f_host(iblocktemp,ktemp) = x3_file[length[3]*iblocktemp+ktemp]; + if (fname_fill != "none") x3_fill_host(iblocktemp,ktemp) = x3_filefill[length[3]*iblocktemp+ktemp]; + } + } + // re-arrange uvec such that it can be read in the VLOOP + int vector_file_index, scalar_file_index; + for (int iblocktemp = 0; iblocktemp < length[0]; iblocktemp++) { + for (int itemp = 0; itemp < length[1]; itemp++) { + for (int jtemp = 0; jtemp < length[2]; jtemp++) { + for (int ktemp = 0; ktemp < length[3]; ktemp++) { + scalar_file_index = length[1]*(length[2]*(length[3]*iblocktemp+ktemp)+jtemp)+itemp; + + rho_f_host(iblocktemp,ktemp,jtemp,itemp) = rho_file[scalar_file_index]; + u_f_host(iblocktemp,ktemp,jtemp,itemp) = u_file[scalar_file_index]; + if (fname_fill != "none") { + rho_fill_host(iblocktemp,ktemp,jtemp,itemp) = rho_filefill[scalar_file_index]; + u_fill_host(iblocktemp,ktemp,jtemp,itemp) = u_filefill[scalar_file_index]; + } + for (int ltemp = 0; ltemp < 3; ltemp++) { + //vector_file_index = 3*(scalar_file_index)+ltemp; // outdated parthenon phdf5 saving order + vector_file_index = length[1]*(length[2]*(length[3]*(NVEC*iblocktemp+ltemp)+ktemp)+jtemp)+itemp; + + uvec_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_file[vector_file_index]; + if (include_B) B_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_file[vector_file_index]; if (fname_fill != "none") { - rho_fill_host(iblocktemp,ktemp,jtemp,itemp) = rho_filefill[scalar_file_index]; - u_fill_host(iblocktemp,ktemp,jtemp,itemp) = u_filefill[scalar_file_index]; - } - for (int ltemp = 0; ltemp < 3; ltemp++) { - //vector_file_index = 3*(scalar_file_index)+ltemp; // outdated parthenon phdf5 saving order - vector_file_index = length[1]*(length[2]*(length[3]*(3*iblocktemp+ltemp)+ktemp)+jtemp)+itemp; - - uvec_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_file[vector_file_index]; - if (include_B) B_f_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_file[vector_file_index]; - if (fname_fill != "none") { - uvec_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_filefill[vector_file_index]; - if (include_B) B_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_filefill[vector_file_index]; - } + uvec_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = uvec_filefill[vector_file_index]; + if (include_B) B_fill_host(ltemp,iblocktemp,ktemp,jtemp,itemp) = B_filefill[vector_file_index]; } } } } } - //std::cout << "Hyerin: first five Bs" << B_file[0] << " " << B_file[1] << " " << B_file[2] << " " << B_file[3] << " " << B_file[4] << std::endl; - //std::cout << "Hyerin: 6,7,8,9,10 B_f " << B_f_host(0,0,0,0,6) << " " << B_f_host(0,0,0,0,7) << " " << B_f_host(0,0,0,0,8) << " " << B_f_host(0,0,0,0,9) << " " << B_f_host(0,0,0,0,10) << std::endl; - const bool is_spherical = pmb->packages.Get("GRMHD")->Param("spherical"); - const Real mdot = pmb->packages.Get("GRMHD")->Param("mdot"); - const Real rs = pmb->packages.Get("GRMHD")->Param("rs"); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + } - - // Deep copy to device - x1_f_device.DeepCopy(x1_f_host); - x2_f_device.DeepCopy(x2_f_host); - x3_f_device.DeepCopy(x3_f_host); - rho_f_device.DeepCopy(rho_f_host); - u_f_device.DeepCopy(u_f_host); - uvec_f_device.DeepCopy(uvec_f_host); - if (include_B) B_f_device.DeepCopy(B_f_host); - if (fname_fill != "none") { - x1_fill_device.DeepCopy(x1_fill_host); - x2_fill_device.DeepCopy(x2_fill_host); - x3_fill_device.DeepCopy(x3_fill_host); - rho_fill_device.DeepCopy(rho_fill_host); - u_fill_device.DeepCopy(u_fill_host); - uvec_fill_device.DeepCopy(uvec_fill_host); - if (include_B) B_fill_device.DeepCopy(B_fill_host); - } - //if (pin->GetOrAddString("b_field", "type", "none") != "none") { - // B_P.DeepCopy(B_host); - //} - Kokkos::fence(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + // Deep copy to device + x1_f_device.DeepCopy(x1_f_host); + x2_f_device.DeepCopy(x2_f_host); + x3_f_device.DeepCopy(x3_f_host); + rho_f_device.DeepCopy(rho_f_host); + u_f_device.DeepCopy(u_f_host); + uvec_f_device.DeepCopy(uvec_f_host); + if (include_B) B_f_device.DeepCopy(B_f_host); + if (fname_fill != "none") { + x1_fill_device.DeepCopy(x1_fill_host); + x2_fill_device.DeepCopy(x2_fill_host); + x3_fill_device.DeepCopy(x3_fill_host); + rho_fill_device.DeepCopy(rho_fill_host); + u_fill_device.DeepCopy(u_fill_host); + uvec_fill_device.DeepCopy(uvec_fill_host); + if (include_B) B_fill_device.DeepCopy(B_fill_host); + } + Kokkos::fence(); + + PackIndexMap prims_map, cons_map; + auto P = GRMHD::PackMHDPrims(rc.get(), prims_map); + auto U = GRMHD::PackMHDCons(rc.get(), cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); - // Device-side interpolate & copy into the mirror array - pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - get_prim_restart_kharma(G, coords, P, m_p, - fx1min, fx1max, fnghost, should_fill, is_spherical, include_B, gam, rs, mdot, length, - x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, - x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, + // Device-side interpolate & copy into the mirror array + if (MPIRank0() && verbose > 0) { + std::cout << "Initializing KHARMA restart. Filling " << fx1min << " to " << fx1max << " from " << fname + << " and the rest from " << fname_fill << std::endl; + std::cout << "Vacuum gam: " << gam << " mdot: " << mdot << " rs: " << rs << std::endl; + } + + // Read to the entire meshblock -- we'll set the Dirichlet boundaries based on the + // ghost zone data we read here. + auto domain = IndexDomain::entire; + int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); + int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); + int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); + + pmb->par_for("copy_restart_state_kharma", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + get_prim_restart_kharma(G, coords, P, m_p, + fx1min, fx1max, fnghost, should_fill, is_spherical, include_B, gam, rs, mdot, length, + x1_f_device, x2_f_device, x3_f_device, rho_f_device, u_f_device, uvec_f_device, B_f_device, + x1_fill_device, x2_fill_device, x3_fill_device, rho_fill_device, u_fill_device, uvec_fill_device, B_fill_device, + k, j, i); + if (include_B) { + get_B_restart_kharma(G, U, m_u, + fx1min, fx1max, should_fill, length, + x1_f_device, x2_f_device, x3_f_device, B_f_device, + x1_fill_device, x2_fill_device, x3_fill_device, B_fill_device, k, j, i); - //if (pin->GetOrAddString("b_field", "type", "none") != "none") { - // VLOOP B_host(v, k, j, i) = interp_scalar(G, X, startx, stopx, dx, is_spherical, false, n3tot, n2tot, n1tot, &(B_file[v*block_sz])); - //} - if (include_B) { - get_B_restart_kharma(G, P, m_p, - fx1min, fx1max, should_fill, length, - x1_f_device, x2_f_device, x3_f_device, B_f_device, - x1_fill_device, x2_fill_device, x3_fill_device, B_fill_device, B_Save, - k, j, i); - } } - ); - } + } + ); + // Fill the + Flux::BlockPtoUMHD(rc.get(), IndexDomain::entire, false); + B_FluxCT::BlockUtoP(rc.get(), IndexDomain::entire, false); + + // Register a Dirichlet boundary condition + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; + bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; + return TaskStatus::complete; } diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 034eca75..18729681 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -69,97 +69,100 @@ KOKKOS_INLINE_FUNCTION void Xtoindex(const GReal XG[GR_DIM], del[1] = 0.; //(XG[1] - ((i) * dx[1] + startx[1])) / dx[1]; del[2] = 0.;//(XG[2] - ((j) * dx[2] + startx[2])) / dx[2]; del[3] = 0.;// (phi - ((k) * dx[3] + startx[3])) / dx[3]; - if (m::abs(dx2_min/m::pow(XG[1],2.))>1.e-8) printf("Xtoindex: dx2 pretty large = %g at r= %g \n",dx2_min, XG[1]); + if (m::abs(dx2_min / m::pow(XG[1],2.)) > 1.e-8) printf("Xtoindex: dx2 pretty large = %g at r= %g \n", dx2_min, XG[1]); } +// TOOD(BSP) these can be merged and moved back into the fn body now + KOKKOS_INLINE_FUNCTION void get_prim_restart_kharma(const GRCoordinates& G, const CoordinateEmbedding& coords, const VariablePack& P, const VarMap& m_p, const Real fx1min, const Real fx1max, const Real fnghost, const bool should_fill, const bool is_spherical, const bool include_B, const Real gam, const Real rs, const Real mdot, const hsize_t length[GR_DIM], - const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho, const GridScalar& u, const GridVector& uvec, const GridVector& B, + const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridScalar& rho_file, const GridScalar& u_file, const GridVector& uvec_file, const GridVector& B_file, const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridScalar& rho_fill, const GridScalar& u_fill, const GridVector& uvec_fill, const GridVector& B_fill, const int& k, const int& j, const int& i) { - Real rho_temp, u_temp; - Real u_prim[NVEC]; //, B_prim[NVEC]; - + Real rho = 0, u = 0; + Real u_prim[NVEC] = {0}; //, B_prim[NVEC]; + GReal X[GR_DIM]; G.coord(k, j, i, Loci::center, X); GReal del[GR_DIM]; // not really needed now since I am doing nearest neighbor interpolation int iblocktemp, itemp, jtemp, ktemp; + // Interpolate the value at this location from the global grid if ((!should_fill) && (X[1]fx1max)||(X[1] 1 || u_prim[2] > 1) { + // printf("Fill prims: %g %g %g %g %g from bondi,fill,file: %d %d %d\n", rho_temp, u_temp, u_prim[0], u_prim[1], u_prim[2], filled_bondi, filled_fill, filled_file); + // } + P(m_p.RHO, k, j, i) = rho; + P(m_p.UU, k, j, i) = u; P(m_p.U1, k, j, i) = u_prim[0]; P(m_p.U2, k, j, i) = u_prim[1]; P(m_p.U3, k, j, i) = u_prim[2]; } -KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, +KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const VariablePack& U, const VarMap& m_u, const Real fx1min, const Real fx1max, const bool should_fill, const hsize_t length[GR_DIM], const GridScalar& x1, const GridScalar& x2, const GridScalar& x3, const GridVector& B, - const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridVector& B_fill, const GridVector& B_save, + const GridScalar& x1_fill, const GridScalar& x2_fill, const GridScalar& x3_fill, const GridVector& B_fill, const int& k, const int& j, const int& i) { - //Real B_prim[NVEC]; Real B_cons[NVEC]; GReal X[GR_DIM]; @@ -169,21 +172,15 @@ KOKKOS_INLINE_FUNCTION void get_B_restart_kharma(const GRCoordinates& G, const V // Interpolate the value at this location from the global grid if ((!should_fill) && (X[1]fx1max)||(X[1] pmb, BoundaryFace face) -{ - return !(pmb->boundary_flag[face] == BoundaryFlag::block || - pmb->boundary_flag[face] == BoundaryFlag::periodic); -} - inline bool BoundaryIsInner(IndexDomain domain) { return domain == IndexDomain::inner_x1 || @@ -225,12 +216,37 @@ inline std::string BoundaryName(IndexDomain domain) } } +inline IndexDomain BoundaryDomain(const BoundaryFace face) +{ + switch (face) { + case BoundaryFace::inner_x1: + return IndexDomain::inner_x1; + case BoundaryFace::outer_x1: + return IndexDomain::outer_x1; + case BoundaryFace::inner_x2: + return IndexDomain::inner_x2; + case BoundaryFace::outer_x2: + return IndexDomain::outer_x2; + case BoundaryFace::inner_x3: + return IndexDomain::inner_x3; + case BoundaryFace::outer_x3: + return IndexDomain::outer_x3; + case BoundaryFace::undef: + throw std::runtime_error("Undefined boundary face has no domain!"); + } +} + /** - * Get zones in the domain interior + * Function for checking boundary flags: is this a domain or internal bound? */ - +inline bool IsDomainBound(std::shared_ptr pmb, BoundaryFace face) +{ + return !(pmb->boundary_flag[face] == BoundaryFlag::block || + pmb->boundary_flag[face] == BoundaryFlag::periodic); +} /** - * Get the + * Get zones which are inside the physical domain, i.e. set by computation or MPI halo sync, + * not by problem boundary conditions. */ inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& bounds) { diff --git a/machines/darwin.sh b/machines/darwin.sh index 74d882c4..b649ef0e 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -14,41 +14,59 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then # Run ""./make.sh hdf5" to build it PREFIX_PATH="$SOURCE_DIR/external/hdf5" - if [[ "$ARGS" == *"gcc12"* ]]; then - module load cuda/12.0.0 openmpi gcc/12.1.0 - C_NATIVE=gcc - CXX_NATIVE=g++ - elif [[ "$ARGS" == *"gcc"* ]]; then - module load cuda openmpi gcc/10.2.0 - C_NATIVE=gcc - CXX_NATIVE=g++ + if [[ "$ARGS" == *"cuda"* ]]; then + if [[ "$ARGS" == *"gcc12"* ]]; then + module load cuda/12.0.0 openmpi gcc/12.1.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + elif [[ "$ARGS" == *"gcc"* ]]; then + module load cuda openmpi gcc/10.2.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + else + module load nvhpc/23.3 cuda/11.7.0 + C_NATIVE="nvc" + CXX_NATIVE="nvc++" + # New NVHPC doesn't like CUDA_HOME + export NVHPC_CUDA_HOME="$CUDA_HOME" + unset CUDA_HOME + fi else - module load nvhpc/23.3 cuda/11.7.0 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - # New NVHPC doesn't like CUDA_HOME - export NVHPC_CUDA_HOME="$CUDA_HOME" - unset CUDA_HOME + if [[ "$ARGS" == *"gcc"* ]]; then + module load openmpi gcc/10.2.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + export CXXFLAGS="-fno-builtin-memset" + else + module load openmpi intel + C_NATIVE=icx + CXX_NATIVE=icpx + fi fi - # These are + # These are orthogonal to above. + # Just don't compile for an nv arch without "cuda" + NPROC=$(($(nproc) / 2)) # TODO robust? if [[ "$ARGS" == *"arm-nv"* ]]; then HOST_ARCH="ARMV81" DEVICE_ARCH="AMPERE80" MPI_NUM_PROCS=2 - MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=40" + MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" elif [[ "$ARGS" == *"ampere"* ]]; then HOST_ARCH="ZEN3" DEVICE_ARCH="AMPERE80" MPI_NUM_PROCS=2 - MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=4" + MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" elif [[ "$ARGS" == *"volta"* ]]; then HOST_ARCH="HSW" DEVICE_ARCH="VOLTA70" MPI_NUM_PROCS=1 + # Some nodes only have 1 GPU but be conservative + MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" else - echo "No target arch specified: must list a target arch for Darwin" - exit + HOST_ARCH="HSW" + MPI_NUM_PROCS=1 + MPI_EXTRA_ARGS="--map-by ppr:1:node:pe=$(($NPROC))" fi # Runtime diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index 445c01fc..14b602d6 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -29,8 +29,8 @@ hslope = 1.0 r_in = 200. r_out = 300. - -use_dirichlet = true + +prob_uses_dirichlet = true check_inflow_inner = false check_inflow_outer = false @@ -77,9 +77,6 @@ input = ODE disable_floors = true emhd_limits = false - -use_dirichlet = true - verbose = 1 diff --git a/pars/sane.par b/pars/sane.par index ff324c5c..6e72bcef 100644 --- a/pars/sane.par +++ b/pars/sane.par @@ -36,7 +36,7 @@ cfl = 0.9 gamma = 1.666667 -type = kharma +type = imex two_sync = true reconstruction = weno5 diff --git a/tests/bclean/bondi_multizone.par b/tests/bclean/bondi_multizone.par new file mode 100755 index 00000000..d61f8e39 --- /dev/null +++ b/tests/bclean/bondi_multizone.par @@ -0,0 +1,115 @@ +# Bondi flow problem +# Model a spherically symmetric, unmagnetized inflow +# Uses more MeshBlocks than necessary, for debugging + + +problem_id = bondi #gizmo_shell + + +# Full mesh size, no refinement +refinement = none +numlevel = 1 +nx1 = 64 +nx2 = 64 +nx3 = 64 + + +# Split into 2 meshblocks default +nx1 = 64 +nx2 = 64 +nx3 = 32 + + +base = spherical_ks_extg +transform = superexp +a = 0. # updated from run_kharma.sh +r_in = 0. # updated from run_kharma.sh +r_out = 0. # updated from run_kharma.sh + + +tlim = 5289680481 # updated from run_kharma.sh +nlim = -1 # updated from run_kharma.sh +dt_min = 0.00001 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 +implicit = false + + +mdot = 1.0 +rs = 316.22776601683796 +vacuum_logrho = -8.2014518 +vacuum_log_u_over_rho = -5.2915149 # updated from run_kharma.sh +r_shell = 8388608 # updated from run_kharma.sh +use_gizmo = false +use_dirichlet = false + + +datfn = none # updated from run_kharma.sh + + +fname = none # updated from run_kharma.sh +fname_fill = none # updated from run_kharma.sh +use_dt = false +base = 8 +nzone = 7 + + +disable_floors = false +rho_min_geom = 1.0e-6 +u_min_geom = 1.0e-8 +bsq_over_rho_max=100 +bsq_over_u_max=50 + +# We'll be adding material, and that's okay + +prob_uses_dirichlet = false +check_inflow_outer = false +check_inflow_inner = false # Hyerin test (12/22/22) +fix_corner = false +#fix_flux_pole = 0 # Hyerin test (12/22/22) + + +u_jitter=0 + + +type = vertical +solver = flux_ct +bz = 1e-4 # updated from run_kharma.sh +fix_flux_x1 = 0 +initial_cleanup = true # updated from run_kharma.sh +#fix_polar_flux = 0 # Hyerin test (12/22/22) + + +output_before_cleanup = true +rel_tolerance = 1.e-8 +always_solve = false + + +verbose = 1 +flag_verbose = 0 +extra_checks = 0 + + +type = kharma +two_sync = 1 + + +file_type = hdf5 +dt = 528968040 # output0_dt updated from run_kharma.sh +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag, divB, bounds.inner_x1, bounds.outer_x1 +ghost_zones = true + + +file_type = rst +dt = 2644840240 # output1_dt updated from run_kharma.sh +variables = prims.rho, prims.u, prims.uvec, prims.B +ghost_zones = true + + +file_type = hst +dt = 52896800 # output2_dt updated from run_kharma.sh + diff --git a/tests/bclean/bondi_multizone_00000.par b/tests/bclean/bondi_multizone_00000.par deleted file mode 100755 index 4829e892..00000000 --- a/tests/bclean/bondi_multizone_00000.par +++ /dev/null @@ -1,120 +0,0 @@ -# Bondi flow problem -# Model a spherically symmetric, unmagnetized inflow -# Uses more MeshBlocks than necessary, for debugging - - -problem_id = bondi #gizmo_shell #bondi_shell #_multizone - - -# Full mesh size, no refinement -refinement = none -numlevel = 1 -nx1 = 64 #128 # -nx2 = 64 #128 # -nx3 = 64 #128 # nx3_mesh updated from run_kharma.sh - - -# Split into blocks mesh -# Don't bother with xN boundaries for spherical coordinate systems -# KHARMA will automatically place ~5 zones inside the EH -nx1 = 32 #64 # nx1_meshblock updated from run_kharma.sh -nx2 = 32 #64 # nx2_meshblock updated from run_kharma.sh -nx3 = 64 #128 #64 # nx3_meshblock updated from run_kharma.sh - - -base = ks -transform = fmks -mks_smooth = 0 -a = 0.0 # spin updated from run_kharma.sh -ext_g = true # updated from run_kharma.sh -hslope = 0.3 -r_out = 16777216 # updated from run_kharma.sh -r_in = 262144 # updated from run_kharma.sh -#nghost = 6 # test Hyerin (12/28/22) - - -tlim = 5289680481 # updated from run_kharma.sh -nlim = -1 # updated from run_kharma.sh -dt_min = 0.00001 - - -cfl = 0.9 -gamma = 1.666667 -reconstruction = weno5 -implicit = false - - -mdot = 1.0 -rs = 316.22776601683796 # (1e2.5)#1000.0 #8.0 #300.0 # -vacuum_logrho= -8.2014518 #-9.6983 #-10 #-5 -vacuum_log_u_over_rho = -5.2915149 # updated from run_kharma.sh -r_shell = 8388608 # updated from run_kharma.sh -use_gizmo = false - - -datfn = /n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/data/gizmo/first_test/dat.txt - - -fname = /n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/data/bondi_multizone_021623_bondi_b_clean/bondi_multizone_00000/bondi.out1.final.rhdf # updated from run_kharma.sh -fname_fill = none #, updated from run_kharma.sh -use_dt = false -base = 8 -nzone = 7 - -# Disable floors - -disable_floors = true -rho_min_geom = 1.0e-12 -u_min_geom = 1.0e-15 - -# We'll be adding material, and that's okay - -check_inflow_outer = false -check_inflow_inner = false # Hyerin test (12/22/22) -#fix_flux_pole = 0 # Hyerin test (12/22/22) - - -u_jitter=0 - - -type = vertical # b_field_type updated from run_kharma.sh -solver = none # b_field_solver updated from run_kharma.sh -#norm = true # Hyerin (12/29/22) this increases divB in the boundaries, so won't use here -#beta_min=1000 # Hyerin (12/29/22) -bz = 1e-4 #1e-6 -#fix_polar_flux = 0 # Hyerin test (12/22/22) -fix_flux_x1 = 0 #1 # Hyerin test (02/16/23) -initial_cleanup = true # updated from run_kharma.sh - - -rel_tolerance = 1.e-8 -always_solve = true - - -verbose = 1 - - -type = imex ## -two_sync = 1 - - -max_nonlinear_iter = 3 - - -file_type = hdf5 -dt = 528968040 # output0_dt updated from run_kharma.sh -single_precision_output = true #false -variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag #G.gcov -ghost_zones = true - - -file_type = rst -dt = 2644840240 # output1_dt updated from run_kharma.sh -single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B #, prims.B #, cons.rho, cons.u, cons.uvec -ghost_zones = true - - -file_type = hst -dt = 52896800 # output2_dt updated from run_kharma.sh - diff --git a/tests/bclean/run.sh b/tests/bclean/run.sh index ce2ebdcb..1500f82c 100755 --- a/tests/bclean/run.sh +++ b/tests/bclean/run.sh @@ -16,7 +16,7 @@ DRTAG="." # Set paths PDR="." ## parent directory DR="." -parfilename="./bondi_multizone_00000.par" # parameter file +parfilename="./bondi_multizone.par" # parameter file KHARMA_DIR=../.. # other values determined automatically @@ -92,9 +92,11 @@ do echo "Restarting with $fname, filling using $fname_fill" args+=(" resize_restart/fname=$fname parthenon/time/dt_min=$dt_new") args+=(" resize_restart/fname_fill=$fname_fill ") + use_dirichlet="true" else r_shell=$((${r_out}/2)) args+=(" bondi/r_shell=$r_shell ") + use_dirichlet="false" fi # data_dir, logfiles @@ -103,28 +105,18 @@ do err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" $KHARMA_DIR/run.sh -n 1 -i ${parfilename} \ - parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ - parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=64 \ parthenon/job/problem_id=$prob \ parthenon/time/tlim=${start_time} \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/hslope=1 coordinates/transform=mks \ - bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ - floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 \ - b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ - b_field/fix_flux_x1=0 b_field/initial_cleanup=$init_c \ - b_cleanup/rel_tolerance=1.e-8 \ + coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin \ + bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ + b_field/bz=${bz} b_field/initial_cleanup=$init_c \ + boundaries/prob_uses_dirichlet=$use_dirichlet \ resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ parthenon/output0/dt=$output0_dt \ parthenon/output1/dt=$output1_dt \ parthenon/output2/dt=$output2_dt \ ${args[@]} \ -d ${data_dir} 1> ${out_fn} 2>${err_fn} - # kharma/b_flux_ct/seed_B_ct.cpp - # nlim=10000 for 1e-3 - # floors/u_over_rho_max=2 - #b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ - #coordinates/transform=mks coordinates/hslope=1 \ this, for some reason does not work for b cleaning? if [ $VAR -ne 0 ]; then if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then From 8672a9f4b0ff0d5dcbd76b1942bcdb9796a1acec Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 12 Apr 2023 16:04:11 -0600 Subject: [PATCH 055/219] Fix GPU compile. Fix a double-apply bug when periodically B cleaning --- external/patches/parthenon-use-gr-coordinates.patch | 13 +++++++++++-- kharma/b_cleanup/b_cleanup.cpp | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index c29c1a3b..48dbb5ef 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index f45cc979..23fb0f45 100644 +index f45cc979..9d99a1bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,7 +11,16 @@ index f45cc979..23fb0f45 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -301,6 +301,8 @@ lint_target(parthenon) +@@ -281,6 +281,8 @@ endif() + + target_link_libraries(parthenon PUBLIC Kokkos::kokkos) + ++target_link_libraries(parthenon PUBLIC stdc++fs) ++ + if (PARTHENON_ENABLE_ASCENT) + if (ENABLE_MPI) + target_link_libraries(parthenon PUBLIC ascent::ascent_mpi) +@@ -301,6 +303,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 45ae55df..aed63544 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -135,6 +135,8 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // Declare fields if we're doing that if (manage_field) { // Stolen verbatim from FluxCT, except we don't register the FixFlux step obvs + // Probably will crash due to not having the right parameters: add as needed. + // Best to crash, this mode is very not supported. // TODO preserve an easier form of divB in this case? // Mark if we're evolving implicitly From 44b38158975b7fdc4f6a851e5f71e48a5c782357 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 18 Apr 2023 16:36:10 +0000 Subject: [PATCH 056/219] Polaris compile & run scripts: currently GCC only. --- machines/incite.sh | 38 +++++++++++++++----------------------- pars/orszag_tang.par | 8 ++++---- scripts/batch/polaris.qsub | 29 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 scripts/batch/polaris.qsub diff --git a/machines/incite.sh b/machines/incite.sh index 1268469a..b79e4dbe 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -39,29 +39,21 @@ if [[ $HOST == *".summit.olcf.ornl.gov" ]]; then fi if [[ $HOST == *".alcf.anl.gov" ]]; then - if [[ "$ARGS" == *"cuda"* ]]; then - module purge - module load Core/StdEnv cmake - module load nvhpc/21.7 - #module load nvhpc - module load openmpi - #module load hdf5 - HOST_ARCH="AMDAVX" - DEVICE_ARCH="AMPERE80" + HOST_ARCH=HSW + DEVICE_ARCH=AMPERE80 + module load PrgEnv-gnu + module load cudatoolkit-standalone + #module load PrgEnv-nvhpc + module load cray-hdf5-parallel cmake + #export CRAY_CPU_TARGET=x86-64 - #CXXFLAGS="-mp" - C_NATIVE="gcc" - CXX_NATIVE="g++" - #export CXXFLAGS="-g -pg" + # Correct some vars set by default PrgEnv-nvhpc + #unset CC + #unset F77 + #unset CXX + #unset FC + #unset F90 - EXTRA_FLAGS="-DCUDAToolkit_ROOT_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/ $EXTRA_FLAGS" - EXTRA_FLAGS="-DCUDAToolkit_BIN_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/bin $EXTRA_FLAGS" - EXTRA_FLAGS="-DCUDAToolkit_INCLUDE_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/include $EXTRA_FLAGS" - PREFIX_PATH="$HOME/libs/hdf5-gcc-openmpi" - #PREFIX_PATH="/soft/thetagpu/hpc-sdk/Linux_x86_64/21.7/" - else - echo "Compiling for KNL" - HOST_ARCH="KNL" - PREFIX_PATH="$MPICH_DIR" - fi + EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" fi + diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index 79a71cd7..7c68aeb9 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -8,13 +8,13 @@ problem_id = orszag_tang refinement = none numlevel = 1 -nx1 = 256 +nx1 = 768 x1min = -3.141592653589793 x1max = 3.141592653589793 ix1_bc = periodic ox1_bc = periodic -nx2 = 256 +nx2 = 768 x2min = -3.141592653589793 x2max = 3.141592653589793 ix2_bc = periodic @@ -27,8 +27,8 @@ ix3_bc = periodic ox3_bc = periodic -nx1 = 256 -nx2 = 128 +nx1 = 384 +nx2 = 384 nx3 = 1 diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub new file mode 100644 index 00000000..7cc9782b --- /dev/null +++ b/scripts/batch/polaris.qsub @@ -0,0 +1,29 @@ +#!/bin/bash -l +#PBS -N KHARMA +#PBS -l select=1:ncpus=64 +#PBS -l walltime=0:10:00 +#PBS -q debug +##PBS -q gpu-hackathon +#PBS -A gpu_hack +#PBS -l filesystems=home:eagle + +KHARMA_DIR=~/kharma-dev +KHARMA_ARGS="-i $KHARMA_DIR/pars/orszag_tang.par" + +# Print ranks +NNODES=`wc -l < $PBS_NODEFILE` +NRANKS=4 # Number of MPI ranks to spawn per node +NDEPTH=8 # Number of hardware threads per rank (i.e. spacing between MPI ranks) +NTHREADS=8 # Number of software threads per rank to launch (i.e. OMP_NUM_THREADS) +NTOTRANKS=$(( NNODES * NRANKS )) +echo "NUM_OF_NODES= ${NNODES} TOTAL_NUM_RANKS= ${NTOTRANKS} RANKS_PER_NODE= ${NRANKS} THREADS_PER_RANK= ${NTHREADS}" + +# OpenMP config +export OMP_PROC_BIND=spread +export OMP_PLACES=threads + +# Run KHARMA with mapping +cd $PBS_O_WORKDIR +mpiexec --np ${NTOTRANKS} -ppn ${NRANKS} -d ${NDEPTH} --cpu-bind depth -env OMP_NUM_THREADS=${NTHREADS} $KHARMA_DIR/kharma.cuda $KHARMA_ARGS + + From ee63c01253640c14d8d5c5335935008881da5642 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 18 Apr 2023 18:43:35 +0000 Subject: [PATCH 057/219] Compile updates for Polaris * nvcc_wrapper -> Kokkos 4.0 version * Working NVHPC 23.1 now default stack on Polaris * Eliminate newlines because old NVHPC warns --- bin/nvcc_wrapper | 110 ++++++++++++++++++--- kharma/harm_driver.hpp | 2 +- kharma/prob/emhd/conducting_atmosphere.hpp | 2 +- kharma/prob/emhd/emhdshock.hpp | 2 +- kharma/prob/interpolation.hpp | 2 +- machines/incite.sh | 77 ++++++--------- 6 files changed, 127 insertions(+), 68 deletions(-) diff --git a/bin/nvcc_wrapper b/bin/nvcc_wrapper index 8ef357ff..5570c45b 100755 --- a/bin/nvcc_wrapper +++ b/bin/nvcc_wrapper @@ -10,10 +10,12 @@ # Default settings: change those according to your machine. For # example, you may have have two different wrappers with either icpc # or g++ as their back-end compiler. The defaults can be overwritten -# by using the usual arguments (e.g., -arch=sm_30 -ccbin icpc). +# by using the usual arguments (e.g., -arch=sm_80 -ccbin icpc). +# sm_70 is supported by every CUDA version from 9-12 and is thus +# chosen as default -#default_arch="sm_35" default_arch="sm_70" +#default_arch="sm_80" # # The default C++ compiler. @@ -96,10 +98,10 @@ replace_pragma_ident=0 first_xcompiler_arg=1 # Allow for setting temp dir without setting TMPDIR in parent (see https://docs.olcf.ornl.gov/systems/summit_user_guide.html#setting-tmpdir-causes-jsm-jsrun-errors-job-state-flip-flop) -if [[ ! -z ${NVCC_WRAPPER_TMPDIR+x} ]]; then +if [[ -z ${NVCC_WRAPPER_TMPDIR+x} ]]; then temp_dir=${TMPDIR:-/tmp} else - temp_dir=${NVCC_WRAPPER_TMPDIR+x} + temp_dir=${NVCC_WRAPPER_TMPDIR} fi # optimization flag added as a command-line argument @@ -149,11 +151,16 @@ do *.cpp|*.cxx|*.cc|*.C|*.c++|*.cu) cpp_files="$cpp_files $1" ;; - # Ensure we only have one optimization flag because NVCC doesn't allow muliple + # Ensure we only have one optimization flag because NVCC doesn't allow multiple -O*) if [ -n "$optimization_flag" ]; then - echo "nvcc_wrapper - *warning* you have set multiple optimization flags (-O*), only the last is used because nvcc can only accept a single optimization setting." - shared_args=${shared_args/ $optimization_flag/} + if [ "$1" = "$optimization_flag" ]; then + # Silently consume duplicates of the same argument + shift + continue + fi + echo "nvcc_wrapper - *warning* you have set multiple optimization flags (-O*), only the last is used because nvcc can only accept a single optimization setting." + shared_args=${shared_args/ $optimization_flag/} fi if [ "$1" = "-O" ]; then optimization_flag="-O2" @@ -222,21 +229,92 @@ do fi ;; #Handle known nvcc args - --dryrun|--verbose|--keep|--keep-dir*|-G|-lineinfo|-expt-extended-lambda|-expt-relaxed-constexpr|--resource-usage|-Xptxas*|--fmad=*|--use_fast_math|--Wext-lambda-captures-this|-Wext-lambda-captures-this) + --dryrun|--verbose|--keep|--source-in-ptx|-src-in-ptx|--keep-dir*|-G|-lineinfo|-extended-lambda|-expt-extended-lambda|-expt-relaxed-constexpr|--resource-usage|--fmad=*|--use_fast_math|--Wext-lambda-captures-this|-Wext-lambda-captures-this) cuda_args="$cuda_args $1" ;; #Handle more known nvcc args - --expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets) + --extended-lambda|--expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets|-allow-unsupported-compiler|--allow-unsupported-compiler) cuda_args="$cuda_args $1" ;; #Handle known nvcc args that have an argument - -maxrregcount=*|--maxrregcount=*) + -maxrregcount=*|--maxrregcount=*|-time=*|-Xptxas=*) cuda_args="$cuda_args $1" ;; - -maxrregcount|--default-stream|-Xnvlink|--fmad|-cudart|--cudart|-include) + -maxrregcount|--default-stream|-Xnvlink|--fmad|-cudart|--cudart|-include|-time|-Xptxas) cuda_args="$cuda_args $1 $2" shift ;; + # Handle Werror. Note, we must differentiate between the ones going to nvcc and the host compiler + # --Werror kind,... OR --Werror=kind,... <- always to nvcc + --Werror) + cuda_args="$cuda_args $1 $2" + shift + ;; + --Werror=*) + cuda_args="$cuda_args $1" + ;; + # -Werror kind,... where kind is one of {all-warnings, cross-execution-space-call, reorder, default-stream-launch, missing-launch-bounds, ext-lambda-captures-this, deprecated-declarations} <- goes to nvcc + # -Werror not followed by any kind as mentioned above goes to host compiler without any arguments + -Werror) + if [ $# -gt 1 ]; then + IFS="," read -r -a kinds <<< "$2" + first_kind=${kinds[0]} + # check if the first kind is one of the allowed ones, then this must be an nvcc list so put all of them to the cuda compiler + case $first_kind in + all-warnings|cross-execution-space-call|reorder|default-stream-launch|missing-launch-bounds|ext-lambda-captures-this|deprecated-declarations) + cuda_args="$cuda_args $1 $2" + shift + ;; + *) + if [ $first_xcompiler_arg -eq 1 ]; then + xcompiler_args="$1" + first_xcompiler_arg=0 + else + xcompiler_args="$xcompiler_args,$1" + fi + ;; + esac + fi + ;; + # -Werror=kind,... will be split into two parts, those kinds that belong to nvcc (see above) go there, while all others go towards the host compiler + -Werror=*) + kinds_str="${1:8}" # strip -Werror= + IFS="," read -r -a kinds <<< ${kinds_str} + first_werror_cuda=1 + first_werror_host=1 + xcompiler_args_werror= + # loop over all kinds that are sparated via ',' + for kind in "${kinds[@]}" + do + case ${kind} in + all-warnings|cross-execution-space-call|reorder|default-stream-launch|missing-launch-bounds|ext-lambda-captures-this|deprecated-declarations) + if [ $first_werror_cuda -ne 0 ]; then + cuda_args="$cuda_args -Werror=" + first_werror_cuda=0 + else + cuda_args="$cuda_args," + fi + cuda_args="$cuda_args$kind" + ;; + *) + if [ $first_werror_host -eq 0 ]; then + xcompiler_args_werror="${xcompiler_args_werror}," + fi + first_werror_host=0 + xcompiler_args_werror="$xcompiler_args_werror-Werror=$kind" + ;; + esac + done + if [ $first_werror_host -eq 0 ]; then + if [ $first_xcompiler_arg -eq 1 ]; then + xcompiler_args="$xcompiler_args_werror" + first_xcompiler_arg=0 + else + xcompiler_args="$xcompiler_args,$xcompiler_args_werror" + fi + fi + ;; + # End of Werror handling #Handle unsupported standard flags --std=c++1y|-std=c++1y|--std=gnu++1y|-std=gnu++1y|--std=c++1z|-std=c++1z|--std=gnu++1z|-std=gnu++1z|--std=c++2a|-std=c++2a) fallback_std_flag="-std=c++14" @@ -305,7 +383,7 @@ do -std=c++98|--std=c++98) ;; #strip of pedantic because it produces endless warnings about #LINE added by the preprocessor - -pedantic|-Wpedantic|-ansi) + -pedantic|-pedantic-errors|-Wpedantic|-ansi) ;; #strip of -Woverloaded-virtual to avoid "cc1: warning: command line option ‘-Woverloaded-virtual’ is valid for C++/ObjC++ but not for C" -Woverloaded-virtual) @@ -552,14 +630,14 @@ if [ $host_only -eq 1 ]; then $host_command elif [ -n "$nvcc_depfile_command" ]; then if [ "$NVCC_WRAPPER_SHOW_COMMANDS_BEING_RUN" == "1" ] ; then - echo "$nvcc_command && $nvcc_depfile_command" + echo "TMPDIR=${temp_dir} $nvcc_command && TMPDIR=${temp_dir} $nvcc_depfile_command" fi - $nvcc_command && $nvcc_depfile_command + TMPDIR=${temp_dir} $nvcc_command && TMPDIR=${temp_dir} $nvcc_depfile_command else if [ "$NVCC_WRAPPER_SHOW_COMMANDS_BEING_RUN" == "1" ] ; then - echo "$nvcc_command" + echo "TMPDIR=${temp_dir} $nvcc_command" fi - $nvcc_command + TMPDIR=${temp_dir} $nvcc_command fi error_code=$? diff --git a/kharma/harm_driver.hpp b/kharma/harm_driver.hpp index 31980ebb..772c03d3 100644 --- a/kharma/harm_driver.hpp +++ b/kharma/harm_driver.hpp @@ -77,4 +77,4 @@ class HARMDriver : public MultiStageDriver { * usually w.r.t. fluid "state" being spread across the primitive and conserved quantities */ TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); -}; \ No newline at end of file +}; diff --git a/kharma/prob/emhd/conducting_atmosphere.hpp b/kharma/prob/emhd/conducting_atmosphere.hpp index 56844aba..954968dc 100644 --- a/kharma/prob/emhd/conducting_atmosphere.hpp +++ b/kharma/prob/emhd/conducting_atmosphere.hpp @@ -46,4 +46,4 @@ TaskStatus InitializeAtmosphere(MeshBlockData *rc, ParameterInput *pin); -TaskStatus dirichlet_bc(MeshBlockData *rc, IndexDomain domain, bool coarse); \ No newline at end of file +TaskStatus dirichlet_bc(MeshBlockData *rc, IndexDomain domain, bool coarse); diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index 1c17836a..d93c6b41 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -219,4 +219,4 @@ TaskStatus InitializeEMHDShock(MeshBlockData *rc, ParameterInput *pin) return TaskStatus::complete; -} \ No newline at end of file +} diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/interpolation.hpp index 9827bf71..181d371c 100644 --- a/kharma/prob/interpolation.hpp +++ b/kharma/prob/interpolation.hpp @@ -120,4 +120,4 @@ KOKKOS_INLINE_FUNCTION Real linear(const int& i, const int& j, const int& k, return interp; } -} // Interpolation \ No newline at end of file +} // Interpolation diff --git a/machines/incite.sh b/machines/incite.sh index b79e4dbe..10c090df 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -1,59 +1,40 @@ # INCITE resources -if [[ $HOST == *".summit.olcf.ornl.gov" ]]; then - HOST_ARCH="POWER9" - DEVICE_ARCH="VOLTA70" - # Avoid sysadmin's wrath - NPROC=8 - # Runtime options for one-node test runs - MPI_EXE="jsrun --smpiargs="-gpu" -r 6 -a 1 -g 1 -c 6 -d packed -b packed:6" - OMP_NUM_THREADS=24 - KOKKOS_NUM_DEVICES=1 - MPI_NUM_PROCS=6 - # Summit *hates* C++17. - # Use GCC with 14 - module load cmake - if [[ "$ARGS" == *"xl"* ]]; then - # xlC: OpenMP CXX problems - #module load xl cuda - C_NATIVE='xlc' - CXX_NATIVE='xlc++' - export NVCC_WRAPPER_HOST_EXTRA_FLAGS='-O3 -qmaxmem=-1' - export NVCC_WRAPPER_CUDA_EXTRA_FLAGS='-O3 -Xcompiler -qmaxmem=-1' - #PREFIX_PATH="/sw/summit/hdf5/1.10.6_align/xl/16.1.1-5/" - elif [[ "$ARGS" == *"nvhpc"* ]]; then - # Use nvc++ compiler in NVHPC - module load cuda/11.5.2 nvhpc/22.5 spectrum-mpi hdf5/1.10.7 +# ALCF: Polaris +if [[ $HOST == *".polaris.alcf.anl.gov" ]]; then + HOST_ARCH=ZEN3 + DEVICE_ARCH=AMPERE80 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - export CXXFLAGS="-mp" - PREFIX_PATH="/gpfs/alpine/proj-shared/ast171/libs/hdf5-nvhpc-21.9" + module purge + if [[ $ARGS == *"nvhpc233"* ]]; then + # DOES NOT WORK: "CUDA 11.4 not installed with this NVHPC" + module load PrgEnv-nvhpc nvhpc/23.3 + # Guide new NVHPC to a working CUDA? + # export NVHPC_CUDA_HOME="/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/11.4" + # export NVHPC_DEFAULT_CUDA=11.4 + # export NVCC_WRAPPER_CUDA_EXTRA_FLAGS="-gpu=cuda11.4" + # EXTRA_FLAGS="-DCUDA_TOOLKIT_ROOT_DIR=/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/11.4 $EXTRA_FLAGS" + elif [[ $ARGS == *"nvhpc219"* ]]; then + # DOES NOT WORK: compile errors in pmmintrin.h & AVX512 intrinsics headers + module load PrgEnv-nvhpc + # Correct some vars set by default PrgEnv-nvhpc + unset CC CXX F77 F90 FC + # Try not to require intrinsics? + #HOST_ARCH=BDW + elif [[ $ARGS == *"gcc"* ]]; then + module load PrgEnv-gnu + module load cudatoolkit-standalone else - # Use default GCC - module load gcc cuda hdf5 - C_NATIVE='gcc' - CXX_NATIVE='g++' + module load PrgEnv-nvhpc nvhpc/23.1 fi -fi - -if [[ $HOST == *".alcf.anl.gov" ]]; then - HOST_ARCH=HSW - DEVICE_ARCH=AMPERE80 - module load PrgEnv-gnu - module load cudatoolkit-standalone - #module load PrgEnv-nvhpc + # Common modules module load cray-hdf5-parallel cmake - #export CRAY_CPU_TARGET=x86-64 - # Correct some vars set by default PrgEnv-nvhpc - #unset CC - #unset F77 - #unset CXX - #unset FC - #unset F90 + # Since we ran 'module purge', + # The Cray wrappers will warn unless we set this + export CRAY_CPU_TARGET=x86-64 + # TODO(BSP) need to set CRAYPE_LINK_TYPE=dynamic long-term? EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" fi - From 47d23c34ba93b4ec49e25ef30a067c2bc1db107c Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Sun, 23 Apr 2023 14:07:37 -0500 Subject: [PATCH 058/219] Can now evolve ideal MHD problem with ImEx solver. The previous commit implicitly assumed ImEx=>EMHD. --- kharma/floors/floors.cpp | 1 + kharma/flux.cpp | 2 +- kharma/flux_functions.hpp | 16 ++++++++-------- kharma/implicit/implicit.cpp | 2 +- kharma/implicit/implicit.hpp | 2 +- kharma/kharma.cpp | 11 ++++++----- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 3ad83c58..cab13537 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -205,6 +205,7 @@ TaskStatus ApplyFloors(MeshBlockData *mbd, IndexDomain domain) const IndexRange ib = mbd->GetBoundsI(domain); const IndexRange jb = mbd->GetBoundsJ(domain); const IndexRange kb = mbd->GetBoundsK(domain); + pmb->par_for("apply_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA_3D { if (((int) pflag(k, j, i)) >= InversionStatus::success) { diff --git a/kharma/flux.cpp b/kharma/flux.cpp index cd027998..c40e243e 100644 --- a/kharma/flux.cpp +++ b/kharma/flux.cpp @@ -178,4 +178,4 @@ TaskStatus Flux::AddSource(MeshData *md, MeshData *mdudt) Flag(mdudt, "Added"); return TaskStatus::complete; -} \ No newline at end of file +} diff --git a/kharma/flux_functions.hpp b/kharma/flux_functions.hpp index 2fde9e9c..c7270614 100644 --- a/kharma/flux_functions.hpp +++ b/kharma/flux_functions.hpp @@ -53,7 +53,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& dir, Real T[GR_DIM]) { - if (emhd_params.conduction || emhd_params.viscosity) { + if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary Real q, dP; Real qtilde, dPtilde; @@ -82,7 +82,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, const int& k, const int& j, const int& i, const int& dir, Real T[GR_DIM]) { - if (emhd_params.conduction || emhd_params.viscosity) { + if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary Real q, dP; @@ -181,9 +181,9 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, } // EMHD Variables: advect like rho - if (emhd_params.conduction) + if (m_p.Q >= 0) flux(m_u.Q) = P(m_p.Q) * D.ucon[dir] * gdet; - if (emhd_params.viscosity) + if (m_p.DP >= 0) flux(m_u.DP) = P(m_p.DP) * D.ucon[dir] * gdet; // Electrons: normalized by density @@ -216,7 +216,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - if (emhd_params.conduction || emhd_params.viscosity) { + if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary Real q, dP; @@ -268,9 +268,9 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P } // EMHD Variables: advect like rho - if (emhd_params.conduction) + if (m_p.Q >= 0) flux(m_u.Q, k, j, i) = P(m_p.Q, k, j, i) * D.ucon[dir] * gdet; - if (emhd_params.viscosity) + if (m_p.DP >= 0) flux(m_u.DP, k, j, i) = P(m_p.DP, k, j, i) * D.ucon[dir] * gdet; // Electrons: normalized by density @@ -330,7 +330,7 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real ef = P(m.RHO) + gam * P(m.UU); const Real cs2 = gam * (gam - 1) * P(m.UU) / ef; Real cms2; - if (emhd_params.conduction || emhd_params.viscosity) { + if (m.Q >= 0 || m.DP >= 0) { // Get the EGRMHD parameters Real tau, chi_e, nu_e; EMHD::set_parameters(G, P, m, emhd_params, gam, k, j, i, tau, chi_e, nu_e); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index d2a5cbae..b30001af 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -369,7 +369,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Now that we know that it isn't a bad zone, reset solve_fail for this iteration solve_fail() = SolverStatus::converged; - if (emhd_params_sub_step_init.conduction || emhd_params_sub_step_init.viscosity) { + if (m_p.Q >= 0 || m_p.DP >= 0) { Real dUq, dUdP; EMHD::implicit_sources(G, P_full_step_init, P_sub_step_init, m_p, gam, k, j, i, emhd_params_sub_step_init, dUq, dUdP); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index cbf8a2f8..045f72bf 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -107,7 +107,7 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P // (U_test - Ui)/dt - dudt_explicit ... FLOOP residual(ip) = (tmp(ip) - Ui(ip)) / dt - dudt_explicit(ip); - if (emhd_params.conduction || emhd_params.viscosity) { + if (m_p.Q >= 0 || m_p.DP >= 0) { // Compute new implicit source terms and time derivative source terms Real dUq, dUdP; // Don't need full array for these EMHD::implicit_sources(G, P_test, Ps, m_p, gam, k, j, i, emhd_params_s, dUq, dUdP); // dU_new diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 41ae0a21..7bcb021e 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -227,16 +227,17 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr& pin) bool b_cleanup = b_cleanup_package || is_resize || initial_cleanup; // TODO enable this iff jcon is in the list of outputs - bool add_jcon = pin->GetOrAddBoolean("GRMHD", "add_jcon", true); - bool do_electrons = pin->GetOrAddBoolean("electrons", "on", false); + bool add_jcon = pin->GetOrAddBoolean("GRMHD", "add_jcon", true); + bool do_electrons = pin->GetOrAddBoolean("electrons", "on", false); bool do_reductions = pin->GetOrAddBoolean("reductions", "on", true); - bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); - bool do_wind = pin->GetOrAddBoolean("wind", "on", false); + bool do_implicit = pin->GetOrAddBoolean("GRMHD", "implicit", false); + bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); + bool do_wind = pin->GetOrAddBoolean("wind", "on", false); // Set the default driver all the way up here, so packages know how to flag // prims vs cons (imex stepper syncs prims, but it's the packages' job to mark them) std::string driver_type; - if (do_emhd) { + if (do_implicit) { // Default to implicit step for EMHD driver_type = pin->GetOrAddString("driver", "type", "imex"); } else { From 2c673254f5436d959d2d47594abd3e12ec441735 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 Apr 2023 16:24:02 +0000 Subject: [PATCH 059/219] Polaris compile & run script fixes, remove I/O from perf standard --- machines/incite.sh | 1 + pars/sane_perf.par | 18 ++---------------- scripts/batch/polaris.qsub | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/machines/incite.sh b/machines/incite.sh index 10c090df..c095707c 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -9,6 +9,7 @@ if [[ $HOST == *".polaris.alcf.anl.gov" ]]; then module purge if [[ $ARGS == *"nvhpc233"* ]]; then # DOES NOT WORK: "CUDA 11.4 not installed with this NVHPC" + module use /soft/compilers/nvhpc/modulefiles module load PrgEnv-nvhpc nvhpc/23.3 # Guide new NVHPC to a working CUDA? # export NVHPC_CUDA_HOME="/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/11.4" diff --git a/pars/sane_perf.par b/pars/sane_perf.par index 24e7d660..1be1dc1d 100644 --- a/pars/sane_perf.par +++ b/pars/sane_perf.par @@ -34,12 +34,12 @@ tlim = 10000.0 nlim = 1000 -cfl = 0.9 +cfl = 0.8 gamma = 1.666667 reconstruction = weno5 -type = harm +type = imex two_sync = true @@ -63,17 +63,3 @@ u_over_rho_max = 2 verbose = 1 extra_checks = 1 flag_verbose = 0 - - -file_type = hdf5 -dt = 100.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, fflag, pflag - - -file_type = rst -dt = 100.0 - - -file_type = hst -dt = 0.5 diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index 7cc9782b..0358f781 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -5,10 +5,10 @@ #PBS -q debug ##PBS -q gpu-hackathon #PBS -A gpu_hack -#PBS -l filesystems=home:eagle +#PBS -l filesystems=home:grand KHARMA_DIR=~/kharma-dev -KHARMA_ARGS="-i $KHARMA_DIR/pars/orszag_tang.par" +KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" # Print ranks NNODES=`wc -l < $PBS_NODEFILE` @@ -22,8 +22,17 @@ echo "NUM_OF_NODES= ${NNODES} TOTAL_NUM_RANKS= ${NTOTRANKS} RANKS_PER_NODE= ${NR export OMP_PROC_BIND=spread export OMP_PLACES=threads +# Load any defaults/modules from the machine file +HOST=$(hostname -f) +ARGS=$(cat $KHARMA_DIR/make_args) +for machine in $KHARMA_DIR/machines/*.sh +do + source $machine +done + # Run KHARMA with mapping +# TODO passing OMP_NUM_THREADS here segfaults cd $PBS_O_WORKDIR -mpiexec --np ${NTOTRANKS} -ppn ${NRANKS} -d ${NDEPTH} --cpu-bind depth -env OMP_NUM_THREADS=${NTHREADS} $KHARMA_DIR/kharma.cuda $KHARMA_ARGS - - +set +x +mpiexec --np ${NTOTRANKS} -ppn ${NRANKS} -d ${NDEPTH} --cpu-bind depth $KHARMA_DIR/kharma.cuda $KHARMA_ARGS +set -x From ff4a1c39ff569cf22451cf7c0f5debb0a6b7a3be Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 Apr 2023 18:46:11 +0000 Subject: [PATCH 060/219] Polaris build & run fixes again! --- machines/incite.sh | 3 ++- scripts/batch/polaris.qsub | 16 ++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/machines/incite.sh b/machines/incite.sh index c095707c..f624f089 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -31,7 +31,8 @@ if [[ $HOST == *".polaris.alcf.anl.gov" ]]; then fi # Common modules module load cray-hdf5-parallel cmake - + module load craype-accel-nvidia80 + # Since we ran 'module purge', # The Cray wrappers will warn unless we set this export CRAY_CPU_TARGET=x86-64 diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index 0358f781..cec984a5 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -1,6 +1,6 @@ #!/bin/bash -l #PBS -N KHARMA -#PBS -l select=1:ncpus=64 +#PBS -l select=1 #PBS -l walltime=0:10:00 #PBS -q debug ##PBS -q gpu-hackathon @@ -10,11 +10,14 @@ KHARMA_DIR=~/kharma-dev KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" +# For applications that internally handle binding MPI/OpenMP processes to GPUs +mpiexec -n ${NTOTRANKS} --ppn ${NRANKS_PER_NODE} --depth=${NDEPTH} --cpu-bind depth --env OMP_NUM_THREADS=${NTHREADS} -env OMP_PLACES=threads ./hello_affinity + # Print ranks NNODES=`wc -l < $PBS_NODEFILE` -NRANKS=4 # Number of MPI ranks to spawn per node +NRANKS=$(nvidia-smi -L | wc -l) # Number of MPI ranks to spawn per node NDEPTH=8 # Number of hardware threads per rank (i.e. spacing between MPI ranks) -NTHREADS=8 # Number of software threads per rank to launch (i.e. OMP_NUM_THREADS) +NTHREADS=1 # Number of software threads per rank to launch (i.e. OMP_NUM_THREADS) NTOTRANKS=$(( NNODES * NRANKS )) echo "NUM_OF_NODES= ${NNODES} TOTAL_NUM_RANKS= ${NTOTRANKS} RANKS_PER_NODE= ${NRANKS} THREADS_PER_RANK= ${NTHREADS}" @@ -22,6 +25,8 @@ echo "NUM_OF_NODES= ${NNODES} TOTAL_NUM_RANKS= ${NTOTRANKS} RANKS_PER_NODE= ${NR export OMP_PROC_BIND=spread export OMP_PLACES=threads +export MPICH_GPU_SUPPORT_ENABLED=1 + # Load any defaults/modules from the machine file HOST=$(hostname -f) ARGS=$(cat $KHARMA_DIR/make_args) @@ -31,8 +36,7 @@ do done # Run KHARMA with mapping -# TODO passing OMP_NUM_THREADS here segfaults cd $PBS_O_WORKDIR -set +x -mpiexec --np ${NTOTRANKS} -ppn ${NRANKS} -d ${NDEPTH} --cpu-bind depth $KHARMA_DIR/kharma.cuda $KHARMA_ARGS set -x +mpiexec -n ${NTOTRANKS} --ppn ${NRANKS_PER_NODE} --depth=${NDEPTH} --cpu-bind depth --env OMP_NUM_THREADS=${NTHREADS} -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap $KHARMA_DIR/kharma.cuda $KHARMA_ARGS +set +x From 0c1a39b3c8b7b72ed3c848d9b7ba673f3067b1da Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 Apr 2023 19:49:43 +0000 Subject: [PATCH 061/219] Scaling script for Polaris --- scripts/batch/polaris.qsub | 4 +- scripts/batch/scaling_polaris.qsub | 160 +++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 3 deletions(-) create mode 100755 scripts/batch/scaling_polaris.qsub diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index cec984a5..dc1c5b2e 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -10,15 +10,13 @@ KHARMA_DIR=~/kharma-dev KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" -# For applications that internally handle binding MPI/OpenMP processes to GPUs -mpiexec -n ${NTOTRANKS} --ppn ${NRANKS_PER_NODE} --depth=${NDEPTH} --cpu-bind depth --env OMP_NUM_THREADS=${NTHREADS} -env OMP_PLACES=threads ./hello_affinity - # Print ranks NNODES=`wc -l < $PBS_NODEFILE` NRANKS=$(nvidia-smi -L | wc -l) # Number of MPI ranks to spawn per node NDEPTH=8 # Number of hardware threads per rank (i.e. spacing between MPI ranks) NTHREADS=1 # Number of software threads per rank to launch (i.e. OMP_NUM_THREADS) NTOTRANKS=$(( NNODES * NRANKS )) +#NTOTRANKS=1 # To set manually for scaling/testing echo "NUM_OF_NODES= ${NNODES} TOTAL_NUM_RANKS= ${NTOTRANKS} RANKS_PER_NODE= ${NRANKS} THREADS_PER_RANK= ${NTHREADS}" # OpenMP config diff --git a/scripts/batch/scaling_polaris.qsub b/scripts/batch/scaling_polaris.qsub new file mode 100755 index 00000000..d17a6f85 --- /dev/null +++ b/scripts/batch/scaling_polaris.qsub @@ -0,0 +1,160 @@ +#!/bin/bash + +# Submit/run script for a KHARMA scaling test +# *should* work on Polaris + +#PBS -N KHARMA +#PBS -l select=128 +#PBS -l walltime=1:00:00 +#PBS -q gpu-hackathon +#PBS -A gpu_hack +#PBS -l filesystems=home:grand +NNODES=`wc -l < $PBS_NODEFILE` +NRANKS=$(nvidia-smi -L | wc -l) + +DO_STRONG=true +DO_WEAK=true + +KHARMA_DIR=~/kharma-dev + +# Gotta specify this inline since bsub doesn't do arguments +PARFILE=~/kharma-dev/pars/scaling_torus.par +# Allocate in full nodes, vs individual gpus +min_nodes=1 +min_gpus=1 #$(( $NRANKS * $min_nodes )) + +# OpenMP options +export OMP_PROC_BIND=spread +export OMP_PLACES=threads +export OMP_NUM_THREADS=1 + +# MPI options +export MPICH_GPU_SUPPORT_ENABLED=1 + +# Profiling +#export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so + +# CD to working dir +cd $PBS_O_WORKDIR + +# Stuff for posterity +date +echo "Job run on nodes:" +mpiexec --np $NNODES --ppn 1 hostname + +# Strong scaling. Possibly not optimal due to requiring cubic meshblocks +if [[ $DO_STRONG == "true" ]]; then + for size in 192 384 768 + do + gpus=$min_gpus + while (( $gpus <= $NNODES * $NRANKS )) + do + np=$gpus + + nm=1 + div1=1 + div2=1 + div3=1 + to_div=3 + # Stop when we have half enough, since we'll use 2*N1 sized mesh + while (( $nm < $np )) + do + nm=$(( $nm * 2 )) + if [[ $to_div == "1" ]]; then + div1=$(( $div1 * 2 )) + to_div=3 + elif [[ $to_div == "2" ]]; then + div2=$(( $div2 * 2 )) + to_div=1 + else + div3=$(( $div3 * 2 )) + to_div=2 + fi + done + msize1=$(( $size / $div1 )) + msize2=$(( $size / $div2 )) + msize3=$(( $size / $div3 )) + + echo "cycle=100 Running ${size}x${size}x${size} cubed problem with KHARMA on $gpus GPUs (blocksize ${msize1}x${msize2}x${msize3})" + + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap \ + $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ + parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ + parthenon/meshblock/nx1=$msize1 parthenon/meshblock/nx2=$msize2 parthenon/meshblock/nx3=$msize3 + + gpus=$(( $gpus * 2 )) + done + done +fi + +# Weak scaling +if [[ $DO_WEAK == "true" ]]; then + for size in 64 128 + do + gpus=$min_gpus + while (( $gpus <= $NNODES * $NRANKS )) + do + np=$gpus + + # This runs the risk of wild inefficiencies + # TODO find a decomposition that doesn't + mul1=1 + mul2=1 + mul3=1 + if (( $np >= 2 )); then + mul3=$(( $mul3 * 2 )) + fi + if (( $np >= 4 )); then + mul3=$(( $mul3 * 3 )) + fi + if (( $np >= 8 )); then + mul2=$(( $mul2 * 2 )) + fi + if (( $np >= 16 )); then + mul1=$(( $mul1 * 2 )) + fi + if (( $np >= 32 )); then + mul3=$(( $mul3 * 2 )) + fi + if (( $np >= 64 )); then + mul2=$(( $mul2 * 2 )) + fi + if (( $np >= 128 )); then + mul1=$(( $mul1 * 2 )) + fi + if (( $np >= 256 )); then + mul3=$(( $mul3 * 2 )) + fi + if (( $np >= 512 )); then + mul2=$(( $mul2 * 2 )) + fi + if (( $np >= 1024 )); then + mul1=$(( $mul1 * 2 )) + fi + if (( $np >= 2048 )); then + mul3=$(( $mul3 * 2 )) + fi + if (( $np >= 4096 )); then + mul2=$(( $mul2 * 2 )) + fi + if (( $np >= 8192 )); then + mul1=$(( $mul1 * 2 )) + fi + if (( $np >= 16384 )); then + mul3=$(( $mul3 * 2 )) + fi + tsize1=$(( $mul1 * $size )) + tsize2=$(( $mul2 * $size )) + tsize3=$(( $mul3 * $size )) + nblock=$(( $mul1 * $mul2 * $mul3 )) + echo "cycle=100 Running $size per node problem with KHARMA on $gpus GPUs (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" + + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap \ + $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ + parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ + parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size + + gpus=$(( $gpus * 2 )) + done + done +fi From 16768dc10018e6537d2f4b68e263e6a3af50f4f1 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 Apr 2023 19:58:41 +0000 Subject: [PATCH 062/219] Add Polaris GPU assignment wrapper --- bin/mpi_gpu_wrap | 8 ++++++++ scripts/batch/polaris.qsub | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100755 bin/mpi_gpu_wrap diff --git a/bin/mpi_gpu_wrap b/bin/mpi_gpu_wrap new file mode 100755 index 00000000..86648833 --- /dev/null +++ b/bin/mpi_gpu_wrap @@ -0,0 +1,8 @@ +#!/bin/bash -l +num_gpus=4 +# need to assign GPUs in reverse order due to topology +# See Polaris Device Affinity Information https://www.alcf.anl.gov/support/user-guides/polaris/hardware-overview/machine-overview/index.html +gpu=$((${num_gpus} - 1 - ${PMI_LOCAL_RANK} % ${num_gpus})) +export CUDA_VISIBLE_DEVICES=$gpu +echo “RANK= ${PMI_RANK} LOCAL_RANK= ${PMI_LOCAL_RANK} gpu= ${gpu}” +exec "$@" \ No newline at end of file diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index dc1c5b2e..e2cb07ec 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -8,6 +8,7 @@ #PBS -l filesystems=home:grand KHARMA_DIR=~/kharma-dev +WRAPPER=$KHARMA_DIR/bin/mpi_gpu_wrap KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" # Print ranks @@ -36,5 +37,6 @@ done # Run KHARMA with mapping cd $PBS_O_WORKDIR set -x -mpiexec -n ${NTOTRANKS} --ppn ${NRANKS_PER_NODE} --depth=${NDEPTH} --cpu-bind depth --env OMP_NUM_THREADS=${NTHREADS} -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap $KHARMA_DIR/kharma.cuda $KHARMA_ARGS +mpiexec -n ${NTOTRANKS} --ppn ${NRANKS_PER_NODE} --depth=${NDEPTH} --cpu-bind depth --env OMP_NUM_THREADS=${NTHREADS} -env OMP_PLACES=threads \ + $WRAPPER $KHARMA_DIR/kharma.cuda $KHARMA_ARGS set +x From a01582339182da3e5dcebe0ee3e44665bf4b83bf Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 29 Mar 2023 16:01:05 -0600 Subject: [PATCH 063/219] Cherry-pick parfile changes from next --- kharma/main.cpp | 89 +++++++++++++++++++------------------------ pars/mad.par | 2 +- pars/sane_divb_2d.par | 1 - pars/sane_emhd.par | 1 - pars/sane_imex.par | 1 - 5 files changed, 40 insertions(+), 54 deletions(-) diff --git a/kharma/main.cpp b/kharma/main.cpp index 78c6d3dc..37d5d48b 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -136,60 +136,49 @@ int main(int argc, char *argv[]) signal(SIGSEGV, print_backtrace); #endif - auto pin = pman.pinput.get(); // All parameters in the input file or command line - auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc - auto papp = pman.app_input.get(); // The list of callback functions specified above - - if(MPIRank0()) { - // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation - if (pmesh->packages.Get("Globals")->Param("verbose") > 0) { - // Print a list of all loaded packages. Surprisingly useful for debugging init logic - std::cout << "Packages in use: " << std::endl; - for (auto package : pmesh->packages.AllPackages()) { - std::cout << package.first << std::endl; + { + auto pin = pman.pinput.get(); // All parameters in the input file or command line + auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc + auto papp = pman.app_input.get(); // The list of callback functions specified above + + if(MPIRank0()) { + // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation + if (pmesh->packages.Get("Globals")->Param("verbose") > 0) { + // Print a list of all loaded packages. Surprisingly useful for debugging init logic + std::cout << "Packages in use: " << std::endl; + for (auto package : pmesh->packages.AllPackages()) { + std::cout << package.first << std::endl; + } + std::cout << std::endl; } - std::cout << std::endl; + std::cout << "Running post-initialization tasks..." << std::endl; } - std::cout << "Running post-initialization tasks..." << std::endl; - } - // PostInitialize: Add magnetic field to the problem, initialize ghost zones. - // Any init which may be run even when restarting, or requires all - // MeshBlocks to be initialized already - auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); - KHARMA::PostInitialize(pin, pmesh, is_restart); - Flag("Post-initialization completed"); - - // Construct a temporary driver purely for parameter parsing - KHARMADriver driver(pin, papp, pmesh); - - // We could still have set parameters during driver initialization - // Note the order here is *extremely important* as the first statement has a - // side effect which must occur on all MPI ranks - if(pin->GetOrAddBoolean("debug", "archive_parameters", false) && MPIRank0()) { - // Write *all* parameters to a parfile for posterity - std::ostringstream ss; - auto itt_now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - ss << "kharma_parsed_parameters_" << std::put_time(std::gmtime(&itt_now), "%FT%TZ") << ".par"; - std::fstream pars; - pars.open(ss.str(), std::fstream::out | std::fstream::trunc); - pin->ParameterDump(pars); - pars.close(); - } - // Also write parameters to console if we should be wordy - if ((pmesh->packages.Get("Globals")->Param("verbose") > 0) && MPIRank0()) { - // This dumps the full Kokkos config, useful for double-checking - // that the compile did what we wanted - ShowConfig(); - pin->ParameterDump(std::cout); - } + // PostInitialize: Add magnetic field to the problem, initialize ghost zones. + // Any init which may be run even when restarting, or requires all + // MeshBlocks to be initialized already + auto prob = pin->GetString("parthenon/job", "problem_id"); + bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); + KHARMA::PostInitialize(pin, pmesh, is_restart); + Flag("Post-initialization completed"); + + // Construct a temporary driver purely for parameter parsing + KHARMADriver driver(pin, papp, pmesh); + + // Write parameters to console if we should be wordy + if ((pmesh->packages.Get("Globals")->Param("verbose") > 0) && MPIRank0()) { + // This dumps the full Kokkos config, useful for double-checking + // that the compile did what we wanted + ShowConfig(); + pin->ParameterDump(std::cout); + } - // Then execute the driver. This is a Parthenon function inherited by our HARMDriver object, - // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion - // of each step until a stop criterion is reached. - Flag("Executing Driver"); - auto driver_status = driver.Execute(); + // Then execute the driver. This is a Parthenon function inherited by our HARMDriver object, + // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion + // of each step until a stop criterion is reached. + Flag("Executing Driver"); + auto driver_status = driver.Execute(); + } // Parthenon cleanup includes Kokkos, MPI Flag("Finalizing"); diff --git a/pars/mad.par b/pars/mad.par index 7cf6e6f7..3c315b36 100644 --- a/pars/mad.par +++ b/pars/mad.par @@ -3,6 +3,7 @@ # setup details +archive_parameters_timestamp = true problem_id = torus @@ -57,7 +58,6 @@ bsq_over_rho_max = 100 u_over_rho_max = 2 -archive_parameters = true verbose = 1 extra_checks = 1 flag_verbose = 0 diff --git a/pars/sane_divb_2d.par b/pars/sane_divb_2d.par index 6157ed55..ac4106b6 100644 --- a/pars/sane_divb_2d.par +++ b/pars/sane_divb_2d.par @@ -57,7 +57,6 @@ u_over_rho_max = 2 frame = fluid -archive_parameters = false verbose = 1 extra_checks = 1 flag_verbose = 0 diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par index df41df12..621a6d34 100644 --- a/pars/sane_emhd.par +++ b/pars/sane_emhd.par @@ -83,7 +83,6 @@ u_over_rho_max = 2 enable_emhd_limits = true -archive_parameters = true verbose = 1 extra_checks = 1 flag_verbose = 0 diff --git a/pars/sane_imex.par b/pars/sane_imex.par index 80af3404..aa79714f 100644 --- a/pars/sane_imex.par +++ b/pars/sane_imex.par @@ -73,7 +73,6 @@ bsq_over_rho_max = 100 u_over_rho_max = 2 -archive_parameters = true verbose = 1 extra_checks = 1 flag_verbose = 0 From bf59ec75dfc3f4b185b11c276ee740824b68e54a Mon Sep 17 00:00:00 2001 From: Hyerin Cho Date: Tue, 2 May 2023 22:23:52 +0000 Subject: [PATCH 064/219] Cherry-pick EMF temps optimization from Hyerin on kharma-next --- kharma/b_flux_ct/b_flux_ct.cpp | 44 +++++++--------- machines/incite.sh | 93 +++++++++++++--------------------- 2 files changed, 52 insertions(+), 85 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 5b372af0..e0092da5 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -116,6 +116,14 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); + // Declare EMF temporary variables, to avoid malloc/free during each step + // These are edge-centered but we only need the interior + 1-zone halo anyway + std::vector flags_emf = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; + m = Metadata(flags_emf, s_vector); + pkg->AddField("emf", m); + + // CALLBACKS + // We exist basically to do this pkg->FixFlux = B_FluxCT::FixFlux; @@ -233,6 +241,7 @@ void FluxCT(MeshData *md) // Pack variables const auto& B_F = md->PackVariablesAndFluxes(std::vector{"cons.B"}); + const auto& emf_pack = md->PackVariables(std::vector{"emf"}); // Get sizes const IndexRange ib = md->GetBoundsI(IndexDomain::interior); @@ -244,26 +253,16 @@ void FluxCT(MeshData *md) const IndexRange jl = IndexRange{jb.s, jb.e + 1}; const IndexRange kl = (ndim > 2) ? IndexRange{kb.s, kb.e + 1} : kb; - // Declare temporaries - // TODO make these a true Edge field when that's available - const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); - const int n2 = pmb0->cellbounds.ncellsj(IndexDomain::entire); - const int n3 = pmb0->cellbounds.ncellsk(IndexDomain::entire); - const int nb = md->NumBlocks(); - GridScalar emf1("emf1", nb, n3, n2, n1); - GridScalar emf2("emf2", nb, n3, n2, n1); - GridScalar emf3("emf3", nb, n3, n2, n1); - // Calculate emf around each face Flag(md, "Calc EMFs"); pmb0->par_for("flux_ct_emf", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { - emf3(b, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - + emf_pack(b, V3, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - B_F(b).flux(X2DIR, V1, k, j, i) - B_F(b).flux(X2DIR, V1, k, j, i-1)); if (ndim > 2) { - emf2(b, k, j, i) = -0.25 * (B_F(b).flux(X1DIR, V3, k, j, i) + B_F(b).flux(X1DIR, V3, k-1, j, i) - + emf_pack(b, V2, k, j, i) = -0.25 * (B_F(b).flux(X1DIR, V3, k, j, i) + B_F(b).flux(X1DIR, V3, k-1, j, i) - B_F(b).flux(X3DIR, V1, k, j, i) - B_F(b).flux(X3DIR, V1, k, j, i-1)); - emf1(b, k, j, i) = 0.25 * (B_F(b).flux(X2DIR, V3, k, j, i) + B_F(b).flux(X2DIR, V3, k-1, j, i) - + emf_pack(b, V1, k, j, i) = 0.25 * (B_F(b).flux(X2DIR, V3, k, j, i) + B_F(b).flux(X2DIR, V3, k-1, j, i) - B_F(b).flux(X3DIR, V2, k, j, i) - B_F(b).flux(X3DIR, V2, k, j-1, i)); } } @@ -277,29 +276,22 @@ void FluxCT(MeshData *md) pmb0->par_for("flux_ct_1", block.s, block.e, kb.s, kb.e, jb.s, jb.e, il.s, il.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { B_F(b).flux(X1DIR, V1, k, j, i) = 0.0; - B_F(b).flux(X1DIR, V2, k, j, i) = 0.5 * (emf3(b, k, j, i) + emf3(b, k, j+1, i)); - if (ndim > 2) B_F(b).flux(X1DIR, V3, k, j, i) = -0.5 * (emf2(b, k, j, i) + emf2(b, k+1, j, i)); - - /* - if (k <15 && k>13 && j>jb.s-1 && j 2) B_F(b).flux(X1DIR, V3, k, j, i) = -0.5 * (emf_pack(b, V2, k, j, i) + emf_pack(b, V2, k+1, j, i)); } ); pmb0->par_for("flux_ct_2", block.s, block.e, kb.s, kb.e, jl.s, jl.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { - B_F(b).flux(X2DIR, V1, k, j, i) = -0.5 * (emf3(b, k, j, i) + emf3(b, k, j, i+1)); + B_F(b).flux(X2DIR, V1, k, j, i) = -0.5 * (emf_pack(b, V3, k, j, i) + emf_pack(b, V3, k, j, i+1)); B_F(b).flux(X2DIR, V2, k, j, i) = 0.0; - if (ndim > 2) B_F(b).flux(X2DIR, V3, k, j, i) = 0.5 * (emf1(b, k, j, i) + emf1(b, k+1, j, i)); + if (ndim > 2) B_F(b).flux(X2DIR, V3, k, j, i) = 0.5 * (emf_pack(b, V1, k, j, i) + emf_pack(b, V1, k+1, j, i)); } ); if (ndim > 2) { pmb0->par_for("flux_ct_3", block.s, block.e, kl.s, kl.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { - B_F(b).flux(X3DIR, V1, k, j, i) = 0.5 * (emf2(b, k, j, i) + emf2(b, k, j, i+1)); - B_F(b).flux(X3DIR, V2, k, j, i) = -0.5 * (emf1(b, k, j, i) + emf1(b, k, j+1, i)); + B_F(b).flux(X3DIR, V1, k, j, i) = 0.5 * (emf_pack(b, V2, k, j, i) + emf_pack(b, V2, k, j, i+1)); + B_F(b).flux(X3DIR, V2, k, j, i) = -0.5 * (emf_pack(b, V1, k, j, i) + emf_pack(b, V1, k, j+1, i)); B_F(b).flux(X3DIR, V3, k, j, i) = 0.0; } ); diff --git a/machines/incite.sh b/machines/incite.sh index 1268469a..f624f089 100644 --- a/machines/incite.sh +++ b/machines/incite.sh @@ -1,67 +1,42 @@ # INCITE resources -if [[ $HOST == *".summit.olcf.ornl.gov" ]]; then - HOST_ARCH="POWER9" - DEVICE_ARCH="VOLTA70" - # Avoid sysadmin's wrath - NPROC=8 - # Runtime options for one-node test runs - MPI_EXE="jsrun --smpiargs="-gpu" -r 6 -a 1 -g 1 -c 6 -d packed -b packed:6" - OMP_NUM_THREADS=24 - KOKKOS_NUM_DEVICES=1 - MPI_NUM_PROCS=6 - # Summit *hates* C++17. - # Use GCC with 14 - module load cmake - if [[ "$ARGS" == *"xl"* ]]; then - # xlC: OpenMP CXX problems - #module load xl cuda - C_NATIVE='xlc' - CXX_NATIVE='xlc++' - export NVCC_WRAPPER_HOST_EXTRA_FLAGS='-O3 -qmaxmem=-1' - export NVCC_WRAPPER_CUDA_EXTRA_FLAGS='-O3 -Xcompiler -qmaxmem=-1' - #PREFIX_PATH="/sw/summit/hdf5/1.10.6_align/xl/16.1.1-5/" - elif [[ "$ARGS" == *"nvhpc"* ]]; then - # Use nvc++ compiler in NVHPC - module load cuda/11.5.2 nvhpc/22.5 spectrum-mpi hdf5/1.10.7 +# ALCF: Polaris +if [[ $HOST == *".polaris.alcf.anl.gov" ]]; then + HOST_ARCH=ZEN3 + DEVICE_ARCH=AMPERE80 - C_NATIVE="nvc" - CXX_NATIVE="nvc++" - export CXXFLAGS="-mp" - PREFIX_PATH="/gpfs/alpine/proj-shared/ast171/libs/hdf5-nvhpc-21.9" + module purge + if [[ $ARGS == *"nvhpc233"* ]]; then + # DOES NOT WORK: "CUDA 11.4 not installed with this NVHPC" + module use /soft/compilers/nvhpc/modulefiles + module load PrgEnv-nvhpc nvhpc/23.3 + # Guide new NVHPC to a working CUDA? + # export NVHPC_CUDA_HOME="/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/11.4" + # export NVHPC_DEFAULT_CUDA=11.4 + # export NVCC_WRAPPER_CUDA_EXTRA_FLAGS="-gpu=cuda11.4" + # EXTRA_FLAGS="-DCUDA_TOOLKIT_ROOT_DIR=/opt/nvidia/hpc_sdk/Linux_x86_64/21.9/cuda/11.4 $EXTRA_FLAGS" + elif [[ $ARGS == *"nvhpc219"* ]]; then + # DOES NOT WORK: compile errors in pmmintrin.h & AVX512 intrinsics headers + module load PrgEnv-nvhpc + # Correct some vars set by default PrgEnv-nvhpc + unset CC CXX F77 F90 FC + # Try not to require intrinsics? + #HOST_ARCH=BDW + elif [[ $ARGS == *"gcc"* ]]; then + module load PrgEnv-gnu + module load cudatoolkit-standalone else - # Use default GCC - module load gcc cuda hdf5 - C_NATIVE='gcc' - CXX_NATIVE='g++' + module load PrgEnv-nvhpc nvhpc/23.1 fi -fi - -if [[ $HOST == *".alcf.anl.gov" ]]; then - if [[ "$ARGS" == *"cuda"* ]]; then - module purge - module load Core/StdEnv cmake - module load nvhpc/21.7 - #module load nvhpc - module load openmpi - #module load hdf5 - HOST_ARCH="AMDAVX" - DEVICE_ARCH="AMPERE80" - - #CXXFLAGS="-mp" - C_NATIVE="gcc" - CXX_NATIVE="g++" - #export CXXFLAGS="-g -pg" + # Common modules + module load cray-hdf5-parallel cmake + module load craype-accel-nvidia80 + + # Since we ran 'module purge', + # The Cray wrappers will warn unless we set this + export CRAY_CPU_TARGET=x86-64 + # TODO(BSP) need to set CRAYPE_LINK_TYPE=dynamic long-term? - EXTRA_FLAGS="-DCUDAToolkit_ROOT_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/ $EXTRA_FLAGS" - EXTRA_FLAGS="-DCUDAToolkit_BIN_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/bin $EXTRA_FLAGS" - EXTRA_FLAGS="-DCUDAToolkit_INCLUDE_DIR=/soft/hpc-sdk/Linux_x86_64/21.7/cuda/11.4/include $EXTRA_FLAGS" - PREFIX_PATH="$HOME/libs/hdf5-gcc-openmpi" - #PREFIX_PATH="/soft/thetagpu/hpc-sdk/Linux_x86_64/21.7/" - else - echo "Compiling for KNL" - HOST_ARCH="KNL" - PREFIX_PATH="$MPICH_DIR" - fi + EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" fi From 91fc1d8142cb59e076674661c8ff8c5d3750fbdf Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 10 May 2023 16:19:09 -0500 Subject: [PATCH 065/219] Better boundaries This commit gives all boundaries control to KHARMA, and cleans up some things around the last couple cherry-picks/forward-ports (like adding reconstruction types, from multizone_stable). Still in progress: Moving to PEP1 functions Get<>, GetOfType<> Moving to Flags as Kokkos prof regions Updating par files for new boundaries parameters --- external/parthenon | 2 +- .../parthenon-use-gr-coordinates.patch | 17 +- kharma/b_cleanup/b_cleanup.cpp | 9 +- kharma/b_flux_ct/b_flux_ct.cpp | 339 +++--------- kharma/boundaries/boundaries.cpp | 522 ++++++++---------- kharma/boundaries/boundaries.hpp | 32 +- kharma/boundaries/boundary_types.hpp | 186 +++++++ kharma/boundaries/dirichlet.cpp | 137 +++++ kharma/boundaries/dirichlet.hpp | 55 ++ kharma/coordinates/coordinate_embedding.hpp | 42 +- kharma/decs.hpp | 12 +- kharma/driver/imex_step.cpp | 2 +- kharma/driver/kharma_driver.cpp | 28 +- kharma/driver/kharma_step.cpp | 2 +- kharma/electrons/electrons.cpp | 7 +- kharma/floors/floors.cpp | 17 +- kharma/floors/floors.hpp | 3 +- kharma/grmhd/grmhd.cpp | 2 - kharma/implicit/implicit.cpp | 21 +- kharma/kharma.cpp | 70 ++- kharma/kharma_package.cpp | 44 +- kharma/kharma_package.hpp | 16 +- kharma/main.cpp | 10 +- kharma/prob/bondi.cpp | 25 +- kharma/prob/bondi.hpp | 12 +- kharma/prob/bz_monopole.cpp | 7 - kharma/prob/elec/driven_turbulence.hpp | 1 - kharma/prob/elec/hubble.cpp | 14 +- kharma/prob/elec/hubble.hpp | 14 +- kharma/prob/elec/noh.hpp | 2 - kharma/prob/emhd/anisotropic_conduction.hpp | 1 - kharma/prob/emhd/conducting_atmosphere.cpp | 76 +-- kharma/prob/emhd/emhdmodes.hpp | 1 - kharma/prob/emhd/emhdshock.hpp | 1 - kharma/prob/fm_torus.cpp | 4 +- kharma/prob/gizmo.cpp | 2 - kharma/prob/mhdmodes.hpp | 2 - kharma/prob/orszag_tang.hpp | 1 - kharma/prob/problem.cpp | 14 +- kharma/prob/resize_restart_kharma.cpp | 10 +- kharma/prob/shock_tube.hpp | 1 - kharma/reconstruction.hpp | 69 ++- kharma/reductions/reductions.cpp | 3 +- kharma/types.hpp | 156 +++--- machines/darwin.sh | 24 +- machines/delta.sh | 9 +- pars/anisotropic_conduction.par | 12 +- pars/bondi.par | 32 +- pars/bondi_b_vertical.par | 2 - pars/bz_monopole_vertical.par | 76 --- pars/orszag_tang.par | 12 +- pars/sane.par | 2 +- tests/bclean/bondi_multizone.par | 2 +- 53 files changed, 1087 insertions(+), 1075 deletions(-) create mode 100644 kharma/boundaries/boundary_types.hpp create mode 100644 kharma/boundaries/dirichlet.cpp create mode 100644 kharma/boundaries/dirichlet.hpp delete mode 100644 pars/bz_monopole_vertical.par diff --git a/external/parthenon b/external/parthenon index 6e4d9ea9..b6c1979d 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 6e4d9ea9b3961b5d0129cb5b1254256f5f2331be +Subproject commit b6c1979d6f826f8461556958f09dd81e7fd45095 diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index 48dbb5ef..3b4b816c 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index f45cc979..9d99a1bf 100644 +index 45566b0b..a9abdc1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,16 +11,7 @@ index f45cc979..9d99a1bf 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -281,6 +281,8 @@ endif() - - target_link_libraries(parthenon PUBLIC Kokkos::kokkos) - -+target_link_libraries(parthenon PUBLIC stdc++fs) -+ - if (PARTHENON_ENABLE_ASCENT) - if (ENABLE_MPI) - target_link_libraries(parthenon PUBLIC ascent::ascent_mpi) -@@ -301,6 +303,8 @@ lint_target(parthenon) +@@ -309,6 +309,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ @@ -55,10 +46,10 @@ index 6a1d72c9..b5ba609b 100644 return it->second; } diff --git a/src/interface/meshblock_data.cpp b/src/interface/meshblock_data.cpp -index ca4aa5fb..d7cc33ec 100644 +index 8d5dca57..0ab7dad8 100644 --- a/src/interface/meshblock_data.cpp +++ b/src/interface/meshblock_data.cpp -@@ -430,7 +430,7 @@ MeshBlockData::GetVariablesByFlag(const Metadata::FlagCollection &flags, +@@ -440,7 +440,7 @@ MeshBlockData::GetVariablesByUid(const std::vector &uids) { template void MeshBlockData::Remove(const std::string &label) { diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index aed63544..c4428b30 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -64,7 +64,6 @@ using namespace parthenon::solvers; std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing B Field Cleanup"); auto pkg = std::make_shared("B_Cleanup"); Params ¶ms = pkg->AllParams(); @@ -246,9 +245,9 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) // There's no MeshData-wide 'Remove' so we go block-by-block for (auto& pmb : pmesh->block_list) { auto rc_s = pmb->meshblock_data.Get("solve"); - auto varlabels = rc_s->GetVariablesByFlag({Metadata::GetUserFlag("MHD")}).labels(); - for (auto varlabel : varlabels) { - rc_s->Remove(varlabel); + auto vars = rc_s->GetVariablesByFlag({Metadata::GetUserFlag("MHD")}).vars(); + for (auto var : vars) { + rc_s->Remove(var->label()); } } auto &msolve = pmesh->mesh_data.GetOrAdd("solve", 0); @@ -294,7 +293,6 @@ TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) // TODO anything FillGhost & not Conserved or Primitive for (auto& pmb : blocks) { auto rc_s = pmb->meshblock_data.Get(); - //auto varlabels = rc_s->GetVariablesByName({"pk0", "res0", "divB_RHS", "p"}).labels(); for (auto varlabel : {"pk0", "res0", "temp0", "divB_RHS", "p"}) { if (rc_s->HasCellVariable(varlabel)) rc_s->Remove(varlabel); @@ -323,7 +321,6 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); double b1, b2, b3; - B_FluxCT::MeshUtoP(md, IndexDomain::interior); B_FluxCT::center_grad(G, P, b, k, j, i, ndim > 2, b1, b2, b3); B(b, V1, k, j, i) -= b1; B(b, V2, k, j, i) -= b2; diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index e0092da5..7d8b2b0c 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -53,21 +53,25 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetBoolean("coordinates", "spherical"); // TODO could do package + // TODO turn into fix_flux_x2 etc. + bool spherical = pin->GetBoolean("coordinates", "spherical"); bool fix_polar_flux = pin->GetOrAddBoolean("b_field", "fix_polar_flux", spherical); params.Add("fix_polar_flux", fix_polar_flux); - // These options do the same to the inner and outer edges. They are NOT as well tested, and it's - // questionable whether you'd want to do this anyway. - // They would require at least B1 to be reflected across the EH, probably straight-up reflecting conditions - bool fix_eh_flux = pin->GetOrAddBoolean("b_field", "fix_eh_flux", false); - params.Add("fix_eh_flux", fix_eh_flux); - bool fix_exterior_flux = pin->GetOrAddBoolean("b_field", "fix_exterior_flux", false); - params.Add("fix_exterior_flux", fix_exterior_flux); - // This option uses a different (better but slower) fix which allows magnetic flux through the X1 boundaries, - // at the cost of some speed and potentially some instability due to the non-local nature of the solve. - // Much better tested than above options - bool fix_x1_flux = pin->GetOrAddBoolean("b_field", "fix_x1_flux", false); - params.Add("fix_x1_flux", fix_x1_flux); + // These options do a similar fix to the inner and outer radial edges, which is less commonly necessary. + // They require constant (Dirichlet) boundary conditions + // These are the "Bflux0" prescription designed by Hyerin Cho + bool fix_flux_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_x1", false); + // Split out options. Turn off inner edge by default if inner bound is within EH + bool r_in_eh = spherical && pin->GetBoolean("coordinates", "domain_intersects_eh"); + bool fix_flux_inner_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_inner_x1", fix_flux_x1 && !r_in_eh); + params.Add("fix_flux_inner_x1", fix_flux_inner_x1); + bool fix_flux_outer_x1 = pin->GetOrAddBoolean("b_field", "fix_flux_outer_x1", fix_flux_x1); + params.Add("fix_flux_outer_x1", fix_flux_outer_x1); + // This reverts to a more ham-fisted fix which explicitly disallows flux crossing the X1 face. + // This version requires *inverted* B1 across the face, potentially just using reflecting conditions for B + // Using this version is tremendously inadvisable: consult your simulator before applying. + bool use_old_x1_fix = pin->GetOrAddBoolean("b_field", "use_old_x1_fix", false); + params.Add("use_old_x1_fix", use_old_x1_fix); // KHARMA requires some kind of field transport if there is a magnetic field allocated // Use this if you actually want to disable all magnetic field flux corrections, @@ -75,6 +79,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("b_field", "disable_flux_ct", false); params.Add("disable_flux_ct", disable_flux_ct); + // Default to stopping execution when divB is large, which generally indicates something + // has gone wrong. As always, can be disabled by the brave. bool kill_on_large_divb = pin->GetOrAddBoolean("b_field", "kill_on_large_divb", true); params.Add("kill_on_large_divb", kill_on_large_divb); Real kill_on_divb_over = pin->GetOrAddReal("b_field", "kill_on_divb_over", 1.e-3); @@ -103,7 +109,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), @@ -215,13 +221,10 @@ void FixFlux(MeshData *md) FixBoundaryFlux(md, IndexDomain::inner_x2, false); FixBoundaryFlux(md, IndexDomain::outer_x2, false); } - if (params.Get("fix_x1_flux")) { - FixX1Flux(md); - } - if (params.Get("fix_eh_flux")) { + if (params.Get("fix_flux_inner_x1")) { FixBoundaryFlux(md, IndexDomain::inner_x1, false); } - if (params.Get("fix_exterior_flux")) { + if (params.Get("fix_flux_outer_x1")) { FixBoundaryFlux(md, IndexDomain::outer_x1, false); } FluxCT(md); @@ -308,6 +311,9 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) const int ndim = pmesh->ndim; if (ndim < 2) return; + // Option for old, pre-Bflux0 + const bool use_old_x1_fix = pmb0->packages.Get("B_FluxCT")->Param("use_old_x1_fix"); + auto bounds = coarse ? pmb0->c_cellbounds : pmb0->cellbounds; const IndexRange ib = bounds.GetBoundsI(IndexDomain::interior); const IndexRange jb = bounds.GetBoundsJ(IndexDomain::interior); @@ -370,259 +376,82 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) ); } - // TODO the following is dead without an accompanying inverted-B1 or reflecting boundary - // for magnetic fields in KBoundaries. (Unless you want to reflect everything, which, don't.) - // Keeping special boundaries for this silly test kicking around KBoundaries was ugly, so they're - // removed. Could investigate further when Parthenon's better boundary support appears. - - // We can do the same with the outflow bounds. Kind of. - // See, actually, outflow bounds will *always* generate divergence on the domain face. - // So if we want to clean it up here, we would need to arrange for B1 to be inverted in ghost cells. - // This is no longer pure outflow, but might be thought of as a "nicer" version of - // reflecting conditions: - // 1. Since B1 is inverted, B1 on the domain face will tend to 0 (it's not quite reflected, but basically) - // (obviously don't enable this for monopole test problems!) - // 2. However, B2 and B3 are normal outflow conditions -- despite the fluxes here, the outflow - // conditions will set them equal to the last zone. - if (domain == IndexDomain::inner_x1 && - pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_b_in", kbs.s, kbs.e, jbs.s, jbs.e, ibf.s, ibf.s, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - B_F.flux(X1DIR, V2, k, j, i) = 0.; - B_F.flux(X1DIR, V3, k, j, i) = 0.; - B_F.flux(X2DIR, V1, k, j, i - 1) = -B_F.flux(X2DIR, V1, k, j, i); - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i - 1) = -B_F.flux(X3DIR, V1, k, j, i); - } - ); - } - - if (domain == IndexDomain::outer_x1 && - pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_b_out", kbs.s, kbs.e, jbs.s, jbs.e, ibf.e, ibf.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - B_F.flux(X1DIR, V2, k, j, i) = 0.; - B_F.flux(X1DIR, V3, k, j, i) = 0.; - B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, i - 1); - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, i - 1); - } - ); - } - - } - - Flag(md, "Fixed polar B"); -} - -TaskStatus FixX1Flux(MeshData *md) -{ - Flag(md, "Fixing X1 fluxes"); - auto pmesh = md->GetMeshPointer(); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - IndexDomain domain = IndexDomain::interior; - int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); - int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); - int js_all = pmb0->cellbounds.js(IndexDomain::entire), je_all = pmb0->cellbounds.je(IndexDomain::entire); // added by Hyerin (12/28/22) - int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); - int ks_all = pmb0->cellbounds.ks(IndexDomain::entire), ke_all = pmb0->cellbounds.ke(IndexDomain::entire); // added by Hyerin (12/28/22) - const int ndim = pmesh->ndim; - - int je_e = (ndim > 1) ? je + 1 : je; - //int je_e = (ndim > 1) ? je_all + 1 : je_all; // test Hyerin(12/28/22) - int ke_e = (ndim > 2) ? ke + 1 : ke; - //int ke_e = (ndim > 2) ? ke_all + 1 : ke_all; // test Hyerin (12/28/22) - int js_new, je_new; // Hyerin (02/21/23) - bool in_x2, out_x2; // Hyerin - - Real x1min = pmb0->packages.Get("GRMHD")->Param("x1min"); //Hyerin (01/31/23) - - // (03/08/23) places to store - //const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); - //const int n2 = pmb0->cellbounds.ncellsj(IndexDomain::entire); - //const int n3 = pmb0->cellbounds.ncellsk(IndexDomain::entire); - //GridScalar B_F_X2_V1("B_F_X2_V1", n3, n2, n1); // for B_F.flux(X2DIR,V1,k,j,i) - //GridScalar B_F_X3_V1("B_F_X3_V1", n3, n2, n1); // for B_F.flux(X3DIR,V1,k,j,i) - //auto B_F_X2_V1_host = B_F_X2_V1.GetHostMirror(); - //auto B_F_X3_V1_host = B_F_X3_V1.GetHostMirror(); - //auto B_F_host = x2_fill_device.GetHostMirror(); - GridVector F1, F2, F3; - - // TODO(BSP) try to eliminate full-array copies. Host-parallel applications to inner/outer? - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - auto& B_F = rc->PackVariablesAndFluxes(std::vector{"cons.B"}); - - // (03/08/23) - F1 = rc->Get("cons.B").flux[X1DIR]; // B_F.flux(X1DIR,v,k,j,i) - F2 = rc->Get("cons.B").flux[X2DIR]; // B_F.flux(X2DIR,v,k,j,i) - F3 = rc->Get("cons.B").flux[X3DIR]; // B_F.flux(X3DIR,v,k,j,i) - auto F1_host=F1.GetHostMirrorAndCopy(); - auto F2_host=F2.GetHostMirrorAndCopy(); - auto F3_host=F3.GetHostMirrorAndCopy(); - - // update the j and k bounds (Hyerin 02/21/23) - js_new = js+1; //js-1; - je_new = je_e+1; //je_e+1; - in_x2 = false; - out_x2 = false; - if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { - in_x2 = true; - js_new = js; - } - if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { - out_x2 = true; - je_new = je; //_e; - } - - //printf("HYERIN: test F1V2 %g\n",F1_host(V2,30,30,is)); - //pmb->par_for("test", 30,30,30,30,is,is, - // KOKKOS_LAMBDA_3D { - // printf("HYERIN: test B_F(X1DIR,V2) %g, F1V2 %g \n",B_F.flux(X1DIR,V2,k,j,i),F1(V2,k,j,i)); - // } - //); - - //added by Hyerin (12/23/22) TODO: it has to ask if x2 boundary is inner_x2 or outer_x2 and update the jj bounds - if ((pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) && (x1min>1) ) // only apply fix flux for inner bc when it is far from the EH - { - for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { - for (int jtemp = js_new; jtemp <= je_new; jtemp++) { - //pmb->par_for("fix_flux_b_l", ktemp, ktemp, jtemp, jtemp, is, is, // Hyerin (02/20/23) for 3rd prescription, sequential - //pmb->par_for("fix_flux_b_l", ks_all+2, ke_all, js_new, je_new, is, is, // Hyerin (02/20/23) for 3rd prescription - //pmb->par_for("fix_flux_b_l", ks_all+1, ke_all+1, js_all+1, je_all+1, is, is, // Hyerin (12/28/22) for 1st & 2nd prescription - // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - /* 1st prescription to make the X1DIR flux = 0 - B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is); - if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is); - */ - // (02/06/23) 2nd prescription that allows nonzero flux across X1 boundary but still keeps divB=0 (turns out effectively to have 0 flux) - //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) + B_F.flux(X1DIR, V2, k, j-1, is); - //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) + B_F.flux(X1DIR, V3, k-1, j, is); - // - // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux - if (ndim > 1) { - //B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, is) + B_F.flux(X1DIR, V2, k, j, is) - B_F.flux(X1DIR, V2, k, j-2, is) + B_F.flux(X2DIR, V1, k, j-1, is) + B_F.flux(X2DIR, V1, k, j-1, is-1); - F2_host(V1, ktemp, jtemp, is-1) = -F2_host(V1, ktemp, jtemp, is) + F1_host(V2, ktemp, jtemp, is) - F1_host(V2, ktemp, jtemp-2, is) + F2_host(V1, ktemp, jtemp-1, is) + F2_host(V1, ktemp, jtemp-1, is-1); - } - if (ndim > 2) { - //B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, is) + B_F.flux(X1DIR, V3, k, j, is) - B_F.flux(X1DIR, V3, k-2, j, is) + B_F.flux(X3DIR, V1, k-1, j, is) + B_F.flux(X3DIR, V1, k-1, j, is-1); - F3_host(V1, ktemp, jtemp, is-1) = -F3_host(V1, ktemp, jtemp, is) + F1_host(V3, ktemp, jtemp, is) - F1_host(V3, ktemp-2, jtemp, is) + F3_host(V1, ktemp-1, jtemp, is) + F3_host(V1, ktemp-1, jtemp, is-1); + // TODO(BSP) could check here we're operating with the right boundaries: Dirichlet for Bflux0, + // reflecting/B1 reflect for old stuff + if (!use_old_x1_fix) { + // "Bflux0" prescription for keeping divB~=0 on zone corners of the interior & exterior X1 faces + // Courtesy of & implemented by Hyerin Cho + // Allows nonzero flux across X1 boundary but still keeps divB=0 (turns out effectively to have 0 flux) + // Usable only for Dirichlet conditions + if (domain == IndexDomain::inner_x1 && + pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) + { + pmb->par_for("fix_flux_b_in", kbs.s, kbs.e, jbs.s, jbs.e, ibf.s, ibf.s, // Hyerin (12/28/22) for 1st & 2nd prescription + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // Allows nonzero flux across X1 boundary but still keeps divB=0 (turns out effectively to have 0 flux) + if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, j, i) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i-1) = -B_F.flux(X3DIR, V1, k, j, i) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); } + ); - //if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) - if (in_x2 && (jtemp==js)) {// (corners are tricky so let's just initialize) - //B_F.flux(X2DIR, V1,k,j,i-1) = -B_F.flux(X1DIR,V2,k,j,i+1) -B_F.flux(X1DIR,V2,k,j-1,i+1); - F2_host(V1,ktemp,jtemp,is-1) = -F1_host(V2,ktemp,jtemp,is+1) -F1_host(V2,ktemp,jtemp-1,is+1); - //B_F.flux(X2DIR, V1,k,j,i) = -0.5*B_F.flux(X2DIR,V1,k,j,i-1); - F2_host(V1,ktemp,jtemp,is) = -0.5*F2_host(V1,ktemp,jtemp,is-1); + } + if (domain == IndexDomain::outer_x2 && + pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) + { + pmb->par_for("fix_flux_b_out", kbs.s, kbs.e, jbs.s, jbs.e, ibf.e, ibf.e, // Hyerin (12/28/22) for 1st & 2nd prescription + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // (02/06/23) 2nd prescription that allows nonzero flux across X1 boundary but still keeps divB=0 + if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, i-1) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, i-1) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); } - //if (out_x2 && (j==je_e)) {// (corners are tricky) - if (out_x2 && (jtemp==je_e)) {// (corners are tricky) ( so maybe just don't touch it...? (03/12/23) - //B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, je, is) - B_F.flux(X2DIR, V1, k, je, is-1) - // +B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je-1, is); - //B_F.flux(X2DIR, V1, k, j, i-1) = -2.*B_F.flux(X1DIR, V2, k, je-1, is) -B_F.flux(X1DIR, V2, k, je, is) + B_F.flux(X1DIR, V2, k, je+1, is) - // +2.*B_F.flux(X2DIR, V1, k, je, is) + 2.*B_F.flux(X2DIR, V1, k, je, is-1); - //B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); - F1_host(V2,ktemp,jtemp-1,is) = -F1_host(V2,ktemp,je-1,is)+F2_host(V1,ktemp,je,is)+F2_host(V1,ktemp,je,is-1); - //B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); - F1_host(V2,ktemp,jtemp,is) = -F1_host(V2,ktemp,je,is); + ); + } + } else { + // These boundary conditions need to arrange for B1 to be inverted in ghost cells. + // This is no longer pure outflow, but might be thought of as a "nicer" version of + // reflecting conditions: + // 1. Since B1 is inverted, B1 on the domain face will tend to 0 (it's not quite reflected, but basically) + // (obviously don't enable this for monopole test problems!) + // 2. However, B2 and B3 are normal outflow conditions -- despite the fluxes here, the outflow + // conditions will set them equal to the last zone. + if (domain == IndexDomain::inner_x1 && + pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_in_old", kbs.s, kbs.e, jbs.s, jbs.e, ibf.s, ibf.s, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X1DIR, V2, k, j, i) = 0.; + B_F.flux(X1DIR, V3, k, j, i) = 0.; + B_F.flux(X2DIR, V1, k, j, i - 1) = -B_F.flux(X2DIR, V1, k, j, i); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i - 1) = -B_F.flux(X3DIR, V1, k, j, i); } - - - //} - // ); - } + ); } - } - if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) - { - for (int ktemp = ks_all+2; ktemp <=ke_all; ktemp++) { - for (int jtemp = js_new; jtemp <= je_new; jtemp++) { - //pmb->par_for("fix_flux_b_r", ktemp, ktemp, jtemp, jtemp, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription, sequential - //pmb->par_for("fix_flux_b_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, // Hyerin (02/20/23) for 3rd prescription - //pmb->par_for("fix_flux_b_r", ks_all+1, ke_all+1, js_all+1, je_all+1, ie+1, ie+1, // Hyerin (12/28/22) for 1st & 2nd prescription - // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - /* 1st prescription to make the X1DIR flux = 0 - B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie); - if (ndim > 1) VLOOP B_F.flux(X1DIR, V1+v, k, j, i) = 0; - if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie); - */ - // (02/06/23) 2nd prescription that allows nonzero flux across X1 boundary but still keeps divB=0 - //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, i) + B_F.flux(X1DIR, V2, k, j-1, i); - //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, i) + B_F.flux(X1DIR, V3, k-1, j, i); - // - // (02/20/23) 3rd prescription that is similar to 2nd prescription but not local and nonzero effective flux - //if (ndim > 1) B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, ie) + B_F.flux(X1DIR, V2, k, j, ie+1) - // - B_F.flux(X1DIR, V2, k, j-2, ie+1) + B_F.flux(X2DIR, V1, k, j-1, ie) + B_F.flux(X2DIR, V1, k, j-1, ie+1); - if (ndim > 1) F2_host(V1, ktemp, jtemp, ie+1) = -F2_host(V1, ktemp, jtemp, ie) + F1_host(V2, ktemp, jtemp, ie+1) - - F1_host(V2, ktemp, jtemp-2, ie+1) + F2_host(V1, ktemp, jtemp-1, ie) + F2_host(V1, ktemp, jtemp-1, ie+1); - //if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, ie) + B_F.flux(X1DIR, V3, k, j, ie+1) - // - B_F.flux(X1DIR, V3, k-2, j, ie+1) + B_F.flux(X3DIR, V1, k-1, j, ie) + B_F.flux(X3DIR, V1, k-1, j, ie+1); - if (ndim > 2) F3_host(V1, ktemp, jtemp, ie+1) = -F3_host(V1, ktemp, jtemp, ie) + F1_host(V3, ktemp, jtemp, ie+1) - - F1_host(V3, ktemp-2, jtemp, ie+1) + F3_host(V1, ktemp-1, jtemp, ie) + F3_host(V1, ktemp-1, jtemp, ie+1); - - //if (in_x2 && (j==js)) {// (corners are tricky so let's just initialize) - if (in_x2 && (jtemp==js)) {// (corners are tricky so let's just initialize) - //B_F.flux(X2DIR, V1,k,j,i) = -B_F.flux(X1DIR,V2,k,j,ie) -B_F.flux(X1DIR,V2,k,j-1,ie); - F2_host(V1,ktemp,jtemp,ie+1) = -F1_host(V2,ktemp,jtemp,ie) -F1_host(V2,ktemp,jtemp-1,ie); - //B_F.flux(X2DIR, V1,k,j,i-1) = -0.5*B_F.flux(X2DIR,V1,k,j,i); - F2_host(V1,ktemp,jtemp,ie) = -0.5*F2_host(V1,ktemp,jtemp,ie+1); + if (domain == IndexDomain::outer_x1 && + pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { + pmb->par_for("fix_flux_b_out_old", kbs.s, kbs.e, jbs.s, jbs.e, ibf.e, ibf.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_F.flux(X1DIR, V2, k, j, i) = 0.; + B_F.flux(X1DIR, V3, k, j, i) = 0.; + B_F.flux(X2DIR, V1, k, j, i) = -B_F.flux(X2DIR, V1, k, j, i - 1); + if (ndim > 2) B_F.flux(X3DIR, V1, k, j, i) = -B_F.flux(X3DIR, V1, k, j, i - 1); } - //if (out_x2 && (j==je_e)) {// (corners are tricky) - if (out_x2 && (jtemp==je_e)) {// (corners are tricky) - //B_F.flux(X2DIR, V1, k, j, i-1) = -B_F.flux(X2DIR, V1, k, je, ie) - B_F.flux(X2DIR, V1, k, je, ie+1) - // +B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je-1, ie+1); - //B_F.flux(X2DIR, V1, k, j, i) = -2.*B_F.flux(X1DIR, V2, k, je-1, ie+1) -B_F.flux(X1DIR, V2, k, je, ie+1) + B_F.flux(X1DIR, V2, k, je+1, ie+1) - // +2.*B_F.flux(X2DIR, V1, k, je, ie) + 2.*B_F.flux(X2DIR, V1, k, je, ie+1); - //B_F.flux(X1DIR,V2,k,j-1,i) = -B_F.flux(X1DIR,V2,k,je-1,i)+B_F.flux(X2DIR,V1,k,je,i)+B_F.flux(X2DIR,V1,k,je,i-1); - F1_host(V2,ktemp,jtemp-1,ie+1) = -F1_host(V2,ktemp,je-1,ie+1)+F2_host(V1,ktemp,je,ie+1)+F2_host(V1,ktemp,je,ie); - //B_F.flux(X1DIR,V2,k,j,i) = -B_F.flux(X1DIR,V2,k,je,i); - F1_host(V2,ktemp,jtemp,ie+1) = -F1_host(V2,ktemp,je,ie+1); - } - //} - //); - } + ); } } - // Deep copy to device - F1.DeepCopy(F1_host); - F2.DeepCopy(F2_host); - F3.DeepCopy(F3_host); - Kokkos::fence(); - - // put it back to B_F.flux. is this even needed? - //pmb->par_for("copy_to_B_F_l", ks_all+2, ke_all, js_new, je_new, is, is, - // KOKKOS_LAMBDA_3D { - // VLOOP B_F.flux(X1DIR,v,k,j,i) = F1(v,k,j,i); - // VLOOP B_F.flux(X2DIR,v,k,j,i) = F2(v,k,j,i); - // VLOOP B_F.flux(X3DIR,v,k,j,i) = F3(v,k,j,i); - // } - //); - //pmb->par_for("copy_to_B_F_r", ks_all+2, ke_all, js_new, je_new, ie+1, ie+1, - // KOKKOS_LAMBDA_3D { - // VLOOP B_F.flux(X1DIR,v,k,j,i) = F1(v,k,j,i); - // VLOOP B_F.flux(X2DIR,v,k,j,i) = F2(v,k,j,i); - // VLOOP B_F.flux(X3DIR,v,k,j,i) = F3(v,k,j,i); - // } - //); - - + } - Flag(md, "Fixed X1 B"); - return TaskStatus::complete; + Flag(md, "Fixed polar B"); } -// Outflow boundary conditions without the fix_eh_flux special sauce *always* generate divB. -// Don't report it, as we expect it. -// TODO we could stay off x2 if two_sync, but I wanna drive home that's weird for a cycle IndexRange ValidDivBX1(MeshBlock *pmb) { + // All user, physical (not MPI/periodic) boundary conditions in X1 will generate divB on corners + // intersecting the interior & exterior faces. Don't report these zones, as we expect it. const IndexRange ibl = pmb->meshblock_data.Get()->GetBoundsI(IndexDomain::interior); - bool avoid_inner = (!pmb->packages.Get("B_FluxCT")->Param("fix_eh_flux") && + bool avoid_inner = (!pmb->packages.Get("B_FluxCT")->Param("fix_flux_inner_x1") && pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user); - bool avoid_outer = (!pmb->packages.Get("B_FluxCT")->Param("fix_exterior_flux") && + bool avoid_outer = (!pmb->packages.Get("B_FluxCT")->Param("fix_flux_outer_x1") && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user); return IndexRange{ibl.s + (avoid_inner), ibl.e + (!avoid_outer)}; } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 05e635ef..e3160037 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -1,25 +1,25 @@ -/* +/* * File: boundaries.cpp - * + * * BSD 3-Clause License - * + * * Copyright (c) 2020, AFD Group at UIUC * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -44,55 +44,47 @@ // Parthenon's boundaries #include -std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std::shared_ptr& packages) +std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std::shared_ptr &packages) { - Flag("Initializing Boundaries"); - auto pkg = std::make_shared("Boundaries"); Params ¶ms = pkg->AllParams(); - // Prevent inflow at boundaries. - // This is two separate checks, but default to enabling/disabling together + // OPTIONS FOR SPECIFIC BOUNDARIES bool spherical = pin->GetBoolean("coordinates", "spherical"); - bool check_inflow = pin->GetOrAddBoolean("boundaries", "check_inflow", spherical); - bool check_inflow_inner = pin->GetOrAddBoolean("boundaries", "check_inflow_inner", check_inflow); - params.Add("check_inflow_inner", check_inflow_inner); - bool check_inflow_flux_inner = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_inner", check_inflow_inner); - params.Add("check_inflow_flux_inner", check_inflow_flux_inner); - bool check_inflow_outer = pin->GetOrAddBoolean("boundaries", "check_inflow_outer", check_inflow); - params.Add("check_inflow_outer", check_inflow_outer); - bool check_inflow_flux_outer = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_outer", check_inflow_outer); - params.Add("check_inflow_flux_outer", check_inflow_flux_outer); + // Global check inflow sets inner/outer X1 by default + bool check_inflow_global = pin->GetOrAddBoolean("boundaries", "check_inflow", spherical); // Ensure fluxes through the zero-size face at the pole are zero - bool fix_flux_pole = pin->GetOrAddBoolean("boundaries", "fix_flux_pole", spherical); - params.Add("fix_flux_pole", fix_flux_pole); + bool zero_polar_flux = pin->GetOrAddBoolean("boundaries", "zero_polar_flux", spherical); + params.Add("zero_polar_flux", zero_polar_flux); // Fix the X1/X2 corner by replacing the reflecting condition with the inflow - // Only needed if x1min is inside BH event horizon, otherwise a nuisance for divB on corners + // Never use this if not in spherical coordinates + // Activates by default only with reflecting X2/outflow X1 and interior boundary inside EH + // TODO(BSP) may also be specific to Funky MKS coords with zero_point==startx1 + bool fix_corner = false; if (spherical) { - const Real a = pin->GetReal("coordinates", "a"); - bool inside_eh = pin->GetBoolean("coordinates", "r_in") < (1 + sqrt(1 - a*a)); - bool fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", inside_eh); - params.Add("fix_corner", fix_corner); + bool correct_bounds = + (pin->GetString("boundaries", "inner_x2") == "reflecting" && + pin->GetString("boundaries", "outer_x2") == "reflecting" && + pin->GetString("boundaries", "inner_x1") == "outflow"); + bool inside_eh = pin->GetBoolean("coordinates", "domain_intersects_eh"); + fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", correct_bounds && inside_eh); } + params.Add("fix_corner", fix_corner); - // Allocate space for Dirichlet boundaries if they'll be used - // We have to trust the user here since the problem will set the function pointers later - // TODO specify which boundaries individually for cleanliness? - bool use_dirichlet = pin->GetOrAddBoolean("boundaries", "prob_uses_dirichlet", false); - params.Add("use_dirichlet", use_dirichlet); - if (use_dirichlet) { - auto& driver = packages->Get("Driver")->AllParams(); - + Metadata m_x1, m_x2, m_x3; + { // We can't use GetVariablesByFlag yet, so walk through and count manually int nvar = 0; for (auto pkg : packages->AllPackages()) { - //std::cerr << pkg.first << ": "; for (auto field : pkg.second->AllFields()) { - //std::cerr << field.first.label() << " "; // Specifically ignore the B_Cleanup variables, we don't handle their boundary conditions - if (field.second.IsSet(Metadata::FillGhost) && !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup"))) { + // TODO "Present" or "Has" in Packages_t + bool is_not_cleanup = packages->AllPackages().count("B_Cleanup") + ? !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup")) + : true; + if (field.second.IsSet(Metadata::FillGhost) && is_not_cleanup) { if (field.second.Shape().size() < 1) { nvar += 1; } else { @@ -100,80 +92,168 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: } } } - //std::cerr << std::endl; } // We also don't know the mesh size, since it's not constructed. We infer. const int ng = pin->GetInteger("parthenon/mesh", "nghost"); const int nx1 = pin->GetInteger("parthenon/meshblock", "nx1"); - const int n1 = nx1 + 2*ng; + const int n1 = nx1 + 2 * ng; const int nx2 = pin->GetInteger("parthenon/meshblock", "nx2"); - const int n2 = (nx2 == 1) ? nx2 : nx2 + 2*ng; + const int n2 = (nx2 == 1) ? nx2 : nx2 + 2 * ng; const int nx3 = pin->GetInteger("parthenon/meshblock", "nx3"); - const int n3 = (nx3 == 1) ? nx3 : nx3 + 2*ng; - - if (pin->GetInteger("debug", "verbose") > 0) { - std::cout << "Allocating Dirichlet boundaries for " << nvar << " variables." << std::endl; - if (pin->GetInteger("debug", "verbose") > 1) { - std::cout << "Initializing Dirichlet bounds with dimensions nvar,n1,n2,n3: " << nvar << " " << n1 << " " << n2 << " " << n3 << " and ng: " << ng << std::endl; - } - } + const int n3 = (nx3 == 1) ? nx3 : nx3 + 2 * ng; // These are declared *backward* from how they will be indexed std::vector s_x1({ng, n2, n3, nvar}); std::vector s_x2({n1, ng, n3, nvar}); std::vector s_x3({n1, n2, ng, nvar}); // Dirichlet conditions must be restored when restarting! Needs Metadata::Restart when this works! - Metadata m_x1 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x1); - Metadata m_x2 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x2); - Metadata m_x3 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x3); - pkg->AddField("bound.inner_x1", m_x1); - pkg->AddField("bound.outer_x1", m_x1); - pkg->AddField("bound.inner_x2", m_x2); - pkg->AddField("bound.outer_x2", m_x2); - pkg->AddField("bound.inner_x3", m_x3); - pkg->AddField("bound.outer_x3", m_x3); + m_x1 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x1); + m_x2 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x2); + m_x3 = Metadata({Metadata::Real, Metadata::Derived, Metadata::OneCopy}, s_x3); + } + + // Set options for each boundary + for (int i = 0; i < BOUNDARY_NFACES; i++) + { + const auto bface = (BoundaryFace) i; + const auto bdomain = BoundaryDomain(bface); + const auto bname = BoundaryName(bface); + const auto bdir = BoundaryDirection(bface); + const auto binner = BoundaryIsInner(bface); + + // OPTIONS FOR ANY BOUNDARY + + // Prevent inflow at boundaries. + // This is two separate checks, but default to enabling/disabling together for X1 and not elsewhere + bool check_inflow = pin->GetOrAddBoolean("boundaries", "check_inflow_" + bname, check_inflow_global && bdir == X1DIR); + params.Add("check_inflow_" + bname, check_inflow); + bool check_inflow_flux = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_" + bname, check_inflow); + params.Add("check_inflow_flux_" + bname, check_inflow_flux); + + // Ensure fluxes through the zero-size face at the pole are zero + bool zero_flux = pin->GetOrAddBoolean("boundaries", "zero_flux_" + bname, zero_polar_flux && bdir == X2DIR); + params.Add("zero_flux_" + bname, zero_flux); + + // BOUNDARY TYPES + // Get the boundary type we specified in kharma + auto btype = pin->GetString("boundaries", bname); + params.Add(bname, btype); + + // String manip to get the Parthenon boundary name, e.g., "ox1_bc" + auto bname_parthenon = bname.substr(0, 1) + "x" + bname.substr(7, 8) + "_bc"; + // Parthenon implements periodic conditions + // For the rest, they should call our default wrapper, which we register in main() + if (btype == "periodic") { + pin->SetString("parthenon/mesh", bname_parthenon, "periodic"); + } else { + pin->SetString("parthenon/mesh", bname_parthenon, "user"); + } + + // TODO TODO any way to save this verbosity with constexpr/macros/something? + if (btype == "dirichlet") { + // Dirichlet boundaries: allocate + pkg->AddField("bound." + bname, (bdir == X1DIR) ? m_x1 : ((bdir == X2DIR) ? m_x2 : m_x3)); + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + } + } else if (btype == "reflecting") { + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX1; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX1; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX2; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX2; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX3; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX3; + break; + } + } else if (btype == "outflow") { + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX1; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX1; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX2; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX2; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX3; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX3; + break; + } + } } // Callbacks // Fix flux pkg->FixFlux = KBoundaries::FixFlux; - - // KHARMA boundary functions take a domain and are trusted to handle it - pkg->KHARMAInnerX1Boundary = KBoundaries::DefaultBoundary; - pkg->KHARMAOuterX1Boundary = KBoundaries::DefaultBoundary; - pkg->KHARMAInnerX2Boundary = KBoundaries::DefaultBoundary; - pkg->KHARMAOuterX2Boundary = KBoundaries::DefaultBoundary; - - Flag("Initialized"); return pkg; } void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag("Applying a KHARMA boundary"); + Flag("Apply boundary"); // KHARMA has to do some extra tasks in addition to just applying the usual // boundary conditions. Therefore, we "wrap" Parthenon's (or our own) // boundary functions with this one. - // TODO call for all packages? auto pmb = rc->GetBlockPointer(); auto pkg = static_cast(pmb->packages.Get("Boundaries").get()); + auto& params = pkg->AllParams(); - // Disambiguate in order to call our pointers - int dir = BoundarySide(domain); - if (dir == 1) { - if (BoundaryIsInner(domain)) { - pkg->KHARMAInnerX1Boundary(rc, domain, coarse); - } else { - pkg->KHARMAOuterX1Boundary(rc, domain, coarse); - } - } else if (dir == 2) { - if (BoundaryIsInner(domain)) { - pkg->KHARMAInnerX2Boundary(rc, domain, coarse); - } else { - pkg->KHARMAOuterX2Boundary(rc, domain, coarse); - } + const auto bface = BoundaryFaceOf(domain); + const auto bname = BoundaryName(bface); + const auto btype_name = params.Get(bname); + const auto bdir = BoundaryDirection(bface); + + Flag("Apply "+bname+" boundary: "+btype_name); + pkg->KBoundaries[bface](rc, coarse); + EndFlag("Apply "+bname+" boundary"); + + // Prevent inflow of material by changing fluid speeds, + // anywhere we've specified. + if (params.Get("check_inflow_" + bname)) { + CheckInflow(rc, domain, coarse); + } + + // If specified, fix corner values when applying X2 boundaries (see function) + if (params.Get("fix_corner") && bdir == X2DIR) { + FixCorner(rc, domain, coarse); } // Respect the fluid primitives on boundaries (*not* B) @@ -181,259 +261,121 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // For everything else, respect conserved variables Packages::BlockUtoPExceptMHD(rc.get(), domain, coarse); - Flag("Applied boundary"); + EndFlag("Apply boundary"); } -void KBoundaries::CheckInflow(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag(rc, "Checking inflow"); + Flag("CheckInflow"); std::shared_ptr pmb = rc->GetBlockPointer(); - const auto& G = pmb->coords; + const auto &G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - bool check_inner = pmb->packages.Get("Boundaries")->Param("check_inflow_inner"); - bool check_outer = pmb->packages.Get("Boundaries")->Param("check_inflow_outer"); - const bool check_inflow = ((check_inner && domain == IndexDomain::inner_x1) - || (check_outer && domain == IndexDomain::outer_x1)); - if (!check_inflow) return; - PackIndexMap prims_map; auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); const VarMap m_p(prims_map, false); // Inflow check // Iterate over zones w/p=0 - pmb->par_for_bndry("Outflow_check_inflow", IndexRange{0,0}, domain, coarse, - KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { + pmb->par_for_bndry( + "Outflow_check_inflow", IndexRange{0, 0}, domain, coarse, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); - - Flag(rc, "Checked"); + EndFlag("CheckInflow"); } -void KBoundaries::FixCorner(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +void KBoundaries::FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag(rc, "Fixing X1/X2 corner block"); + Flag("FixCorner"); std::shared_ptr pmb = rc->GetBlockPointer(); - if (pmb->pmy_mesh->ndim < 2 || - !pmb->packages.Get("Boundaries")->Param("fix_corner")) + if (pmb->pmy_mesh->ndim < 2) return; // If we're on the interior edge, re-apply that edge for our block by calling // exactly the same function that Parthenon does. This ensures we're applying // the same thing, just emulating calling it after X2. - if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) + { ApplyBoundary(rc, IndexDomain::inner_x1, coarse); } - - Flag(rc, "Fixed"); -} - -// void KBoundaries::CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -// { -// Flag(rc, "Correcting the B field w/metric"); -// std::shared_ptr pmb = rc->GetBlockPointer(); -// const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - -// auto B_P = rc->PackVariables(std::vector{"prims.B"}); -// // Return if no field to correct -// if (B_P.GetDim(4) == 0) return; - -// const auto& G = pmb->coords; - -// const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; -// const int dir = BoundarySide(domain); -// const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) -// : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) -// : bounds.GetBoundsK(IndexDomain::interior)); -// const int ref = BoundaryIsInner(domain) ? range.s : range.e; - -// pmb->par_for_bndry("Correct_B_P", IndexRange{0,NVEC-1}, domain, coarse, -// KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { -// B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) -// / G.gdet(Loci::center, j, i); -// } -// ); - -// Flag(rc, "Corrected"); -// } - -void KBoundaries::DefaultBoundary(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -{ - // Default function for applying any (non-periodic) boundary condition: - // outflow in X1 with inflow check, Reflect in X2 with corner fix - auto pmb = rc->GetBlockPointer(); - const int dir = BoundarySide(domain); - if (dir == 1) { - if (BoundaryIsInner(domain)) { - parthenon::BoundaryFunction::OutflowInnerX1(rc, coarse); - } else { - parthenon::BoundaryFunction::OutflowOuterX1(rc, coarse); - } - CheckInflow(rc, domain, coarse); - } else if (dir == 2) { - if (BoundaryIsInner(domain)) { - parthenon::BoundaryFunction::ReflectInnerX2(rc, coarse); - } else { - parthenon::BoundaryFunction::ReflectOuterX2(rc, coarse); - } - FixCorner(rc, domain, coarse); - } -} - -void KBoundaries::SetDomainDirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { - Flag("Setting Dirichlet bound"); - - std::shared_ptr pmb = rc->GetBlockPointer(); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - using FC = Metadata::FlagCollection; - auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); - auto bound = rc->Get("bound."+BoundaryName(domain)).data; - - if (q.GetDim(4) != bound.GetDim(4)) { - std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; - } - - const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; - const bool right = !BoundaryIsInner(domain); - - // Subtract off the starting index if we're on the right - const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundarySide(domain); - const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior)+1 : 0; - const int je = (dir == 2) ? bounds.je(IndexDomain::interior)+1 : 0; - const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior)+1 : 0; - - const auto& G = pmb->coords; - - pmb->par_for_bndry("dirichlet_boundary", vars, domain, coarse, - KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { - if (right) { - bound(p, k-ke, j-je, i-ie) = q(p, k, j, i); - } else { - bound(p, k, j, i) = q(p, k, j, i); - } - } - ); - - Flag("Set"); -} - -void KBoundaries::Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -{ - Flag(rc, "Applying Dirichlet bound"); - - std::shared_ptr pmb = rc->GetBlockPointer(); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - using FC = Metadata::FlagCollection; - auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); - auto bound = rc->Get("bound."+BoundaryName(domain)).data; - - if (q.GetDim(4) != bound.GetDim(4)) { - std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; - } - - const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; - const bool right = !BoundaryIsInner(domain); - - // Subtract off the starting index if we're on the right - const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundarySide(domain); - const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior)+1 : 0; - const int je = (dir == 2) ? bounds.je(IndexDomain::interior)+1 : 0; - const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior)+1 : 0; - - const auto& G = pmb->coords; - - pmb->par_for_bndry("dirichlet_boundary", vars, domain, coarse, - KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { - if (right) { - q(p, k, j, i) = bound(p, k-ke, j-je, i-ie); - } else { - q(p, k, j, i) = bound(p, k, j, i); - } - } - ); - - Flag(rc, "Applied"); + EndFlag("FixCorner"); } TaskStatus KBoundaries::FixFlux(MeshData *md) { - Flag("Fixing fluxes"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); auto& params = pmb0->packages.Get("Boundaries")->AllParams(); - bool check_inflow_inner = params.Get("check_inflow_flux_inner"); - bool check_inflow_outer = params.Get("check_inflow_flux_outer"); - bool fix_flux_pole = params.Get("fix_flux_pole"); - - IndexDomain domain = IndexDomain::interior; - const int is = pmb0->cellbounds.is(domain), ie = pmb0->cellbounds.ie(domain); - const int js = pmb0->cellbounds.js(domain), je = pmb0->cellbounds.je(domain); - const int ks = pmb0->cellbounds.ks(domain), ke = pmb0->cellbounds.ke(domain); - const int ndim = pmesh->ndim; // Fluxes are defined at faces, so there is one more valid flux than // valid cell in the face direction. That is, e.g. F1 is valid on // an (N1+1)xN2xN3 grid, F2 on N1x(N2+1)xN3, etc. // These functions do *not* need an extra row outside the domain, // like B_FluxCT::FixBoundaryFlux does. - const int ie_l = ie + 1; - const int je_l = (ndim > 1) ? je + 1 : je; - //const int ke_l = (ndim > 2) ? ke + 1 : ke; - - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - - PackIndexMap cons_map; - auto& F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); - const int m_rho = cons_map["cons.rho"].first; - - if (check_inflow_inner) { - if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_l", ks, ke, js, je, is, is, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); - } - ); - } - } - if (check_inflow_outer) { - if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { - pmb->par_for("fix_flux_in_r", ks, ke, js, je, ie_l, ie_l, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - F.flux(X1DIR, m_rho, k, j, i) = m::max(F.flux(X1DIR, m_rho, k, j, i), 0.); - } - ); + const int ndim = pmesh->ndim; + // Ranges for sides + const IndexRange ibs = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); + const IndexRange jbs = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); + const IndexRange kbs = pmb0->cellbounds.GetBoundsK(IndexDomain::interior); + // Ranges for faces + const IndexRange ibf = IndexRange{ibs.s, ibs.e + 1}; + const IndexRange jbf = IndexRange{jbs.s, jbs.e + (ndim > 1)}; + const IndexRange kbf = IndexRange{kbs.s, kbs.e + (ndim > 2)}; + + for (auto &pmb : pmesh->block_list) + { + auto &rc = pmb->meshblock_data.Get(); + + for (int i = 0; i < BOUNDARY_NFACES; i++) + { + BoundaryFace bface = (BoundaryFace)i; + auto bname = BoundaryName(bface); + auto bdir = BoundaryDirection(bface); + auto binner = BoundaryIsInner(bface); + + // Set ranges based + IndexRange ib = ibs, jb = jbs, kb = kbs; + // Range for inner_x1 bounds is first face only, etc. + if (bdir == 1) { + ib.s = ib.e = (binner) ? ibf.s : ibf.e; + } else if (bdir == 2) { + jb.s = jb.e = (binner) ? jbf.s : jbf.e; + } else { + kb.s = kb.e = (binner) ? kbf.s : kbf.e; } - } - // This is a lot of zero fluxes! - if (fix_flux_pole) { - if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { - // This loop covers every flux we need - pmb->par_for("fix_flux_pole_l", 0, F.GetDim(4) - 1, ks, ke, js, js, is, ie, - KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { - F.flux(X2DIR, p, k, j, i) = 0.; - } - ); + PackIndexMap cons_map; + auto &F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); + + // If we should check inflow on this face... + if (params.Get("check_inflow_flux_" + bname)) { + const int m_rho = cons_map["cons.rho"].first; + // ...and if this face of the block corresponds to a global boundary... + if (pmb->boundary_flag[bface] == BoundaryFlag::user) { + pmb->par_for( + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); + }); + } } - if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { - pmb->par_for("fix_flux_pole_r", 0, F.GetDim(4) - 1, ks, ke, je_l, je_l, is, ie, - KOKKOS_LAMBDA (const int &p, const int &k, const int &j, const int &i) { - F.flux(X2DIR, p, k, j, i) = 0.; - } - ); + // If we should zero flux through this face... + if (params.Get("zero_flux_" + bname)) { + // ...and if this face of the block corresponds to a global boundary... + if (pmb->boundary_flag[bface] == BoundaryFlag::user) { + pmb->par_for( + "zero_flux_" + bname, 0, F.GetDim(4) - 1, kb.s, kb.e, jb.s, jb.s, ib.s, ib.e, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + F.flux(X2DIR, p, k, j, i) = 0.; + }); + } } } } - Flag("Fixed fluxes"); return TaskStatus::complete; } \ No newline at end of file diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index 8994bc33..cf412551 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -35,6 +35,8 @@ #include "decs.hpp" +#include "boundary_types.hpp" +#include "dirichlet.hpp" #include "flux.hpp" #include "grmhd_functions.hpp" @@ -56,12 +58,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

U */ void ApplyBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse); // Template version to conform to Parthenon's calling convention. See above. @@ -69,24 +68,6 @@ template inline void ApplyBoundaryTemplate(std::shared_ptr> &rc, bool coarse) { ApplyBoundary(rc, domain, coarse); } -/** - * Boundary conditions when not overridden by a problem (or handled by Parthenon). - * Outflow boundaries in X1 with an optional check for inflow, Reflecting boundaries in X2 - */ -void DefaultBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse); - -/** - * Dirichlet boundaries implementation. - * Problems can assign these to the KHARMA*Boundary callbacks, then fill the "bound.*" - * fields populated as a part of the "Boundaries" package. - */ -void Dirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse); - -/** - * Set the current contents of a domain to be the Dirichlet boundary conditions. - */ -void SetDomainDirichlet(std::shared_ptr>& rc, IndexDomain domain, bool coarse); - /** * Fix fluxes on physical boundaries. * 1. Ensure no inflow of density onto the domain @@ -117,13 +98,6 @@ void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, b */ void FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse); -/** - * We apply Parthenon's boundary condition implementations, which are not GR-aware. - * When applied to the magnetic field values, the result must be scaled by the relative change - * in metric determinant. This function applies that change. - */ -void CorrectBField(std::shared_ptr>& rc, IndexDomain domain, bool coarse); - /** * Check for velocity toward the simulation domain in a zone, and eliminate it. */ diff --git a/kharma/boundaries/boundary_types.hpp b/kharma/boundaries/boundary_types.hpp new file mode 100644 index 00000000..3400774b --- /dev/null +++ b/kharma/boundaries/boundary_types.hpp @@ -0,0 +1,186 @@ +/* + * File: boundary_types.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include + +using namespace parthenon; + +namespace KBoundaries { + +inline bool BoundaryIsInner(const IndexDomain domain) +{ + return domain == IndexDomain::inner_x1 || + domain == IndexDomain::inner_x2 || + domain == IndexDomain::inner_x3; +} + +inline bool BoundaryIsInner(const BoundaryFace bface) +{ + return bface == BoundaryFace::inner_x1 || + bface == BoundaryFace::inner_x2 || + bface == BoundaryFace::inner_x3; +} + +inline int BoundaryDirection(const IndexDomain domain) +{ + switch (domain) { + case IndexDomain::inner_x1: + case IndexDomain::outer_x1: + return X1DIR; + case IndexDomain::inner_x2: + case IndexDomain::outer_x2: + return X2DIR; + case IndexDomain::inner_x3: + case IndexDomain::outer_x3: + return X3DIR; + default: + return 0; + } +} + +inline int BoundaryDirection(const BoundaryFace face) +{ + switch (face) { + case BoundaryFace::inner_x1: + case BoundaryFace::outer_x1: + return X1DIR; + case BoundaryFace::inner_x2: + case BoundaryFace::outer_x2: + return X2DIR; + case BoundaryFace::inner_x3: + case BoundaryFace::outer_x3: + return X3DIR; + default: + return 0; + } +} + +inline std::string BoundaryName(const BoundaryFace face) +{ + switch (face) { + case BoundaryFace::inner_x1: + return "inner_x1"; + case BoundaryFace::outer_x1: + return "outer_x1"; + case BoundaryFace::inner_x2: + return "inner_x2"; + case BoundaryFace::outer_x2: + return "outer_x2"; + case BoundaryFace::inner_x3: + return "inner_x3"; + case BoundaryFace::outer_x3: + return "outer_x3"; + default: + return "unknown"; + } +} + +inline std::string DomainName(const IndexDomain domain) +{ + switch (domain) { + case IndexDomain::inner_x1: + return "inner_x1"; + case IndexDomain::outer_x1: + return "outer_x1"; + case IndexDomain::inner_x2: + return "inner_x2"; + case IndexDomain::outer_x2: + return "outer_x2"; + case IndexDomain::inner_x3: + return "inner_x3"; + case IndexDomain::outer_x3: + return "outer_x3"; + case IndexDomain::interior: + return "interior"; + case IndexDomain::entire: + return "entire"; + default: + return "unknown"; + } +} + +inline IndexDomain BoundaryDomain(const BoundaryFace face) +{ + switch (face) { + case BoundaryFace::inner_x1: + return IndexDomain::inner_x1; + case BoundaryFace::outer_x1: + return IndexDomain::outer_x1; + case BoundaryFace::inner_x2: + return IndexDomain::inner_x2; + case BoundaryFace::outer_x2: + return IndexDomain::outer_x2; + case BoundaryFace::inner_x3: + return IndexDomain::inner_x3; + case BoundaryFace::outer_x3: + return IndexDomain::outer_x3; + case BoundaryFace::undef: + throw std::runtime_error("Undefined boundary face has no domain!"); + } +} + +inline BoundaryFace BoundaryFaceOf(const IndexDomain domain) +{ + switch (domain) { + case IndexDomain::inner_x1: + return BoundaryFace::inner_x1; + case IndexDomain::outer_x1: + return BoundaryFace::outer_x1; + case IndexDomain::inner_x2: + return BoundaryFace::inner_x2; + case IndexDomain::outer_x2: + return BoundaryFace::outer_x2; + case IndexDomain::inner_x3: + return BoundaryFace::inner_x3; + case IndexDomain::outer_x3: + return BoundaryFace::outer_x3; + case IndexDomain::interior: + case IndexDomain::entire: + return BoundaryFace::undef; + } +} + +/** + * Function for checking boundary flags: is this a domain or internal bound? + */ +inline bool IsPhysicalBoundary(std::shared_ptr pmb, const BoundaryFace face) +{ + return !(pmb->boundary_flag[face] == BoundaryFlag::block || + pmb->boundary_flag[face] == BoundaryFlag::periodic); +} + +} diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp new file mode 100644 index 00000000..30aa5ee4 --- /dev/null +++ b/kharma/boundaries/dirichlet.cpp @@ -0,0 +1,137 @@ +/* + * File: dirichlet.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dirichlet.hpp" + +#include + +using namespace parthenon; + +void KBoundaries::DirichletImpl(std::shared_ptr> &rc, BoundaryFace bface, bool coarse) +{ + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + using FC = Metadata::FlagCollection; + auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); + auto bound = rc->Get("bound." + BoundaryName(bface)).data; + + if (q.GetDim(4) != bound.GetDim(4)) + { + std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + } + + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; + const bool right = !BoundaryIsInner(bface); + + // Subtract off the starting index if we're on the right + const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundaryDirection(bface); + const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior) + 1 : 0; + const int je = (dir == 2) ? bounds.je(IndexDomain::interior) + 1 : 0; + const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior) + 1 : 0; + + const auto &G = pmb->coords; + + const auto domain = BoundaryDomain(bface); + pmb->par_for_bndry( + "dirichlet_boundary", vars, domain, coarse, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + if (right) { + q(p, k, j, i) = bound(p, k - ke, j - je, i - ie); + } else { + q(p, k, j, i) = bound(p, k, j, i); + } + }); +} + +void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) +{ + // For each face... + for (int i=0; i < BOUNDARY_NFACES; i++) { + BoundaryFace bface = (BoundaryFace) i; + auto bname = BoundaryName(bface); + auto pmesh = md->GetMeshPointer(); + // ...if this boundary is dirichlet... + if (pmesh->packages.Get("Boundaries")->Param(bname) == "dirichlet") { + // ...on all blocks... + for (int i=0; i < md->NumBlocks(); i++) { + auto rc = md->GetBlockData(i); + std::shared_ptr pmb = rc->GetBlockPointer(); + auto domain = BoundaryDomain(bface); + // Set whatever is in that domain as the Dirichlet bound + SetDomainDirichlet(rc, domain, false); + } + } + } +} + +void KBoundaries::SetDomainDirichlet(std::shared_ptr> &rc, IndexDomain domain, bool coarse) +{ + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const BoundaryFace bface = BoundaryFaceOf(domain); + + using FC = Metadata::FlagCollection; + auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); + auto bound = rc->Get("bound." + BoundaryName(bface)).data; + + // TODO error? + if (q.GetDim(4) != bound.GetDim(4)) { + std::cerr << "Dirichlet boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + } + + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; + const bool right = !BoundaryIsInner(domain); + + // Subtract off the starting index if we're on the right + const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundaryDirection(bface); + const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior) + 1 : 0; + const int je = (dir == 2) ? bounds.je(IndexDomain::interior) + 1 : 0; + const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior) + 1 : 0; + + const auto &G = pmb->coords; + + pmb->par_for_bndry( + "dirichlet_boundary", vars, domain, coarse, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + if (right) { + bound(p, k - ke, j - je, i - ie) = q(p, k, j, i); + } else { + bound(p, k, j, i) = q(p, k, j, i); + } + } + ); +} \ No newline at end of file diff --git a/kharma/boundaries/dirichlet.hpp b/kharma/boundaries/dirichlet.hpp new file mode 100644 index 00000000..3cc7d903 --- /dev/null +++ b/kharma/boundaries/dirichlet.hpp @@ -0,0 +1,55 @@ +/* + * File: dirichlet.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "boundary_types.hpp" + +namespace KBoundaries { + +void DirichletImpl(std::shared_ptr> &rc, BoundaryFace bface, bool coarse); + +template +void Dirichlet(std::shared_ptr> &rc, bool coarse) +{ + DirichletImpl(rc, bface, coarse); +} + +/** + * Freeze any dirichlet boundary conditions in their current forms. + */ +void FreezeDirichlet(std::shared_ptr> &md); + +void SetDomainDirichlet(std::shared_ptr> &rc, IndexDomain domain, bool coarse); + +} diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index 465f4b17..935d75a3 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -200,8 +200,27 @@ class CoordinateEmbedding { EmplaceSystems(src.base, src.transform); return *this; } + // Convenience functions to get common things: + // Names (host only) +#pragma hd_warning_disable + KOKKOS_INLINE_FUNCTION std::string variant_names() const + { + std::string basename( + mpark::visit( [&](const auto& self) { + return self.name; + }, base) + ); + + std::string transformname( + mpark::visit( [&](const auto& self) { + return self.name; + }, transform) + ); - // Convenience functions to get common things + return basename + " " + transformname; + } + + // Properties (host or device) KOKKOS_INLINE_FUNCTION bool is_spherical() const { return mpark::visit( [&](const auto& self) { @@ -211,7 +230,9 @@ class CoordinateEmbedding { KOKKOS_INLINE_FUNCTION GReal get_horizon() const { if (mpark::holds_alternative(base) || - mpark::holds_alternative(base)) { + mpark::holds_alternative(base) || + mpark::holds_alternative(base) || + mpark::holds_alternative(base)) { const GReal a = get_a(); return 1 + m::sqrt(1 - a * a); } else { @@ -246,23 +267,6 @@ class CoordinateEmbedding { return mpark::holds_alternative(base) && mpark::holds_alternative(transform); } - KOKKOS_INLINE_FUNCTION std::string variant_names() const - { - std::string basename( - mpark::visit( [&](const auto& self) { - return self.name; - }, base) - ); - - std::string transformname( - mpark::visit( [&](const auto& self) { - return self.name; - }, transform) - ); - - return basename + " " + transformname; - } - // Spell out the interface we take from BaseCoords // TODO add a gcon_embed, gdet_embed KOKKOS_INLINE_FUNCTION void gcov_embed(const GReal Xembed[GR_DIM], Real gcov[GR_DIM][GR_DIM]) const diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 991b9067..615d5009 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -64,12 +64,12 @@ namespace m = std; // Bare Parthenon defs // Anything more leads to circular deps from gr_coordinates.hpp -#include "parameter_input.hpp" -#include "parthenon_arrays.hpp" -#include "parthenon_mpi.hpp" -#include "globals.hpp" -#include "bvals/bvals_interfaces.hpp" -#include "mesh/domain.hpp" +#include +#include +#include +#include +#include +#include // KHARMA DEFINITIONS diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 35652ad7..db731188 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -285,7 +285,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // B Field cleanup: this is a separate solve so it's split out // It's also really slow when enabled so we don't care too much about limiting regions, etc. - if (use_b_cleanup && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { + if (use_b_cleanup && (stage == integrator->nstages) && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { TaskRegion &cleanup_region = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { auto &tl = cleanup_region[i]; diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 45b864c1..6f9608dc 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -41,7 +41,6 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing KHARMA Driver"); // This function builds and returns a "KHARMAPackage" object, which is a light // superset of Parthenon's "StateDescriptor" class for packages. // The most important part of this object is a member of type "Params", @@ -80,14 +79,20 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // Reconstruction scheme: plm, weno5, ppm... // Allow an old parameter location - std::string recon = pin->GetOrAddString("driver", "reconstruction", - pin->GetOrAddString("GRMHD", "reconstruction", "weno5")); + std::string grmhd_recon_option = pin->GetOrAddString("GRMHD", "reconstruction", "weno5"); + std::string recon = pin->GetOrAddString("driver", "reconstruction", grmhd_recon_option); + bool lower_edges = pin->GetOrAddBoolean("driver", "lower_edges", false); + bool lower_poles = pin->GetOrAddBoolean("driver", "lower_poles", false); if (recon == "donor_cell") { params.Add("recon", KReconstruction::Type::donor_cell); } else if (recon == "linear_vl") { params.Add("recon", KReconstruction::Type::linear_vl); } else if (recon == "linear_mc") { params.Add("recon", KReconstruction::Type::linear_mc); + } else if (recon == "weno5_lower_edges" || (recon == "weno5" && lower_edges)) { + params.Add("recon", KReconstruction::Type::weno5_lower_edges); + } else if (recon == "weno5_lower_poles" || (recon == "weno5" && lower_poles)) { + params.Add("recon", KReconstruction::Type::weno5_lower_poles); } else if (recon == "weno5") { params.Add("recon", KReconstruction::Type::weno5); } else { @@ -148,7 +153,8 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: for (int i_bnd = 0; i_bnd < BOUNDARY_NFACES; i_bnd++) { if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { - t_all_ptou[i_task] = tl.AddTask(t_start, Flux::BlockPtoU_Send, rc.get(), BoundaryDomain((BoundaryFace) i_bnd), false); + const auto bdomain = KBoundaries::BoundaryDomain((BoundaryFace) i_bnd); + t_all_ptou[i_task] = tl.AddTask(t_start, Flux::BlockPtoU_Send, rc.get(), bdomain, false); t_ptou_final = t_ptou_final | t_all_ptou[i_task]; i_task++; } @@ -173,7 +179,8 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: for (int i_bnd = 0; i_bnd < BOUNDARY_NFACES; i_bnd++) { if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { - t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BlockUtoPExceptMHD, rc.get(), BoundaryDomain((BoundaryFace) i_bnd), false); + const auto bdomain = KBoundaries::BoundaryDomain((BoundaryFace) i_bnd); + t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BlockUtoPExceptMHD, rc.get(), bdomain, false); t_utop_final = t_utop_final | t_all_utop[i_task]; i_task++; } @@ -242,9 +249,18 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); break; + case RType::weno5_lower_edges: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; + case RType::weno5_lower_poles: + t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + break; case RType::ppm: case RType::mp5: - case RType::weno5_lower_poles: std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 6423a2ba..30a1b398 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -261,7 +261,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // B Field cleanup: this is a separate solve so it's split out // It's also really slow when enabled so we don't care too much about limiting regions, etc. - if (use_b_cleanup && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { + if (use_b_cleanup && (stage == integrator->nstages) && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { TaskRegion &cleanup_region = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { auto &tl = cleanup_region[i]; diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index 3eb294db..630dd527 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -63,8 +63,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddReal("electrons", "gamma_p", 5./3); params.Add("gamma_p", gamma_p); - bool diss_sign = pin->GetOrAddBoolean("electrons", "diss_sign", true); - params.Add("diss_sign", diss_sign); + bool enforce_positive_dissipation = pin->GetOrAddBoolean("electrons", "enforce_positive_dissipation", true); + params.Add("enforce_positive_dissipation", enforce_positive_dissipation); bool kel_lim = pin->GetOrAddBoolean("electrons", "kel_lim", true); params.Add("kel_lim", kel_lim); // This is used only in constant model @@ -331,6 +331,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // Floors const Real tptemin = pmb->packages.Get("Electrons")->Param("tp_over_te_min"); const Real tptemax = pmb->packages.Get("Electrons")->Param("tp_over_te_max"); + const bool enforce_positive_diss = pmb->packages.Get("Electrons")->Param("enforce_positive_dissipation"); // This function (and any primitive-variable sources) needs to be run over the entire domain, // because the boundary zones have already been updated and so the same calculations must be applied @@ -358,7 +359,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // Default is True diss_sign == Enforce nonnegative // Due to floors we can end up with diss==0 or even *slightly* <0, so we require it to be positive here - const Real diss = pmb->packages.Get("Electrons")->Param("diss_sign") ? m::max(diss_tmp, 0.0) : diss_tmp; + const Real diss = enforce_positive_diss ? m::max(diss_tmp, 0.0) : diss_tmp; // Reset the entropy to measure next (sub-)step's dissipation P_new(m_p.KTOT, k, j, i) = k_energy_conserving; diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index aff752bd..3c6d3e0d 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -48,7 +48,6 @@ int CountFFlags(MeshData *md) std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing Floors"); auto pkg = std::make_shared("Floors"); Params ¶ms = pkg->AllParams(); @@ -153,15 +152,14 @@ std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shar // add callbacks for HST output to the Params struct, identified by the `hist_param_key` pkg->AddParam<>(parthenon::hist_param_key, hst_vars); - Flag("Initialized"); return pkg; } -TaskStatus Floors::ApplyInitialFloors(MeshBlockData *mbd, IndexDomain domain) +TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData *mbd, IndexDomain domain) { Flag(mbd, "Applying first floors"); - auto pmb = mbd->GetBlockPointer(); + auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); @@ -174,17 +172,16 @@ TaskStatus Floors::ApplyInitialFloors(MeshBlockData *mbd, IndexDomain doma // If we're going to apply floors through the run, apply the same ones at init - // Otherwise pick sensible defaults + // Otherwise stick to specified/default geometric floors Floors::Prescription floors_tmp; if (pmb->packages.AllPackages().count("Floors")) { floors_tmp = Floors::Prescription(pmb->packages.Get("Floors")->AllParams()); } else { // JUST rho & u geometric - floors_tmp.rho_min_geom = 1e-6; - floors_tmp.u_min_geom = 1e-8; - floors_tmp.r_char = 10.; //unused - floors_tmp.frame_switch = 50.; //unused + floors_tmp.rho_min_geom = pin->GetOrAddReal("floors", "rho_min_geom", 1e-6); + floors_tmp.u_min_geom = pin->GetOrAddReal("floors", "u_min_geom", 1e-8); + // Disable everything else, even if it's specified floors_tmp.bsq_over_rho_max = 1e20; floors_tmp.bsq_over_u_max = 1e20; floors_tmp.u_over_rho_max = 1e20; @@ -192,11 +189,13 @@ TaskStatus Floors::ApplyInitialFloors(MeshBlockData *mbd, IndexDomain doma floors_tmp.gamma_max = 1e20; floors_tmp.use_r_char = false; + floors_tmp.r_char = 0.; //unused floors_tmp.temp_adjust_u = false; floors_tmp.adjust_k = false; floors_tmp.fluid_frame = true; floors_tmp.mixed_frame = false; + floors_tmp.frame_switch = 0.; //unused floors_tmp.drift_frame = false; } const Floors::Prescription floors = floors_tmp; diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index b6d88b34..272926f3 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -151,8 +151,7 @@ TaskStatus ApplyGRMHDFloors(MeshBlockData *rc, IndexDomain domain); * "whatever the floor value is." * This function can be called even if the Floors package is not initialized. */ -TaskStatus ApplyInitialFloors(MeshBlockData *rc, IndexDomain domain); - +TaskStatus ApplyInitialFloors(ParameterInput *pin, MeshBlockData *mbd, IndexDomain domain); /** * Print a summary of floors hit */ diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index ac3ebc1e..bc8c972b 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -59,7 +59,6 @@ namespace GRMHD std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing GRMHD"); // This function builds and returns a "KHARMAPackage" object, which is a light // superset of Parthenon's "StateDescriptor" class for packages. // The most important part of this object is a member of type "Params", @@ -214,7 +213,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Implicit::GetOrderedNames(MeshBlockData *rc, cons { auto pmb0 = rc->GetBlockPointer(); std::vector out; - auto vars = rc->GetVariablesByFlag(std::vector({Metadata::GetUserFlag("Implicit"), flag})).labels(); + auto vars = rc->GetVariablesByFlag({Metadata::GetUserFlag("Implicit"), flag}).vars(); for (int i=0; i < vars.size(); ++i) { - if (rc->Contains(vars[i])) { - out.push_back(vars[i]); + if (rc->Contains(vars[i]->label())) { + out.push_back(vars[i]->label()); } } if (!only_implicit) { - vars = rc->GetVariablesByFlag(std::vector({Metadata::GetUserFlag("Explicit"), flag})).labels(); + vars = rc->GetVariablesByFlag({Metadata::GetUserFlag("Explicit"), flag}).vars(); for (int i=0; i < vars.size(); ++i) { - if (rc->Contains(vars[i])) { - out.push_back(vars[i]); + if (rc->Contains(vars[i]->label())) { + out.push_back(vars[i]->label()); } } } @@ -82,7 +82,6 @@ std::vector Implicit::GetOrderedNames(MeshBlockData *rc, cons std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing Implicit Package"); auto pkg = std::make_shared("Implicit"); Params ¶ms = pkg->AllParams(); @@ -145,7 +144,6 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh // Anything we need to run from this package on callbacks // Maybe a post-step L2 or flag count or similar - Flag("Initialized"); return pkg; } @@ -156,6 +154,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ Flag(md_sub_step_init, "Implicit Iteration start, sub step"); Flag(md_flux_src, "Implicit Iteration start, divF and sources"); Flag(md_linesearch, "Linesearch"); + // Pull out the block pointers for each sub-step, as we need the *mutable parameters* + // of the EMHD package. TODO(BSP) restrict state back to the variables... auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_sub_step_init = md_sub_step_init->GetBlockData(0)->GetBlockPointer(); auto pmb_solver = md_solver->GetBlockData(0)->GetBlockPointer(); @@ -199,8 +199,9 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // The implicit variables need to be first, so we know how to iterate over just them to fill // just the residual & Jacobian we care about, which makes the solve faster. auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); - auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::Conserved); + + auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); + auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 76ce1494..80e63748 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -60,7 +60,6 @@ std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, std::shared_ptr& packages) { - Flag("Initializing Globals"); // All truly global state. Mostly mutable state in order to avoid scope creep auto pkg = std::make_shared("Globals"); Params ¶ms = pkg->AllParams(); @@ -92,7 +91,6 @@ std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, st pkg->MeshPreStepUserWorkInLoop = KHARMA::MeshPreStepUserWorkInLoop; pkg->MeshPostStepUserWorkInLoop = KHARMA::MeshPostStepUserWorkInLoop; - Flag("Initialized"); return pkg; } void KHARMA::ResetGlobals(ParameterInput *pin, Mesh *pmesh) @@ -163,6 +161,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) pin->SetBoolean("coordinates", "spherical", tmp_coords.is_spherical()); // Do a bunch of autodetection/setting in spherical coordinates + // Note frequent use of "GetOrAddX": this sets a default if not present but allows overriding if (tmp_coords.is_spherical()) { // Spherical systems can specify r_out and optionally r_in, // instead of xNmin/max. @@ -184,7 +183,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) GReal Rin = pin->GetReal("coordinates", "r_in"); GReal x1min = tmp_coords.r_to_native(Rin); pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); - if (Rin < 2.5){ // warn if there are fewer than 5 zones inside the event horizon + if (Rin < 2.0){ // warn if there are fewer than 5 zones inside the event horizon GReal dx = (x1max - x1min) / pin->GetInteger("parthenon/mesh", "nx1"); if (tmp_coords.X1_to_embed(x1min + 5*dx) > tmp_coords.get_horizon()) { std::cerr << "WARNING: inner radius is near/in the EH, but does not allow 5 zones inside!" << std::endl; @@ -206,40 +205,39 @@ void KHARMA::FixParameters(std::unique_ptr& pin) pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(Rhor)); } - - //cout << "Setting x1min: " << x1min << " x1max " << x1max << " based on BH with a=" << a << endl; - } } + // If the simulation domain extends inside the EH, we change some boundary options + pin->SetBoolean("coordinates", "domain_intersects_eh", pin->GetReal("coordinates", "r_in") < tmp_coords.get_horizon()); + // Spherical systems will also want KHARMA's spherical boundary conditions. - // By default, this means inflow in x1 and reflecting in x2, but can be chosen - // by *KHARMA* options (not here, since we certainly don't want periodic pole/radial bounds) - pin->GetOrAddString("parthenon/mesh", "ix1_bc", "user"); - pin->GetOrAddString("parthenon/mesh", "ox1_bc", "user"); - pin->GetOrAddString("parthenon/mesh", "ix2_bc", "user"); - pin->GetOrAddString("parthenon/mesh", "ox2_bc", "user"); - pin->GetOrAddString("parthenon/mesh", "ix3_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ox3_bc", "periodic"); + // Note boundaries are now exclusively set by KBoundaries package + pin->GetOrAddString("boundaries", "inner_x1", "outflow"); + pin->GetOrAddString("boundaries", "outer_x1", "outflow"); + pin->GetOrAddString("boundaries", "inner_x2", "reflecting"); + pin->GetOrAddString("boundaries", "outer_x2", "reflecting"); + pin->GetOrAddString("boundaries", "inner_x3", "periodic"); + pin->GetOrAddString("boundaries", "outer_x3", "periodic"); } else { // We can set reasonable default boundary conditions for Cartesian sims, // but not default domain bounds - pin->GetOrAddString("parthenon/mesh", "ix1_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ox1_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ix2_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ox2_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ix3_bc", "periodic"); - pin->GetOrAddString("parthenon/mesh", "ox3_bc", "periodic"); + pin->GetOrAddString("boundaries", "inner_x1", "periodic"); + pin->GetOrAddString("boundaries", "outer_x1", "periodic"); + pin->GetOrAddString("boundaries", "inner_x2", "periodic"); + pin->GetOrAddString("boundaries", "outer_x2", "periodic"); + pin->GetOrAddString("boundaries", "inner_x3", "periodic"); + pin->GetOrAddString("boundaries", "outer_x3", "periodic"); } - // Set default bounds covering our coordinates/transform - std::cout << "Coordinate transform has boundaries: " - << tmp_coords.startx(1) << " " - << tmp_coords.startx(2) << " " - << tmp_coords.startx(3) << " to " - << tmp_coords.stopx(1) << " " - << tmp_coords.stopx(2) << " " - << tmp_coords.stopx(3) << std::endl; + // Default boundaries are to cover the domain of our native coordinate system + // std::cout << "Coordinate transform has boundaries: " + // << tmp_coords.startx(1) << " " + // << tmp_coords.startx(2) << " " + // << tmp_coords.startx(3) << " to " + // << tmp_coords.stopx(1) << " " + // << tmp_coords.stopx(2) << " " + // << tmp_coords.stopx(3) << std::endl; // TODO(BSP) is this worth looping? I say probably no. if (tmp_coords.startx(1) >= 0) pin->GetOrAddReal("parthenon/mesh", "x1min", tmp_coords.startx(1)); @@ -261,21 +259,24 @@ TaskStatus KHARMA::AddPackage(std::shared_ptr& packages, std::function(ParameterInput*, std::shared_ptr&)> package_init, ParameterInput *pin) { - packages->Add(package_init(pin, packages)); + Flag("AddPackage"); + const auto& pkg = package_init(pin, packages); + packages->Add(pkg); + EndFlag("AddPackage "+pkg->label()); return TaskStatus::complete; } Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) { - // See above + // See above. Only run if + //if () FixParameters(pin); - Flag("Initializing packages"); + Flag("ProcessPackages"); // Allocate the packages list as a shared pointer, to be updated in various tasks auto packages = std::make_shared(); - Flag("Building task collection"); TaskCollection tc; auto& tr = tc.AddRegion(1); auto& tl = tr[0]; @@ -341,7 +342,6 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) } // Execute the whole collection (just in case we do something fancy?) - Flag("Running package loading tasks"); while (!tr.Execute()); // TODO this will inf-loop on error // The boundaries package may need to know variable counts for allocating memory, @@ -358,8 +358,6 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) pin->SetString("parthenon/time", "integrator", "vl2"); } - - - Flag("Finished initializing all packages"); // TODO print full package list way up here? + EndFlag("ProcessPackages"); // TODO print full package list way up here? return std::move(*packages); } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 587173b2..88592fe7 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -42,44 +42,26 @@ TaskStatus Packages::FixFlux(MeshData *md) { - Flag("Fixing fluxes on mesh"); - for (auto &package : md->GetMeshPointer()->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->FixFlux != nullptr) - kpackage->FixFlux(md); + Flag("FixFlux"); + auto kpackages = md->GetMeshPointer()->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->FixFlux != nullptr) { + Flag("FixFlux_"+kpackage.first); + kpackage.second->FixFlux(md); + EndFlag("FixFlux_"+kpackage.first); } } - Flag("Fixed"); + EndFlag("FixFlux"); return TaskStatus::complete; } -// TaskStatus Packages::BlockPtoU(MeshBlockData *mbd, IndexDomain domain, bool coarse) -// { -// Flag("Getting conserved variables on block"); -// for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { -// if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { -// if (kpackage->BlockPtoU != nullptr) -// kpackage->BlockPtoU(mbd, domain, coarse); -// } -// } -// Flag("Done"); -// return TaskStatus::complete; -// } -// TaskStatus Packages::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) -// { -// for (int i=0; i < md->NumBlocks(); ++i) -// PtoU(md->GetBlockData(i).get(), domain, coarse); -// return TaskStatus::complete; -// } - -TaskStatus Packages::BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse) +TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag("Recovering primitive variables"); - for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->BlockUtoP != nullptr) - kpackage->BlockUtoP(mbd, domain, coarse); - } + auto kpackages = rc->GetBlockPointer()->packages.ListPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage->BlockUtoP != nullptr) + kpackage->BlockUtoP(rc, domain, coarse); } Flag("Recovered"); return TaskStatus::complete; diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index 9748afad..1d55e585 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -55,14 +55,13 @@ class KHARMAPackage : public StateDescriptor { // PHYSICS // Recovery of primitive variables from conserved. - // These can be host-side functions because they are not called from the Uberkernel -- + // These can be host-side functions because they are not called from GetFlux() // rather, they are called on zone center values once per step only. - // Called by various Flux::*UtoP* std::function*, IndexDomain, bool)> BlockUtoP = nullptr; std::function*, IndexDomain, bool)> MeshUtoP = nullptr; - // Maybe at some point we'll have - // Since Flux::prim_to_flux must cover everything, it's not worth splitting now + // Going the other way, however, is handled by Flux::PtoU. + // All PtoU implementations are device-side (called prim_to_flux) //std::function*, IndexDomain, bool)> BlockPtoU = nullptr; // Source term to add to the conserved variables during each step @@ -93,12 +92,9 @@ class KHARMAPackage : public StateDescriptor { std::function BlockUserWorkBeforeOutput = nullptr; // BOUNDARIES - // Currently only used by the "boundaries" package, or overridden during problem initialization - // Note these functions take the boundary domain as an argument, so you can assign the same function to multiple boundaries. - std::function>&, IndexDomain, bool)> KHARMAInnerX1Boundary = nullptr; - std::function>&, IndexDomain, bool)> KHARMAOuterX1Boundary = nullptr; - std::function>&, IndexDomain, bool)> KHARMAInnerX2Boundary = nullptr; - std::function>&, IndexDomain, bool)> KHARMAOuterX2Boundary = nullptr; + // Currently only used by the "boundaries" package + // Note these functions take the boundary IndexDomain as an argument, so you can assign the same function to multiple boundaries. + std::array>&, bool)>, 6> KBoundaries = {nullptr}; }; /** diff --git a/kharma/main.cpp b/kharma/main.cpp index 37d5d48b..000f59ab 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -108,16 +108,18 @@ int main(int argc, char *argv[]) pman.app_input->PostStepDiagnosticsInLoop = Packages::PostStepDiagnostics; // Registering KHARMA's boundary functions here doesn't mean they will *always* run: - // all periodic & internal boundary conditions are handled by Parthenon. - // KHARMA sets the correct boundaries automatically for spherical coordinate systems. + // periodic & internal boundary conditions are handled by Parthenon. + // KHARMA sets what will run in boundaries.cpp pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x1] = KBoundaries::ApplyBoundaryTemplate; pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x1] = KBoundaries::ApplyBoundaryTemplate; pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x2] = KBoundaries::ApplyBoundaryTemplate; pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x2] = KBoundaries::ApplyBoundaryTemplate; + pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x3] = KBoundaries::ApplyBoundaryTemplate; + pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x3] = KBoundaries::ApplyBoundaryTemplate; // Parthenon init includes Kokkos, MPI, parses parameters & cmdline, // then calls ProcessPackages and ProcessProperties, then constructs the Mesh - Flag("Parthenon Initializing"); + Flag("Parthenon Init"); auto manager_status = pman.ParthenonInit(argc, argv); if (manager_status == ParthenonStatus::complete) { pman.ParthenonFinalize(); @@ -127,7 +129,7 @@ int main(int argc, char *argv[]) pman.ParthenonFinalize(); return 1; } - Flag("Parthenon Initialized"); + EndFlag("Parthenon Init"); #if DEBUG // Replace Parthenon signal handlers with something that just prints a backtrace diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 513314a8..ca357452 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -43,7 +43,6 @@ */ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing Bondi problem"); auto pmb = rc->GetBlockPointer(); const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); @@ -80,38 +79,32 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - if (pin->GetOrAddBoolean("bondi", "use_dirichlet", false)) { - SetBondi(rc, IndexDomain::entire); - // Register a Dirichlet boundary condition - bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; - bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; - // Fill the Dirichlet caches based on the current ghost zone contents - KBoundaries::SetDomainDirichlet(rc, IndexDomain::inner_x1, false); - KBoundaries::SetDomainDirichlet(rc, IndexDomain::outer_x1, false); + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries")); + if (pin->GetString("boundaries", "inner_x1") == "dirichlet" || + pin->GetString("boundaries", "outer_x1") == "dirichlet") { + SetBondi(rc); // TODO iterate & set any bounds specifically? } else { if (pin->GetOrAddBoolean("bondi", "set_outer_bound", true)) { - bound_pkg->KHARMAOuterX1Boundary = SetBondi; + bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetBondi; } if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { - bound_pkg->KHARMAInnerX1Boundary = SetBondi; + bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetBondi; } // Set the interior domain to the analytic solution to begin // This tests that PostInitialize will correctly fill ghost zones with the boundary we set - SetBondi(rc, IndexDomain::interior); + SetBondi(rc); } if (rin_bondi > pin->GetReal("coordinates", "r_in") && !(fill_interior)) { // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable. - Floors::ApplyInitialFloors(rc.get(), IndexDomain::interior); + Floors::ApplyInitialFloors(pin, rc.get(), IndexDomain::interior); } - Flag(rc, "Initialized"); return TaskStatus::complete; } -TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { Flag(rc, "Setting Bondi zones"); auto pmb = rc->GetBlockPointer(); diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index fc1732d5..4b9d03b0 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -50,11 +50,15 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterInput *pin); /** - * Set all values on a given domain to the Bondi inflow analytic steady-state solution - * - * Used for initialization and boundary conditions + * Set all values on a given domain to the Bondi inflow analytic steady-state solution. + * Use the template version when possible, which just calls through */ -TaskStatus SetBondi(std::shared_ptr>& rc, IndexDomain domain, bool coarse=false); +TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain domain, bool coarse); + +template +TaskStatus SetBondi(std::shared_ptr>& rc, bool coarse=false) { + SetBondiImpl(rc, domain, coarse); +} /** * Supporting functions for Bondi flow calculations diff --git a/kharma/prob/bz_monopole.cpp b/kharma/prob/bz_monopole.cpp index 8d737866..c5c4ee0e 100644 --- a/kharma/prob/bz_monopole.cpp +++ b/kharma/prob/bz_monopole.cpp @@ -42,8 +42,6 @@ TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing BZ monopole problem"); - auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; @@ -62,10 +60,6 @@ TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, Parame const auto& G = pmb->coords; const GReal a = G.coords.get_a(); - if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { - std::cout << "Initializing BZ monopole." << std::endl; - } - pmb->par_for("fm_torus_init", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal Xembed[GR_DIM]; @@ -86,7 +80,6 @@ TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, Parame } ); - Flag(rc, "Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/elec/driven_turbulence.hpp b/kharma/prob/elec/driven_turbulence.hpp index 1d86f2f9..5257857e 100644 --- a/kharma/prob/elec/driven_turbulence.hpp +++ b/kharma/prob/elec/driven_turbulence.hpp @@ -43,7 +43,6 @@ using namespace parthenon; TaskStatus InitializeDrivenTurbulence(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing Driven Turbulence problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; diff --git a/kharma/prob/elec/hubble.cpp b/kharma/prob/elec/hubble.cpp index 3d75e7a5..6c86b87a 100644 --- a/kharma/prob/elec/hubble.cpp +++ b/kharma/prob/elec/hubble.cpp @@ -38,7 +38,6 @@ TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterInput *pin) { - Flag("Initializing Hubble Flow Electron Heating problem"); auto pmb = rc->GetBlockPointer(); const Real mach = pin->GetOrAddReal("hubble", "mach", 1.); @@ -74,21 +73,19 @@ TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterI } // Replace the boundary conditions - auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - bound_pkg->KHARMAInnerX1Boundary = SetHubble; - bound_pkg->KHARMAOuterX1Boundary = SetHubble; + auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries")); + bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetHubble; + bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetHubble; bound_pkg->BlockApplyPrimSource = ApplyHubbleHeating; // Then call the general function to fill the grid - SetHubble(rc, IndexDomain::interior); + SetHubble(rc); - Flag("Initialized"); return TaskStatus::complete; } -TaskStatus SetHubble(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +TaskStatus SetHubbleImpl(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { - Flag("Setting zones to Hubble Flow"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; @@ -179,7 +176,6 @@ TaskStatus SetHubble(std::shared_ptr>& rc, IndexDomain domai } } pmb->packages.Get("GRMHD")->UpdateParam("counter", ++counter); - Flag("Set"); return TaskStatus::complete; } diff --git a/kharma/prob/elec/hubble.hpp b/kharma/prob/elec/hubble.hpp index 0bf84d84..05132934 100644 --- a/kharma/prob/elec/hubble.hpp +++ b/kharma/prob/elec/hubble.hpp @@ -50,11 +50,17 @@ using namespace parthenon; TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterInput *pin); /** - * Set all values on a given domain to the Hubble flow solution - * - * Used for initialization and boundary conditions + * Set all values on a given domain to the Hubble flow solution, + * for both initialization and boundary conditions. + * Use template version when posible, which just calls through to "impl" implementation */ -TaskStatus SetHubble(std::shared_ptr>& rc,IndexDomain domain, bool coarse=false); +TaskStatus SetHubbleImpl(std::shared_ptr>& rc, IndexDomain domain, bool coarse); + +template +TaskStatus SetHubble(std::shared_ptr>& rc, bool coarse=false) +{ + SetHubbleImpl(rc, domain, coarse); +} /** * Apply the source term. Registered as ApplyPrimSource to run at end of step, once per step operator-split diff --git a/kharma/prob/elec/noh.hpp b/kharma/prob/elec/noh.hpp index 511adaab..7c0b3997 100644 --- a/kharma/prob/elec/noh.hpp +++ b/kharma/prob/elec/noh.hpp @@ -42,7 +42,6 @@ using namespace parthenon; */ TaskStatus InitializeNoh(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing 1D (Noh) Shock test"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; @@ -100,6 +99,5 @@ TaskStatus InitializeNoh(std::shared_ptr>& rc, ParameterInpu ); } - Flag(rc, "Initialized 1D (Noh) Shock test"); return TaskStatus::complete; } diff --git a/kharma/prob/emhd/anisotropic_conduction.hpp b/kharma/prob/emhd/anisotropic_conduction.hpp index ce83ddc7..b467efce 100644 --- a/kharma/prob/emhd/anisotropic_conduction.hpp +++ b/kharma/prob/emhd/anisotropic_conduction.hpp @@ -44,7 +44,6 @@ using namespace parthenon; */ TaskStatus InitializeAnisotropicConduction(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing EMHD Modes problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 95c3763a..9ad879ae 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -50,7 +50,6 @@ using namespace parthenon; */ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, ParameterInput *pin) { - auto pmb = rc->GetBlockPointer(); // Obtain EMHD params @@ -58,13 +57,10 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame bool higher_order_terms = false; EMHD::EMHD_parameters emhd_params_tmp; if (use_emhd) { - Flag(rc, "Initializing hydrostatic conducting atmosphere problem"); - + std::cout << "Hydrostatic atmosphere will be conducting w/EMHD" << std::endl; const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); emhd_params_tmp = emhd_pars.Get("emhd_params"); higher_order_terms = emhd_params_tmp.higher_order_terms; - } else { - Flag(rc, "Initializing hydrostatic atmosphere problem"); } const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; @@ -77,8 +73,6 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame auto P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); VarMap m_p(prims_map, false); - const int nvar = P.GetDim(4); - const auto& G = pmb->coords; // Type of input to the problem @@ -137,31 +131,15 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame dP_host = dP.GetHostMirror(); } - // Set dirichlet boundary conditions - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; - bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; - // Define ParArrays to store radial boundary values - // TODO could probably standardize index use a bit here - IndexRange ib_in = pmb->cellbounds.GetBoundsI(IndexDomain::interior); - IndexRange jb_in = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); - IndexRange kb_in = pmb->cellbounds.GetBoundsK(IndexDomain::interior); - const int n1 = pmb->cellbounds.ncellsi(IndexDomain::interior); - const int ng = ib.e - ib_in.e; - - // auto p_bound_left = rc->Get("bound.inner_x1").data; - // auto p_bound_left_host = p_bound_left.GetHostMirror(); - // auto p_bound_right = rc->Get("bound.outer_x1").data; - // auto p_bound_right_host = p_bound_right.GetHostMirror(); - // Load coordinates 'r' and compare against grid values - double rCoords[n1 + 2*ng]; + const int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); + double rCoords[n1]; double error = 0.; + IndexRange jb_in = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); for (int i = ib.s; i <= ib.e; i++) { fscanf(fp_r, "%lf", &(rCoords[i])); - GReal Xnative[GR_DIM], Xembed[GR_DIM]; - G.coord(0, ng, i, Loci::center, Xnative); // j and k don't matter since we need to compare only the radial coordinate - G.coord_embed(0, ng, i, Loci::center, Xembed); + GReal Xembed[GR_DIM]; + G.coord_embed(0, jb_in.s, i, Loci::center, Xembed); error = fabs(Xembed[1] - rCoords[i]); if (error > 1.e-10) { fprintf(stdout, "Error at radial zone i = %d, Error = %8.5e KHARMA: %8.7e, sage nb: %8.7e\n", i, error, Xembed[1], rCoords[i]); @@ -173,14 +151,13 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame double rho_temp, u_temp, q_temp; for (int i = ib.s; i <= ib.e; i++) { - fscanf(fp_rho, "%lf", &(rho_temp)); fscanf(fp_u, "%lf", &(u_temp)); if (use_emhd) fscanf(fp_q, "%lf", &(q_temp)); - for (int j = jb_in.s; j <= jb_in.e; j++) { - for (int k = kb_in.s; k <= kb_in.e; k++) { + for (int j = jb.s; j <= jb.e; j++) { + for (int k = kb.s; k <= kb.e; k++) { GReal Xnative[GR_DIM], Xembed[GR_DIM]; G.coord(k, j, i, Loci::center, Xnative); @@ -206,7 +183,6 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame // For a fluid at rest wrt. the normal observer, ucon = {-1/g_tt,0,0,0}. // We need to use this info to obtain the correct values for U1, U2 and U3 // TODO is this just fourvel_to_prim? - Real ucon[GR_DIM] = {0}; Real gcov[GR_DIM][GR_DIM] = {0}; @@ -250,36 +226,6 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame q_host(k, j, i) = q_tilde; dP_host(k, j, i) = dP_tilde; } - - // Save boundary values for Dirichlet boundary conditions - // if (i < ng) { - // p_bound_left_host(m_p.RHO, k, j, i) = rho_host(k, j, i); - // p_bound_left_host(m_p.UU, k, j, i) = u_host(k, j, i); - // p_bound_left_host(m_p.U1, k, j, i) = uvec_host(V1, k, j, i); - // p_bound_left_host(m_p.U2, k, j, i) = uvec_host(V2, k, j, i); - // p_bound_left_host(m_p.U3, k, j, i) = uvec_host(V3, k, j, i); - // p_bound_left_host(m_p.B1, k, j, i) = B_host(V1, k, j, i); - // p_bound_left_host(m_p.B2, k, j, i) = B_host(V2, k, j, i); - // p_bound_left_host(m_p.B3, k, j, i) = B_host(V3, k, j, i); - // if (use_emhd) { - // p_bound_left_host(m_p.Q, k, j, i) = q_host(k, j, i); - // p_bound_left_host(m_p.DP, k, j, i) = dP_host(k, j, i); - // } - // } else if (i >= n1 + ng) { - // int ii = i - (n1 + ng); - // p_bound_right_host(m_p.RHO, k, j, ii) = rho_host(k, j, i); - // p_bound_right_host(m_p.UU, k, j, ii) = u_host(k, j, i); - // p_bound_right_host(m_p.U1, k, j, ii) = uvec_host(V1, k, j, i); - // p_bound_right_host(m_p.U2, k, j, ii) = uvec_host(V2, k, j, i); - // p_bound_right_host(m_p.U3, k, j, ii) = uvec_host(V3, k, j, i); - // p_bound_right_host(m_p.B1, k, j, ii) = B_host(V1, k, j, i); - // p_bound_right_host(m_p.B2, k, j, ii) = B_host(V2, k, j, i); - // p_bound_right_host(m_p.B3, k, j, ii) = B_host(V3, k, j, i); - // if (use_emhd) { - // p_bound_right_host(m_p.Q, k, j, ii) = q_host(k, j, i); - // p_bound_right_host(m_p.DP, k, j, ii) = dP_host(k, j, i); - // } - // } } } } @@ -292,6 +238,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame fclose(fp_q); // Deep copy to device + Kokkos::fence(); rho.DeepCopy(rho_host); u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); @@ -300,13 +247,8 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame q.DeepCopy(q_host); dP.DeepCopy(dP_host); } - // p_bound_left.DeepCopy(p_bound_left_host); - // p_bound_right.DeepCopy(p_bound_right_host); Kokkos::fence(); - KBoundaries::SetDomainDirichlet(rc, IndexDomain::inner_x1, false); - KBoundaries::SetDomainDirichlet(rc, IndexDomain::outer_x1, false); - Flag("Initialized"); return TaskStatus::complete; diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index a220cc35..b745be13 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -49,7 +49,6 @@ using namespace parthenon; */ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing EMHD Modes problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index 16c6a6c1..93baf4d2 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -56,7 +56,6 @@ using namespace parthenon; */ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing EMHD shock problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index e7814294..b29f2ef2 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -43,8 +43,6 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing torus problem"); - auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; @@ -200,7 +198,7 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter // Since the conserved vars U are not initialized, this is done in *fluid frame*, // even if NOF frame is chosen (iharm3d does the same iiuc) // This is probably not a huge issue, just good to state explicitly - Floors::ApplyInitialFloors(rc.get(), IndexDomain::interior); + Floors::ApplyInitialFloors(pin, rc.get(), IndexDomain::interior); return TaskStatus::complete; } diff --git a/kharma/prob/gizmo.cpp b/kharma/prob/gizmo.cpp index 2ce4602c..a3c88f71 100644 --- a/kharma/prob/gizmo.cpp +++ b/kharma/prob/gizmo.cpp @@ -43,7 +43,6 @@ */ TaskStatus InitializeGIZMO(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing GIZMO problem"); auto pmb = rc->GetBlockPointer(); const Real mdot = pin->GetOrAddReal("bondi", "mdot", 1.0); @@ -65,7 +64,6 @@ TaskStatus InitializeGIZMO(std::shared_ptr>& rc, ParameterIn // This tests that PostInitialize will correctly fill ghost zones with the boundary we set SetGIZMO(rc, IndexDomain::interior); - Flag(rc, "Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/mhdmodes.hpp b/kharma/prob/mhdmodes.hpp index e4b12bbb..c12e4255 100644 --- a/kharma/prob/mhdmodes.hpp +++ b/kharma/prob/mhdmodes.hpp @@ -59,7 +59,6 @@ using namespace parthenon; */ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing MHD Modes problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; @@ -239,6 +238,5 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete pin->SetReal("parthenon/time", "tlim", 2. * M_PI / m::abs(omega.imag())); } - Flag(rc, "Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/orszag_tang.hpp b/kharma/prob/orszag_tang.hpp index 74ea6500..a9d1d870 100644 --- a/kharma/prob/orszag_tang.hpp +++ b/kharma/prob/orszag_tang.hpp @@ -19,7 +19,6 @@ using namespace parthenon; */ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing Orszag-Tang problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index d75ffaa7..003505aa 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -73,16 +73,16 @@ using namespace parthenon; void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) { auto rc = pmb->meshblock_data.Get(); - Flag(rc, "Initializing Block"); - - // Breakout to call the appropriate initialization function, - // defined in accompanying headers. - auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter - + Flag("Initialize "+prob); + // Also just print this, it's important if (MPIRank0()) { std::cout << "Initializing problem: " << prob << std::endl; } + + // Breakout to call the appropriate initialization function, + // defined in accompanying headers. + TaskStatus status = TaskStatus::fail; // MHD if (prob == "mhdmodes") { @@ -159,5 +159,5 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // Floors are NOT automatically applied at this point anymore. - Flag(rc, "Initialized Block"); + EndFlag("Initialize "+prob); } diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index cbae5318..892cbd6a 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -357,15 +357,9 @@ TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterI } } ); - // Fill the + // Fill the fluid conserved and magnetic field primitive variables Flux::BlockPtoUMHD(rc.get(), IndexDomain::entire, false); B_FluxCT::BlockUtoP(rc.get(), IndexDomain::entire, false); - // Register a Dirichlet boundary condition - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); - bound_pkg->KHARMAInnerX1Boundary = KBoundaries::Dirichlet; - bound_pkg->KHARMAOuterX1Boundary = KBoundaries::Dirichlet; - - - return TaskStatus::complete; + return TaskStatus::complete; } diff --git a/kharma/prob/shock_tube.hpp b/kharma/prob/shock_tube.hpp index 191ede1e..b41eaf6a 100644 --- a/kharma/prob/shock_tube.hpp +++ b/kharma/prob/shock_tube.hpp @@ -12,7 +12,6 @@ using namespace parthenon; */ TaskStatus InitializeShockTube(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Initializing Shock Tube problem"); auto pmb = rc->GetBlockPointer(); GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 15d25215..9a220d4b 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -49,7 +49,7 @@ namespace KReconstruction constexpr Real EPS = 1.e-26; // Enum for types. -enum class Type{donor_cell=0, linear_mc, linear_vl, ppm, mp5, weno5, weno5_lower_poles}; +enum class Type{donor_cell=0, linear_mc, linear_vl, ppm, mp5, weno5, weno5_lower_edges, weno5_lower_poles}; // BUILD UP (a) LINEAR MC RECONSTRUCTION @@ -487,6 +487,73 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_ KReconstruction::WENO5X3l(member, k - 1, j, is_l, ie_l, P, ql); KReconstruction::WENO5X3r(member, k, j, is_l, ie_l, P, qr); } +// WENO5 lowered edges: +// Linear X1 reconstruction near X1 boundaries +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + // This prioiritizes using the same-order fluxes on faces rather than for cells. + // Neither is transparently wrong (afaict) but this feels nicer + constexpr int o = 5; // offset + KReconstruction::WENO5X1(member, k, j, is_l+o, ie_l-o, P, ql, qr); + KReconstruction::PiecewiseLinearX1(member, k, j, is_l, is_l+o-1, P, ql, qr); + KReconstruction::PiecewiseLinearX1(member, k, j, ie_l-o+1, ie_l, P, ql, qr); +} +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); +} +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); +} +// WENO5 lowered poles: +// Linear X2 reconstruction near X2 boundaries +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); +} +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + // This prioiritizes using the same fluxes on faces rather than for cells. + // Neither is transparently wrong (afaict) but this feels nicer + constexpr int o = 5; + if (j > o || j < P.GetDim(1) - o) { + KReconstruction::WENO5X2l(member, k, j - 1, is_l+o, ie_l-o, P, ql); + KReconstruction::WENO5X2r(member, k, j, is_l+o, ie_l-o, P, qr); + } else { + ScratchPad2D q_u(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); + KReconstruction::PiecewiseLinearX2(member, k, j - 1, is_l, is_l+o-1, P, ql, q_u); + KReconstruction::PiecewiseLinearX2(member, k, j, is_l, is_l+o-1, P, q_u, qr); + } +} +template <> +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, + const GRCoordinates& G, const VariablePack &P, + const int& k, const int& j, const int& is_l, const int& ie_l, + ScratchPad2D ql, ScratchPad2D qr) +{ + reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); +} /** * Versions computing just the (limited) slope, for linear reconstructions. diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index 011e249f..792ec4ab 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -37,7 +37,7 @@ #include - +#pragma hd_warning_disable Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone) { Flag("Performing accretion reduction"); @@ -103,6 +103,7 @@ Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::f return result; } +#pragma hd_warning_disable Real Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg) { Flag("Performing domain reduction"); diff --git a/kharma/types.hpp b/kharma/types.hpp index 49ee8e5d..2ede7968 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -35,6 +35,7 @@ #include "decs.hpp" +#include "boundary_types.hpp" #include "kharma_package.hpp" #include @@ -150,6 +151,7 @@ class VarMap { B2 = B1 + 1; B3 = B1 + 2; } + // TODO TODO track total nvar and provide a function }; /** @@ -168,104 +170,29 @@ KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, return !outside(k, j, i, kb, jb, ib); } -inline bool BoundaryIsInner(IndexDomain domain) -{ - return domain == IndexDomain::inner_x1 || - domain == IndexDomain::inner_x2 || - domain == IndexDomain::inner_x3; -} - -inline int BoundarySide(IndexDomain domain) -{ - switch (domain) { - case IndexDomain::inner_x1: - case IndexDomain::outer_x1: - return 1; - case IndexDomain::inner_x2: - case IndexDomain::outer_x2: - return 2; - case IndexDomain::inner_x3: - case IndexDomain::outer_x3: - return 3; - default: - return 0; - } -} - -inline std::string BoundaryName(IndexDomain domain) -{ - switch (domain) { - case IndexDomain::inner_x1: - return "inner_x1"; - case IndexDomain::outer_x1: - return "outer_x1"; - case IndexDomain::inner_x2: - return "inner_x2"; - case IndexDomain::outer_x2: - return "outer_x2"; - case IndexDomain::inner_x3: - return "inner_x3"; - case IndexDomain::outer_x3: - return "outer_x3"; - case IndexDomain::interior: - return "interior"; - case IndexDomain::entire: - return "entire"; - default: - return "unknown"; - } -} - -inline IndexDomain BoundaryDomain(const BoundaryFace face) -{ - switch (face) { - case BoundaryFace::inner_x1: - return IndexDomain::inner_x1; - case BoundaryFace::outer_x1: - return IndexDomain::outer_x1; - case BoundaryFace::inner_x2: - return IndexDomain::inner_x2; - case BoundaryFace::outer_x2: - return IndexDomain::outer_x2; - case BoundaryFace::inner_x3: - return IndexDomain::inner_x3; - case BoundaryFace::outer_x3: - return IndexDomain::outer_x3; - case BoundaryFace::undef: - throw std::runtime_error("Undefined boundary face has no domain!"); - } -} - -/** - * Function for checking boundary flags: is this a domain or internal bound? - */ -inline bool IsDomainBound(std::shared_ptr pmb, BoundaryFace face) -{ - return !(pmb->boundary_flag[face] == BoundaryFlag::block || - pmb->boundary_flag[face] == BoundaryFlag::periodic); -} /** * Get zones which are inside the physical domain, i.e. set by computation or MPI halo sync, * not by problem boundary conditions. */ inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& bounds) { - return IndexRange3{IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x1) + using KBoundaries::IsPhysicalBoundary; + return IndexRange3{IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x1) ? bounds.is(IndexDomain::interior) : bounds.is(IndexDomain::entire), - IsDomainBound(pmb, BoundaryFace::outer_x1) + IsPhysicalBoundary(pmb, BoundaryFace::outer_x1) ? bounds.ie(IndexDomain::interior) : bounds.ie(IndexDomain::entire)}, - IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x2) + IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x2) ? bounds.js(IndexDomain::interior) : bounds.js(IndexDomain::entire), - IsDomainBound(pmb, BoundaryFace::outer_x2) + IsPhysicalBoundary(pmb, BoundaryFace::outer_x2) ? bounds.je(IndexDomain::interior) : bounds.je(IndexDomain::entire)}, - IndexRange{IsDomainBound(pmb, BoundaryFace::inner_x3) + IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x3) ? bounds.ks(IndexDomain::interior) : bounds.ks(IndexDomain::entire), - IsDomainBound(pmb, BoundaryFace::outer_x3) + IsPhysicalBoundary(pmb, BoundaryFace::outer_x3) ? bounds.ke(IndexDomain::interior) : bounds.ke(IndexDomain::entire)}}; } @@ -343,13 +270,13 @@ inline void PrintZone(MeshBlockData *rc) inline void Flag(std::string label) { - if(MPIRank0()) std::cerr << label << std::endl; + if(MPIRank0()) std::cerr << "Entering " << label << std::endl; } inline void Flag(MeshBlockData *rc, std::string label) { if(MPIRank0()) { - std::cerr << label << std::endl; + std::cerr << "Entering " << label << std::endl; if(PRINTCORNERS) PrintCorner(rc); if(PRINTZONE) PrintZone(rc); } @@ -358,7 +285,35 @@ inline void Flag(MeshBlockData *rc, std::string label) inline void Flag(MeshData *md, std::string label) { if(MPIRank0()) { - std::cerr << label << std::endl; + std::cerr << "Entering " << label << std::endl; + if(PRINTCORNERS || PRINTZONE) { + auto rc = md->GetBlockData(0).get(); + if(PRINTCORNERS) PrintCorner(rc); + if(PRINTZONE) PrintZone(rc); + } + } +} + +inline void EndFlag() {} + +inline void EndFlag(std::string label) +{ + if(MPIRank0()) std::cerr << "Exiting " << label << std::endl; +} + +inline void EndFlag(MeshBlockData *rc, std::string label) +{ + if(MPIRank0()) { + std::cerr << "Exiting " << label << std::endl; + if(PRINTCORNERS) PrintCorner(rc); + if(PRINTZONE) PrintZone(rc); + } +} + +inline void EndFlag(MeshData *md, std::string label) +{ + if(MPIRank0()) { + std::cerr << "Exiting " << label << std::endl; if(PRINTCORNERS || PRINTZONE) { auto rc = md->GetBlockData(0).get(); if(PRINTCORNERS) PrintCorner(rc); @@ -368,9 +323,34 @@ inline void Flag(MeshData *md, std::string label) } #else -inline void Flag(std::string label) {} -inline void Flag(MeshBlockData *rc, std::string label) {} -inline void Flag(MeshData *md, std::string label) {} +inline void Flag(std::string label) +{ + Kokkos::Profiling::pushRegion(label); +} +inline void Flag(MeshBlockData *rc, std::string label) +{ + Kokkos::Profiling::pushRegion(label); +} +inline void Flag(MeshData *md, std::string label) +{ + Kokkos::Profiling::pushRegion(label); +} +inline void EndFlag() +{ + Kokkos::Profiling::popRegion(); +} +inline void EndFlag(std::string label) +{ + Kokkos::Profiling::popRegion(); +} +inline void EndFlag(MeshBlockData *rc, std::string label) +{ + Kokkos::Profiling::popRegion(); +} +inline void EndFlag(MeshData *md, std::string label) +{ + Kokkos::Profiling::popRegion(); +} #endif /** * Versions of Flag() that take shared_ptr objects and call through with get() diff --git a/machines/darwin.sh b/machines/darwin.sh index b649ef0e..4bf58f07 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -46,29 +46,39 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then # These are orthogonal to above. # Just don't compile for an nv arch without "cuda" - NPROC=$(($(nproc) / 2)) # TODO robust? + NPROC=$(($(nproc) / 2)) if [[ "$ARGS" == *"arm-nv"* ]]; then HOST_ARCH="ARMV81" DEVICE_ARCH="AMPERE80" MPI_NUM_PROCS=2 - MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" + NODE_SLICE=2 elif [[ "$ARGS" == *"ampere"* ]]; then HOST_ARCH="ZEN3" DEVICE_ARCH="AMPERE80" MPI_NUM_PROCS=2 - MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" + NODE_SLICE=2 elif [[ "$ARGS" == *"volta"* ]]; then HOST_ARCH="HSW" DEVICE_ARCH="VOLTA70" MPI_NUM_PROCS=1 - # Some nodes only have 1 GPU but be conservative - MPI_EXTRA_ARGS="--map-by ppr:2:node:pe=$(($NPROC / 2))" - else + # Some nodes have 2 GPUs, be conservative + NODE_SLICE=2 + elif [[ "$ARGS" == *"knl"* ]]; then + HOST_ARCH="KNL" + MPI_NUM_PROCS=1 + # 4-way SMT, not 2 + NODE_SLICE=2 + elif [[ "$ARGS" == *"hsw"* ]]; then HOST_ARCH="HSW" MPI_NUM_PROCS=1 - MPI_EXTRA_ARGS="--map-by ppr:1:node:pe=$(($NPROC))" + NODE_SLICE=1 + else + echo "Must specify an architecture on Darwin!" + exit fi # Runtime MPI_EXE="mpirun" + # Lead MPI to water + MPI_EXTRA_ARGS="--map-by ppr:${MPI_NUM_PROCS}:node:pe=$(($NPROC / $NODE_SLICE))" fi diff --git a/machines/delta.sh b/machines/delta.sh index 469a93cb..923d0a22 100644 --- a/machines/delta.sh +++ b/machines/delta.sh @@ -8,15 +8,14 @@ # Also note that Delta's hdf5 is no longer serviceable (?) # So run './make.sh hdf5 clean cuda' -if [[ $HOST == *".delta.internal.ncsa.edu" ]] +if [[ $HOST == *".delta.internal.ncsa.edu" || $HOST == *".delta.ncsa.illinois.edu" ]] then HOST_ARCH=ZEN3 DEVICE_ARCH=AMPERE80 + MPI_EXE=mpirun # Load common modules module purge - module load modtree/gpu cmake - MPI_EXE=mpirun if [[ $ARGS == *"cuda"* ]] then @@ -25,6 +24,8 @@ then MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=16" MPI_NUM_PROCS=4 + module load modtree/gpu hdf5 cmake + if [[ $ARGS == *"nvhpc"* ]]; then # nvhpc only on request, MPI crashes module load nvhpc_latest openmpi-5.0_beta @@ -36,7 +37,7 @@ then fi else # CPU Compile - module load modtree/cpu gcc + module load modtree/cpu gcc hdf5 cmake MPI_NUM_PROCS=1 fi fi diff --git a/pars/anisotropic_conduction.par b/pars/anisotropic_conduction.par index 31fa910d..5562fae6 100644 --- a/pars/anisotropic_conduction.par +++ b/pars/anisotropic_conduction.par @@ -6,26 +6,17 @@ problem_id = anisotropic_conduction -refinement = none -numlevel = 1 - nx1 = 256 x1min = 0.0 x1max = 1.0 -ix1_bc = periodic -ox1_bc = periodic nx2 = 256 x2min = 0.0 x2max = 1.0 -ix2_bc = periodic -ox2_bc = periodic nx3 = 1 x3min = 0.0 x3max = 1.0 -ix3_bc = periodic -ox3_bc = periodic nx1 = 128 @@ -33,13 +24,12 @@ nx2 = 128 nx3 = 1 +# Setting Cartesian coords defaults to periodic boundaries base = cartesian_minkowski transform = null tlim = 10.0 -# "RK2" is the only option for implicit solver -integrator = rk2 dt_min = 1e-6 diff --git a/pars/bondi.par b/pars/bondi.par index e7de780a..3a1aa163 100644 --- a/pars/bondi.par +++ b/pars/bondi.par @@ -7,27 +7,31 @@ problem_id = bondi # Full mesh size, no refinement -refinement = none -numlevel = 1 +# Don't bother with xN boundaries for spherical coordinate systems +# KHARMA will automatically place ~5 zones inside the EH nx1 = 128 nx2 = 128 nx3 = 1 -# Split into blocks mesh -# Don't bother with xN boundaries for spherical coordinate systems -# KHARMA will automatically place ~5 zones inside the EH nx1 = 128 nx2 = 128 nx3 = 1 -base = ks +# Spherical Kerr-Schild coords +base = spherical_ks +# MKS of Gammie '03 transform = mks +# BH spin a = 0.0 +# MKS parameter hslope = 0.3 +# Radial domain in r_g r_in = 3.0 r_out = 30.0 +# If using "Funky" MKS later, where is "startx1"? +fmks_zero_point = 0.0 tlim = 50.0 @@ -38,15 +42,24 @@ gamma = 1.666667 reconstruction = weno5 +# Bondi problem parameters: +# density scaling/accretion rate mdot = 1.0 +# Sonic point rs = 8.0 -# Disable floors +# Disable floors disable_floors = true +# If using B field, enable w/: +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 100 +gamma_max = 10 -# We'll be adding material, and that's okay +# We'll be adding material, and that's okay check_inflow_outer = false @@ -56,6 +69,9 @@ solver = none # To add magnetic field #type = monopole #b10 = 1 +# Or +#type = vertical +#bz = 0.1 verbose = 0 diff --git a/pars/bondi_b_vertical.par b/pars/bondi_b_vertical.par index 44d83564..4777ac50 100644 --- a/pars/bondi_b_vertical.par +++ b/pars/bondi_b_vertical.par @@ -9,8 +9,6 @@ problem_id = bondi # Full mesh size, no refinement # Don't bother with xN boundaries for spherical coordinate systems # KHARMA will automatically place ~5 zones inside the EH -refinement = none -numlevel = 1 nx1 = 64 nx2 = 64 nx3 = 32 diff --git a/pars/bz_monopole_vertical.par b/pars/bz_monopole_vertical.par deleted file mode 100644 index 20868cf0..00000000 --- a/pars/bz_monopole_vertical.par +++ /dev/null @@ -1,76 +0,0 @@ -# FIXME TODO rewrite this head -# SANE model mirroring the simulation library -# Overall simulation size 50M, to allow -# running at small scale on e.g. a laptop -# Uses MKS coordinates, not Funky variant - - -problem_id = bz_monopole - - -refinement = none -numlevel = 1 -nx1 = 64 -nx2 = 64 -nx3 = 1 - - -nx1 = 64 -nx2 = 64 -nx3 = 1 - - -base = spherical_ks -transform = fmks -r_out = 30. -a = 0.9375 -hslope = 0.3 -mks_smooth = 0.5 -poly_xt = 0.82 -poly_alpha = 14.0 - - -tlim = 100.0 -nlim = -1 - - -verbose = 1 -extra_checks = 1 -flag_verbose = 0 - - -cfl = 0.9 -gamma = 1.444444 -reconstruction = weno5 - - -type = vertical -bz = 0.01 - - -type = imex -two_sync = true - - -bsq_over_rho_max = 5000 -bsq_over_u_max = 50 -rho_min_geom = 1e-20 -u_min_geom = 1e-20 - - -on = false -ne = 1.e-4 -Tp = 100 -u1 = 0.4 -power = 40 - - -file_type = hdf5 -dt = 5.0 -single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag -ghost_zones = true - - -file_type = hst -dt = 0.1 diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index c3340a98..c5bf3e24 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -8,13 +8,13 @@ problem_id = orszag_tang refinement = none numlevel = 1 -nx1 = 768 +nx1 = 1024 x1min = -3.141592653589793 x1max = 3.141592653589793 ix1_bc = periodic ox1_bc = periodic -nx2 = 768 +nx2 = 1024 x2min = -3.141592653589793 x2max = 3.141592653589793 ix2_bc = periodic @@ -27,8 +27,8 @@ ix3_bc = periodic ox3_bc = periodic -nx1 = 768 -nx2 = 768 +nx1 = 512 +nx2 = 512 nx3 = 1 @@ -59,6 +59,10 @@ variables = prims.rho, prims.u, prims.uvec, prims.B, jcon file_type = hst dt = 0.1 + +on = false +cleanup_interval = 10 + # This problem is generally much too short to need # checkpointing. However, we have a test which uses it. # diff --git a/pars/sane.par b/pars/sane.par index 6e72bcef..c6b792fe 100644 --- a/pars/sane.par +++ b/pars/sane.par @@ -13,7 +13,7 @@ nx2 = 64 nx3 = 64 -nx1 = 128 +nx1 = 64 nx2 = 64 nx3 = 32 diff --git a/tests/bclean/bondi_multizone.par b/tests/bclean/bondi_multizone.par index d61f8e39..c515107f 100755 --- a/tests/bclean/bondi_multizone.par +++ b/tests/bclean/bondi_multizone.par @@ -112,4 +112,4 @@ ghost_zones = true file_type = hst dt = 52896800 # output2_dt updated from run_kharma.sh - +ghost_zones = true From 83a54869dc3a4a9142fdc05db526d8c7f8efd25d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 10 May 2023 18:57:02 -0500 Subject: [PATCH 066/219] Fix B field init, try to make it more logical --- external/parthenon | 2 +- kharma/b_flux_ct/seed_B_ct.cpp | 44 ++++--- kharma/b_flux_ct/seed_B_ct.hpp | 16 +-- kharma/boundaries/boundaries.cpp | 3 +- kharma/boundaries/boundary_types.hpp | 2 + kharma/electrons/electrons.cpp | 48 +++----- kharma/prob/bondi.cpp | 2 +- kharma/prob/bondi.hpp | 2 +- kharma/prob/elec/hubble.cpp | 2 +- kharma/prob/elec/hubble.hpp | 2 +- kharma/prob/post_initialize.cpp | 169 +++++++++++++-------------- pars/sane.par | 3 +- 12 files changed, 138 insertions(+), 157 deletions(-) diff --git a/external/parthenon b/external/parthenon index b6c1979d..de25712e 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit b6c1979d6f826f8461556958f09dd81e7fd45095 +Subproject commit de25712e6f24b15ae2d1b1a8fc2db851b633b3a6 diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index caae709c..2e89a1c2 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -62,6 +62,8 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // as well as for good errors, many->one maps, etc. BSeedType b_field_flag = ParseBSeedType(b_field_type); + std::cout << "Seeding B field with type " << b_field_type << std::endl; + // Other parameters we need auto prob = pin->GetString("parthenon/job", "problem_id"); bool is_torus = (prob == "torus"); @@ -69,17 +71,9 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Require and load what we need if necessary Real a, rin, rmax, gam, kappa, rho_norm; Real tilt = 0; // Needs to be initialized - Real b10 = 0, b20 = 0, b30 = 0, bz = 0; + Real bz = 0; switch (b_field_flag) { - case BSeedType::constant: - b10 = pin->GetOrAddReal("b_field", "b10", 0.); - b20 = pin->GetOrAddReal("b_field", "b20", 0.); - b30 = pin->GetOrAddReal("b_field", "b30", 0.); - break; - case BSeedType::monopole: - b10 = pin->GetReal("b_field", "b10"); - break; case BSeedType::sane: case BSeedType::ryan: case BSeedType::ryan_quadrupole: @@ -103,9 +97,11 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) case BSeedType::vertical: bz = pin->GetOrAddReal("b_field", "bz", 0.); break; + default: + break; } - IndexDomain domain = IndexDomain::interior; + IndexDomain domain = IndexDomain::entire; int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); @@ -116,6 +112,9 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // Shortcut to field values for easy fields if (b_field_flag == BSeedType::constant) { + const Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); + const Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); + const Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); pmb->par_for("B_field_B", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly @@ -125,7 +124,9 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } ); return TaskStatus::complete; - } else if (b_field_flag == BSeedType::monopole) { + } + if (b_field_flag == BSeedType::monopole) { + const Real b10 = pin->GetReal("b_field", "b10"); // required pmb->par_for("B_field_B", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // Set B1 directly by normalizing @@ -135,7 +136,8 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) } ); return TaskStatus::complete; - } else if (b_field_flag == BSeedType::monopole_cube) { + } + if (b_field_flag == BSeedType::monopole_cube) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { // This ignores rin_bondi to keep divB consistent @@ -147,11 +149,11 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) B_P(V3, k, j, i) = 0.; } ); + return TaskStatus::complete; } // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, // But for tilted conditions we must keep track of all components - // TODO there should be an ncornersi,j,k ParArrayND A("A", NVEC, n3+1, n2+1, n1+1); pmb->par_for("B_field_A", ks, ke+1, js, je+1, is, ie+1, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { @@ -255,8 +257,9 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); VLOOP A(v, k, j, i) = A_tilt_lower[1+v]; } else { - // Some problems rely on a very accurate A->B, which the - A(V3, k, j, i) = q; + // Some problems rely on a very accurate A->B, which the rotation lacks. + // So, we preserve exact values in the no-tilt case. + A(V3, k, j, i) = q; } } ); @@ -265,16 +268,21 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) if (ndim > 2) { pmb->par_for("B_field_B_3D", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - get_B_from_A_3D(G, A, B_U, k, j, i); + averaged_curl_3D(G, A, B_U, k, j, i); } ); - } else { + } else if (ndim > 1) { pmb->par_for("B_field_B_2D", ks, ke, js, je, is, ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - get_B_from_A_2D(G, A, B_U, k, j, i); + averaged_curl_2D(G, A, B_U, k, j, i); } ); + } else { + throw std::runtime_error("Must initialize 1D field directly!"); } + // Finally, make sure we initialize the primitive field too + B_FluxCT::BlockUtoP(rc, IndexDomain::entire, false); + return TaskStatus::complete; } diff --git a/kharma/b_flux_ct/seed_B_ct.hpp b/kharma/b_flux_ct/seed_B_ct.hpp index 063ab6e9..41d4f1bf 100644 --- a/kharma/b_flux_ct/seed_B_ct.hpp +++ b/kharma/b_flux_ct/seed_B_ct.hpp @@ -18,17 +18,7 @@ namespace B_FluxCT */ TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); -/** - * Add flux to BH horizon - * Applicable to any Kerr-space GRMHD sim, run after import/initialization - * Preserves divB==0 with a Flux-CT step at end - */ -//void SeedBHFlux(MeshBlockData *rc, Real BHflux); - -} // namespace B_FluxCT - - -KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) { // Take a flux-ct step from the corner potentials. // This needs to be 3D because post-tilt A may not point in the phi direction only @@ -70,7 +60,7 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_3D(const GRCoordinates& G, const GridVe B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } -KOKKOS_INLINE_FUNCTION void get_B_from_A_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) { // A3,2 derivative const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; @@ -84,3 +74,5 @@ KOKKOS_INLINE_FUNCTION void get_B_from_A_2D(const GRCoordinates& G, const GridVe B_U(V3, k, j, i) = 0; } + +} // namespace B_FluxCT diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index e3160037..8bea3902 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -233,7 +233,8 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // boundary functions with this one. auto pmb = rc->GetBlockPointer(); - auto pkg = static_cast(pmb->packages.Get("Boundaries").get()); + //auto pkg = static_cast(pmb->packages.Get("Boundaries").get()); + auto pkg = pmb->packages.Get("Boundaries"); auto& params = pkg->AllParams(); const auto bface = BoundaryFaceOf(domain); diff --git a/kharma/boundaries/boundary_types.hpp b/kharma/boundaries/boundary_types.hpp index 3400774b..e9ef80ec 100644 --- a/kharma/boundaries/boundary_types.hpp +++ b/kharma/boundaries/boundary_types.hpp @@ -149,6 +149,7 @@ inline IndexDomain BoundaryDomain(const BoundaryFace face) case BoundaryFace::outer_x3: return IndexDomain::outer_x3; case BoundaryFace::undef: + default: throw std::runtime_error("Undefined boundary face has no domain!"); } } @@ -170,6 +171,7 @@ inline BoundaryFace BoundaryFaceOf(const IndexDomain domain) return BoundaryFace::outer_x3; case IndexDomain::interior: case IndexDomain::entire: + default: return BoundaryFace::undef; } } diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index 630dd527..7b0176cc 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -63,10 +63,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddReal("electrons", "gamma_p", 5./3); params.Add("gamma_p", gamma_p); - bool enforce_positive_dissipation = pin->GetOrAddBoolean("electrons", "enforce_positive_dissipation", true); + // Whether to enforce that dissipation be positive, i.e. increasing entropy + // Probably more accurate to keep off. + bool enforce_positive_dissipation = pin->GetOrAddBoolean("electrons", "enforce_positive_dissipation", false); params.Add("enforce_positive_dissipation", enforce_positive_dissipation); - bool kel_lim = pin->GetOrAddBoolean("electrons", "kel_lim", true); - params.Add("kel_lim", kel_lim); // This is used only in constant model Real fel_const = pin->GetOrAddReal("electrons", "fel_constant", 0.1); params.Add("fel_constant", fel_const); @@ -82,12 +82,13 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("electrons", "limit_kel", true); + params.Add("limit_kel", limit_kel); Real tp_over_te_min = pin->GetOrAddReal("electrons", "tp_over_te_min", 0.001); params.Add("tp_over_te_min", tp_over_te_min); Real tp_over_te_max = pin->GetOrAddReal("electrons", "tp_over_te_max", 1000.0); params.Add("tp_over_te_max", tp_over_te_max); - Real ktot_max = pin->GetOrAddReal("floors", "ktot_max", 1.e20); - params.Add("ktot_max", ktot_max); // Model options bool do_constant = pin->GetOrAddBoolean("electrons", "constant", false); @@ -104,31 +105,15 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

!!! - // std::vector masses = parse_list(pin->GetOrAddString("units", "MBH", "1.0")); - // if (masses != std::vector{1.0}) - // { - // std::vector> munits; - // for (int i=1; i <= masses.size(); ++i) { - // munits.push_back(parse_list(pin->GetString("units", "M_unit_" + std::to_string(i)))); - // } - - // if (MPIRank0() && packages->Get("Globals")->Param("verbose") > 0) { - // std::cout << "Using unit sets:" << std::endl; - // for (int i=0; i < masses.size(); ++i) { - // std::cout << std::endl << masses[i] << ":"; - // for (auto munit : munits[i]) { - // std::cout << " " << munit; - // } - // } - // std::cout << std::endl; - // } - // // This is a vector of Reals - // params.Add("masses", masses); - // // This is a vector of vectors of Reals - // params.Add("munits", munits); - // } + // TODO actually respect them of course + std::vector masses = pin->GetOrAddVector("electrons", "masses", std::vector{}); + if (masses.size() > 0) { + std::vector mass_names = pin->GetVector("electrons", "masses"); + std::vector> munits; + for (auto mass_name : mass_names) { + munits.push_back(pin->GetVector("electrons", "munits_"+mass_name)); + } + } // Default implicit iff GRMHD is done implicitly. TODO can we do explicit? auto& driver = packages->Get("Driver")->AllParams(); @@ -332,6 +317,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData const Real tptemin = pmb->packages.Get("Electrons")->Param("tp_over_te_min"); const Real tptemax = pmb->packages.Get("Electrons")->Param("tp_over_te_max"); const bool enforce_positive_diss = pmb->packages.Get("Electrons")->Param("enforce_positive_dissipation"); + const bool limit_kel = pmb->packages.Get("Electrons")->Param("limit_kel"); // This function (and any primitive-variable sources) needs to be run over the entire domain, // because the boundary zones have already been updated and so the same calculations must be applied @@ -387,7 +373,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData if (m_p.K_CONSTANT >= 0) { const Real fel = fel_const; // Default is true then enforce kel limits with clamp/clip, else no restrictions on kel - if (pmb->packages.Get("Electrons")->Param("kel_lim")) { + if (limit_kel) { P_new(m_p.K_CONSTANT, k, j, i) = clip(P_new(m_p.K_CONSTANT, k, j, i) + fel * diss, kel_min, kel_max); } else { P_new(m_p.K_CONSTANT, k, j, i) += fel * diss; diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index ca357452..2d6807f6 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -79,7 +79,7 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries")); + auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); if (pin->GetString("boundaries", "inner_x1") == "dirichlet" || pin->GetString("boundaries", "outer_x1") == "dirichlet") { SetBondi(rc); // TODO iterate & set any bounds specifically? diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 4b9d03b0..5ee84d26 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -57,7 +57,7 @@ TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain do template TaskStatus SetBondi(std::shared_ptr>& rc, bool coarse=false) { - SetBondiImpl(rc, domain, coarse); + return SetBondiImpl(rc, domain, coarse); } /** diff --git a/kharma/prob/elec/hubble.cpp b/kharma/prob/elec/hubble.cpp index 6c86b87a..47255c49 100644 --- a/kharma/prob/elec/hubble.cpp +++ b/kharma/prob/elec/hubble.cpp @@ -73,7 +73,7 @@ TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterI } // Replace the boundary conditions - auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries")); + auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetHubble; bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetHubble; bound_pkg->BlockApplyPrimSource = ApplyHubbleHeating; diff --git a/kharma/prob/elec/hubble.hpp b/kharma/prob/elec/hubble.hpp index 05132934..1c13ed83 100644 --- a/kharma/prob/elec/hubble.hpp +++ b/kharma/prob/elec/hubble.hpp @@ -59,7 +59,7 @@ TaskStatus SetHubbleImpl(std::shared_ptr>& rc, IndexDomain d template TaskStatus SetHubble(std::shared_ptr>& rc, bool coarse=false) { - SetHubbleImpl(rc, domain, coarse); + return SetHubbleImpl(rc, domain, coarse); } /** diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index bd32c501..2fddaa30 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -109,50 +109,66 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrpackages.AllPackages().count("B_CD"); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - // Add the field for torus problems as a second pass - // Preserves P==U and ends with all physical zones fully defined - if (pin->GetOrAddString("b_field", "type", "none") != "none") { + Flag("SeedBField"); + // Seed the magnetic field on each block + for (auto &pmb : pmesh->block_list) { + auto& rc = pmb->meshblock_data.Get(); + + // This initializes B_P & B_U + if (use_b_flux_ct) { + B_FluxCT::SeedBField(rc.get(), pin); + } else if (use_b_cd) { + B_CD::SeedBField(rc.get(), pin); + } + } + EndFlag("SeedBField"); + + // Then, if we're in a torus problem or we explicitly ask for it, + // normalize the magnetic field according to the density + auto prob = pin->GetString("parthenon/job", "problem_id"); + if (pin->GetOrAddBoolean("b_field", "norm", (prob == "torus"))) { + Flag("NormBField"); + // Default to the general literature beta_min of 100. + // As noted above, by default this uses the definition max(P)/max(P_B)! + Real desired_beta_min = pin->GetOrAddReal("b_field", "beta_min", 100.); + // "Legacy" is the much more common normalization: // It's the ratio of max values over the domain i.e. max(P) / max(P_B), // not necessarily a local min(beta) - Real beta_calc_legacy = pin->GetOrAddBoolean("b_field", "legacy", true); + Real beta_calc_legacy = pin->GetOrAddBoolean("b_field", "legacy_norm", true); - Flag("Seeding magnetic field"); - // Seed the magnetic field on each block - Real beta_min = 1.e100, p_max = 0., bsq_max = 0., bsq_min = 0.; - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); + // Calculate current beta_min value + Real bsq_max, p_max, beta_min; + if (beta_calc_legacy) { + std::cout << "Max is " << MaxBsq(md.get()) << std::endl; + bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); + beta_min = p_max / (0.5 * bsq_max); + } else { + beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); + } - // This initializes B_P & B_U - // TODO callback, also what about B_Cleanup? - if (use_b_flux_ct) { - B_FluxCT::SeedBField(rc.get(), pin); - } else if (use_b_cd) { - B_CD::SeedBField(rc.get(), pin); + if (MPIRank0() && verbose > 0) { + if (beta_calc_legacy) { + std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; + std::cout << "Pressure max pre-norm: " << p_max << std::endl; } - - // TODO should this be added after normalization? - // TODO option to add flux slowly during the run? - // Real BHflux = pin->GetOrAddReal("b_field", "bhflux", 0.0); - // if (BHflux > 0.) { - // if (use_b_flux_ct) { - // B_FluxCT::SeedBHFlux(rc.get(), pin); - // } else if (use_b_cd) { - // B_CD::SeedBHFlux(rc.get(), pin); - // } - // } + std::cout << "Beta min pre-norm: " << beta_min << std::endl; } - // Then, if we're in a torus problem or explicitly ask for it, - // normalize the magnetic field according to the density - auto prob = pin->GetString("parthenon/job", "problem_id"); - if (pin->GetOrAddBoolean("b_field", "norm", (prob == "torus"))) { - // Default to the general literature beta_min of 100. - // As noted above, by default this uses the definition max(P)/max(P_B)! - Real desired_beta_min = pin->GetOrAddReal("b_field", "beta_min", 100.); + // Then normalize B by sqrt(beta/beta_min) + Flag("Normalizing magnetic field"); + if (beta_min > 0) { + Real norm = m::sqrt(beta_min/desired_beta_min); + for (auto &pmb : pmesh->block_list) { + auto& rc = pmb->meshblock_data.Get(); + KHARMADriver::Scale(std::vector{"prims.B"}, rc.get(), norm); + } + } - // Calculate current beta_min value - Real bsq_min, bsq_max, p_max, beta_min; + // Measure again to check. We'll add divB too, later + if (verbose > 0) { + Real bsq_max, p_max, beta_min; if (beta_calc_legacy) { bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); @@ -160,48 +176,19 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr 0) { + if (MPIRank0()) { if (beta_calc_legacy) { - std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; - std::cout << "Pressure max pre-norm: " << p_max << std::endl; - } - std::cout << "Beta min pre-norm: " << beta_min << std::endl; - } - - // Then normalize B by sqrt(beta/beta_min) - Flag("Normalizing magnetic field"); - if (beta_min > 0) { - Real norm = m::sqrt(beta_min/desired_beta_min); - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - KHARMADriver::Scale(std::vector{"prims.B"}, rc.get(), norm); - } - } - - // Measure again to check. We'll add divB too, later - if (verbose > 0) { - Real bsq_min, bsq_max, p_max, beta_min; - if (beta_calc_legacy) { - bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); - p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); - beta_min = p_max / (0.5 * bsq_max); - } else { - beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); - } - if (MPIRank0()) { - if (beta_calc_legacy) { - std::cout << "B^2 max post-norm: " << bsq_max << std::endl; - std::cout << "Pressure max post-norm: " << p_max << std::endl; - } - std::cout << "Beta min post-norm: " << beta_min << std::endl; + std::cout << "B^2 max post-norm: " << bsq_max << std::endl; + std::cout << "Pressure max post-norm: " << p_max << std::endl; } + std::cout << "Beta min post-norm: " << beta_min << std::endl; } } + EndFlag("NormBField"); } // We've been initializing/manipulating P - Flux::MeshPtoU(md.get(), IndexDomain::interior); + Flux::MeshPtoU(md.get(), IndexDomain::entire); Flag("Added B Field"); } @@ -227,21 +214,25 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) auto& pkgs = pmesh->packages.AllPackages(); - // Then, add/modify any magnetic field left until this step - // (since B field initialization can depend on global maxima, - // & is handled by the B field transport package, it's sometimes done here) - if (!is_restart) { - // B field init is not stencil-1, needs boundaries sync'd + // Magnetic field operations + if (pin->GetString("b_field", "solver") != "none") { + // If we need to seed a field based on the problem's fluid initialization... + if (pin->GetOrAddString("b_field", "type", "none") != "none" && !is_restart) { + // B field init is not stencil-1, needs boundaries sync'd. + // FreezeDirichlet ensures any Dirichlet conditions aren't overwritten by zeros + KBoundaries::FreezeDirichlet(md); + KHARMADriver::SyncAllBounds(md); + + // Then init B field on each block... + KHARMA::SeedAndNormalizeB(pin, md); + } + + // Regardless, if evolving a field we should print max(divB) + // divB is not stencil-1 and we may not have run the above. + // If we did, we still need another sync, so it works out + KBoundaries::FreezeDirichlet(md); KHARMADriver::SyncAllBounds(md); - // Then init B field on each block - KHARMA::SeedAndNormalizeB(pin, md); - } - // Print divB - if (pin->GetString("b_field", "solver") != "none") { - // Another sync to update B fields - KHARMA::SeedAndNormalizeB(pin, md); - // If a B field exists, print divB here if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); } else if (pkgs.count("B_CD")) { @@ -267,8 +258,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } // Clean the B field if we've introduced a divergence somewhere - // Call this any time the package is loaded, all the - // logic about parsing whether to clean is there + // We call this function any time the package is loaded: + // if we decided to load it in kharma.cpp, we need to clean. if (pkgs.count("B_Cleanup")) { if (pin->GetOrAddBoolean("b_cleanup", "output_before_cleanup", false)) { auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); @@ -276,17 +267,19 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); } + // This does its own MPI syncs B_Cleanup::CleanupDivergence(md); B_Cleanup::RemoveExtraFields(pmesh->block_list); } // Finally, synchronize boundary values. - // This should be the first sync if there is no B field + // Freeze any Dirichlet physical boundaries as they are now, after cleanup/sync/etc. + KBoundaries::FreezeDirichlet(md); + // This is the first sync if there is no B field KHARMADriver::SyncAllBounds(md); - // And make sure the (trivial) primitive values are up-to-date + // And make sure the trivial primitive values are up-to-date Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); - Flag("Post-initialization finished"); } diff --git a/pars/sane.par b/pars/sane.par index c6b792fe..e4e0d39b 100644 --- a/pars/sane.par +++ b/pars/sane.par @@ -50,8 +50,7 @@ u_jitter = 0.04 type = sane beta_min = 100. -fix_eh_flux = false -fix_exterior_flux = false +norm = false rho_min_geom = 1e-6 From 6237f90ff078d96efdd7bd6fe29594f58a3803c4 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 10 May 2023 21:25:35 -0500 Subject: [PATCH 067/219] Try to make B field init a bit clearer --- kharma/b_flux_ct/b_flux_ct.cpp | 24 ++++++++++++++++++++++++ kharma/b_flux_ct/b_flux_ct.hpp | 6 ++++++ kharma/b_flux_ct/seed_B_ct.cpp | 11 +++++++++-- kharma/b_flux_ct/seed_B_ct.hpp | 16 +++++++--------- kharma/implicit/implicit.cpp | 1 - tests/bondi/run.sh | 2 +- 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 7d8b2b0c..604768b2 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -213,6 +213,30 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) ); } +void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + Flag(rc, "B PtoU Block"); + auto pmb = rc->GetBlockPointer(); + + auto B_U = rc->PackVariables(std::vector{"cons.B"}); + auto B_P = rc->PackVariables(std::vector{"prims.B"}); + + const auto& G = pmb->coords; + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + const IndexRange vec = IndexRange({0, B_U.GetDim(4)-1}); + + pmb->par_for("UtoP_B", vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &mu, const int &k, const int &j, const int &i) { + // Update the conserved B-fields + B_U(mu, k, j, i) = B_P(mu, k, j, i) * G.gdet(Loci::center, j, i); + } + ); +} + void FixFlux(MeshData *md) { auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 6b1800f0..92a7a4e9 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -66,6 +66,12 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*md, IndexDomain domain, bool coarse=false); void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); +/** + * Reverse of the above. Only used alone during initialization. + * Generally, use Flux::BlockPtoU or Flux::BlockPtoUExceptMHD. + */ +void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); + /** * All flux corrections required by this package */ diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index 2e89a1c2..ded91bbb 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -111,6 +111,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) int ndim = pmb->pmy_mesh->ndim; // Shortcut to field values for easy fields + bool early_field = false; if (b_field_flag == BSeedType::constant) { const Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); const Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); @@ -123,7 +124,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) B_P(V3, k, j, i) = b30; } ); - return TaskStatus::complete; + early_field = true; } if (b_field_flag == BSeedType::monopole) { const Real b10 = pin->GetReal("b_field", "b10"); // required @@ -135,7 +136,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) B_P(V3, k, j, i) = 0.; } ); - return TaskStatus::complete; + early_field = true; } if (b_field_flag == BSeedType::monopole_cube) { pmb->par_for("B_field_B", ks, ke, js, je, is, ie, @@ -149,9 +150,15 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) B_P(V3, k, j, i) = 0.; } ); + early_field = true; + } + // We still need to update conserved flux values, but then we're done + if (early_field) { + B_FluxCT::BlockPtoU(rc, IndexDomain::entire, false); return TaskStatus::complete; } + // For all other fields... // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, // But for tilted conditions we must keep track of all components ParArrayND A("A", NVEC, n3+1, n2+1, n1+1); diff --git a/kharma/b_flux_ct/seed_B_ct.hpp b/kharma/b_flux_ct/seed_B_ct.hpp index 41d4f1bf..c679e67c 100644 --- a/kharma/b_flux_ct/seed_B_ct.hpp +++ b/kharma/b_flux_ct/seed_B_ct.hpp @@ -8,17 +8,14 @@ namespace B_FluxCT { /** - * Seed an axisymmetric initialization with magnetic field proportional to fluid density, - * or density and radius, to create a SANE or MAD flow - * Note this function expects a normalized P for which rho_max==1 - * - * @param rin is the interior radius of the torus - * @param min_rho_q is the minimum density at which there will be magnetic vector potential - * @param b_field_type is one of "sane" "ryan" "r3s3" or "gaussian", described below (TODO test or remove opts) + * Seed a divergence-free magnetic field of user's choice, optionally + * proportional to existing fluid density. + * Updates primitive and conserved variables. */ TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); -KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, + const int& k, const int& j, const int& i) { // Take a flux-ct step from the corner potentials. // This needs to be 3D because post-tilt A may not point in the phi direction only @@ -60,7 +57,8 @@ KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridV B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } -KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) +KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, + const int& k, const int& j, const int& i) { // A3,2 derivative const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index fc5b195a..7d25d6cf 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -113,7 +113,6 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh Metadata m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("solve_norm", m_real); // Integer field that saves where the solver fails (rho + drho < 0 || u + du < 0) - // Metadata m_int = Metadata({Metadata::Integer, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariable diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index ee929895..f2cbffe9 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -29,7 +29,7 @@ conv_2d() { } # Test coordinates -conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" +#conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" conv_2d mks coordinates/transform=mks "in 2D, MKS coordinates" conv_2d eks coordinates/transform=eks "in 2D, EKS coordinates" # TODO broken From ecc84763084c5b49379df46f3c271dff7567625f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 10 May 2023 21:29:11 -0500 Subject: [PATCH 068/219] Add multizone supervisor script from multizone_stable --- scripts/batch/multizone/multizone.par | 123 +++++++++ scripts/batch/multizone/multizone_chicoma.sb | 22 ++ scripts/batch/multizone/run.py | 276 +++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100755 scripts/batch/multizone/multizone.par create mode 100755 scripts/batch/multizone/multizone_chicoma.sb create mode 100755 scripts/batch/multizone/run.py diff --git a/scripts/batch/multizone/multizone.par b/scripts/batch/multizone/multizone.par new file mode 100755 index 00000000..59711f3f --- /dev/null +++ b/scripts/batch/multizone/multizone.par @@ -0,0 +1,123 @@ +# Multizone Bondi flow template +# Many options are replaced by accompanying "run.py" script + + +problem_id = bondi + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 128 +nx3 = 128 + + +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +base = ks +transform = mks +a = 0.0 +# w/B field, hslope=>0.3 +hslope = 1.0 +ext_g = false + + +# Time set in run.py +tlim = 5289680481 +nlim = -1 +dt_min = 0.00001 + + +# w/B, cfl=>0.5 +cfl = 0.9 +gamma = 1.666667 +reconstruction = linear_vl +add_jcon = false + + +mdot = 1.0 +# All these will be updated by run.py +rs = 316.22776601683796 +vacuum_logrho = -8.2014518 +vacuum_log_u_over_rho = -5.2915149 +r_shell = 8388608 +use_gizmo = false + + +datfn = none + + +# All set in run.py +fname = none +fname_fill = none +use_dt = false +base = 8 +nzone = 7 + + +# disable by default -- enabling B field overrides +disable_floors = true +# This caused issues? +# frame = drift +# Usual geometric floors, no beta floor +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_u_max = 1e20 +u_over_rho_max = 100 +# Strict sigma, gamma +bsq_over_rho_max = 100 +gamma_max = 10 +# Does not affect these floors +adjust_k = 0 + + +# Inflow is allowed +check_inflow_outer = false +check_inflow_inner = false +# Otherwise defaults + + +# overridden +u_jitter=0 + + +# No field by default +# No cleanup by default as fix_flux_x1 preserves divB +type = none +solver = none +fix_flux_x1 = true + + +# Be very clear about errors +verbose = 1 +flag_verbose = 2 +extra_checks = 1 +archive_parameters = 1 + + +type = harm +two_sync = true + + +max_nonlinear_iter = 3 + +# Output timings set in run.py + +file_type = hdf5 +dt = 528968040 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag, divB +ghost_zones = true + + +file_type = rst +dt = 2644840240 +ghost_zones = true + + +file_type = hst +dt = 52896800 + diff --git a/scripts/batch/multizone/multizone_chicoma.sb b/scripts/batch/multizone/multizone_chicoma.sb new file mode 100755 index 00000000..5f0da05c --- /dev/null +++ b/scripts/batch/multizone/multizone_chicoma.sb @@ -0,0 +1,22 @@ +#!/bin/bash +# Admin stuff +#SBATCH -J KHARMA-MZ +#SBATCH -t 12:00:00 +#SBATCH -N 1 +#SBATCH -o "out-%j.txt" + +# Partition +##SBATCH -p gpu_debug --reservation gpu_debug --qos debug +#SBATCH -p gpu + +# Node options: full, all CPU +# Note we could do 32 if HT is faster +#SBATCH --tasks-per-node=4 +#SBATCH --cpus-per-task=16 +#SBATCH --exclusive +#SBATCH --mem=0 + +# Everything is called from the supervising python script +# No point in setting a walltime limit, this invokes KHARMA many times +KHARMA_DIR=$HOME/Code/kharma-multizone +exec $KHARMA_DIR/scripts/batch/multizone/run.py "$@" diff --git a/scripts/batch/multizone/run.py b/scripts/batch/multizone/run.py new file mode 100755 index 00000000..a3256891 --- /dev/null +++ b/scripts/batch/multizone/run.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python + +# This runs a "multi-zone" KHARMA sequence +# See --help + +import os +import click +import glob +import subprocess +import pickle + +import numpy as np +import h5py + +import pyharm + +def format_args(args): + """Format a dict in var=val format for Parthenon""" + arg_list = [] + for key in args.keys(): + arg_list += [key+"={}".format(args[key]).lower()] + return arg_list + + +def calc_runtime(r_out, r_b): + """r/v where v=sqrt(v_ff**2+c_s**2)""" + return r_out/np.sqrt(1./r_out + 1./r_b) + +def data_dir(n): + """Data directory naming scheme""" + return "{:05d}".format(n) + +@click.command() +# Run parameters +@click.option('--nx1', default=64, help="1-Run radial resolution") +@click.option('--nx2', default=64, help="1-Run theta resolution") +@click.option('--nx3', default=64, help="1-Run phi resolution") +@click.option('--nx1_mb', default=64, help="1-Run radial block resolution") +@click.option('--nx2_mb', default=32, help="1-Run theta block resolution") +@click.option('--nx3_mb', default=32, help="1-Run phi block resolution") +@click.option('--nzones', default=8, help="Total number of zones (annuli)") +@click.option('--base', default=8, help="Exponent base for annulus sizes") +@click.option('--nruns', default=300, help="Total number of runs to perform") +@click.option('--spin', default=0.0, help="BH spin") +@click.option('--bz', default=0.0, help="B field Z component. Zero for no field") +@click.option('--cfl', default=0.9, help="Courant condition fraction. Defaults to 0.5 in B field") +@click.option('--tlim', default=None, help="Enforce a specific tlim for every run (for testing)") +@click.option('--nlim', default=-1, help="Consistent max number of steps for each run") +@click.option('--r_b', default=1.e5, help="Bondi radius. None chooses based on nzones") +@click.option('--jitter', default=0.0, help="Proportional jitter to apply to starting state. Default 10% w/B field") +# Flags and options +@click.option('--kharma_args', default="", help="Arguments for KHARMA run.sh") +@click.option('--short_t_out', default=True, help="Use shorter outermost annulus") +@click.option('--restart', is_flag=True, help="Restart from most recent run parameters") +@click.option('--parfile', default=None, help="Parameter filename") +@click.option('--gizmo', is_flag=True, help="Start from GIZMO data") +@click.option('--gizmo_fname', default="../gizmo_data.txt", help="Filename of GIZMO data") +@click.option('--ext_g', is_flag=True, help="Include external gravity") +# Don't use this +@click.option('--start_time', default=0.0, help="Starting time. Only use if you know what you're doing.") +def run_multizone(**kwargs): + """This script runs a "multi-zone" KHARMA sequence. + The idea is to divide a large domain (~1e8M radius) into several "zones," + then evolve them one at a time while keeping the others constant. + This allows recovering long-term steady-state behavior quickly, by evolving each + "zone" on its own timescale. + Each run takes the final state of the last run, expands the domain inward or outward, and + evolves the resulting domain/state. + + This mode now supports magnetic fields, arbitrary overlaps and coordinates, and other niceties. + """ + # We're kept in a script subdirectory in kharma/ + mz_dir = os.path.dirname(os.path.realpath(__file__)) + # parent + kharma_dir = mz_dir+"/../../.." + # Get our name from the working dir + run_name = os.getcwd().split("/")[-1] + + # Assign initial arguments, based on either: + # 1. Loading last-started run when restarting + # 2. Computing arguments from kwargs if beginning fresh + if kwargs['restart']: + restart_file = open('restart.p', 'rb') + kwargs = pickle.load(restart_file) + args = pickle.load(restart_file) + restart_file.close() + else: + # First run arguments + base = kwargs['base'] + args = {} + args['parthenon/job/problem_id'] = "bondi" + args['resize_restart/base'] = base + args['resize_restart/nzone'] = kwargs['nzones'] + args['resize_restart/iteration'] = 1 + kwargs['start_run'] = 0 + + turn_around = kwargs['nzones'] - 1 + args['coordinates/r_out'] = base**(turn_around+2) + args['coordinates/r_in'] = base**turn_around + # Initialize half-vacuum, unless it's the first GIZMO run + if kwargs['gizmo']: + args['bondi/r_shell'] = args['coordinates/r_in'] + else: + args['bondi/r_shell'] = base**(turn_around+2)/2. + + # bondi & vacuum parameters + # TODO derive these from r_b or gizmo + if kwargs['nzones'] == 3: + kwargs['r_b'] = 256 + logrho = -4.13354231 + log_u_over_rho = -2.57960521 + elif kwargs['gizmo']: + kwargs['r_b'] = 1e5 + logrho = -7.80243572 + log_u_over_rho = -5.34068635 + else: + kwargs['r_b'] = 1e5 + logrho = -8.2014518 + log_u_over_rho = -5.2915149 + args['bondi/vacuum_logrho'] = logrho + args['bondi/vacuum_log_u_over_rho'] = log_u_over_rho + args['bondi/rs'] = np.sqrt(float(kwargs['r_b'])) + + # B field additions + if kwargs['bz'] != 0.0: + # Set a field to initialize with + args['b_field/type'] = "vertical" + args['b_field/solver'] = "flux_ct" + args['b_field/bz'] = kwargs['bz'] + # Compress coordinates to save time + args['coordinates/transform'] = "mks" + args['coordinates/hslope'] = 0.3 + # Enable the floors + args['floors/disable_floors'] = False + # And modify a bunch of defaults + # Assume we will always want jitter if we have B + if kwargs['jitter'] == 0.0: + kwargs['jitter'] = 0.1 + # Lower the cfl condition in B field + kwargs['cfl'] = 0.5 + # And limit runtime + kwargs['nlim'] = int(5e4) + + # Parameters directly from defaults/cmd + args['perturbation/u_jitter'] = kwargs['jitter'] + args['GRMHD/cfl'] = kwargs['cfl'] + args['coordinates/a'] = kwargs['spin'] + args['coordinates/ext_g'] = kwargs['ext_g'] + args['bondi/use_gizmo'] = kwargs['gizmo'] + args['gizmo_shell/datfn'] = kwargs['gizmo_fname'] + args['parthenon/time/nlim'] = kwargs['nlim'] + # Mesh size + args['parthenon/mesh/nx1'] = kwargs['nx1'] + args['parthenon/mesh/nx2'] = kwargs['nx2'] + args['parthenon/mesh/nx3'] = kwargs['nx3'] + args['parthenon/meshblock/nx1'] = kwargs['nx1_mb'] + args['parthenon/meshblock/nx2'] = kwargs['nx2_mb'] + args['parthenon/meshblock/nx3'] = kwargs['nx3_mb'] + + # Any derived parameters once we've loaded args/kwargs + # Default parameters are in mz_dir + if kwargs['parfile'] is None: + kwargs['parfile'] = mz_dir+"/multizone.par" + + # Iterate, starting with the default args and updating as we go + for run_num in np.arange(kwargs['start_run'], kwargs['nruns']): + # run times for each annulus + r_out = args['coordinates/r_out'] + r_b = float(kwargs['r_b']) + base = args['resize_restart/base'] + outermost_zone = 2 * (kwargs['nzones'] - 1) + if kwargs['tlim'] is None: + # Calculate free-fall time + if kwargs['short_t_out'] and run_num % outermost_zone == 0: + runtime = calc_runtime(r_out/base, r_b) + print("SHORT_T_OUT @ RUN # {}: r_out={:.4g}, but next largest annulus r_out={:.4g} used for the runtime".format(run_num, r_out, r_out/base)) + else: + runtime = calc_runtime(r_out, r_b) + # B field runs use half this + if kwargs['bz'] != 0.0: + runtime /= 2 + else: + runtime = float(kwargs['tlim']) + args['parthenon/time/tlim'] = kwargs['start_time'] + runtime + + # Output timing (TODO make options) + args['parthenon/output0/dt'] = max(int(runtime/10.), 1) + args['parthenon/output1/dt'] = max(int(runtime/5.), 1) + args['parthenon/output2/dt'] = runtime/100. + + # Start any future run from this point + kwargs['start_run'] = run_num + + # Now that we've determined all parameters, save them as used + restart_file = open('restart.p', 'wb') + pickle.dump(kwargs, restart_file) + pickle.dump(args, restart_file) + restart_file.close() + # And print them + print(run_name+": iter {}, run {} : radius {:.4g} to {:.4g}, time {:.4g} to {:.4g}".format( + args['resize_restart/iteration'], run_num, + args['coordinates/r_in'], args['coordinates/r_out'], + kwargs['start_time'], args['parthenon/time/tlim'])) + + ddir = data_dir(run_num) + os.makedirs(ddir, exist_ok=True) + fout = open(ddir+"/kharma.log", "w") + ret_obj = subprocess.run([kharma_dir+"/run.sh",] + ["-i", kwargs['parfile'], "-d", ddir] + format_args(args), + stdout=fout, stderr=subprocess.STDOUT) + fout.close() + + # Don't continue (& save restart data, etc) if KHARMA returned error + if ret_obj.returncode != 0: + print("KHARMA returned error: {}. Exiting.".format(ret_obj.retcode)) + exit(-1) + + # Update parameters for the next pass + # This updates both kwargs (start_time) and args (coordinates, dt, iteration #, fnames) + update_args(run_num, kwargs, args) + + +def update_args(run_num, kwargs, args): + # Update the dictionary of args to prepare for the *next* run (run_num+1). + # Called after the first run is finished, and after each run subsequently + + # We'll always be restarting after the first run + args['parthenon/job/problem_id']="resize_restart_kharma" + + # Filename to restart from + fname_dir = "{:05d}".format(run_num) + fname=glob.glob(fname_dir+"/*final.rhdf")[0] + # Get start_time, ncycle, dt from previous run + kwargs['start_time'] = pyharm.io.get_dump_time(fname) + d = pyharm.load_dump(fname) + iteration = d['iteration'] + last_r_out = d['r_out'] + last_r_in = d['r_in'] + del d + # TODO read all of Params/Info in pyharm + f = h5py.File(fname, 'r') + dt_last = f['Params'].attrs['Globals/dt_last'] + f.close() + + # Increment iteration count when we just finished the outermost zone + if run_num > 0 and run_num % (kwargs['nzones'] - 1) == 0: + iteration += 1 + args['resize_restart/iteration'] = iteration + + # Are we moving inward? + out_to_in=(-1)**(1+iteration) # if iteration odd, out_to_in=1, if even, out_to_in=-1 + # if out_to_in > 0: + # print("Moving inward:") + # else: + # print("Moving outward:") + + # Choose timestep and radii for the next run: smaller/larger as we step in/out + args['parthenon/time/dt_min'] = max(dt_last * kwargs['base']**(-3./2.*out_to_in) / 4, 1e-5) + if out_to_in > 0: + args['coordinates/r_out'] = last_r_out / kwargs['base'] + args['coordinates/r_in'] = last_r_in / kwargs['base'] + else: + args['coordinates/r_out'] = last_r_out * kwargs['base'] + args['coordinates/r_in'] = last_r_in * kwargs['base'] + + # Get filename to fill in the rest that fname doesn't cover + if run_num + 1 < kwargs['nzones']: + fname_fill = "none" + else: + # TODO explain why this number is correct + fname_fill_dir = data_dir(2 * (iteration - 1) * (kwargs['nzones'] - 1) - (run_num + 1)) + fname_fill = glob.glob(fname_fill_dir+"/*final.rhdf")[0] + args['resize_restart/fname'] = fname + args['resize_restart/fname_fill'] = fname_fill + +if __name__=="__main__": + run_multizone() From 13057685976e0c2bab8ef40b364cad1c2a457ebc Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 11 May 2023 09:45:45 -0500 Subject: [PATCH 069/219] Fix the obvious bugs, EMHD problems now run. Something in EMHDmodes convergence still, turns into 0/NaNs in torii --- kharma/emhd/emhd.cpp | 7 +++---- kharma/emhd/emhd_sources.hpp | 9 +++++---- kharma/prob/emhd/fm_torus_emhd.cpp | 4 ++-- pars/sane_emhd.par | 11 +++++------ tests/emhdmodes/run.sh | 6 +++--- tests/mhdmodes/run.sh | 4 ++-- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index cd4b9c5c..9d0a8718 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -131,10 +131,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->Param("ImplicitFlag"); - Metadata m_con = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, isImplicit, + Metadata m_con = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("Implicit"), Metadata::Conserved, Metadata::WithFluxes, Metadata::GetUserFlag("EMHD")}); - Metadata m_prim = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, isImplicit, + Metadata m_prim = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Implicit"), Metadata::FillGhost, Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("EMHD")}); // Heat conduction @@ -199,7 +198,7 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Get temporary ucov, Theta for gradients PackIndexMap temps_map; - auto Temps = md->PackVariables(std::vector{Metadata::GetUserFlag("EMHDTemp")}, temps_map); + auto Temps = md->PackVariables(std::vector{Metadata::GetUserFlag("EMHDTemporary")}, temps_map); int m_ucov = temps_map["ucov"].first; int m_theta = temps_map["Theta"].first; diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index c0498aec..6f2831a1 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -83,6 +83,7 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); + const double mag_b = m::sqrt(bsq); // TIME DERIVATIVES Real ucon[GR_DIM], ucov_new[GR_DIM], ucov_old[GR_DIM]; @@ -107,11 +108,11 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons if (emhd_params.conduction) { const Real& qtilde = P(m_p.Q); - Real q0 = -rho * chi_e * (Dtmp.bcon[0] / m::sqrt(bsq)) * dt_Theta; - DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / m::sqrt(bsq)) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; + Real q0 = -rho * chi_e * (Dtmp.bcon[0] / mag_b) * dt_Theta; + DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / mag_b) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; Real q0_tilde = q0; if (emhd_params.higher_order_terms) - q0_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho * m::pow(Theta, 2)) ) : 0.; + q0_tilde *= (chi_e != 0) * m::sqrt(tau / (chi_e * rho * m::pow(Theta, 2)) ); dUq = G.gdet(Loci::center, j, i) * (q0_tilde / tau); if (emhd_params.higher_order_terms) @@ -124,7 +125,7 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; Real dP0_tilde = dP0; if (emhd_params.higher_order_terms) - dP0_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho * Theta) ) : 0.; + dP0_tilde *= (nu_e != 0) * m::sqrt(tau / (nu_e * rho * Theta) ); dUdP = G.gdet(Loci::center, j, i) * (dP0_tilde / tau); if (emhd_params.higher_order_terms) diff --git a/kharma/prob/emhd/fm_torus_emhd.cpp b/kharma/prob/emhd/fm_torus_emhd.cpp index 91fef9f2..8920a564 100644 --- a/kharma/prob/emhd/fm_torus_emhd.cpp +++ b/kharma/prob/emhd/fm_torus_emhd.cpp @@ -177,7 +177,7 @@ TaskStatus InitializeFMTorusEMHD(std::shared_ptr>& rc, Param // If we print diagnostics, do so only from block 0 as the others do exactly the same thing // Since this is initialization, we are guaranteed to have a block 0 - if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Calculating maximum density:" << std::endl; std::cout << "a = " << a << std::endl; std::cout << "dx = " << dx << std::endl; @@ -210,7 +210,7 @@ TaskStatus InitializeFMTorusEMHD(std::shared_ptr>& rc, Param // Record and print normalization factor if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rho_norm"))) pmb->packages.Get("GRMHD")->AllParams().Add("rho_norm", rho_max); - if (pmb->gid == 0 && pmb->packages.Get("GRMHD")->Param("verbose") > 0) { + if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { std::cout << "Initial maximum density is " << rho_max << std::endl; } diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par index b15beec4..ad666cac 100644 --- a/pars/sane_emhd.par +++ b/pars/sane_emhd.par @@ -56,7 +56,7 @@ reconstruction = weno5 implicit = false type = sane beta_min = 100. -initial_cleanup = true +initial_cleanup = false # This block must be present and values filled in all EGRMHD simulations @@ -77,16 +77,15 @@ u_jitter = 0.04 frame = drift -rho_min_geom = 1e-3 -u_min_geom = 1e-5 +rho_min_geom = 1e-5 +u_min_geom = 1e-7 bsq_over_rho_max = 100 -bsq_over_u_max = 100 +bsq_over_u_max = 1e20 u_over_rho_max = 100 -gamma_max = 10 +gamma_max = 5 enable_emhd_limits = true -archive_parameters = true verbose = 1 extra_checks = 1 flag_verbose = 0 diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index c29c1719..8484cc52 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -euo pipefail +#set -euo pipefail BASE=../.. @@ -32,9 +32,9 @@ conv_2d() { # 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 # Just one default mode -ALL_RES="32,64,128" +ALL_RES="16,32,64,128" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "EMHD mode in 2D, WENO5" -ALL_RES="32,64,128,256" +ALL_RES="16,32,64,128,256" conv_2d emhd2d_mc GRMHD/reconstruction=linear_mc "EMHD mode in 2D, linear/MC reconstruction" # Test that higher-order terms don't mess anything up diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index 4c5ea46e..663c11ba 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -31,8 +31,8 @@ conv_3d() { fi } conv_2d() { - ALL_RES="16,24,32,48,64,96,128,256,512" - for res in 16 24 32 48 64 96 128 256 512 + ALL_RES="16,24,32,48,64,96,128,256" + for res in 16 24 32 48 64 96 128 256 do # Four blocks half=$(( $res / 2 )) From d258c10357f3e6a2445db662c151c6d3fd7dfbaa Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 11 May 2023 12:01:12 -0500 Subject: [PATCH 070/219] Switch to Parthenon with Face field support, make necessary naming changes --- .gitmodules | 2 +- external/parthenon | 2 +- kharma/b_cleanup/b_cleanup.cpp | 2 +- .../b_flux_ct/{seed_B_ct.cpp => seed_B_flux_ct.cpp} | 4 ++-- .../b_flux_ct/{seed_B_ct.hpp => seed_B_flux_ct.hpp} | 0 kharma/driver/imex_step.cpp | 11 +++++------ kharma/driver/kharma_driver.cpp | 2 +- kharma/driver/kharma_step.cpp | 11 +++++------ kharma/prob/post_initialize.cpp | 2 +- 9 files changed, 17 insertions(+), 19 deletions(-) rename kharma/b_flux_ct/{seed_B_ct.cpp => seed_B_flux_ct.cpp} (99%) rename kharma/b_flux_ct/{seed_B_ct.hpp => seed_B_flux_ct.hpp} (100%) diff --git a/.gitmodules b/.gitmodules index d5ec6b1b..4f3484cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "external/parthenon"] path = external/parthenon url = https://github.com/parthenon-hpc-lab/parthenon.git - branch = bprather/backport-bicgstab + branch = bprather/reqs-for-ct [submodule "external/variant"] path = external/variant url = https://github.com/mpark/variant.git diff --git a/external/parthenon b/external/parthenon index de25712e..f80cdce7 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit de25712e6f24b15ae2d1b1a8fc2db851b633b3a6 +Subproject commit f80cdce71dbf35cd463b0947f6d4e3f7e50ea088 diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index c4428b30..d4a5b9ea 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -294,7 +294,7 @@ TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) for (auto& pmb : blocks) { auto rc_s = pmb->meshblock_data.Get(); for (auto varlabel : {"pk0", "res0", "temp0", "divB_RHS", "p"}) { - if (rc_s->HasCellVariable(varlabel)) + if (rc_s->HasVariable(varlabel)) rc_s->Remove(varlabel); } } diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_flux_ct.cpp similarity index 99% rename from kharma/b_flux_ct/seed_B_ct.cpp rename to kharma/b_flux_ct/seed_B_flux_ct.cpp index ded91bbb..665e0120 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_flux_ct.cpp @@ -1,5 +1,5 @@ /* - * File: seed_B_ct.cpp + * File: seed_B_flux_ct.cpp * * BSD 3-Clause License * @@ -34,7 +34,7 @@ // Seed a torus of some type with a magnetic field according to its density -#include "seed_B_ct.hpp" +#include "seed_B_flux_ct.hpp" #include "b_field_tools.hpp" #include "b_flux_ct.hpp" diff --git a/kharma/b_flux_ct/seed_B_ct.hpp b/kharma/b_flux_ct/seed_B_flux_ct.hpp similarity index 100% rename from kharma/b_flux_ct/seed_B_ct.hpp rename to kharma/b_flux_ct/seed_B_flux_ct.hpp diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index db731188..ea87e6b8 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -127,11 +127,10 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta std::shared_ptr> &md_solver = (use_implicit) ? pmesh->mesh_data.GetOrAdd("solver", i) : md_sub_step_final; // Start receiving flux corrections and ghost cells - namespace cb = parthenon::cell_centered_bvars; - auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; if (pmesh->multilevel) - t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face // This reconstructs the primitives (P) at faces and uses them to calculate fluxes @@ -142,9 +141,9 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; if (pmesh->multilevel) { - tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 6f9608dc..30ca11d5 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -163,7 +163,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: t_start_sync = t_ptou_final; } - auto t_sync_done = parthenon::cell_centered_bvars::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); + auto t_sync_done = parthenon::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); auto t_bounds = t_sync_done; // TODO(BSP) careful about how AMR interacts with below diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 30a1b398..d2c0d8f0 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -124,11 +124,10 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); // Start receiving flux corrections and ghost cells - namespace cb = parthenon::cell_centered_bvars; - auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; if (pmesh->multilevel) - t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face // This reconstructs the primitives (P) at faces and uses them to calculate fluxes @@ -139,9 +138,9 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; if (pmesh->multilevel) { - tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 2fddaa30..7469380f 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -48,7 +48,7 @@ #include "reductions.hpp" #include "types.hpp" -#include "seed_B_ct.hpp" +#include "seed_B_flux_ct.hpp" #include "seed_B_cd.hpp" /** From 694ce8960485cdae90ce1f5ac4c793fee4d6af45 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 4 May 2023 14:54:52 +0000 Subject: [PATCH 071/219] Merge flux kernel split from performance branch --- bin/nvcc_wrapper | 1 + kharma/b_flux_ct/b_flux_ct.cpp | 3 +- kharma/debug.cpp | 9 +- kharma/decs.hpp | 4 +- kharma/driver/kharma_driver.cpp | 15 ++- kharma/emhd/emhd.hpp | 38 +++++- kharma/flux/flux.cpp | 46 ++++++- kharma/flux/flux.hpp | 2 + kharma/flux/flux_functions.hpp | 13 +- kharma/flux/get_flux.hpp | 195 ++++++++++++++++------------- kharma/grmhd/grmhd.cpp | 14 +-- kharma/grmhd/grmhd_functions.hpp | 12 +- kharma/grmhd/grmhd_reductions.hpp | 6 +- kharma/kharma.cpp | 4 +- kharma/reconstruction.hpp | 108 ++++++++-------- scripts/batch/scaling_polaris.qsub | 9 +- 16 files changed, 294 insertions(+), 185 deletions(-) diff --git a/bin/nvcc_wrapper b/bin/nvcc_wrapper index 5570c45b..fc7e27f7 100755 --- a/bin/nvcc_wrapper +++ b/bin/nvcc_wrapper @@ -44,6 +44,7 @@ xcompiler_args="$NVCC_WRAPPER_HOST_EXTRA_FLAGS" # Cuda (NVCC) only arguments cuda_args="-allow-unsupported-compiler --expt-relaxed-constexpr $NVCC_WRAPPER_CUDA_EXTRA_FLAGS" +# --resource-usage # Arguments for both NVCC and Host compiler shared_args="" diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 604768b2..778fd2e3 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -104,7 +104,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

()); // FIELDS - + // Vector size: 3x[grid shape] std::vector s_vector({NVEC}); // Mark if we're evolving implicitly @@ -145,6 +145,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

BlockUserWorkBeforeOutput = B_FluxCT::FillOutput; m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); diff --git a/kharma/debug.cpp b/kharma/debug.cpp index 4701c1ed..33fab669 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -47,13 +47,14 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) // TODO verbose option? // Pack variables - auto& ctop = md->PackVariables(std::vector{"ctop"}); + auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); + auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); // Get sizes IndexRange ib = md->GetBoundsI(IndexDomain::interior); IndexRange jb = md->GetBoundsJ(IndexDomain::interior); IndexRange kb = md->GetBoundsK(IndexDomain::interior); - IndexRange block = IndexRange{0, ctop.GetDim(5) - 1}; + IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; // TODO these two kernels can be one with some Kokkos magic int nzero = 0, nnan = 0; @@ -61,14 +62,14 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) Kokkos::Sum nan_reducer(nnan); pmb0->par_reduce("ctop_zeros", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (ctop(b, dir-1, k, j, i) <= 0.) { + if (m::max(cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i)) <= 0.) { ++local_result; } } , zero_reducer); pmb0->par_reduce("ctop_nans", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (m::isnan(ctop(b, dir-1, k, j, i))) { + if (m::isnan(m::max(cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i)))) { ++local_result; printf("ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT } diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 615d5009..689b86cc 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -53,9 +53,9 @@ // Libraries I need directly #include "Kokkos_Core.hpp" -#if 0 +#if 1 // Resolve math functions to new Kokkos versions. Faster, maybe -namespace m = Kokkos::Experimental; +namespace m = Kokkos; #else // Resolve to standard library namespace m = std; diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 30ca11d5..142de866 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -239,11 +239,11 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); break; - case RType::linear_vl: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); - break; + // case RType::linear_vl: + // t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); + // t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); + // t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + // break; case RType::weno5: t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); @@ -259,9 +259,8 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); break; - case RType::ppm: - case RType::mp5: - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl + default: + std::cerr << "Reconstruction type not supported! Main supported reconstructions:" << std::endl << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 81e622c0..2ff94dce 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -39,6 +39,11 @@ using namespace parthenon; +// Always disabled when implicit solver is disabled +#if DISABLE_IMPLICIT +#define DISABLE_EMHD 1 +#endif + /** * This physics package implements the Extended GRMHD "EGRMHD" scheme of Chandra et al. 2015, * First implemented in GRIM, of Chandra et al. 2017. @@ -101,10 +106,38 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt); */ void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin); +#if DISABLE_EMHD + +template +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, + const EMHD_parameters& emhd_params, const Real& gam, + const int& j, const int& i, + Real& tau, Real& chi_e, Real& nu_e) {} + +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, + const EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, + Real& tau, Real& chi_e, Real& nu_e) {} + +KOKKOS_INLINE_FUNCTION void set_parameters_init(const GRCoordinates& G, const Real& rho, const Real& u, + const EMHD_parameters& emhd_params, const Real& gam, + const int& k, const int& j, const int& i, + Real& tau, Real& chi_e, Real& nu_e) {} + +KOKKOS_INLINE_FUNCTION void calc_tensor(const Real& rho, const Real& u, const Real& pgas, + const EMHD::EMHD_parameters& emhd_params, + const Real& q, const Real& dP, + const FourVectors& D, const int& dir, + Real emhd[GR_DIM]) {} + +KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Real& dP_tilde, + const Real& rho, const Real& Theta, const Real& cs2, + const EMHD_parameters& emhd_params, Real& q, Real& dP) {} + +#else + /** * Set chi, nu, tau. Problem dependent - * - * TODO Local & Global, when we're sure */ template KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, @@ -399,5 +432,6 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea } } } +#endif } // namespace EMHD diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 8cf0619f..8df260fb 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -41,6 +41,49 @@ using namespace parthenon; // GetFlux is in the header file get_flux.hpp, as it is templated on reconstruction scheme and flux direction +std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + Flag("Initializing Flux"); + auto pkg = std::make_shared("Flux"); + Params ¶ms = pkg->AllParams(); + + // We can't use GetVariablesByFlag yet, so walk through and count manually + int nvar = 0; + for (auto pkg : packages->AllPackages()) { + for (auto field : pkg.second->AllFields()) { + // Specifically ignore the B_Cleanup variables, we don't handle their boundary conditions + if (field.second.IsSet(Metadata::WithFluxes)) { + if (field.second.Shape().size() < 1) { + nvar += 1; + } else { + nvar += field.second.Shape()[0]; + } + } + } + } + std::vector s_flux({nvar}); + std::vector flags_temp = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; + Metadata m = Metadata(flags_temp, s_flux); + pkg->AddField("Flux.Pr", m); + pkg->AddField("Flux.Pl", m); + pkg->AddField("Flux.Ur", m); + pkg->AddField("Flux.Ul", m); + pkg->AddField("Flux.Fr", m); + pkg->AddField("Flux.Fl", m); + + std::vector s_vec({NVEC}); + m = Metadata(flags_temp, s_vec); + pkg->AddField("Flux.cmax", m); + pkg->AddField("Flux.cmin", m); + + // Velocities, for upwinding later + //pkg->AddField("Flux.vr", m); + //pkg->AddField("Flux.vl", m); + + Flag("Initialized"); + return pkg; +} + TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag(rc, "Getting conserved GRMHD variables"); @@ -72,7 +115,6 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool } ); - Flag(rc, "Got conserved variables"); return TaskStatus::complete; } @@ -221,7 +263,7 @@ void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) Real Tmu[GR_DIM] = {0}; Real new_du[GR_DIM] = {0}; for (int mu = 0; mu < GR_DIM; ++mu) { - Flux::calc_tensor(G, P(b), m_p, D, emhd_params, gam, k, j, i, mu, Tmu); + Flux::calc_tensor(P(b), m_p, D, emhd_params, gam, k, j, i, mu, Tmu); for (int nu = 0; nu < GR_DIM; ++nu) { // Contract mhd stress tensor with connection, and multiply by metric determinant for (int lam = 0; lam < GR_DIM; ++lam) { diff --git a/kharma/flux/flux.hpp b/kharma/flux/flux.hpp index 1760a75d..289aa43d 100644 --- a/kharma/flux/flux.hpp +++ b/kharma/flux/flux.hpp @@ -46,6 +46,8 @@ namespace Flux { +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + /** * Add the geometric source term present in the covariant derivative of the stress-energy tensor, * S_nu = sqrt(-g) T^kap_lam Gamma^lam_nu_kap diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index 1c1304e2..afc4e485 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -51,7 +51,7 @@ namespace Flux // TODO Q > 0 != emhd_enabled. Store enablement in emhd_params since we need it anyway template -KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, +KOKKOS_INLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& dir, Real T[GR_DIM]) { @@ -79,7 +79,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, } template -KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, +KOKKOS_INLINE_FUNCTION void calc_tensor(const Global& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const int& dir, Real T[GR_DIM]) @@ -139,7 +139,8 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, // Stress-energy tensor Real T[GR_DIM]; - calc_tensor(G, P, m_p, D, emhd_params, gam, dir, T); + //calc_tensor(P, m_p, D, emhd_params, gam, dir, T); + GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); flux(m_u.UU) = T[0] * gdet + flux(m_u.RHO); flux(m_u.U1) = T[1] * gdet; flux(m_u.U2) = T[2] * gdet; @@ -190,7 +191,6 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, if (m_p.K_SHARMA >= 0) flux(m_u.K_SHARMA) = flux(m_u.RHO) * P(m_p.K_SHARMA); } - } template @@ -204,7 +204,8 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - calc_tensor(G, P, m_p, D, emhd_params, gam, k, j, i, dir, T); + //calc_tensor(P, m_p, D, emhd_params, gam, k, j, i, dir, T); + GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); flux(m_u.U1, k, j, i) = T[1] * gdet; flux(m_u.U2, k, j, i) = T[2] * gdet; @@ -271,7 +272,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux_mhd(const GRCoordinates& G, const Globa flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - calc_tensor(G, P, m_p, D, emhd_params, gam, k, j, i, dir, T); + calc_tensor(P, m_p, D, emhd_params, gam, k, j, i, dir, T); flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); flux(m_u.U1, k, j, i) = T[1] * gdet; flux(m_u.U2, k, j, i) = T[2] * gdet; diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index b00f0558..92856c78 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -45,8 +45,8 @@ namespace Flux { * * Memory-wise, this fills the "flux" portions of the "conserved" fields. These will be used * over the course of the step to calculate an update to the zone-centered values. - * This function also fills the "ctop" vector with the signal speed mhd_vchar, - * used to estimate the timestep later. + * This function also fills the "Flux.cmax" & "Flux.cmin" vectors with the signal speeds, + * and potentially the "Flux.vl" and "Flux.vr" vectors with the fluid velocities * * This function is defined in the header because it is templated on the reconstruction scheme and * direction. Since there are only a few reconstruction schemes supported, and we will only ever @@ -98,18 +98,26 @@ inline TaskStatus GetFlux(MeshData *md) // Pack variables. Keep ctop separate PackIndexMap prims_map, cons_map; - const auto& ctop = md->PackVariables(std::vector{"ctop"}); + const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); + const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); //Flag(md, "Packed variables"); + const auto& Pl_all = md->PackVariables(std::vector{"Flux.Pl"}); + const auto& Pr_all = md->PackVariables(std::vector{"Flux.Pr"}); + const auto& Ul_all = md->PackVariables(std::vector{"Flux.Ul"}); + const auto& Ur_all = md->PackVariables(std::vector{"Flux.Ur"}); + const auto& Fl_all = md->PackVariables(std::vector{"Flux.Fl"}); + const auto& Fr_all = md->PackVariables(std::vector{"Flux.Fr"}); + // Get sizes const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); const IndexRange ib = md->GetBoundsI(IndexDomain::interior); const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); const IndexRange kb = md->GetBoundsK(IndexDomain::interior); - const IndexRange block = IndexRange{0, ctop.GetDim(5) - 1}; + const IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; const int nvar = U_all.GetDim(4); // 1-zone halo in nontrivial dimensions // We leave is/ie, js/je, ks/ke with their usual definitions for consistency, and define @@ -122,38 +130,29 @@ inline TaskStatus GetFlux(MeshData *md) // Allocate scratch space const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM const size_t var_size_in_bytes = parthenon::ScratchPad2D::shmem_size(nvar, n1); - const size_t speed_size_in_bytes = parthenon::ScratchPad2D::shmem_size(1, n1); // Allocate enough to cache prims, conserved, and fluxes, for left and right faces, // plus temporaries inside reconstruction (most use 1, WENO5 uses none, linear_vl uses a bunch) - // Then add cmax and cmin! - const size_t total_scratch_bytes = (6 + 1*(Recon != KReconstruction::Type::weno5) + - 4*(Recon == KReconstruction::Type::linear_vl)) * var_size_in_bytes - + 2 * speed_size_in_bytes; + const size_t recon_scratch_bytes = (2 + 1*(Recon != KReconstruction::Type::weno5) + + 4*(Recon == KReconstruction::Type::linear_vl)) * var_size_in_bytes; + const size_t flux_scratch_bytes = 3 * var_size_in_bytes; - Flag(md, "Flux kernel"); + Flag(md, "Recon kernel"); // This isn't a pmb0->par_for_outer because Parthenon's current overloaded definitions // do not accept three pairs of bounds, which we need in order to iterate over blocks - parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux", pmb0->exec_space, - total_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, + parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_recon", pmb0->exec_space, + recon_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { const auto& G = U_all.GetCoords(b); ScratchPad2D Pl_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Pr_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D Ul_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D Ur_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D Fl_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad2D Fr_s(member.team_scratch(scratch_level), nvar, n1); - ScratchPad1D cmax(member.team_scratch(scratch_level), n1); - ScratchPad1D cmin(member.team_scratch(scratch_level), n1); // Wrapper for a big switch statement between reconstruction schemes. Possibly slow. // This function is generally a lot of if statements - KReconstruction::reconstruct(member, G, P_all(b), k, j, il.s, il.e, Pl_s, Pr_s); + KReconstruction::reconstruct(member, P_all(b), k, j, il.s, il.e, Pl_s, Pr_s); // Sync all threads in the team so that scratch memory is consistent member.team_barrier(); - // Calculate conserved fluxes at centers & faces parthenon::par_for_inner(member, il.s, il.e, [&](const int& i) { auto Pl = Kokkos::subview(Pl_s, Kokkos::ALL(), i); @@ -164,19 +163,48 @@ inline TaskStatus GetFlux(MeshData *md) Floors::apply_geo_floors(G, Pl, m_p, gam, j, i, floors, loc); Floors::apply_geo_floors(G, Pr, m_p, gam, j, i, floors, loc); } -#if !FUSE_FLUX_KERNELS } ); member.team_barrier(); - // LEFT FACES, final ctop + // Copy out state (TODO(BSP) eliminate) + for (int p=0; p < nvar; ++p) { + parthenon::par_for_inner(member, il.s, il.e, + [&](const int& i) { + Pl_all(b, p, k, j, i) = Pl_s(p, i); + Pr_all(b, p, k, j, i) = Pr_s(p, i); + } + ); + } + + } + ); + + Flag(md, "PtoU Left"); + parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_left", pmb0->exec_space, + flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, + KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { + const auto& G = U_all.GetCoords(b); + ScratchPad2D Pl_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D Ul_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D Fl_s(member.team_scratch(scratch_level), nvar, n1); + + // Copy in state (TODO(BSP) eliminate) + for (int p=0; p < nvar; ++p) { + parthenon::par_for_inner(member, il.s, il.e, + [&](const int& i) { + Pl_s(p, i) = Pl_all(b, p, k, j, i); + } + ); + } + member.team_barrier(); + + // LEFT FACES parthenon::par_for_inner(member, il.s, il.e, [&](const int& i) { auto Pl = Kokkos::subview(Pl_s, Kokkos::ALL(), i); -#endif auto Ul = Kokkos::subview(Ul_s, Kokkos::ALL(), i); auto Fl = Kokkos::subview(Fl_s, Kokkos::ALL(), i); - // LR -> flux // Declare temporary vectors FourVectors Dtmp; @@ -189,26 +217,53 @@ inline TaskStatus GetFlux(MeshData *md) Real cmaxL, cminL; Flux::vchar(G, Pl, m_p, Dtmp, gam, emhd_params, k, j, i, loc, dir, cmaxL, cminL); -#if !FUSE_FLUX_KERNELS // Record speeds - cmax(i) = m::max(0., cmaxL); - cmin(i) = m::max(0., -cminL); + cmax(b, dir-1, k, j, i) = m::max(0., cmaxL); + cmin(b, dir-1, k, j, i) = m::max(0., -cminL); } ); member.team_barrier(); - // RIGHT FACES, final ctop + // Copy out state + for (int p=0; p < nvar; ++p) { + parthenon::par_for_inner(member, il.s, il.e, + [&](const int& i) { + Ul_all(b, p, k, j, i) = Ul_s(p, i); + Fl_all(b, p, k, j, i) = Fl_s(p, i); + } + ); + } + } + ); + + Flag(md, "PtoU Right"); + parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_right", pmb0->exec_space, + flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, + KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { + const auto& G = U_all.GetCoords(b); + ScratchPad2D Pr_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D Ur_s(member.team_scratch(scratch_level), nvar, n1); + ScratchPad2D Fr_s(member.team_scratch(scratch_level), nvar, n1); + + // Copy in state (TODO(BSP) eliminate) + for (int p=0; p < nvar; ++p) { + parthenon::par_for_inner(member, il.s, il.e, + [&](const int& i) { + Pr_s(p, i) = Pr_all(b, p, k, j, i); + } + ); + } + member.team_barrier(); + + // RIGHT FACES, finalize signal speed parthenon::par_for_inner(member, il.s, il.e, [&](const int& i) { - // LR -> flux - // Declare temporary vectors - FourVectors Dtmp; auto Pr = Kokkos::subview(Pr_s, Kokkos::ALL(), i); -#endif auto Ur = Kokkos::subview(Ur_s, Kokkos::ALL(), i); auto Fr = Kokkos::subview(Fr_s, Kokkos::ALL(), i); + // Declare temporary vectors + FourVectors Dtmp; // Right - // TODO GRMHD/GRHD versions of this GRMHD::calc_4vecs(G, Pr, m_p, j, i, loc, Dtmp); Flux::prim_to_flux(G, Pr, m_p, Dtmp, emhd_params, gam, j, i, 0, Ur, m_u, loc); Flux::prim_to_flux(G, Pr, m_p, Dtmp, emhd_params, gam, j, i, dir, Fr, m_u, loc); @@ -217,64 +272,36 @@ inline TaskStatus GetFlux(MeshData *md) Real cmaxR, cminR; Flux::vchar(G, Pr, m_p, Dtmp, gam, emhd_params, k, j, i, loc, dir, cmaxR, cminR); -#if FUSE_FLUX_KERNELS - // Calculate cmax/min from local variables - cmax(i) = m::abs(m::max(cmaxL, cmaxR)); - cmin(i) = m::abs(m::max(-cminL, -cminR)); - - if (use_hlle) { - for (int p=0; p < nvar; ++p) - U_all(b).flux(dir, p, k, j, i) = hlle(Fl(p), Fr(p), cmax(i), cmin(i), Ul(p), Ur(p)); - } else { - for (int p=0; p < nvar; ++p) - U_all(b).flux(dir, p, k, j, i) = llf(Fl(p), Fr(p), cmax(i), cmin(i), Ul(p), Ur(p)); - } - if (use_b_cd) { - // The unphysical variable psi and its corrections can propagate at the max speed - // for the stepsize, rather than the sound speed - // Since the speeds are the same it will always correspond to the LLF flux - U_all(b).flux(dir, m_u.PSI, k, j, i) = llf(Fl(m_u.PSI), Fr(m_u.PSI), ctop_max, ctop_max, Ul(m_u.PSI), Ur(m_u.PSI)); - U_all(b).flux(dir, m_u.B1+dir-1, k, j, i) = llf(Fl(m_u.B1+dir-1), Fr(m_u.B1+dir-1), ctop_max, ctop_max, Ul(m_u.B1+dir-1), Ur(m_u.B1+dir-1)); - } -#else // Calculate cmax/min based on comparison with cached values - cmax(i) = m::abs(m::max(cmax(i), cmaxR)); - cmin(i) = m::abs(m::max(cmin(i), -cminR)); -#endif - // TODO is it faster to write ctop elsewhere? - ctop(b, dir-1, k, j, i) = m::max(cmax(i), cmin(i)); + cmax(b, dir-1, k, j, i) = m::abs(m::max(cmax(b, dir-1, k, j, i), cmaxR)); + cmin(b, dir-1, k, j, i) = m::abs(m::max(cmin(b, dir-1, k, j, i), -cminR)); } ); member.team_barrier(); -#if !FUSE_FLUX_KERNELS - // Apply what we've calculated + // Copy out state for (int p=0; p < nvar; ++p) { - if (use_b_cd && (p == m_u.PSI || p == m_u.B1+dir-1)) { - // The unphysical variable psi and its corrections can propagate at the max speed for the stepsize, rather than the sound speed - // Since the speeds are the same it will always correspond to the LLF flux - parthenon::par_for_inner(member, il.s, il.e, - [&](const int& i) { - U_all(b).flux(dir, p, k, j, i) = llf(Fl_s(p,i), Fr_s(p,i), ctop_max, ctop_max, Ul_s(p,i), Ur_s(p,i)); - } - ); - } else if (use_hlle) { - // Option to try HLLE fluxes for everything else - parthenon::par_for_inner(member, il.s, il.e, - [&](const int& i) { - U_all(b).flux(dir, p, k, j, i) = hlle(Fl_s(p,i), Fr_s(p,i), cmax(i), cmin(i), Ul_s(p,i), Ur_s(p,i)); - } - ); - } else { - // Or LLF, probably safest option - parthenon::par_for_inner(member, il.s, il.e, - [&](const int& i) { - U_all(b).flux(dir, p, k, j, i) = llf(Fl_s(p,i), Fr_s(p,i), cmax(i), cmin(i), Ul_s(p,i), Ur_s(p,i)); - } - ); - } + parthenon::par_for_inner(member, il.s, il.e, + [&](const int& i) { + Ur_all(b, p, k, j, i) = Ur_s(p, i); + Fr_all(b, p, k, j, i) = Fr_s(p, i); + } + ); } -#endif + + } + ); + + Flag(md, "Riemann kernel"); + pmb0->par_for("flux_solve", block.s, block.e, 0, nvar-1, kl.s, kl.e, jl.s, jl.e, il.s, il.e, + KOKKOS_LAMBDA(const int& b, const int& p, const int& k, const int& j, const int& i) { + // Apply what we've calculated + // TODO OTHER FLUXES AGAIN + U_all(b).flux(dir, p, k, j, i) = llf(Fl_all(b, p, k, j, i), Fr_all(b, p, k, j, i), + cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i), + Ul_all(b, p, k, j, i), Ur_all(b, p, k, j, i)); + + } ); diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index e0ed9945..3a5bdf2f 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -190,11 +190,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.uvec", m); - // Maximum signal speed (magnitude). - // Needs to be cached from flux updates for calculating the timestep later - m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); - pkg->AddField("ctop", m); - // No magnetic fields here. KHARMA should operate fine in GRHD without them, // so they are allocated only by B field packages. @@ -227,7 +222,8 @@ Real EstimateTimestep(MeshBlockData *rc) IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); const auto& G = pmb->coords; - auto& ctop = rc->Get("ctop").data; + auto& cmax = rc->Get("Flux.cmax").data; + auto& cmin = rc->Get("Flux.cmin").data; // TODO: move timestep limiter into an override of SetGlobalTimestep // TODO: keep location of the max, or be able to look it up in diagnostics @@ -266,9 +262,9 @@ Real EstimateTimestep(MeshBlockData *rc) pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int k, const int j, const int i, typename Kokkos::MinMax::value_type &lminmax) { - double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / ctop(0, k, j, i)) + - 1 / (G.Dxc<2>(j) / ctop(1, k, j, i)) + - 1 / (G.Dxc<3>(k) / ctop(2, k, j, i))); + double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / m::max(cmax(0, k, j, i), cmin(0, k, j, i))) + + 1 / (G.Dxc<2>(j) / m::max(cmax(1, k, j, i), cmin(1, k, j, i))) + + 1 / (G.Dxc<3>(k) / m::max(cmax(2, k, j, i), cmin(2, k, j, i)))); // Effective "max speed" used for the timestep double ctop_max_zone = m::min(G.Dxc<1>(i), m::min(G.Dxc<2>(j), G.Dxc<3>(k))) / ndt_zone; diff --git a/kharma/grmhd/grmhd_functions.hpp b/kharma/grmhd/grmhd_functions.hpp index f62027e6..8a8855e1 100644 --- a/kharma/grmhd/grmhd_functions.hpp +++ b/kharma/grmhd/grmhd_functions.hpp @@ -227,19 +227,21 @@ KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Local& P, c const int& j, const int& i, const Loci loc, FourVectors& D) { const Real gamma = lorentz_calc(G, P, m, j, i, loc); - const Real alpha = 1. / m::sqrt(-G.gcon(loc, j, i, 0, 0)); + const Real inv_alpha = m::sqrt(-G.gcon(loc, j, i, 0, 0)); - D.ucon[0] = gamma / alpha; - VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); + D.ucon[0] = gamma * inv_alpha; + VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma / inv_alpha * G.gcon(loc, j, i, 0, v+1); - G.lower(D.ucon, D.ucov, 0, j, i, loc); + //G.lower(D.ucon, D.ucov, 0, j, i, loc); + DLOOP2 D.ucov[mu] += G.gcov(loc, j, i, mu, nu) * D.ucon[nu]; if (m.B1 >= 0) { D.bcon[0] = 0; VLOOP D.bcon[0] += P(m.B1 + v) * D.ucov[v+1]; VLOOP D.bcon[v+1] = (P(m.B1 + v) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; - G.lower(D.bcon, D.bcov, 0, j, i, loc); + //G.lower(D.bcon, D.bcov, 0, j, i, loc); + DLOOP2 D.bcov[mu] += G.gcov(loc, j, i, mu, nu) * D.bcon[nu]; } else { DLOOP1 D.bcon[mu] = D.bcov[mu] = 0.; } diff --git a/kharma/grmhd/grmhd_reductions.hpp b/kharma/grmhd/grmhd_reductions.hpp index 169a06d2..246ab4bd 100644 --- a/kharma/grmhd/grmhd_reductions.hpp +++ b/kharma/grmhd/grmhd_reductions.hpp @@ -59,7 +59,7 @@ KOKKOS_INLINE_FUNCTION Real edot(REDUCE_FUNCTION_ARGS_EH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); // \dot{E} == \int - T^1_0 * gdet * dx2 * dx3 return -T1[X0DIR] * G.gdet(Loci::center, j, i); } @@ -68,7 +68,7 @@ KOKKOS_INLINE_FUNCTION Real ldot(REDUCE_FUNCTION_ARGS_EH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); // \dot{L} == \int T^1_3 * gdet * dx2 * dx3 return T1[X3DIR] * G.gdet(Loci::center, j, i); } @@ -121,7 +121,7 @@ KOKKOS_INLINE_FUNCTION Real jet_lum(REDUCE_FUNCTION_ARGS_MESH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(G, P(b), m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(P(b), m_p, Dtmp, gam, k, j, i, X1DIR, T1); // If sigma > 1... if ((dot(Dtmp.bcon, Dtmp.bcov) / P(b, m_p.RHO, k, j, i)) > 1.) { // Energy flux, like at EH. 2D integral jacobian, so we have to take X1 off of auto-applied dV diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 80e63748..a08d6475 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -344,8 +344,10 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // Execute the whole collection (just in case we do something fancy?) while (!tr.Execute()); // TODO this will inf-loop on error - // The boundaries package may need to know variable counts for allocating memory, + // The Flux package needs to know variable counts for allocating memory, // so we initialize it after the main dependency tree + KHARMA::AddPackage(packages, Flux::Initialize, pin.get()); + // Same with boundaries // TODO only init if at least one boundary is "user" KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 9a220d4b..5e0dc5ac 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -351,17 +351,17 @@ KOKKOS_INLINE_FUNCTION void WENO5X3r(parthenon::team_mbr_t const &member, const * This is basically a compile-time 'if' or 'switch' statement, where all the options get generated * at compile-time (see driver.cpp for the different instantiations) * - * We could template these directly on the function if Parthenon could agree on what argument list to use + * We could template these directly on the function if Partheconst GRCoordinates& G, non could agree on what argument list to use * Better than a runtime decision per outer loop I think */ template -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const GRCoordinates& G, const VariablePack &P, +KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) {} // DONOR CELL template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -369,7 +369,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -379,7 +379,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -388,53 +388,53 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team DonorCellX3(member, k, j, is_l, ie_l, P, q_u, qr); } // LINEAR W/VAN LEER -template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, - const int& k, const int& j, const int& is_l, const int& ie_l, - ScratchPad2D ql, ScratchPad2D qr) -{ - // Extra scratch space for Parthenon's VL limiter stuff - ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - PiecewiseLinearX1(member, k, j, is_l, ie_l, G, P, ql, qr, qc, dql, dqr, dqm); -} -template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, - const int& k, const int& j, const int& is_l, const int& ie_l, - ScratchPad2D ql, ScratchPad2D qr) -{ - // Extra scratch space for Parthenon's VL limiter stuff - ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D q_u(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - PiecewiseLinearX2(member, k, j - 1, is_l, ie_l, G, P, ql, q_u, qc, dql, dqr, dqm); - PiecewiseLinearX2(member, k, j, is_l, ie_l, G, P, q_u, qr, qc, dql, dqr, dqm); -} -template <> -KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, - const int& k, const int& j, const int& is_l, const int& ie_l, - ScratchPad2D ql, ScratchPad2D qr) -{ - // Extra scratch space for Parthenon's VL limiter stuff - ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - ScratchPad2D q_u(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); - PiecewiseLinearX3(member, k - 1, j, is_l, ie_l, G, P, ql, q_u, qc, dql, dqr, dqm); - PiecewiseLinearX3(member, k, j, is_l, ie_l, G, P, q_u, qr, qc, dql, dqr, dqm); -} +// template <> +// KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +// const VariablePack &P, +// const int& k, const int& j, const int& is_l, const int& ie_l, +// ScratchPad2D ql, ScratchPad2D qr) +// { +// // Extra scratch space for Parthenon's VL limiter stuff +// ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// PiecewiseLinearX1(member, k, j, is_l, ie_l, G, P, ql, qr, qc, dql, dqr, dqm); +// } +// template <> +// KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +// const VariablePack &P, +// const int& k, const int& j, const int& is_l, const int& ie_l, +// ScratchPad2D ql, ScratchPad2D qr) +// { +// // Extra scratch space for Parthenon's VL limiter stuff +// ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D q_u(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// PiecewiseLinearX2(member, k, j - 1, is_l, ie_l, G, P, ql, q_u, qc, dql, dqr, dqm); +// PiecewiseLinearX2(member, k, j, is_l, ie_l, G, P, q_u, qr, qc, dql, dqr, dqm); +// } +// template <> +// KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, +// const VariablePack &P, +// const int& k, const int& j, const int& is_l, const int& ie_l, +// ScratchPad2D ql, ScratchPad2D qr) +// { +// // Extra scratch space for Parthenon's VL limiter stuff +// ScratchPad2D qc(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dql(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqr(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D dqm(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// ScratchPad2D q_u(member.team_scratch(1), P.GetDim(4), P.GetDim(1)); +// PiecewiseLinearX3(member, k - 1, j, is_l, ie_l, G, P, ql, q_u, qc, dql, dqr, dqm); +// PiecewiseLinearX3(member, k, j, is_l, ie_l, G, P, q_u, qr, qc, dql, dqr, dqm); +// } // LINEAR WITH MC template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -442,7 +442,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_ } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -452,7 +452,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_ } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -463,7 +463,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_ // WENO5 template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -471,7 +471,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_ } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -480,7 +480,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_ } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { diff --git a/scripts/batch/scaling_polaris.qsub b/scripts/batch/scaling_polaris.qsub index d17a6f85..efda6705 100755 --- a/scripts/batch/scaling_polaris.qsub +++ b/scripts/batch/scaling_polaris.qsub @@ -6,7 +6,8 @@ #PBS -N KHARMA #PBS -l select=128 #PBS -l walltime=1:00:00 -#PBS -q gpu-hackathon +# large queues: prod, large, backfill-large +#PBS -q prod #PBS -A gpu_hack #PBS -l filesystems=home:grand NNODES=`wc -l < $PBS_NODEFILE` @@ -77,8 +78,8 @@ if [[ $DO_STRONG == "true" ]]; then echo "cycle=100 Running ${size}x${size}x${size} cubed problem with KHARMA on $gpus GPUs (blocksize ${msize1}x${msize2}x${msize3})" - mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap \ - $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $KHARMA_DIR/bin/mpi_gpu_wrap \ + $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ parthenon/meshblock/nx1=$msize1 parthenon/meshblock/nx2=$msize2 parthenon/meshblock/nx3=$msize3 @@ -149,7 +150,7 @@ if [[ $DO_WEAK == "true" ]]; then nblock=$(( $mul1 * $mul2 * $mul3 )) echo "cycle=100 Running $size per node problem with KHARMA on $gpus GPUs (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" - mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads ~/bin/mpi_gpu_wrap \ + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $KHARMA_DIR/bin/mpi_gpu_wrap \ $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size From d63f61a71c169a655d03339026980c90b94b5ca7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 4 May 2023 18:06:26 +0000 Subject: [PATCH 072/219] Try some modifications to WENO recon --- kharma/reconstruction.hpp | 85 ++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 9a220d4b..a8ffd88e 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -128,77 +128,90 @@ KOKKOS_INLINE_FUNCTION void weno5(const Real& x1, const Real& x2, const Real& x3 Real &lout, Real &rout) { // Smoothness indicators, T07 A18 or S11 8 - // TODO are small arrays really the play here? Should I further reduce cache by increasing flops? - Real beta[3], c1, c2; + Real tmp1, tmp2, tmp3, c1, c2; c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; + tmp1 = 1./(EPS + tmp1*tmp1); + tmp2 = 1./(EPS + tmp2*tmp2); + tmp3 = 1./(EPS + tmp3*tmp3); - const Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; - const Real Wr = wtr[0] + wtr[1] + wtr[2]; + const Real wtr1 = (1./16.) * tmp1; + const Real wtr2 = (5./8. ) * tmp2; + const Real wtr3 = (5./16.) * tmp3; + const Real Wr = wtr1 + wtr2 + wtr3; - const Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; - const Real Wl = wtl[0] + wtl[1] + wtl[2]; + const Real wtl1 = (1./16.) * tmp3; + const Real wtl2 = (5./8. ) * tmp2; + const Real wtl3 = (5./16.) * tmp1; + const Real Wl = wtl1 + wtl2 + wtl3; // S11 1, 2, 3 - lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + - ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl[1] / Wl) + - ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl[2] / Wl); - rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr[0] / Wr) + - ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr[1] / Wr) + - ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr[2] / Wr); + lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl1 / Wl) + + ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl2 / Wl) + + ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl3 / Wl); + rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr1 / Wr) + + ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr2 / Wr) + + ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr3 / Wr); } KOKKOS_INLINE_FUNCTION void weno5l(const Real x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &lout) { // Smoothness indicators, T07 A18 or S11 8 - Real beta[3], c1, c2; + Real tmp1, tmp2, tmp3, c1, c2; c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; + tmp1 = 1./(EPS + tmp1*tmp1); + tmp2 = 1./(EPS + tmp2*tmp2); + tmp3 = 1./(EPS + tmp3*tmp3); - const Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; - const Real Wl = wtl[0] + wtl[1] + wtl[2]; + const Real wtl1 = (1./16.) * tmp3; + const Real wtl2 = (5./8. ) * tmp2; + const Real wtl3 = (5./16.) * tmp1; + const Real Wl = wtl1 + wtl2 + wtl3; // S11 1, 2, 3 - lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + - ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl[1] / Wl) + - ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl[2] / Wl); + lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl1 / Wl) + + ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl2 / Wl) + + ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl3 / Wl); } KOKKOS_INLINE_FUNCTION void weno5r(const Real& x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &rout) { // Smoothness indicators, T07 A18 or S11 8 - Real beta[3], c1, c2; + Real tmp1, tmp2, tmp3, c1, c2; c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den[3] = {EPS + beta[0]*beta[0], EPS + beta[1]*beta[1], EPS + beta[2]*beta[2]}; + tmp1 = 1./(EPS + tmp1*tmp1); + tmp2 = 1./(EPS + tmp2*tmp2); + tmp3 = 1./(EPS + tmp3*tmp3); - const Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; - const Real Wr = wtr[0] + wtr[1] + wtr[2]; + const Real wtr1 = (1./16.) * tmp1; + const Real wtr2 = (5./8. ) * tmp2; + const Real wtr3 = (5./16.) * tmp3; + const Real Wr = wtr1 + wtr2 + wtr3; // S11 1, 2, 3 - rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr[0] / Wr) + - ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr[1] / Wr) + - ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr[2] / Wr); + rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr1 / Wr) + + ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr2 / Wr) + + ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr3 / Wr); } // Row-wise implementations From 74b655f000c2186e3ba7eb7ca17ba3847ae4265f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 12 May 2023 16:05:42 -0500 Subject: [PATCH 073/219] Touch-ups: reconstruction, EMHD. No big news --- kharma/emhd/emhd_limits.hpp | 2 +- kharma/flux/get_flux.hpp | 2 + kharma/reconstruction.hpp | 123 ++++++++++++++--------------- tests/conducting_atmosphere/run.sh | 48 +++++------ 4 files changed, 86 insertions(+), 89 deletions(-) diff --git a/kharma/emhd/emhd_limits.hpp b/kharma/emhd/emhd_limits.hpp index 0c8cf9e5..7815cbc8 100644 --- a/kharma/emhd/emhd_limits.hpp +++ b/kharma/emhd/emhd_limits.hpp @@ -88,7 +88,7 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons if (emhd_params.conduction) { - Real qmax = 1.07 * rho * m::pow(cs, 3.); + Real qmax = 1.07 * rho * cs*cs*cs; Real max_frac = m::max(m::abs(q) / qmax, 1.); if (fabs(q) / qmax > 1.) eflag |= HIT_Q_LIMIT; diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index b00f0558..a0b3e6fe 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -31,6 +31,8 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + #include "flux.hpp" #include "floors_functions.hpp" diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index a8ffd88e..232bf833 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -128,90 +128,85 @@ KOKKOS_INLINE_FUNCTION void weno5(const Real& x1, const Real& x2, const Real& x3 Real &lout, Real &rout) { // Smoothness indicators, T07 A18 or S11 8 - Real tmp1, tmp2, tmp3, c1, c2; - c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; + const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + + (1./4.)*SQR(x4 - x2); + const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + + (1./4.)*SQR(x5 - 4*x4 + 3*x3); // Nonlinear weights S11 9 - tmp1 = 1./(EPS + tmp1*tmp1); - tmp2 = 1./(EPS + tmp2*tmp2); - tmp3 = 1./(EPS + tmp3*tmp3); + const Real den_inv1 = 1./(EPS + beta1*beta1); + const Real den_inv2 = 1./(EPS + beta2*beta2); + const Real den_inv3 = 1./(EPS + beta3*beta3); - const Real wtr1 = (1./16.) * tmp1; - const Real wtr2 = (5./8. ) * tmp2; - const Real wtr3 = (5./16.) * tmp3; - const Real Wr = wtr1 + wtr2 + wtr3; + // S11 1, 2, 3 left + const Real wtl1 = 0.5 * den_inv3; + const Real wtl2 = 5 * den_inv2; + const Real wtl3 = 2.5 * den_inv1; + lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + + (-x4 + 6*x3 + 3*x2)*wtl2 + + (3*x3 + 6*x2 - x1)*wtl3) + / (8*(wtl1 + wtl2 + wtl3)); - const Real wtl1 = (1./16.) * tmp3; - const Real wtl2 = (5./8. ) * tmp2; - const Real wtl3 = (5./16.) * tmp1; - const Real Wl = wtl1 + wtl2 + wtl3; - - // S11 1, 2, 3 - lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl1 / Wl) + - ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl2 / Wl) + - ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl3 / Wl); - rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr1 / Wr) + - ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr2 / Wr) + - ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr3 / Wr); + // S11 1, 2, 3 right + const Real wtr1 = 0.5 * den_inv1; + const Real wtr2 = 5 * den_inv2; + const Real wtr3 = 2.5 * den_inv3; + rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + + (-x2 + 6*x3 + 3*x4)*wtr2 + + (3*x3 + 6*x4 - x5)*wtr3) + / (8*(wtr1 + wtr2 + wtr3)); } KOKKOS_INLINE_FUNCTION void weno5l(const Real x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &lout) { // Smoothness indicators, T07 A18 or S11 8 - Real tmp1, tmp2, tmp3, c1, c2; - c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; + const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + + (1./4.)*SQR(x4 - x2); + const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + + (1./4.)*SQR(x5 - 4*x4 + 3*x3); // Nonlinear weights S11 9 - tmp1 = 1./(EPS + tmp1*tmp1); - tmp2 = 1./(EPS + tmp2*tmp2); - tmp3 = 1./(EPS + tmp3*tmp3); - - const Real wtl1 = (1./16.) * tmp3; - const Real wtl2 = (5./8. ) * tmp2; - const Real wtl3 = (5./16.) * tmp1; - const Real Wl = wtl1 + wtl2 + wtl3; + const Real den_inv1 = 1./(EPS + beta1*beta1); + const Real den_inv2 = 1./(EPS + beta2*beta2); + const Real den_inv3 = 1./(EPS + beta3*beta3); - // S11 1, 2, 3 - lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl1 / Wl) + - ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl2 / Wl) + - ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl3 / Wl); + // S11 1, 2, 3 left + const Real wtl1 = 0.5 * den_inv3; + const Real wtl2 = 5 * den_inv2; + const Real wtl3 = 2.5 * den_inv1; + lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + + (-x4 + 6*x3 + 3*x2)*wtl2 + + (3*x3 + 6*x2 - x1)*wtl3) + / (8*(wtl1 + wtl2 + wtl3)); } KOKKOS_INLINE_FUNCTION void weno5r(const Real& x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &rout) { // Smoothness indicators, T07 A18 or S11 8 - Real tmp1, tmp2, tmp3, c1, c2; - c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; - tmp1 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; - tmp2 = (13./12.)*c1*c1 + (1./4.)*c2*c2; - c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; - tmp3 = (13./12.)*c1*c1 + (1./4.)*c2*c2; + const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + + (1./4.)*SQR(x4 - x2); + const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + + (1./4.)*SQR(x5 - 4*x4 + 3*x3); // Nonlinear weights S11 9 - tmp1 = 1./(EPS + tmp1*tmp1); - tmp2 = 1./(EPS + tmp2*tmp2); - tmp3 = 1./(EPS + tmp3*tmp3); - - const Real wtr1 = (1./16.) * tmp1; - const Real wtr2 = (5./8. ) * tmp2; - const Real wtr3 = (5./16.) * tmp3; - const Real Wr = wtr1 + wtr2 + wtr3; + const Real den_inv1 = 1./(EPS + beta1*beta1); + const Real den_inv2 = 1./(EPS + beta2*beta2); + const Real den_inv3 = 1./(EPS + beta3*beta3); - // S11 1, 2, 3 - rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr1 / Wr) + - ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr2 / Wr) + - ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr3 / Wr); + // S11 1, 2, 3 right + const Real wtr1 = 0.5 * den_inv1; + const Real wtr2 = 5 * den_inv2; + const Real wtr3 = 2.5 * den_inv3; + rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + + (-x2 + 6*x3 + 3*x4)*wtr2 + + (3*x3 + 6*x4 - x5)*wtr3) + / (8*(wtr1 + wtr2 + wtr3)); } // Row-wise implementations diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 47f30046..a717ab50 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -9,31 +9,31 @@ exit_code=0 # We'll use just 1 MPI rank to circumvent the somewhat annoying ODE initialization conv_2d() { - IFS=',' read -ra RES_LIST <<< "$ALL_RES" - for res in "${RES_LIST[@]}" - do - cp -r ${BASE}/kharma/prob/emhd/conducting_atmosphere_${res}_default/*txt ./ - $BASE/run.sh -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ - parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ - b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" + do + cp -r ${BASE}/kharma/prob/emhd/conducting_atmosphere_${res}_default/*txt ./ + $BASE/run.sh -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ + parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ + parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 - mv conducting_atmosphere.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf - mv conducting_atmosphere.out0.final.phdf emhd_2d_${res}_end_${1}.phdf - done - check_code=0 - pyharm-convert --double *.phdf - python check.py $ALL_RES $1 2d || check_code=$? - rm -r *.phdf - rm -r *.xdmf - rm -r *.out0* - rm -r ./*.txt - if [[ $check_code != 0 ]]; then - echo Conducting atmosphere test $3 FAIL: $check_code - exit_code=1 - else - echo Conducting atmosphere test $3 success - fi + mv conducting_atmosphere.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv conducting_atmosphere.out0.final.phdf emhd_2d_${res}_end_${1}.phdf + done + check_code=0 + pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + rm -r *.phdf + rm -r *.xdmf + rm -r *.out0* + rm -r ./*.txt + if [[ $check_code != 0 ]]; then + echo Conducting atmosphere test $3 FAIL: $check_code + exit_code=1 + else + echo Conducting atmosphere test $3 success + fi } ALL_RES="64,128,256,512" From 9e3ff78494d5e6be184ae4131c12916c0b2b9b64 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 15 May 2023 12:33:39 -0500 Subject: [PATCH 074/219] Re-add post-implicit-solve fixups. Delta performance fixes. --- kharma/driver/imex_step.cpp | 9 +- kharma/implicit/fixup.cpp | 154 +++++++++++++++++++++++++++++++++++ kharma/implicit/implicit.cpp | 32 ++++++-- kharma/implicit/implicit.hpp | 5 ++ scripts/batch/delta.sb | 14 ++-- tests/mhdmodes/run.sh | 45 +++++----- 6 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 kharma/implicit/fixup.cpp diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index db731188..fa752f46 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -250,7 +250,14 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk auto t_fix_p = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); - auto t_set_bc = tl.AddTask(t_fix_p, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + // Fix unconverged (bad) zones in the solver + // TODO fixups as a callback? + auto t_fix_solve = t_fix_p; + if (pkgs.at("GRMHD")->Param("implicit")) { + t_fix_solve = tl.AddTask(t_fix_p, Implicit::FixSolve, mbd_sub_step_final.get()); + } + + auto t_set_bc = tl.AddTask(t_fix_solve, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); // Any package- (likely, problem-) specific source terms which must be applied to primitive variables // Apply these only after the final step so they're operator-split diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp new file mode 100644 index 00000000..608e02a2 --- /dev/null +++ b/kharma/implicit/fixup.cpp @@ -0,0 +1,154 @@ +/* + * File: fixup.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "implicit.hpp" + +#include "floors.hpp" +#include "flux_functions.hpp" + +TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { + + Flag(mbd, "Fixing implicit solver failures"); + // Get MeshBlock pointer and obtain flag for primitives + auto pmb = mbd->GetBlockPointer(); + + // Get number of implicit variables + PackIndexMap implicit_prims_map; + auto implicit_vars = Implicit::GetOrderedNames(mbd, Metadata::GetUserFlag("Primitive"), true); + auto& P = mbd->PackVariables(implicit_vars, implicit_prims_map); + const int nfvar = P.GetDim(4); + + // Get grid object + const auto& G = pmb->coords; + + GridScalar solve_fail = mbd->Get("solve_fail").data; + GridScalar fflag = mbd->Get("fflag").data; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + // TODO flag_verbose here. Merge with other fixup into separate package or in GRMHD? + // We'll want to try new in-depth fixes w/implicit as we go... + const int verbose = pmb->packages.Get("Globals")->Param("verbose"); + const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); + + // Boundaries were synced just before the call to this function (cf. imex_driver.cpp). + // Which means unsuccessful values were copied to ghost zones. Therefore, we need to loop over entire domain. + const IndexRange ib = mbd->GetBoundsI(IndexDomain::entire); + const IndexRange jb = mbd->GetBoundsJ(IndexDomain::entire); + const IndexRange kb = mbd->GetBoundsK(IndexDomain::entire); + + auto bounds = pmb->cellbounds; + const int n1 = bounds.ncellsi(IndexDomain::entire); + const int n2 = bounds.ncellsj(IndexDomain::entire); + const int n3 = bounds.ncellsk(IndexDomain::entire); + + const IndexRange ib_b = mbd->GetBoundsI(IndexDomain::interior); + const IndexRange jb_b = mbd->GetBoundsJ(IndexDomain::interior); + const IndexRange kb_b = mbd->GetBoundsK(IndexDomain::interior); + + // TODO don't allocate here + ParArrayND sum("sum_good_neighbors", nfvar, n3+1, n2+1, n1+1); + ParArrayND sum_x("sum_all_neighbors", nfvar, n3+1, n2+1, n1+1); + + pmb->par_for("fix_solver_failures", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { + FLOOP { + sum(ip, k, j, i) = 0.; + sum_x(ip, k, j, i) = 0.; + } + // Fix only bad zones + if ((solve_fail(k, j, i)) == SolverStatus::fail) { + //printf("Fixing zone %d %d %d!\n", i, j, k); + double wsum = 0., wsum_x = 0.; + // double sum[nfvar] = {0.}, sum_x[nfvar] = {0.}; + // For all neighboring cells... + for (int n = -1; n <= 1; n++) { + for (int m = -1; m <= 1; m++) { + for (int l = -1; l <= 1; l++) { + int ii = i + l, jj = j + m, kk = k + n; + // If we haven't overstepped array bounds... + if (inside(kk, jj, ii, kb, jb, ib)) { + // Weight by distance + // TODO abs(l) == l*l always? + double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); + + // Count only the good cells, if we can + if ((solve_fail(kk, jj, ii)) != SolverStatus::fail) { + // Weight by distance. Note interpolated "fixed" cells stay flagged + wsum += w; + FLOOP sum(ip, k, j, i) += w * P(ip, kk, jj, ii); + } + // Just in case, keep a sum of even the bad ones + wsum_x += w; + FLOOP sum_x(ip, k, j, i) += w * P(ip, kk, jj, ii); + } + } + } + } + + if(wsum < 1.e-10) { + // TODO probably should crash here. Or average anyway? +#ifndef KOKKOS_ENABLE_SYCL + if (verbose >= 1 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... + printf("No neighbors were available at %d %d %d!\n", i, j, k); +#endif // TODO SYCL has cout + } else { + FLOOP P(ip, k, j, i) = sum(ip, k, j, i)/wsum; + } + } + } + ); + + // Since floors were applied earlier, we assume the zones obtained by averaging the neighbors also respect the floors. + // Compute new conserved variables + PackIndexMap prims_map, cons_map; + auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto& U_all = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + // Get new sizes + const int nvar = P_all.GetDim(4); + + // Need emhd_params object + EMHD_parameters emhd_params = EMHD::GetEMHDParameters(pmb->packages); + + pmb->par_for("fix_solver_failures_PtoU", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { + if (( solve_fail(k, j, i)) == SolverStatus::fail) + Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); + } + ); + + Flag(mbd, "Fixed solver failures"); + return TaskStatus::complete; + +} diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 7d25d6cf..4d1bf8c5 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -38,6 +38,7 @@ #include "grmhd.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" +#include "reductions.hpp" #if DISABLE_IMPLICIT @@ -328,8 +329,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ if (iter == 1) { // New beginnings solve_fail_s(i) = SolverStatus::converged; - } - else { + } else { // Need this to check if the zone had failed in any of the previous iterations. // If so, we don't attempt to update it again in the implicit solver. solve_fail_s(i) = solve_fail_all(b, 0, k, j, i); @@ -409,7 +409,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ emhd_params_sub_step_init, nvar, nfvar, k, j, i, delta, gam, dt, jacobian, residual); // Solve against the negative residual FLOOP delta_prim(ip) = -residual(ip); -#if 1 +#if 0 } } ); @@ -444,7 +444,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ KokkosBatched::SerialApplyPivot ::invoke(pivot, delta_prim); } -#if 1 +#if 0 } } ); @@ -496,8 +496,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } // If the solver failed, we don't want to update the implicit primitives for those zones - if (solve_fail() != SolverStatus::fail) - { + if (solve_fail() != SolverStatus::fail) { // Linesearch if (linesearch) { solve_norm() = 0; @@ -589,6 +588,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ if (verbose >= 1 && MPIRank0()) printf("Iteration %d max L2 norm: %g\n", iter, max_norm.val); // Count total number of solver fails + // TODO move reductions like this to PostStep int nfails = 0; Kokkos::Sum sum_reducer(nfails); pmb_sub_step_init->par_reduce("count_solver_fails", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, @@ -613,4 +613,24 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ return TaskStatus::complete; } + +TaskStatus Implicit::PostStepDiagnostics(const SimTime& tm, MeshData *md) +{ + Flag("Printing Implicit solver diagnostics"); + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Options + const auto& pars = pmesh->packages.Get("Globals")->AllParams(); + const int flag_verbose = pars.Get("flag_verbose"); + + // Debugging/diagnostic info about implicit solver + // TODO status names + // if (flag_verbose >= 1) { + // int nflags = Reductions::CountFlags(md, "solve_fail", Implicit::status_names, IndexDomain::interior, flag_verbose, false); + // // TODO TODO yell here if there are too many flags + // } + + return TaskStatus::complete; +} + #endif diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index e9d384b8..6493022d 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -99,6 +99,11 @@ std::vector GetOrderedNames(MeshBlockData *rc, const Metadata */ TaskStatus FixSolve(MeshBlockData *mbd); +/** + * Print diagnostics about number of failed solves + */ +TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md); + /** * Calculate the residual generated by the trial primitives P_test * diff --git a/scripts/batch/delta.sb b/scripts/batch/delta.sb index d97210b9..7a208232 100755 --- a/scripts/batch/delta.sb +++ b/scripts/batch/delta.sb @@ -4,7 +4,7 @@ #SBATCH -t 24:00:00 #SBATCH -N 1 #SBATCH -o "out-%j.txt" -#SBATCH --account=bbhr-delta-gpu +#SBATCH --account=bbgv-delta-gpu # Nodes we want #SBATCH --partition=gpuA100x4 @@ -17,7 +17,7 @@ # Node options # 8-way nodes are 2 sockets, so this is constant -#SBATCH --cpus-per-task=16 +#SBATCH --cpus-per-task=4 # ALWAYS reserve full nodes to mitigate memory leaks #SBATCH --exclusive #SBATCH --mem=0 @@ -25,8 +25,10 @@ # NCSA Delta run script # OpenMP directives: use all available threads -export OMP_PROC_BIND=spread -export OMP_PLACES=threads +# Currently this slows things down on many machines +# TODO properly assign to particular cores +#export OMP_PROC_BIND=spread +#export OMP_PLACES=threads # If you see weird GPU race conditions, setting this # to 1 *might* fix them. Maybe. @@ -35,7 +37,7 @@ export CUDA_LAUNCH_BLOCKING=0 #export KOKKOS_DEVICE_ID=0 # Choose the kharma from compiled options in order of preference -KHARMA_DIR=${KHARMA_DIR:-"$HOME/kharma"} +KHARMA_DIR=${KHARMA_DIR:-"$HOME/Code/kharma"} # Optionally use the Kokkos tools to profile kernels #export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so @@ -53,4 +55,4 @@ export KOKKOS_NUM_DEVICES=$SLURM_NTASKS_PER_NODE # Run with srun # TODO auto-switch to mpirun in interactive? -srun $KHARMA_DIR/kharma.cuda -t 23:50:00 -d dumps_kharma "$@" +mpirun -n 4 --map-by ppr:4:node:pe=16 $KHARMA_DIR/kharma.cuda -t 23:50:00 -d dumps_kharma "$@" diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index 663c11ba..0b35f925 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -54,34 +54,33 @@ conv_2d() { fi } -#conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D" +# Normal MHD modes, 2D, defaults conv_2d slow mhdmodes/nmode=1 "slow mode in 2D" -#conv_2d alfven mhdmodes/nmode=2 "Alfven mode in 2D" +conv_2d alfven mhdmodes/nmode=2 "Alfven mode in 2D" conv_2d fast mhdmodes/nmode=3 "fast mode in 2D" -# These 3 double as a demo of why WENO is great -#conv_3d entropy mhdmodes/nmode=0 "entropy mode in 3D" -#conv_3d entropy_mc "mhdmodes/nmode=0 GRMHD/reconstruction=linear_mc" "entropy mode in 3D, linear/MC reconstruction" -#conv_3d entropy_vl "mhdmodes/nmode=0 GRMHD/reconstruction=linear_vl" "entropy mode in 3D, linear/VL reconstruction" -# Other modes don't benefit, exercise WENO most since we use it -#conv_3d slow "mhdmodes/nmode=1 mhdmodes/dir=3" "slow mode in 3D" -#conv_3d alfven "mhdmodes/nmode=2 mhdmodes/dir=3" "Alfven mode in 3D" -#conv_3d fast "mhdmodes/nmode=3 mhdmodes/dir=3" "fast mode in 3D" -# And we've got to test classic/GRIM stepping -#conv_3d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 3D, ImEx explicit" -#conv_3d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 3D, ImEx explicit" -#conv_3d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 3D, ImEx explicit" +# Entropy mode as reconstruction demo +#conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D, no B field" # TODO init currently requires B +conv_3d entropy mhdmodes/nmode=0 "entropy mode in 3D" +conv_3d entropy_mc "mhdmodes/nmode=0 GRMHD/reconstruction=linear_mc" "entropy mode in 3D, linear/MC reconstruction" +conv_3d entropy_vl "mhdmodes/nmode=0 GRMHD/reconstruction=linear_vl" "entropy mode in 3D, linear/VL reconstruction" + +# ImEx driver +conv_2d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 3D, ImEx explicit" +conv_2d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 3D, ImEx explicit" +conv_2d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 3D, ImEx explicit" # B field totally explicit -#conv_3d slow_imex_semi "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "slow mode 3D, ImEx semi-implicit" -#conv_3d alfven_imex_semi "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "Alfven mode 3D, ImEx semi-implicit" -#conv_3d fast_imex_semi "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "fast mode 3D, ImEx semi-implicit" +conv_2d slow_imex_semi "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "slow mode 3D, ImEx semi-implicit" +conv_2d alfven_imex_semi "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "Alfven mode 3D, ImEx semi-implicit" +conv_2d fast_imex_semi "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "fast mode 3D, ImEx semi-implicit" # All variables semi-implicit -#conv_3d slow_imex_im "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "slow mode 3D, ImEx implicit" -#conv_3d alfven_imex_im "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "Alfven mode 3D, ImEx implicit" -#conv_3d fast_imex_im "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=true" "fast mode 3D, ImEx implicit" +conv_2d slow_imex_im "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "slow mode 3D, ImEx implicit" +conv_2d alfven_imex_im "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "Alfven mode 3D, ImEx implicit" +conv_2d fast_imex_im "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "fast mode 3D, ImEx implicit" -# 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 -# Currently very slow, plus modes are incorrect -#conv_2d fast2d mhdmodes/nmode=3 +# 3D versions, basics only +conv_3d slow "mhdmodes/nmode=1 mhdmodes/dir=3" "slow mode in 3D" +conv_3d alfven "mhdmodes/nmode=2 mhdmodes/dir=3" "Alfven mode in 3D" +conv_3d fast "mhdmodes/nmode=3 mhdmodes/dir=3" "fast mode in 3D" exit $exit_code From 1a7a6bcc031f191d91c64790e9e31dec0dc512fb Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 17 May 2023 09:51:50 -0500 Subject: [PATCH 075/219] Test updates (Not as big as it looks, lots of moving files) Updates: 1. Move analytic result files for emhd tests to the test/ directory 2. Rename some tests, clear out multizone problems which are not tests (and don't function with this KHARMA branch anyway) 3. Fix Dirichlet boundaries for test cases, get rid of manual tracking of numbers of variables. 4. Fix EMHD problems without floors, now that floors package isn't loaded --- kharma/b_cd/b_cd.cpp | 10 - kharma/b_flux_ct/b_flux_ct.cpp | 9 - kharma/b_flux_ct/seed_B_ct.cpp | 2 + kharma/boundaries/boundaries.cpp | 19 +- kharma/boundaries/dirichlet.cpp | 37 ++- kharma/boundaries/dirichlet.hpp | 3 +- kharma/driver/kharma_driver.cpp | 4 - kharma/electrons/electrons.cpp | 9 - kharma/emhd/emhd.cpp | 7 +- kharma/grmhd/grmhd.cpp | 11 +- kharma/implicit/fixup.cpp | 10 +- kharma/implicit/implicit.cpp | 34 +-- kharma/kharma.cpp | 22 +- kharma/kharma.hpp | 34 +++ kharma/prob/emhd/conducting_atmosphere.cpp | 49 ++-- kharma/prob/emhd/emhdmodes.hpp | 3 +- kharma/prob/emhd/emhdshock.hpp | 8 +- kharma/prob/emhd/fm_torus_emhd.cpp | 225 ------------------ kharma/prob/fm_torus.cpp | 5 +- kharma/prob/fm_torus.hpp | 5 - kharma/prob/post_initialize.cpp | 4 + kharma/prob/problem.cpp | 14 +- pars/bondi_b.par | 4 +- pars/bondi_b_vertical.par | 4 +- pars/bondi_viscous.par | 27 +-- pars/bz_monopole.par | 15 +- pars/conducting_atmosphere.par | 48 ++-- pars/emhdmodes.par | 2 +- pars/hubble.par | 4 +- pars/noh.par | 6 +- pars/sane_emhd.par | 2 +- run.sh | 9 +- scripts/batch/multizone/multizone.par | 6 +- tests/all_pars/run.sh | 3 +- tests/anisotropic_conduction/make_plots.py | 112 +++++++++ tests/bflux/run.sh | 139 ----------- tests/bondi/run.sh | 2 + tests/bondi_multizone/run.sh | 143 ----------- .../bondi_analytic_128.txt | 0 .../bondi_analytic_256.txt | 0 .../bondi_analytic_32.txt | 0 .../bondi_analytic_64.txt | 0 tests/bondi_viscous/check.py | 165 +++++++------ tests/bondi_viscous/run.sh | 54 ++--- tests/bz_monopole/check.py | 16 +- tests/bz_monopole/run.sh | 10 +- tests/clean_tests.sh | 2 +- tests/conducting_atmosphere/check.py | 171 ++++++------- .../atmosphere_soln_phi.txt | 0 .../atmosphere_soln_rCoords.txt | 0 .../atmosphere_soln_rho.txt | 0 .../atmosphere_soln_u.txt | 0 .../atmosphere_soln_phi.txt | 0 .../atmosphere_soln_rCoords.txt | 0 .../atmosphere_soln_rho.txt | 0 .../atmosphere_soln_u.txt | 0 .../atmosphere_soln_phi.txt | 0 .../atmosphere_soln_rCoords.txt | 0 .../atmosphere_soln_rho.txt | 0 .../atmosphere_soln_u.txt | 0 .../atmosphere_soln_phi.txt | 0 .../atmosphere_soln_rCoords.txt | 0 .../atmosphere_soln_rho.txt | 0 .../atmosphere_soln_u.txt | 0 .../atmosphere_soln_phi.txt | 0 .../atmosphere_soln_rCoords.txt | 0 .../atmosphere_soln_rho.txt | 0 .../atmosphere_soln_u.txt | 0 tests/conducting_atmosphere/run.sh | 16 +- tests/emhdmodes/run.sh | 2 +- tests/emhdshock/run.sh | 6 +- .../shock_soln_1024_default/shock_soln_dP.txt | 0 .../shock_soln_1024_default/shock_soln_q.txt | 0 .../shock_soln_rho.txt | 0 .../shock_soln_1024_default/shock_soln_u.txt | 0 .../shock_soln_1024_default/shock_soln_u1.txt | 0 .../shock_soln_xCoords.txt | 0 .../shock_soln_2048_default/shock_soln_dP.txt | 0 .../shock_soln_2048_default/shock_soln_q.txt | 0 .../shock_soln_rho.txt | 0 .../shock_soln_2048_default/shock_soln_u.txt | 0 .../shock_soln_2048_default/shock_soln_u1.txt | 0 .../shock_soln_xCoords.txt | 0 .../shock_soln_256_default/shock_soln_dP.txt | 0 .../shock_soln_256_default/shock_soln_q.txt | 0 .../shock_soln_256_default/shock_soln_rho.txt | 0 .../shock_soln_256_default/shock_soln_u.txt | 0 .../shock_soln_256_default/shock_soln_u1.txt | 0 .../shock_soln_xCoords.txt | 0 .../shock_soln_512_default/shock_soln_dP.txt | 0 .../shock_soln_512_default/shock_soln_q.txt | 0 .../shock_soln_512_default/shock_soln_rho.txt | 0 .../shock_soln_512_default/shock_soln_u.txt | 0 .../shock_soln_512_default/shock_soln_u1.txt | 0 .../shock_soln_xCoords.txt | 0 tests/gizmo_shell/run.sh | 133 ----------- tests/{hubble => hubble_flow}/make_plots.py | 0 .../{bclean => multizone}/bondi_multizone.par | 4 +- tests/{bclean => multizone}/run.sh | 0 tests/noh/run.sh | 2 +- tests/torus_sanity/run.sh | 2 +- 101 files changed, 543 insertions(+), 1090 deletions(-) delete mode 100644 kharma/prob/emhd/fm_torus_emhd.cpp create mode 100644 tests/anisotropic_conduction/make_plots.py delete mode 100755 tests/bflux/run.sh delete mode 100755 tests/bondi_multizone/run.sh rename {kharma/prob/emhd => tests/bondi_viscous}/bondi_viscous_128_default/bondi_analytic_128.txt (100%) rename {kharma/prob/emhd => tests/bondi_viscous}/bondi_viscous_256_default/bondi_analytic_256.txt (100%) rename {kharma/prob/emhd => tests/bondi_viscous}/bondi_viscous_32_default/bondi_analytic_32.txt (100%) rename {kharma/prob/emhd => tests/bondi_viscous}/bondi_viscous_64_default/bondi_analytic_64.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_128_default/atmosphere_soln_phi.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_128_default/atmosphere_soln_rCoords.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_128_default/atmosphere_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_128_default/atmosphere_soln_u.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_256_default/atmosphere_soln_phi.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_256_default/atmosphere_soln_rCoords.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_256_default/atmosphere_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_256_default/atmosphere_soln_u.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_32_default/atmosphere_soln_phi.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_32_default/atmosphere_soln_rCoords.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_32_default/atmosphere_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_32_default/atmosphere_soln_u.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_512_default/atmosphere_soln_phi.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_512_default/atmosphere_soln_rCoords.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_512_default/atmosphere_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_512_default/atmosphere_soln_u.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_64_default/atmosphere_soln_phi.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_64_default/atmosphere_soln_rCoords.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_64_default/atmosphere_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/conducting_atmosphere}/conducting_atmosphere_64_default/atmosphere_soln_u.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_dP.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_q.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_u.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_u1.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_1024_default/shock_soln_xCoords.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_dP.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_q.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_u.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_u1.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_2048_default/shock_soln_xCoords.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_dP.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_q.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_u.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_u1.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_256_default/shock_soln_xCoords.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_dP.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_q.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_rho.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_u.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_u1.txt (100%) rename {kharma/prob/emhd => tests/emhdshock}/shock_soln_512_default/shock_soln_xCoords.txt (100%) delete mode 100755 tests/gizmo_shell/run.sh rename tests/{hubble => hubble_flow}/make_plots.py (100%) rename tests/{bclean => multizone}/bondi_multizone.par (96%) rename tests/{bclean => multizone}/run.sh (100%) diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index 6042db1f..f5ff3aaf 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -57,16 +57,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->AllParams(); - // if (implicit_b) { - // int n_current = driver.Get("n_implicit_vars"); - // driver.Update("n_implicit_vars", n_current+3); - // } else { - // int n_current = driver.Get("n_explicit_vars"); - // driver.Update("n_explicit_vars", n_current+3); - // } - std::vector s_vector({NVEC}); // B field as usual diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 604768b2..d1facae9 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -92,15 +92,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("b_field", "implicit", false); params.Add("implicit", implicit_b); - // Update variable numbers - if (implicit_b) { - int n_current = driver.Get("n_implicit_vars"); - driver.Update("n_implicit_vars", n_current+3); - } else { - int n_current = driver.Get("n_explicit_vars"); - driver.Update("n_explicit_vars", n_current+3); - } - params.Add("divb_reducer", AllReduce()); // FIELDS diff --git a/kharma/b_flux_ct/seed_B_ct.cpp b/kharma/b_flux_ct/seed_B_ct.cpp index ded91bbb..c19960eb 100644 --- a/kharma/b_flux_ct/seed_B_ct.cpp +++ b/kharma/b_flux_ct/seed_B_ct.cpp @@ -38,6 +38,7 @@ #include "b_field_tools.hpp" #include "b_flux_ct.hpp" +#include "boundaries.hpp" #include "coordinate_utils.hpp" #include "fm_torus.hpp" #include "grmhd_functions.hpp" @@ -155,6 +156,7 @@ TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) // We still need to update conserved flux values, but then we're done if (early_field) { B_FluxCT::BlockPtoU(rc, IndexDomain::entire, false); + KBoundaries::FreezeDirichletBlock(rc); return TaskStatus::complete; } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 8bea3902..1e3ce83c 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -53,6 +53,7 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: bool spherical = pin->GetBoolean("coordinates", "spherical"); // Global check inflow sets inner/outer X1 by default bool check_inflow_global = pin->GetOrAddBoolean("boundaries", "check_inflow", spherical); + // TODO TODO Support old option names check_inflow_inner, check_inflow_outer // Ensure fluxes through the zero-size face at the pole are zero bool zero_polar_flux = pin->GetOrAddBoolean("boundaries", "zero_polar_flux", spherical); @@ -76,23 +77,7 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: Metadata m_x1, m_x2, m_x3; { // We can't use GetVariablesByFlag yet, so walk through and count manually - int nvar = 0; - for (auto pkg : packages->AllPackages()) { - for (auto field : pkg.second->AllFields()) { - // Specifically ignore the B_Cleanup variables, we don't handle their boundary conditions - // TODO "Present" or "Has" in Packages_t - bool is_not_cleanup = packages->AllPackages().count("B_Cleanup") - ? !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup")) - : true; - if (field.second.IsSet(Metadata::FillGhost) && is_not_cleanup) { - if (field.second.Shape().size() < 1) { - nvar += 1; - } else { - nvar += field.second.Shape()[0]; - } - } - } - } + int nvar = KHARMA::CountVars(packages.get(), Metadata::FillGhost); // We also don't know the mesh size, since it's not constructed. We infer. const int ng = pin->GetInteger("parthenon/mesh", "nghost"); diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 30aa5ee4..edc715ba 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -43,12 +43,16 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda std::shared_ptr pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + // Get all ghosts, minus those in the B_Cleanup package if it is present using FC = Metadata::FlagCollection; - auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); + FC main_ghosts = pmb->packages.AllPackages().count("B_Cleanup") + ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) + : FC({Metadata::FillGhost}); + auto q = rc->PackVariables(main_ghosts, coarse); auto bound = rc->Get("bound." + BoundaryName(bface)).data; - if (q.GetDim(4) != bound.GetDim(4)) - { + // TODO TODO NAMES + if (q.GetDim(4) != bound.GetDim(4)) { std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; } @@ -73,7 +77,8 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda } else { q(p, k, j, i) = bound(p, k, j, i); } - }); + } + ); } void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) @@ -87,7 +92,7 @@ void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) if (pmesh->packages.Get("Boundaries")->Param(bname) == "dirichlet") { // ...on all blocks... for (int i=0; i < md->NumBlocks(); i++) { - auto rc = md->GetBlockData(i); + auto rc = md->GetBlockData(i).get(); std::shared_ptr pmb = rc->GetBlockPointer(); auto domain = BoundaryDomain(bface); // Set whatever is in that domain as the Dirichlet bound @@ -96,15 +101,33 @@ void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) } } } +void KBoundaries::FreezeDirichletBlock(MeshBlockData *rc) +{ + // For each face... + for (int i=0; i < BOUNDARY_NFACES; i++) { + BoundaryFace bface = (BoundaryFace) i; + auto bname = BoundaryName(bface); + auto pmb = rc->GetBlockPointer(); + // ...if this boundary is dirichlet... + if (pmb->packages.Get("Boundaries")->Param(bname) == "dirichlet") { + auto domain = BoundaryDomain(bface); + // Set whatever is in that domain as the Dirichlet bound + SetDomainDirichlet(rc, domain, false); + } + } +} -void KBoundaries::SetDomainDirichlet(std::shared_ptr> &rc, IndexDomain domain, bool coarse) +void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain, bool coarse) { std::shared_ptr pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); const BoundaryFace bface = BoundaryFaceOf(domain); using FC = Metadata::FlagCollection; - auto q = rc->PackVariables(FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}), coarse); + FC main_ghosts = pmb->packages.AllPackages().count("B_Cleanup") + ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) + : FC({Metadata::FillGhost}); + auto q = rc->PackVariables(main_ghosts, coarse); auto bound = rc->Get("bound." + BoundaryName(bface)).data; // TODO error? diff --git a/kharma/boundaries/dirichlet.hpp b/kharma/boundaries/dirichlet.hpp index 3cc7d903..0e2d2c72 100644 --- a/kharma/boundaries/dirichlet.hpp +++ b/kharma/boundaries/dirichlet.hpp @@ -49,7 +49,8 @@ void Dirichlet(std::shared_ptr> &rc, bool coarse) * Freeze any dirichlet boundary conditions in their current forms. */ void FreezeDirichlet(std::shared_ptr> &md); +void FreezeDirichletBlock(MeshBlockData *rc); -void SetDomainDirichlet(std::shared_ptr> &rc, IndexDomain domain, bool coarse); +void SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain, bool coarse); } diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 6f9608dc..eb55e7ba 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -108,10 +108,6 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std Metadata::AddUserFlag("Implicit"); Metadata::AddUserFlag("Explicit"); - // Keep track of numbers of variables - params.Add("n_explicit_vars", 0, true); - params.Add("n_implicit_vars", 0, true); - return pkg; } diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index 7b0176cc..f74e3cc7 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -175,15 +175,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Kel? // TODO record nKs and find a nice way to loop/vector the device-side layout? - // Update variable numbers - if (implicit_e) { - int n_current = driver.Get("n_implicit_vars"); - driver.Update("n_implicit_vars", n_current+nKs); - } else { - int n_current = driver.Get("n_explicit_vars"); - driver.Update("n_explicit_vars", n_current+nKs); - } - // Problem-specific fields if (packages->Get("Globals")->Param("problem") == "driven_turbulence") { std::vector s_vector({2}); diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 9d0a8718..5832055e 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -118,15 +118,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("floors", "emhd_limits", false) || - pin->GetOrAddBoolean("emhd", "limits", false); + pin->GetOrAddBoolean("emhd", "stability_limits", false); // Only enable limits internally if we're actually doing EMHD params.Add("enable_emhd_limits", enable_emhd_limits); - // Update variable numbers - auto& driver = packages->Get("Driver")->AllParams(); - int n_current = driver.Get("n_implicit_vars"); - driver.Update("n_implicit_vars", n_current+2); - Metadata::AddUserFlag("EMHD"); // General options for primitive and conserved scalar variables in ImEx driver diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index e0ed9945..f5c6963f 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -112,15 +112,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); params.Add("implicit", implicit_grmhd); - // Update variable numbers - if (implicit_grmhd) { - int n_current = driver.Get("n_implicit_vars"); - driver.Update("n_implicit_vars", n_current+5); - } else { - int n_current = driver.Get("n_explicit_vars"); - driver.Update("n_explicit_vars", n_current+5); - } - // AMR PARAMETERS // Adaptive mesh refinement options // Only active if "refinement" and "numlevel" parameters allow @@ -264,7 +255,7 @@ Real EstimateTimestep(MeshBlockData *rc) typename Kokkos::MinMax::value_type minmax; pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA(const int k, const int j, const int i, + KOKKOS_LAMBDA (const int k, const int j, const int i, typename Kokkos::MinMax::value_type &lminmax) { double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / ctop(0, k, j, i)) + 1 / (G.Dxc<2>(j) / ctop(1, k, j, i)) + diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index 608e02a2..e8377905 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -53,13 +53,11 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { const auto& G = pmb->coords; GridScalar solve_fail = mbd->Get("solve_fail").data; - GridScalar fflag = mbd->Get("fflag").data; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); // TODO flag_verbose here. Merge with other fixup into separate package or in GRMHD? // We'll want to try new in-depth fixes w/implicit as we go... - const int verbose = pmb->packages.Get("Globals")->Param("verbose"); - const Floors::Prescription floors(pmb->packages.Get("Floors")->AllParams()); + const int flag_verbose = pmb->packages.Get("Globals")->Param("flag_verbose"); // Boundaries were synced just before the call to this function (cf. imex_driver.cpp). // Which means unsuccessful values were copied to ghost zones. Therefore, we need to loop over entire domain. @@ -119,7 +117,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { if(wsum < 1.e-10) { // TODO probably should crash here. Or average anyway? #ifndef KOKKOS_ENABLE_SYCL - if (verbose >= 1 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... + if (flag_verbose >= 3 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... printf("No neighbors were available at %d %d %d!\n", i, j, k); #endif // TODO SYCL has cout } else { @@ -135,15 +133,13 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); auto& U_all = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); - // Get new sizes - const int nvar = P_all.GetDim(4); // Need emhd_params object EMHD_parameters emhd_params = EMHD::GetEMHDParameters(pmb->packages); pmb->par_for("fix_solver_failures_PtoU", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { - if (( solve_fail(k, j, i)) == SolverStatus::fail) + if (solve_fail(k, j, i) == SolverStatus::fail) Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); } ); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 4d1bf8c5..27f47e49 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -37,6 +37,7 @@ #include "debug.hpp" #include "grmhd.hpp" #include "grmhd_functions.hpp" +#include "kharma.hpp" #include "pack.hpp" #include "reductions.hpp" @@ -86,6 +87,9 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh auto pkg = std::make_shared("Implicit"); Params ¶ms = pkg->AllParams(); + // Implicit evolution must use predictor-corrector i.e. "vl2" integrator + pin->SetString("parthenon/time", "integrator", "vl2"); + // Implicit solver parameters Real jacobian_delta = pin->GetOrAddReal("implicit", "jacobian_delta", 4.e-8); params.Add("jacobian_delta", jacobian_delta); @@ -117,28 +121,16 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh m_real = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); pkg->AddField("solve_fail", m_real); // TODO: Replace with m_int once Integer is supported for CellVariable - // TODO: Find a way to save all residuals based on a runtime parameter, e.g. below. We don't want to allocate - // a vector field equal to the number of implicit variables over the entire meshblock if we don't have to. - // Should the solve save the residual vector field? Useful for debugging purposes. Default is NO. - // bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); - // params.Add("save_residual", save_residual); - - // Vector field to store residual components (only for those variables that are evolved implicitly) - // if (save_residual) { - // auto driver_type = pin->GetString("driver", "type"); - // bool grmhd_implicit = (driver_type == "imex") && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); - // bool implicit_b = (driver_type == "imex") && (pin->GetOrAddBoolean("b_field", "implicit", grmhd_implicit)); - // bool emhd_enabled = pin->GetOrAddBoolean("emhd", "on", false); - // int nvars_implicit = // Get this from "Driver" - - // // flags_vec = std::vector({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); - // // auto flags_vec(flags_vec); - // // flags_vec.push_back(Metadata::Vector); - // std::vector s_vector({nfvar}); - // Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); - // pkg->AddField("residual", m); - // } + bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); + params.Add("save_residual", save_residual); + if (save_residual) { + int nvars_implicit = KHARMA::CountVars(packages.get(), Metadata::GetUserFlag("Implicit")); + + std::vector s_vars_implicit({nvars_implicit}); + Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vars_implicit); + pkg->AddField("residual", m); + } return pkg; } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 80e63748..0e657ee0 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -93,6 +93,7 @@ std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, st return pkg; } + void KHARMA::ResetGlobals(ParameterInput *pin, Mesh *pmesh) { // The globals package was loaded & exists, retrieve it @@ -275,6 +276,7 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) Flag("ProcessPackages"); // Allocate the packages list as a shared pointer, to be updated in various tasks + // TODO print what we're doing here & do some sanity checks, if verbose auto packages = std::make_shared(); TaskCollection tc; @@ -324,9 +326,8 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) if (t_b_field == t_none) t_b_field = t_b_cleanup; } - // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it) + // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it). // Since it is never required to restart, this is the only time we'd write (hence, need) it - // TODO use GetVector & == when available if (FieldIsOutput(pin.get(), "jcon") && t_b_field != t_none) { auto t_current = tl.AddTask(t_b_field, KHARMA::AddPackage, packages, Current::Initialize, pin.get()); } @@ -344,20 +345,17 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // Execute the whole collection (just in case we do something fancy?) while (!tr.Execute()); // TODO this will inf-loop on error - // The boundaries package may need to know variable counts for allocating memory, - // so we initialize it after the main dependency tree - // TODO only init if at least one boundary is "user" - KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); - - // Load the implicit package *last*, if there are any variables which need implicit evolution - // TODO print what we're doing here & do some sanity checks, if verbose - int n_implicit = packages->Get("Driver")->Param("n_implicit_vars"); + // Load the implicit package last, and only if there are any variables which need implicit evolution + int n_implicit = CountVars(packages.get(), Metadata::GetUserFlag("Implicit")); if (n_implicit > 0) { KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); - // Implicit evolution must use predictor-corrector i.e. "vl2" integrator - pin->SetString("parthenon/time", "integrator", "vl2"); } + // The boundaries package may need to know variable counts for allocating memory, + // so we initialize it after *everything* else + // TODO avoid init if e.g. all periodic boundaries? + KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); + EndFlag("ProcessPackages"); // TODO print full package list way up here? return std::move(*packages); } diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index 20afbadd..5928ec3a 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -82,6 +82,8 @@ void FixParameters(std::unique_ptr& pin); */ Packages_t ProcessPackages(std::unique_ptr& pin); +// TODO(BSP) not sure where to put these + /** * Check whether a given field is anywhere in outputs. * Used to avoid calculating expensive fields (jcon, divB) if they @@ -103,4 +105,36 @@ inline bool FieldIsOutput(ParameterInput *pin, std::string name) return false; } +/** + * This fn calculates the size a VariablePack *would* be, without making one -- + * it uses only the package list, and counts through each variable in each package. + * Mostly useful for initialization. + * TODO can this take flagcollections? Move to Parthenon... + */ +inline int CountVars(Packages_t* packages, MetadataFlag flag) +{ + int nvar = 0; + for (auto pkg : packages->AllPackages()) { + for (auto field : pkg.second->AllFields()) { + // Specifically ignore the B_Cleanup variables, we'll never want them separately like this + bool is_not_cleanup = packages->AllPackages().count("B_Cleanup") + ? !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup")) + : true; + if (field.second.IsSet(flag) && is_not_cleanup) { + int var_len = 0; + if (field.second.IsSet(Metadata::Face)) { + var_len = 3; // TODO non-scalar face fields? + } else if (field.second.Shape().size() < 1) { + var_len = 1; + } else { + var_len = field.second.Shape()[0]; + } + //std::cout << "flag: " << flag << " var: " << field.first.label() << " size: " << var_len << std::endl; + nvar += var_len; + } + } + } + return nvar; +} + } diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 9ad879ae..1a6a6e42 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -34,6 +34,7 @@ #include "emhd/conducting_atmosphere.hpp" +#include "b_flux_ct.hpp" #include "boundaries.hpp" #include "coordinate_utils.hpp" @@ -54,15 +55,8 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame // Obtain EMHD params const bool use_emhd = pmb->packages.AllPackages().count("EMHD"); - bool higher_order_terms = false; - EMHD::EMHD_parameters emhd_params_tmp; - if (use_emhd) { - std::cout << "Hydrostatic atmosphere will be conducting w/EMHD" << std::endl; - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); - emhd_params_tmp = emhd_pars.Get("emhd_params"); - higher_order_terms = emhd_params_tmp.higher_order_terms; - } - const EMHD::EMHD_parameters& emhd_params = emhd_params_tmp; + EMHD::EMHD_parameters emhd_params = EMHD::GetEMHDParameters(pmb->packages); + emhd_params.higher_order_terms = false; // Obtain GRMHD params const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); @@ -148,6 +142,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame } // Initialize primitives + // TODO read->copy->assign on device? double rho_temp, u_temp, q_temp; for (int i = ib.s; i <= ib.e; i++) { @@ -173,7 +168,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame uvec_host(V1, k, j, i) = 0.; uvec_host(V2, k, j, i) = 0.; uvec_host(V3, k, j, i) = 0.; - B_host(V1, k, j, i) = 1./pow(Xembed[1], 3.); + B_host(V1, k, j, i) = 1./(Xembed[1]*Xembed[1]*Xembed[1]); B_host(V2, k, j, i) = 0.; B_host(V3, k, j, i) = 0.; if (use_emhd) @@ -187,28 +182,21 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame Real ucon[GR_DIM] = {0}; Real gcov[GR_DIM][GR_DIM] = {0}; Real gcon[GR_DIM][GR_DIM] = {0}; - G.gcov(Loci::center, j, i, gcov); - G.gcon(Loci::center, j, i, gcon); + // Use functions because we're host-side + G.coords.gcov_native(Xnative, gcov); + G.coords.gcon_native(Xnative, gcon); - ucon[0] = 1./sqrt(-gcov[0][0]); + ucon[0] = 1. / m::sqrt(-gcov[0][0]); ucon[1] = 0.; ucon[2] = 0.; ucon[3] = 0.; - double alpha, beta[GR_DIM], gamma; - - // Solve for primitive velocities (utilde) - alpha = 1/sqrt(-gcon[0][0]); - gamma = ucon[0] * alpha; - - beta[0] = 0.; - beta[1] = alpha*alpha*gcon[0][1]; - beta[2] = alpha*alpha*gcon[0][2]; - beta[3] = alpha*alpha*gcon[0][3]; - - uvec_host(V1, k, j, i) = ucon[1] + beta[1]*gamma/alpha; - uvec_host(V2, k, j, i) = ucon[2] + beta[2]*gamma/alpha; - uvec_host(V3, k, j, i) = ucon[3] + beta[3]*gamma/alpha; + // Solve for & assign primitive velocities (utilde) + Real u_prim[NVEC]; + fourvel_to_prim(gcon, ucon, u_prim); + uvec_host(V1, k, j, i) = u_prim[V1]; + uvec_host(V2, k, j, i) = u_prim[V2]; + uvec_host(V3, k, j, i) = u_prim[V3]; if (use_emhd) { // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde @@ -220,8 +208,8 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); const Real Theta = (gam - 1.) * u_temp / rho_temp; - q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho_temp * pow(Theta, 2.))) : 0.; - dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; + q_tilde *= (chi_e != 0) * m::sqrt(tau / (chi_e * rho_temp * Theta * Theta)); + dP_tilde *= (nu_e != 0) * m::sqrt(tau / (nu_e * rho_temp * Theta)); } q_host(k, j, i) = q_tilde; dP_host(k, j, i) = dP_tilde; @@ -249,6 +237,9 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame } Kokkos::fence(); + // Also fill cons.B + B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); + Flag("Initialized"); return TaskStatus::complete; diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index b745be13..66034e11 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -62,8 +62,7 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet const Real amp = pin->GetOrAddReal("emhdmodes", "amp", 1e-8); - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); - const EMHD::EMHD_parameters& emhd_params = emhd_pars.Get("emhd_params"); + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); const Real& gam = grmhd_pars.Get("gamma"); diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index 7887a865..ee335953 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -71,8 +71,7 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet const std::string input = pin->GetOrAddString("emhdshock", "input", "BVP"); // Obtain EMHD params - const auto& emhd_pars = pmb->packages.Get("EMHD")->AllParams(); - const EMHD::EMHD_parameters& emhd_params = emhd_pars.Get("emhd_params"); + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); // Obtain GRMHD params const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); const Real& gam = grmhd_pars.Get("gamma"); @@ -170,10 +169,7 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet dP.DeepCopy(dP_host); Kokkos::fence(); - } - - // Any other input corresponds to ideal MHD shock initial conditions - else { + } else { // Any other input corresponds to ideal MHD shock initial conditions // Need the limits of the problem size to determine center const Real x1min = pin->GetReal("parthenon/mesh", "x1min"); diff --git a/kharma/prob/emhd/fm_torus_emhd.cpp b/kharma/prob/emhd/fm_torus_emhd.cpp deleted file mode 100644 index 8920a564..00000000 --- a/kharma/prob/emhd/fm_torus_emhd.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * File: fm_torus.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "fm_torus.hpp" - -#include "types.hpp" - -#include -#include "Kokkos_Random.hpp" - -TaskStatus InitializeFMTorusEMHD(std::shared_ptr>& rc, ParameterInput *pin) -{ - Flag(rc, "Initializing torus problem"); - - auto pmb = rc->GetBlockPointer(); - GridScalar rho = rc->Get("prims.rho").data; - GridScalar u = rc->Get("prims.u").data; - GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - - // This problem init is exclusively for the EMHD torus; get copies of q and dP - const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", true); - const bool conduction = pmb->packages.Get("EMHD")->Param("conduction"); - const bool viscosity = pmb->packages.Get("EMHD")->Param("viscosity"); - - // Proxy initializations - auto q = rho; - auto dP = rho; - if (conduction) - q = rc->Get("prims.q").data; - if (viscosity) - dP = rc->Get("prims.dP").data; - - const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); - const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); - const Real kappa = pin->GetOrAddReal("torus", "kappa", 1.e-3); - const GReal tilt_deg = pin->GetOrAddReal("torus", "tilt", 0.0); - const GReal tilt = tilt_deg / 180. * M_PI; - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - IndexDomain domain = IndexDomain::interior; - const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - const int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - - // Get coordinate systems - // G clearly holds a reference to an existing system G.coords.base, - // but we don't know if it's KS or BL coordinates - // Since we can't create a system and assign later, we just - // rebuild copies of both based on the BH spin "a" - const auto& G = pmb->coords; - const bool use_ks = G.coords.is_ks(); - const GReal a = G.coords.get_a(); - const SphBLCoords blcoords = SphBLCoords(a); - const SphKSCoords kscoords = SphKSCoords(a); - - // Fishbone-Moncrief parameters - Real l = lfish_calc(a, rmax); - - pmb->par_for("fm_torus_init", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { - GReal Xnative[GR_DIM], Xembed[GR_DIM], Xmidplane[GR_DIM]; - G.coord(k, j, i, Loci::center, Xnative); - G.coord_embed(k, j, i, Loci::center, Xembed); - // What are our corresponding "midplane" values for evaluating the function? - rotate_polar(Xembed, tilt, Xmidplane); - - GReal r = Xmidplane[1], th = Xmidplane[2]; - GReal sth = sin(th); - GReal cth = cos(th); - - Real lnh = lnh_calc(a, l, rin, r, th); - - // Region inside magnetized torus; u^i is calculated in - // Boyer-Lindquist coordinates, as per Fishbone & Moncrief, - // so it needs to be transformed at the end - // everything outside is left 0 to be added by the floors - if (lnh >= 0. && r >= rin) { - Real r2 = r*r; - Real a2 = a*a; - Real DD = r2 - 2. * r + a2; - Real AA = m::pow(r2 + a2, 2) - DD * a2 * sth * sth; - Real SS = r2 + a2 * cth * cth; - - // Calculate rho and u - Real hm1 = exp(lnh) - 1.; - Real rho_l = m::pow(hm1 * (gam - 1.) / (kappa * gam), 1. / (gam - 1.)); - Real u_l = kappa * m::pow(rho_l, gam) / (gam - 1.); - - // Calculate u^phi - Real expm2chi = SS * SS * DD / (AA * AA * sth * sth); - Real up1 = m::sqrt((-1. + m::sqrt(1. + 4. * l * l * expm2chi)) / 2.); - Real up = 2. * a * r * m::sqrt(1. + up1 * up1) / m::sqrt(AA * SS * DD) + - m::sqrt(SS / AA) * up1 / sth; - - const Real ucon_tilt[GR_DIM] = {0., 0., 0., up}; - Real ucon_bl[GR_DIM]; - rotate_polar_vec(Xmidplane, ucon_tilt, -tilt, Xembed, ucon_bl); - - Real gcov_bl[GR_DIM][GR_DIM]; - blcoords.gcov_embed(Xembed, gcov_bl); - set_ut(gcov_bl, ucon_bl); - - // Then transform that 4-vector to KS if necessary, - // and then to native coordinates - Real ucon_native[GR_DIM]; - if (use_ks) { - Real ucon_ks[GR_DIM]; - kscoords.vec_from_bl(Xembed, ucon_bl, ucon_ks); - G.coords.con_vec_to_native(Xnative, ucon_ks, ucon_native); - } else { - G.coords.con_vec_to_native(Xnative, ucon_bl, ucon_native); - } - - // Convert native 4-vector to primitive u-twiddle, see Gammie '04 - Real gcon[GR_DIM][GR_DIM], u_prim[NVEC]; - G.gcon(Loci::center, j, i, gcon); - fourvel_to_prim(gcon, ucon_native, u_prim); - - rho(k, j, i) = rho_l; - u(k, j, i) = u_l; - uvec(0, k, j, i) = u_prim[0]; - uvec(1, k, j, i) = u_prim[1]; - uvec(2, k, j, i) = u_prim[2]; - // EMHD variables - if (conduction) - q(k, j, i) = 0.; - if (viscosity) - dP(k, j, i) = 0.; - } - } - ); - - // Find rho_max "analytically" by looking over the whole mesh domain for the maximum in the midplane - // Done device-side for speed (for large 2D meshes this may get bad) but may work fine in HostSpace - // Note this covers the full domain on each rank: it doesn't need a grid so it's not a memory problem, - // and an MPI synch as is done for beta_min would be a headache - GReal x1min = pmb->pmy_mesh->mesh_size.x1min; - GReal x1max = pmb->pmy_mesh->mesh_size.x1max; - // Add back 2D if torus solution may not be largest in midplane (before tilt ofc) - //GReal x2min = pmb->pmy_mesh->mesh_size.x2min; - //GReal x2max = pmb->pmy_mesh->mesh_size.x2max; - GReal dx = 0.001; - int nx1 = (x1max - x1min) / dx; - //int nx2 = (x2max - x2min) / dx; - - // If we print diagnostics, do so only from block 0 as the others do exactly the same thing - // Since this is initialization, we are guaranteed to have a block 0 - if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { - std::cout << "Calculating maximum density:" << std::endl; - std::cout << "a = " << a << std::endl; - std::cout << "dx = " << dx << std::endl; - std::cout << "x1min->x1max: " << x1min << " " << x1max << std::endl; - std::cout << "nx1 = " << nx1 << std::endl; - //cout << "x2min->x2max: " << x2min << " " << x2max << std::endl; - //cout << "nx2 = " << nx2 << std::endl; - } - - Real rho_max = 0; - Kokkos::Max max_reducer(rho_max); - pmb->par_reduce("fm_torus_maxrho", 0, nx1, - KOKKOS_LAMBDA (const int& i, Real& local_result) { - GReal x1 = x1min + i*dx; - //GReal x2 = x2min + j*dx; - GReal Xnative[GR_DIM] = {0,x1,0,0}; - GReal Xembed[GR_DIM]; - G.coords.coord_to_embed(Xnative, Xembed); - const GReal r = Xembed[1]; - // Regardless of native coordinate shenanigans, - // set th=pi/2 since the midplane is densest in the solution - const GReal rho = fm_torus_rho(a, rin, rmax, gam, kappa, r, M_PI/2.); - // TODO umax for printing/recording? - - // Record max - if (rho > local_result) local_result = rho; - } - , max_reducer); - - // Record and print normalization factor - if(! (pmb->packages.Get("GRMHD")->AllParams().hasKey("rho_norm"))) - pmb->packages.Get("GRMHD")->AllParams().Add("rho_norm", rho_max); - if (pmb->gid == 0 && pmb->packages.Get("Globals")->Param("verbose") > 0) { - std::cout << "Initial maximum density is " << rho_max << std::endl; - } - - pmb->par_for("fm_torus_normalize", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { - rho(k, j, i) /= rho_max; - u(k, j, i) /= rho_max; - } - ); - - return TaskStatus::complete; -} diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index b29f2ef2..af968674 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -49,8 +49,9 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter GridVector uvec = rc->Get("prims.uvec").data; GridVector B_P = rc->Get("prims.B").data; - // Have a look at InitializeFMTorusEMHD for the EMHD torus initialization - const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", false); + // Are we using EMHD? + // TODO does anything really change? If so use packages.count + //const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", false); const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index c01c5148..7406ac32 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -11,11 +11,6 @@ * @param rmax is the radius of maximum density of the F-M torus in r_g */ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin); -/** - * Need a different initialization function since we have additional fields (q, dP) - * for the EMHD problem that are declared at runtime - */ -TaskStatus InitializeFMTorusEMHD(std::shared_ptr>& rc, ParameterInput *pin); /** * Perturb the internal energy by a uniform random proportion per cell. diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 2fddaa30..a0182798 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -281,5 +281,9 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // And make sure the trivial primitive values are up-to-date Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); + auto pouts = std::make_unique(pmesh, pin, &tm); + pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); + Flag("Post-initialization finished"); } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 6540bed6..7cd9ab4d 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -80,10 +80,6 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) std::cout << "Initializing problem: " << prob << std::endl; } - // Using EMHD package affects problem dispatch - // TODO(BSP) handle in fm_torus problem? - auto use_emhd = pin->GetOrAddBoolean("emhd", "on", false); - // Breakout to call the appropriate initialization function, // defined in accompanying headers. @@ -120,13 +116,9 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) status = InitializeEMHDShock(rc, pin); } else if (prob == "conducting_atmosphere") { status = InitializeAtmosphere(rc, pin); - } else if (prob == "bondi_viscous") { - status = InitializeBondi(rc, pin); // Everything - } else if ((prob == "torus") && (!use_emhd)) { + } else if (prob == "torus") { status = InitializeFMTorus(rc, pin); - } else if ((prob == "torus") && (use_emhd)){ - status = InitializeFMTorusEMHD(rc, pin); } else if (prob == "resize_restart") { status = ReadIharmRestart(rc, pin); } else if (prob == "resize_restart_kharma") { // Hyerin @@ -173,6 +165,10 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) Flux::BlockPtoU(rc.get(), IndexDomain::interior); // Floors are NOT automatically applied at this point anymore. + // If needed, they should be applied inside the problem's InitializeXXXX + + // Finally, freeze in the current ghost zone values if using Dirichlet conditions + KBoundaries::FreezeDirichletBlock(rc.get()); EndFlag("Initialize "+prob); } diff --git a/pars/bondi_b.par b/pars/bondi_b.par index 50d011d3..f92aa641 100644 --- a/pars/bondi_b.par +++ b/pars/bondi_b.par @@ -49,8 +49,8 @@ rho_min_geom = 1e-20 u_min_geom = 1e-20 # We'll be adding material, and that's okay - -check_inflow_outer = false + +check_inflow_outer_x1 = false verbose = 1 diff --git a/pars/bondi_b_vertical.par b/pars/bondi_b_vertical.par index 4777ac50..bc54296e 100644 --- a/pars/bondi_b_vertical.par +++ b/pars/bondi_b_vertical.par @@ -43,8 +43,8 @@ rs = 8.0 disable_floors = true # We'll be adding material, and that's okay - -check_inflow_outer = false + +check_inflow_outer_x1 = false # Constant pure-vertical field diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index 500444a9..e2ce9389 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -1,7 +1,7 @@ # Viscous Bondi flow problem -problem_id = bondi_viscous +problem_id = bondi # Full mesh size, no refinement @@ -12,7 +12,7 @@ nx2 = 128 nx3 = 1 -nx1 = 128 +nx1 = 64 nx2 = 64 nx3 = 1 @@ -34,6 +34,7 @@ reconstruction = weno5 implicit = true +type = monopole_cube implicit = false initial_cleanup = false @@ -52,27 +53,25 @@ use_qr = true # IMPORTANT: This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = true -feedback = false - +feedback = false +stability_limits = true conduction = false -viscosity = true - +viscosity = true closure_type = kappa_eta -tau = 30. -eta = 0.01 +tau = 30. +eta = 0.01 mdot = 1.0 rs = 8.0 -disable_floors = true -enable_emhd_limits = false +disable_floors = true - -check_inflow_outer = false + +check_inflow_outer_x1 = false verbose = 1 @@ -82,7 +81,7 @@ flag_verbose = 2 file_type = hdf5 dt = 100.0 single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.dP, solve_norm, solve_fail +variables = prims, solve_norm, solve_fail file_type = hst diff --git a/pars/bz_monopole.par b/pars/bz_monopole.par index ba0ba260..cbf3b13f 100644 --- a/pars/bz_monopole.par +++ b/pars/bz_monopole.par @@ -1,8 +1,4 @@ -# FIXME TODO rewrite this head -# SANE model mirroring the simulation library -# Overall simulation size 50M, to allow -# running at small scale on e.g. a laptop -# Uses MKS coordinates, not Funky variant +# Monopole in vacuum problem_id = bz_monopole @@ -39,7 +35,7 @@ extra_checks = 1 flag_verbose = 0 -cfl = 0.9 +cfl = 0.7 gamma = 1.444444 reconstruction = weno5 @@ -48,10 +44,11 @@ type = bz_monopole norm = false -bsq_over_rho_max = 5000 -bsq_over_u_max = 50 +bsq_over_rho_max = 100 +#bsq_over_u_max = 100 rho_min_geom = 1e-20 u_min_geom = 1e-20 +gamma_max = 5 on = false @@ -64,7 +61,7 @@ power = 40 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, fflag, pflag +variables = prims, fflag, pflag ghost_zones = true diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index 14b602d6..831a6613 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -30,9 +30,10 @@ r_in = 200. r_out = 300. -prob_uses_dirichlet = true -check_inflow_inner = false -check_inflow_outer = false +inner_x1 = dirichlet +outer_x1 = dirichlet +check_inflow_inner_x1 = false +check_inflow_outer_x1 = false tlim = 150. @@ -40,14 +41,6 @@ tlim = 150. type = imex - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 -linesearch = true -max_linesearch_iter = 3 -linesearch_eps = 1.e-4 - implicit = true cfl = 0.9 @@ -58,34 +51,43 @@ reconstruction = weno5 implicit = false initial_cleanup = false + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 # IMPORTANT: This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = true -feedback = true - -closure_type = kappa_eta -tau = 10. -kappa = 0.1 -eta = 0.0 +feedback = true +stability_limits = true +closure_type = kappa_eta +tau = 10. +kappa = 0.1 +eta = 0.0 input = ODE disable_floors = true -emhd_limits = false verbose = 1 +flag_verbose = 2 +extra_checks = 1 -file_type = hdf5 -dt = 10 +file_type = hdf5 +dt = 10 single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP +variables = prims, cons, solve_norm, solve_fail +ghost_zones = true file_type = hst -dt = 100 +dt = 100 diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index f89404ae..e6299d56 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -58,7 +58,6 @@ amp = 1e-8 disable_floors = true -enable_emhd_limits = false min_nonlinear_iter = 1 @@ -87,6 +86,7 @@ flag_verbose = 0 on = true higher_order_terms = false feedback = true +stability_limits = true conduction = true viscosity = true diff --git a/pars/hubble.par b/pars/hubble.par index 17d6c53c..b38e112f 100644 --- a/pars/hubble.par +++ b/pars/hubble.par @@ -32,8 +32,8 @@ nx2 = 1 nx3 = 1 -check_inflow_inner = false -check_inflow_outer = false +check_inflow_inner_x1 = false +check_inflow_outer_x1 = false base = cartesian_minkowski diff --git a/pars/noh.par b/pars/noh.par index 3361cdb7..d7af06c7 100644 --- a/pars/noh.par +++ b/pars/noh.par @@ -13,8 +13,6 @@ numlevel = 1 nx1 = 2000 x1min = 0.0 x1max = 1.0 -ix1_bc = reflecting -ox1_bc = outflow nx2 = 1 x2min = 0.0 @@ -33,6 +31,10 @@ nx3 = 1 base = cartesian_minkowski transform = null + +inner_x1 = reflecting +outer_x1 = outflow + tlim = 20.0 integrator = rk2 diff --git a/pars/sane_emhd.par b/pars/sane_emhd.par index ad666cac..9f423006 100644 --- a/pars/sane_emhd.par +++ b/pars/sane_emhd.par @@ -63,6 +63,7 @@ initial_cleanup = false on = true higher_order_terms = true feedback = true +stability_limits = true closure_type = torus conduction_alpha = 1.0 @@ -83,7 +84,6 @@ bsq_over_rho_max = 100 bsq_over_u_max = 1e20 u_over_rho_max = 100 gamma_max = 5 -enable_emhd_limits = true verbose = 1 diff --git a/run.sh b/run.sh index bbf0f414..9b3f81a9 100755 --- a/run.sh +++ b/run.sh @@ -17,8 +17,8 @@ MPI_NUM_PROCS=${MPI_NUM_PROCS:-1} MPI_EXTRA_ARGS=${MPI_EXTRA_ARGS:-} # Default OpenMP directives: use all available threads -export OMP_PROC_BIND=${OMP_PROC_BIND:-spread} -export OMP_PLACES=${OMP_PLACES:-threads} +#export OMP_PROC_BIND=${OMP_PROC_BIND:-spread} +#export OMP_PLACES=${OMP_PLACES:-threads} # Force a number of OpenMP threads if it doesn't autodetect #export OMP_NUM_THREADS=28 @@ -68,6 +68,11 @@ if [[ "$1" == "-nt" ]]; then shift shift fi +if [[ "$1" == "-b" ]]; then + EXE_NAME="$2" + shift + shift +fi # Run based on preferences if [ -z "$MPI_EXE" ]; then diff --git a/scripts/batch/multizone/multizone.par b/scripts/batch/multizone/multizone.par index 59711f3f..0f961f04 100755 --- a/scripts/batch/multizone/multizone.par +++ b/scripts/batch/multizone/multizone.par @@ -73,10 +73,10 @@ gamma_max = 10 # Does not affect these floors adjust_k = 0 - + # Inflow is allowed -check_inflow_outer = false -check_inflow_inner = false +check_inflow_outer_x1 = false +check_inflow_inner_x1 = false # Otherwise defaults diff --git a/tests/all_pars/run.sh b/tests/all_pars/run.sh index 9159a4d0..9b561044 100755 --- a/tests/all_pars/run.sh +++ b/tests/all_pars/run.sh @@ -3,5 +3,6 @@ set -euo pipefail for fil in ../../pars/*.par do - ../../run.sh -i $fil parthenon/time/nlim=2 + ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 + rm *.{hst,phdf,rhdf,xdmf} done diff --git a/tests/anisotropic_conduction/make_plots.py b/tests/anisotropic_conduction/make_plots.py new file mode 100644 index 00000000..04699509 --- /dev/null +++ b/tests/anisotropic_conduction/make_plots.py @@ -0,0 +1,112 @@ +# PLOT SNAKE TEST + +import numpy as np +import os, h5py, psutil, glob +import multiprocessing as mp +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +from matplotlib import gridspec +from mpl_toolkits.axes_grid1 import make_axes_locatable +import matplotlib as mpl +import warnings +import pyharm + +warnings.filterwarnings("ignore") + +mpl.rcParams['figure.dpi'] = 120 +mpl.rcParams['savefig.dpi'] = 120 +mpl.rcParams['figure.autolayout'] = True +mpl.rcParams['figure.figsize'] = (6,6) +mpl.rcParams['axes.titlesize'] = 18 +mpl.rcParams['axes.labelsize'] = 16 +mpl.rcParams['xtick.labelsize'] = 14 +mpl.rcParams['ytick.labelsize'] = 14 +mpl.rcParams['text.usetex'] = False +mpl.rcParams['font.family'] = 'serif' +mpl.rcParams["font.serif"] = 'cmr10', +mpl.rcParams["font.monospace"] = 'Computer Modern Typewriter' +mpl.rcParams["mathtext.fontset"]= 'cm' + +params = {} +fluxes = {} + + +def calc_threads(pad=0.8): + Nthreads = int(psutil.cpu_count(logical=False)*pad) + return Nthreads + + +def run_parallel(function, dlist, nthreads): + pool = mp.Pool(nthreads) + pool.map_async(function,dlist).get(720000) + pool.close() + pool.join() + + +# Function to overlay field lines +# Argument must be axes object, B1, B2 and 'nlines' -> a parameter to account for density of field lines +def plotting_bfield_lines(ax, dump, nlines=20): + B1 = np.squeeze(dump['B1']) + B2 = np.squeeze(dump['B2']) + AJ_phi = np.zeros([dump['n1'], dump['n2']]) + for j in range(dump['n2']): + for i in range(dump['n1']): + AJ_phi[dump['n1']-1-i,j] = (np.trapz(B2[:i,j], dx=dump['dx1']) - np.trapz(B1[i,:j], dx=dump['dx2'])) + AJ_phi -= AJ_phi.min() + levels = np.linspace(0, AJ_phi.max(), nlines) + ax.contour(np.squeeze(dump['X1']), np.squeeze(dump['X2']), AJ_phi, levels=levels, colors='k') + + +# Plot +def plot(dumpno): + print("Plotting dump {0:04d}".format(dumpno)) + + dump = pyharm.load_dump(os.path.join(params['dumpsdir'], 'anisotropic_conduction.out0.{:05d}.phdf'.format(dumpno))) + + fig = plt.figure() + nrows = 1 + ncols = 1 + heights = [1,16] + gs = gridspec.GridSpec(nrows=nrows, ncols=ncols, figure=fig) + + # t = "{:.3f}".format(dump['t']) + + # ax0 = fig.add_subplot(gs[0,:]) + # ax0.annotate('t= '+str(t)+'M', xy=(0.5,0.5), xycoords='axes fraction', va='center', ha='center', fontsize='xx-large') + # ax0.axis("off") + + ax1 = fig.add_subplot(gs[0,0]) + temp_plot = ax1.pcolormesh(np.squeeze(dump['X1']), np.squeeze(dump['X2']), np.squeeze(dump['Theta']),\ + cmap = 'viridis', shading='gouraud') + plotting_bfield_lines(ax1, dump, nlines=20) + ax1.set_xlim(0,1) + ax1.set_ylim(0,1) + ax1.set_xticks([0,0.25,0.5,0.75,1]) + ax1.set_xticklabels([0,0.25,0.5,0.75,1]) + ax1.set_yticks([0,0.25,0.5,0.75,1]) + ax1.set_yticklabels([0,0.25,0.5,0.75,1]) + # ax1.set_title('$\\Theta$') + ax1.set_xlabel('$x (GM/c^2)$') + ax1.set_ylabel('$y (GM/c^2)$') + ax1.set_aspect('equal') + # divider = make_axes_locatable(ax1) + # cax = divider.append_axes("right", size="5%", pad=0.05) + # cbar = plt.colorbar(temp_plot, cax=cax) + + plt.savefig(os.path.join(params['plotsdir'], 'temperature_plot_{:04d}.png'.format(dumpno))) + plt.close() + + +if __name__=='__main__': + params['dumpsdir'] = './dumps_kharma' + params['dfirst'] = 0 + params['dlast'] = int(sorted(glob.glob(os.path.join(params['dumpsdir'], 'anisotropic_conduction.out0.0*phdf')))[-1][-9:-5]) + dlist = range(params['dfirst'], params['dlast']+1) + + params['plotsdir'] = './plots' + if not os.path.exists(params['plotsdir']): + os.makedirs(params['plotsdir']) + + nthreads = calc_threads() + run_parallel(plot, dlist, nthreads) \ No newline at end of file diff --git a/tests/bflux/run.sh b/tests/bflux/run.sh deleted file mode 100755 index 2494f1a7..00000000 --- a/tests/bflux/run.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash -# Hyerin (02/17/23) copied from Ben's code - -# Bash script testing nonzero b flux - -# User specified values here -KERR=false -bz=1e-4 -DIM=3 -NZONES=7 -BASE=8 -NRUNS=100 -START_RUN=0 -DRTAG="bondi_multizone_032023_fixfluxx1_${bz}_n7b8" - -# Set paths -KHARMADIR=../.. -PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory -DR="${PDR}data/${DRTAG}" -parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file - -# other values determined automatically -turn_around=$(($NZONES-1)) -start_time=0 #83964 # -out_to_in=1 -iteration=1 #13 # -r_out=$((${BASE}**($turn_around+2))) #64 # -r_in=$((${BASE}**$turn_around)) #1 # - -# if the directories are not present, make them. -if [ ! -d "${DR}" ]; then - mkdir "${DR}" -fi -if [ ! -d "${PDR}logs/${DRTAG}" ]; then - mkdir "${PDR}logs/${DRTAG}" -fi - -### Start running zone by zone -for (( VAR=$START_RUN; VAR<$NRUNS; VAR++ )) -do - args=() - echo "${DRTAG}: iter $iteration, $VAR : t = $start_time, r_out = $r_out, r_in = $r_in" - logruntime=`echo "scale=20; l($r_out)*3./2-l(1.+$r_out/100000)/2." | bc -l` # round to an integer for the free-fall time (cs^2=0.01 should be updated from the desired rs value) # GIZMO - runtime=`echo "scale=0; e($logruntime)+1" | bc -l` - log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) - start_time=$(($start_time+$runtime)) - - #parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_$(printf %05d ${VAR}).par" # parameter file - - # set problem type and cleanup - if [ $VAR -eq 0 ]; then - prob="bondi" - else - prob="resize_restart_kharma" - fi - - # set BH spin - if [[ $KERR == "true" ]]; then - spin=0.99 - else - spin=0.0 - fi - - # output time steps - output0_dt=$((${runtime}/100*10)) - #output1_dt=$((${runtime}/20*10)) - output1_dt=$((${runtime}/200*10)) # test Hyerin (02/20/23) - output2_dt=$((${runtime}/1000*10)) - - # dt, fname, fname_fill - if [ $VAR -ne 0 ]; then - # update dt from the previous run - tag=($( tail -n 10 ${PDR}/logs/${DRTAG}/log_multizone$(printf %05d $((${VAR}-1)))_out )) - dt=$(printf "%.18g" "${tag[2]:3}") # previous dt - dt_new=$(echo "scale=14; $dt*sqrt($BASE^(-3*$out_to_in))/4" | bc -l) # new dt ## TODO: r^3/2 - if (( $(echo "$dt_new > 0.00001" |bc -l) )); then - dt_new=$dt_new - else - dt_new=0.00001 - fi - fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" - fname=$(find ${fname_dir} -type f -iname "*final.rhdf") - if [ $VAR -ge $NZONES ]; then - fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) - fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" - fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") - else - fname_fill="none" - fi - args+=(" resize_restart/fname=$fname parthenon/time/dt_min=$dt_new") - args+=(" resize_restart/fname_fill=$fname_fill ") - else - r_shell=$((${r_out}/2)) - args+=(" bondi/r_shell=$r_shell ") - fi - - # data_dir, logfiles - data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" - out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" - err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - - srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ - parthenon/mesh/nx1=64 parthenon/mesh/nx2=64 parthenon/mesh/nx3=64 \ - parthenon/meshblock/nx1=32 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=32 \ - parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time} \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=false\ - coordinates/transform=mks coordinates/hslope=1 \ - bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - floors/disable_floors=false floors/rho_min_geom=1e-6 floors/u_min_geom=1e-8 \ - floors/bsq_over_rho_max=100 floors/bsq_over_u_max=50 floors/u_over_rho_max=100 floors/gamma_max=5 \ - b_field/type=vertical b_field/solver=flux_ct b_field/bz=${bz} \ - b_field/fix_flux_x1=1 b_field/initial_cleanup=0 \ - resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ - parthenon/output0/dt=$output0_dt \ - parthenon/output1/dt=$output1_dt \ - parthenon/output2/dt=$output2_dt \ - ${args[@]} \ - -d ${data_dir} 1> ${out_fn} 2>${err_fn} - # parthenon/time/nlim=$((10000*($VAR+1))) - #floors/bsq_over_rho_max=100 floors/u_over_rho_max=2 \ - - if [ $VAR -ne 0 ]; then - if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then - out_to_in=$(($out_to_in*(-1))) - iteration=$(($iteration+1)) - fi - fi - - if [ $out_to_in -gt 0 ]; then - # half the radii - r_out=$((${r_out}/$BASE)) - r_in=$((${r_in}/$BASE)) - else - # double the radii - r_out=$((${r_out}*$BASE)) - r_in=$((${r_in}*$BASE)) - fi -done diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index f2cbffe9..ff75ed2d 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -28,6 +28,8 @@ conv_2d() { fi } +conv_2d dirichlet "boundaries/inner_x1=dirichlet boundaries/outer_x1=dirichlet" "in 2D, Dirichlet boundaries" + # Test coordinates #conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" conv_2d mks coordinates/transform=mks "in 2D, MKS coordinates" diff --git a/tests/bondi_multizone/run.sh b/tests/bondi_multizone/run.sh deleted file mode 100755 index 395a7e64..00000000 --- a/tests/bondi_multizone/run.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash -# Hyerin (02/17/23) copied from Ben's code - -# Bash script testing HD bondi - -# User specified values here -KERR=false -JITTER=false #true # -DIM=3 -NZONES=7 -BASE=8 -NRUNS=300 -START_RUN=53 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from -DRTAG="bondi_multizone_030723_bondi_128^3" - -# Set paths -KHARMADIR=../.. -PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory -DR="${PDR}data/${DRTAG}" -parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file - -# other values determined automatically -turn_around=$(($NZONES-1)) -start_time=32963095169 #0 # -out_to_in=1 # -1 # -iteration=9 # eq : (iteration-1)*(NZONES-1) 0.00001" |bc -l) )); then - dt_new=$dt_new - else - dt_new=0.00001 - fi - fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" - fname=$(find ${fname_dir} -type f -iname "*final.rhdf") - if [ $VAR -ge $NZONES ]; then - fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) - fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" - fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") - else - fname_fill="none" - fi - args+=(" resize_restart/fname=$fname resize_restart/use_dt=false parthenon/time/dt_min=$dt_new") - args+=(" resize_restart/fname_fill=$fname_fill ") - else - r_shell=$((${r_out}/2)) - args+=(" bondi/r_shell=$r_shell ") - if [[ $JITTER == "true" ]]; then - args+=(" perturbation/u_jitter=0.3 ") - else - args+=(" perturbation/u_jitter=0.0 ") - fi - fi - - - - # data_dir, logfiles - data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" - out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" - err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - - srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ - parthenon/job/problem_id=$prob \ - parthenon/time/tlim=${start_time} parthenon/time/nlim=-1 \ - parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ - parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=128 \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=false \ - coordinates/transform=mks coordinates/hslope=1 \ - bounds/fix_flux_pole=1 \ - bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} bondi/use_gizmo=false \ - b_field/type=none b_field/solver=none b_field/bz=1e-3 \ - b_field/fix_flux_x1=0 b_field/initial_cleanup=0 \ - resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration\ - parthenon/output0/dt=$output0_dt \ - parthenon/output1/dt=$output1_dt \ - parthenon/output2/dt=$output2_dt \ - ${args[@]} \ - -d ${data_dir} 1> ${out_fn} 2>${err_fn} - - if [ $VAR -ne 0 ]; then - if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then - out_to_in=$(($out_to_in*(-1))) - iteration=$(($iteration+1)) - fi - fi - - if [ $out_to_in -gt 0 ]; then - # half the radii - r_out=$((${r_out}/$BASE)) - r_in=$((${r_in}/$BASE)) - else - # double the radii - r_out=$((${r_out}*$BASE)) - r_in=$((${r_in}*$BASE)) - fi -done diff --git a/kharma/prob/emhd/bondi_viscous_128_default/bondi_analytic_128.txt b/tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt similarity index 100% rename from kharma/prob/emhd/bondi_viscous_128_default/bondi_analytic_128.txt rename to tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt diff --git a/kharma/prob/emhd/bondi_viscous_256_default/bondi_analytic_256.txt b/tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt similarity index 100% rename from kharma/prob/emhd/bondi_viscous_256_default/bondi_analytic_256.txt rename to tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt diff --git a/kharma/prob/emhd/bondi_viscous_32_default/bondi_analytic_32.txt b/tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt similarity index 100% rename from kharma/prob/emhd/bondi_viscous_32_default/bondi_analytic_32.txt rename to tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt diff --git a/kharma/prob/emhd/bondi_viscous_64_default/bondi_analytic_64.txt b/tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt similarity index 100% rename from kharma/prob/emhd/bondi_viscous_64_default/bondi_analytic_64.txt rename to tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index de26b944..065220d7 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -9,97 +9,96 @@ if __name__=='__main__': - outputdir = './' - kharmadir = '../../' + outputdir = './' + kharmadir = '../../' - NVAR = 3 - VARS = ['rho', 'u', 'dP'] - RES = [int(r) for r in sys.argv[1].split(",")] - LONG = sys.argv[2] - SHORT = sys.argv[3] - - L1 = np.zeros([len(RES), NVAR]) - fit = np.zeros([len(RES), NVAR]) + NVAR = 3 + VARS = ['rho', 'u', 'dP'] + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] + + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) - for r, res in enumerate(RES): - - # load analytic result - rho_analytic, uu_analytic, dP_analytic = np.loadtxt(os.path.join(kharmadir, \ - 'kharma/prob/emhd/','bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)), \ - usecols=(0,1,3), unpack=True) - - # load code data - dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) - - params = dump.params - rho = np.squeeze(dump['RHO']) - uu = np.squeeze(dump['UU']) - dP_tilde = np.squeeze(dump['prims'][8,Ellipsis]) + for r, res in enumerate(RES): + + # load analytic result + fpath = os.path.join(os.curdir,'bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)) + rho_analytic, uu_analytic, dP_analytic = np.loadtxt(fpath, usecols=(0,1,3), unpack=True) + + # load code data + dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) + + params = dump.params + rho = np.squeeze(dump['RHO']) + uu = np.squeeze(dump['UU']) + dP_tilde = np.squeeze(dump['prims'][8,Ellipsis]) - t = dump['t'] - gam = params['gam'] - tau = params['tau'] - eta = params['eta'] - higher_order_terms = params['higher_order_terms'] + t = dump['t'] + gam = params['gam'] + tau = params['tau'] + eta = params['eta'] + higher_order_terms = params['higher_order_terms'] # compute dP - if higher_order_terms=="true": - print("Res: "+str(res)+"; higher order terms enabled") - P = (gam - 1.) * uu - Theta = P / rho - nu_emhd = eta / rho - dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) - else: - dP = dP_tilde - - # compute L1 norm - L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) - L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) - L1[r,2] = np.mean(np.fabs(dP - dP_analytic[:,None])[1:-1]) + if higher_order_terms=="true": + print("Res: "+str(res)+"; higher order terms enabled") + P = (gam - 1.) * uu + Theta = P / rho + nu_emhd = eta / rho + dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) + else: + dP = dP_tilde + + # compute L1 norm + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(dP - dP_analytic[:,None])[1:-1]) - # MEASURE CONVERGENCE - L1 = np.array(L1) - powerfits = [0.,]*NVAR - fail = 0 - for k in range(NVAR): - powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] - print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) - if powerfits[k] > -2 or powerfits[k] < -2.7: - fail = 1 - - - # plotting parameters - mpl.rcParams['figure.dpi'] = 300 - mpl.rcParams['savefig.dpi'] = 300 - mpl.rcParams['figure.autolayout'] = True - mpl.rcParams['axes.titlesize'] = 16 - mpl.rcParams['axes.labelsize'] = 14 - mpl.rcParams['xtick.labelsize'] = 12 - mpl.rcParams['ytick.labelsize'] = 12 - mpl.rcParams['axes.xmargin'] = 0.02 - mpl.rcParams['axes.ymargin'] = 0.02 - mpl.rcParams['legend.fontsize'] = 'medium' - colors = ['indigo', 'goldenrod', 'darkgreen', 'crimson', 'xkcd:blue'] + # MEASURE CONVERGENCE + L1 = np.array(L1) + powerfits = [0.,]*NVAR + fail = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -2 or powerfits[k] < -2.7: + fail = 1 + + + # plotting parameters + mpl.rcParams['figure.dpi'] = 300 + mpl.rcParams['savefig.dpi'] = 300 + mpl.rcParams['figure.autolayout'] = True + mpl.rcParams['axes.titlesize'] = 16 + mpl.rcParams['axes.labelsize'] = 14 + mpl.rcParams['xtick.labelsize'] = 12 + mpl.rcParams['ytick.labelsize'] = 12 + mpl.rcParams['axes.xmargin'] = 0.02 + mpl.rcParams['axes.ymargin'] = 0.02 + mpl.rcParams['legend.fontsize'] = 'medium' + colors = ['indigo', 'goldenrod', 'darkgreen', 'crimson', 'xkcd:blue'] - # plot - plt.close() - fig = plt.figure(figsize=(6,6)) - ax = fig.add_subplot(1,1,1) + # plot + plt.close() + fig = plt.figure(figsize=(6,6)) + ax = fig.add_subplot(1,1,1) - # loop over prims - tracker = 0 - for n in range(len(VARS)): - color = colors[tracker] - ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) - tracker+=1 + # loop over prims + tracker = 0 + for n in range(len(VARS)): + color = colors[tracker] + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) + tracker+=1 - ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') - # ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') - plt.xscale('log', base=2) - ax.set_xlabel('Resolution') - ax.set_ylabel('L1 norm') - ax.legend() - plt.savefig(os.path.join(outputdir, "bondi_viscous_convergence_"+SHORT+".png"), dpi=300) + ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + # ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + plt.xscale('log', base=2) + ax.set_xlabel('Resolution') + ax.set_ylabel('L1 norm') + ax.legend() + plt.savefig(os.path.join(outputdir, "bondi_viscous_convergence_"+SHORT+".png"), dpi=300) - exit(fail) + exit(fail) diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index a5a01b31..b8320ee6 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -euo pipefail +#set -euo pipefail BASE=../.. @@ -8,34 +8,34 @@ exit_code=0 # Viscous bondi inflow convergence to exercise all terms in the evolution equation of dP conv_2d() { - IFS=',' read -ra RES_LIST <<< "$ALL_RES" - for res in "${RES_LIST[@]}" - do - # Four blocks - # half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ - parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ - parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ - b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" + do + # Four blocks + half=$(( $res / 2 )) + $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ + parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ + parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ + b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 - mv bondi_viscous.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf - mv bondi_viscous.out0.final.phdf emhd_2d_${res}_end_${1}.phdf - done - check_code=0 - # pyharm-convert --double *.phdf - python check.py $ALL_RES $1 2d || check_code=$? - # rm -r *.phdf - rm -r *.xdmf - rm -r *.out0* - if [[ $check_code != 0 ]]; then - echo Viscous Bondi test $3 FAIL: $check_code - exit_code=1 - else - echo Viscous Bondi test $3 success - fi + mv bondi.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf + mv bondi.out0.final.phdf emhd_2d_${res}_end_${1}.phdf + done + check_code=0 + # pyharm-convert --double *.phdf + python check.py $ALL_RES $1 2d || check_code=$? + # rm -r *.phdf + rm -r *.xdmf + rm -r *.out0* + if [[ $check_code != 0 ]]; then + echo Viscous Bondi test $3 FAIL: $check_code + exit_code=1 + else + echo Viscous Bondi test $3 success + fi } -ALL_RES="32,64,128,256" -conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Viscous Bondi in 2D, WENO5" +ALL_RES="64,128,256" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" exit $exit_code diff --git a/tests/bz_monopole/check.py b/tests/bz_monopole/check.py index acb2dc3f..4ac9aa99 100755 --- a/tests/bz_monopole/check.py +++ b/tests/bz_monopole/check.py @@ -11,11 +11,17 @@ # Plots ONLY; no automated failures for dumpname in np.sort(glob.glob("bz_monopole.out0.*.phdf")): dump = pyharm.load_dump(dumpname) - fig, ax = plt.subplots(1,1,figsize=(7,7)) - hplt.plot_xz(ax, dump, 'log_U1', arrayspace=True, window=[0,1,0,1]) - plt.savefig(dumpname+"_U1.png") + try: + fig, ax = plt.subplots(1,1,figsize=(7,7)) + hplt.plot_xz(ax, dump, 'U1', log=True, arrayspace=True, window=[0,1,0,1]) + fig.savefig(dumpname+"_U1.png") + plt.close(fig) + except: + print("Error plotting U1 of {}".format(dumpname)) fig, ax = plt.subplots(1,1,figsize=(4,7)) - hplt.plot_xz(ax, dump, 'rho', window=(-10,0,-10,10)) + hplt.plot_xz(ax, dump, 'rho', log=True, window=(-10,0,-10,10)) hplt.overlay_field(ax, dump, nlines=8) - plt.savefig(dumpname+"_rho.png") + fig.savefig(dumpname+"_rho.png") + plt.close(fig) + del dump diff --git a/tests/bz_monopole/run.sh b/tests/bz_monopole/run.sh index 8d31c601..ad9ff9de 100755 --- a/tests/bz_monopole/run.sh +++ b/tests/bz_monopole/run.sh @@ -1,16 +1,18 @@ #!/bin/bash -set -euo pipefail +#set -euo pipefail BASE=../.. +exit_code=0 + # Full run to test stability to completion -$BASE/run.sh -i $BASE/pars/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 +$BASE/run.sh -i $BASE/pars/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 #|| exit_code=$? # At *least* check divB -pyharm-check-basics bz_monopole.out0.final.phdf +pyharm-check-basics bz_monopole.out0.final.phdf || exit_code=$? # Take 1 step to look for early signs of non-fatal instabilities -$BASE/run.sh -i $BASE/pars/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 +$BASE/run.sh -i $BASE/pars/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 #|| exit_code=$? # Check is for plots only! python ./check.py diff --git a/tests/clean_tests.sh b/tests/clean_tests.sh index 62249051..1a6c541c 100755 --- a/tests/clean_tests.sh +++ b/tests/clean_tests.sh @@ -2,4 +2,4 @@ # Cleans all temporary/gitignore files from tests TEST_DIR=$(dirname "$(readlink -f "$0")") -rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* +rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,h5,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index c73cd5e6..6f758167 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -9,98 +9,99 @@ if __name__=='__main__': - outputdir = './' - kharmadir = '../../' + outputdir = './' + kharmadir = '../../' - NVAR = 3 - VARS = ['rho', 'u', 'q'] - NG = 4 - RES = [int(r) for r in sys.argv[1].split(",")] - LONG = sys.argv[2] - SHORT = sys.argv[3] + NVAR = 3 + VARS = ['rho', 'u', 'q'] + NG = 4 + RES = [int(r) for r in sys.argv[1].split(",")] + LONG = sys.argv[2] + SHORT = sys.argv[3] - L1 = np.zeros([len(RES), NVAR]) - fit = np.zeros([len(RES), NVAR]) + L1 = np.zeros([len(RES), NVAR]) + fit = np.zeros([len(RES), NVAR]) - for r, res in enumerate(RES): - - # load analytic result - rho_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_rho.txt'))[NG:-NG] - uu_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_u.txt'))[NG:-NG] - q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/','conducting_atmosphere_{}_default'.format(res), 'atmosphere_soln_phi.txt'))[NG:-NG] - - # load code data - dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') - - rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) - uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) - q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) - - t = dfile['t'][()] - gam = dfile['header/gam'][()] - higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') + for r, res in enumerate(RES): + + # load analytic result + folder = os.path.join(os.curdir,'conducting_atmosphere_{}_default'.format(res)) + rho_analytic = np.loadtxt(os.path.join(folder, 'atmosphere_soln_rho.txt'))[NG:-NG] + uu_analytic = np.loadtxt(os.path.join(folder, 'atmosphere_soln_u.txt'))[NG:-NG] + q_analytic = np.loadtxt(os.path.join(folder, 'atmosphere_soln_phi.txt'))[NG:-NG] - # compute q - if higher_order_terms=="TRUE": - print("Res: "+str(res)+"; higher order terms enabled") - tau = 10. - kappa = 0.1 - P = (gam - 1.) * uu - Theta = P / rho - chi_emhd = kappa / rho - q = q_tilde * np.sqrt(chi_emhd * rho * Theta**2 / tau) - else: - q = q_tilde - - # compute L1 norm - # compute L1 norm - L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) - L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) - L1[r,2] = np.mean(np.fabs(q - q_analytic[:,None])[1:-1]) + # load code data + dfile = h5py.File('emhd_2d_{}_end_emhd2d_weno.h5'.format(res), 'r') + + rho = np.squeeze(dfile['prims'][Ellipsis,0][()]) + uu = np.squeeze(dfile['prims'][Ellipsis,1][()]) + q_tilde = np.squeeze(dfile['prims'][Ellipsis,8][()]) + + t = dfile['t'][()] + gam = dfile['header/gam'][()] + higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') - # MEASURE CONVERGENCE - L1 = np.array(L1) - powerfits = [0.,]*NVAR - fail = 0 - for k in range(NVAR): - powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] - print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) - if powerfits[k] > -1.6 or powerfits[k] < -2.2: - fail = 1 - - - # plotting parameters - mpl.rcParams['figure.dpi'] = 300 - mpl.rcParams['savefig.dpi'] = 300 - mpl.rcParams['figure.autolayout'] = True - mpl.rcParams['axes.titlesize'] = 16 - mpl.rcParams['axes.labelsize'] = 14 - mpl.rcParams['xtick.labelsize'] = 12 - mpl.rcParams['ytick.labelsize'] = 12 - mpl.rcParams['axes.xmargin'] = 0.02 - mpl.rcParams['axes.ymargin'] = 0.02 - mpl.rcParams['legend.fontsize'] = 'medium' - colors = ['indigo', 'goldenrod', 'darkgreen', 'crimson', 'xkcd:blue'] + # compute q + if higher_order_terms=="TRUE": + print("Res: "+str(res)+"; higher order terms enabled") + tau = 10. + kappa = 0.1 + P = (gam - 1.) * uu + Theta = P / rho + chi_emhd = kappa / rho + q = q_tilde * np.sqrt(chi_emhd * rho * Theta**2 / tau) + else: + q = q_tilde + + # compute L1 norm + # compute L1 norm + L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) + L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) + L1[r,2] = np.mean(np.fabs(q - q_analytic[:,None])[1:-1]) + # MEASURE CONVERGENCE + L1 = np.array(L1) + powerfits = [0.,]*NVAR + fail_flag = 0 + for k in range(NVAR): + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + if powerfits[k] > -1.6 or powerfits[k] < -2.2: + fail_flag = 1 + + + # plotting parameters + mpl.rcParams['figure.dpi'] = 300 + mpl.rcParams['savefig.dpi'] = 300 + mpl.rcParams['figure.autolayout'] = True + mpl.rcParams['axes.titlesize'] = 16 + mpl.rcParams['axes.labelsize'] = 14 + mpl.rcParams['xtick.labelsize'] = 12 + mpl.rcParams['ytick.labelsize'] = 12 + mpl.rcParams['axes.xmargin'] = 0.02 + mpl.rcParams['axes.ymargin'] = 0.02 + mpl.rcParams['legend.fontsize'] = 'medium' + colors = ['indigo', 'goldenrod', 'darkgreen', 'crimson', 'xkcd:blue'] - # plot - plt.close() - fig = plt.figure(figsize=(6,6)) - ax = fig.add_subplot(1,1,1) - # loop over prims - tracker = 0 - for n in range(len(VARS)): - color = colors[tracker] - ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) - tracker+=1 + # plot + plt.close() + fig = plt.figure(figsize=(6,6)) + ax = fig.add_subplot(1,1,1) - ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') - ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') - plt.xscale('log', base=2) - ax.set_xlabel('Resolution') - ax.set_ylabel('L1 norm') - ax.legend() - plt.savefig(os.path.join(outputdir, "conducting_atmosphere_convergence_"+SHORT+".png"), dpi=300) + # loop over prims + tracker = 0 + for n in range(len(VARS)): + color = colors[tracker] + ax.loglog(RES, L1[:,n], color=color, marker='o', label=VARS[n]) + tracker+=1 - exit(fail) + ax.loglog([RES[0], RES[-1]], 0.1*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + ax.loglog([RES[0], RES[-1]], 0.001*np.asarray([float(RES[0]), float(RES[-1])])**(-2), color='k', linestyle='dashed', label='$N^{-2}$') + plt.xscale('log', base=2) + ax.set_xlabel('Resolution') + ax.set_ylabel('L1 norm') + ax.legend() + plt.savefig(os.path.join(outputdir, "conducting_atmosphere_convergence_"+SHORT+".png"), dpi=300) + + exit(fail_flag) diff --git a/kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_phi.txt b/tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_phi.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_phi.txt rename to tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_phi.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_rCoords.txt b/tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_rCoords.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_rCoords.txt rename to tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_rCoords.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_rho.txt b/tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_rho.txt rename to tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_rho.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_u.txt b/tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_u.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_128_default/atmosphere_soln_u.txt rename to tests/conducting_atmosphere/conducting_atmosphere_128_default/atmosphere_soln_u.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_phi.txt b/tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_phi.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_phi.txt rename to tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_phi.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_rCoords.txt b/tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_rCoords.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_rCoords.txt rename to tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_rCoords.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_rho.txt b/tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_rho.txt rename to tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_rho.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_u.txt b/tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_u.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_256_default/atmosphere_soln_u.txt rename to tests/conducting_atmosphere/conducting_atmosphere_256_default/atmosphere_soln_u.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_phi.txt b/tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_phi.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_phi.txt rename to tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_phi.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_rCoords.txt b/tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_rCoords.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_rCoords.txt rename to tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_rCoords.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_rho.txt b/tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_rho.txt rename to tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_rho.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_u.txt b/tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_u.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_32_default/atmosphere_soln_u.txt rename to tests/conducting_atmosphere/conducting_atmosphere_32_default/atmosphere_soln_u.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_phi.txt b/tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_phi.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_phi.txt rename to tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_phi.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_rCoords.txt b/tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_rCoords.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_rCoords.txt rename to tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_rCoords.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_rho.txt b/tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_rho.txt rename to tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_rho.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_u.txt b/tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_u.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_512_default/atmosphere_soln_u.txt rename to tests/conducting_atmosphere/conducting_atmosphere_512_default/atmosphere_soln_u.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_phi.txt b/tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_phi.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_phi.txt rename to tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_phi.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_rCoords.txt b/tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_rCoords.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_rCoords.txt rename to tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_rCoords.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_rho.txt b/tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_rho.txt rename to tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_rho.txt diff --git a/kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_u.txt b/tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_u.txt similarity index 100% rename from kharma/prob/emhd/conducting_atmosphere_64_default/atmosphere_soln_u.txt rename to tests/conducting_atmosphere/conducting_atmosphere_64_default/atmosphere_soln_u.txt diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index a717ab50..10e0b7b2 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -12,22 +12,19 @@ conv_2d() { IFS=',' read -ra RES_LIST <<< "$ALL_RES" for res in "${RES_LIST[@]}" do - cp -r ${BASE}/kharma/prob/emhd/conducting_atmosphere_${res}_default/*txt ./ - $BASE/run.sh -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ + cp conducting_atmosphere_${res}_default/atmosphere_soln_*.txt . + $BASE/run.sh -n 1 -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ - b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 + $2 >log_${1}_${res}.txt 2>&1 mv conducting_atmosphere.out0.00000.phdf emhd_2d_${res}_start_${1}.phdf mv conducting_atmosphere.out0.final.phdf emhd_2d_${res}_end_${1}.phdf + rm atmosphere_soln_*.txt done check_code=0 pyharm-convert --double *.phdf python check.py $ALL_RES $1 2d || check_code=$? - rm -r *.phdf - rm -r *.xdmf - rm -r *.out0* - rm -r ./*.txt if [[ $check_code != 0 ]]; then echo Conducting atmosphere test $3 FAIL: $check_code exit_code=1 @@ -36,5 +33,6 @@ conv_2d() { fi } -ALL_RES="64,128,256,512" -conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "Conducting atmosphere in 2D, WENO5" +#ALL_RES="64,128,256,512" +ALL_RES="64,128" +conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index 8484cc52..b0dc7579 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -32,7 +32,7 @@ conv_2d() { # 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 # Just one default mode -ALL_RES="16,32,64,128" +ALL_RES="16,32,64" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "EMHD mode in 2D, WENO5" ALL_RES="16,32,64,128,256" conv_2d emhd2d_mc GRMHD/reconstruction=linear_mc "EMHD mode in 2D, linear/MC reconstruction" diff --git a/tests/emhdshock/run.sh b/tests/emhdshock/run.sh index ff5b6a50..120201c7 100755 --- a/tests/emhdshock/run.sh +++ b/tests/emhdshock/run.sh @@ -9,13 +9,13 @@ BASE=~/kharma conv_1d() { for res in 256 512 1024 2048 do - cp -r ${BASE}/kharma/prob/emhd/shock_soln_${res}_default/*txt ./ - $BASE/run.sh -i $BASE/pars/emhdshock.par debug/verbose=1 \ + cp shock_soln_${res}_default/shock_soln_*.txt ./ + $BASE/run.sh -n 1 -i $BASE/pars/emhdshock.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=1 parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=1 parthenon/meshblock/nx3=1 mv emhdshock.out0.00000.phdf emhd_1d_${res}_start.phdf mv emhdshock.out0.final.phdf emhd_1d_${res}_end.phdf - rm ./shock_soln*.txt + rm ./shock_soln_*.txt done } diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_dP.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_dP.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_dP.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_dP.txt diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_q.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_q.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_q.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_q.txt diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_rho.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_rho.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_rho.txt diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_u.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_u.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_u.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_u.txt diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_u1.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_u1.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_u1.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_u1.txt diff --git a/kharma/prob/emhd/shock_soln_1024_default/shock_soln_xCoords.txt b/tests/emhdshock/shock_soln_1024_default/shock_soln_xCoords.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_1024_default/shock_soln_xCoords.txt rename to tests/emhdshock/shock_soln_1024_default/shock_soln_xCoords.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_dP.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_dP.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_dP.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_dP.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_q.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_q.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_q.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_q.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_rho.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_rho.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_rho.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_u.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_u.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_u.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_u.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_u1.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_u1.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_u1.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_u1.txt diff --git a/kharma/prob/emhd/shock_soln_2048_default/shock_soln_xCoords.txt b/tests/emhdshock/shock_soln_2048_default/shock_soln_xCoords.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_2048_default/shock_soln_xCoords.txt rename to tests/emhdshock/shock_soln_2048_default/shock_soln_xCoords.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_dP.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_dP.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_dP.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_dP.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_q.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_q.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_q.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_q.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_rho.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_rho.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_rho.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_u.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_u.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_u.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_u.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_u1.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_u1.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_u1.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_u1.txt diff --git a/kharma/prob/emhd/shock_soln_256_default/shock_soln_xCoords.txt b/tests/emhdshock/shock_soln_256_default/shock_soln_xCoords.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_256_default/shock_soln_xCoords.txt rename to tests/emhdshock/shock_soln_256_default/shock_soln_xCoords.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_dP.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_dP.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_dP.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_dP.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_q.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_q.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_q.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_q.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_rho.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_rho.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_rho.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_rho.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_u.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_u.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_u.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_u.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_u1.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_u1.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_u1.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_u1.txt diff --git a/kharma/prob/emhd/shock_soln_512_default/shock_soln_xCoords.txt b/tests/emhdshock/shock_soln_512_default/shock_soln_xCoords.txt similarity index 100% rename from kharma/prob/emhd/shock_soln_512_default/shock_soln_xCoords.txt rename to tests/emhdshock/shock_soln_512_default/shock_soln_xCoords.txt diff --git a/tests/gizmo_shell/run.sh b/tests/gizmo_shell/run.sh deleted file mode 100755 index 64ee3796..00000000 --- a/tests/gizmo_shell/run.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash -# Hyerin (02/18/23) copied from Ben's code - -# Bash script testing gizmo shell run (no b field) - -# User specified values here -KERR=false -EXT_G=false #true # -DIM=3 -NZONES=7 -BASE=8 -NRUNS=300 -START_RUN=8 # if this is not 0, then update start_time, out_to_in, iteration, r_out, r_in to values that you are re-starting from -DRTAG="bondi_multizone_030723_gizmo_no_ext_g_128^3" - -# Set paths -KHARMADIR=../.. -PDR="/n/holylfs05/LABS/bhi/Users/hyerincho/grmhd/" ## parent directory -DR="${PDR}data/${DRTAG}" -parfilename="${PDR}/kharma/pars/bondi_multizone/bondi_multizone_00000.par" # parameter file - -# other values determined automatically -turn_around=$(($NZONES-1)) -start_time=6013548357 #0 # -out_to_in=-1 # 1 # -iteration=2 # 1 #eq : (iteration-1)*(NZONES-1) 0.00001" |bc -l) )); then - dt_new=$dt_new - else - dt_new=0.00001 - fi - fname_dir="${DR}/bondi_multizone_$(printf %05d $((${VAR}-1)))" - fname=$(find ${fname_dir} -type f -iname "*final.rhdf") - if [ $VAR -ge $NZONES ]; then - fname_fill_num=$((2*($iteration-1)*(${NZONES}-1)-${VAR})) - fname_fill_dir="${DR}/bondi_multizone_$(printf %05d $fname_fill_num)" - fname_fill=$(find ${fname_fill_dir} -type f -iname "*final.rhdf") - else - fname_fill="none" - fi - args+=(" resize_restart/fname=$fname resize_restart/use_dt=false parthenon/time/dt_min=$dt_new") - args+=(" resize_restart/fname_fill=$fname_fill ") - else - r_shell=$r_in - args+=(" bondi/r_shell=$r_shell ") - fi - - # data_dir, logfiles - data_dir="${DR}/bondi_multizone_$(printf %05d ${VAR})" - out_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_out" - err_fn="${PDR}/logs/${DRTAG}/log_multizone$(printf %05d ${VAR})_err" - - srun --mpi=pmix ${PDR}/kharma.cuda -i ${parfilename} \ - parthenon/job/problem_id=$prob \ - parthenon/mesh/nx1=128 parthenon/mesh/nx2=128 parthenon/mesh/nx3=128 \ - parthenon/meshblock/nx1=64 parthenon/meshblock/nx2=64 parthenon/meshblock/nx3=128 \ - parthenon/time/tlim=${start_time} \ - coordinates/r_in=${r_in} coordinates/r_out=${r_out} coordinates/a=$spin coordinates/ext_g=$EXT_G \ - coordinates/transform=mks coordinates/hslope=1 \ - bondi/vacuum_logrho=-8.2014518 bondi/vacuum_log_u_over_rho=${log_u_over_rho} \ - bondi/use_gizmo=true \ - b_field/type=none b_field/solver=none \ - b_field/fix_flux_x1=0 b_field/initial_cleanup=0 \ - resize_restart/base=$BASE resize_restart/nzone=$NZONES resize_restart/iteration=$iteration \ - parthenon/output0/dt=$output0_dt \ - parthenon/output1/dt=$output1_dt \ - parthenon/output2/dt=$output2_dt \ - ${args[@]} \ - -d ${data_dir} 1> ${out_fn} 2>${err_fn} - - if [ $VAR -ne 0 ]; then - if [ $(($VAR % ($NZONES-1))) -eq 0 ]; then - out_to_in=$(($out_to_in*(-1))) - iteration=$(($iteration+1)) - fi - fi - - if [ $out_to_in -gt 0 ]; then - # half the radii - r_out=$((${r_out}/$BASE)) - r_in=$((${r_in}/$BASE)) - else - # double the radii - r_out=$((${r_out}*$BASE)) - r_in=$((${r_in}*$BASE)) - fi -done diff --git a/tests/hubble/make_plots.py b/tests/hubble_flow/make_plots.py similarity index 100% rename from tests/hubble/make_plots.py rename to tests/hubble_flow/make_plots.py diff --git a/tests/bclean/bondi_multizone.par b/tests/multizone/bondi_multizone.par similarity index 96% rename from tests/bclean/bondi_multizone.par rename to tests/multizone/bondi_multizone.par index c515107f..29fdd09a 100755 --- a/tests/bclean/bondi_multizone.par +++ b/tests/multizone/bondi_multizone.par @@ -66,8 +66,8 @@ bsq_over_u_max=50 # We'll be adding material, and that's okay prob_uses_dirichlet = false -check_inflow_outer = false -check_inflow_inner = false # Hyerin test (12/22/22) +check_inflow_outer_x1 = false +check_inflow_inner_x1 = false # Hyerin test (12/22/22) fix_corner = false #fix_flux_pole = 0 # Hyerin test (12/22/22) diff --git a/tests/bclean/run.sh b/tests/multizone/run.sh similarity index 100% rename from tests/bclean/run.sh rename to tests/multizone/run.sh diff --git a/tests/noh/run.sh b/tests/noh/run.sh index 6e10b03b..3bc841d5 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -12,7 +12,7 @@ noh_test() { for res in 64 128 256 512 1024 2048 do eighth=$(($res / 8)) - $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par parthenon/output0/dt=1000 debug/verbose=1 \ + $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par debug/verbose=1 \ #parthenon/output0/dt=1000 \ parthenon/mesh/nx1=$res parthenon/meshblock/nx1=$eighth \ >log_noh_${res}.txt 2>&1 diff --git a/tests/torus_sanity/run.sh b/tests/torus_sanity/run.sh index e5382810..e2212aa8 100755 --- a/tests/torus_sanity/run.sh +++ b/tests/torus_sanity/run.sh @@ -5,7 +5,7 @@ exit_code=0 check_sanity() { # mad_test.par is basically only used for this, so common options are there. - $BASE/run.sh -i $BASE/pars/mad_test.par $2 >log_divb_${1}.txt 2>&1 || exit_code=$? + $BASE/run.sh -i $BASE/pars/mad_test.par $2 >log_divb_${1}.txt 2>&1 #|| exit_code=$? pyharm check-basics -d --allowed_divb=1e-10 torus.out0.final.phdf || exit_code=$? } From 0c89229be94886cd27aa9ceb65f33e851fec5209 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 17 May 2023 20:56:46 -0500 Subject: [PATCH 076/219] EMHD fixes, Flag stuff This also looks bigger than it is... 1. Fixes ctop of NaN when evolving EMHD 2. Drive-by math optimizations 3. Improve Flag(). Flag() calls must now be matched to EndFlag(), and produce indented output for readability. Zone monitors can still be implemented, but must be (as is likely more convenient) file outputs w/ a new function OutputNow(). This does *not* fix non-convergence of conducting atmo. Problem converges with higher_order_terms off, with or without evolving dP. Does not converge if higher order terms are enabled, with or without P ->therefore somewhere in q higher-order stuff. --- kharma/b_cd/b_cd.cpp | 6 - kharma/b_cd/seed_B_cd.cpp | 2 +- kharma/b_cleanup/b_cleanup.cpp | 2 - kharma/b_flux_ct/b_flux_ct.cpp | 24 +- kharma/boundaries/boundaries.cpp | 17 +- kharma/boundaries/dirichlet.cpp | 6 +- kharma/coordinates/gr_coordinates.cpp | 2 - kharma/current/current.cpp | 7 - kharma/debug.cpp | 9 +- kharma/driver/imex_step.cpp | 2 - kharma/driver/kharma_driver.cpp | 4 +- kharma/driver/kharma_step.cpp | 4 +- kharma/driver/simple_step.cpp | 1 - kharma/electrons/electrons.cpp | 13 +- kharma/emhd/emhd.cpp | 49 ++++- kharma/emhd/emhd.hpp | 118 +++++----- kharma/emhd/emhd_limits.hpp | 6 +- kharma/emhd/emhd_sources.hpp | 4 +- kharma/floors/floors.cpp | 9 +- kharma/floors/floors_functions.hpp | 63 +++--- kharma/flux/flux.cpp | 15 +- kharma/flux/flux_functions.hpp | 53 ++--- kharma/flux/get_flux.hpp | 7 +- kharma/grmhd/grmhd.cpp | 24 +- kharma/grmhd/grmhd_reductions.hpp | 2 +- kharma/implicit/fixup.cpp | 4 +- kharma/implicit/implicit.cpp | 11 +- kharma/implicit/implicit.hpp | 6 +- kharma/inverter/fixup.cpp | 4 +- kharma/inverter/inverter.cpp | 4 - kharma/inverter/onedw.hpp | 10 +- kharma/kharma.cpp | 13 +- kharma/kharma_package.cpp | 145 +++++++----- kharma/main.cpp | 23 +- kharma/prob/bondi.cpp | 4 +- kharma/prob/elec/driven_turbulence.hpp | 2 - kharma/prob/elec/hubble.cpp | 5 +- kharma/prob/emhd/conducting_atmosphere.cpp | 52 ++--- kharma/prob/emhd/emhdmodes.hpp | 4 +- kharma/prob/emhd/emhdshock.hpp | 2 +- kharma/prob/fm_torus.cpp | 2 - kharma/prob/gizmo.cpp | 2 - kharma/prob/post_initialize.cpp | 16 +- kharma/prob/problem.cpp | 23 +- kharma/prob/resize_restart.cpp | 5 - kharma/prob/resize_restart_kharma.cpp | 2 - kharma/reductions/reductions.cpp | 14 +- kharma/types.hpp | 243 ++++----------------- kharma/wind/wind.cpp | 4 +- pars/anisotropic_conduction.par | 1 + pars/conducting_atmosphere.par | 2 +- tests/anisotropic_conduction/make_plots.py | 11 +- tests/anisotropic_conduction/run.sh | 5 + 53 files changed, 434 insertions(+), 634 deletions(-) create mode 100755 tests/anisotropic_conduction/run.sh diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index f5ff3aaf..a89a0a6d 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -101,7 +101,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc, IndexDomain domain, bool coarse) { - Flag(rc, "B field UtoP"); auto pmb = rc->GetBlockPointer(); auto& B_U = rc->Get("cons.B").data; @@ -124,12 +123,10 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) psi_P(k, j, i) = psi_U(k, j, i) / gdet; } ); - Flag(rc, "End B field UtoP"); } TaskStatus AddSource(MeshData *md, MeshData *mdudt) { - Flag(md, "Adding constraint damping source"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); const int ndim = pmesh->ndim; @@ -186,7 +183,6 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) } ); - Flag("Added"); return TaskStatus::complete; } @@ -227,7 +223,6 @@ Real MaxDivB(MeshData *md) TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) { - Flag(md, "Printing B field diagnostics"); auto pmesh = md->GetMeshPointer(); // Print this unless we quash everything @@ -243,7 +238,6 @@ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) } } - Flag(md, "Printed"); return TaskStatus::complete; } diff --git a/kharma/b_cd/seed_B_cd.cpp b/kharma/b_cd/seed_B_cd.cpp index 962460d6..3d5a39a4 100644 --- a/kharma/b_cd/seed_B_cd.cpp +++ b/kharma/b_cd/seed_B_cd.cpp @@ -131,7 +131,7 @@ TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) break; case BSeedType::ryan: // BR's smoothed poloidal in-torus - q = m::pow(sin(th), 3) * m::pow(r / rin, 3) * m::exp(-r / 400) * rho_av - min_rho_q; + q = m::pow(m::sin(th), 3) * m::pow(r / rin, 3) * m::exp(-r / 400) * rho_av - min_rho_q; break; case BSeedType::r3s3: // Just the r^3 sin^3 th term, proposed EHT standard MAD diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index c4428b30..4ecc47f7 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -304,7 +304,6 @@ TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) { - Flag(md, "Applying correction from P"); // Apply on physical zones only, we'll be syncing/updating ghosts const IndexRange ib = md->GetBoundsI(IndexDomain::interior); const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); @@ -333,7 +332,6 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_var, MeshData* md_again, const std::string& lap_var) { - Flag(md, "Calculating & summing divB"); // Cover ghost cells; maximize since both ops have stencil >1 const IndexRange ib = md->GetBoundsI(IndexDomain::entire); const IndexRange jb = md->GetBoundsJ(IndexDomain::entire); diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index d1facae9..cbdf1f07 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -159,7 +159,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*md, IndexDomain domain, bool coarse) { - Flag(md, "B UtoP Mesh"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); const auto& B_U = md->PackVariables(std::vector{"cons.B"}); @@ -182,7 +181,6 @@ void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) } void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "B UtoP Block"); auto pmb = rc->GetBlockPointer(); auto B_U = rc->PackVariables(std::vector{"cons.B"}); @@ -206,7 +204,6 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "B PtoU Block"); auto pmb = rc->GetBlockPointer(); auto B_U = rc->PackVariables(std::vector{"cons.B"}); @@ -230,6 +227,7 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) void FixFlux(MeshData *md) { + // TODO flags here auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); auto& params = pmb0->packages.Get("B_FluxCT")->AllParams(); if (params.Get("fix_polar_flux")) { @@ -249,7 +247,6 @@ void FixFlux(MeshData *md) void FluxCT(MeshData *md) { - Flag(md, "Flux CT"); // Pointers auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -272,7 +269,6 @@ void FluxCT(MeshData *md) const IndexRange kl = (ndim > 2) ? IndexRange{kb.s, kb.e + 1} : kb; // Calculate emf around each face - Flag(md, "Calc EMFs"); pmb0->par_for("flux_ct_emf", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { emf_pack(b, V3, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - @@ -288,8 +284,6 @@ void FluxCT(MeshData *md) // Rewrite EMFs as fluxes, after Toth (2000) // Note that zeroing FX(BX) is *necessary* -- this flux gets filled by GetFlux - Flag(md, "Calc Fluxes"); - // Note these each have different domains, eg il vs ib. The former extends one index farther if appropriate pmb0->par_for("flux_ct_1", block.s, block.e, kb.s, kb.e, jb.s, jb.e, il.s, il.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { @@ -314,13 +308,10 @@ void FluxCT(MeshData *md) } ); } - - Flag(md, "CT Finished"); } void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) { - Flag(md, "Fixing polar B fluxes"); auto pmesh = md->GetMeshPointer(); auto pmb0 = pmesh->block_list[0]; const int ndim = pmesh->ndim; @@ -455,8 +446,6 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) } } - - Flag(md, "Fixed polar B"); } IndexRange ValidDivBX1(MeshBlock *pmb) @@ -473,7 +462,6 @@ IndexRange ValidDivBX1(MeshBlock *pmb) double MaxDivB(MeshData *md) { - Flag(md, "Calculating divB Mesh"); auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; @@ -510,7 +498,6 @@ double MaxDivB(MeshData *md) if (max_divb_block > max_divb) max_divb = max_divb_block; } - Flag("Calculated"); return max_divb; } @@ -525,13 +512,11 @@ double GlobalMaxDivB(MeshData *md) TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) { - Flag(md, "Printing B field diagnostics"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Since this is in the history file now, I don't bother printing it // unless we're being verbose. It's not costly to calculate though if (pmb0->packages.Get("Globals")->Param("verbose") >= 1) { - Flag(md, "Printing divB"); // Calculate the maximum from/on all nodes const double divb_max = B_FluxCT::GlobalMaxDivB(md); // Print on rank zero @@ -544,7 +529,6 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) } } - Flag(md, "Printed B field diagnostics"); return TaskStatus::complete; } @@ -552,7 +536,6 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) void CalcDivB(MeshData *md, std::string divb_field_name) { - Flag(md, "Calculating divB for output"); auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; @@ -580,13 +563,10 @@ void CalcDivB(MeshData *md, std::string divb_field_name) } ); } - - Flag("Calculated"); } void FillOutput(MeshBlock *pmb, ParameterInput *pin) { auto rc = pmb->meshblock_data.Get().get(); - Flag(rc, "Calculating divB for output"); const int ndim = pmb->pmy_mesh->ndim; if (ndim < 2) return; @@ -608,8 +588,6 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin) divB(0, k, j, i) = corner_div(G, B_U, 0, k, j, i, ndim > 2); } ); - - Flag(rc, "Output divB"); } } // namespace B_FluxCT diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 1e3ce83c..6c56fc3b 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -138,7 +138,7 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: // TODO TODO any way to save this verbosity with constexpr/macros/something? if (btype == "dirichlet") { // Dirichlet boundaries: allocate - pkg->AddField("bound." + bname, (bdir == X1DIR) ? m_x1 : ((bdir == X2DIR) ? m_x2 : m_x3)); + pkg->AddField("bounds." + bname, (bdir == X1DIR) ? m_x1 : ((bdir == X2DIR) ? m_x2 : m_x3)); switch (bface) { case BoundaryFace::inner_x1: pkg->KBoundaries[bface] = KBoundaries::Dirichlet; @@ -212,13 +212,12 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag("Apply boundary"); + Flag("ApplyBoundary"); // this is not a callback, flag for ourselves // KHARMA has to do some extra tasks in addition to just applying the usual // boundary conditions. Therefore, we "wrap" Parthenon's (or our own) // boundary functions with this one. auto pmb = rc->GetBlockPointer(); - //auto pkg = static_cast(pmb->packages.Get("Boundaries").get()); auto pkg = pmb->packages.Get("Boundaries"); auto& params = pkg->AllParams(); @@ -229,17 +228,21 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD Flag("Apply "+bname+" boundary: "+btype_name); pkg->KBoundaries[bface](rc, coarse); - EndFlag("Apply "+bname+" boundary"); + EndFlag(); // Prevent inflow of material by changing fluid speeds, // anywhere we've specified. if (params.Get("check_inflow_" + bname)) { + Flag("CheckInflow"); CheckInflow(rc, domain, coarse); + EndFlag(); } // If specified, fix corner values when applying X2 boundaries (see function) if (params.Get("fix_corner") && bdir == X2DIR) { + Flag("FixCorner"); FixCorner(rc, domain, coarse); + EndFlag(); } // Respect the fluid primitives on boundaries (*not* B) @@ -247,12 +250,11 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // For everything else, respect conserved variables Packages::BlockUtoPExceptMHD(rc.get(), domain, coarse); - EndFlag("Apply boundary"); + EndFlag(); } void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag("CheckInflow"); std::shared_ptr pmb = rc->GetBlockPointer(); const auto &G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); @@ -269,12 +271,10 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); - EndFlag("CheckInflow"); } void KBoundaries::FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - Flag("FixCorner"); std::shared_ptr pmb = rc->GetBlockPointer(); if (pmb->pmy_mesh->ndim < 2) return; @@ -286,7 +286,6 @@ void KBoundaries::FixCorner(std::shared_ptr> &rc, IndexDomai { ApplyBoundary(rc, IndexDomain::inner_x1, coarse); } - EndFlag("FixCorner"); } TaskStatus KBoundaries::FixFlux(MeshData *md) diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index edc715ba..04890b52 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -49,7 +49,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) : FC({Metadata::FillGhost}); auto q = rc->PackVariables(main_ghosts, coarse); - auto bound = rc->Get("bound." + BoundaryName(bface)).data; + auto bound = rc->Get("bounds." + BoundaryName(bface)).data; // TODO TODO NAMES if (q.GetDim(4) != bound.GetDim(4)) { @@ -90,6 +90,7 @@ void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) auto pmesh = md->GetMeshPointer(); // ...if this boundary is dirichlet... if (pmesh->packages.Get("Boundaries")->Param(bname) == "dirichlet") { + //std::cout << "Freezing dirichlet " << bname << " on mesh." << std::endl; // ...on all blocks... for (int i=0; i < md->NumBlocks(); i++) { auto rc = md->GetBlockData(i).get(); @@ -110,6 +111,7 @@ void KBoundaries::FreezeDirichletBlock(MeshBlockData *rc) auto pmb = rc->GetBlockPointer(); // ...if this boundary is dirichlet... if (pmb->packages.Get("Boundaries")->Param(bname) == "dirichlet") { + //std::cout << "Freezing dirichlet " << bname << " on block." << std::endl; auto domain = BoundaryDomain(bface); // Set whatever is in that domain as the Dirichlet bound SetDomainDirichlet(rc, domain, false); @@ -128,7 +130,7 @@ void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) : FC({Metadata::FillGhost}); auto q = rc->PackVariables(main_ghosts, coarse); - auto bound = rc->Get("bound." + BoundaryName(bface)).data; + auto bound = rc->Get("bounds." + BoundaryName(bface)).data; // TODO error? if (q.GetDim(4) != bound.GetDim(4)) { diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index a94a3491..cf8e42f0 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -244,7 +244,5 @@ void init_GRCoordinates(GRCoordinates& G) { } ); } - - Flag("GRCoordinates metric init"); } #endif // FAST_CARTESIAN diff --git a/kharma/current/current.cpp b/kharma/current/current.cpp index 961f27f6..a3e481fa 100644 --- a/kharma/current/current.cpp +++ b/kharma/current/current.cpp @@ -51,8 +51,6 @@ std::shared_ptr Current::Initialize(ParameterInput *pin, std::sha TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockData *rc1, const double& dt) { - Flag("Calculating current"); - auto pmb = rc0->GetBlockPointer(); GridVector uvec_old = rc0->Get("prims.uvec").data; GridVector B_P_old = rc0->Get("prims.B").data; @@ -109,14 +107,11 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockDatameshblock_data.Get(); @@ -136,6 +131,4 @@ void Current::FillOutput(MeshBlock *pmb, ParameterInput *pin) Real dt_last = pmb->packages.Get("Globals")->Param("dt_last"); Current::CalculateCurrent(rc0.get(), rc1.get(), dt_last); - - Flag("Added"); } diff --git a/kharma/debug.cpp b/kharma/debug.cpp index 4701c1ed..f6b642a3 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -40,11 +40,11 @@ #include "grmhd_functions.hpp" #include "types.hpp" +// TODO make this a DomainReduce, and add better verbosity options TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) { - Flag("Checking ctop for NaNs"); + Flag("CheckNaN"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - // TODO verbose option? // Pack variables auto& ctop = md->PackVariables(std::vector{"ctop"}); @@ -96,13 +96,13 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) // TODO reimplement printing *where* these values were hit? // May not even be that useful, as the cause is usually much earlier - Flag("Checked"); + EndFlag(); return TaskStatus::complete; } TaskStatus CheckNegative(MeshData *md, IndexDomain domain) { - Flag("Counting negative values"); + Flag("CheckNegative"); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Pack variables auto rho_p = md->PackVariables(std::vector{"prims.rho"}); @@ -159,5 +159,6 @@ TaskStatus CheckNegative(MeshData *md, IndexDomain domain) std::cout << "Number of negative primitive rho, u: " << nless_rho << "," << nless_u << std::endl; } + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index fa752f46..db724e8b 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -55,7 +55,6 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int stage) { - Flag("Generating default task collection"); // Reminder that this list is created BEFORE any of the list contents are run! // Prints or function calls here will likely not do what you want: instead, add to the list by calling tl.AddTask() @@ -308,7 +307,6 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); - return tc; } diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index eb55e7ba..9081659b 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -190,7 +190,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) { - Flag("Syncing all bounds"); + Flag("SyncAllBounds"); TaskID t_none(0); // 1. PtoU on the interior to ensure we're up-to-date @@ -214,7 +214,7 @@ void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_ } } - Flag("Sync'd"); + EndFlag(); } TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md) diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 30a1b398..38c669b9 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -55,6 +55,7 @@ TaskCollection KHARMADriver::MakeTaskCollection(BlockList_t &blocks, int stage) { std::string driver_type = blocks[0]->packages.Get("Driver")->Param("type"); + Flag("MakeTaskCollection_"+driver_type); if (driver_type == "imex") { return MakeImExTaskCollection(blocks, stage); } else if (driver_type == "simple") { @@ -62,11 +63,11 @@ TaskCollection KHARMADriver::MakeTaskCollection(BlockList_t &blocks, int stage) } else { return MakeDefaultTaskCollection(blocks, stage); } + EndFlag(); } TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int stage) { - Flag("Generating default task collection"); // Reminder that this list is created BEFORE any of the list contents are run! // Prints or function calls here will likely not do what you want: instead, add to the list by calling tl.AddTask() @@ -277,6 +278,5 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); - Flag("Generated"); return tc; } diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index 3d86a819..a7bd46d8 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -38,7 +38,6 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int stage) { - Flag("Generating non-MPI task collection"); // This is probably incompatible with everything // TODO check for incompatibilities at some point: diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index f74e3cc7..0d03fe27 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -191,7 +191,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

>& rc, ParameterInput *pin) { - Flag("Initializing electron/fluid entropy values"); + Flag("InitElectrons"); auto pmb = rc->GetBlockPointer(); // Don't initialize entropies if we've already done so e.g. in Hubble problem @@ -229,13 +229,12 @@ TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInpu // iharm3d syncs bounds here, but we do all that in PostInit - Flag("Initialized electron/fluid entropy values"); + EndFlag(); return TaskStatus::complete; } void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "UtoP electrons"); auto pmb = rc->GetBlockPointer(); // No need for a "map" here, we just want everything that fits these @@ -259,7 +258,6 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "PtoU electrons"); auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; @@ -283,10 +281,7 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) } TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData *rc) -{ // takes in '_sub_step_init' and '_sub_step_final' - Flag(rc, "Applying electron heating"); - auto pmb = rc->GetBlockPointer(); - +{ // Need to distinguish different electron models // So far, Parthenon's maps of the same sets of variables are consistent, // so we only bother with one map of the primitives @@ -297,6 +292,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData auto& U_new = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); + auto pmb = rc->GetBlockPointer(); const auto& G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); @@ -430,7 +426,6 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData } ); - Flag(rc, "Applied"); return TaskStatus::complete; } diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 5832055e..793fba13 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -127,9 +127,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

AddField("eflag", m); + // Callbacks + + // This is for boundary syncs and output + pkg->BlockUtoP = EMHD::BlockUtoP; + + // Add all explicit source terms -- implicit terms are called from Implicit::Step pkg->AddSource = EMHD::AddSource; + // Add floors if (enable_emhd_limits) { pkg->BlockApplyFloors = EMHD::ApplyEMHDLimits; } @@ -165,6 +172,38 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc, IndexDomain domain, bool coarse) +{ + auto pmb = rc->GetBlockPointer(); + + PackIndexMap prims_map, cons_map; + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); + auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + + const auto& G = pmb->coords; + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + + pmb->par_for("UtoP_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, Loci::center); + const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + const Real ucon0 = gamma * inv_alpha; + + // Update the primitive EMHD fields + if (m_p.Q >= 0) + P(m_p.Q, k, j, i) = U_E(m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + if (m_p.DP >= 0) + P(m_p.DP, k, j, i) = U_E(m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + } + ); + Kokkos::fence(); +} + void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin) { // Do we actually need anything here? @@ -172,7 +211,6 @@ void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput TaskStatus AddSource(MeshData *md, MeshData *mdudt) { - Flag(mdudt, "Adding EMHD Explicit Sources"); // Pointers auto pmesh = mdudt->GetMeshPointer(); auto pmb0 = mdudt->GetBlockData(0)->GetBlockPointer(); @@ -257,7 +295,7 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / mag_b) * Theta * D.ucon[nu] * grad_ucov[nu][mu]; Real q0_tilde = q0; if (emhd_params.higher_order_terms) - q0_tilde *= (chi_e != 0) * m::sqrt(tau / (chi_e * rho * Theta * Theta) ); + q0_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho * Theta * Theta)) : 0.0; dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; if (emhd_params.higher_order_terms) @@ -270,7 +308,7 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) DLOOP2 dP0 += 3. * rho * nu_e * (D.bcon[mu] * D.bcon[nu] / bsq) * grad_ucov[mu][nu]; Real dP0_tilde = dP0; if (emhd_params.higher_order_terms) - dP0_tilde *= (nu_e != 0) * m::sqrt(tau / (nu_e * rho * Theta) ); + dP0_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho * Theta)) : 0.0; dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * dP0_tilde / tau; if (emhd_params.higher_order_terms) @@ -279,7 +317,6 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) } ); - Flag(mdudt, "Added"); return TaskStatus::complete; } diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 81e622c0..da320a9a 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -75,6 +75,25 @@ class EMHD_parameters { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); +/** + * Add EGRMHD explicit source terms: anything which can be calculated once + * and added to the general dU/dt term along with e.g. GRMHD source, wind, etc + */ +TaskStatus AddSource(MeshData *md, MeshData *mdudt); + +/** + * Set q and dP to sensible starting values if they are not initialized by the problem. + * Currently a no-op as sensible values are zeros. + */ +void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin); + +/** + * Recover primitive qtilde, dPtilde from "conserved" forms {qtilde,dPtilde}*u^0*gdet. + * Since the implicit step does this for us, this is only needed for boundaries, + * which sync/set conserved forms. + */ +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse); + /** * Get the EMHD parameters needed on the device side. * This function exists to be able to easily return a null @@ -89,22 +108,8 @@ inline EMHD_parameters GetEMHDParameters(Packages_t& packages) return emhd_params_tmp; } -/** - * Add EGRMHD explicit source terms: anything which can be calculated once - * and added to the general dU/dt term along with e.g. GRMHD source, wind, etc - */ -TaskStatus AddSource(MeshData *md, MeshData *mdudt); - -/** - * Set q and dP to sensible starting values if they are not initialized by the problem. - * Currently a no-op as sensible values are zeros. - */ -void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin); - /** * Set chi, nu, tau. Problem dependent - * - * TODO Local & Global, when we're sure */ template KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, @@ -151,12 +156,12 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& const GReal r = Xembed[1]; // Compute dynamical time scale - const Real tau_dyn = m::pow(r, 1.5); + const Real tau_dyn = m::sqrt(r*r*r); const Real pg = (gam - 1.) * P(m_p.UU); const Real Theta = pg / P(m_p.RHO); // Compute local sound speed - const Real cs = m::sqrt(gam * pg / (P(m_p.RHO) + (gam * P(m_p.UU)))); + const Real cs2 = gam * pg / (P(m_p.RHO) + (gam * P(m_p.UU))); Real lambda = 0.01; Real inv_exp_g = 0.; @@ -166,10 +171,10 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& if (emhd_params.conduction) { Real q = P(m_p.Q); if (emhd_params.higher_order_terms) - q *= sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * m::pow(cs, 2.) * m::pow(Theta, 2.)); - Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * m::pow(cs, 3.); - Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); + q *= m::sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * cs2 * Theta * Theta); + Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * cs2 * m::sqrt(cs2); + Real q_ratio = m::abs(q) / q_max; + inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; tau = m::min(tau, f_fmin * tau_dyn); @@ -179,16 +184,12 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& if (emhd_params.viscosity) { Real dP = P(m_p.DP); if (emhd_params.higher_order_terms) - dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * m::pow(cs, 2.) * Theta); + dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * cs2 * Theta); Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; + Real dP_max = (dP > 0.) ? dP_plus : dP_minus; Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); @@ -198,11 +199,11 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& } // Update thermal diffusivity and kinematic viscosity - Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); + Real max_alpha = (1 - cs2) / (2 * cs2 + 1.e-12); if (emhd_params.conduction) - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs2 * tau; if (emhd_params.viscosity) - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs2 * tau; } // else yell? } @@ -231,7 +232,7 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl if (emhd_params.viscosity) nu_e = emhd_params.viscosity_alpha * cs2 * tau; - } else if (emhd_params.type == ClosureType::kappa_eta){ + } else if (emhd_params.type == ClosureType::kappa_eta) { // Set tau = const, chi = kappa / rho, nu = eta / rho tau = emhd_params.tau; @@ -241,24 +242,22 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl nu_e = emhd_params.eta / m::max(P(m_p.RHO, k, j, i), SMALL); } else if (emhd_params.type == ClosureType::torus) { - Real rho = P(m_p.RHO, k, j, i); - Real uu = P(m_p.UU, k, j, i); - FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + // TODO need this max() if we're correcting later? double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, Loci::center, Xembed); - GReal r = Xembed[1]; + const GReal r = Xembed[1]; // Compute dynamical time scale - Real tau_dyn = pow(r, 1.5); + const Real tau_dyn = m::sqrt(r*r*r); - Real pg = (gam - 1.) * uu; - Real Theta = pg / rho; + const Real pg = (gam - 1.) * P(m_p.UU, k, j, i); + const Real Theta = pg / P(m_p.RHO, k, j, i); // Compute local sound speed - Real cs = sqrt(gam * pg / (rho + (gam * uu))); + const Real cs2 = gam * pg / (P(m_p.RHO, k, j, i) + (gam * P(m_p.UU, k, j, i))); Real lambda = 0.01; Real inv_exp_g = 0.; @@ -266,13 +265,12 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl // Correction due to heat conduction if (emhd_params.conduction) { - Real qtilde = P(m_p.Q, k, j, i); - Real q = qtilde; + Real q = P(m_p.Q, k, j, i); if (emhd_params.higher_order_terms) - q *= (rho * emhd_params.conduction_alpha * pow(cs, 2.) * pow(Theta, 2.)); - Real q_max = emhd_params.conduction_alpha * rho * pow(cs, 3.); - Real q_ratio = fabs(q) / q_max; - inv_exp_g = exp(-(q_ratio - 1.) / lambda); + q *= m::sqrt(P(m_p.RHO, k, j, i) * emhd_params.conduction_alpha * cs2 * Theta * Theta); + Real q_max = emhd_params.conduction_alpha * P(m_p.RHO, k, j, i) * cs2 * m::sqrt(cs2); + Real q_ratio = m::abs(q) / q_max; + inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; tau = m::min(tau, f_fmin * tau_dyn); @@ -280,19 +278,14 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl // Correction due to pressure anisotropy if (emhd_params.viscosity) { - Real dPtilde = P(m_p.DP, k, j, i); - Real dP = dPtilde; + Real dP = P(m_p.DP, k, j, i); if (emhd_params.higher_order_terms) - dP *= sqrt(rho * emhd_params.viscosity_alpha * pow(cs, 2.) * Theta); + dP *= m::sqrt(P(m_p.RHO, k, j, i) * emhd_params.viscosity_alpha * cs2 * Theta); Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - Real dP_max = 0.; - if (dP > 0.) - dP_max = dP_plus; - else - dP_max = dP_minus; + Real dP_max = (dP > 0.) ? dP_plus : dP_minus; Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); @@ -302,11 +295,11 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl } // Update thermal diffusivity and kinematic viscosity - Real max_alpha = (1 - m::pow(cs, 2.)) / (2*m::pow(cs, 2.) + 1.e-12); + Real max_alpha = (1 - cs2) / (2 * cs2 + 1.e-12); if (emhd_params.conduction) - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * m::pow(cs, 2.) * tau; + chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs2 * tau; if (emhd_params.viscosity) - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * m::pow(cs, 2.) * tau; + nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs2 * tau; } // else yell? } @@ -358,18 +351,19 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Real& rho, const Real& u, const Re Real emhd[GR_DIM]) { const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); + const Real b_mag = m::sqrt(bsq); const Real eta = pgas + rho + u + bsq; const Real ptot = pgas + 0.5 * bsq; DLOOP1 emhd[mu] = eta * D.ucon[dir] * D.ucov[mu] + ptot * (dir == mu) - D.bcon[dir] * D.bcov[mu]; - + if (emhd_params.feedback) { if (emhd_params.conduction) DLOOP1 - emhd[mu] += (q / m::sqrt(bsq)) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])); + emhd[mu] += (q / b_mag) * ((D.ucon[dir] * D.bcov[mu]) + (D.bcon[dir] * D.ucov[mu])); if (emhd_params.viscosity) DLOOP1 - emhd[mu] -= dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); + emhd[mu] -= dP * ((D.bcon[dir] * D.bcov[mu] / bsq) - (1./3.) * ((dir == mu) + D.ucon[dir] * D.ucov[mu])); } } @@ -383,10 +377,12 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea q = q_tilde; if (emhd_params.higher_order_terms) { if (emhd_params.type == ClosureType::kappa_eta) - q *= m::sqrt(emhd_params.kappa * m::pow(Theta, 2) / emhd_params.tau); + q *= m::sqrt(emhd_params.kappa * Theta * Theta / emhd_params.tau); else - q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * m::pow(Theta, 2)); + q *= m::sqrt(rho * emhd_params.conduction_alpha * cs2 * Theta * Theta); } + } else { + q = 0.; } if (emhd_params.viscosity) { @@ -397,6 +393,8 @@ KOKKOS_INLINE_FUNCTION void convert_prims_to_q_dP(const Real& q_tilde, const Rea else dP *= m::sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta); } + } else { + dP = 0.; } } diff --git a/kharma/emhd/emhd_limits.hpp b/kharma/emhd/emhd_limits.hpp index 7815cbc8..8da45c8a 100644 --- a/kharma/emhd/emhd_limits.hpp +++ b/kharma/emhd/emhd_limits.hpp @@ -90,7 +90,7 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons if (emhd_params.conduction) { Real qmax = 1.07 * rho * cs*cs*cs; Real max_frac = m::max(m::abs(q) / qmax, 1.); - if (fabs(q) / qmax > 1.) + if (m::abs(q) / qmax > 1.) eflag |= HIT_Q_LIMIT; P(m_p.Q, k, j, i) = P(m_p.Q, k, j, i) / max_frac; @@ -126,8 +126,6 @@ KOKKOS_INLINE_FUNCTION int apply_instability_limits(const GRCoordinates& G, cons */ inline void ApplyEMHDLimits(MeshBlockData *mbd, IndexDomain domain) { - Flag(mbd, "Applying EMHD limits"); - auto pmb = mbd->GetBlockPointer(); auto packages = pmb->packages; @@ -156,8 +154,6 @@ inline void ApplyEMHDLimits(MeshBlockData *mbd, IndexDomain domain) eflag(k, j, i) = apply_instability_limits(G, P, m_p, gam, emhd_params, k, j, i, U, m_u); } ); - - Flag(mbd, "Applied"); } } // EMHD diff --git a/kharma/emhd/emhd_sources.hpp b/kharma/emhd/emhd_sources.hpp index 6f2831a1..06d5cb83 100644 --- a/kharma/emhd/emhd_sources.hpp +++ b/kharma/emhd/emhd_sources.hpp @@ -112,7 +112,7 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 q0 -= rho * chi_e * (Dtmp.bcon[mu] / mag_b) * Theta * Dtmp.ucon[0] * dt_ucov[mu]; Real q0_tilde = q0; if (emhd_params.higher_order_terms) - q0_tilde *= (chi_e != 0) * m::sqrt(tau / (chi_e * rho * m::pow(Theta, 2)) ); + q0_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho * Theta * Theta)) : 0.0; dUq = G.gdet(Loci::center, j, i) * (q0_tilde / tau); if (emhd_params.higher_order_terms) @@ -125,7 +125,7 @@ KOKKOS_INLINE_FUNCTION void time_derivative_sources(const GRCoordinates& G, cons DLOOP1 dP0 += 3. * rho * nu_e * (Dtmp.bcon[0] * Dtmp.bcon[mu] / bsq) * dt_ucov[mu]; Real dP0_tilde = dP0; if (emhd_params.higher_order_terms) - dP0_tilde *= (nu_e != 0) * m::sqrt(tau / (nu_e * rho * Theta) ); + dP0_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho * Theta)) : 0.0; dUdP = G.gdet(Loci::center, j, i) * (dP0_tilde / tau); if (emhd_params.higher_order_terms) diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index f7cf14cd..5185586c 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -157,7 +157,7 @@ std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shar TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData *mbd, IndexDomain domain) { - Flag(mbd, "Applying first floors"); + Flag("ApplyInitialFloors"); auto pmb = mbd->GetBlockPointer(); @@ -216,14 +216,12 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * } ); - Flag(mbd, "Applied"); + EndFlag(); return TaskStatus::complete; } TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain) { - Flag(mbd, "Applying GRMHD floors"); - auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; @@ -285,13 +283,11 @@ TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain } ); - Flag(mbd, "Applied"); return TaskStatus::complete; } TaskStatus Floors::PostStepDiagnostics(const SimTime& tm, MeshData *md) { - Flag("Printing Floor diagnostics"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options @@ -300,7 +296,6 @@ TaskStatus Floors::PostStepDiagnostics(const SimTime& tm, MeshData *md) // Debugging/diagnostic info about floor and inversion flags if (flag_verbose >= 1) { - Flag("Printing flags"); Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, flag_verbose, true); } return TaskStatus::complete; diff --git a/kharma/floors/floors_functions.hpp b/kharma/floors/floors_functions.hpp index 97811922..3b61c1b0 100644 --- a/kharma/floors/floors_functions.hpp +++ b/kharma/floors/floors_functions.hpp @@ -60,7 +60,7 @@ KOKKOS_INLINE_FUNCTION int apply_ceilings(const GRCoordinates& G, const Variable if (gamma > floors.gamma_max) { fflag |= FFlag::GAMMA; - Real f = m::sqrt((m::pow(floors.gamma_max, 2) - 1.)/(m::pow(gamma, 2) - 1.)); + Real f = m::sqrt((SQR(floors.gamma_max) - 1.) / (SQR(gamma) - 1.)); VLOOP P(m_p.U1+v, k, j, i) *= f; } @@ -130,14 +130,14 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa if (floors.use_r_char) { // Steeper floor from iharm3d - const Real rhoscal = 1 / (r * r * (1 + r / floors.r_char)); + Real rhoscal = 1. / ((r*r) * (1 + r / floors.r_char)); rhoflr_geom = floors.rho_min_geom * rhoscal; uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); } else { // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - // TODO(BSP) kharmaim moves this to -1.5. Logical? - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + Real rhoscal = 1. / m::sqrt(r*r*r); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * rhoscal / r; } } else { rhoflr_geom = floors.rho_min_geom; @@ -193,13 +193,11 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa } else if (use_df) { // Drift frame floors. Refer to Appendix B3 in https://doi.org/10.1093/mnras/stx364 (hereafter R17) - const Real gdet = G.gdet(Loci::center, j, i); - const Real lapse = 1./m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + const Real lapse2 = 1. / (-G.gcon(Loci::center, j, i, 0, 0)); double beta[GR_DIM] = {0}; - - beta[1] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 1); - beta[2] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 2); - beta[3] = lapse * lapse * G.gcon(Loci::center, j, i, 0, 3); + beta[1] = lapse2 * G.gcon(Loci::center, j, i, 0, 1); + beta[2] = lapse2 * G.gcon(Loci::center, j, i, 0, 2); + beta[3] = lapse2 * G.gcon(Loci::center, j, i, 0, 3); // Fluid quantities (four velocities have been computed above) const Real rho = P(m_p.RHO, k, j, i); @@ -216,6 +214,7 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa Bcon[3] = P(m_p.B3, k, j, i); DLOOP2 Bcov[mu] += G.gcov(Loci::center, j, i, mu, nu) * Bcon[nu]; const Real Bsq = m::max(dot(Bcon, Bcov), SMALL); + const Real B_mag = m::sqrt(Bsq); // Normal observer fluid momentum Real Qcov[GR_DIM] = {0}; @@ -228,15 +227,13 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa double QdotB = dot(Bcon, Qcov); // Initial parallel velocity (refer R17 Eqn B10) - Real vpar = QdotB / (sqrt(Bsq) * w_old * pow(Dtmp.ucon[0], 2.)); + Real vpar = QdotB / (B_mag * w_old * Dtmp.ucon[0]*Dtmp.ucon[0]); Real ucon_dr[GR_DIM] = {0}; // t-component of drift velocity (refer R17 Eqn B13) - ucon_dr[0] = 1. / sqrt(pow(Dtmp.ucon[0], -2.) + pow(vpar, 2.)); + ucon_dr[0] = 1. / m::sqrt(1. / (Dtmp.ucon[0]*Dtmp.ucon[0]) + vpar*vpar); // spatial components of drift velocity (refer R17 Eqn B11) - for (int mu = 1; mu < GR_DIM; mu++) { - ucon_dr[mu] = Dtmp.ucon[mu] * (ucon_dr[0] / Dtmp.ucon[0]) - (vpar * Bcon[mu] * ucon_dr[0] / sqrt(Bsq)); - } + DLOOP1 ucon_dr[mu] = Dtmp.ucon[mu] * (ucon_dr[0] / Dtmp.ucon[0]) - (vpar * Bcon[mu] * ucon_dr[0] / B_mag); // Update rho, uu and compute new enthalpy P(m_p.RHO, k, j, i) = m::max(rho, rhoflr_max); @@ -245,27 +242,21 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa const Real w_new = P(m_p.RHO, k, j, i) + P(m_p.UU, k, j, i) + pg_new; // New parallel velocity (refer R17 Eqn B14) - const Real x = (2. * QdotB) / (sqrt(Bsq) * w_new * ucon_dr[0]); - vpar = x / (1 + sqrt(1 + x*x)) * (1. / ucon_dr[0]); + const Real x = (2. * QdotB) / (B_mag * w_new * ucon_dr[0]); + vpar = x / (1 + m::sqrt(1 + x*x)) * (1. / ucon_dr[0]); // New fluid four velocity (refer R17 Eqns B13 and B11) - Dtmp.ucon[0] = 1. / sqrt(pow(ucon_dr[0], -2.) - pow(vpar, 2.)); - for (int mu = 1; mu < GR_DIM; mu++) { - Dtmp.ucon[mu] = ucon_dr[mu] * (Dtmp.ucon[0] / ucon_dr[0]) + (vpar * Bcon[mu] * Dtmp.ucon[0] / sqrt(Bsq)); - } + Dtmp.ucon[0] = 1. / m::sqrt(1/(ucon_dr[0]*ucon_dr[0]) - vpar*vpar); + DLOOP1 Dtmp.ucon[mu] = ucon_dr[mu] * (Dtmp.ucon[0] / ucon_dr[0]) + (vpar * Bcon[mu] * Dtmp.ucon[0] / B_mag); G.lower(Dtmp.ucon, Dtmp.ucov, k, j, i, Loci::center); - // New Lorentz factor - const Real gamma = Dtmp.ucon[0] * lapse; - // New velocity primitives - P(m_p.U1, k, j, i) = Dtmp.ucon[1] + (beta[1] * gamma/lapse); - P(m_p.U2, k, j, i) = Dtmp.ucon[2] + (beta[2] * gamma/lapse); - P(m_p.U3, k, j, i) = Dtmp.ucon[3] + (beta[3] * gamma/lapse); + P(m_p.U1, k, j, i) = Dtmp.ucon[1] + (beta[1] * Dtmp.ucon[0]); + P(m_p.U2, k, j, i) = Dtmp.ucon[2] + (beta[2] * Dtmp.ucon[0]); + P(m_p.U3, k, j, i) = Dtmp.ucon[3] + (beta[3] * Dtmp.ucon[0]); // Update the conserved variables Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u, loc); - } else { // Add the material in the normal observer frame, by: // Adding the floors to the primitive variables @@ -344,13 +335,14 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Local& P, co if (floors.use_r_char) { // Steeper floor from iharm3d - Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); + Real rhoscal = 1. / ((r*r) * (1 + r / floors.r_char)); rhoflr_geom = floors.rho_min_geom * rhoscal; uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); } else { // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + Real rhoscal = 1. / m::sqrt(r*r*r); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * rhoscal / r; } } else { rhoflr_geom = floors.rho_min_geom; @@ -385,13 +377,14 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, c if (floors.use_r_char) { // Steeper floor from iharm3d - Real rhoscal = m::pow(r, -2.) * 1 / (1 + r / floors.r_char); + Real rhoscal = 1. / ((r*r) * (1 + r / floors.r_char)); rhoflr_geom = floors.rho_min_geom * rhoscal; uflr_geom = floors.u_min_geom * m::pow(rhoscal, gam); } else { // Original floors from iharm2d - rhoflr_geom = floors.rho_min_geom * m::pow(r, -1.5); - uflr_geom = floors.u_min_geom * m::pow(r, -2.5); //rhoscal/r as in iharm2d + Real rhoscal = 1. / m::sqrt(r*r*r); + rhoflr_geom = floors.rho_min_geom * rhoscal; + uflr_geom = floors.u_min_geom * rhoscal / r; } } else { rhoflr_geom = floors.rho_min_geom; diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 8cf0619f..8905182a 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -43,7 +43,7 @@ using namespace parthenon; TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "Getting conserved GRMHD variables"); + Flag("Flux::BlockPtoUMHD"); // Pointers auto pmb = rc->GetBlockPointer(); // Options @@ -57,7 +57,6 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); - const int nvar = U.GetDim(4); auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; const IndexRange ib = bounds.GetBoundsI(domain); @@ -72,14 +71,12 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool } ); - - Flag(rc, "Got conserved variables"); + EndFlag(); return TaskStatus::complete; } TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "Getting conserved GRMHD variables"); // Pointers auto pmb = rc->GetBlockPointer(); // Options @@ -108,8 +105,6 @@ TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coa } ); - - Flag(rc, "Got conserved variables"); return TaskStatus::complete; } @@ -122,7 +117,7 @@ TaskStatus Flux::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "Getting conserved GRMHD variables"); + // // Pointers auto pmb = rc->GetBlockPointer(); const int ndim = pmb->pmy_mesh->ndim; @@ -167,7 +162,7 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo if (ndim < 3) return TaskStatus::complete; kb.s -= ng; kb.e -= ng; - } + } // TODO(BSP) error? const auto& G = pmb->coords; @@ -177,8 +172,6 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo } ); - - Flag(rc, "Got conserved variables"); return TaskStatus::complete; } diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index 1c1304e2..9ec19561 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -57,14 +57,14 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, { if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary - Real q, dP; - Real qtilde, dPtilde; + Real qtilde = 0., dPtilde = 0.; if (emhd_params.conduction) qtilde = P(m_p.Q); if (emhd_params.viscosity) dPtilde = P(m_p.DP); const Real Theta = (gam - 1) * P(m_p.UU) / P(m_p.RHO); const Real cs2 = gam * (gam - 1) * P(m_p.UU) / (P(m_p.RHO) + gam * P(m_p.UU)); + Real q, dP; EMHD::convert_prims_to_q_dP(qtilde, dPtilde, P(m_p.RHO), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor @@ -87,14 +87,14 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary - Real q, dP; - Real qtilde, dPtilde; + Real qtilde = 0., dPtilde = 0.; if (emhd_params.conduction) qtilde = P(m_p.Q, k, j, i); if (emhd_params.viscosity) dPtilde = P(m_p.DP, k, j, i); const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); + Real q, dP; EMHD::convert_prims_to_q_dP(qtilde, dPtilde, P(m_p.RHO, k, j, i), Theta, cs2, emhd_params, q, dP); // Then calculate the tensor @@ -108,20 +108,6 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Global& P, } } -// template -// KOKKOS_INLINE_FUNCTION void calc_tensor(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, -// const Real& gam, const int& dir, -// Real T[GR_DIM]) -// { -// if (m_p.B1 >= 0) { -// // GRMHD stress-energy tensor w/ first index up, second index down -// GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); -// } else { -// // GRHD stress-energy tensor w/ first index up, second index down -// GRHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); -// } -// } - /** * Turn the primitive variables at a location into: * a. conserved variables (dir==0), or @@ -334,26 +320,22 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const // Find fast magnetosonic speed const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); - const Real ee = bsq + ef; - const Real va2 = bsq / ee; - - Real ccond2 = 0.; - Real cvis2 = 0.; + const Real va2 = bsq / (bsq + ef); - if (emhd_params.conduction) - ccond2 = (gam - 1.) * emhd_params.conduction_alpha * cs2; - if (emhd_params.viscosity) - cvis2 = (4./3.) / (P(m.RHO) + (gam * P(m.UU)) ) * P(m.RHO) * emhd_params.viscosity_alpha * cs2; + const Real ccond2 = (emhd_params.conduction) + ? (gam - 1.) * emhd_params.conduction_alpha * cs2 + : 0.0; + const Real cvis2 = (emhd_params.viscosity) + ? (4./3.) / (P(m.RHO) + (gam * P(m.UU)) ) * P(m.RHO) * emhd_params.viscosity_alpha * cs2 + : 0.0; - const Real cscond = 0.5*(cs2 + ccond2 + sqrt(cs2*cs2 + ccond2*ccond2) ) ; - const Real cs2_emhd = cscond + cvis2; + const Real cs2_emhd = 0.5*(cs2 + ccond2 + m::sqrt(cs2*cs2 + ccond2*ccond2)) + cvis2; cms2 = cs2_emhd + va2 - cs2_emhd*va2; } else if (m.B1 >= 0) { // Find fast magnetosonic speed const Real bsq = m::max(dot(D.bcon, D.bcov), SMALL); - const Real ee = bsq + ef; - const Real va2 = bsq / ee; + const Real va2 = bsq / (bsq + ef); cms2 = cs2 + va2 - cs2 * va2; } else { @@ -376,13 +358,10 @@ KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const const Real Au = dot(Acov, D.ucon); const Real Bu = dot(Bcov, D.ucon); const Real AB = dot(Acon, Bcov); - const Real Au2 = Au * Au; - const Real Bu2 = Bu * Bu; - const Real AuBu = Au * Bu; - A = Bu2 - (Bsq + Bu2) * cms2; - B = 2. * (AuBu - (AB + AuBu) * cms2); - C = Au2 - (Asq + Au2) * cms2; + A = Bu*Bu - (Bsq + Bu*Bu) * cms2; + B = 2. * (Au*Bu - (AB + Au*Bu) * cms2); + C = Au*Au - (Asq + Au*Au) * cms2; } Real discr = m::sqrt(m::max(B * B - 4. * A * C, 0.)); diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index a0b3e6fe..b040d7bd 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -59,7 +59,6 @@ namespace Flux { template inline TaskStatus GetFlux(MeshData *md) { - Flag(md, "Recon and flux"); // Pointers auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -68,6 +67,8 @@ inline TaskStatus GetFlux(MeshData *md) if (ndim < 3 && dir == X3DIR) return TaskStatus::complete; if (ndim < 2 && dir == X2DIR) return TaskStatus::complete; + Flag("GetFlux_"+std::to_string(dir)); + // Options const auto& pars = pmb0->packages.Get("Driver")->AllParams(); const auto& mhd_pars = pmb0->packages.Get("GRMHD")->AllParams(); @@ -104,7 +105,6 @@ inline TaskStatus GetFlux(MeshData *md) const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); - //Flag(md, "Packed variables"); // Get sizes const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); @@ -132,7 +132,6 @@ inline TaskStatus GetFlux(MeshData *md) 4*(Recon == KReconstruction::Type::linear_vl)) * var_size_in_bytes + 2 * speed_size_in_bytes; - Flag(md, "Flux kernel"); // This isn't a pmb0->par_for_outer because Parthenon's current overloaded definitions // do not accept three pairs of bounds, which we need in order to iterate over blocks parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux", pmb0->exec_space, @@ -280,7 +279,7 @@ inline TaskStatus GetFlux(MeshData *md) } ); - Flag(md, "Finished recon and flux"); + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index f5c6963f..b6944354 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -212,7 +212,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc) { - Flag(rc, "Estimating timestep"); + // Normally the caller would place this flag before calling us, but this is from Parthenon + // This function is a nice demo of why client-side flagging + // like this is inadvisable: you have to EndFlag() at every different return + Flag("EstimateTimestep"); auto pmb = rc->GetBlockPointer(); IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); @@ -239,17 +242,20 @@ Real EstimateTimestep(MeshBlockData *rc) } else { globals.Add("dt_light", dt); } + EndFlag(); return dt; } else { // Or Just take from parameters double dt = grmhd_pars.Get("dt_start"); // Record this, since we'll use it to determine the max step increase globals.Update("dt_last", dt); + EndFlag(); return dt; } } // If we're still using the light crossing time, skip the rest if (grmhd_pars.Get("use_dt_light")) { + EndFlag(); return globals.Get("dt_light"); } @@ -288,13 +294,13 @@ Real EstimateTimestep(MeshBlockData *rc) b_cd_params.Update("ctop_max", nctop); } - Flag(rc, "Estimated"); + EndFlag(); return ndt; } Real EstimateRadiativeTimestep(MeshBlockData *rc) { - Flag(rc, "Estimating shortest light crossing time"); + Flag("EstimateRadiativeTimestep"); auto pmb = rc->GetBlockPointer(); IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); @@ -317,16 +323,16 @@ Real EstimateRadiativeTimestep(MeshBlockData *rc) if (phase_speed) { for (int mu = 1; mu < GR_DIM; mu++) { - if(m::pow(G.gcon(Loci::center, j, i, 0, mu), 2) - + if(SQR(G.gcon(Loci::center, j, i, 0, mu)) - G.gcon(Loci::center, j, i, mu, mu)*G.gcon(Loci::center, j, i, 0, 0) >= 0.) { double cplus = m::abs((-G.gcon(Loci::center, j, i, 0, mu) + - m::sqrt(m::pow(G.gcon(Loci::center, j, i, 0, mu), 2) - + m::sqrt(SQR(G.gcon(Loci::center, j, i, 0, mu)) - G.gcon(Loci::center, j, i, mu, mu)*G.gcon(Loci::center, j, i, 0, 0)))/ G.gcon(Loci::center, j, i, 0, 0)); double cminus = m::abs((-G.gcon(Loci::center, j, i, 0, mu) - - m::sqrt(m::pow(G.gcon(Loci::center, j, i, 0, mu), 2) - + m::sqrt(SQR(G.gcon(Loci::center, j, i, 0, mu)) - G.gcon(Loci::center, j, i, mu, mu)*G.gcon(Loci::center, j, i, 0, 0)))/ G.gcon(Loci::center, j, i, 0, 0)); @@ -354,7 +360,7 @@ Real EstimateRadiativeTimestep(MeshBlockData *rc) const double cfl = grmhd_pars.Get("cfl"); const double ndt = minmax.min_val * cfl; - Flag(rc, "Estimated"); + EndFlag(); return ndt; } @@ -390,13 +396,11 @@ AmrTag CheckRefinement(MeshBlockData *rc) TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) { - Flag("Printing GRMHD diagnostics"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options const auto& pars = pmesh->packages.Get("Globals")->AllParams(); const int extra_checks = pars.Get("extra_checks"); - Flag("Got pointers"); // Check for a soundspeed (ctop) of 0 or NaN // This functions as a "last resort" check to stop a @@ -410,11 +414,9 @@ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) // Further checking for any negative values. Floors should // prevent this, so we save it for dire debugging if (extra_checks >= 2) { - Flag("Printing negative zones"); CheckNegative(md, IndexDomain::interior); } - Flag("Printed"); return TaskStatus::complete; } diff --git a/kharma/grmhd/grmhd_reductions.hpp b/kharma/grmhd/grmhd_reductions.hpp index 169a06d2..a98f40a3 100644 --- a/kharma/grmhd/grmhd_reductions.hpp +++ b/kharma/grmhd/grmhd_reductions.hpp @@ -101,7 +101,7 @@ KOKKOS_INLINE_FUNCTION Real eht_lum(REDUCE_FUNCTION_ARGS_MESH) Real rho = P(m_p.RHO, b, k, j, i); Real Pg = (gam - 1.) * P(b, m_p.UU, k, j, i); Real Bmag = m::sqrt(dot(Dtmp.bcon, Dtmp.bcov)); - Real j_eht = rho*rho*rho/Pg/Pg * m::exp(-0.2 * m::pow(rho * rho / (Bmag * Pg * Pg), 1./3.)); + Real j_eht = rho*rho*rho/Pg/Pg * m::exp(-0.2 * m::cbrt(rho * rho / (Bmag * Pg * Pg))); return j_eht; } else { return 0.; diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index e8377905..f6d92a7e 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -39,7 +39,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { - Flag(mbd, "Fixing implicit solver failures"); + Flag("FixSolve"); // Get MeshBlock pointer and obtain flag for primitives auto pmb = mbd->GetBlockPointer(); @@ -144,7 +144,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { } ); - Flag(mbd, "Fixed solver failures"); + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 27f47e49..6254055a 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -138,10 +138,7 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_sub_step_init, MeshData *md_flux_src, MeshData *md_linesearch, MeshData *md_solver, const Real& dt) { - Flag(md_full_step_init, "Implicit Iteration start, full step"); - Flag(md_sub_step_init, "Implicit Iteration start, sub step"); - Flag(md_flux_src, "Implicit Iteration start, divF and sources"); - Flag(md_linesearch, "Linesearch"); + Flag("Implicit::Step"); // Pull out the block pointers for each sub-step, as we need the *mutable parameters* // of the EMHD package. TODO(BSP) restrict state back to the variables... auto pmb_full_step_init = md_full_step_init->GetBlockData(0)->GetBlockPointer(); @@ -272,7 +269,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // different zones, so probably acceptable speed loss. for (int iter=1; iter <= iter_max; ++iter) { // Flags per iter, since debugging here will be rampant - Flag(md_solver, "Implicit Iteration:"); + Flag("ImplicitIteration_"+std::to_string(iter)); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "implicit_solve", pmb_sub_step_init->exec_space, total_scratch_bytes, scratch_level, block.s, block.e, kb.s, kb.e, jb.s, jb.e, @@ -598,9 +595,10 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Break if max_norm is less than the total tolerance we set. TODO per-zone version of this? if (iter >= iter_min && max_norm.val < rootfind_tol) break; } + EndFlag(); } - Flag(md_solver, "Implicit Iteration: final"); + EndFlag(); return TaskStatus::complete; @@ -608,7 +606,6 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ TaskStatus Implicit::PostStepDiagnostics(const SimTime& tm, MeshData *md) { - Flag("Printing Implicit solver diagnostics"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index 6493022d..7004cd9f 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -150,15 +150,15 @@ KOKKOS_INLINE_FUNCTION void calc_residual(const GRCoordinates& G, const Local& P residual(m_u.Q) *= tau; if (emhd_params.viscosity) residual(m_u.DP) *= tau; - if (emhd_params.higher_order_terms){ + if (emhd_params.higher_order_terms) { Real rho = Ps(m_p.RHO); Real uu = Ps(m_p.UU); Real Theta = (gam - 1.) * uu / rho; if (emhd_params.conduction) - residual(m_u.Q) *= (chi_e != 0) ? sqrt(rho * chi_e * tau * pow(Theta, 2)) / tau : 1.; + residual(m_u.Q) *= (chi_e != 0) ? m::sqrt(rho * chi_e * tau * Theta * Theta) / tau : 1.; if (emhd_params.viscosity) - residual(m_u.DP) *= (nu_e != 0) ? sqrt(rho * nu_e * tau * Theta) / tau : 1.; + residual(m_u.DP) *= (nu_e != 0) ? m::sqrt(rho * nu_e * tau * Theta) / tau : 1.; } } diff --git a/kharma/inverter/fixup.cpp b/kharma/inverter/fixup.cpp index 0826df89..0f436fae 100644 --- a/kharma/inverter/fixup.cpp +++ b/kharma/inverter/fixup.cpp @@ -55,7 +55,7 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) return TaskStatus::complete; } - Flag(rc, "Fixing U to P inversions"); + Flag("Inverter::FixUtoP"); // Only fixup the core 5 prims auto P = GRMHD::PackHDPrims(rc); @@ -152,6 +152,6 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) ); } - Flag(rc, "Fixed U to P inversions"); + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index 316bed09..64e35528 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -45,7 +45,6 @@ template inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "Filling Primitives"); auto pmb = rc->GetBlockPointer(); const auto& G = pmb->coords; @@ -81,7 +80,6 @@ inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, b } } ); - Flag(rc, "Filled"); } std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::shared_ptr& packages) @@ -147,7 +145,6 @@ void Inverter::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coars TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) { - Flag("Printing Floor diagnostics"); auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Options @@ -156,7 +153,6 @@ TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) // Debugging/diagnostic info about floor and inversion flags if (flag_verbose >= 1) { - Flag("Printing flags"); int nflags = Reductions::CountFlags(md, "pflag", Inverter::status_names, IndexDomain::interior, flag_verbose, false); // TODO TODO yell here if there are too many flags } diff --git a/kharma/inverter/onedw.hpp b/kharma/inverter/onedw.hpp index dcdc9ee6..dea8f05d 100644 --- a/kharma/inverter/onedw.hpp +++ b/kharma/inverter/onedw.hpp @@ -83,11 +83,11 @@ KOKKOS_INLINE_FUNCTION Real err_eqn(const Real& gam, const Real& Bsq, const Real const Real W = Wp + D; const Real gamma = lorentz_calc_w(Bsq, D, QdB, Qtsq, Wp); if (gamma < 1) eflag = Status::bad_ut; - const Real w = W / m::pow(gamma,2); + const Real w = W / (gamma*gamma); const Real rho = D / gamma; const Real p = (w - rho) * (gam - 1) / gam; - return -Ep + Wp - p + 0.5 * Bsq + 0.5 * (Bsq * Qtsq - QdB * QdB) / m::pow((Bsq + W), 2); + return -Ep + Wp - p + 0.5 * Bsq + 0.5 * (Bsq * Qtsq - QdB * QdB) / SQR(Bsq + W); } @@ -138,7 +138,7 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const Real Qtcon[GR_DIM]; DLOOP1 Qtcon[mu] = Qcon[mu] + ncon[mu] * Qdotn; - const Real Qtsq = dot(Qcon, Qcov) + m::pow(Qdotn, 2); + const Real Qtsq = dot(Qcon, Qcov) + Qdotn*Qdotn; // Set up eqtn for W'; this is the energy density const Real Ep = -Qdotn - D; @@ -170,9 +170,9 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const // Attempt a Halley/Muller/Bailey/Press step const Real dedW = (errp - errm) / (Wpp - Wpm); - const Real dedW2 = (errp - 2. * err + errm) / m::pow(h,2); + const Real dedW2 = (errp - 2. * err + errm) / (h*h); // TODO look into changing these clipped values? - const Real f = clip(0.5 * err * dedW2 / m::pow(dedW,2), -0.3, 0.3); + const Real f = clip(0.5 * err * dedW2 / (dedW*dedW), -0.3, 0.3); dW = clip(-err / dedW / (1. - f), -0.5*Wp, 2.0*Wp); } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 0e657ee0..2f0cec53 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -110,7 +110,6 @@ void KHARMA::ResetGlobals(ParameterInput *pin, Mesh *pmesh) void KHARMA::MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("KHARMA Pre-step"); auto& globals = pmesh->packages.Get("Globals")->AllParams(); if (!globals.Get("in_loop")) { globals.Update("in_loop", true); @@ -121,7 +120,6 @@ void KHARMA::MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const S void KHARMA::MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("KHARMA Post-step"); // Knowing this works took a little digging into Parthenon's EvolutionDriver. // The order of operations after calling Step() is: // 1. Call PostStepUserWorkInLoop and PostStepDiagnostics (this function and following) @@ -253,17 +251,18 @@ void KHARMA::FixParameters(std::unique_ptr& pin) if (tmp_coords.stopx(3) >= 0) pin->GetOrAddReal("parthenon/mesh", "x3max", tmp_coords.stopx(3)); - Flag("Fixed"); + EndFlag(); } TaskStatus KHARMA::AddPackage(std::shared_ptr& packages, std::function(ParameterInput*, std::shared_ptr&)> package_init, ParameterInput *pin) { - Flag("AddPackage"); + // TODO package names before initialization const auto& pkg = package_init(pin, packages); packages->Add(pkg); - EndFlag("AddPackage "+pkg->label()); + Flag("AddPackage_"+pkg->label()); + EndFlag(); return TaskStatus::complete; } @@ -356,6 +355,8 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // TODO avoid init if e.g. all periodic boundaries? KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); - EndFlag("ProcessPackages"); // TODO print full package list way up here? + // TODO print full package list as soon as we know it, up here + + EndFlag(); return std::move(*packages); } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 88592fe7..967c0a20 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -37,8 +37,7 @@ // PHYSICS-RELATED // TODO take & accumulate TaskStatus? Useful for ::incomplete if we ever want to do that -// TODO Several of these are unused & commented, but will be used as I meshify different drivers. -// Then, I can work on meshifying packages by degrees +// TODO continue meshification until all is mesh TaskStatus Packages::FixFlux(MeshData *md) { @@ -48,114 +47,130 @@ TaskStatus Packages::FixFlux(MeshData *md) if (kpackage.second->FixFlux != nullptr) { Flag("FixFlux_"+kpackage.first); kpackage.second->FixFlux(md); - EndFlag("FixFlux_"+kpackage.first); + EndFlag(); } } - EndFlag("FixFlux"); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag("Recovering primitive variables"); - auto kpackages = rc->GetBlockPointer()->packages.ListPackagesOfType(); + Flag("BlockUtoP"); + auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage->BlockUtoP != nullptr) - kpackage->BlockUtoP(rc, domain, coarse); + if (kpackage.second->BlockUtoP != nullptr) { + Flag("BlockUtoP_"+kpackage.first); + kpackage.second->BlockUtoP(rc, domain, coarse); + EndFlag(); + } } - Flag("Recovered"); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) { + Flag("MeshUtoP"); for (int i=0; i < md->NumBlocks(); ++i) BlockUtoP(md->GetBlockData(i).get(), domain, coarse); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::BlockUtoPExceptMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag(rc, "Recovering primitive variables on boundaries"); + Flag("BlockUtoPExceptMHD"); // We need to re-fill the primitive variables on the physical boundaries, // since the driver has already called UtoP for the step. // However, this does *not* apply to the GRMHD variables, as the boundary call // used/filled their primitive values. Instead, they will need a PtoU call - auto pmb = rc->GetBlockPointer(); - for (auto &package : pmb->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (package.first != "GRMHD" && package.first != "Inverter") { - if (kpackage->BlockUtoP != nullptr) - kpackage->BlockUtoP(rc, domain, coarse); + auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.first != "GRMHD" && kpackage.first != "Inverter") { + if (kpackage.second->BlockUtoP != nullptr) { + Flag("BlockUtoPExceptMHD_"+kpackage.first); + kpackage.second->BlockUtoP(rc, domain, coarse); + EndFlag(); } } } - Flag(rc, "Recovered"); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::MeshUtoPExceptMHD(MeshData *md, IndexDomain domain, bool coarse) { + Flag("MeshUtoPExceptMHD"); for (int i=0; i < md->NumBlocks(); ++i) BlockUtoPExceptMHD(md->GetBlockData(i).get(), domain, coarse); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::AddSource(MeshData *md, MeshData *mdudt) { - Flag("Adding source terms"); - for (auto &package : md->GetMeshPointer()->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->AddSource != nullptr) - kpackage->AddSource(md, mdudt); + Flag("AddSource"); + auto kpackages = md->GetMeshPointer()->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->AddSource != nullptr) { + Flag("AddSource_"+kpackage.first); + kpackage.second->AddSource(md, mdudt); + EndFlag(); } } - Flag("Added"); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::BlockApplyPrimSource(MeshBlockData *rc) { - Flag("Applying primitive source terms"); - for (auto &package : rc->GetBlockPointer()->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->BlockApplyPrimSource != nullptr) - kpackage->BlockApplyPrimSource(rc); + // TODO print only if there's calls inside? + Flag("BlockApplyPrimSource"); + auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->BlockApplyPrimSource != nullptr) { + kpackage.second->BlockApplyPrimSource(rc); } } - Flag("Added"); + EndFlag(); return TaskStatus::complete; } -// TODO will these need to be done on coarse versions? TaskStatus Packages::BlockApplyFloors(MeshBlockData *mbd, IndexDomain domain) { - Flag("Applying floors"); + Flag("BlockApplyFloors"); auto pmb = mbd->GetBlockPointer(); auto pkgs = pmb->packages.AllPackages(); // Apply the version from "Floors" package first if (pkgs.count("Floors")) { - KHARMAPackage *kpackage = dynamic_cast(pkgs.at("Floors").get()); - // We *want* to crash on null deref if this kpackage is null, something would be wrong - if (kpackage->BlockApplyFloors != nullptr) - kpackage->BlockApplyFloors(mbd, domain); + KHARMAPackage *pkpackage = pmb->packages.Get("Floors"); + if (pkpackage->BlockApplyFloors != nullptr) { + Flag("BlockApplyFloors_Floors"); + pkpackage->BlockApplyFloors(mbd, domain); + EndFlag(); + } } // Then anything else - for (auto &package : mbd->GetBlockPointer()->packages.AllPackages()) { - if (package.first != "Floors") { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->BlockApplyFloors != nullptr) - kpackage->BlockApplyFloors(mbd, domain); + auto kpackages = pmb->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.first != "Floors") { + if (kpackage.second->BlockApplyFloors != nullptr) { + Flag("BlockApplyFloors_"+kpackage.first); + kpackage.second->BlockApplyFloors(mbd, domain); + EndFlag(); } } } - Flag("Applied"); + EndFlag(); return TaskStatus::complete; } TaskStatus Packages::MeshApplyFloors(MeshData *md, IndexDomain domain) { + Flag("MeshApplyFloors"); for (int i=0; i < md->NumBlocks(); ++i) BlockApplyFloors(md->GetBlockData(i).get(), domain); + EndFlag(); return TaskStatus::complete; } @@ -163,49 +178,61 @@ TaskStatus Packages::MeshApplyFloors(MeshData *md, IndexDomain domain) // TODO this will need to be mesh'd too void Packages::UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin) { - Flag("Filling output arrays"); - for (auto &package : pmb->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->BlockUserWorkBeforeOutput != nullptr) - kpackage->BlockUserWorkBeforeOutput(pmb, pin); + Flag("UserWorkBeforeOutput"); + auto kpackages = pmb->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->BlockUserWorkBeforeOutput != nullptr) { + Flag("UserWorkBeforeOutput_"+kpackage.first); + kpackage.second->BlockUserWorkBeforeOutput(pmb, pin); + EndFlag(); } } - Flag("Filled"); + EndFlag(); } void Packages::PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("Pre-step package work"); - for (auto &package : pmesh->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->MeshPreStepUserWorkInLoop != nullptr) - kpackage->MeshPreStepUserWorkInLoop(pmesh, pin, tm); + Flag("PreStepUserWorkInLoop"); + auto kpackages = pmesh->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->MeshPreStepUserWorkInLoop != nullptr) { + Flag("PreStepUserWorkInLoop_"+kpackage.first); + kpackage.second->MeshPreStepUserWorkInLoop(pmesh, pin, tm); + EndFlag(); } } - Flag("Done pre-step package work"); + EndFlag(); } void Packages::PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("Post-step package work"); - for (auto &package : pmesh->packages.AllPackages()) { - if (KHARMAPackage *kpackage = dynamic_cast(package.second.get())) { - if (kpackage->MeshPostStepUserWorkInLoop != nullptr) - kpackage->MeshPostStepUserWorkInLoop(pmesh, pin, tm); + Flag("PostStepUserWorkInLoop"); + auto kpackages = pmesh->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->MeshPostStepUserWorkInLoop != nullptr) { + Flag("PostStepUserWorkInLoop_"+kpackage.first); + kpackage.second->MeshPostStepUserWorkInLoop(pmesh, pin, tm); + EndFlag(); } } + EndFlag(); } void Packages::PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { // Parthenon's version of this has a bug, but I would probably subclass it anyway. // very useful to have a single per-step spot to control any routine print statements + Flag("PostStepDiagnostics"); const auto& md = pmesh->mesh_data.GetOrAdd("base", 0).get(); if (md->NumBlocks() > 0) { for (auto &package : pmesh->packages.AllPackages()) { - if (package.second->PostStepDiagnosticsMesh != nullptr) + if (package.second->PostStepDiagnosticsMesh != nullptr) { + Flag("PostStepDiagnostics_"+package.first); package.second->PostStepDiagnosticsMesh(tm, md); + EndFlag(); + } } } + EndFlag(); } diff --git a/kharma/main.cpp b/kharma/main.cpp index 000f59ab..218e9b88 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -76,6 +76,11 @@ void print_backtrace(int sig) { exit(1); } #endif +// Single globals for proper indentation when tracing execution +#if TRACE +int kharma_debug_trace_indent = 0; +int kharma_debug_trace_mutex = 0; +#endif using namespace parthenon; @@ -119,7 +124,7 @@ int main(int argc, char *argv[]) // Parthenon init includes Kokkos, MPI, parses parameters & cmdline, // then calls ProcessPackages and ProcessProperties, then constructs the Mesh - Flag("Parthenon Init"); + Flag("ParthenonInit"); auto manager_status = pman.ParthenonInit(argc, argv); if (manager_status == ParthenonStatus::complete) { pman.ParthenonFinalize(); @@ -129,7 +134,7 @@ int main(int argc, char *argv[]) pman.ParthenonFinalize(); return 1; } - EndFlag("Parthenon Init"); + EndFlag(); #if DEBUG // Replace Parthenon signal handlers with something that just prints a backtrace @@ -161,8 +166,14 @@ int main(int argc, char *argv[]) // MeshBlocks to be initialized already auto prob = pin->GetString("parthenon/job", "problem_id"); bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); + Flag("PostInitialize"); KHARMA::PostInitialize(pin, pmesh, is_restart); - Flag("Post-initialization completed"); + EndFlag(); + +#if DEBUG + // Carry the ParameterInput with us, for generating outputs whenever we want + pmesh->packages.Get("Globals")->AllParams().Add("pin", pin); +#endif // Construct a temporary driver purely for parameter parsing KHARMADriver driver(pin, papp, pmesh); @@ -178,13 +189,15 @@ int main(int argc, char *argv[]) // Then execute the driver. This is a Parthenon function inherited by our HARMDriver object, // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion // of each step until a stop criterion is reached. - Flag("Executing Driver"); + Flag("driver.Execute"); auto driver_status = driver.Execute(); + EndFlag(); } // Parthenon cleanup includes Kokkos, MPI - Flag("Finalizing"); + Flag("ParthenonFinalize"); pman.ParthenonFinalize(); + EndFlag(); return 0; } diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 2d6807f6..180258ab 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -79,7 +79,7 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! - auto bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + auto bound_pkg = pmb->packages.Get("Boundaries"); if (pin->GetString("boundaries", "inner_x1") == "dirichlet" || pin->GetString("boundaries", "outer_x1") == "dirichlet") { SetBondi(rc); // TODO iterate & set any bounds specifically? @@ -106,7 +106,6 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { - Flag(rc, "Setting Bondi zones"); auto pmb = rc->GetBlockPointer(); //std::cerr << "Bondi on domain: " << BoundaryName(domain) << std::endl; @@ -177,6 +176,5 @@ TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain do } ); - Flag(rc, "Set"); return TaskStatus::complete; } diff --git a/kharma/prob/elec/driven_turbulence.hpp b/kharma/prob/elec/driven_turbulence.hpp index 5257857e..a80e7dee 100644 --- a/kharma/prob/elec/driven_turbulence.hpp +++ b/kharma/prob/elec/driven_turbulence.hpp @@ -85,7 +85,6 @@ TaskStatus InitializeDrivenTurbulence(std::shared_ptr>& rc, } ); - Flag(rc, "Initialized"); return TaskStatus::complete; } @@ -96,7 +95,6 @@ TaskStatus InitializeDrivenTurbulence(std::shared_ptr>& rc, */ void ApplyDrivingTurbulence(MeshBlockData *rc) { - Flag("Applying Driven Turbulence kick"); auto pmb = rc->GetBlockPointer(); const IndexRange myib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); const IndexRange myjb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); diff --git a/kharma/prob/elec/hubble.cpp b/kharma/prob/elec/hubble.cpp index 47255c49..bd3c4aae 100644 --- a/kharma/prob/elec/hubble.cpp +++ b/kharma/prob/elec/hubble.cpp @@ -73,7 +73,7 @@ TaskStatus InitializeHubble(std::shared_ptr>& rc, ParameterI } // Replace the boundary conditions - auto *bound_pkg = static_cast(pmb->packages.Get("Boundaries").get()); + auto bound_pkg = pmb->packages.Get("Boundaries"); bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetHubble; bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetHubble; bound_pkg->BlockApplyPrimSource = ApplyHubbleHeating; @@ -181,7 +181,6 @@ TaskStatus SetHubbleImpl(std::shared_ptr>& rc, IndexDomain d void ApplyHubbleHeating(MeshBlockData *mbase) { - Flag(mbase, "Applying heating"); auto pmb0 = mbase->GetBlockPointer(); PackIndexMap prims_map; @@ -206,6 +205,4 @@ void ApplyHubbleHeating(MeshBlockData *mbase) P_mbase(m_p.UU, k, j, i) += Q*dt*0.5; } ); - - Flag(mbase, "Applied heating"); } diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 1a6a6e42..bbfaff0b 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -106,22 +106,25 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; GridVector B_P = rc->Get("prims.B").data; - GridScalar q; - GridScalar dP; - if (use_emhd) { - q = rc->Get("prims.q").data; - dP = rc->Get("prims.dP").data; - } + // Host side mirror of primitives auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); auto B_host = B_P.GetHostMirror(); + + // Then for EMHD if enabled + GridScalar q; + GridScalar dP; // Temporary initializations are necessary for auto type auto q_host = rho.GetHostMirror(); auto dP_host = rho.GetHostMirror(); - if (use_emhd) { + if (use_emhd && emhd_params.conduction) { + q = rc->Get("prims.q").data; q_host = q.GetHostMirror(); + } + if (use_emhd && emhd_params.viscosity) { + dP = rc->Get("prims.dP").data; dP_host = dP.GetHostMirror(); } @@ -134,7 +137,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame fscanf(fp_r, "%lf", &(rCoords[i])); GReal Xembed[GR_DIM]; G.coord_embed(0, jb_in.s, i, Loci::center, Xembed); - error = fabs(Xembed[1] - rCoords[i]); + error = m::abs(Xembed[1] - rCoords[i]); if (error > 1.e-10) { fprintf(stdout, "Error at radial zone i = %d, Error = %8.5e KHARMA: %8.7e, sage nb: %8.7e\n", i, error, Xembed[1], rCoords[i]); exit(-1); @@ -165,19 +168,15 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame q_host(k, j, i) = q_temp; // Now the remaining primitives - uvec_host(V1, k, j, i) = 0.; - uvec_host(V2, k, j, i) = 0.; - uvec_host(V3, k, j, i) = 0.; B_host(V1, k, j, i) = 1./(Xembed[1]*Xembed[1]*Xembed[1]); B_host(V2, k, j, i) = 0.; B_host(V3, k, j, i) = 0.; - if (use_emhd) + if (use_emhd && emhd_params.viscosity) dP_host(k, j, i) = 0.; // Note that the velocity primitives defined up there aren't quite right. // For a fluid at rest wrt. the normal observer, ucon = {-1/g_tt,0,0,0}. // We need to use this info to obtain the correct values for U1, U2 and U3 - // TODO is this just fourvel_to_prim? Real ucon[GR_DIM] = {0}; Real gcov[GR_DIM][GR_DIM] = {0}; @@ -198,21 +197,15 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame uvec_host(V2, k, j, i) = u_prim[V2]; uvec_host(V3, k, j, i) = u_prim[V3]; - if (use_emhd) { + if (use_emhd && emhd_params.higher_order_terms) { // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde - Real q_tilde = q_host(k, j, i); - Real dP_tilde = dP_host(k, j, i); - - if (emhd_params.higher_order_terms) { - Real tau, chi_e, nu_e; - EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); - const Real Theta = (gam - 1.) * u_temp / rho_temp; - - q_tilde *= (chi_e != 0) * m::sqrt(tau / (chi_e * rho_temp * Theta * Theta)); - dP_tilde *= (nu_e != 0) * m::sqrt(tau / (nu_e * rho_temp * Theta)); - } - q_host(k, j, i) = q_tilde; - dP_host(k, j, i) = dP_tilde; + Real tau, chi_e, nu_e; + EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + const Real Theta = (gam - 1.) * u_temp / rho_temp; + if (emhd_params.conduction) + q_host(k, j, i) *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho_temp * Theta * Theta)) : 0; + if (emhd_params.viscosity) + dP_host(k, j, i) *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho_temp * Theta)) : 0; } } } @@ -231,16 +224,15 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); B_P.DeepCopy(B_host); - if (use_emhd) { + if (use_emhd && emhd_params.conduction) q.DeepCopy(q_host); + if (use_emhd && emhd_params.viscosity) dP.DeepCopy(dP_host); - } Kokkos::fence(); // Also fill cons.B B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); - Flag("Initialized"); return TaskStatus::complete; } diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 66034e11..af14cdcd 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -131,8 +131,8 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet Real q_tilde = q(k, j, i); Real dP_tilde = dP(k, j, i); if (emhd_params.higher_order_terms) { - q_tilde *= (chi_e != 0) ? sqrt(tau / (chi_e * rho(k, j, i) * pow(Theta, 2.))) : 0.; - dP_tilde *= (nu_e != 0) ? sqrt(tau / (nu_e * rho(k, j, i) * Theta)) : 0.; + q_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho(k, j, i) * Theta * Theta)) : 0.; + dP_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho(k, j, i) * Theta)) : 0.; } q(k, j, i) = q_tilde; dP(k, j, i) = dP_tilde; diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index ee335953..92766836 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -143,7 +143,7 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet Real q_tilde = q_host(k, j, i); Real dP_tilde = dP_host(k, j, i); if (emhd_params.higher_order_terms) { - q_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho_temp * m::pow(Theta, 2.))) : 0.; + q_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho_temp * Theta * Theta)) : 0.; dP_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho_temp * Theta)) : 0.; } q_host(k, j, i) = q_tilde; diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index af968674..b4f11eaf 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -207,7 +207,6 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter // TODO move this to a different file TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Applying U perturbation"); auto pmb = rc->GetBlockPointer(); auto rho = rc->Get("prims.rho").data; auto u = rc->Get("prims.u").data; @@ -258,6 +257,5 @@ TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pi ); } - Flag(rc, "Applied"); return TaskStatus::complete; } diff --git a/kharma/prob/gizmo.cpp b/kharma/prob/gizmo.cpp index a3c88f71..0e604f50 100644 --- a/kharma/prob/gizmo.cpp +++ b/kharma/prob/gizmo.cpp @@ -69,7 +69,6 @@ TaskStatus InitializeGIZMO(std::shared_ptr>& rc, ParameterIn TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { - Flag(rc, "Setting zones from GIZMO output"); auto pmb = rc->GetBlockPointer(); //std::cerr << "GIZMO on domain: " << BoundaryName(domain) << std::endl; @@ -156,6 +155,5 @@ TaskStatus SetGIZMO(std::shared_ptr>& rc, IndexDomain domain } ); - Flag(rc, "Set"); return TaskStatus::complete; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index a0182798..43ee9076 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -109,6 +109,8 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrpackages.AllPackages().count("B_CD"); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); + // TODO this should be restructured... + Flag("SeedBField"); // Seed the magnetic field on each block for (auto &pmb : pmesh->block_list) { @@ -121,7 +123,7 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr 0) { Real norm = m::sqrt(beta_min/desired_beta_min); for (auto &pmb : pmesh->block_list) { @@ -184,18 +185,15 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr(pmesh, pin, &tm); - pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); - - Flag("Post-initialization finished"); } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 7cd9ab4d..435cd87e 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -74,7 +74,7 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) { auto rc = pmb->meshblock_data.Get(); auto prob = pin->GetString("parthenon/job", "problem_id"); // Required parameter - Flag("Initialize "+prob); + Flag("ProblemGenerator_"+prob); // Also just print this, it's important if (MPIRank0()) { std::cout << "Initializing problem: " << prob << std::endl; @@ -150,25 +150,20 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) } } - // Note that at this stage we have initialized the fluid primitives ONLY in the torus. - // What this means is that in the following `PtoU` call, we will get the NaNs for the conserved vars - // outside the torus since the floors are not called yet (we need conserved vars for NOF floors). - // In the subsequent `ApplyFloors` call we are able to initialize the NOF floors despite this - // because it falls back to fluid frame floors in the event the UtoP is unsuccessful. - // TODO: Maybe let the user know that despite asking for NOF floors, fluid frame floors will be applied - // the very first time during problem init. - // For now, I've opened an issue on github to address this. + // TODO blob here? + + // Floors are NOT automatically applied at this point anymore. + // If needed, they are applied within the problem-specific call. + // See InitializeFMTorus in fm_torus.cpp for the details for torus problems. // Fill the conserved variables U, // which we'll usually treat as the independent/fundamental state. // This will need to be repeated once magnetic field is seeded - Flux::BlockPtoU(rc.get(), IndexDomain::interior); - - // Floors are NOT automatically applied at this point anymore. - // If needed, they should be applied inside the problem's InitializeXXXX + // Note we do the whole domain, in case we're using Dirichlet conditions + Flux::BlockPtoU(rc.get(), IndexDomain::entire); // Finally, freeze in the current ghost zone values if using Dirichlet conditions KBoundaries::FreezeDirichletBlock(rc.get()); - EndFlag("Initialize "+prob); + EndFlag(); } diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 2e2b277a..0a60caaf 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -233,8 +233,6 @@ void ReadIharmRestartHeader(std::string fname, std::unique_ptr& TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterInput *pin) { - Flag(rc, "Restarting from iharm3d checkpoint file"); - auto pmb = rc->GetBlockPointer(); const auto fname = pin->GetString("resize_restart", "fname"); // Require this, don't guess @@ -434,7 +432,6 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI auto uvec_host = uvec.GetHostMirror(); auto B_host = B_P.GetHostMirror(); - Flag("Interpolating meshblock..."); // Interpolate on the host side & copy into the mirror Views // Nearest-neighbor interpolation is currently only used when grids exactly correspond -- otherwise, linear interpolation is used // to minimize the resulting B field divergence. @@ -485,7 +482,6 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI } // Deep copy to device - Flag("Copying meshblock to device..."); rho.DeepCopy(rho_host); u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); @@ -493,7 +489,6 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI Kokkos::fence(); // Delete our cache. Only we ever used it, so we're safe here. - Flag("Deleting cached interpolation values"); delete[] ptmp; return TaskStatus::complete; diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 892cbd6a..9ccdcd8a 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -124,8 +124,6 @@ void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& TaskStatus ReadKharmaRestart(std::shared_ptr> rc, ParameterInput *pin) { - Flag(rc, "Restarting from KHARMA checkpoint file"); - auto pmb = rc->GetBlockPointer(); const hsize_t n1tot = pin->GetInteger("parthenon/mesh", "restart_nx1"); diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index 792ec4ab..07f7f1f7 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -40,7 +40,7 @@ #pragma hd_warning_disable Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone) { - Flag("Performing accretion reduction"); + Flag("EHReduction"); auto pmesh = md->GetMeshPointer(); Real result = 0.; @@ -99,14 +99,14 @@ Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::f } } - Flag("Reduced"); + EndFlag(); return result; } #pragma hd_warning_disable Real Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg) { - Flag("Performing domain reduction"); + Flag("DomainReduction"); auto pmesh = md->GetMeshPointer(); // TODO TODO MESHDATA THIS @@ -160,7 +160,7 @@ Real Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, st } } - Flag("Reduced"); + EndFlag(); return result; } @@ -195,7 +195,7 @@ int Reductions::CountFlag(MeshData *md, std::string field_name, const int& int Reductions::CountFlags(MeshData *md, std::string field_name, std::map flag_values, IndexDomain domain, int verbose, bool is_bitflag) { - Flag("Counting inversion failures"); + Flag("CountFlags_"+field_name); int nflags = 0; auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -226,7 +226,7 @@ int Reductions::CountFlags(MeshData *md, std::string field_name, std::map< // 0, nang1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, // KOKKOS_LAMBDA(const int b, const int n, const int k, const int j, const int i, // array_sum::array_type& dsum) { - // dsum.my_array[0] += fabs(iiter(b,n,k,j,i) - iout(b,n,k,j,i)); + // dsum.my_array[0] += m::abs(iiter(b,n,k,j,i) - iout(b,n,k,j,i)); // dsum.my_array[1] += iout(b,n,k,j,i); // }, array_sum::GlobalSum(res)); @@ -288,6 +288,6 @@ int Reductions::CountFlags(MeshData *md, std::string field_name, std::map< // TODO Print zone locations of bad inversions } - Flag("Counted"); + EndFlag(); return nflags; } diff --git a/kharma/types.hpp b/kharma/types.hpp index d0869a4a..f0a4e545 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -151,7 +151,7 @@ class VarMap { B2 = B1 + 1; B3 = B1 + 2; } - // TODO TODO track total nvar and provide a function + }; /** @@ -197,222 +197,73 @@ inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& : bounds.ke(IndexDomain::entire)}}; } +#if DEBUG /** - * Functions for "tracing" execution by printing strings (and optionally state of zones) - * at each important function entry/exit + * Function to generate outputs wherever, whenever. */ -#if TRACE -#define PRINTCORNERS 0 -#define PRINTZONE 0 -#define PRINTTILE 0 -#define iPRINT 7 -#define jPRINT 111 -#define kPRINT 0 -inline void PrintCorner(MeshBlockData *rc) +inline void OutputNow(Mesh *pmesh, std::string name) { - auto rhop = rc->Get("prims.rho").data.GetHostMirrorAndCopy(); - auto up = rc->Get("prims.u").data.GetHostMirrorAndCopy(); - auto uvecp = rc->Get("prims.uvec").data.GetHostMirrorAndCopy(); - auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); - auto rhoc = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); - auto uc = rc->Get("cons.u").data.GetHostMirrorAndCopy(); - auto uvecc = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); - auto Bu = rc->Get("cons.B").data.GetHostMirrorAndCopy(); - //auto p = rc->Get("p").data.GetHostMirrorAndCopy(); - auto pflag = rc->Get("pflag").data.GetHostMirrorAndCopy(); - //auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); - //auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); - const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); - std::cerr << "p:"; - for (int j=0; j<8; j++) { - std::cerr << std::endl; - for (int i=0; i<8; i++) { - fprintf(stderr, "%.5g\t", pflag(kb.s, j, i)); - } - } - // std::cerr << std::endl << "B1:"; - // for (int j=0; j<8; j++) { - // std::cerr << std::endl; - // for (int i=0; i<8; i++) { - // fprintf(stderr, "%.5g\t", Bu(V1, kb.s, j, i)); - // } - // } - std::cerr << std::endl << std::endl; -} - -inline void PrintZone(MeshBlockData *rc) -{ - auto rhop = rc->Get("prims.rho").data.GetHostMirrorAndCopy(); - auto up = rc->Get("prims.u").data.GetHostMirrorAndCopy(); - auto uvecp = rc->Get("prims.uvec").data.GetHostMirrorAndCopy(); - auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); - auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); - auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - - auto rhoU = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); - auto uU = rc->Get("cons.u").data.GetHostMirrorAndCopy(); - auto uvecU = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); - auto BU = rc->Get("cons.B").data.GetHostMirrorAndCopy(); - auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); - auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); - - std::cerr << "(PRIM) RHO: " << rhop(kPRINT,jPRINT,iPRINT) - << " UU: " << up(kPRINT,jPRINT,iPRINT) - << " U: " << uvecp(0,kPRINT,jPRINT,iPRINT) << " " << uvecp(1,kPRINT,jPRINT,iPRINT)<< " " << uvecp(2,kPRINT,jPRINT,iPRINT) - << " B: " << Bp(0,kPRINT,jPRINT,iPRINT) << " " << Bp(1,kPRINT,jPRINT,iPRINT) << " " << Bp(2,kPRINT,jPRINT,iPRINT) - << " q: " << q(kPRINT,jPRINT,iPRINT) - << " dP: " << dP(kPRINT,jPRINT,iPRINT) << std::endl; - std::cerr << "(CONS) RHO: " << rhoU(kPRINT,jPRINT,iPRINT) - << " UU: " << uU(kPRINT,jPRINT,iPRINT) - << " U: " << uvecU(0,kPRINT,jPRINT,iPRINT) << " " << uvecU(1,kPRINT,jPRINT,iPRINT)<< " " << uvecU(2,kPRINT,jPRINT,iPRINT) - << " B: " << BU(0,kPRINT,jPRINT,iPRINT) << " " << BU(1,kPRINT,jPRINT,iPRINT) << " " << BU(2,kPRINT,jPRINT,iPRINT) - << " q: " << qU(kPRINT,jPRINT,iPRINT) - << " dP: " << dPU(kPRINT,jPRINT,iPRINT) << std::endl; -} - -inline void PrintTile(MeshBlockData *rc) -{ - auto rhop = rc->Get("prims.rho").data.GetHostMirrorAndCopy(); - auto up = rc->Get("prims.u").data.GetHostMirrorAndCopy(); - auto uvecp = rc->Get("prims.uvec").data.GetHostMirrorAndCopy(); - auto Bp = rc->Get("prims.B").data.GetHostMirrorAndCopy(); - auto q = rc->Get("prims.q").data.GetHostMirrorAndCopy(); - auto dP = rc->Get("prims.dP").data.GetHostMirrorAndCopy(); - - auto rhoU = rc->Get("cons.rho").data.GetHostMirrorAndCopy(); - auto uU = rc->Get("cons.u").data.GetHostMirrorAndCopy(); - auto uvecU = rc->Get("cons.uvec").data.GetHostMirrorAndCopy(); - auto BU = rc->Get("cons.B").data.GetHostMirrorAndCopy(); - auto qU = rc->Get("cons.q").data.GetHostMirrorAndCopy(); - auto dPU = rc->Get("cons.dP").data.GetHostMirrorAndCopy(); - - const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); - const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); - std::cerr << "q(cons):"; - for (int j=jPRINT-3; j(pmesh, pin, &tm); + auto pin = pmesh->packages.Get("Globals")->Param("pin"); + pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); + // TODO: find most recently written "now" files and move them to "name" } +#endif +/** + * Functions for "tracing" execution by printing strings at each entry/exit. + * Normally, they profile the code, but they can print a nested execution trace. + * + * Don't laugh at my dumb mutex, it works. + */ +#if TRACE +// Can we namespace these? +extern int kharma_debug_trace_indent; +extern int kharma_debug_trace_mutex; +#define MAX_INDENT_SPACES 160 inline void Flag(std::string label) -{ - if(MPIRank0()) std::cerr << "Entering " << label << std::endl; -} - -inline void Flag(MeshBlockData *rc, std::string label) { if(MPIRank0()) { - std::cerr << "Entering " << label << std::endl; - if(PRINTCORNERS) PrintCorner(rc); - if(PRINTZONE) PrintZone(rc); - if(PRINTTILE) PrintTile(rc); + int& indent = kharma_debug_trace_indent; + int& mutex = kharma_debug_trace_mutex; + // If no other thread is printing one of these... + while (mutex != 0); + // ... take the mutex and print + mutex = 1; + char tab[MAX_INDENT_SPACES] = {0}; + // Make very sure the indent does not exceed the available space. + // Forgetting EndFlag() is easy and buffer overflows are bad. + indent = m::max(m::min(indent, MAX_INDENT_SPACES/2), 0); + for (int i=0; i < indent; i++) tab[i*2] = tab[i*2+1] = ' '; + // Print everything in one call so we have the best chance of coherence + fprintf(stderr, "%sStarting %s\n", tab, label.c_str()); + indent = m::min(indent++, MAX_INDENT_SPACES/2); + // Release mutex + mutex = 0; } } - -inline void Flag(MeshData *md, std::string label) -{ - if(MPIRank0()) { - std::cerr << "Entering " << label << std::endl; - if(PRINTCORNERS || PRINTZONE) { - auto rc = md->GetBlockData(0).get(); - if(PRINTCORNERS) PrintCorner(rc); - if(PRINTZONE) PrintZone(rc); - } - } -} - -inline void EndFlag() {} - -inline void EndFlag(std::string label) -{ - if(MPIRank0()) std::cerr << "Exiting " << label << std::endl; -} - -inline void EndFlag(MeshBlockData *rc, std::string label) -{ - if(MPIRank0()) { - std::cerr << "Exiting " << label << std::endl; - if(PRINTCORNERS) PrintCorner(rc); - if(PRINTZONE) PrintZone(rc); - } -} - -inline void EndFlag(MeshData *md, std::string label) +inline void EndFlag() { if(MPIRank0()) { - std::cerr << "Exiting " << label << std::endl; - if(PRINTCORNERS || PRINTZONE) { - auto rc = md->GetBlockData(0).get(); - if(PRINTCORNERS) PrintCorner(rc); - if(PRINTZONE) PrintZone(rc); - if(PRINTTILE) PrintTile(rc); - } + int& indent = kharma_debug_trace_indent; + int& mutex = kharma_debug_trace_mutex; + while (mutex != 0); + mutex = 1; + indent = m::min(m::max(indent--, 0), MAX_INDENT_SPACES/2); + char tab[MAX_INDENT_SPACES] = {0}; + for (int i=0; i < indent; i++) tab[i*2] = tab[i*2+1] = ' '; + fprintf(stderr, "%sDone\n", tab); + mutex = 0; } } - #else inline void Flag(std::string label) { Kokkos::Profiling::pushRegion(label); } -inline void Flag(MeshBlockData *rc, std::string label) -{ - Kokkos::Profiling::pushRegion(label); -} -inline void Flag(MeshData *md, std::string label) -{ - Kokkos::Profiling::pushRegion(label); -} inline void EndFlag() { Kokkos::Profiling::popRegion(); } -inline void EndFlag(std::string label) -{ - Kokkos::Profiling::popRegion(); -} -inline void EndFlag(MeshBlockData *rc, std::string label) -{ - Kokkos::Profiling::popRegion(); -} -inline void EndFlag(MeshData *md, std::string label) -{ - Kokkos::Profiling::popRegion(); -} #endif -/** - * Versions of Flag() that take shared_ptr objects and call through with get() - * Avoids having to pay attention to shared_ptr vs * pointers in adding Flag() calls - * when diagnosing a problem. - */ -inline void Flag(std::shared_ptr>& rc, std::string label) { Flag(rc.get(), label); } -inline void Flag(std::shared_ptr>& md, std::string label) { Flag(md.get(), label); } diff --git a/kharma/wind/wind.cpp b/kharma/wind/wind.cpp index 3c0483fe..1a560fa1 100644 --- a/kharma/wind/wind.cpp +++ b/kharma/wind/wind.cpp @@ -61,7 +61,6 @@ std::shared_ptr Wind::Initialize(ParameterInput *pin, std::shared TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) { - Flag(mdudt, "Adding wind"); // Pointers auto pmesh = mdudt->GetMeshPointer(); auto pmb0 = mdudt->GetBlockData(0)->GetBlockPointer(); @@ -101,7 +100,7 @@ TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) GReal r = Xembed[1], th = Xembed[2]; // Particle addition rate: concentrate at poles - Real drhopdt = current_n * m::pow(m::cos(th), power) / m::pow(1. + r * r, 2); + Real drhopdt = current_n * m::pow(m::cos(th), power) / SQR(1. + r * r); // Insert fluid moving in positive U1, without B field // Ramp up like density, since we're not at a set proportion @@ -121,6 +120,5 @@ TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) } ); - Flag(mdudt, "Added"); return TaskStatus::complete; } diff --git a/pars/anisotropic_conduction.par b/pars/anisotropic_conduction.par index 5562fae6..b255d68a 100644 --- a/pars/anisotropic_conduction.par +++ b/pars/anisotropic_conduction.par @@ -51,6 +51,7 @@ extra_checks = 1 on = true closure_type = constant +higher_order_terms = true tau = 0.1 conduction_alpha = 0.01 viscosity_alpha = 0.0 diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index 831a6613..e02506be 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -64,7 +64,7 @@ linesearch_eps = 1.e-4 on = true higher_order_terms = true feedback = true -stability_limits = true +stability_limits = false closure_type = kappa_eta tau = 10. kappa = 0.1 diff --git a/tests/anisotropic_conduction/make_plots.py b/tests/anisotropic_conduction/make_plots.py index 04699509..63a1dc90 100644 --- a/tests/anisotropic_conduction/make_plots.py +++ b/tests/anisotropic_conduction/make_plots.py @@ -1,7 +1,10 @@ # PLOT SNAKE TEST +import os +import sys +import h5py, psutil, glob + import numpy as np -import os, h5py, psutil, glob import multiprocessing as mp import matplotlib matplotlib.use('Agg') @@ -99,14 +102,14 @@ def plot(dumpno): if __name__=='__main__': - params['dumpsdir'] = './dumps_kharma' + params['dumpsdir'] = sys.argv[1] params['dfirst'] = 0 params['dlast'] = int(sorted(glob.glob(os.path.join(params['dumpsdir'], 'anisotropic_conduction.out0.0*phdf')))[-1][-9:-5]) dlist = range(params['dfirst'], params['dlast']+1) - params['plotsdir'] = './plots' + params['plotsdir'] = '.' if not os.path.exists(params['plotsdir']): os.makedirs(params['plotsdir']) nthreads = calc_threads() - run_parallel(plot, dlist, nthreads) \ No newline at end of file + run_parallel(plot, dlist, nthreads) diff --git a/tests/anisotropic_conduction/run.sh b/tests/anisotropic_conduction/run.sh new file mode 100755 index 00000000..8e641822 --- /dev/null +++ b/tests/anisotropic_conduction/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +../../run.sh -i ../../pars/anisotropic_conduction.par + +python make_plots.py . From b14a1220db9b0177147ffaac806f9f99a2f1589b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 18 May 2023 10:52:54 -0500 Subject: [PATCH 077/219] Fix reconstruction & flags after merge --- kharma/flux/get_flux.hpp | 11 ++++++++--- kharma/reconstruction.hpp | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 783c78e1..d1929b49 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -140,6 +140,7 @@ inline TaskStatus GetFlux(MeshData *md) // This isn't a pmb0->par_for_outer because Parthenon's current overloaded definitions // do not accept three pairs of bounds, which we need in order to iterate over blocks + Flag("GetFlux_"+std::to_string(dir)+"_recon"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_recon", pmb0->exec_space, recon_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { @@ -180,8 +181,9 @@ inline TaskStatus GetFlux(MeshData *md) } ); + EndFlag(); - Flag(md, "PtoU Left"); + Flag("GetFlux_"+std::to_string(dir)+"_left"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_left", pmb0->exec_space, flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { @@ -236,8 +238,9 @@ inline TaskStatus GetFlux(MeshData *md) } } ); + EndFlag(); - Flag(md, "PtoU Right"); + Flag("GetFlux_"+std::to_string(dir)+"_right"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_right", pmb0->exec_space, flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { @@ -292,8 +295,9 @@ inline TaskStatus GetFlux(MeshData *md) } ); + EndFlag(); - Flag(md, "Riemann kernel"); + Flag("GetFlux_"+std::to_string(dir)+"_riemann"); pmb0->par_for("flux_solve", block.s, block.e, 0, nvar-1, kl.s, kl.e, jl.s, jl.e, il.s, il.e, KOKKOS_LAMBDA(const int& b, const int& p, const int& k, const int& j, const int& i) { // Apply what we've calculated @@ -305,6 +309,7 @@ inline TaskStatus GetFlux(MeshData *md) } ); + EndFlag(); EndFlag(); return TaskStatus::complete; diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 8e29d567..47d74f09 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -359,7 +359,7 @@ KOKKOS_INLINE_FUNCTION void WENO5X3r(parthenon::team_mbr_t const &member, const * This is basically a compile-time 'if' or 'switch' statement, where all the options get generated * at compile-time (see driver.cpp for the different instantiations) * - * We could template these directly on the function if Partheconst GRCoordinates& G, non could agree on what argument list to use + * We could template these directly on the function if Parthenon could agree on what argument list to use * Better than a runtime decision per outer loop I think */ template @@ -499,7 +499,7 @@ KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_ // Linear X1 reconstruction near X1 boundaries template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -512,33 +512,33 @@ KOKKOS_INLINE_FUNCTION void reconstruct(partheno } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { - reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); + reconstruct(member, P, k, j, is_l, ie_l, ql, qr); } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { - reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); + reconstruct(member, P, k, j, is_l, ie_l, ql, qr); } // WENO5 lowered poles: // Linear X2 reconstruction near X2 boundaries template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { - reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); + reconstruct(member, P, k, j, is_l, ie_l, ql, qr); } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { @@ -556,11 +556,11 @@ KOKKOS_INLINE_FUNCTION void reconstruct(partheno } template <> KOKKOS_INLINE_FUNCTION void reconstruct(parthenon::team_mbr_t& member, - const GRCoordinates& G, const VariablePack &P, + const VariablePack &P, const int& k, const int& j, const int& is_l, const int& ie_l, ScratchPad2D ql, ScratchPad2D qr) { - reconstruct(member, G, P, k, j, is_l, ie_l, ql, qr); + reconstruct(member, P, k, j, is_l, ie_l, ql, qr); } /** From f7ab6b5e82679be4e40a6fb68b95b4c40466023c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 18 May 2023 20:07:40 -0500 Subject: [PATCH 078/219] Handle This reverts to syncing & bounding primitive q,dP, and allows packages to choose whether KHARMA calls UtoP or PtoU after sync. q in conducting atmo grows too large on domain, outpaces Dirichlet bounds. All prims in viscous Bondi converge at -1 (related to using r_in rather than Rhor?) Also fixes/improves new indented trace output --- kharma/b_cleanup/b_cleanup.cpp | 4 +- kharma/b_flux_ct/b_flux_ct.cpp | 2 + kharma/boundaries/boundaries.cpp | 7 +- kharma/boundaries/dirichlet.cpp | 11 ++- kharma/coordinates/coordinate_embedding.hpp | 2 +- kharma/driver/imex_step.cpp | 18 ++--- kharma/driver/kharma_driver.cpp | 5 +- kharma/driver/kharma_step.cpp | 8 ++- kharma/emhd/emhd.cpp | 80 +++++++++++++++------ kharma/emhd/emhd.hpp | 8 ++- kharma/emhd/emhd_utils.hpp | 29 ++------ kharma/flux/flux.cpp | 2 - kharma/grmhd/grmhd.cpp | 3 + kharma/implicit/fixup.cpp | 4 +- kharma/kharma.cpp | 2 +- kharma/kharma_package.cpp | 33 ++++----- kharma/kharma_package.hpp | 23 +++--- kharma/prob/bondi.cpp | 2 +- kharma/prob/emhd/conducting_atmosphere.cpp | 5 +- kharma/prob/post_initialize.cpp | 2 +- kharma/reconstruction.hpp | 12 ++-- kharma/types.hpp | 8 +-- pars/bondi_viscous.par | 19 ++--- pars/conducting_atmosphere.par | 9 +-- tests/conducting_atmosphere/run.sh | 4 +- 25 files changed, 178 insertions(+), 124 deletions(-) diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 4ecc47f7..16baa1db 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -272,8 +272,8 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) // Synchronize to update ghost zones KHARMADriver::SyncAllBounds(md); - // Make sure all primitive vars reflect the solution - Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + // Make sure primitive B reflects solution + B_FluxCT::MeshUtoP(md.get(), IndexDomain::entire, false); // Recalculate divB max for one last check const double divb_end = B_FluxCT::GlobalMaxDivB(md.get()); diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index cbdf1f07..b52ecfdc 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -129,6 +129,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

MeshUtoP = B_FluxCT::MeshUtoP; pkg->BlockUtoP = B_FluxCT::BlockUtoP; } + // Still need UtoP on boundaries + pkg->BoundaryUtoP = B_FluxCT::BlockUtoP; // Register the other callbacks pkg->PostStepDiagnosticsMesh = B_FluxCT::PostStepDiagnostics; diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 6c56fc3b..52f9eff8 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -245,10 +245,11 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD EndFlag(); } - // Respect the fluid primitives on boundaries (*not* B) - Flux::BlockPtoUMHD(rc.get(), domain, coarse); + // Respect the fluid primitives on boundaries (does not include B) + // Also currently the EMHD extra variables q, dP + Packages::BoundaryPtoU(rc.get(), domain, coarse); // For everything else, respect conserved variables - Packages::BlockUtoPExceptMHD(rc.get(), domain, coarse); + Packages::BoundaryUtoP(rc.get(), domain, coarse); EndFlag(); } diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 04890b52..56858b03 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -48,12 +48,15 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda FC main_ghosts = pmb->packages.AllPackages().count("B_Cleanup") ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) : FC({Metadata::FillGhost}); - auto q = rc->PackVariables(main_ghosts, coarse); + PackIndexMap ghostmap; + auto q = rc->PackVariables(main_ghosts, ghostmap, coarse); + const int q_index = ghostmap["prims.q"].first; auto bound = rc->Get("bounds." + BoundaryName(bface)).data; - // TODO TODO NAMES if (q.GetDim(4) != bound.GetDim(4)) { std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + std::cerr << "Variables with ghost zones:" << std::endl; + ghostmap.print(); } const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; @@ -68,6 +71,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda const auto &G = pmb->coords; + // printf("Freezing bounds:\n"); const auto domain = BoundaryDomain(bface); pmb->par_for_bndry( "dirichlet_boundary", vars, domain, coarse, @@ -77,8 +81,11 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda } else { q(p, k, j, i) = bound(p, k, j, i); } + // if (p == q_index) printf("%g ", q(p, k, j, i)); } ); + // Kokkos::fence(); + // printf("\n\n"); } void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index 935d75a3..b3ae341b 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -75,7 +75,7 @@ * * Each possible class is added to a couple of mpark::variant containers, and then to the chains of if statements below. * - * TODO convenience functions. Intelligent r/th/phi, x/y/z, KS and BL, a, rhor, etc by auto-translating contents + * TODO convenience functions. Intelligent r/th/phi, x/y/z, KS and BL, a, etc by auto-translating contents */ class CoordinateEmbedding { public: diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index db724e8b..e614ab83 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -185,7 +185,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta } // Make sure the primitive values of *explicitly-evolved* variables are updated. - // Each package should have a guard which makes UtoP a no-op if it's implicitly evolved + // Packages with implicitly-evolved vars should only register BoundaryUtoP or BoundaryPtoU auto t_explicit_UtoP = tl.AddTask(t_copy_prims, Packages::MeshUtoP, md_solver.get(), IndexDomain::interior, false); // Done with explicit update @@ -243,17 +243,19 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta auto &mbd_sub_step_init = pmb->meshblock_data.Get(integrator->stage_name[stage-1]); auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); - // If we're evolving the GRMHD variables explicitly, we need to fix UtoP variable inversion failures + // If we're evolving the GRMHD variables explicitly, we need to fix UtoP variable inversion failures. + // If implicitly, we run a (very similar) fix for solver failures. // Syncing bounds before calling this, and then running it over the whole domain, will make // behavior for different mesh breakdowns much more similar (identical?), since bad zones in // relevant ghost zone ranks will get to use all the same neighbors as if they were in the bulk - auto t_fix_p = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); - - // Fix unconverged (bad) zones in the solver // TODO fixups as a callback? - auto t_fix_solve = t_fix_p; - if (pkgs.at("GRMHD")->Param("implicit")) { - t_fix_solve = tl.AddTask(t_fix_p, Implicit::FixSolve, mbd_sub_step_final.get()); + auto t_fix_utop = t_none; + if (!pkgs.at("GRMHD")->Param("implicit")) { + t_fix_utop = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); + } + auto t_fix_solve = t_fix_utop; + if (use_implicit) { + t_fix_solve = tl.AddTask(t_fix_utop, Implicit::FixSolve, mbd_sub_step_final.get()); } auto t_set_bc = tl.AddTask(t_fix_solve, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 9081659b..c6ed1a4f 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -140,6 +140,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: { auto t_start_sync = t_start; + // TODO this is likely part of syncing cons of e.g. implicit vars, etc. if (0) { //(mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_ptou_final(0); @@ -165,7 +166,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: // TODO(BSP) careful about how AMR interacts with below Kokkos::fence(); - // If we're "syncing primitive variables" but just exchanged cons.B, we need to recover the prims + // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims if (mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); @@ -176,7 +177,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { const auto bdomain = KBoundaries::BoundaryDomain((BoundaryFace) i_bnd); - t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BlockUtoPExceptMHD, rc.get(), bdomain, false); + t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BoundaryUtoP, rc.get(), bdomain, false); t_utop_final = t_utop_final | t_all_utop[i_task]; i_task++; } diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 38c669b9..8e990ad4 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -56,14 +56,16 @@ TaskCollection KHARMADriver::MakeTaskCollection(BlockList_t &blocks, int stage) { std::string driver_type = blocks[0]->packages.Get("Driver")->Param("type"); Flag("MakeTaskCollection_"+driver_type); + TaskCollection tc; if (driver_type == "imex") { - return MakeImExTaskCollection(blocks, stage); + tc = MakeImExTaskCollection(blocks, stage); } else if (driver_type == "simple") { - return MakeSimpleTaskCollection(blocks, stage); + tc = MakeSimpleTaskCollection(blocks, stage); } else { - return MakeDefaultTaskCollection(blocks, stage); + tc = MakeDefaultTaskCollection(blocks, stage); } EndFlag(); + return tc; } TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int stage) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 793fba13..34153ce0 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -125,11 +125,12 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

BlockUtoP = EMHD::BlockUtoP; + // UtoP is *only* for boundary syncs and output, only register that function + // TODO support syncing cons someday + //pkg->BoundaryUtoP = EMHD::BlockUtoP; + + // For now, sync primitive variables & call PtoU on physical boundaries + pkg->BoundaryPtoU = EMHD::BlockPtoU; // Add all explicit source terms -- implicit terms are called from Implicit::Step pkg->AddSource = EMHD::AddSource; @@ -172,7 +177,41 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc, IndexDomain domain, bool coarse) +// TODO is relying on GRMHD P variables a mistake here? They're available on physical boundaries at least, +// maybe not internal? +// void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +// { +// auto pmb = rc->GetBlockPointer(); + +// PackIndexMap prims_map, cons_map; +// auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); +// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); +// const VarMap m_p(prims_map, false), m_u(cons_map, true); + +// const auto& G = pmb->coords; + +// auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; +// const IndexRange ib = bounds.GetBoundsI(domain); +// const IndexRange jb = bounds.GetBoundsJ(domain); +// const IndexRange kb = bounds.GetBoundsK(domain); + +// pmb->par_for("UtoP_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, +// KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { +// const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, Loci::center); +// const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); +// const Real ucon0 = gamma * inv_alpha; + +// // Update the primitive EMHD fields +// if (m_p.Q >= 0) +// P(m_p.Q, k, j, i) = U_E(m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); +// if (m_p.DP >= 0) +// P(m_p.DP, k, j, i) = U_E(m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); +// } +// ); +// Kokkos::fence(); +// } + +void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); @@ -194,14 +233,13 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); const Real ucon0 = gamma * inv_alpha; - // Update the primitive EMHD fields + // Update the conserved EMHD fields if (m_p.Q >= 0) - P(m_p.Q, k, j, i) = U_E(m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + U_E(m_u.Q, k, j, i) = P(m_p.Q, k, j, i) * ucon0 * G.gdet(Loci::center, j, i); if (m_p.DP >= 0) - P(m_p.DP, k, j, i) = U_E(m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + U_E(m_u.DP, k, j, i) = P(m_p.DP, k, j, i) * ucon0 * G.gdet(Loci::center, j, i); } ); - Kokkos::fence(); } void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin) @@ -223,11 +261,11 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) const EMHD_parameters& emhd_params = pars.Get("emhd_params"); // Pack variables - PackIndexMap prims_map, cons_map; + PackIndexMap prims_map, cons_map, source_map; auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); auto U = md->PackVariables(std::vector{Metadata::Conserved}, cons_map); - auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}); - const VarMap m_p(prims_map, false), m_u(cons_map, true); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, source_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true), m_s(source_map, true); // Get temporary ucov, Theta for gradients PackIndexMap temps_map; @@ -272,7 +310,6 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) FourVectors D; GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, D); const double bsq = m::max(dot(D.bcon, D.bcov), SMALL); - const double mag_b = m::sqrt(bsq); // Compute gradient of ucov and Theta Real grad_ucov[GR_DIM][GR_DIM], grad_Theta[GR_DIM]; @@ -285,21 +322,22 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Compute+add explicit source terms (conduction and viscosity) const Real& rho = P(b)(m_p.RHO, k, j, i); - const Real& Theta = Temps(b)(m_theta, k, j, i); + const Real& Theta = Temps(b)(m_theta, k, j, i); if (emhd_params.conduction) { const Real& qtilde = P(b)(m_p.Q, k, j, i); + const double inv_mag_b = 1. / m::sqrt(bsq); Real q0 = 0; - DLOOP1 q0 -= rho * chi_e * (D.bcon[mu] / mag_b) * grad_Theta[mu]; - DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] / mag_b) * Theta * D.ucon[nu] * grad_ucov[nu][mu]; + DLOOP1 q0 -= rho * chi_e * (D.bcon[mu] * inv_mag_b) * grad_Theta[mu]; + DLOOP2 q0 -= rho * chi_e * (D.bcon[mu] * inv_mag_b) * Theta * D.ucon[nu] * grad_ucov[nu][mu]; Real q0_tilde = q0; if (emhd_params.higher_order_terms) q0_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho * Theta * Theta)) : 0.0; - dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; + dUdt(b, m_s.Q, k, j, i) += G.gdet(Loci::center, j, i) * q0_tilde / tau; if (emhd_params.higher_order_terms) - dUdt(b, m_u.Q, k, j, i) += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; + dUdt(b, m_s.Q, k, j, i) += G.gdet(Loci::center, j, i) * (qtilde / 2.) * div_ucon; } if (emhd_params.viscosity) { @@ -310,9 +348,9 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) if (emhd_params.higher_order_terms) dP0_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho * Theta)) : 0.0; - dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * dP0_tilde / tau; + dUdt(b, m_s.DP, k, j, i) += G.gdet(Loci::center, j, i) * dP0_tilde / tau; if (emhd_params.higher_order_terms) - dUdt(b, m_u.DP, k, j, i) += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; + dUdt(b, m_s.DP, k, j, i) += G.gdet(Loci::center, j, i) * (dPtilde / 2.) * div_ucon; } } ); diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index da320a9a..63a12bb6 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -88,11 +88,13 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt); void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput *pin); /** - * Recover primitive qtilde, dPtilde from "conserved" forms {qtilde,dPtilde}*u^0*gdet. - * Since the implicit step does this for us, this is only needed for boundaries, - * which sync/set conserved forms. + * Recover primitive qtilde, dPtilde from "conserved" forms {qtilde,dPtilde}*u^0*gdet, + * and vice versa. + * These are *not* called in the usual places for explicitly-evolved variables, but instead + * only on boundaries in order to sync the primitive/conserved variables specifically. */ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse); +void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse); /** * Get the EMHD parameters needed on the device side. diff --git a/kharma/emhd/emhd_utils.hpp b/kharma/emhd/emhd_utils.hpp index deb42a73..1d239f12 100644 --- a/kharma/emhd/emhd_utils.hpp +++ b/kharma/emhd/emhd_utils.hpp @@ -62,19 +62,10 @@ KOKKOS_INLINE_FUNCTION void gradient_calc(const GRCoordinates& G, const Variable // Compute gradient of ucov DLOOP1 { grad_ucov[0][mu] = 0; - // slope in direction nu of component mu - grad_ucov[1][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); - if (do_2d) { - grad_ucov[2][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); - } else { - grad_ucov[2][mu] = 0.; - } - if (do_3d) { - grad_ucov[3][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); - } else { - grad_ucov[3][mu] = 0.; - } + grad_ucov[1][mu] = slope_calc(G, Temps, uvec_index + mu, k, j, i); + grad_ucov[2][mu] = (do_2d) ? slope_calc(G, Temps, uvec_index + mu, k, j, i) : 0.; + grad_ucov[3][mu] = (do_3d) ? slope_calc(G, Temps, uvec_index + mu, k, j, i) : 0.; } // TODO skip this if flat space? DLOOP3 grad_ucov[mu][nu] -= G.conn(j, i, lam, mu, nu) * Temps(uvec_index + lam, k, j, i); @@ -82,17 +73,9 @@ KOKKOS_INLINE_FUNCTION void gradient_calc(const GRCoordinates& G, const Variable // Compute temperature gradient // Time derivative component is computed in time_derivative_sources grad_Theta[0] = 0; - grad_Theta[1] = slope_calc(G, Temps, theta_index, k, j, i); - if (do_2d) { - grad_Theta[2] = slope_calc(G, Temps, theta_index, k, j, i); - } else { - grad_Theta[2] = 0.; - } - if (do_3d) { - grad_Theta[3] = slope_calc(G, Temps, theta_index, k, j, i); - } else { - grad_Theta[3] = 0.; - } + grad_Theta[1] = slope_calc(G, Temps, theta_index, k, j, i); + grad_Theta[2] = (do_2d) ? slope_calc(G, Temps, theta_index, k, j, i) : 0.; + grad_Theta[3] = (do_3d) ? slope_calc(G, Temps, theta_index, k, j, i) : 0.; } } // namespace EMHD diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 8905182a..3e038e3d 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -43,7 +43,6 @@ using namespace parthenon; TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag("Flux::BlockPtoUMHD"); // Pointers auto pmb = rc->GetBlockPointer(); // Options @@ -71,7 +70,6 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool } ); - EndFlag(); return TaskStatus::complete; } diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index b6944354..23517949 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -197,6 +197,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddSource = Flux::AddGeoSource; + // On physical boundaries, even if we've sync'd both, respect the application to primitive variables + pkg->BoundaryPtoU = Flux::BlockPtoUMHD; + // Finally, the StateDescriptor/Package object determines the Callbacks Parthenon makes to // a particular package -- that is, some portion of the things that the package needs done // at each step, which must be done at specific times. diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index f6d92a7e..fbac7845 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -54,9 +54,9 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { GridScalar solve_fail = mbd->Get("solve_fail").data; + // TODO generalize & make this into FixUtoP also? + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - // TODO flag_verbose here. Merge with other fixup into separate package or in GRMHD? - // We'll want to try new in-depth fixes w/implicit as we go... const int flag_verbose = pmb->packages.Get("Globals")->Param("flag_verbose"); // Boundaries were synced just before the call to this function (cf. imex_driver.cpp). diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 2f0cec53..cbd4d949 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -202,7 +202,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) throw std::invalid_argument("Not enough radial zones were specified to put 5 zones inside EH!"); } pin->GetOrAddReal("parthenon/mesh", "x1min", x1min); - pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(Rhor)); + pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(x1min)); } } } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 967c0a20..508a3a81 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -77,31 +77,32 @@ TaskStatus Packages::MeshUtoP(MeshData *md, IndexDomain domain, bool coars return TaskStatus::complete; } -TaskStatus Packages::BlockUtoPExceptMHD(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus Packages::BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag("BlockUtoPExceptMHD"); - // We need to re-fill the primitive variables on the physical boundaries, - // since the driver has already called UtoP for the step. - // However, this does *not* apply to the GRMHD variables, as the boundary call - // used/filled their primitive values. Instead, they will need a PtoU call + Flag("BoundaryUtoP"); auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage.first != "GRMHD" && kpackage.first != "Inverter") { - if (kpackage.second->BlockUtoP != nullptr) { - Flag("BlockUtoPExceptMHD_"+kpackage.first); - kpackage.second->BlockUtoP(rc, domain, coarse); - EndFlag(); - } + if (kpackage.second->BoundaryUtoP != nullptr) { + Flag("BoundaryUtoP_"+kpackage.first); + kpackage.second->BoundaryUtoP(rc, domain, coarse); + EndFlag(); } } EndFlag(); return TaskStatus::complete; } -TaskStatus Packages::MeshUtoPExceptMHD(MeshData *md, IndexDomain domain, bool coarse) + +TaskStatus Packages::BoundaryPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag("MeshUtoPExceptMHD"); - for (int i=0; i < md->NumBlocks(); ++i) - BlockUtoPExceptMHD(md->GetBlockData(i).get(), domain, coarse); + Flag("BoundaryPtoU"); + auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->BoundaryPtoU != nullptr) { + Flag("BoundaryPtoU_"+kpackage.first); + kpackage.second->BoundaryPtoU(rc, domain, coarse); + EndFlag(); + } + } EndFlag(); return TaskStatus::complete; } diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index 1d55e585..4ddaf79a 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -59,6 +59,12 @@ class KHARMAPackage : public StateDescriptor { // rather, they are called on zone center values once per step only. std::function*, IndexDomain, bool)> BlockUtoP = nullptr; std::function*, IndexDomain, bool)> MeshUtoP = nullptr; + // Allow applying UtoP only/separately for physical boundary domains after sync/prolong/restrict + // e.g., GRMHD does *not* register this as boundaries are applied to prims, + // whereas implicitly-evolved vars *only* register this. + std::function*, IndexDomain, bool)> BoundaryUtoP = nullptr; + // Same thing, the other way. For packages syncing primitives, e.g. GRMHD + std::function*, IndexDomain, bool)> BoundaryPtoU = nullptr; // Going the other way, however, is handled by Flux::PtoU. // All PtoU implementations are device-side (called prim_to_flux) @@ -110,20 +116,21 @@ namespace Packages { TaskStatus FixFlux(MeshData *md); /** - * + * Fill the primitive variables P using the conserved U */ TaskStatus BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** - * Fill the primitive variables P using the conserved U, for every package except "GRMHD." - * That is, currently, B fields and electrons. - * This is used for KHARMA's boundaries, which act on (e.g., reflect or outflow) the - * conserved variables where available (and thus must recover primitives), - * but act on primitive rho,u,uvec and must leave those alone. + * Version of UtoP specifically for boundaries. Some packages sync & apply boundaries to + * conserved variables, some to primitive variables. + */ +TaskStatus BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); +/** + * P to U for boundaries. As it's internal to the flux updates, the "normal" PtoU is + * implemented device-side and called from the "Flux" package */ -TaskStatus BlockUtoPExceptMHD(MeshBlockData *rc, IndexDomain domain, bool coarse=false); -TaskStatus MeshUtoPExceptMHD(MeshData *md, IndexDomain domain, bool coarse=false); +TaskStatus BoundaryPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse=false); /** * Fill all conserved variables (U) from primitive variables (P), over a whole block diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 180258ab..cbd52208 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -65,7 +65,7 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn const bool zero_velocity = pin->GetOrAddBoolean("bondi", "zero_velocity", false); // Add these to package properties, since they continue to be needed on boundaries - // TODO Problems need params + // TODO Problems NEED params if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("mdot")) pmb->packages.Get("GRMHD")->AddParam("mdot", mdot); if(! pmb->packages.Get("GRMHD")->AllParams().hasKey("rs")) diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index bbfaff0b..50f068f4 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -230,8 +230,11 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame dP.DeepCopy(dP_host); Kokkos::fence(); - // Also fill cons.B + // Also fill cons B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); + EMHD::BlockPtoU(rc.get(), IndexDomain::entire, false); + // Freeze the boundaries as soon as we have everything in place + KBoundaries::FreezeDirichletBlock(rc.get()); return TaskStatus::complete; diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 43ee9076..79c85b76 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -277,5 +277,5 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // This is the first sync if there is no B field KHARMADriver::SyncAllBounds(md); // And make sure the trivial primitive values are up-to-date - Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + //Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); } diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index 232bf833..a3289ca6 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -616,37 +616,37 @@ template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc<1>(i)); + return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc(i)); } template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc<2>(j)); + return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc(j)); } template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc<3>(k)); + return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc(k)); } template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc<1>(i)); + return slope_limit(P(p, k, j, i-1), P(p, k, j, i), P(p, k, j, i+1), G.Dxc(i)); } template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc<2>(j)); + return slope_limit(P(p, k, j-1, i), P(p, k, j, i), P(p, k, j+1, i), G.Dxc(j)); } template <> KOKKOS_INLINE_FUNCTION Real slope_calc(const GRCoordinates& G, const VariablePack& P, const int& p, const int& k, const int& j, const int& i) { - return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc<3>(k)); + return slope_limit(P(p, k-1, j, i), P(p, k, j, i), P(p, k+1, j, i), G.Dxc(k)); } } // namespace KReconstruction diff --git a/kharma/types.hpp b/kharma/types.hpp index f0a4e545..3ff8fe8b 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -221,7 +221,7 @@ inline void OutputNow(Mesh *pmesh, std::string name) // Can we namespace these? extern int kharma_debug_trace_indent; extern int kharma_debug_trace_mutex; -#define MAX_INDENT_SPACES 160 +#define MAX_INDENT_SPACES 80 inline void Flag(std::string label) { if(MPIRank0()) { @@ -231,14 +231,14 @@ inline void Flag(std::string label) while (mutex != 0); // ... take the mutex and print mutex = 1; - char tab[MAX_INDENT_SPACES] = {0}; // Make very sure the indent does not exceed the available space. // Forgetting EndFlag() is easy and buffer overflows are bad. indent = m::max(m::min(indent, MAX_INDENT_SPACES/2), 0); + char tab[MAX_INDENT_SPACES] = {0}; for (int i=0; i < indent; i++) tab[i*2] = tab[i*2+1] = ' '; // Print everything in one call so we have the best chance of coherence fprintf(stderr, "%sStarting %s\n", tab, label.c_str()); - indent = m::min(indent++, MAX_INDENT_SPACES/2); + indent = m::min(indent+1, MAX_INDENT_SPACES/2); // Release mutex mutex = 0; } @@ -250,7 +250,7 @@ inline void EndFlag() int& mutex = kharma_debug_trace_mutex; while (mutex != 0); mutex = 1; - indent = m::min(m::max(indent--, 0), MAX_INDENT_SPACES/2); + indent = m::min(m::max(indent-1, 0), MAX_INDENT_SPACES/2); char tab[MAX_INDENT_SPACES] = {0}; for (int i=0; i < indent; i++) tab[i*2] = tab[i*2+1] = ' '; fprintf(stderr, "%sDone\n", tab); diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index e2ce9389..517883c8 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -22,7 +22,7 @@ transform = mks a = 0.0 hslope = 1.0 r_out = 20 -Rhor = 3 +r_in = 3 tlim = 400.0 @@ -53,14 +53,15 @@ use_qr = true # IMPORTANT: This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = true -feedback = false -stability_limits = true -conduction = false -viscosity = true +feedback = false +stability_limits = false +conduction = false +viscosity = true + closure_type = kappa_eta -tau = 30. +tau = 30. eta = 0.01 @@ -71,11 +72,13 @@ rs = 8.0 disable_floors = true +outer_x1 = dirichlet check_inflow_outer_x1 = false verbose = 1 -flag_verbose = 2 +flag_verbose = 0 +extra_checks = 1 file_type = hdf5 diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index e02506be..3eb29ca9 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -63,12 +63,13 @@ linesearch_eps = 1.e-4 on = true higher_order_terms = true -feedback = true -stability_limits = false +feedback = true +stability_limits = false + closure_type = kappa_eta -tau = 10. +tau = 10. kappa = 0.1 -eta = 0.0 +eta = 0.0 input = ODE diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 10e0b7b2..ae70088b 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -33,6 +33,6 @@ conv_2d() { fi } -#ALL_RES="64,128,256,512" -ALL_RES="64,128" +ALL_RES="64,128,256,512" +#ALL_RES="64,128" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" From 1d8f6ce100f87ef61f86512fcf4e34bbbbb70916 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 18 May 2023 22:53:15 -0500 Subject: [PATCH 079/219] Fixes, EMHD calls changes Fixes an issue with torus closure in emhd, as well as fixing the viscous Bondi problem domain, and sets drift frame floors default. Also unifies EMHD set_parameters as a prototype for doing so in more device-side functions. --- kharma/emhd/emhd.hpp | 214 +++++---------------- kharma/floors/floors.cpp | 2 +- kharma/prob/emhd/conducting_atmosphere.cpp | 3 +- kharma/prob/emhd/emhdmodes.hpp | 3 +- kharma/prob/emhd/emhdshock.hpp | 3 +- pars/bondi_viscous.par | 4 +- 6 files changed, 60 insertions(+), 169 deletions(-) diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 63a12bb6..d27e3b41 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -113,15 +113,14 @@ inline EMHD_parameters GetEMHDParameters(Packages_t& packages) /** * Set chi, nu, tau. Problem dependent */ -template -KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, - const EMHD_parameters& emhd_params, const Real& gam, - const int& j, const int& i, - Real& tau, Real& chi_e, Real& nu_e) +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Real& rho, const Real& u, + const Real& qtilde, const Real& dPtilde, const Real& bsq, + const EMHD_parameters& emhd_params, const Real& gam, + const int& j, const int& i, + Real& tau, Real& chi_e, Real& nu_e) { if (emhd_params.type == ClosureType::constant) { // Set tau, nu, chi to constants - tau = emhd_params.tau; if (emhd_params.conduction) chi_e = emhd_params.conduction_alpha; @@ -130,72 +129,65 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& } else if (emhd_params.type == ClosureType::soundspeed) { // Set tau=const, chi/nu prop. to sound speed squared - Real cs2 = (gam * (gam - 1.) * P(m_p.UU)) / (P(m_p.RHO) + (gam * P(m_p.UU))); - + const Real cs2 = (gam * (gam - 1.) * u) / (rho + (gam * u)); tau = emhd_params.tau; if (emhd_params.conduction) chi_e = emhd_params.conduction_alpha * cs2 * tau; if (emhd_params.viscosity) nu_e = emhd_params.viscosity_alpha * cs2 * tau; - } else if (emhd_params.type == ClosureType::kappa_eta) { + } else if (emhd_params.type == ClosureType::kappa_eta){ // Set tau = const, chi = kappa / rho, nu = eta / rho - tau = emhd_params.tau; if (emhd_params.conduction) - chi_e = emhd_params.kappa / m::max(P(m_p.RHO), SMALL); + chi_e = emhd_params.kappa / m::max(rho, SMALL); if (emhd_params.viscosity) - nu_e = emhd_params.eta / m::max(P(m_p.RHO), SMALL); + nu_e = emhd_params.eta / m::max(rho, SMALL); } else if (emhd_params.type == ClosureType::torus) { - FourVectors Dtmp; - GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); - // TODO need this max() if we're correcting later? - double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); - GReal Xembed[GR_DIM]; G.coord_embed(0, j, i, Loci::center, Xembed); const GReal r = Xembed[1]; // Compute dynamical time scale const Real tau_dyn = m::sqrt(r*r*r); + tau = tau_dyn; - const Real pg = (gam - 1.) * P(m_p.UU); - const Real Theta = pg / P(m_p.RHO); - // Compute local sound speed - const Real cs2 = gam * pg / (P(m_p.RHO) + (gam * P(m_p.UU))); + const Real pg = (gam - 1.) * u; + const Real Theta = pg / rho; + // Compute local sound speed, ensure it is defined and >0 + // Passing NaN disables an upper bound (TODO should we have one?) + const Real cs2 = clip(gam * pg / (rho + (gam * u)), SMALL, 0./0.); - Real lambda = 0.01; - Real inv_exp_g = 0.; - Real f_fmin = 0.; + constexpr Real lambda = 0.01; // Correction due to heat conduction if (emhd_params.conduction) { - Real q = P(m_p.Q); - if (emhd_params.higher_order_terms) - q *= m::sqrt(P(m_p.RHO) * emhd_params.conduction_alpha * cs2 * Theta * Theta); - Real q_max = emhd_params.conduction_alpha * P(m_p.RHO) * cs2 * m::sqrt(cs2); - Real q_ratio = m::abs(q) / q_max; - inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + const Real q = (emhd_params.higher_order_terms) + ? qtilde * m::sqrt(rho * emhd_params.conduction_alpha * cs2 * Theta * Theta) + : qtilde; + const Real q_max = emhd_params.conduction_alpha * rho * cs2 * m::sqrt(cs2); + const Real q_ratio = m::abs(q) / q_max; + const Real inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); + const Real f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; tau = m::min(tau, f_fmin * tau_dyn); } // Correction due to pressure anisotropy if (emhd_params.viscosity) { - Real dP = P(m_p.DP); - if (emhd_params.higher_order_terms) - dP *= sqrt(P(m_p.RHO) * emhd_params.viscosity_alpha * cs2 * Theta); - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); - Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - - Real dP_max = (dP > 0.) ? dP_plus : dP_minus; - - Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); - inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; + const Real dP = (emhd_params.higher_order_terms) + ? dPtilde * sqrt(rho * emhd_params.viscosity_alpha * cs2 * Theta) + : dPtilde; + const Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / + m::max(pg + 1./3. * dP, SMALL); + const Real dP_max = (dP > 0.) + ? m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07) + : m::max(-bsq, -2.99 * pg / 1.07); + + const Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); + const Real inv_exp_g = m::exp((1. - dP_comp_ratio) / lambda); + const Real f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; tau = m::min(tau, f_fmin * tau_dyn); } @@ -206,137 +198,31 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs2 * tau; if (emhd_params.viscosity) nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs2 * tau; - } // else yell? + } } - -KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, +template +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& P, const VarMap& m_p, const EMHD_parameters& emhd_params, const Real& gam, - const int& k, const int& j, const int& i, + const int& j, const int& i, Real& tau, Real& chi_e, Real& nu_e) { - if (emhd_params.type == ClosureType::constant) { - // Set tau, nu, chi to constants - // So far none of our problems use this. Also, the expressions are not quite right based on dimensional analysis. - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.conduction_alpha; - if (emhd_params.viscosity) - nu_e = emhd_params.viscosity_alpha; - - } else if (emhd_params.type == ClosureType::soundspeed) { - // Set tau=const, chi/nu prop. to sound speed squared - const Real cs2 = (gam * (gam - 1.) * P(m_p.UU, k, j, i)) / - (P(m_p.RHO, k, j, i) + (gam * P(m_p.UU, k, j, i))); - - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.conduction_alpha * cs2 * tau; - if (emhd_params.viscosity) - nu_e = emhd_params.viscosity_alpha * cs2 * tau; - - } else if (emhd_params.type == ClosureType::kappa_eta) { - // Set tau = const, chi = kappa / rho, nu = eta / rho - - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.kappa / m::max(P(m_p.RHO, k, j, i), SMALL); - if (emhd_params.viscosity) - nu_e = emhd_params.eta / m::max(P(m_p.RHO, k, j, i), SMALL); - - } else if (emhd_params.type == ClosureType::torus) { - FourVectors Dtmp; - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - // TODO need this max() if we're correcting later? - double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); - - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, Loci::center, Xembed); - const GReal r = Xembed[1]; - - // Compute dynamical time scale - const Real tau_dyn = m::sqrt(r*r*r); - - const Real pg = (gam - 1.) * P(m_p.UU, k, j, i); - const Real Theta = pg / P(m_p.RHO, k, j, i); - // Compute local sound speed - const Real cs2 = gam * pg / (P(m_p.RHO, k, j, i) + (gam * P(m_p.UU, k, j, i))); - - Real lambda = 0.01; - Real inv_exp_g = 0.; - Real f_fmin = 0.; - - // Correction due to heat conduction - if (emhd_params.conduction) { - Real q = P(m_p.Q, k, j, i); - if (emhd_params.higher_order_terms) - q *= m::sqrt(P(m_p.RHO, k, j, i) * emhd_params.conduction_alpha * cs2 * Theta * Theta); - Real q_max = emhd_params.conduction_alpha * P(m_p.RHO, k, j, i) * cs2 * m::sqrt(cs2); - Real q_ratio = m::abs(q) / q_max; - inv_exp_g = m::exp(-(q_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); - } - - // Correction due to pressure anisotropy - if (emhd_params.viscosity) { - Real dP = P(m_p.DP, k, j, i); - if (emhd_params.higher_order_terms) - dP *= m::sqrt(P(m_p.RHO, k, j, i) * emhd_params.viscosity_alpha * cs2 * Theta); - Real dP_comp_ratio = m::max(pg - 2./3. * dP, SMALL) / m::max(pg + 1./3. * dP, SMALL); - Real dP_plus = m::min(0.5 * bsq * dP_comp_ratio, 1.49 * pg / 1.07); - Real dP_minus = m::max(-bsq, -2.99 * pg / 1.07); - - Real dP_max = (dP > 0.) ? dP_plus : dP_minus; - - Real dP_ratio = m::abs(dP) / (m::abs(dP_max) + SMALL); - inv_exp_g = m::exp(-(dP_comp_ratio - 1.) / lambda); - f_fmin = inv_exp_g / (inv_exp_g + 1.) + 1.e-5; - - tau = m::min(tau, f_fmin * tau_dyn); - } - - // Update thermal diffusivity and kinematic viscosity - Real max_alpha = (1 - cs2) / (2 * cs2 + 1.e-12); - if (emhd_params.conduction) - chi_e = m::min(max_alpha, emhd_params.conduction_alpha) * cs2 * tau; - if (emhd_params.viscosity) - nu_e = m::min(max_alpha, emhd_params.viscosity_alpha) * cs2 * tau; - } // else yell? + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); + double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); + set_parameters(G, P(m_p.RHO), P(m_p.UU), P(m_p.Q), P(m_p.DP), + bsq, emhd_params, gam, j, i, tau, chi_e, nu_e); } -// ONLY FOR TEST PROBLEMS INITIALIZATION (local version) -KOKKOS_INLINE_FUNCTION void set_parameters_init(const GRCoordinates& G, const Real& rho, const Real& u, +KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, const EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, Real& tau, Real& chi_e, Real& nu_e) { - if (emhd_params.type == ClosureType::constant) { - // Set tau, nu, chi to constants - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.conduction_alpha; - if (emhd_params.viscosity) - nu_e = emhd_params.viscosity_alpha; - - } else if (emhd_params.type == ClosureType::soundspeed) { - // Set tau=const, chi/nu prop. to sound speed squared - const Real cs2 = (gam * (gam - 1.) * u) / (rho + (gam * u)); - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.conduction_alpha * cs2 * tau; - if (emhd_params.viscosity) - nu_e = emhd_params.viscosity_alpha * cs2 * tau; - - } else if (emhd_params.type == ClosureType::kappa_eta){ - // Set tau = const, chi = kappa / rho, nu = eta / rho - tau = emhd_params.tau; - if (emhd_params.conduction) - chi_e = emhd_params.kappa / m::max(rho, SMALL); - if (emhd_params.viscosity) - nu_e = emhd_params.eta / m::max(rho, SMALL); - - } // else yell? + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); + set_parameters(G, P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), + bsq, emhd_params, gam, j, i, tau, chi_e, nu_e); } /** diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 5185586c..bd17890f 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -104,7 +104,7 @@ std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shar // less reliable but velocity reconstructions potentially more robust. // Drift frame floors are now available and preferred when using // the implicit solver to avoid UtoP calls. - std::string frame = pin->GetOrAddString("floors", "frame", "normal"); + std::string frame = pin->GetOrAddString("floors", "frame", "drift"); // TODO TODO ENUM THIS params.Add("frame", frame); if (frame == "normal" || frame == "nof") { diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 50f068f4..a617fa74 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -200,7 +200,8 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame if (use_emhd && emhd_params.higher_order_terms) { // Update q_host (and dP_host, which is zero in this problem). These are now q_tilde and dP_tilde Real tau, chi_e, nu_e; - EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + // Zeros are q, dP, and bsq, only needed for torus closure + EMHD::set_parameters(G, rho_temp, u_temp, 0., 0., 0., emhd_params, gam, j, i, tau, chi_e, nu_e); const Real Theta = (gam - 1.) * u_temp / rho_temp; if (emhd_params.conduction) q_host(k, j, i) *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho_temp * Theta * Theta)) : 0; diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index af14cdcd..2848075f 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -126,7 +126,8 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet if (emhd_params.higher_order_terms) { Real tau, chi_e, nu_e; - EMHD::set_parameters_init(G, rho(k, j, i), u(k, j, i), emhd_params, gam, k, j, i, tau, chi_e, nu_e); + // Zeros are q, dP, and bsq, only needed for torus closure + EMHD::set_parameters(G, rho(k, j, i), u(k, j, i), 0., 0., 0., emhd_params, gam, j, i, tau, chi_e, nu_e); Real Theta = (gam - 1) * u(k, j, i) / rho(k, j, i); Real q_tilde = q(k, j, i); Real dP_tilde = dP(k, j, i); diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index 92766836..c61e07c1 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -137,7 +137,8 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet // Set EMHD parameters Real tau, chi_e, nu_e; - EMHD::set_parameters_init(G, rho_temp, u_temp, emhd_params, gam, k, j, i, tau, chi_e, nu_e); + // Zeros are q, dP, and bsq, only needed for torus closure + EMHD::set_parameters(G, rho_temp, u_temp, 0., 0., 0., emhd_params, gam, j, i, tau, chi_e, nu_e); // Update q and dP (which now are q_tilde and dP_tilde) Real q_tilde = q_host(k, j, i); diff --git a/pars/bondi_viscous.par b/pars/bondi_viscous.par index 517883c8..d7b10a2b 100644 --- a/pars/bondi_viscous.par +++ b/pars/bondi_viscous.par @@ -22,7 +22,9 @@ transform = mks a = 0.0 hslope = 1.0 r_out = 20 -r_in = 3 +# Needed to place 5 zones inside 3M, +# to match the analytic files. +Rhor = 3 tlim = 400.0 From 4008e787d498a620257bc8f34ac2194dd113b173 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 23 May 2023 14:57:38 -0500 Subject: [PATCH 080/219] Fix a silly bug from Polaris optimization branch --- .../parthenon-solvers-new-boundary.patch | 84 +++++++++++++++++++ kharma/debug.cpp | 2 +- kharma/grmhd/grmhd_functions.hpp | 12 ++- 3 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 external/patches/parthenon-solvers-new-boundary.patch diff --git a/external/patches/parthenon-solvers-new-boundary.patch b/external/patches/parthenon-solvers-new-boundary.patch new file mode 100644 index 00000000..2005cfea --- /dev/null +++ b/external/patches/parthenon-solvers-new-boundary.patch @@ -0,0 +1,84 @@ +diff --git a/src/solvers/bicgstab_solver.hpp b/src/solvers/bicgstab_solver.hpp +index 4d23a7e0..b4cede0d 100644 +--- a/src/solvers/bicgstab_solver.hpp ++++ b/src/solvers/bicgstab_solver.hpp +@@ -149,29 +149,29 @@ class BiCGStabSolver : BiCGStabCounter { + precom2 = task_list.AddTask(precom, this->user_precomm_scale, spmd.get(), vec_name); + } + auto send = +- task_list.AddTask(precom2, parthenon::cell_centered_bvars::SendBoundaryBuffers, spmd); ++ task_list.AddTask(precom2, parthenon::SendBoundaryBuffers, spmd); + auto recv = task_list.AddTask( +- precom, parthenon::cell_centered_bvars::ReceiveBoundaryBuffers, spmd); ++ precom, parthenon::ReceiveBoundaryBuffers, spmd); + auto setb = +- task_list.AddTask(recv, parthenon::cell_centered_bvars::SetBoundaries, spmd); +- auto prolong = +- task_list.AddTask(setb, parthenon::ProlongateBoundariesMD, spmd); +- auto postcomm = prolong; ++ task_list.AddTask(recv, parthenon::SetBoundaries, spmd); ++ //auto prolong = ++ // task_list.AddTask(setb, parthenon::ProlongateBounds, spmd); ++ auto postcomm = setb; + if (this->user_postcomm_scale) { +- postcomm = task_list.AddTask(prolong, this->user_postcomm_scale, spmd.get(), vec_name); ++ postcomm = task_list.AddTask(setb, this->user_postcomm_scale, spmd.get(), vec_name); + } + + auto update_rhs = postcomm; + if (this->user_MatVec) { +- auto preflx = prolong; ++ auto preflx = setb; + if (this->user_pre_fluxcor) { +- auto calc_flx = task_list.AddTask(prolong, this->user_pre_fluxcor, spmd.get(), vec_name, spmd.get(), name_out); ++ auto calc_flx = task_list.AddTask(setb, this->user_pre_fluxcor, spmd.get(), vec_name, spmd.get(), name_out); + auto send_flx = +- task_list.AddTask(calc_flx, parthenon::cell_centered_bvars::LoadAndSendFluxCorrections, spmd); ++ task_list.AddTask(calc_flx, parthenon::LoadAndSendFluxCorrections, spmd); + auto recv_flx = +- task_list.AddTask(calc_flx, parthenon::cell_centered_bvars::ReceiveFluxCorrections, spmd); ++ task_list.AddTask(calc_flx, parthenon::ReceiveFluxCorrections, spmd); + preflx = +- task_list.AddTask(recv_flx, parthenon::cell_centered_bvars::SetFluxCorrections, spmd); ++ task_list.AddTask(recv_flx, parthenon::SetFluxCorrections, spmd); + } + update_rhs = task_list.AddTask(preflx, this->user_MatVec, spmd.get(), vec_name, spmd.get(), name_out); + } else { +diff --git a/src/solvers/cg_solver.hpp b/src/solvers/cg_solver.hpp +index 7f374114..44114453 100644 +--- a/src/solvers/cg_solver.hpp ++++ b/src/solvers/cg_solver.hpp +@@ -180,11 +180,11 @@ class CG_Solver : public CG_Counter { + + // ghost exchange. + auto send = +- solver.AddTask(axpy1, parthenon::cell_centered_bvars::SendBoundaryBuffers, md); ++ solver.AddTask(axpy1, parthenon::SendBoundaryBuffers, md); + auto recv = solver.AddTask( +- none, parthenon::cell_centered_bvars::ReceiveBoundaryBuffers, md); ++ none, parthenon::ReceiveBoundaryBuffers, md); + auto setb = +- solver.AddTask(recv | axpy1, parthenon::cell_centered_bvars::SetBoundaries, md); ++ solver.AddTask(recv | axpy1, parthenon::SetBoundaries, md); + + // matvec Ap = J*p + auto matvec = +diff --git a/src/solvers/newton_krylov.hpp b/src/solvers/newton_krylov.hpp +index ba946542..94ed63ec 100644 +--- a/src/solvers/newton_krylov.hpp ++++ b/src/solvers/newton_krylov.hpp +@@ -178,11 +178,11 @@ class NewtonKrylov : NewtonKrylov_Counter { + md.get(), mdelta.get()); + // share \Delta x + auto send = +- ls->AddTask(update, parthenon::cell_centered_bvars::SendBoundaryBuffers, md); ++ ls->AddTask(update, parthenon::SendBoundaryBuffers, md); + auto recv = ls->AddTask(none, +- parthenon::cell_centered_bvars::ReceiveBoundaryBuffers, md); ++ parthenon::ReceiveBoundaryBuffers, md); + auto setb = +- ls->AddTask(recv | update, parthenon::cell_centered_bvars::SetBoundaries, md); ++ ls->AddTask(recv | update, parthenon::SetBoundaries, md); + + // apply physical boundary conditions + auto copy = ls->AddTask(setb, &NewtonKrylov::Copy, this, diff --git a/kharma/debug.cpp b/kharma/debug.cpp index c6c0bdd8..b2172a67 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -90,7 +90,7 @@ TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) if (MPIRank0() && (nzero > 0 || nnan > 0)) { // TODO string formatting in C++ that doesn't suck - printf("Max signal speed ctop was 0 or NaN, direction %d (%d zero, %d NaN)", dir, nzero, nnan); + fprintf(stderr, "Max signal speed ctop was 0 or NaN, direction %d (%d zero, %d NaN)", dir, nzero, nnan); throw std::runtime_error("Bad ctop!"); } diff --git a/kharma/grmhd/grmhd_functions.hpp b/kharma/grmhd/grmhd_functions.hpp index 8a8855e1..f62027e6 100644 --- a/kharma/grmhd/grmhd_functions.hpp +++ b/kharma/grmhd/grmhd_functions.hpp @@ -227,21 +227,19 @@ KOKKOS_INLINE_FUNCTION void calc_4vecs(const GRCoordinates& G, const Local& P, c const int& j, const int& i, const Loci loc, FourVectors& D) { const Real gamma = lorentz_calc(G, P, m, j, i, loc); - const Real inv_alpha = m::sqrt(-G.gcon(loc, j, i, 0, 0)); + const Real alpha = 1. / m::sqrt(-G.gcon(loc, j, i, 0, 0)); - D.ucon[0] = gamma * inv_alpha; - VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma / inv_alpha * G.gcon(loc, j, i, 0, v+1); + D.ucon[0] = gamma / alpha; + VLOOP D.ucon[v+1] = P(m.U1 + v) - gamma * alpha * G.gcon(loc, j, i, 0, v+1); - //G.lower(D.ucon, D.ucov, 0, j, i, loc); - DLOOP2 D.ucov[mu] += G.gcov(loc, j, i, mu, nu) * D.ucon[nu]; + G.lower(D.ucon, D.ucov, 0, j, i, loc); if (m.B1 >= 0) { D.bcon[0] = 0; VLOOP D.bcon[0] += P(m.B1 + v) * D.ucov[v+1]; VLOOP D.bcon[v+1] = (P(m.B1 + v) + D.bcon[0] * D.ucon[v+1]) / D.ucon[0]; - //G.lower(D.bcon, D.bcov, 0, j, i, loc); - DLOOP2 D.bcov[mu] += G.gcov(loc, j, i, mu, nu) * D.bcon[nu]; + G.lower(D.bcon, D.bcov, 0, j, i, loc); } else { DLOOP1 D.bcon[mu] = D.bcov[mu] = 0.; } From fbe9448cb7cd1fc99671bc79d12921b17b1bec4f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 24 May 2023 18:00:06 -0500 Subject: [PATCH 081/219] oops i dropped face ct into kharma --- kharma/CMakeLists.txt | 2 + kharma/b_cd/b_cd.hpp | 11 + kharma/b_cd/seed_B_cd.cpp | 3 +- kharma/b_ct/b_ct.cpp | 395 ++++++++++++++++++ kharma/b_ct/b_ct.hpp | 187 +++++++++ .../seed_B_cd.hpp => b_ct/seed_B_ct.cpp} | 44 +- kharma/b_flux_ct/b_flux_ct.cpp | 18 +- kharma/b_flux_ct/b_flux_ct.hpp | 88 +++- kharma/b_flux_ct/seed_B_flux_ct.cpp | 3 +- kharma/b_flux_ct/seed_B_flux_ct.hpp | 76 ---- kharma/debug.cpp | 2 + kharma/decs.hpp | 7 +- kharma/domain.hpp | 171 ++++++++ kharma/driver/kharma_driver.cpp | 45 +- kharma/driver/kharma_driver.hpp | 23 + kharma/driver/kharma_step.cpp | 26 +- kharma/floors/floors.cpp | 5 +- kharma/floors/floors_functions.hpp | 14 +- kharma/flux/flux.cpp | 44 +- kharma/flux/flux_functions.hpp | 44 +- kharma/flux/get_flux.hpp | 114 ++--- kharma/{ => flux}/reconstruction.hpp | 0 kharma/grmhd/grmhd_functions.hpp | 2 +- kharma/implicit/fixup.cpp | 5 +- kharma/inverter/fixup.cpp | 13 +- kharma/inverter/invert_template.hpp | 1 - kharma/inverter/inverter.cpp | 16 +- kharma/inverter/onedw.hpp | 3 + kharma/kharma.cpp | 17 +- kharma/kharma_package.cpp | 15 +- kharma/main.cpp | 7 +- kharma/prob/b_field_tools.hpp | 18 + kharma/prob/orszag_tang.hpp | 76 +++- kharma/prob/post_initialize.cpp | 10 +- kharma/types.hpp | 91 ++-- pars/orszag_tang_new.par | 67 +++ run.sh | 9 +- 37 files changed, 1303 insertions(+), 369 deletions(-) create mode 100644 kharma/b_ct/b_ct.cpp create mode 100644 kharma/b_ct/b_ct.hpp rename kharma/{b_cd/seed_B_cd.hpp => b_ct/seed_B_ct.cpp} (66%) delete mode 100644 kharma/b_flux_ct/seed_B_flux_ct.hpp create mode 100644 kharma/domain.hpp rename kharma/{ => flux}/reconstruction.hpp (100%) create mode 100644 pars/orszag_tang_new.par diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index 20e10ef6..7b54f6e8 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -18,6 +18,7 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/flux EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_cd EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_ct EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/boundaries EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/current EXE_NAME_SRC) @@ -41,6 +42,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/flux) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_cd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_ct) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/boundaries) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/current) diff --git a/kharma/b_cd/b_cd.hpp b/kharma/b_cd/b_cd.hpp index 42014db0..93b70545 100644 --- a/kharma/b_cd/b_cd.hpp +++ b/kharma/b_cd/b_cd.hpp @@ -56,6 +56,17 @@ namespace B_CD { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); +/** + * Seed an axisymmetric initialization with magnetic field proportional to fluid density, + * or density and radius, to create a SANE or MAD flow + * Note this function expects a normalized P for which rho_max==1 + * + * @param rin is the interior radius of the torus + * @param min_rho_q is the minimum density at which there will be magnetic vector potential + * @param b_field_type is one of "sane" "ryan" "r3s3" or "gaussian", described below (TODO test or remove opts) + */ +TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); + /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". * Also applies floors to the calculated primitives, and fixes up any inversion errors. diff --git a/kharma/b_cd/seed_B_cd.cpp b/kharma/b_cd/seed_B_cd.cpp index 3d5a39a4..88fcaa7e 100644 --- a/kharma/b_cd/seed_B_cd.cpp +++ b/kharma/b_cd/seed_B_cd.cpp @@ -34,11 +34,10 @@ // Seed a torus of some type with a magnetic field according to its density -#include "seed_B_cd.hpp" +#include "b_cd.hpp" #include "b_field_tools.hpp" -#include "b_flux_ct.hpp" #include "grmhd_functions.hpp" using namespace parthenon; diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp new file mode 100644 index 00000000..49ff00e5 --- /dev/null +++ b/kharma/b_ct/b_ct.cpp @@ -0,0 +1,395 @@ +/* + * File: b_flux_ct.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "b_ct.hpp" + +#include "decs.hpp" +#include "domain.hpp" +#include "grmhd.hpp" +#include "kharma.hpp" + +#include + +using namespace parthenon; + +std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + auto pkg = std::make_shared("B_CT"); + Params ¶ms = pkg->AllParams(); + + // Diagnostic & inadvisable flags + + // KHARMA requires some kind of field transport if there is a magnetic field allocated. + // Use this flag if you actually want to disable all magnetic field flux corrections, + // and allow a field divergence to grow unchecked, usually for debugging or comparison reasons + bool disable_ct = pin->GetOrAddBoolean("b_field", "disable_ct", false); + params.Add("disable_ct", disable_ct); + + // Default to stopping execution when divB is large, which generally indicates something + // has gone wrong. As always, can be disabled by the brave. + bool kill_on_large_divb = pin->GetOrAddBoolean("b_field", "kill_on_large_divb", true); + params.Add("kill_on_large_divb", kill_on_large_divb); + Real kill_on_divb_over = pin->GetOrAddReal("b_field", "kill_on_divb_over", 1.e-3); + params.Add("kill_on_divb_over", kill_on_divb_over); + + // TODO selector BS/LDZ04/LDZ07/GS + + // Add a reducer for divB to params + params.Add("divb_reducer", AllReduce()); + + // FIELDS + + // TODO maybe one day implicit? + + // Flags for B fields on faces. + // We don't mark these as "Primitive" and "Conserved" else they'd be bundled + // with all the cell vars in a bunch of places we don't want + // TODO this won't apply in ghosts, probably... if so we'll need to bundle only ::Cell in lots of places + std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, + Metadata::GetUserFlag("Explicit")}; + std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, + Metadata::GetUserFlag("Explicit")}; // TODO TODO Restart, FillGhost + auto m = Metadata(flags_prim_f); + pkg->AddField("prims.fB", m); + m = Metadata(flags_cons_f); + pkg->AddField("cons.fB", m); + + // Cell-centered versions. Needed for BS, not for other schemes. + // Probably will want to keep primitives for e.g. correct PtoU of MHD vars, but cons maybe can go + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::Conserved, Metadata::WithFluxes, + Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; + std::vector s_vector({NVEC}); + m = Metadata(flags_prim, s_vector); + pkg->AddField("prims.B", m); + m = Metadata(flags_cons, s_vector); + pkg->AddField("cons.B", m); + + // EMF on edges. + // TODO TODO ADD Metadata::FillGhost + std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy}; + m = Metadata(flags_emf); + pkg->AddField("B_CT.emf", m); + + // CALLBACKS + + // We implement a source term replacement, rather than addition, + // but same difference, really + pkg->AddSource = B_CT::AddSource; + + // Also ensure that prims get filled, both during step and on boundaries + //pkg->MeshUtoP = B_CT::MeshUtoP; + pkg->BlockUtoP = B_CT::BlockUtoP; + pkg->BoundaryUtoP = B_CT::BlockUtoP; + + // Register the other callbacks + pkg->PostStepDiagnosticsMesh = B_CT::PostStepDiagnostics; + // TODO TODO prolongation/restriction will be registered here too + + // The definition of MaxDivB we care about actually changes per-transport, + // so calculating it is handled by the transport package + // We'd only ever need to declare or calculate divB for output (getting the max is independent) + if (KHARMA::FieldIsOutput(pin, "divB")) { + pkg->BlockUserWorkBeforeOutput = B_CT::FillOutput; + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); + pkg->AddField("divB", m); + } + + // List (vector) of HistoryOutputVars that will all be enrolled as output variables + // LATER + // parthenon::HstVar_list hst_vars = {}; + // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_CT::MaxDivB, "MaxDivB")); + // // Event horizon magnetization. Might be the same or different for different representations? + // if (pin->GetBoolean("coordinates", "spherical")) { + // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi0, "Phi_0")); + // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi5, "Phi_EH")); + // } + // // add callbacks for HST output to the Params struct, identified by the `hist_param_key` + // pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + + return pkg; +} + +TaskStatus B_CT::MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) +{ + // TODO later + for (int i=0; i < md->NumBlocks(); i++) + B_CT::BlockUtoP(md->GetBlockData(i).get(), domain, coarse); + return TaskStatus::complete; +} + +void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + auto pmb = rc->GetBlockPointer(); + const int ndim = pmb->pmy_mesh->ndim; + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + auto B_Pf = rc->PackVariables(std::vector{"prims.fB"}); + auto B_U = rc->PackVariables(std::vector{"cons.B"}); + auto B_P = rc->PackVariables(std::vector{"prims.B"}); + const auto& G = pmb->coords; + + // Update the primitive B-fields on faces + const IndexRange3 bf = KDomain::GetRange(rc, domain, 0, 1, coarse); + pmb->par_for("UtoP_B", bf.ks, bf.ke, bf.js, bf.je, bf.is, bf.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // TODO will we need face area here? + B_Pf(F1, 0, k, j, i) = B_Uf(F1, 0, k, j, i) / G.gdet(Loci::face1, j, i); + B_Pf(F2, 0, k, j, i) = B_Uf(F2, 0, k, j, i) / G.gdet(Loci::face2, j, i); + B_Pf(F3, 0, k, j, i) = B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i); + } + ); + Kokkos::fence(); + // Average the primitive vals for zone centers (TODO right?) + const IndexRange3 bc = KDomain::GetRange(rc, domain, coarse); + pmb->par_for("UtoP_B_center", bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_P(V1, k, j, i) = (B_Pf(F1, 0, k, j, i) + B_Pf(F1, 0, k, j, i + 1)) / 2; + B_P(V2, k, j, i) = (ndim > 1) ? (B_Pf(F2, 0, k, j, i) + B_Pf(F2, 0, k, j + 1, i)) / 2 + : B_Pf(F2, 0, k, j, i); + B_P(V3, k, j, i) = (ndim > 2) ? (B_Pf(F3, 0, k, j, i) + B_Pf(F3, 0, k + 1, j, i)) / 2 + : B_Pf(F3, 0, k, j, i); + } + ); + Kokkos::fence(); + pmb->par_for("UtoP_B_centerPtoU", 0, NVEC-1, bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + B_U(v, k, j, i) = B_P(v, k, j, i) * G.gdet(Loci::center, j, i); + } + ); + Kokkos::fence(); +} + +// TODO this isn't really a source... it's a replacement of the +// face-centered fields according to constrained transport rules +void B_CT::AddSource(MeshData *md, MeshData *mdudt) +{ + auto pmesh = md->GetMeshPointer(); + const int ndim = pmesh->ndim; + + // This is what we're replacing + auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); + + // EMF temporary + auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); + + // Figure out indices + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 1); + const IndexRange block = IndexRange{0, dB_Uf_dt.GetDim(5)-1}; + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + + // Calculate circulation by averaging fluxes (Balsara & Spicer) + auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); + pmb0->par_for("B_CT_emf_BS", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // TODO will we need gdet/cell length here? + const auto& G = B_U.GetCoords(bl); + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) = + 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i) + B_U(bl).flux(X2DIR, V3, k, j, i) + - B_U(bl).flux(X3DIR, V2, k, j - 1, i) - B_U(bl).flux(X3DIR, V2, k, j, i)); + emf_pack(bl, E2, 0, k, j, i) = + 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1) + B_U(bl).flux(X3DIR, V1, k, j, i) + - B_U(bl).flux(X1DIR, V3, k - 1, j, i) - B_U(bl).flux(X1DIR, V3, k, j, i)); + } + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); + } + ); + + // TODO LDZ04, LDZ07, GS? + + // Circulation -> change in flux at face + // Note we *replace* whatever this term in the source term was "supposed" to be + // TODO stick to defined faces? Or don't bother? + pmb0->par_for("B_CT_Circ", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = dB_Uf_dt.GetCoords(bl); + dB_Uf_dt(bl, F1, 0, k, j, i) = emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i); + dB_Uf_dt(bl, F2, 0, k, j, i) = -emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i); + dB_Uf_dt(bl, F3, 0, k, j, i) = 0.; + if (ndim > 2) { + dB_Uf_dt(bl, F1, 0, k, j, i) += -emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i); + dB_Uf_dt(bl, F2, 0, k, j, i) += emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i); + dB_Uf_dt(bl, F3, 0, k, j, i) += emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i) + - emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i); + } + + } + ); +} + + + +double B_CT::MaxDivB(MeshData *md) +{ + auto pmesh = md->GetMeshPointer(); + const int ndim = pmesh->ndim; + + auto B_U = md->PackVariables(std::vector{"cons.fB"}); + + // Figure out indices + const IndexRange ibl = md->GetBoundsI(IndexDomain::interior); + const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); + const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + + double max_divb; + Kokkos::Max max_reducer(max_divb); + pmb0->par_reduce("divB_max", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { + const auto& G = B_U.GetCoords(b); + double local_divb = face_div(G, B_U(b), ndim, k, j, i); + if (local_divb > local_result) local_result = local_divb; + } + , max_reducer); + + return max_divb; +} +double B_CT::BlockMaxDivB(MeshBlockData *rc) +{ + const int ndim = KDomain::GetNDim(rc); + + auto B_U = rc->PackVariables(std::vector{"cons.fB"}); + + // Figure out indices + const IndexRange3 b = KDomain::GetRange(rc, IndexDomain::interior, 0, 1); + + auto pmb = rc->GetBlockPointer(); + + double max_divb; + Kokkos::Max max_reducer(max_divb); + pmb->par_reduce("divB_max", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { + const auto& G = B_U.GetCoords(); + double local_divb = face_div(G, B_U, ndim, k, j, i); + if (local_divb > local_result) local_result = local_divb; + } + , max_reducer); + + return max_divb; +} + +double B_CT::GlobalMaxDivB(MeshData *md) +{ + static AllReduce max_divb; + max_divb.val = MaxDivB(md); + max_divb.StartReduce(MPI_MAX); + while (max_divb.CheckReduce() == TaskStatus::incomplete); + return max_divb.val; +} + +TaskStatus B_CT::PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) +{ + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + + // Since this is in the history file now, I don't bother printing it + // unless we're being verbose. It's not costly to calculate though + if (pmb0->packages.Get("Globals")->Param("verbose") >= 1) { + // Calculate the maximum from/on all nodes + const double divb_max = B_CT::GlobalMaxDivB(md); + // Print on rank zero + if (MPIRank0()) { + std::cout << "Max DivB: " << divb_max << std::endl; + } + if (kill_on_large_divb) { + if (divb_max > pmb0->packages.Get("B_CT")->Param("kill_on_divb_over")) + throw std::runtime_error("DivB exceeds maximum! Quitting..."); + } + } + + return TaskStatus::complete; +} + +// TODO unify these by adding FillOutputMesh option + +void B_CT::CalcDivB(MeshData *md, std::string divb_field_name) +{ + auto pmesh = md->GetMeshPointer(); + const int ndim = pmesh->ndim; + + // Packing out here avoids frequent per-mesh packs. Do we need to? + auto B_U = md->PackVariables(std::vector{"cons.fB"}); + auto divB = md->PackVariables(std::vector{divb_field_name}); + + const IndexRange ibl = md->GetBoundsI(IndexDomain::interior); + const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); + const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + + // See MaxDivB for details + pmb0->par_for("calc_divB", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i) { + const auto& G = B_U.GetCoords(b); + divB(b, 0, k, j, i) = face_div(G, B_U(b), ndim, k, j, i); + } + ); +} + +void B_CT::FillOutput(MeshBlock *pmb, ParameterInput *pin) +{ + auto rc = pmb->meshblock_data.Get().get(); + const int ndim = pmb->pmy_mesh->ndim; + if (ndim < 2) return; + + auto B_U = rc->PackVariables(std::vector{"cons.fB"}); + auto divB = rc->PackVariables(std::vector{"divB"}); + + const IndexRange ibl = rc->GetBoundsI(IndexDomain::interior); + const IndexRange jbl = rc->GetBoundsJ(IndexDomain::interior); + const IndexRange kbl = rc->GetBoundsK(IndexDomain::interior); + + const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; + const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; + const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + + pmb->par_for("divB_output", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + const auto& G = B_U.GetCoords(); + divB(0, k, j, i) = face_div(G, B_U, ndim, k, j, i); + } + ); +} diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp new file mode 100644 index 00000000..c472bd00 --- /dev/null +++ b/kharma/b_ct/b_ct.hpp @@ -0,0 +1,187 @@ +/* + * File: b_flux_ct.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "grmhd_functions.hpp" +#include "reductions.hpp" +#include "types.hpp" + +#include + +#include + +/** + * This physics package implements Constrained Transport of a split face-centered B field. + * Any CT implementations should probably go here. + */ +namespace B_CT { +/** + * Declare fields, initialize (few) parameters + */ +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + +/** + * Seed a divergence-free magnetic field of user's choice, optionally + * proportional to existing fluid density. + * Updates primitive and conserved variables. + */ +TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); + +/** + * Get the primitive variables, which in Parthenon's nomenclature are "derived". + * Also applies floors to the calculated primitives, and fixes up any inversion errors + * + * Defaults to entire domain, as the KHARMA algorithm relies on applying UtoP over ghost zones. + * + * input: Conserved B = sqrt(-gdet) * B^i + * output: Primitive B = B^i + */ +void BlockUtoP(MeshBlockData *md, IndexDomain domain, bool coarse=false); +TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); + +/** + * Reverse of the above. Only used alone during initialization. + * Generally, use Flux::BlockPtoU or Flux::BlockPtoUExceptMHD. + */ +void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); + +/** + * Replace conserved face B field components with versions calculated + * by constrained transport. + */ +void AddSource(MeshData *md, MeshData *mdudt); + +// TODO UNIFY ALL THE FOLLOWING + +/** + * Calculate maximum corner-centered divergence of magnetic field, + * to check it is being preserved ~=0 + * Used as a Parthenon History function, so must take exactly the + * listed arguments + */ +double MaxDivB(MeshData *md); +double BlockMaxDivB(MeshBlockData *rc); + +/** + * Returns the global maximum value, rather than the maximum over this rank's MeshData + */ +double GlobalMaxDivB(MeshData *md); + +/** + * Diagnostics printed/computed after each step + * Currently just max divB + */ +TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb=false); + +/** + * Diagnostics function should print divB, and optionally stop execution if it's large + */ +inline TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) +{ + auto& params = md->GetMeshPointer()->block_list[0]->packages.Get("B_CT")->AllParams(); + return PrintGlobalMaxDivB(md, params.Get("kill_on_large_divb")); +} + +/** + * Fill fields which are calculated only for output to file, i.e., divB + */ +void FillOutput(MeshBlock *pmb, ParameterInput *pin); +/** + * Fill field "name" with divB + */ +void CalcDivB(MeshData *md, std::string divb_field_name="divB"); + +// Reductions: FOR LATER +// KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) +// { +// // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 +// return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B +// } + +// inline Real ReducePhi0(MeshData *md) +// { +// return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); +// } +// inline Real ReducePhi5(MeshData *md) +// { +// return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); +// } + +// Device functions +template +KOKKOS_INLINE_FUNCTION Real face_div(const GRCoordinates &G, Global &v, const int &ndim, const int &k, const int &j, const int &i) +{ + Real du = (v(F1, 0, k, j, i + 1) - v(F1, 0, k, j, i)); + if (ndim > 1) { + du += (v(F2, 0, k, j + 1, i) - v(F2, 0, k, j, i)); + } + if (ndim > 2) { + du += (v(F3, 0, k + 1, j, i) - v(F3, 0, k, j, i)); + } + return du / G.CellVolume(k, j, i); +} + +// KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, +// const int& k, const int& j, const int& i) +// { +// B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j);// A3,2 derivative +// B_U(F2, 0, k, j, i) =-(A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative +// B_U(F3, 0, k, j, i) = 0.; +// } + +KOKKOS_INLINE_FUNCTION void curl_3D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, + const int& k, const int& j, const int& i) +{ + // "CT" to faces from a cell-centered potential + + B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j) // A3,2 derivative + - (A(V2, k + 1, j, i) - A(V2, k, j, i)) / G.Dxc<3>(k);// A2,3 derivative + + B_U(F2, 0, k, j, i) = (A(V1, k + 1, j, i) - A(V1, k, j, i)) / G.Dxc<3>(k) // A1,3 derivative + - (A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative + + B_U(F3, 0, k, j, i) = (A(V2, k, j, i + 1) - A(V2, k, j, i)) / G.Dxc<1>(i) // A2,1 derivative + - (A(V1, k, j + 1, i) - A(V1, k, j, i)) / G.Dxc<2>(j);// A1,2 derivative +} + +KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, + const int& k, const int& j, const int& i) +{ + B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j);// A3,2 derivative + B_U(F2, 0, k, j, i) =-(A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative + B_U(F3, 0, k, j, i) = 0.; +} + +} diff --git a/kharma/b_cd/seed_B_cd.hpp b/kharma/b_ct/seed_B_ct.cpp similarity index 66% rename from kharma/b_cd/seed_B_cd.hpp rename to kharma/b_ct/seed_B_ct.cpp index f162d01c..c79f0bd2 100644 --- a/kharma/b_cd/seed_B_cd.hpp +++ b/kharma/b_ct/seed_B_ct.cpp @@ -1,5 +1,5 @@ /* - * File: seed_B_cd.hpp + * File: seed_B_ct.cpp * * BSD 3-Clause License * @@ -33,30 +33,30 @@ */ // Seed a torus of some type with a magnetic field according to its density -#pragma once -#include "decs.hpp" -#include "types.hpp" +#include "b_ct.hpp" -namespace B_CD +#include "b_field_tools.hpp" +#include "coordinate_utils.hpp" +#include "fm_torus.hpp" +#include "grmhd_functions.hpp" + +using namespace parthenon; + +TaskStatus B_CT::SeedBField(MeshBlockData *rc, ParameterInput *pin) { + auto pmb = rc->GetBlockPointer(); -/** - * Seed an axisymmetric initialization with magnetic field proportional to fluid density, - * or density and radius, to create a SANE or MAD flow - * Note this function expects a normalized P for which rho_max==1 - * - * @param rin is the interior radius of the torus - * @param min_rho_q is the minimum density at which there will be magnetic vector potential - * @param b_field_type is one of "sane" "ryan" "r3s3" or "gaussian", described below (TODO test or remove opts) - */ -TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); + const auto& G = pmb->coords; + GridScalar rho = rc->Get("prims.rho").data; + GridVector B_P = rc->Get("prims.B").data; + GridVector B_U = rc->Get("cons.B").data; -/** - * Add flux to BH horizon - * Applicable to any Kerr-space GRMHD sim, run after import/initialization - * Preserves divB==0 with a Flux-CT step at end - */ -//void SeedBHFlux(MeshBlockData *rc, Real BHflux); + // Orszag-Tang Vortex + + + // Finally, make sure we initialize the primitive field too + B_CT::BlockUtoP(rc, IndexDomain::entire, false); -} // namespace B_CD + return TaskStatus::complete; +} diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index c957dede..af2f93e6 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -274,14 +274,14 @@ void FluxCT(MeshData *md) // Calculate emf around each face pmb0->par_for("flux_ct_emf", block.s, block.e, kl.s, kl.e, jl.s, jl.e, il.s, il.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { - emf_pack(b, V3, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - - B_F(b).flux(X2DIR, V1, k, j, i) - B_F(b).flux(X2DIR, V1, k, j, i-1)); if (ndim > 2) { - emf_pack(b, V2, k, j, i) = -0.25 * (B_F(b).flux(X1DIR, V3, k, j, i) + B_F(b).flux(X1DIR, V3, k-1, j, i) - - B_F(b).flux(X3DIR, V1, k, j, i) - B_F(b).flux(X3DIR, V1, k, j, i-1)); emf_pack(b, V1, k, j, i) = 0.25 * (B_F(b).flux(X2DIR, V3, k, j, i) + B_F(b).flux(X2DIR, V3, k-1, j, i) - B_F(b).flux(X3DIR, V2, k, j, i) - B_F(b).flux(X3DIR, V2, k, j-1, i)); + emf_pack(b, V2, k, j, i) = 0.25 * (B_F(b).flux(X3DIR, V1, k, j, i) + B_F(b).flux(X3DIR, V1, k, j, i-1) - + B_F(b).flux(X1DIR, V3, k, j, i) - B_F(b).flux(X1DIR, V3, k-1, j, i)); } + emf_pack(b, V3, k, j, i) = 0.25 * (B_F(b).flux(X1DIR, V2, k, j, i) + B_F(b).flux(X1DIR, V2, k, j-1, i) - + B_F(b).flux(X2DIR, V1, k, j, i) - B_F(b).flux(X2DIR, V1, k, j, i-1)); } ); @@ -330,13 +330,13 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) // Imagine a corner of the domain, with ghost and physical zones // as below, denoted w/'g' and 'p' respectively. - // - // g | p | p - //----------- + // ... // g | p | p - //xxx-------- + //----------- 1 + // g | p | p ... + //xxx-------- 0 // g | g | g - // + //-1 0 1 // The flux through 'x' is not important for updating a physical zone, // as it does not border any. However, FluxCT considers it when updating // nearby fluxes, two of which affect physical zones. diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 92a7a4e9..0fa89728 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -54,6 +54,13 @@ namespace B_FluxCT { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); +/** + * Seed a divergence-free magnetic field of user's choice, optionally + * proportional to existing fluid density. + * Updates primitive and conserved variables. + */ +TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); + /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". * Also applies floors to the calculated primitives, and fixes up any inversion errors @@ -130,6 +137,23 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin); */ void CalcDivB(MeshData *md, std::string divb_field_name="divB"); +// Reductions: phi uses global machinery, but divB is too +// Can also sum the hemispheres independently to be fancy (TODO?) +KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) +{ + // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 + return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B +} + +inline Real ReducePhi0(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); +} +inline Real ReducePhi5(MeshData *md) +{ + return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); +} + /** * ND divergence, averaging to cell corners * TODO likely better templated, as with all ND stuff @@ -216,21 +240,63 @@ KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, B3 = norm*term3/G.Dxc<3>(k); } -// Reductions: phi uses global machinery, but divB is too -// Can also sum the hemispheres independently to be fancy (TODO?) -KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) +KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, + const int& k, const int& j, const int& i) { - // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 - return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B -} + // Take a flux-ct step from the corner potentials. + // This needs to be 3D because post-tilt A may not point in the phi direction only -inline Real ReducePhi0(MeshData *md) -{ - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); + // A3,2 derivative + const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1) + + A(V3, k + 1, j + 1, i) + A(V3, k + 1, j + 1, i + 1)) / 4; + const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1) + + A(V3, k + 1, j, i) + A(V3, k + 1, j, i + 1)) / 4; + // A2,3 derivative + const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j, i + 1) + + A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; + const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + + A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); + + // A1,3 derivative + const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + + A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; + const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + + A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 4; + // A3,1 derivative + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1) + + A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + + A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; + B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); + + // A2,1 derivative + const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + + A(V2, k + 1, j, i + 1) + A(V2, k + 1, j + 1, i + 1)) / 4; + const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i) + + A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 4; + // A1,2 derivative + const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1) + + A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; + const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + + A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; + B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } -inline Real ReducePhi5(MeshData *md) + +KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, + const int& k, const int& j, const int& i) { - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); + // A3,2 derivative + const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; + const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; + B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j); + + // A3,1 derivative + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; + B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.Dxc<1>(i); + + B_U(V3, k, j, i) = 0; } } diff --git a/kharma/b_flux_ct/seed_B_flux_ct.cpp b/kharma/b_flux_ct/seed_B_flux_ct.cpp index 89c31bfb..baeb67aa 100644 --- a/kharma/b_flux_ct/seed_B_flux_ct.cpp +++ b/kharma/b_flux_ct/seed_B_flux_ct.cpp @@ -34,10 +34,9 @@ // Seed a torus of some type with a magnetic field according to its density -#include "seed_B_flux_ct.hpp" +#include "b_flux_ct.hpp" #include "b_field_tools.hpp" -#include "b_flux_ct.hpp" #include "boundaries.hpp" #include "coordinate_utils.hpp" #include "fm_torus.hpp" diff --git a/kharma/b_flux_ct/seed_B_flux_ct.hpp b/kharma/b_flux_ct/seed_B_flux_ct.hpp deleted file mode 100644 index c679e67c..00000000 --- a/kharma/b_flux_ct/seed_B_flux_ct.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// Seed a torus of some type with a magnetic field according to its density -#pragma once - -#include "decs.hpp" -#include "types.hpp" - -namespace B_FluxCT -{ - -/** - * Seed a divergence-free magnetic field of user's choice, optionally - * proportional to existing fluid density. - * Updates primitive and conserved variables. - */ -TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); - -KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, - const int& k, const int& j, const int& i) -{ - // Take a flux-ct step from the corner potentials. - // This needs to be 3D because post-tilt A may not point in the phi direction only - - // A3,2 derivative - const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1) + - A(V3, k + 1, j + 1, i) + A(V3, k + 1, j + 1, i + 1)) / 4; - const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1) + - A(V3, k + 1, j, i) + A(V3, k + 1, j, i + 1)) / 4; - // A2,3 derivative - const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j, i + 1) + - A(V2, k + 1, j + 1, i) + A(V2, k + 1, j + 1, i + 1)) / 4; - const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j, i + 1) + - A(V2, k, j + 1, i) + A(V2, k, j + 1, i + 1)) / 4; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); - - // A1,3 derivative - const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1) + - A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; - const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + - A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 4; - // A3,1 derivative - const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1) + - A(V3, k, j + 1, i + 1) + A(V3, k + 1, j + 1, i + 1)) / 4; - const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i) + - A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 4; - B_U(V2, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); - - // A2,1 derivative - const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1) + - A(V2, k + 1, j, i + 1) + A(V2, k + 1, j + 1, i + 1)) / 4; - const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i) + - A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 4; - // A1,2 derivative - const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1) + - A(V1, k + 1, j + 1, i) + A(V1, k + 1, j + 1, i + 1)) / 4; - const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1) + - A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 4; - B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); -} - -KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, - const int& k, const int& j, const int& i) -{ - // A3,2 derivative - const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k, j + 1, i + 1)) / 2; - const Real A3c2b = (A(V3, k, j, i) + A(V3, k, j, i + 1)) / 2; - B_U(V1, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j); - - // A3,1 derivative - const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k, j + 1, i + 1)) / 2; - const Real A3c1b = (A(V3, k, j, i) + A(V3, k, j + 1, i)) / 2; - B_U(V2, k, j, i) = - (A3c1f - A3c1b) / G.Dxc<1>(i); - - B_U(V3, k, j, i) = 0; -} - -} // namespace B_FluxCT diff --git a/kharma/debug.cpp b/kharma/debug.cpp index b2172a67..be3aa6e7 100644 --- a/kharma/debug.cpp +++ b/kharma/debug.cpp @@ -41,6 +41,8 @@ #include "types.hpp" // TODO make this a DomainReduce, and add better verbosity options +// TODO + TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) { Flag("CheckNaN"); diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 689b86cc..479a77bb 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -95,7 +95,7 @@ using GReal = double; #define NVEC 3 #define VLOOP for(int v = 0; v < NVEC; ++v) -// Useful Enums to avoid lots of #defines +// Useful enum to avoid lots of #defines // See following functions and coord() in gr_coordinates.hpp to // get an idea of these locations. All faces/corner are *left* of center #define NLOC 5 @@ -143,14 +143,13 @@ inline bool MPIRank0() } #else /** - * Am I rank 0? Saves typing vs comparing the global every time. - * DUMMY function for no-MPI case: constexpr return for slight optimizations. + * DUMMY version for no-MPI case: constexpr return for slight optimizations. */ inline bool MPIRank0() { return true; } #endif // MPI_PARALLEL // A few generic "NDArray" overloads for readability. -// TODO torn on futures of these, as they're used inconsistently +// TODO torn on futures of these: they're explicitly per-block // Shape+3D ("Grid") arrays using GridScalar = parthenon::ParArrayND; using GridVector = parthenon::ParArrayND; diff --git a/kharma/domain.hpp b/kharma/domain.hpp new file mode 100644 index 00000000..d26ceec1 --- /dev/null +++ b/kharma/domain.hpp @@ -0,0 +1,171 @@ +/* + * File: domain.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "types.hpp" + +#include "boundaries.hpp" + +namespace KDomain { + +/** + * Functions for checking boundaries in 3D. + * Uses IndexRange objects, or this would be in kharma_utils.hpp + */ +KOKKOS_INLINE_FUNCTION bool outside(const int& k, const int& j, const int& i, + const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) +{ + return (i < ib.s) || (i > ib.e) || (j < jb.s) || (j > jb.e) || (k < kb.s) || (k > kb.e); +} +KOKKOS_INLINE_FUNCTION bool outside(const int& k, const int& j, const int& i, const IndexRange3& b) +{ + return (i < b.is) || (i > b.ie) || (j < b.js) || (j > b.je) || (k < b.ks) || (k > b.ke); +} +KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, + const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) +{ + // This is faster in the case that the point is outside + return !outside(k, j, i, kb, jb, ib); +} +KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, const IndexRange3& b) +{ + // This is faster in the case that the point is outside + return !outside(k, j, i, b); +} + +// TODO(BSP) these really should be in Parthenon +template +inline const int& GetNDim(MeshBlockData* rc) +{ return rc->GetBlockPointer()->pmy_mesh->ndim; } +template +inline const int& GetNDim(std::shared_ptr> rc) +{ return rc->GetBlockPointer()->pmy_mesh->ndim; } +template +inline const int& GetNDim(MeshData* md) +{ return md->GetMeshPointer()->ndim; } +template +inline const int& GetNDim(std::shared_ptr> md) +{ return md->GetMeshPointer()->ndim; } + +template +inline const IndexShape& GetCellbounds(MeshBlockData* rc, bool coarse=false) +{ return (coarse) ? rc->GetBlockPointer()->c_cellbounds + : rc->GetBlockPointer()->cellbounds; } +template +inline const IndexShape& GetCellbounds(std::shared_ptr> rc, bool coarse=false) +{ return GetCellbounds(rc.get()); } +template +inline const IndexShape& GetCellbounds(MeshData* md, bool coarse=false) +{ return (coarse) ? md->GetBlockData(0)->GetBlockPointer()->c_cellbounds + : md->GetBlockData(0)->GetBlockPointer()->cellbounds; } +template +inline const IndexShape& GetCellbounds(std::shared_ptr> md, bool coarse=false) +{ return GetCellbounds(md.get()); } + +/** + * Get the actual indices corresponding to an IndexDomain, optionally with some halo. + * Note both "halo" values are *added*, i.e. measured to the *right*. That is, the + * size of GetRange(rc, interior, -1, 1) is [N1+2, N2+2, N3+2]. + * This seemed more natural for people coming from for loops. + */ +template +inline IndexRange3 GetRange(T data, IndexDomain domain, int left_halo=0, int right_halo=0, bool coarse=false) +{ + // TODO also offsets for e.g. PtoU_Send? + // Get sizes + const auto& cellbounds = GetCellbounds(data, coarse); + const IndexRange ib = cellbounds.GetBoundsI(domain); + const IndexRange jb = cellbounds.GetBoundsJ(domain); + const IndexRange kb = cellbounds.GetBoundsK(domain); + // Compute sizes with specified halo zones included in non-trivial dimensions + const int& ndim = GetNDim(data); + // If ghost & not x1 direction + // if + const IndexRange il = IndexRange{ib.s + left_halo, ib.e + right_halo}; + const IndexRange jl = (ndim > 1) ? IndexRange{jb.s + left_halo, jb.e + right_halo} : jb; + const IndexRange kl = (ndim > 2) ? IndexRange{kb.s + left_halo, kb.e + right_halo} : kb; + return IndexRange3{(uint) il.s, (uint) il.e, + (uint) jl.s, (uint) jl.e, + (uint) kl.s, (uint) kl.e}; +} +template +inline IndexRange3 GetRange(T data, IndexDomain domain, bool coarse) +{ + return GetRange(data, domain, 0, 0, coarse); +} +/** + * Get zones which are inside the physical domain, i.e. set by computation or MPI halo sync, + * not by problem boundary conditions. + */ +template +inline IndexRange3 GetPhysicalRange(MeshBlockData* rc) +{ + using KBoundaries::IsPhysicalBoundary; + + const auto& bounds = GetCellbounds(rc); + const auto pmb = rc->GetBlockPointer(); + + return IndexRange3{IsPhysicalBoundary(pmb, BoundaryFace::inner_x1) + ? (uint) bounds.is(IndexDomain::interior) + : (uint) bounds.is(IndexDomain::entire), + IsPhysicalBoundary(pmb, BoundaryFace::outer_x1) + ? (uint) bounds.ie(IndexDomain::interior) + : (uint) bounds.ie(IndexDomain::entire), + IsPhysicalBoundary(pmb, BoundaryFace::inner_x2) + ? (uint) bounds.js(IndexDomain::interior) + : (uint) bounds.js(IndexDomain::entire), + IsPhysicalBoundary(pmb, BoundaryFace::outer_x2) + ? (uint) bounds.je(IndexDomain::interior) + : (uint) bounds.je(IndexDomain::entire), + IsPhysicalBoundary(pmb, BoundaryFace::inner_x3) + ? (uint) bounds.ks(IndexDomain::interior) + : (uint) bounds.ks(IndexDomain::entire), + IsPhysicalBoundary(pmb, BoundaryFace::outer_x3) + ? (uint) bounds.ke(IndexDomain::interior) + : (uint) bounds.ke(IndexDomain::entire)}; +} + +template +inline IndexSize3 GetBlockSize(T data, IndexDomain domain=IndexDomain::entire) +{ + // Get sizes + const auto& cellbounds = GetCellbounds(data); + const uint n1 = cellbounds.ncellsi(domain); + const uint n2 = cellbounds.ncellsj(domain); + const uint n3 = cellbounds.ncellsk(domain); + return IndexSize3{n1, n2, n3}; +} + +} diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index d69af2fe..454931a0 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -34,9 +34,9 @@ */ #include "kharma_driver.hpp" +#include "b_ct.hpp" #include "boundaries.hpp" #include "flux.hpp" -// GetFlux #include "get_flux.hpp" std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std::shared_ptr& packages) @@ -220,6 +220,11 @@ void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md) { + // Pre-calculate B field cell-center values + auto t_start_fluxes = t_start; + if (md->GetMeshPointer()->packages.AllPackages().count("B_CT")) + t_start_fluxes = tl.AddTask(t_start, B_CT::MeshUtoP, md, IndexDomain::entire, false); + // Calculate fluxes in each direction using given reconstruction // Must be spelled out so as to generate each templated version of GetFlux<> to be available at runtime // Details in flux/get_flux.hpp @@ -227,38 +232,38 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst TaskID t_calculate_flux1, t_calculate_flux2, t_calculate_flux3; switch (recon) { case RType::donor_cell: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); break; case RType::linear_mc: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); break; // case RType::linear_vl: - // t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - // t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - // t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + // t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + // t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + // t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); // break; case RType::weno5: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); break; case RType::weno5_lower_edges: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); break; case RType::weno5_lower_poles: - t_calculate_flux1 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux2 = tl.AddTask(t_start, Flux::GetFlux, md); - t_calculate_flux3 = tl.AddTask(t_start, Flux::GetFlux, md); + t_calculate_flux1 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux2 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); + t_calculate_flux3 = tl.AddTask(t_start_fluxes, Flux::GetFlux, md); break; default: std::cerr << "Reconstruction type not supported! Main supported reconstructions:" << std::endl - << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; + << "donor_cell, linear_mc, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } return t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index f1336669..1723dd8b 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -141,4 +141,27 @@ class KHARMADriver : public MultiStageDriver { return Update::WeightedSumData, MeshBlockData>(flags, source, source, norm, 0., source); } + static TaskStatus WeightedSumDataFace(const std::vector &flags, MeshData *in1, MeshData *in2, const Real w1, const Real w2, + MeshData *out) { + Kokkos::Profiling::pushRegion("Task_WeightedSumData"); + const auto &x = in1->PackVariables(flags); + const auto &y = in2->PackVariables(flags); + const auto &z = out->PackVariables(flags); + parthenon::par_for( + DEFAULT_LOOP_PATTERN, "WeightedSumData", DevExecSpace(), 0, x.GetDim(5) - 1, 0, + x.GetDim(4) - 1, 0, x.GetDim(3) - 1, 0, x.GetDim(2) - 1, 0, x.GetDim(1) - 1, + KOKKOS_LAMBDA(const int b, const int l, const int k, const int j, const int i) { + // TOOD(someone) This is potentially dangerous and/or not intended behavior + // as we still may want to update (or populate) z if any of those vars are + // not allocated yet. + if (x.IsAllocated(b, l) && y.IsAllocated(b, l) && z.IsAllocated(b, l)) { + z(b, F1, l, k, j, i) = w1 * x(b, F1, l, k, j, i) + w2 * y(b, F1, l, k, j, i); + z(b, F2, l, k, j, i) = w1 * x(b, F2, l, k, j, i) + w2 * y(b, F2, l, k, j, i); + z(b, F3, l, k, j, i) = w1 * x(b, F3, l, k, j, i) + w2 * y(b, F3, l, k, j, i); + } + }); + Kokkos::Profiling::popRegion(); // Task_WeightedSumData + return TaskStatus::complete; + } + }; \ No newline at end of file diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 9766f0ea..f88556a9 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -83,6 +83,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto& pkgs = blocks[0]->packages.AllPackages(); auto& driver_pkg = pkgs.at("Driver")->AllParams(); const bool use_b_cleanup = pkgs.count("B_Cleanup"); + const bool use_b_ct = pkgs.count("B_CT"); const bool use_electrons = pkgs.count("Electrons"); const bool use_jcon = pkgs.count("Current"); @@ -160,17 +161,34 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // Perform the update using the source term // Add any proportion of the step start required by the integrator (e.g., RK2) - auto t_avg_data = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, - std::vector({Metadata::Independent}), + // TODO splitting this is stupid, dig into Parthenon & fix + auto t_avg_data_c = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent, Metadata::Cell}), md_sub_step_init.get(), md_full_step_init.get(), integrator->gam0[stage-1], integrator->gam1[stage-1], md_sub_step_final.get()); + auto t_avg_data = t_avg_data_c; + if (use_b_ct) { + t_avg_data = tl.AddTask(t_avg_data_c, WeightedSumDataFace, + std::vector({Metadata::Independent, Metadata::Face}), + md_sub_step_init.get(), md_full_step_init.get(), + integrator->gam0[stage-1], integrator->gam1[stage-1], + md_sub_step_final.get()); + } // apply du/dt to the result - auto t_update = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, - std::vector({Metadata::Independent}), + auto t_update_c = tl.AddTask(t_avg_data, Update::WeightedSumData, MeshData>, + std::vector({Metadata::Independent, Metadata::Cell}), md_sub_step_final.get(), md_flux_src.get(), 1.0, integrator->beta[stage-1] * integrator->dt, md_sub_step_final.get()); + auto t_update = t_update_c; + if (use_b_ct) { + t_update = tl.AddTask(t_update_c, WeightedSumDataFace, + std::vector({Metadata::Independent, Metadata::Face}), + md_sub_step_final.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_sub_step_final.get()); + } // UtoP needs a guess in order to converge, so we copy in sc0 // (but only the fluid primitives!) Copying and syncing ensures that solves of the same zone diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index bd17890f..97f2412c 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -202,6 +202,9 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); + fprintf(stderr, "%d %d %d %d %d\n", m_p.RHO, m_p.UU, m_p.U1, m_p.U2, m_p.U3); + fprintf(stderr, "%d %d %d %d %d\n", m_u.RHO, m_u.UU, m_u.U1, m_u.U2, m_u.U3); + // Apply floors over the same zones we just updated with UtoP // This selects the entire domain, but we then require pflag >= 0, // which keeps us from covering completely uninitialized zones @@ -209,7 +212,7 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * const IndexRange ib = mbd->GetBoundsI(domain); const IndexRange jb = mbd->GetBoundsJ(domain); const IndexRange kb = mbd->GetBoundsK(domain); - pmb->par_for("apply_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("apply_initial_floors", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { apply_floors(G, P, m_p, gam, emhd_params, k, j, i, floors, U, m_u); apply_ceilings(G, P, m_p, gam, k, j, i, floors, U, m_u); diff --git a/kharma/floors/floors_functions.hpp b/kharma/floors/floors_functions.hpp index 3b61c1b0..0ce4a7bb 100644 --- a/kharma/floors/floors_functions.hpp +++ b/kharma/floors/floors_functions.hpp @@ -188,9 +188,9 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa if (use_ff) { P(m_p.RHO, k, j, i) += m::max(0., rhoflr_max - rho); P(m_p.UU, k, j, i) += m::max(0., uflr_max - u); - // TODO should be all Flux + // Update conserved variables + //Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u, loc); GRMHD::p_to_u(G, P, m_p, gam, k, j, i, U, m_u, loc); - } else if (use_df) { // Drift frame floors. Refer to Appendix B3 in https://doi.org/10.1093/mnras/stx364 (hereafter R17) const Real lapse2 = 1. / (-G.gcon(Loci::center, j, i, 0, 0)); @@ -208,10 +208,12 @@ KOKKOS_INLINE_FUNCTION int apply_floors(const GRCoordinates& G, const VariablePa // Normal observer magnetic field Real Bcon[GR_DIM] = {0}; Real Bcov[GR_DIM] = {0}; - Bcon[0] = 0; - Bcon[1] = P(m_p.B1, k, j, i); - Bcon[2] = P(m_p.B2, k, j, i); - Bcon[3] = P(m_p.B3, k, j, i); + if (m_p.B1 >= 0) { + Bcon[0] = 0; + Bcon[1] = P(m_p.B1, k, j, i); + Bcon[2] = P(m_p.B2, k, j, i); + Bcon[3] = P(m_p.B3, k, j, i); + } DLOOP2 Bcov[mu] += G.gcov(Loci::center, j, i, mu, nu) * Bcon[nu]; const Real Bsq = m::max(dot(Bcon, Bcov), SMALL); const Real B_mag = m::sqrt(Bsq); diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 2a76544f..5f080d2f 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -36,6 +36,7 @@ // Most includes are in the header TODO fix? #include "grmhd.hpp" +#include "kharma.hpp" using namespace parthenon; @@ -47,23 +48,13 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared auto pkg = std::make_shared("Flux"); Params ¶ms = pkg->AllParams(); - // We can't use GetVariablesByFlag yet, so walk through and count manually - int nvar = 0; - for (auto pkg : packages->AllPackages()) { - for (auto field : pkg.second->AllFields()) { - // Specifically ignore the B_Cleanup variables, we don't handle their boundary conditions - if (field.second.IsSet(Metadata::WithFluxes)) { - if (field.second.Shape().size() < 1) { - nvar += 1; - } else { - nvar += field.second.Shape()[0]; - } - } - } - } + // We can't just use GetVariables or something since there's no mesh yet. + // That's what this function is for. + int nvar = KHARMA::CountVars(packages.get(), Metadata::WithFluxes); + std::cout << "Allocating fluxes with nvar: " << nvar << std::endl; std::vector s_flux({nvar}); - std::vector flags_temp = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; - Metadata m = Metadata(flags_temp, s_flux); + std::vector flags_flux = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; + Metadata m = Metadata(flags_flux, s_flux); pkg->AddField("Flux.Pr", m); pkg->AddField("Flux.Pl", m); pkg->AddField("Flux.Ur", m); @@ -71,14 +62,18 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared pkg->AddField("Flux.Fr", m); pkg->AddField("Flux.Fl", m); - std::vector s_vec({NVEC}); - m = Metadata(flags_temp, s_vec); + // TODO move to faces? Not important for these quantities as caches + std::vector s_vector({NVEC}); + std::vector flags_speed = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; + m = Metadata(flags_speed, s_vector); pkg->AddField("Flux.cmax", m); pkg->AddField("Flux.cmin", m); - - // Velocities, for upwinding later - //pkg->AddField("Flux.vr", m); - //pkg->AddField("Flux.vl", m); + // Velocities, for upwinded constrained transport + // TODO can be 2-length someday if we want to get spicy + if (packages->AllPackages().count("B_CT")) { + pkg->AddField("Flux.vr", m); + pkg->AddField("Flux.vl", m); + } Flag("Initialized"); return pkg; @@ -129,7 +124,7 @@ TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coa // Pack variables PackIndexMap prims_map, cons_map; const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const auto& U = rc->PackVariables({Metadata::Conserved, Metadata::Cell}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const int nvar = U.GetDim(4); @@ -158,7 +153,6 @@ TaskStatus Flux::MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, bool coarse) { - // // Pointers auto pmb = rc->GetBlockPointer(); const int ndim = pmb->pmy_mesh->ndim; @@ -207,7 +201,7 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo const auto& G = pmb->coords; - pmb->par_for("p_to_u", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("p_to_u_send", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Flux::p_to_u(G, P, m_p, emhd_params, gam, k, j, i, U, m_u); } diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index 01718a3e..b1de46ea 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -133,7 +133,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, flux(m_u.U3) = T[3] * gdet; // Magnetic field - if (m_p.B1 >= 0) { + if (m_u.B1 >= 0) { // Magnetic field if (dir == 0) { VLOOP flux(m_u.B1 + v) = P(m_p.B1 + v) * gdet; @@ -143,7 +143,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, VLOOP flux(m_u.B1 + v) = (D.bcon[v+1] * D.ucon[dir] - D.bcon[dir] * D.ucon[v+1]) * gdet; } // Extra scalar psi for constraint damping, see B_CD - if (m_p.PSI >= 0) { + if (m_u.PSI >= 0) { if (dir == 0) { flux(m_u.PSI) = P(m_p.PSI) * gdet; } else { @@ -156,25 +156,25 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, } // EMHD Variables: advect like rho - if (m_p.Q >= 0) + if (m_u.Q >= 0) flux(m_u.Q) = P(m_p.Q) * D.ucon[dir] * gdet; - if (m_p.DP >= 0) + if (m_u.DP >= 0) flux(m_u.DP) = P(m_p.DP) * D.ucon[dir] * gdet; // Electrons: normalized by density - if (m_p.KTOT >= 0) { + if (m_u.KTOT >= 0) { flux(m_u.KTOT) = flux(m_u.RHO) * P(m_p.KTOT); - if (m_p.K_CONSTANT >= 0) + if (m_u.K_CONSTANT >= 0) flux(m_u.K_CONSTANT) = flux(m_u.RHO) * P(m_p.K_CONSTANT); - if (m_p.K_HOWES >= 0) + if (m_u.K_HOWES >= 0) flux(m_u.K_HOWES) = flux(m_u.RHO) * P(m_p.K_HOWES); - if (m_p.K_KAWAZURA >= 0) + if (m_u.K_KAWAZURA >= 0) flux(m_u.K_KAWAZURA) = flux(m_u.RHO) * P(m_p.K_KAWAZURA); - if (m_p.K_WERNER >= 0) + if (m_u.K_WERNER >= 0) flux(m_u.K_WERNER) = flux(m_u.RHO) * P(m_p.K_WERNER); - if (m_p.K_ROWAN >= 0) + if (m_u.K_ROWAN >= 0) flux(m_u.K_ROWAN) = flux(m_u.RHO) * P(m_p.K_ROWAN); - if (m_p.K_SHARMA >= 0) + if (m_u.K_SHARMA >= 0) flux(m_u.K_SHARMA) = flux(m_u.RHO) * P(m_p.K_SHARMA); } } @@ -198,7 +198,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.U3, k, j, i) = T[3] * gdet; // Magnetic field - if (m_p.B1 >= 0) { + if (m_u.B1 >= 0) { // Magnetic field if (dir == 0) { VLOOP flux(m_u.B1 + v, k, j, i) = P(m_p.B1 + v, k, j, i) * gdet; @@ -208,7 +208,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P VLOOP flux(m_u.B1 + v, k, j, i) = (D.bcon[v+1] * D.ucon[dir] - D.bcon[dir] * D.ucon[v+1]) * gdet; } // Extra scalar psi for constraint damping, see B_CD - if (m_p.PSI >= 0) { + if (m_u.PSI >= 0) { if (dir == 0) { flux(m_u.PSI, k, j, i) = P(m_p.PSI, k, j, i) * gdet; } else { @@ -221,25 +221,25 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P } // EMHD Variables: advect like rho - if (m_p.Q >= 0) + if (m_u.Q >= 0) flux(m_u.Q, k, j, i) = P(m_p.Q, k, j, i) * D.ucon[dir] * gdet; - if (m_p.DP >= 0) + if (m_u.DP >= 0) flux(m_u.DP, k, j, i) = P(m_p.DP, k, j, i) * D.ucon[dir] * gdet; // Electrons: normalized by density - if (m_p.KTOT >= 0) { + if (m_u.KTOT >= 0) { flux(m_u.KTOT, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.KTOT, k, j, i); - if (m_p.K_CONSTANT >= 0) + if (m_u.K_CONSTANT >= 0) flux(m_u.K_CONSTANT, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_CONSTANT, k, j, i); - if (m_p.K_HOWES >= 0) + if (m_u.K_HOWES >= 0) flux(m_u.K_HOWES, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_HOWES, k, j, i); - if (m_p.K_KAWAZURA >= 0) + if (m_u.K_KAWAZURA >= 0) flux(m_u.K_KAWAZURA, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_KAWAZURA, k, j, i); - if (m_p.K_WERNER >= 0) + if (m_u.K_WERNER >= 0) flux(m_u.K_WERNER, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_WERNER, k, j, i); - if (m_p.K_ROWAN >= 0) + if (m_u.K_ROWAN >= 0) flux(m_u.K_ROWAN, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_ROWAN, k, j, i); - if (m_p.K_SHARMA >= 0) + if (m_u.K_SHARMA >= 0) flux(m_u.K_SHARMA, k, j, i) = flux(m_u.RHO, k, j, i) * P(m_p.K_SHARMA, k, j, i); } } diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index d1929b49..1885af5b 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -35,6 +35,7 @@ #include "flux.hpp" +#include "domain.hpp" #include "floors_functions.hpp" namespace Flux { @@ -103,6 +104,7 @@ inline TaskStatus GetFlux(MeshData *md) PackIndexMap prims_map, cons_map; const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); + // TODO maybe all WithFluxes vars, split into cell & face? const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); @@ -114,20 +116,14 @@ inline TaskStatus GetFlux(MeshData *md) const auto& Fl_all = md->PackVariables(std::vector{"Flux.Fl"}); const auto& Fr_all = md->PackVariables(std::vector{"Flux.Fr"}); - // Get sizes + // Get the domain size + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, -1, 1); + // Get other sizes we need const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); - const IndexRange ib = md->GetBoundsI(IndexDomain::interior); - const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = md->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; const int nvar = U_all.GetDim(4); - // 1-zone halo in nontrivial dimensions - // We leave is/ie, js/je, ks/ke with their usual definitions for consistency, and define - // the loop bounds separately to include the appropriate halo - // TODO halo 2 "shouldn't" crash but does. Artifact of switch to faces? - const IndexRange il = IndexRange{ib.s - 1, ib.e + 1}; - const IndexRange jl = (ndim > 1) ? IndexRange{jb.s - 1, jb.e + 1} : jb; - const IndexRange kl = (ndim > 2) ? IndexRange{kb.s - 1, kb.e + 1} : kb; + //std::cout << "Calculating fluxes for " << cmax.GetDim(5) << " blocks, " + // << nvar << " variables (" << P_all.GetDim(4) << " primitives)" << std::endl; // Allocate scratch space const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM @@ -142,20 +138,21 @@ inline TaskStatus GetFlux(MeshData *md) // do not accept three pairs of bounds, which we need in order to iterate over blocks Flag("GetFlux_"+std::to_string(dir)+"_recon"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_recon", pmb0->exec_space, - recon_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, - KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { - const auto& G = U_all.GetCoords(b); + recon_scratch_bytes, scratch_level, block.s, block.e, b.ks, b.ke, b.js, b.je, + KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& bl, const int& k, const int& j) { + const auto& G = U_all.GetCoords(bl); ScratchPad2D Pl_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Pr_s(member.team_scratch(scratch_level), nvar, n1); - // Wrapper for a big switch statement between reconstruction schemes. Possibly slow. - // This function is generally a lot of if statements - KReconstruction::reconstruct(member, P_all(b), k, j, il.s, il.e, Pl_s, Pr_s); + // We template on reconstruction type to avoid a big switch statement here. + // Instead, a version of GetFlux() is generated separately for each reconstruction/direction pair. + // See reconstruction.hpp for all the implementations. + KReconstruction::reconstruct(member, P_all(bl), k, j, b.is, b.ie, Pl_s, Pr_s); // Sync all threads in the team so that scratch memory is consistent member.team_barrier(); - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { auto Pl = Kokkos::subview(Pl_s, Kokkos::ALL(), i); auto Pr = Kokkos::subview(Pr_s, Kokkos::ALL(), i); @@ -171,39 +168,39 @@ inline TaskStatus GetFlux(MeshData *md) // Copy out state (TODO(BSP) eliminate) for (int p=0; p < nvar; ++p) { - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { - Pl_all(b, p, k, j, i) = Pl_s(p, i); - Pr_all(b, p, k, j, i) = Pr_s(p, i); + Pl_all(bl, p, k, j, i) = Pl_s(p, i); + Pr_all(bl, p, k, j, i) = Pr_s(p, i); } ); } - + member.team_barrier(); } ); EndFlag(); Flag("GetFlux_"+std::to_string(dir)+"_left"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_left", pmb0->exec_space, - flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, - KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { - const auto& G = U_all.GetCoords(b); + flux_scratch_bytes, scratch_level, block.s, block.e, b.ks, b.ke, b.js, b.je, + KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& bl, const int& k, const int& j) { + const auto& G = U_all.GetCoords(bl); ScratchPad2D Pl_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Ul_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Fl_s(member.team_scratch(scratch_level), nvar, n1); // Copy in state (TODO(BSP) eliminate) for (int p=0; p < nvar; ++p) { - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { - Pl_s(p, i) = Pl_all(b, p, k, j, i); + Pl_s(p, i) = Pl_all(bl, p, k, j, i); } ); } member.team_barrier(); // LEFT FACES - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { auto Pl = Kokkos::subview(Pl_s, Kokkos::ALL(), i); auto Ul = Kokkos::subview(Ul_s, Kokkos::ALL(), i); @@ -221,18 +218,18 @@ inline TaskStatus GetFlux(MeshData *md) Flux::vchar(G, Pl, m_p, Dtmp, gam, emhd_params, k, j, i, loc, dir, cmaxL, cminL); // Record speeds - cmax(b, dir-1, k, j, i) = m::max(0., cmaxL); - cmin(b, dir-1, k, j, i) = m::max(0., -cminL); + cmax(bl, dir-1, k, j, i) = m::max(0., cmaxL); + cmin(bl, dir-1, k, j, i) = m::max(0., -cminL); } ); member.team_barrier(); // Copy out state for (int p=0; p < nvar; ++p) { - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { - Ul_all(b, p, k, j, i) = Ul_s(p, i); - Fl_all(b, p, k, j, i) = Fl_s(p, i); + Ul_all(bl, p, k, j, i) = Ul_s(p, i); + Fl_all(bl, p, k, j, i) = Fl_s(p, i); } ); } @@ -242,25 +239,25 @@ inline TaskStatus GetFlux(MeshData *md) Flag("GetFlux_"+std::to_string(dir)+"_right"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_right", pmb0->exec_space, - flux_scratch_bytes, scratch_level, block.s, block.e, kl.s, kl.e, jl.s, jl.e, - KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& b, const int& k, const int& j) { - const auto& G = U_all.GetCoords(b); + flux_scratch_bytes, scratch_level, block.s, block.e, b.ks, b.ke, b.js, b.je, + KOKKOS_LAMBDA(parthenon::team_mbr_t member, const int& bl, const int& k, const int& j) { + const auto& G = U_all.GetCoords(bl); ScratchPad2D Pr_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Ur_s(member.team_scratch(scratch_level), nvar, n1); ScratchPad2D Fr_s(member.team_scratch(scratch_level), nvar, n1); // Copy in state (TODO(BSP) eliminate) for (int p=0; p < nvar; ++p) { - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { - Pr_s(p, i) = Pr_all(b, p, k, j, i); + Pr_s(p, i) = Pr_all(bl, p, k, j, i); } ); } member.team_barrier(); // RIGHT FACES, finalize signal speed - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { auto Pr = Kokkos::subview(Pr_s, Kokkos::ALL(), i); auto Ur = Kokkos::subview(Ur_s, Kokkos::ALL(), i); @@ -277,18 +274,18 @@ inline TaskStatus GetFlux(MeshData *md) Flux::vchar(G, Pr, m_p, Dtmp, gam, emhd_params, k, j, i, loc, dir, cmaxR, cminR); // Calculate cmax/min based on comparison with cached values - cmax(b, dir-1, k, j, i) = m::abs(m::max(cmax(b, dir-1, k, j, i), cmaxR)); - cmin(b, dir-1, k, j, i) = m::abs(m::max(cmin(b, dir-1, k, j, i), -cminR)); + cmax(bl, dir-1, k, j, i) = m::abs(m::max(cmax(bl, dir-1, k, j, i), cmaxR)); + cmin(bl, dir-1, k, j, i) = m::abs(m::max(cmin(bl, dir-1, k, j, i), -cminR)); } ); member.team_barrier(); // Copy out state for (int p=0; p < nvar; ++p) { - parthenon::par_for_inner(member, il.s, il.e, + parthenon::par_for_inner(member, b.is, b.ie, [&](const int& i) { - Ur_all(b, p, k, j, i) = Ur_s(p, i); - Fr_all(b, p, k, j, i) = Fr_s(p, i); + Ur_all(bl, p, k, j, i) = Ur_s(p, i); + Fr_all(bl, p, k, j, i) = Fr_s(p, i); } ); } @@ -297,18 +294,29 @@ inline TaskStatus GetFlux(MeshData *md) ); EndFlag(); + // Apply what we've calculated Flag("GetFlux_"+std::to_string(dir)+"_riemann"); - pmb0->par_for("flux_solve", block.s, block.e, 0, nvar-1, kl.s, kl.e, jl.s, jl.e, il.s, il.e, - KOKKOS_LAMBDA(const int& b, const int& p, const int& k, const int& j, const int& i) { - // Apply what we've calculated - // TODO OTHER FLUXES AGAIN - U_all(b).flux(dir, p, k, j, i) = llf(Fl_all(b, p, k, j, i), Fr_all(b, p, k, j, i), - cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i), - Ul_all(b, p, k, j, i), Ur_all(b, p, k, j, i)); + if (use_hlle) { // More fluxes would need a template + pmb0->par_for("flux_hlle", block.s, block.e, 0, nvar-1, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int& bl, const int& p, const int& k, const int& j, const int& i) { + U_all(bl).flux(dir, p, k, j, i) = hlle(Fl_all(bl, p, k, j, i), Fr_all(bl, p, k, j, i), + cmax(bl, dir-1, k, j, i), cmin(bl, dir-1, k, j, i), + Ul_all(bl, p, k, j, i), Ur_all(bl, p, k, j, i)); - } - ); + } + ); + } else { + pmb0->par_for("flux_llf", block.s, block.e, 0, nvar-1, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int& bl, const int& p, const int& k, const int& j, const int& i) { + U_all(bl).flux(dir, p, k, j, i) = llf(Fl_all(bl, p, k, j, i), Fr_all(bl, p, k, j, i), + cmax(bl, dir-1, k, j, i), cmin(bl, dir-1, k, j, i), + Ul_all(bl, p, k, j, i), Ur_all(bl, p, k, j, i)); + + + } + ); + } EndFlag(); EndFlag(); diff --git a/kharma/reconstruction.hpp b/kharma/flux/reconstruction.hpp similarity index 100% rename from kharma/reconstruction.hpp rename to kharma/flux/reconstruction.hpp diff --git a/kharma/grmhd/grmhd_functions.hpp b/kharma/grmhd/grmhd_functions.hpp index f62027e6..de18e50e 100644 --- a/kharma/grmhd/grmhd_functions.hpp +++ b/kharma/grmhd/grmhd_functions.hpp @@ -333,7 +333,7 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Global& P, cons // Particle number flux U(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * Dtmp.ucon[0] * gdet; - if (m_p.B1 >= 0) { + if (m_u.B1 >= 0) { // MHD stress-energy tensor w/ first index up, second index down Real mhd[GR_DIM]; GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), Dtmp, 0, mhd); diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fixup.cpp index fbac7845..81871696 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fixup.cpp @@ -34,6 +34,7 @@ #include "implicit.hpp" +#include "domain.hpp" #include "floors.hpp" #include "flux_functions.hpp" @@ -95,7 +96,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { for (int l = -1; l <= 1; l++) { int ii = i + l, jj = j + m, kk = k + n; // If we haven't overstepped array bounds... - if (inside(kk, jj, ii, kb, jb, ib)) { + if (KDomain::inside(kk, jj, ii, kb, jb, ib)) { // Weight by distance // TODO abs(l) == l*l always? double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); @@ -117,7 +118,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { if(wsum < 1.e-10) { // TODO probably should crash here. Or average anyway? #ifndef KOKKOS_ENABLE_SYCL - if (flag_verbose >= 3 && inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... + if (flag_verbose >= 3 && KDomain::inside(k, j, i, kb_b, jb_b, ib_b)) // If an interior zone... printf("No neighbors were available at %d %d %d!\n", i, j, k); #endif // TODO SYCL has cout } else { diff --git a/kharma/inverter/fixup.cpp b/kharma/inverter/fixup.cpp index 0f436fae..174c4db9 100644 --- a/kharma/inverter/fixup.cpp +++ b/kharma/inverter/fixup.cpp @@ -34,6 +34,7 @@ #include "inverter.hpp" +#include "domain.hpp" #include "floors.hpp" #include "floors_functions.hpp" #include "flux_functions.hpp" @@ -56,7 +57,7 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) } Flag("Inverter::FixUtoP"); - // Only fixup the core 5 prims + // Only fixup the core 5 prims TODO build by flag, HD + anything implicit auto P = GRMHD::PackHDPrims(rc); GridScalar pflag = rc->Get("pflag").data; @@ -71,11 +72,11 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) // OR in an MPI boundary. This is because it is applied *after* the MPI sync, // but before physical boundary zones are computed (which it should never use anyway) - const IndexRange3 b = GetPhysicalZones(pmb, pmb->cellbounds); + const IndexRange3 b = KDomain::GetPhysicalRange(rc); const auto& G = pmb->coords; - pmb->par_for("fix_U_to_P", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + pmb->par_for("fix_U_to_P", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { if (failed(pflag(k, j, i))) { // Luckily fixups are rare, so we don't have to worry about optimizing this *too* much @@ -87,7 +88,7 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) for (int l = -1; l <= 1; l++) { int ii = i + l, jj = j + m, kk = k + n; // If we haven't overstepped array bounds... - if (inside(kk, jj, ii, b.kb, b.jb, b.ib)) { + if (KDomain::inside(kk, jj, ii, b)) { // Weight by distance double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); @@ -137,12 +138,12 @@ TaskStatus Inverter::FixUtoP(MeshBlockData *rc) // Get floor flag GridScalar fflag = rc->Get("fflag").data; - pmb->par_for("fix_U_to_P_floors", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + pmb->par_for("fix_U_to_P_floors", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { if (failed(pflag(k, j, i))) { // Make sure all fixed values still abide by floors (floors keep lockstep) // TODO Full floors instead of just geo? - apply_geo_floors(G, P, m_p, gam, k, j, i, floors); + Floors::apply_geo_floors(G, P, m_p, gam, k, j, i, floors); // Make sure to keep lockstep // This will only be run for GRMHD, so we can call its p_to_u diff --git a/kharma/inverter/invert_template.hpp b/kharma/inverter/invert_template.hpp index 6f511dd4..1df2ee28 100644 --- a/kharma/inverter/invert_template.hpp +++ b/kharma/inverter/invert_template.hpp @@ -64,7 +64,6 @@ KOKKOS_INLINE_FUNCTION bool failed(T status_flag) { // Return only values >0, among the failure flags return static_cast(status_flag) > static_cast(Status::success); - // TODO if in debug mode check flag < neg_rhou } /** diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index 64e35528..c120d448 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -36,6 +36,7 @@ // This will include headers in the correct order #include "invert_template.hpp" +#include "domain.hpp" #include "reductions.hpp" /** @@ -62,19 +63,14 @@ inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, b const Real stepsize = pmb->packages.Get("Inverter")->Param("stepsize"); // Get the primitives from our conserved versions - // Currently this runs over *all* zones, including all ghosts, even - // uninitialized zones which are still zero. We select for initialized - // zones only in the loop below, to avoid failures to converge while - // calculating primtive vars over as much of the domain as possible - // We could (did formerly) save some time here by running over - // only zones with initialized conserved variables, but the domain - // of such values is not rectangular in the current handling + // Notice we recover variables for only the physical (interior or MPI-boundary) + // zones! These are the only ones which are filled at our point in the step auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const IndexRange3 b = GetPhysicalZones(pmb, bounds); + const IndexRange3 b = KDomain::GetPhysicalRange(rc); - pmb->par_for("U_to_P", b.kb.s, b.kb.e, b.jb.s, b.jb.e, b.ib.s, b.ib.e, + pmb->par_for("U_to_P", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - if (inside(k, j, i, b.kb, b.jb, b.ib)) { + if (KDomain::inside(k, j, i, b)) { // Run over all interior zones and any initialized ghosts pflag(k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); } diff --git a/kharma/inverter/onedw.hpp b/kharma/inverter/onedw.hpp index dea8f05d..181e85eb 100644 --- a/kharma/inverter/onedw.hpp +++ b/kharma/inverter/onedw.hpp @@ -100,6 +100,9 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const const VariablePack& P, const VarMap& m_p, const Loci loc) { + if (i == 10 && j == 11) + printf("CONS: %g %g %g %g %g %g %g %g", U(m_u.RHO, k, j, i), U(m_u.UU, k, j, i), U(m_u.U1, k, j, i), U(m_u.U2, k, j, i), + U(m_u.U3, k, j, i), U(m_u.B1, k, j, i), U(m_u.B2, k, j, i), U(m_u.B3, k, j, i)); // Catch negative density if (U(m_u.RHO, k, j, i) <= 0.) { return Status::neg_input; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 7d713b21..cc9a158c 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -43,6 +43,7 @@ #include "b_flux_ct.hpp" #include "b_cd.hpp" #include "b_cleanup.hpp" +#include "b_ct.hpp" #include "current.hpp" #include "kharma_driver.hpp" #include "electrons.hpp" @@ -299,14 +300,17 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference auto t_b_field = t_none; std::string b_field_solver = pin->GetOrAddString("b_field", "solver", "flux_ct"); - if (b_field_solver == "none" || b_field_solver == "b_cleanup") { + if (b_field_solver == "none" || b_field_solver == "cleanup" || b_field_solver == "b_cleanup") { // Don't add a B field - } else if (b_field_solver == "constraint_damping" || b_field_solver == "b_cd") { + } else if (b_field_solver == "constrained_transport" || b_field_solver == "face_ct") { + t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_CT::Initialize, pin.get()); + } else if (b_field_solver == "constraint_damping" || b_field_solver == "cd") { // Constraint damping, probably only useful for non-GR MHD systems t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_CD::Initialize, pin.get()); - } else { - // Don't even error on bad values. This is probably what you want + } else if (b_field_solver == "flux_ct") { t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_FluxCT::Initialize, pin.get()); + } else { + throw std::invalid_argument("Invalid solver! Must be e.g., flux_ct, face_ct, cd, cleanup..."); } // Cleanup for the B field, using an elliptic solve for eliminating divB // Almost always loaded explicitly in addition to another transport, just for cleaning at simulation start @@ -362,6 +366,11 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // TODO print full package list as soon as we know it, up here +#if DEBUG + // Carry the ParameterInput with us, for generating outputs whenever we want + packages->Get("Globals")->AllParams().Add("pin", pin.get()); +#endif + EndFlag(); return std::move(*packages); } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 508a3a81..7c1f04a1 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -57,9 +57,21 @@ TaskStatus Packages::FixFlux(MeshData *md) TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag("BlockUtoP"); + // Apply UtoP from B_CT, as this fills B primitive var for the GRMHD UtoP + // TODO could maybe call this in Inverter, or handle all ordering there, or something + auto pmb = rc->GetBlockPointer(); + auto pkgs = pmb->packages.AllPackages(); + if (pkgs.count("B_CT")) { + KHARMAPackage *pkpackage = pmb->packages.Get("B_CT"); + if (pkpackage->BlockUtoP != nullptr) { + Flag("BlockUtoP_B_CT"); + pkpackage->BlockUtoP(rc, domain, coarse); + EndFlag(); + } + } auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage.second->BlockUtoP != nullptr) { + if (kpackage.second->BlockUtoP != nullptr && kpackage.first != "B_CT") { Flag("BlockUtoP_"+kpackage.first); kpackage.second->BlockUtoP(rc, domain, coarse); EndFlag(); @@ -70,6 +82,7 @@ TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool } TaskStatus Packages::MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) { + // TODO TODO prefer MeshUtoP implementations and fall back Flag("MeshUtoP"); for (int i=0; i < md->NumBlocks(); ++i) BlockUtoP(md->GetBlockData(i).get(), domain, coarse); diff --git a/kharma/main.cpp b/kharma/main.cpp index 218e9b88..3e7f48b9 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -170,11 +170,8 @@ int main(int argc, char *argv[]) KHARMA::PostInitialize(pin, pmesh, is_restart); EndFlag(); -#if DEBUG - // Carry the ParameterInput with us, for generating outputs whenever we want - pmesh->packages.Get("Globals")->AllParams().Add("pin", pin); -#endif - + std::string driver_type = pmesh->packages.Get("Driver")->Param("type"); + std::cerr << "Initializing and running " << driver_type << " driver" << std::endl; // Construct a temporary driver purely for parameter parsing KHARMADriver driver(pin, papp, pmesh); diff --git a/kharma/prob/b_field_tools.hpp b/kharma/prob/b_field_tools.hpp index 974c6f95..e09406bf 100644 --- a/kharma/prob/b_field_tools.hpp +++ b/kharma/prob/b_field_tools.hpp @@ -36,6 +36,8 @@ #include "decs.hpp" #include "types.hpp" + + // Internal representation of the field initialization preference for quick switch // Avoids string comparsion in kernels enum BSeedType{constant, monopole, monopole_cube, sane, ryan, ryan_quadrupole, r3s3, steep, gaussian, bz_monopole, vertical}; @@ -71,3 +73,19 @@ inline BSeedType ParseBSeedType(std::string b_field_type) throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); } } + +/** + * Initializer for magnetic fields directly: value of a divergence-free configuration at a point + */ +KOKKOS_INLINE_FUNCTION double BSeed_A(BSeedType type, GReal Xembed[GR_DIM]) +{ + +} + +/** + * + */ +KOKKOS_INLINE_FUNCTION double BSeed_B(BSeedType type, GReal Xembed[GR_DIM]) +{ + +} \ No newline at end of file diff --git a/kharma/prob/orszag_tang.hpp b/kharma/prob/orszag_tang.hpp index a9d1d870..41fd17f0 100644 --- a/kharma/prob/orszag_tang.hpp +++ b/kharma/prob/orszag_tang.hpp @@ -1,6 +1,10 @@ #pragma once #include "decs.hpp" +#include "types.hpp" + +#include "b_ct.hpp" +#include "domain.hpp" using namespace parthenon; @@ -15,7 +19,8 @@ using namespace parthenon; * to the nonrelativistic problem; as tscale increases * the problem becomes increasingly relativistic * - * Stolen directly from iharm2d_v3 + * Originally stolen directly from iharm2d_v3, + * now somewhat modified */ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, ParameterInput *pin) { @@ -23,7 +28,6 @@ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, Parame GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto& G = pmb->coords; @@ -32,32 +36,62 @@ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, Parame // Default phase puts the current sheet in the middle of the domain const Real phase = pin->GetOrAddReal("orszag_tang", "phase", M_PI); - IndexDomain domain = IndexDomain::interior; - IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - pmb->par_for("ot_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + // TODO coord_embed for snake coords? + + IndexDomain domain = IndexDomain::entire; + IndexRange3 b = KDomain::GetRange(rc, domain); + pmb->par_for("ot_init", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord(k, j, i, Loci::center, X); rho(k, j, i) = 25./9.; - u(k, j, i) = 5./(3.*(gam - 1.)); - uvec(0, k, j, i) = -sin(X[2] + phase); - uvec(1, k, j, i) = sin(X[1] + phase); + u(k, j, i) = 5./(3.*(gam - 1.)) * tscale * tscale; + uvec(0, k, j, i) = -m::sin(X[2] + phase) * tscale; + uvec(1, k, j, i) = m::sin(X[1] + phase) * tscale; uvec(2, k, j, i) = 0.; - B_P(0, k, j, i) = -sin(X[2] + phase); - B_P(1, k, j, i) = sin(2.*(X[1] + phase)); - B_P(2, k, j, i) = 0.; - } - ); - // Rescale primitive velocities & B field by tscale, and internal energy by the square. - pmb->par_for("ot_renorm", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - u(k, j, i) *= tscale * tscale; - VLOOP uvec(v, k, j, i) *= tscale; - VLOOP B_P(v, k, j, i) *= tscale; } ); + if (pmb->packages.AllPackages().count("B_CT")) { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // Halo one zone right for faces + // We don't need any more than that, since curls never take d1dx1 + IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); + IndexSize3 s = KDomain::GetBlockSize(rc); + GridVector A("A", NVEC, s.n3, s.n2, s.n1); + pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real Xembed[GR_DIM]; + G.coord(k, j, i, Loci::corner, Xembed); + A(V3, k, j, i) = (-0.5*std::cos(2*Xembed[1] + phase) + + std::cos(Xembed[2] + phase)) * tscale; + } + ); + // This fills a couple zones outside the exact interior with bad data + IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); + pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_CT::curl_2D(G, A, B_Uf, k, j, i); + } + ); + B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); + double max_divb = B_CT::BlockMaxDivB(rc.get()); + std::cout << "Block max DivB: " << max_divb << std::endl; + + } else if (pmb->packages.AllPackages().count("B_FluxCT") || + pmb->packages.AllPackages().count("B_CD")) { + GridVector B_P = rc->Get("prims.B").data; + pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real X[GR_DIM]; + G.coord(k, j, i, Loci::center, X); + B_P(V1, k, j, i) = -m::sin(X[2] + phase) * tscale; + B_P(V2, k, j, i) = m::sin(2.*(X[1] + phase)) * tscale; + B_P(V3, k, j, i) = 0.; + } + ); + B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); + } + return TaskStatus::complete; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 8e438855..7a09c3dc 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -34,8 +34,11 @@ #include "post_initialize.hpp" -#include "b_field_tools.hpp" +#include "b_cd.hpp" #include "b_cleanup.hpp" +#include "b_ct.hpp" +#include "b_flux_ct.hpp" +#include "b_field_tools.hpp" #include "blob.hpp" #include "boundaries.hpp" #include "debug.hpp" @@ -48,9 +51,6 @@ #include "reductions.hpp" #include "types.hpp" -#include "seed_B_flux_ct.hpp" -#include "seed_B_cd.hpp" - /** * Perform a Parthenon MPI reduction. * Should only be used in initialization code, as the @@ -233,6 +233,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); + } else if (pkgs.count("B_CT")) { + B_CT::PrintGlobalMaxDivB(md.get()); } else if (pkgs.count("B_CD")) { //B_CD::PrintGlobalMaxDivB(md.get()); } diff --git a/kharma/types.hpp b/kharma/types.hpp index 3ff8fe8b..6a9d8692 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -53,11 +53,20 @@ using parthenon::MeshBlockData; // This provides a way of addressing vectors that matches // directions, to make derivatives etc more readable -// TODO Spammy to namespace. Keep? +// TODO is there something tricky with statics we can do here to type? #define V1 0 #define V2 1 #define V3 2 +// Let's also rename Parthenon's very specific location names to something generic & readable +using TE = parthenon::TopologicalElement; +constexpr TE F1 = TE::FX; +constexpr TE F2 = TE::FY; +constexpr TE F3 = TE::FZ; +constexpr TE E1 = TE::EYZ; +constexpr TE E2 = TE::EXZ; +constexpr TE E3 = TE::EXY; + // Struct for derived 4-vectors at a point, usually calculated and needed together typedef struct { Real ucon[GR_DIM]; @@ -67,11 +76,20 @@ typedef struct { } FourVectors; typedef struct { - IndexRange ib; - IndexRange jb; - IndexRange kb; + uint is; + uint ie; + uint js; + uint je; + uint ks; + uint ke; } IndexRange3; +typedef struct { + uint n1; + uint n2; + uint n3; +} IndexSize3; + /** * Map of the locations of particular variables in a VariablePack * Used for operations conducted over all vars which must still @@ -90,7 +108,7 @@ class VarMap { public: // Use int8. 127 values ought to be enough for anybody, right? // Basic primitive variables - int8_t RHO, UU, U1, U2, U3, B1, B2, B3; + int8_t RHO, UU, U1, U2, U3, B1, B2, B3, Bf1, Bf2, Bf3; // Tracker variables int8_t RHO_ADDED, UU_ADDED, PASSIVE; // Electron entropy/energy tracking @@ -108,6 +126,7 @@ class VarMap { U1 = name_map["cons.uvec"].first; // B B1 = name_map["cons.B"].first; + Bf1 = name_map["cons.fB"].first; PSI = name_map["cons.psi_cd"].first; // Floors RHO_ADDED = name_map["cons.rho_added"].first; @@ -130,6 +149,7 @@ class VarMap { U1 = name_map["prims.uvec"].first; // B B1 = name_map["prims.B"].first; + Bf1 = name_map["prims.fB"].first; PSI = name_map["prims.psi_cd"].first; // Floors (TODO cons only?) RHO_ADDED = name_map["prims.rho_added"].first; @@ -146,57 +166,22 @@ class VarMap { Q = name_map["prims.q"].first; DP = name_map["prims.dP"].first; } - U2 = U1 + 1; - U3 = U1 + 2; - B2 = B1 + 1; - B3 = B1 + 2; + if (U1 >= 0) { + U2 = U1 + 1; + U3 = U1 + 2; + } + if (B1 >= 0) { + B2 = B1 + 1; + B3 = B1 + 2; + } + if (Bf1 >= 0) { + Bf2 = Bf1 + 1; + Bf3 = Bf1 + 2; + } } }; -/** - * Functions for checking boundaries in 3D. - * Uses IndexRange objects, or this would be in kharma_utils.hpp - */ -KOKKOS_INLINE_FUNCTION bool outside(const int& k, const int& j, const int& i, - const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) -{ - return (i < ib.s) || (i > ib.e) || (j < jb.s) || (j > jb.e) || (k < kb.s) || (k > kb.e); -} -KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, - const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) -{ - // This is faster in the case that the point is outside - return !outside(k, j, i, kb, jb, ib); -} - -/** - * Get zones which are inside the physical domain, i.e. set by computation or MPI halo sync, - * not by problem boundary conditions. - */ -inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& bounds) -{ - using KBoundaries::IsPhysicalBoundary; - return IndexRange3{IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x1) - ? bounds.is(IndexDomain::interior) - : bounds.is(IndexDomain::entire), - IsPhysicalBoundary(pmb, BoundaryFace::outer_x1) - ? bounds.ie(IndexDomain::interior) - : bounds.ie(IndexDomain::entire)}, - IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x2) - ? bounds.js(IndexDomain::interior) - : bounds.js(IndexDomain::entire), - IsPhysicalBoundary(pmb, BoundaryFace::outer_x2) - ? bounds.je(IndexDomain::interior) - : bounds.je(IndexDomain::entire)}, - IndexRange{IsPhysicalBoundary(pmb, BoundaryFace::inner_x3) - ? bounds.ks(IndexDomain::interior) - : bounds.ks(IndexDomain::entire), - IsPhysicalBoundary(pmb, BoundaryFace::outer_x3) - ? bounds.ke(IndexDomain::interior) - : bounds.ke(IndexDomain::entire)}}; -} - #if DEBUG /** * Function to generate outputs wherever, whenever. @@ -204,8 +189,8 @@ inline IndexRange3 GetPhysicalZones(std::shared_ptr pmb, IndexShape& inline void OutputNow(Mesh *pmesh, std::string name) { auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); + ParameterInput *pin = pmesh->packages.Get("Globals")->Param("pin"); auto pouts = std::make_unique(pmesh, pin, &tm); - auto pin = pmesh->packages.Get("Globals")->Param("pin"); pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); // TODO: find most recently written "now" files and move them to "name" } diff --git a/pars/orszag_tang_new.par b/pars/orszag_tang_new.par new file mode 100644 index 00000000..9f99b53e --- /dev/null +++ b/pars/orszag_tang_new.par @@ -0,0 +1,67 @@ +# Orszag-Tang Vortex problem: +# Generate current sheets on short timescales + + +problem_id = orszag_tang + + +nx1 = 512 +x1min = -3.141592653589793 +x1max = 3.141592653589793 + +nx2 = 512 +x2min = -3.141592653589793 +x2max = 3.141592653589793 + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 + + +nx1 = 512 +nx2 = 512 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 100.0 +integrator = rk1 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +solver = face_ct +kill_on_large_divb = false + + +# B transport experiments: clean every 10 steps +on = false +cleanup_interval = 10 + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 1.0 +single_precision_output = true +# TODO just prims when face fields supported +variables = prims.rho, prims.u, prims.uvec, prims.B, divB, jcon + + +file_type = hst +dt = 0.1 + +# This problem is generally much too short to need +# checkpointing. However, we have a test which uses it. +# +#file_type = rst +#dt = 10.0 diff --git a/run.sh b/run.sh index 9b3f81a9..fa15df5d 100755 --- a/run.sh +++ b/run.sh @@ -7,10 +7,6 @@ # -nt (number of OpenMP threads) # Note these options must be FIRST and IN ORDER! -# Optionally use the Kokkos tools to profile kernels -#export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so -#export KOKKOS_PROFILE_LIBRARY=$KHARMA_DIR/../kokkos-tools/kp_nvprof_cnnector.so - # Default MPI parameters: don't use MPI or run with 1 process MPI_EXE=${MPI_EXE:-} MPI_NUM_PROCS=${MPI_NUM_PROCS:-1} @@ -45,6 +41,11 @@ else exit fi +# Optionally use the Kokkos tools to profile kernels +#export KOKKOS_TOOLS_LIBS=$KHARMA_DIR/../kokkos-tools/kp_kernel_timer.so +#export KOKKOS_TOOLS_LIBS=$KHARMA_DIR/../kokkos-tools/kp_nvprof_cnnector.so +#export KOKKOS_TOOLS_LIBS=$KHARMA_DIR/../kokkos-tools/kp_kernel_logger.so + # Load environment from the same files as the compile process HOST=$(hostname -f) ARGS=$(cat $KHARMA_DIR/make_args) From ba4a46ba692968eb08344fe4c766e1a154475e6f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 31 May 2023 17:48:42 -0500 Subject: [PATCH 082/219] Working B&S'99, S&G'09 implementations on bulk grid. Still has problems with boundaries. --- external/parthenon | 2 +- kharma/b_ct/b_ct.cpp | 209 ++++++++++++++++++++++++--------- kharma/b_ct/b_ct.hpp | 44 ++++++- kharma/coordinates/matrix.hpp | 31 +++-- kharma/decs.hpp | 2 + kharma/driver/kharma_step.cpp | 15 ++- kharma/flux/flux.cpp | 9 +- kharma/flux/get_flux.hpp | 47 ++++++-- kharma/flux/reconstruction.hpp | 48 ++++++++ kharma/inverter/onedw.hpp | 6 +- kharma/types.hpp | 5 + pars/mhdmodes.par | 2 +- pars/orszag_tang.par | 2 +- pars/orszag_tang_new.par | 13 +- 14 files changed, 339 insertions(+), 96 deletions(-) diff --git a/external/parthenon b/external/parthenon index f80cdce7..00fadd65 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit f80cdce71dbf35cd463b0947f6d4e3f7e50ea088 +Subproject commit 00fadd65b649ff57a99bd766ea8819996be1b69f diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 49ff00e5..4ad25973 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -37,6 +37,8 @@ #include "domain.hpp" #include "grmhd.hpp" #include "kharma.hpp" +// TODO eliminate sync +#include "kharma_driver.hpp" #include @@ -62,7 +64,10 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared Real kill_on_divb_over = pin->GetOrAddReal("b_field", "kill_on_divb_over", 1.e-3); params.Add("kill_on_divb_over", kill_on_divb_over); - // TODO selector BS/LDZ04/LDZ07/GS + // Currently bs99, sg09 + // TODO LDZ04, LDZ07, other GS? + std::string ct_scheme = pin->GetOrAddString("b_field", "ct_scheme", "sg09"); + params.Add("ct_scheme", ct_scheme); // Add a reducer for divB to params params.Add("divb_reducer", AllReduce()); @@ -74,11 +79,10 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // Flags for B fields on faces. // We don't mark these as "Primitive" and "Conserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want - // TODO this won't apply in ghosts, probably... if so we'll need to bundle only ::Cell in lots of places std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::GetUserFlag("Explicit")}; std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, - Metadata::GetUserFlag("Explicit")}; // TODO TODO Restart, FillGhost + Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; // TODO TODO Restart auto m = Metadata(flags_prim_f); pkg->AddField("prims.fB", m); m = Metadata(flags_cons_f); @@ -97,16 +101,22 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared pkg->AddField("cons.B", m); // EMF on edges. - // TODO TODO ADD Metadata::FillGhost - std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy}; + // TODO only sync when needed + std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}; m = Metadata(flags_emf); pkg->AddField("B_CT.emf", m); + if (ct_scheme == "sg09") { + std::vector flags_emf_c = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; + m = Metadata(flags_emf_c, s_vector); + pkg->AddField("B_CT.cemf", m); + } + // CALLBACKS // We implement a source term replacement, rather than addition, // but same difference, really - pkg->AddSource = B_CT::AddSource; + //pkg->AddSource = B_CT::AddSource; // Also ensure that prims get filled, both during step and on boundaries //pkg->MeshUtoP = B_CT::MeshUtoP; @@ -192,63 +202,160 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) // TODO this isn't really a source... it's a replacement of the // face-centered fields according to constrained transport rules -void B_CT::AddSource(MeshData *md, MeshData *mdudt) +TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_ptr>& mdudt) { auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; - // This is what we're replacing - auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); - // EMF temporary auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 1); - const IndexRange block = IndexRange{0, dB_Uf_dt.GetDim(5)-1}; + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 2); + const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); - // Calculate circulation by averaging fluxes (Balsara & Spicer) - auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); - pmb0->par_for("B_CT_emf_BS", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // TODO will we need gdet/cell length here? - const auto& G = B_U.GetCoords(bl); - if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = - 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i) + B_U(bl).flux(X2DIR, V3, k, j, i) - - B_U(bl).flux(X3DIR, V2, k, j - 1, i) - B_U(bl).flux(X3DIR, V2, k, j, i)); - emf_pack(bl, E2, 0, k, j, i) = - 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1) + B_U(bl).flux(X3DIR, V1, k, j, i) - - B_U(bl).flux(X1DIR, V3, k - 1, j, i) - B_U(bl).flux(X1DIR, V3, k, j, i)); + std::string scheme = pmesh->packages.Get("B_CT")->Param("ct_scheme"); + if (scheme == "bs99") { + // Calculate circulation by averaging fluxes (BS88) + auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); + pmb0->par_for("B_CT_emf_BS", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // TODO will we need gdet/cell length here? + const auto& G = B_U.GetCoords(bl); + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) = + 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) + - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)); + emf_pack(bl, E2, 0, k, j, i) = + 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) + - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)); + } + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)); + } + ); + } else if (scheme == "sg09") { + // Average fluxes and derivatives (SG09) + auto& uvec = md->PackVariables(std::vector{"prims.uvec"}); + auto& emfc = md->PackVariables(std::vector{"B_CT.cemf"}); + auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); + auto& B_P = md->PackVariables(std::vector{"prims.B"}); + // emf in center == -v x B + pmb0->par_for("B_CT_emf_GS09", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + VLOOP emfc(bl, v, k, j, i) = 0.; + VLOOP3 emfc(bl, x, k, j, i) -= antisym(v, w, x) * uvec(bl, v, k, j, i) * B_U(bl, w, k, j, i); } - emf_pack(bl, E3, 0, k, j, i) = - 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) - - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); + ); + + // Get primitive velocity at face (on right side) (TODO do we need some average?) + auto& uvecf = md->PackVariables(std::vector{"Flux.vr"}); + + pmb0->par_for("B_CT_emf_GS09", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // TODO will we need gdet/cell length here? + const auto& G = B_U.GetCoords(bl); + + // "simple" flux + upwinding method, Stone & Gardiner '09 but also in Stone+08 etc. + // Upwinded differences take in order (1-indexed): + // 1. EMF component direction to calculate + // 2. Direction of derivative + // 3. Direction of upwinding + // ...then zone number... + // and finally, a boolean indicating a leftward (e.g., i-3/4) vs rightward (i-1/4) position + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) = + 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) + - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); + emf_pack(bl, E2, 0, k, j, i) = + 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) + - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, true)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); + } + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, true)) + + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, true)); + } + ); + } else { + throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, sg09"); + } + + // Parthenon needs a shared_ptr object, but it can be any one... + static std::shared_ptr> my_md(md); + KHARMADriver::SyncAllBounds(my_md, true); + pmb0->par_for("B_CT_Edge1s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.is, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i-1))/2; + emf_pack(bl, E3, 0, k, j, i-1) = emf_pack(bl, E3, 0, k, j, i-2); + } + ); + pmb0->par_for("B_CT_Edge1e", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.ie, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i+1))/2; + emf_pack(bl, E3, 0, k, j, i+1) = emf_pack(bl, E3, 0, k, j, i+2); + } + ); + pmb0->par_for("B_CT_Edge2s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.js, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j-1, i))/2; + emf_pack(bl, E3, 0, k, j-1, i) = emf_pack(bl, E3, 0, k, j-2, i); + } + ); + pmb0->par_for("B_CT_Edge2e", block.s, block.e, b1.ks, b1.ke, b1.je, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j+1, i))/2; + emf_pack(bl, E3, 0, k, j+1, i) = emf_pack(bl, E3, 0, k, j+2, i); } ); - // TODO LDZ04, LDZ07, GS? - + // This is what we're replacing + auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); // Circulation -> change in flux at face // Note we *replace* whatever this term in the source term was "supposed" to be - // TODO stick to defined faces? Or don't bother? - pmb0->par_for("B_CT_Circ", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, + pmb0->par_for("B_CT_Circ_1", block.s, block.e, b.ks, b.ke, b.js, b.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); dB_Uf_dt(bl, F1, 0, k, j, i) = emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i); - dB_Uf_dt(bl, F2, 0, k, j, i) = -emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i); - dB_Uf_dt(bl, F3, 0, k, j, i) = 0.; if (ndim > 2) { dB_Uf_dt(bl, F1, 0, k, j, i) += -emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i); + } + } + ); + pmb0->par_for("B_CT_Circ_2", block.s, block.e, b.ks, b.ke, b1.js, b1.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = dB_Uf_dt.GetCoords(bl); + dB_Uf_dt(bl, F2, 0, k, j, i) = -emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i); + if (ndim > 2) { dB_Uf_dt(bl, F2, 0, k, j, i) += emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i); - dB_Uf_dt(bl, F3, 0, k, j, i) += emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i) - - emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i); } - } ); + if (ndim > 2) { + pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = dB_Uf_dt.GetCoords(bl); + dB_Uf_dt(bl, F3, 0, k, j, i) += emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i) + - emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i); + } + ); + } + return TaskStatus::complete; } @@ -261,12 +368,9 @@ double B_CT::MaxDivB(MeshData *md) auto B_U = md->PackVariables(std::vector{"cons.fB"}); // Figure out indices - const IndexRange ibl = md->GetBoundsI(IndexDomain::interior); - const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); - const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; - const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; - const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange ib = md->GetBoundsI(IndexDomain::interior); + const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kb = md->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); @@ -290,7 +394,7 @@ double B_CT::BlockMaxDivB(MeshBlockData *rc) auto B_U = rc->PackVariables(std::vector{"cons.fB"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(rc, IndexDomain::interior, 0, 1); + const IndexRange3 b = KDomain::GetRange(rc, IndexDomain::interior); auto pmb = rc->GetBlockPointer(); @@ -349,12 +453,9 @@ void B_CT::CalcDivB(MeshData *md, std::string divb_field_name) auto B_U = md->PackVariables(std::vector{"cons.fB"}); auto divB = md->PackVariables(std::vector{divb_field_name}); - const IndexRange ibl = md->GetBoundsI(IndexDomain::interior); - const IndexRange jbl = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kbl = md->GetBoundsK(IndexDomain::interior); - const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; - const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; - const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange ib = md->GetBoundsI(IndexDomain::interior); + const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kb = md->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); @@ -377,13 +478,9 @@ void B_CT::FillOutput(MeshBlock *pmb, ParameterInput *pin) auto B_U = rc->PackVariables(std::vector{"cons.fB"}); auto divB = rc->PackVariables(std::vector{"divB"}); - const IndexRange ibl = rc->GetBoundsI(IndexDomain::interior); - const IndexRange jbl = rc->GetBoundsJ(IndexDomain::interior); - const IndexRange kbl = rc->GetBoundsK(IndexDomain::interior); - - const IndexRange ib = IndexRange{ibl.s, ibl.e + 1}; - const IndexRange jb = IndexRange{jbl.s, jbl.e + (ndim > 1)}; - const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; + const IndexRange ib = rc->GetBoundsI(IndexDomain::interior); + const IndexRange jb = rc->GetBoundsJ(IndexDomain::interior); + const IndexRange kb = rc->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; pmb->par_for("divB_output", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index c472bd00..b5b51dbb 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -35,6 +35,7 @@ #include "decs.hpp" #include "grmhd_functions.hpp" +#include "matrix.hpp" #include "reductions.hpp" #include "types.hpp" @@ -81,7 +82,7 @@ void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); * Replace conserved face B field components with versions calculated * by constrained transport. */ -void AddSource(MeshData *md, MeshData *mdudt); +TaskStatus UpdateFaces(std::shared_ptr>& md, std::shared_ptr>& mdudt); // TODO UNIFY ALL THE FOLLOWING @@ -184,4 +185,45 @@ KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, B_U(F3, 0, k, j, i) = 0.; } +KOKKOS_INLINE_FUNCTION Real upwind_diff(const VariableFluxPack& B_U, const VariablePack& emfc, const VariablePack& uvec, + const int& comp, const int& dir, const int& vdir, + const int& k, const int& j, const int& i, const bool& left_deriv) +{ + // See SG09 eq 23 + // Upwind based on vel(vdir) at the left face in vdir (contact mode) + TopologicalElement face = FaceOf(vdir); + const Real contact_vel = uvec(face, vdir-1, k, j, i); + // Upwind by one zone in dir + const int i_up = (vdir == 1) ? i - 1 : i; + const int j_up = (vdir == 2) ? j - 1 : j; + const int k_up = (vdir == 3) ? k - 1 : k; + // Sign for transforming the flux to EMF, based on directions + const int emf_sign = antisym(comp-1, dir-1, vdir-1); + + // If we're actually taking the derivative at -3/4, back up which center we use, + // and reverse the overall sign + const int i_cent = (left_deriv && dir == 1) ? i - 1 : i; + const int j_cent = (left_deriv && dir == 2) ? j - 1 : j; + const int k_cent = (left_deriv && dir == 3) ? k - 1 : k; + const int i_cent_up = (left_deriv && dir == 1) ? i_up - 1 : i_up; + const int j_cent_up = (left_deriv && dir == 2) ? j_up - 1 : j_up; + const int k_cent_up = (left_deriv && dir == 3) ? k_up - 1 : k_up; + const int return_sign = (left_deriv) ? -1 : 1; + + + // TODO calculate offsets once somehow? + + if (contact_vel > 0) { + // Forward: difference at i + return return_sign * (emfc(0, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i)); + } else if (contact_vel < 0) { + // Back: twice difference at i-1 + return return_sign * (emfc(0, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); + } else { + // Half and half + return return_sign*0.5*(emfc(0, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i) + + emfc(0, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); + } +} + } diff --git a/kharma/coordinates/matrix.hpp b/kharma/coordinates/matrix.hpp index ebdce0ea..465b01e2 100644 --- a/kharma/coordinates/matrix.hpp +++ b/kharma/coordinates/matrix.hpp @@ -109,16 +109,16 @@ KOKKOS_INLINE_FUNCTION Real invert(const Real *m, Real *invOut) /** * Parity calculation. - * Due to Norm Hardy; in principle good for general n, - * but in practice specified for speed/compiler + * Due to Norm Hardy; good for general n */ -KOKKOS_INLINE_FUNCTION int pp(int P[4]) +template +KOKKOS_INLINE_FUNCTION int pp(int P[n]) { int x; int p = 0; - int v[4] = {0}; + int v[n] = {0}; - for (int j = 0; j < 4; j++) { + for (int j = 0; j < n; j++) { if (v[j]) { p++; } else { @@ -140,12 +140,6 @@ KOKKOS_INLINE_FUNCTION int pp(int P[4]) // Completely antisymmetric 4D symbol KOKKOS_INLINE_FUNCTION int antisym(int a, int b, int c, int d) { - // Check for valid permutation - if (a < 0 || a > 3) return 100; - if (b < 0 || b > 3) return 100; - if (c < 0 || c > 3) return 100; - if (d < 0 || d > 3) return 100; - // Entries different? if (a == b) return 0; if (a == c) return 0; @@ -157,5 +151,18 @@ KOKKOS_INLINE_FUNCTION int antisym(int a, int b, int c, int d) // Determine parity of permutation int p[4] = {a, b, c, d}; - return pp(p); + return pp<4>(p); +} + +KOKKOS_INLINE_FUNCTION int antisym(int a, int b, int c) +{ + // Entries different? + if (a == b) return 0; + if (a == c) return 0; + if (b == c) return 0; + + // Determine parity of permutation + int p[3] = {a, b, c}; + + return pp<3>(p); } diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 479a77bb..8048b2f2 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -94,6 +94,8 @@ using GReal = double; #define NVEC 3 #define VLOOP for(int v = 0; v < NVEC; ++v) +#define VLOOP2 VLOOP for(int w = 0; w < NVEC; ++w) +#define VLOOP3 VLOOP2 for(int x = 0; x < NVEC; ++x) // Useful enum to avoid lots of #defines // See following functions and coord() in gr_coordinates.hpp to diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index f88556a9..8ab8eb56 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -38,6 +38,7 @@ #include "b_flux_ct.hpp" #include "b_cd.hpp" #include "b_cleanup.hpp" +#include "b_ct.hpp" #include "electrons.hpp" #include "grmhd.hpp" #include "wind.hpp" @@ -130,7 +131,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // Start receiving flux corrections and ghost cells auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; - if (pmesh->multilevel) + if (pmesh->multilevel || use_b_ct) t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face @@ -141,14 +142,14 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; - if (pmesh->multilevel) { + if (pmesh->multilevel || use_b_ct) { tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: - // 1. CT calculations for B field transport + // 1. Flux-CT calculations for B field transport // 2. Zero fluxes through poles // etc auto t_fix_flux = tl.AddTask(t_flux_bounds, Packages::FixFlux, md_sub_step_init.get()); @@ -159,10 +160,16 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // Add any source terms: geometric \Gamma * T, wind, damping, etc etc auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); + // CT Update step (needs another boundary sync) + auto t_ct_update = t_sources; + if (use_b_ct) { + t_ct_update = tl.AddTask(t_sources, B_CT::UpdateFaces, md_sub_step_init, md_flux_src); + } + // Perform the update using the source term // Add any proportion of the step start required by the integrator (e.g., RK2) // TODO splitting this is stupid, dig into Parthenon & fix - auto t_avg_data_c = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + auto t_avg_data_c = tl.AddTask(t_ct_update, Update::WeightedSumData, MeshData>, std::vector({Metadata::Independent, Metadata::Cell}), md_sub_step_init.get(), md_full_step_init.get(), integrator->gam0[stage-1], integrator->gam1[stage-1], diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 5f080d2f..74b9a7c7 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -53,6 +53,7 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared int nvar = KHARMA::CountVars(packages.get(), Metadata::WithFluxes); std::cout << "Allocating fluxes with nvar: " << nvar << std::endl; std::vector s_flux({nvar}); + // TODO optionally move all these to faces? Not important yet, no output, more memory std::vector flags_flux = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; Metadata m = Metadata(flags_flux, s_flux); pkg->AddField("Flux.Pr", m); @@ -62,15 +63,17 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared pkg->AddField("Flux.Fr", m); pkg->AddField("Flux.Fl", m); - // TODO move to faces? Not important for these quantities as caches + // TODO could formally move this to face std::vector s_vector({NVEC}); std::vector flags_speed = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_speed, s_vector); pkg->AddField("Flux.cmax", m); pkg->AddField("Flux.cmin", m); - // Velocities, for upwinded constrained transport - // TODO can be 2-length someday if we want to get spicy + + // Preserve all velocities at faces, for upwinded constrained transport if (packages->AllPackages().count("B_CT")) { + std::vector flags_vel = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::OneCopy}; + m = Metadata(flags_vel, s_vector); pkg->AddField("Flux.vr", m); pkg->AddField("Flux.vl", m); } diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 1885af5b..534c27c4 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -63,6 +63,7 @@ inline TaskStatus GetFlux(MeshData *md) // Pointers auto pmesh = md->GetMeshPointer(); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + auto& packages = pmb0->packages; // Exit on trivial operations const int ndim = pmesh->ndim; if (ndim < 3 && dir == X3DIR) return TaskStatus::complete; @@ -71,19 +72,20 @@ inline TaskStatus GetFlux(MeshData *md) Flag("GetFlux_"+std::to_string(dir)); // Options - const auto& pars = pmb0->packages.Get("Driver")->AllParams(); - const auto& mhd_pars = pmb0->packages.Get("GRMHD")->AllParams(); - const auto& globals = pmb0->packages.Get("Globals")->AllParams(); + const auto& pars = packages.Get("Driver")->AllParams(); + const auto& mhd_pars = packages.Get("GRMHD")->AllParams(); + const auto& globals = packages.Get("Globals")->AllParams(); const bool use_hlle = pars.Get("use_hlle"); - const bool reconstruction_floors = pmb0->packages.AllPackages().count("Floors") && + // TODO make this an option in Flux package + const bool reconstruction_floors = packages.AllPackages().count("Floors") && (Recon == KReconstruction::Type::weno5); Floors::Prescription floors_temp; if (reconstruction_floors) { // Apply post-reconstruction floors. // Only enabled for WENO since it is not TVD, and only when other // floors are enabled. - const auto& floor_pars = pmb0->packages.Get("Floors")->AllParams(); + const auto& floor_pars = packages.Get("Floors")->AllParams(); // Pull out a struct of just the actual floor values for speed floors_temp = Floors::Prescription(floor_pars); } @@ -93,10 +95,10 @@ inline TaskStatus GetFlux(MeshData *md) // Check whether we're using constraint-damping // (which requires that a variable be propagated at ctop_max) - const bool use_b_cd = pmb0->packages.AllPackages().count("B_CD"); - const double ctop_max = (use_b_cd) ? pmb0->packages.Get("B_CD")->Param("ctop_max_last") : 0.0; + const bool use_b_cd = packages.AllPackages().count("B_CD"); + const double ctop_max = (use_b_cd) ? packages.Get("B_CD")->Param("ctop_max_last") : 0.0; - const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb0->packages); + const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(packages); const Loci loc = loc_of(dir); @@ -180,6 +182,20 @@ inline TaskStatus GetFlux(MeshData *md) ); EndFlag(); + // If we have B field on faces, we must replace reconstructed version with that + if (pmb0->packages.AllPackages().count("B_CT")) { // TODO if variable "cons.fB"? + const auto& Bf = md->PackVariables(std::vector{"cons.fB"}); + const TopologicalElement face = (dir == 1) ? F1 : ((dir == 2) ? F2 : F3); + pmb0->par_for("replace_face", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int& bl, const int& k, const int& j, const int& i) { + const auto& G = U_all.GetCoords(bl); + const double bf = Bf(bl, face, 0, k, j, i) / G.gdet(loc, j, i); + Pl_all(bl, m_p.B1+dir-1, k, j, i) = bf; + Pr_all(bl, m_p.B1+dir-1, k, j, i) = bf; + } + ); + } + Flag("GetFlux_"+std::to_string(dir)+"_left"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_left", pmb0->exec_space, flux_scratch_bytes, scratch_level, block.s, block.e, b.ks, b.ke, b.js, b.je, @@ -319,6 +335,21 @@ inline TaskStatus GetFlux(MeshData *md) } EndFlag(); + // Save the face velocities for upwinding/CT later + if (packages.AllPackages().count("B_CT")) { + Flag("GetFlux_"+std::to_string(dir)+"_store_vel"); + const auto& vl_all = md->PackVariables(std::vector{"Flux.vl"}); + const auto& vr_all = md->PackVariables(std::vector{"Flux.vr"}); + TopologicalElement face = (dir == 1) ? F1 : (dir == 2) ? F2 : F3; + pmb0->par_for("flux_llf", block.s, block.e, 0, NVEC-1, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int& bl, const int& v, const int& k, const int& j, const int& i) { + vl_all(bl, face, v, k, j, i) = Pl_all(bl, m_p.U1+v, k, j, i); + vr_all(bl, face, v, k, j, i) = Pr_all(bl, m_p.U1+v, k, j, i); + } + ); + EndFlag(); + } + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/flux/reconstruction.hpp b/kharma/flux/reconstruction.hpp index 1d97cfe5..990bd146 100644 --- a/kharma/flux/reconstruction.hpp +++ b/kharma/flux/reconstruction.hpp @@ -354,6 +354,54 @@ KOKKOS_INLINE_FUNCTION void WENO5X3r(parthenon::team_mbr_t const &member, const } } +/** + * Parablic reconstruction, see Collela & Woodward '84 + * + * Adapted from iharm2d implementation, + * originally written by Xiaoyue Guan + */ +KOKKOS_INLINE_FUNCTION void para(const Real& x1, const Real& x2, const Real& x3, const Real& x4, const Real& x5, + Real& lout, Real& rout) +{ + Real y[5], dq[5]; // TODO(BSP) can these be eliminated easily? + + y[0]=x1; + y[1]=x2; + y[2]=x3; + y[3]=x4; + y[4]=x5; + + // CW 1.7 + for (int i=1; i <= 3; i++) { + const Real Dqm = 2*(y[i] - y[i-1]); + const Real Dqp = 2*(y[i+1] - y[i]); + if (Dqm*Dqp <= 0.) { + dq[i] = 0.; // CW1.8 + } else { + const Real Dqc = 0.5*(y[i+1] - y[i-1]); + dq[i] = m::copysign(m::min(m::abs(Dqc), m::min(m::abs(Dqm), m::abs(Dqp))), Dqc); + } + } + + // CW 1.6 + lout = 0.5*(y[2] + y[1]) - (1./6.)*(dq[2] - dq[1]); + rout = 0.5*(y[3] + y[2]) - (1./6.)*(dq[3] - dq[2]); + + // CW 1.10 + if (((rout - y[2]) * (y[2] - lout)) <= 0.) { + lout = y[2]; + rout = y[2]; + } + const Real qd = (rout - lout); + const Real qe = 6*(y[2] - 0.5*(lout + rout)); + if (qd * (qd - qe) < 0.) { + lout = 3*y[2] - 2*rout; + } else if (qd * (qd + qe) < 0.) { + rout = 3*y[2] - 2*lout; + } +} + + /** * Templated calls to different reconstruction algorithms * This is basically a compile-time 'if' or 'switch' statement, where all the options get generated diff --git a/kharma/inverter/onedw.hpp b/kharma/inverter/onedw.hpp index 181e85eb..da75f115 100644 --- a/kharma/inverter/onedw.hpp +++ b/kharma/inverter/onedw.hpp @@ -100,9 +100,9 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const const VariablePack& P, const VarMap& m_p, const Loci loc) { - if (i == 10 && j == 11) - printf("CONS: %g %g %g %g %g %g %g %g", U(m_u.RHO, k, j, i), U(m_u.UU, k, j, i), U(m_u.U1, k, j, i), U(m_u.U2, k, j, i), - U(m_u.U3, k, j, i), U(m_u.B1, k, j, i), U(m_u.B2, k, j, i), U(m_u.B3, k, j, i)); + // if (i == 10 && j == 11) + // printf("CONS: %g %g %g %g %g %g %g %g", U(m_u.RHO, k, j, i), U(m_u.UU, k, j, i), U(m_u.U1, k, j, i), U(m_u.U2, k, j, i), + // U(m_u.U3, k, j, i), U(m_u.B1, k, j, i), U(m_u.B2, k, j, i), U(m_u.B3, k, j, i)); // Catch negative density if (U(m_u.RHO, k, j, i) <= 0.) { return Status::neg_input; diff --git a/kharma/types.hpp b/kharma/types.hpp index 6a9d8692..1299c46b 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -67,6 +67,11 @@ constexpr TE E1 = TE::EYZ; constexpr TE E2 = TE::EXZ; constexpr TE E3 = TE::EXY; +// Any basic type manips, see LocOf in decs etc etc +KOKKOS_INLINE_FUNCTION TopologicalElement FaceOf(const int& dir) { + return (dir == 1) ? F1 : (dir == 2) ? F2 : F3; +} + // Struct for derived 4-vectors at a point, usually calculated and needed together typedef struct { Real ucon[GR_DIM]; diff --git a/pars/mhdmodes.par b/pars/mhdmodes.par index 0703cb42..02e01285 100644 --- a/pars/mhdmodes.par +++ b/pars/mhdmodes.par @@ -103,7 +103,7 @@ reconstruction = weno5 file_type = hdf5 # This is so as to output only the final state -dt = 0.01 +dt = 0.5 single_precision_output = true variables = prims.rho, prims.u, prims.uvec, prims.B diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index aeb52dc1..832f6df8 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -42,7 +42,7 @@ integrator = rk2 cfl = 0.9 gamma = 1.666667 -reconstruction = weno5 +reconstruction = linear_mc verbose = 1 diff --git a/pars/orszag_tang_new.par b/pars/orszag_tang_new.par index 9f99b53e..e1dd5d1e 100644 --- a/pars/orszag_tang_new.par +++ b/pars/orszag_tang_new.par @@ -28,7 +28,7 @@ transform = null tlim = 100.0 -integrator = rk1 +integrator = rk2 cfl = 0.9 @@ -38,23 +38,24 @@ reconstruction = weno5 solver = face_ct kill_on_large_divb = false - - -# B transport experiments: clean every 10 steps -on = false -cleanup_interval = 10 +#ct_scheme = bs99 +ct_scheme = sg09 verbose = 1 flag_verbose = 2 extra_checks = 1 + +disable_floors = true + file_type = hdf5 dt = 1.0 single_precision_output = true # TODO just prims when face fields supported variables = prims.rho, prims.u, prims.uvec, prims.B, divB, jcon +ghost_zones = true file_type = hst From 039ca2ed23ceb2b00fd5c2e9daf5119c48c6ce28 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 2 Jun 2023 11:06:41 -0500 Subject: [PATCH 083/219] Update to CT branch as accepted/non-WIP. Works! --- .gitmodules | 2 +- external/parthenon | 2 +- kharma/b_ct/b_ct.cpp | 56 ++++++++++++++++---------------- kharma/boundaries/boundaries.cpp | 2 +- kharma/boundaries/dirichlet.cpp | 6 ++-- kharma/types.hpp | 16 +++++---- 6 files changed, 44 insertions(+), 40 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4f3484cc..d5ec6b1b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "external/parthenon"] path = external/parthenon url = https://github.com/parthenon-hpc-lab/parthenon.git - branch = bprather/reqs-for-ct + branch = bprather/backport-bicgstab [submodule "external/variant"] path = external/variant url = https://github.com/mpark/variant.git diff --git a/external/parthenon b/external/parthenon index 00fadd65..0acb67a8 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 00fadd65b649ff57a99bd766ea8819996be1b69f +Subproject commit 0acb67a8f63cc319d9b1514b4226fcfbb625d49d diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 4ad25973..3a01e844 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -211,8 +211,8 @@ TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_pt auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 1); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 2); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); @@ -297,32 +297,32 @@ TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_pt } // Parthenon needs a shared_ptr object, but it can be any one... - static std::shared_ptr> my_md(md); - KHARMADriver::SyncAllBounds(my_md, true); - pmb0->par_for("B_CT_Edge1s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.is, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i-1))/2; - emf_pack(bl, E3, 0, k, j, i-1) = emf_pack(bl, E3, 0, k, j, i-2); - } - ); - pmb0->par_for("B_CT_Edge1e", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.ie, b1.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i+1))/2; - emf_pack(bl, E3, 0, k, j, i+1) = emf_pack(bl, E3, 0, k, j, i+2); - } - ); - pmb0->par_for("B_CT_Edge2s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.js, b1.is, b1.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j-1, i))/2; - emf_pack(bl, E3, 0, k, j-1, i) = emf_pack(bl, E3, 0, k, j-2, i); - } - ); - pmb0->par_for("B_CT_Edge2e", block.s, block.e, b1.ks, b1.ke, b1.je, b1.je, b1.is, b1.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j+1, i))/2; - emf_pack(bl, E3, 0, k, j+1, i) = emf_pack(bl, E3, 0, k, j+2, i); - } - ); + // static std::shared_ptr> my_md(md); + // KHARMADriver::SyncAllBounds(my_md, true); + // pmb0->par_for("B_CT_Edge1s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.is, + // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i-1))/2; + // emf_pack(bl, E3, 0, k, j, i-1) = emf_pack(bl, E3, 0, k, j, i-2); + // } + // ); + // pmb0->par_for("B_CT_Edge1e", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.ie, b1.ie, + // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i+1))/2; + // emf_pack(bl, E3, 0, k, j, i+1) = emf_pack(bl, E3, 0, k, j, i+2); + // } + // ); + // pmb0->par_for("B_CT_Edge2s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.js, b1.is, b1.ie, + // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j-1, i))/2; + // emf_pack(bl, E3, 0, k, j-1, i) = emf_pack(bl, E3, 0, k, j-2, i); + // } + // ); + // pmb0->par_for("B_CT_Edge2e", block.s, block.e, b1.ks, b1.ke, b1.je, b1.je, b1.is, b1.ie, + // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j+1, i))/2; + // emf_pack(bl, E3, 0, k, j+1, i) = emf_pack(bl, E3, 0, k, j+2, i); + // } + // ); // This is what we're replacing auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 52f9eff8..08890bf1 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -267,7 +267,7 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom // Inflow check // Iterate over zones w/p=0 pmb->par_for_bndry( - "Outflow_check_inflow", IndexRange{0, 0}, domain, coarse, + "Outflow_check_inflow", IndexRange{0, 0}, domain, CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 56858b03..1898ac2d 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -34,6 +34,8 @@ #include "dirichlet.hpp" +#include "types.hpp" + #include using namespace parthenon; @@ -74,7 +76,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda // printf("Freezing bounds:\n"); const auto domain = BoundaryDomain(bface); pmb->par_for_bndry( - "dirichlet_boundary", vars, domain, coarse, + "dirichlet_boundary", vars, domain, CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { if (right) { q(p, k, j, i) = bound(p, k - ke, j - je, i - ie); @@ -157,7 +159,7 @@ void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain const auto &G = pmb->coords; pmb->par_for_bndry( - "dirichlet_boundary", vars, domain, coarse, + "dirichlet_boundary", vars, domain, CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { if (right) { bound(p, k - ke, j - je, i - ie) = q(p, k, j, i); diff --git a/kharma/types.hpp b/kharma/types.hpp index 1299c46b..adeee780 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -58,14 +58,16 @@ using parthenon::MeshBlockData; #define V2 1 #define V3 2 -// Let's also rename Parthenon's very specific location names to something generic & readable +// Pull TopologicalElements out to match the above using TE = parthenon::TopologicalElement; -constexpr TE F1 = TE::FX; -constexpr TE F2 = TE::FY; -constexpr TE F3 = TE::FZ; -constexpr TE E1 = TE::EYZ; -constexpr TE E2 = TE::EXZ; -constexpr TE E3 = TE::EXY; +constexpr TE CC = TE::CC; +constexpr TE F1 = TE::F1; +constexpr TE F2 = TE::F2; +constexpr TE F3 = TE::F3; +constexpr TE E1 = TE::E1; +constexpr TE E2 = TE::E2; +constexpr TE E3 = TE::E3; +constexpr TE NN = TE::NN; // Any basic type manips, see LocOf in decs etc etc KOKKOS_INLINE_FUNCTION TopologicalElement FaceOf(const int& dir) { From 7ff4a2eef438c8c70651ef9e5604e0462be56c05 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 2 Jun 2023 12:06:58 -0500 Subject: [PATCH 084/219] Remove unnecessary sync code, updates to face CT test problem --- kharma/b_ct/b_ct.cpp | 28 ---------------------------- pars/orszag_tang_new.par | 9 ++++++--- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 3a01e844..2b91ed72 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -296,34 +296,6 @@ TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_pt throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, sg09"); } - // Parthenon needs a shared_ptr object, but it can be any one... - // static std::shared_ptr> my_md(md); - // KHARMADriver::SyncAllBounds(my_md, true); - // pmb0->par_for("B_CT_Edge1s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.is, - // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i-1))/2; - // emf_pack(bl, E3, 0, k, j, i-1) = emf_pack(bl, E3, 0, k, j, i-2); - // } - // ); - // pmb0->par_for("B_CT_Edge1e", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.ie, b1.ie, - // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j, i+1))/2; - // emf_pack(bl, E3, 0, k, j, i+1) = emf_pack(bl, E3, 0, k, j, i+2); - // } - // ); - // pmb0->par_for("B_CT_Edge2s", block.s, block.e, b1.ks, b1.ke, b1.js, b1.js, b1.is, b1.ie, - // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j-1, i))/2; - // emf_pack(bl, E3, 0, k, j-1, i) = emf_pack(bl, E3, 0, k, j-2, i); - // } - // ); - // pmb0->par_for("B_CT_Edge2e", block.s, block.e, b1.ks, b1.ke, b1.je, b1.je, b1.is, b1.ie, - // KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // emf_pack(bl, E3, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j, i) + emf_pack(bl, E3, 0, k, j+1, i))/2; - // emf_pack(bl, E3, 0, k, j+1, i) = emf_pack(bl, E3, 0, k, j+2, i); - // } - // ); - // This is what we're replacing auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); // Circulation -> change in flux at face diff --git a/pars/orszag_tang_new.par b/pars/orszag_tang_new.par index e1dd5d1e..0fb3da1a 100644 --- a/pars/orszag_tang_new.par +++ b/pars/orszag_tang_new.par @@ -5,11 +5,11 @@ problem_id = orszag_tang -nx1 = 512 +nx1 = 1024 x1min = -3.141592653589793 x1max = 3.141592653589793 -nx2 = 512 +nx2 = 1024 x2min = -3.141592653589793 x2max = 3.141592653589793 @@ -35,9 +35,12 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 + +flux = hlle + solver = face_ct -kill_on_large_divb = false +kill_on_large_divb = true #ct_scheme = bs99 ct_scheme = sg09 From d3639e50a7d15753b996551359ac3cb0106e3778 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Tue, 6 Jun 2023 10:33:55 -0500 Subject: [PATCH 085/219] Reverting to old WENO5 implementation which gives 2nd order convergence for EMHD modes. --- kharma/reconstruction.hpp | 199 ++++++++++++++++++++++++++------------ pars/emhdmodes.par | 2 +- tests/emhdmodes/run.sh | 2 +- 3 files changed, 138 insertions(+), 65 deletions(-) diff --git a/kharma/reconstruction.hpp b/kharma/reconstruction.hpp index a3289ca6..798206e9 100644 --- a/kharma/reconstruction.hpp +++ b/kharma/reconstruction.hpp @@ -127,86 +127,159 @@ KOKKOS_INLINE_FUNCTION void PiecewiseLinearX3(parthenon::team_mbr_t const &membe KOKKOS_INLINE_FUNCTION void weno5(const Real& x1, const Real& x2, const Real& x3, const Real& x4, const Real& x5, Real &lout, Real &rout) { + // // Smoothness indicators, T07 A18 or S11 8 + // const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + // + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + // const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + // + (1./4.)*SQR(x4 - x2); + // const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + // + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + + // // Nonlinear weights S11 9 + // const Real den_inv1 = 1./(EPS + beta1*beta1); + // const Real den_inv2 = 1./(EPS + beta2*beta2); + // const Real den_inv3 = 1./(EPS + beta3*beta3); + + // // S11 1, 2, 3 left + // const Real wtl1 = 0.5 * den_inv3; + // const Real wtl2 = 5 * den_inv2; + // const Real wtl3 = 2.5 * den_inv1; + // lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + + // (-x4 + 6*x3 + 3*x2)*wtl2 + + // (3*x3 + 6*x2 - x1)*wtl3) + // / (8*(wtl1 + wtl2 + wtl3)); + + // // S11 1, 2, 3 right + // const Real wtr1 = 0.5 * den_inv1; + // const Real wtr2 = 5 * den_inv2; + // const Real wtr3 = 2.5 * den_inv3; + // rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + + // (-x2 + 6*x3 + 3*x4)*wtr2 + + // (3*x3 + 6*x4 - x5)*wtr3) + // / (8*(wtr1 + wtr2 + wtr3)); + + // OLD RECON // Smoothness indicators, T07 A18 or S11 8 - const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) - + (1./4.)*SQR(x1 - 4*x2 + 3*x3); - const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) - + (1./4.)*SQR(x4 - x2); - const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) - + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + Real beta[3], c1, c2; + c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; + beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; + beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; + beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den_inv1 = 1./(EPS + beta1*beta1); - const Real den_inv2 = 1./(EPS + beta2*beta2); - const Real den_inv3 = 1./(EPS + beta3*beta3); - - // S11 1, 2, 3 left - const Real wtl1 = 0.5 * den_inv3; - const Real wtl2 = 5 * den_inv2; - const Real wtl3 = 2.5 * den_inv1; - lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + - (-x4 + 6*x3 + 3*x2)*wtl2 + - (3*x3 + 6*x2 - x1)*wtl3) - / (8*(wtl1 + wtl2 + wtl3)); - - // S11 1, 2, 3 right - const Real wtr1 = 0.5 * den_inv1; - const Real wtr2 = 5 * den_inv2; - const Real wtr3 = 2.5 * den_inv3; - rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + - (-x2 + 6*x3 + 3*x4)*wtr2 + - (3*x3 + 6*x4 - x5)*wtr3) - / (8*(wtr1 + wtr2 + wtr3)); + Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; + den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + + Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; + Real Wr = wtr[0] + wtr[1] + wtr[2]; + + Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; + Real Wl = wtl[0] + wtl[1] + wtl[2]; + + // S11 1, 2, 3 + lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + + ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl[1] / Wl) + + ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl[2] / Wl); + rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr[0] / Wr) + + ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr[1] / Wr) + + ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr[2] / Wr); } KOKKOS_INLINE_FUNCTION void weno5l(const Real x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &lout) { + // // Smoothness indicators, T07 A18 or S11 8 + // const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + // + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + // const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + // + (1./4.)*SQR(x4 - x2); + // const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + // + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + + // // Nonlinear weights S11 9 + // const Real den_inv1 = 1./(EPS + beta1*beta1); + // const Real den_inv2 = 1./(EPS + beta2*beta2); + // const Real den_inv3 = 1./(EPS + beta3*beta3); + + // // S11 1, 2, 3 left + // const Real wtl1 = 0.5 * den_inv3; + // const Real wtl2 = 5 * den_inv2; + // const Real wtl3 = 2.5 * den_inv1; + // lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + + // (-x4 + 6*x3 + 3*x2)*wtl2 + + // (3*x3 + 6*x2 - x1)*wtl3) + // / (8*(wtl1 + wtl2 + wtl3)); + + + // OLD RECON // Smoothness indicators, T07 A18 or S11 8 - const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) - + (1./4.)*SQR(x1 - 4*x2 + 3*x3); - const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) - + (1./4.)*SQR(x4 - x2); - const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) - + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + Real beta[3], c1, c2; + c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; + beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; + beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; + beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den_inv1 = 1./(EPS + beta1*beta1); - const Real den_inv2 = 1./(EPS + beta2*beta2); - const Real den_inv3 = 1./(EPS + beta3*beta3); - - // S11 1, 2, 3 left - const Real wtl1 = 0.5 * den_inv3; - const Real wtl2 = 5 * den_inv2; - const Real wtl3 = 2.5 * den_inv1; - lout = ((3*x5 - 10*x4 + 15*x3)*wtl1 + - (-x4 + 6*x3 + 3*x2)*wtl2 + - (3*x3 + 6*x2 - x1)*wtl3) - / (8*(wtl1 + wtl2 + wtl3)); + Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; + den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + + Real wtl[3] = {(1./16.)/den[2], (5./8. )/den[1], (5./16.)/den[0]}; + Real Wl = wtl[0] + wtl[1] + wtl[2]; + + // S11 1, 2, 3 + lout = ((3./8.)*x5 - (5./4.)*x4 + (15./8.)*x3)*(wtl[0] / Wl) + + ((-1./8.)*x4 + (3./4.)*x3 + (3./8.)*x2)*(wtl[1] / Wl) + + ((3./8.)*x3 + (3./4.)*x2 - (1./8.)*x1)*(wtl[2] / Wl); } KOKKOS_INLINE_FUNCTION void weno5r(const Real& x1, const Real& x2, const Real& x3, const Real x4, const Real& x5, Real &rout) { + // // Smoothness indicators, T07 A18 or S11 8 + // const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) + // + (1./4.)*SQR(x1 - 4*x2 + 3*x3); + // const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) + // + (1./4.)*SQR(x4 - x2); + // const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) + // + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + + // // Nonlinear weights S11 9 + // const Real den_inv1 = 1./(EPS + beta1*beta1); + // const Real den_inv2 = 1./(EPS + beta2*beta2); + // const Real den_inv3 = 1./(EPS + beta3*beta3); + + // // S11 1, 2, 3 right + // const Real wtr1 = 0.5 * den_inv1; + // const Real wtr2 = 5 * den_inv2; + // const Real wtr3 = 2.5 * den_inv3; + // rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + + // (-x2 + 6*x3 + 3*x4)*wtr2 + + // (3*x3 + 6*x4 - x5)*wtr3) + // / (8*(wtr1 + wtr2 + wtr3)); + + + // OLD RECON // Smoothness indicators, T07 A18 or S11 8 - const Real beta1 = (13./12.)*SQR(x1 - 2*x2 + x3) - + (1./4.)*SQR(x1 - 4*x2 + 3*x3); - const Real beta2 = (13./12.)*SQR(x2 - 2*x3 + x4) - + (1./4.)*SQR(x4 - x2); - const Real beta3 = (13./12.)*SQR(x3 - 2*x4 + x5) - + (1./4.)*SQR(x5 - 4*x4 + 3*x3); + Real beta[3], c1, c2; + c1 = x1 - 2.*x2 + x3; c2 = x1 - 4.*x2 + 3.*x3; + beta[0] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x2 - 2.*x3 + x4; c2 = x4 - x2; + beta[1] = (13./12.)*c1*c1 + (1./4.)*c2*c2; + c1 = x3 - 2.*x4 + x5; c2 = x5 - 4.*x4 + 3.*x3; + beta[2] = (13./12.)*c1*c1 + (1./4.)*c2*c2; // Nonlinear weights S11 9 - const Real den_inv1 = 1./(EPS + beta1*beta1); - const Real den_inv2 = 1./(EPS + beta2*beta2); - const Real den_inv3 = 1./(EPS + beta3*beta3); - - // S11 1, 2, 3 right - const Real wtr1 = 0.5 * den_inv1; - const Real wtr2 = 5 * den_inv2; - const Real wtr3 = 2.5 * den_inv3; - rout = ((3*x1 - 10*x2 + 15*x3)*wtr1 + - (-x2 + 6*x3 + 3*x4)*wtr2 + - (3*x3 + 6*x4 - x5)*wtr3) - / (8*(wtr1 + wtr2 + wtr3)); + Real den[3] = {EPS + beta[0], EPS + beta[1], EPS + beta[2]}; + den[0] *= den[0]; den[1] *= den[1]; den[2] *= den[2]; + + Real wtr[3] = {(1./16.)/den[0], (5./8. )/den[1], (5./16.)/den[2]}; + Real Wr = wtr[0] + wtr[1] + wtr[2]; + + rout = ((3./8.)*x1 - (5./4.)*x2 + (15./8.)*x3)*(wtr[0] / Wr) + + ((-1./8.)*x2 + (3./4.)*x3 + (3./8.)*x4)*(wtr[1] / Wr) + + ((3./8.)*x3 + (3./4.)*x4 - (1./8.)*x5)*(wtr[2] / Wr); } // Row-wise implementations diff --git a/pars/emhdmodes.par b/pars/emhdmodes.par index e6299d56..68f20974 100644 --- a/pars/emhdmodes.par +++ b/pars/emhdmodes.par @@ -86,7 +86,7 @@ flag_verbose = 0 on = true higher_order_terms = false feedback = true -stability_limits = true +stability_limits = false conduction = true viscosity = true diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index b0dc7579..231c54c8 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -32,7 +32,7 @@ conv_2d() { # 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 # Just one default mode -ALL_RES="16,32,64" +ALL_RES="32,64,128" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "EMHD mode in 2D, WENO5" ALL_RES="16,32,64,128,256" conv_2d emhd2d_mc GRMHD/reconstruction=linear_mc "EMHD mode in 2D, linear/MC reconstruction" From af5cd9bd6d542d2de1e1892925ccccaa5164eb10 Mon Sep 17 00:00:00 2001 From: vedantdhruv96 Date: Wed, 7 Jun 2023 11:19:19 -0500 Subject: [PATCH 086/219] Fixed conducting atmosphere problem. Problem init was manually turning higher order terms off. --- kharma/prob/emhd/conducting_atmosphere.cpp | 3 +-- pars/conducting_atmosphere.par | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index a617fa74..170cd914 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -54,9 +54,8 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame auto pmb = rc->GetBlockPointer(); // Obtain EMHD params - const bool use_emhd = pmb->packages.AllPackages().count("EMHD"); + const bool use_emhd = pmb->packages.AllPackages().count("EMHD"); EMHD::EMHD_parameters emhd_params = EMHD::GetEMHDParameters(pmb->packages); - emhd_params.higher_order_terms = false; // Obtain GRMHD params const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par index 3eb29ca9..694a8b1b 100644 --- a/pars/conducting_atmosphere.par +++ b/pars/conducting_atmosphere.par @@ -61,11 +61,14 @@ linesearch_eps = 1.e-4 # IMPORTANT: This block must be present and values filled in all EGRMHD simulations -on = true +on = true higher_order_terms = true feedback = true stability_limits = false +conduction = true +viscosity = false + closure_type = kappa_eta tau = 10. kappa = 0.1 @@ -86,7 +89,8 @@ extra_checks = 1 file_type = hdf5 dt = 10 single_precision_output = false -variables = prims, cons, solve_norm, solve_fail +#variables = prims, cons, solve_norm, solve_fail +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, solve_norm, solve_fail ghost_zones = true From 6d28a91b9adac3b247f907f8fb4a5ac9e34f78a1 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 7 Jun 2023 16:09:05 -0600 Subject: [PATCH 087/219] Better logic & explanations for Bondi boundaries --- kharma/prob/bondi.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index cbd52208..1cd0af60 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -79,25 +79,36 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn // Set this problem to control the outer X1 boundary by default // remember to disable inflow_check in parameter file! - auto bound_pkg = pmb->packages.Get("Boundaries"); - if (pin->GetString("boundaries", "inner_x1") == "dirichlet" || - pin->GetString("boundaries", "outer_x1") == "dirichlet") { + // "dirichlet" here means specifically KHARMA's cached boundaries (see boundaries.cpp) + // The boudaries below are technically Dirichlet boundaries, too, but + // aren't called that for our purposes + auto outer_dirichlet = pin->GetString("boundaries", "outer_x1") == "dirichlet"; + auto inner_dirichlet = pin->GetString("boundaries", "inner_x1") == "dirichlet"; + if (outer_dirichlet || inner_dirichlet) { SetBondi(rc); // TODO iterate & set any bounds specifically? } else { - if (pin->GetOrAddBoolean("bondi", "set_outer_bound", true)) { - bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetBondi; - } - if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { - bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetBondi; - } - // Set the interior domain to the analytic solution to begin - // This tests that PostInitialize will correctly fill ghost zones with the boundary we set + // Generally, we only set the interior domain, not the ghost zones. + // This tests that PostInitialize will correctly fill all ghosts SetBondi(rc); } + // Default Bondi boundariy conditions: reset the outer boundary using our set function. + // Register the callback to replace value from boundaries.cpp, & record the change in pin. + auto bound_pkg = pmb->packages.Get("Boundaries"); + if (pin->GetOrAddBoolean("bondi", "set_outer_bound", !outer_dirichlet)) { + pin->SetString("boundaries", "outer_x1", "bondi"); + bound_pkg->KBoundaries[BoundaryFace::outer_x1] = SetBondi; + } + // Option to set the inner boundary too. Ruins convergence + if (pin->GetOrAddBoolean("bondi", "set_inner_bound", false)) { + pin->SetString("boundaries", "inner_x1", "bondi"); + bound_pkg->KBoundaries[BoundaryFace::inner_x1] = SetBondi; + } + + // Apply floors to initialize the any part of the domain we didn't + // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable + // Note this ignores the "disable_floors" parameter, since it's necessary for initialization if (rin_bondi > pin->GetReal("coordinates", "r_in") && !(fill_interior)) { - // Apply floors to initialize the rest of the domain (regardless of the 'disable_floors' param) - // Bondi's BL coordinates do not like the EH, so we replace the zeros with something reasonable. Floors::ApplyInitialFloors(pin, rc.get(), IndexDomain::interior); } From cf95ee40031f1ab6045baf4cf0930151708b1c64 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jun 2023 14:48:34 -0600 Subject: [PATCH 088/219] Oh no more CI --- .gitlab-ci.yml | 78 ++++--------------- scripts/ci/cpu.yml | 62 +++++++++++++++ scripts/ci/darwin.yml | 76 ++++++++++++++++++ .gitlab-ci-docker.yml => scripts/ci/nvhpc.yml | 70 +++-------------- tests/resize/run.sh | 19 +++++ 5 files changed, 181 insertions(+), 124 deletions(-) create mode 100644 scripts/ci/cpu.yml create mode 100644 scripts/ci/darwin.yml rename .gitlab-ci-docker.yml => scripts/ci/nvhpc.yml (63%) create mode 100755 tests/resize/run.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c0cd1ccf..dcdf79a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,33 +1,5 @@ -# Continuous Integration testing for KHARMA -# a.k.a did we break the basics? -# This version run on LANL Darwin -# See .gitlab-ci-docker.yml for a generic version, -# which can be run on any Docker runner w/GPUs - -variables: - GIT_SUBMODULE_STRATEGY: recursive - SCHEDULER_PARAMETERS: "-N 1 --qos=debug -p volta-x86" - HOST_ARCH: HSW - NPROC: "" - OMP_NUM_THREADS: 28 - OMP_PROC_BIND: "false" - MPI_EXE: mpirun - MPI_NUM_PROCS: 2 - HTTP_PROXY: http://proxyout.lanl.gov:8080 - http_proxy: http://proxyout.lanl.gov:8080 - HTTPS_PROXY: http://proxyout.lanl.gov:8080 - https_proxy: http://proxyout.lanl.gov:8080 - NO_PROXY: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 - no_proxy: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 - -### DEFAULT TEST BEHAVIOR ### -default: - tags: - - darwin-slurm-shared - # Load Python - before_script: - - module load miniconda3 +defaults: # Always keep logs and plots. Results should be printed to console! artifacts: when: always @@ -35,42 +7,18 @@ default: - tests/*/*.png - tests/*/*.txt -# Tests can be executed in parallel, -# but be careful about GPU arch -stages: - - build - - tests - -# Default rules -.default-rules: - rules: - - if: $CI_COMMIT_BRANCH == "dev" - when: always - - when: manual - allow_failure: false +trigger_darwin: + trigger: + include: script/ci/darwin.yml -# Build, obviously overrides script/artifacts -build: - extends: .default-rules - stage: build - before_script: - - echo "Skipping pyharm install in build." - script: - - export PREFIX_PATH=$PWD/external/hdf5 - - ./make.sh clean cuda hdf5 volta - artifacts: - paths: - - kharma.* - - make_args +trigger_nvhpc: + trigger: + include: script/ci/nvhpc.yml -# Run all tests in parallel -tests: - extends: .default-rules - stage: tests - script: - - cd tests/$TEST - - ./run.sh - parallel: - matrix: - - TEST: [bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] +trigger_cpu: + trigger: + include: script/ci/cpu.yml + strategy: depend +# TODO trigger_cpu w/intel or similar container +# TODO build containers here diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml new file mode 100644 index 00000000..d1ce3b27 --- /dev/null +++ b/scripts/ci/cpu.yml @@ -0,0 +1,62 @@ +# CI on CPUs: GCC on CentOS oughta build about anything + +image: quay.io/centos/centos:stream9 + +variables: + OMP_NUM_THREADS: 8 + OMP_PROC_BIND: "false" + MPI_EXE: mpirun + MPI_NUM_PROCS: 2 + OMPI_ALLOW_RUN_AS_ROOT: 1 + OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 + GIT_SUBMODULE_STRATEGY: recursive + +### DEFAULT TEST BEHAVIOR ### +default: + # Be default: install pyharm, then run test in cwd + # For new tests, write one run.sh script which runs/verifies + # interleaved, and prints a summary of results. + before_script: + - export PATH="$HOME/.local/bin:$PATH" + - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" + - bash Miniforge3.sh -b -p "/home/conda" + - source "/home/conda/etc/profile.d/conda.sh" + - conda install h5py + - git clone https://github.com/AFD-Illinois/pyharm.git /home/pyharm + - conda activate + - cd /home/pyharm + - pip install --user . + - cd - + +# Tests can be executed in parallel, +# but be careful about GPU arch +stages: + - build + - tests + +# Build, obviously overrides script/artifacts +build: + stage: build + variables: + NPROC: "" + HOST_ARCH: HSW + before_script: + - echo "Skipping pyharm install in build." + script: + - export PREFIX_PATH=$PWD/external/hdf5 + - ./make.sh clean hdf5 + artifacts: + paths: + - kharma.* + - make_args + +#Run all tests in parallel +tests: + extends: .default-rules + stage: tests + script: + - cd tests/$TEST + - ./run.sh + parallel: + matrix: + - TEST: [bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] diff --git a/scripts/ci/darwin.yml b/scripts/ci/darwin.yml new file mode 100644 index 00000000..c0cd1ccf --- /dev/null +++ b/scripts/ci/darwin.yml @@ -0,0 +1,76 @@ +# Continuous Integration testing for KHARMA +# a.k.a did we break the basics? +# This version run on LANL Darwin +# See .gitlab-ci-docker.yml for a generic version, +# which can be run on any Docker runner w/GPUs + +variables: + GIT_SUBMODULE_STRATEGY: recursive + SCHEDULER_PARAMETERS: "-N 1 --qos=debug -p volta-x86" + HOST_ARCH: HSW + NPROC: "" + OMP_NUM_THREADS: 28 + OMP_PROC_BIND: "false" + MPI_EXE: mpirun + MPI_NUM_PROCS: 2 + HTTP_PROXY: http://proxyout.lanl.gov:8080 + http_proxy: http://proxyout.lanl.gov:8080 + HTTPS_PROXY: http://proxyout.lanl.gov:8080 + https_proxy: http://proxyout.lanl.gov:8080 + NO_PROXY: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 + no_proxy: lanl.gov,localhost,127.0.0.1,0.0.0.0,::1 + +### DEFAULT TEST BEHAVIOR ### +default: + tags: + - darwin-slurm-shared + # Load Python + before_script: + - module load miniconda3 + + # Always keep logs and plots. Results should be printed to console! + artifacts: + when: always + paths: + - tests/*/*.png + - tests/*/*.txt + +# Tests can be executed in parallel, +# but be careful about GPU arch +stages: + - build + - tests + +# Default rules +.default-rules: + rules: + - if: $CI_COMMIT_BRANCH == "dev" + when: always + - when: manual + allow_failure: false + +# Build, obviously overrides script/artifacts +build: + extends: .default-rules + stage: build + before_script: + - echo "Skipping pyharm install in build." + script: + - export PREFIX_PATH=$PWD/external/hdf5 + - ./make.sh clean cuda hdf5 volta + artifacts: + paths: + - kharma.* + - make_args + +# Run all tests in parallel +tests: + extends: .default-rules + stage: tests + script: + - cd tests/$TEST + - ./run.sh + parallel: + matrix: + - TEST: [bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] + diff --git a/.gitlab-ci-docker.yml b/scripts/ci/nvhpc.yml similarity index 63% rename from .gitlab-ci-docker.yml rename to scripts/ci/nvhpc.yml index d111f37b..654f3957 100644 --- a/.gitlab-ci-docker.yml +++ b/scripts/ci/nvhpc.yml @@ -4,10 +4,10 @@ # Build on Nvidia image. # Can pretty easily change this out, with changes to build # Someday we'll build & push a KHARMA image, then test that -image: nvcr.io/nvidia/nvhpc:23.1-devel-cuda12.0-rockylinux8 +image: nvcr.io/nvidia/nvhpc:23.5-devel-cuda12.1-rockylinux8 variables: - OMP_NUM_THREADS: 28 + OMP_NUM_THREADS: 8 OMP_PROC_BIND: "false" MPI_EXE: mpirun MPI_NUM_PROCS: 2 @@ -17,6 +17,8 @@ variables: ### DEFAULT TEST BEHAVIOR ### default: + tags: + - gpu # Be default: install pyharm, then run test in cwd # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. @@ -32,13 +34,6 @@ default: - pip install --user . - cd - - # Always keep logs and plots. Results should be printed to console! - artifacts: - when: always - paths: - - tests/*/*.png - - tests/*/*.txt - # Tests can be executed in parallel, # but be careful about GPU arch stages: @@ -61,56 +56,13 @@ build: - kharma.* - make_args -bondi: - stage: tests - script: - - cd tests/bondi - - ./run.sh - -mhdmodes: - stage: tests - script: - - cd tests/mhdmodes - - ./run.sh - -emhdmodes: - stage: tests - script: - - cd tests/emhdmodes - - ./run.sh - -noh: - stage: tests - script: - - cd tests/noh - - ./run.sh - -bz_monopole: - stage: tests - script: - - cd tests/bz_monopole - - ./run.sh - -tilt_init: - stage: tests - script: - - cd tests/tilt_init - - ./run.sh - -torus_sanity: - stage: tests - script: - - cd tests/torus_sanity - - ./run.sh - -restart: - stage: tests - script: - - cd tests/restart - - ./run.sh - -reinit: +#Run all tests in parallel +tests: + extends: .default-rules stage: tests script: - - cd tests/reinit + - cd tests/$TEST - ./run.sh + parallel: + matrix: + - TEST: [bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] diff --git a/tests/resize/run.sh b/tests/resize/run.sh new file mode 100755 index 00000000..5b965bd0 --- /dev/null +++ b/tests/resize/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Bash script testing starting a simulation, then resizing it up + +# Set paths +KHARMADIR=../.. + +$KHARMADIR/run.sh -i $KHARMADIR/pars/sane.par parthenon/time/nlim=5 >log_resize_1.txt 2>&1 + +pyharm convert --to_restart torus.out0.final.phdf + +sleep 1 + +$KHARMADIR/run.sh -i ../../pars/resize_restart >log_resize_2.txt 2>&1 + +mv torus.out0.final.phdf torus.out0.final.restart.phdf + +# Check divB on the re-meshed output +pyharm-check-basics torus.out0.final.restart.phdf From c4c6f8e5f4bbe28c136b12d6f7706dff6e9c7f9d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jun 2023 20:52:07 +0000 Subject: [PATCH 089/219] CI syntax is hard --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dcdf79a9..dbad52e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ -defaults: +default: # Always keep logs and plots. Results should be printed to console! artifacts: when: always From 90cdca7f97a41ce03217712d6aaa822b351c9f3d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jun 2023 20:53:14 +0000 Subject: [PATCH 090/219] names are hard --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dbad52e6..c1662f88 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,15 +9,15 @@ default: trigger_darwin: trigger: - include: script/ci/darwin.yml + include: scripts/ci/darwin.yml trigger_nvhpc: trigger: - include: script/ci/nvhpc.yml + include: scripts/ci/nvhpc.yml trigger_cpu: trigger: - include: script/ci/cpu.yml + include: scripts/ci/cpu.yml strategy: depend # TODO trigger_cpu w/intel or similar container From a7d62839d5b372e5315a0a4d11e52499f463aa82 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jun 2023 14:59:05 -0600 Subject: [PATCH 091/219] CI: Adjust names --- .gitlab-ci.yml | 1 - scripts/ci/cpu.yml | 1 - scripts/ci/nvhpc.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c1662f88..93093c23 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,3 @@ - default: # Always keep logs and plots. Results should be printed to console! artifacts: diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index d1ce3b27..82b04177 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -52,7 +52,6 @@ build: #Run all tests in parallel tests: - extends: .default-rules stage: tests script: - cd tests/$TEST diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 654f3957..24f41ac7 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -58,7 +58,6 @@ build: #Run all tests in parallel tests: - extends: .default-rules stage: tests script: - cd tests/$TEST From a0643135b3e45406d3d3e5baaba2d8353c9233ce Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jun 2023 15:24:03 -0600 Subject: [PATCH 092/219] Limit runners. Better accommodate shell runners. --- scripts/ci/cpu.yml | 3 +++ scripts/ci/nvhpc.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 82b04177..0a8f4c8c 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -13,10 +13,13 @@ variables: ### DEFAULT TEST BEHAVIOR ### default: + tags: + - public-kharma-runner # Be default: install pyharm, then run test in cwd # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: + - module load gnu mpich fftw3 || dnf -y install mpich-devel fftw-devel && module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - bash Miniforge3.sh -b -p "/home/conda" diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 24f41ac7..263c18ef 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -18,7 +18,7 @@ variables: ### DEFAULT TEST BEHAVIOR ### default: tags: - - gpu + - public-kharma-gpu # Be default: install pyharm, then run test in cwd # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. From 8ecf843bbff1d6319cbd3e36730c3b43e5b29d2d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 10:45:52 -0600 Subject: [PATCH 093/219] CI: Prereqs for CPU build --- scripts/ci/cpu.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 0a8f4c8c..477bee54 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -19,7 +19,9 @@ default: # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: - - module load gnu mpich fftw3 || dnf -y install mpich-devel fftw-devel && module load mpi/mpich-x86_64 + - dnf -y groupinstall "Development Tools" + - dnf -y install gcc-c++ cmake git mpich-devel fftw-devel + - module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - bash Miniforge3.sh -b -p "/home/conda" From 0b0e28d04dc558553a5c71ee5b559e2879c3863c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 10:52:29 -0600 Subject: [PATCH 094/219] CI: Fix build prereqs --- scripts/ci/cpu.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 477bee54..adcfa04e 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -3,7 +3,7 @@ image: quay.io/centos/centos:stream9 variables: - OMP_NUM_THREADS: 8 + OMP_NUM_THREADS: 4 OMP_PROC_BIND: "false" MPI_EXE: mpirun MPI_NUM_PROCS: 2 @@ -19,8 +19,6 @@ default: # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: - - dnf -y groupinstall "Development Tools" - - dnf -y install gcc-c++ cmake git mpich-devel fftw-devel - module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" @@ -33,8 +31,7 @@ default: - pip install --user . - cd - -# Tests can be executed in parallel, -# but be careful about GPU arch +# Tests can be executed in parallel stages: - build - tests @@ -44,9 +41,11 @@ build: stage: build variables: NPROC: "" - HOST_ARCH: HSW + HOST_ARCH: AMDAVX before_script: - - echo "Skipping pyharm install in build." + - dnf -y groupinstall "Development Tools" + - dnf -y install hostname gcc-c++ cmake git mpich-devel fftw-devel + - module load mpi/mpich-x86_64 script: - export PREFIX_PATH=$PWD/external/hdf5 - ./make.sh clean hdf5 From a7875181f1ae58eee8c7882d784e2938c3c7c44b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 12:01:17 -0600 Subject: [PATCH 095/219] CI: package dependencies --- scripts/ci/cpu.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index adcfa04e..5098873b 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -19,6 +19,8 @@ default: # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: + - dnf -y install hostname environment-modules mpich fftw + - source /etc/profile - module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" @@ -44,11 +46,11 @@ build: HOST_ARCH: AMDAVX before_script: - dnf -y groupinstall "Development Tools" - - dnf -y install hostname gcc-c++ cmake git mpich-devel fftw-devel + - dnf -y install hostname environment-modules gcc-c++ cmake git mpich-devel hdf5-mpich-devel hdf5-mpich-static fftw-devel + - source /etc/profile - module load mpi/mpich-x86_64 script: - - export PREFIX_PATH=$PWD/external/hdf5 - - ./make.sh clean hdf5 + - ./make.sh clean artifacts: paths: - kharma.* From 72a906700400e79e51ccac1bb92de0febd9da997 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 12:05:33 -0600 Subject: [PATCH 096/219] CI: build HDF5 because no CentOS packages --- scripts/ci/cpu.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 5098873b..cf62479a 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -46,11 +46,11 @@ build: HOST_ARCH: AMDAVX before_script: - dnf -y groupinstall "Development Tools" - - dnf -y install hostname environment-modules gcc-c++ cmake git mpich-devel hdf5-mpich-devel hdf5-mpich-static fftw-devel + - dnf -y install hostname environment-modules cmake mpich-devel fftw-devel - source /etc/profile - module load mpi/mpich-x86_64 script: - - ./make.sh clean + - ./make.sh clean hdf5 artifacts: paths: - kharma.* From ba0f19a1ed0d002900e463e93b68f5c5a9e7650d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 12:12:19 -0600 Subject: [PATCH 097/219] CI: wget --- scripts/ci/cpu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index cf62479a..3dc35c93 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -19,7 +19,7 @@ default: # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: - - dnf -y install hostname environment-modules mpich fftw + - dnf -y install hostname environment-modules mpich fftw wget - source /etc/profile - module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" From 5987dfc7de7d1db65a6ce341523c37f03ffa2552 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 21 Jun 2023 12:30:42 -0600 Subject: [PATCH 098/219] CI: git --- scripts/ci/cpu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 3dc35c93..1d586533 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -19,7 +19,7 @@ default: # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: - - dnf -y install hostname environment-modules mpich fftw wget + - dnf -y install hostname environment-modules git mpich fftw wget - source /etc/profile - module load mpi/mpich-x86_64 - export PATH="$HOME/.local/bin:$PATH" From 1cc76107f40354468ad18c54ab56f8f8863310bb Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 22 Jun 2023 10:01:47 -0600 Subject: [PATCH 099/219] CI: Limit compile jobs --- scripts/ci/cpu.yml | 4 ++-- scripts/ci/nvhpc.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 1d586533..a5889edd 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -42,8 +42,8 @@ stages: build: stage: build variables: - NPROC: "" - HOST_ARCH: AMDAVX + NPROC: 4 + HOST_ARCH: NATIVE before_script: - dnf -y groupinstall "Development Tools" - dnf -y install hostname environment-modules cmake mpich-devel fftw-devel diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 263c18ef..61a5be9c 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -44,8 +44,8 @@ stages: build: stage: build variables: - NPROC: "" - HOST_ARCH: HSW + NPROC: 8 + HOST_ARCH: NATIVE before_script: - echo "Skipping pyharm install in build." script: From 05ab4f608e32b26d3142af52138d59e7f338d0ec Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 21 Jul 2023 13:59:05 -0500 Subject: [PATCH 100/219] Illinois machines: use new Intel compiler by default --- machines/illinois.sh | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/machines/illinois.sh b/machines/illinois.sh index 7f32f0ac..f0355603 100644 --- a/machines/illinois.sh +++ b/machines/illinois.sh @@ -22,10 +22,9 @@ elif [[ $HOST == *".astro.illinois.edu" ]]; then PREFIX_PATH="$SOURCE_DIR/external/hdf5" if [[ $ARGS == *"icc"* ]]; then - # Intel ICC + # Intel ICC ("classic") module purge source /opt/intel/oneapi/setvars.sh - # Use specifically the old compilers because the new stdlib is broken on BH C_NATIVE="icc" CXX_NATIVE="icpc" C_FLAGS="-diag-disable=10441" @@ -38,14 +37,11 @@ elif [[ $HOST == *".astro.illinois.edu" ]]; then CXX_NATIVE="clang++" else - # GNU GCC - if [[ $HOST == "bh29"* ]]; then - # Older GCC has no flag for ZEN2 - HOST_ARCH="ZEN" - fi - module load gnu hdf5 fftw3 - # System HDF5 location - PREFIX_PATH="$MPI_DIR" + # GCC 7.5 is too old to compile KHARMA at all. Use new Intel compiler by default + module purge + source /opt/intel/oneapi/setvars.sh + C_NATIVE="icx" + CXX_NATIVE="icpx" fi fi # BH29 additions From 2bc9c61e1fc574bcf50b3b0f48c6e68fa834ad16 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 24 Jul 2023 10:16:29 -0500 Subject: [PATCH 101/219] Start using relative directories in scripts --- scripts/batch/scaling_delta.sb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/batch/scaling_delta.sb b/scripts/batch/scaling_delta.sb index 7f2a8049..d96e5212 100755 --- a/scripts/batch/scaling_delta.sb +++ b/scripts/batch/scaling_delta.sb @@ -30,7 +30,7 @@ DO_STRONG=true DO_WEAK=true -KHARMA_DIR=~/kharma +KHARMA_DIR=$(dirname "$(readlink -f "$0")")/../.. # Global options export OMP_PROC_BIND=spread @@ -38,7 +38,7 @@ export OMP_PLACES=threads # Strong scaling. Possibly not optimal due to requiring cubic meshblocks if [[ $DO_STRONG == "true" ]]; then - for size in 256 512 + for size in 256 512 1024 do for tpn in 4 do From 6f7aa6cf68ec82bce7c2b4352a0586853df21846 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 24 Jul 2023 09:47:23 -0600 Subject: [PATCH 102/219] Fix currrent bug for kharma-next idiomatically --- kharma/driver/imex_step.cpp | 12 +++++++----- kharma/driver/kharma_driver.hpp | 5 +++-- kharma/driver/kharma_step.cpp | 4 +++- kharma/driver/simple_step.cpp | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index e614ab83..cce50844 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -89,6 +89,8 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // At the end of the step, updating "mbd_sub_step_final" updates the base // So we have to keep a copy at the beginning to calculate jcon pmb->meshblock_data.Add("preserve", base); + // Above only copies on allocate -- ensure we copy every step + Copy>({}, base.get(), pmb->meshblock_data.Get("preserve").get()); } if (use_implicit) { @@ -102,7 +104,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta } } } - + // Big synchronous region: get & apply fluxes to advance the fluid state // num_partitions is nearly always 1 const int num_partitions = pmesh->DefaultNumPartitions(); @@ -180,7 +182,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_solver.get()); } @@ -198,7 +200,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Copy the current state of any implicitly-evolved vars (at least the prims) in as a guess. // This sets md_solver = md_sub_step_init - auto t_copy_guess = tl.AddTask(t_sources, Copy, std::vector({Metadata::GetUserFlag("Implicit")}), + auto t_copy_guess = tl.AddTask(t_sources, Copy>, std::vector({Metadata::GetUserFlag("Implicit")}), md_sub_step_init.get(), md_solver.get()); auto t_guess_ready = t_explicit | t_copy_guess; @@ -208,7 +210,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Copy the primitives to the `linesearch` MeshData object if linesearch was enabled. auto t_copy_linesearch = t_guess_ready; if (use_linesearch) { - t_copy_linesearch = tl.AddTask(t_guess_ready, Copy, std::vector({Metadata::GetUserFlag("Primitive")}), + t_copy_linesearch = tl.AddTask(t_guess_ready, Copy>, std::vector({Metadata::GetUserFlag("Primitive")}), md_solver.get(), md_linesearch.get()); } @@ -221,7 +223,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Copy the entire solver state (everything defined on the grid, i.e. 'Cell') into the final state md_sub_step_final // If we're entirely explicit, we just declare these equal - t_implicit = tl.AddTask(t_implicit_step, Copy, std::vector({Metadata::Cell}), + t_implicit = tl.AddTask(t_implicit_step, Copy>, std::vector({Metadata::Cell}), md_solver.get(), md_sub_step_final.get()); } diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index f1336669..61973da7 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -127,9 +127,10 @@ class KHARMADriver : public MultiStageDriver { * Copy variables matching 'flags' from 'source' to 'dest'. * Mostly makes things easier to read. */ - static TaskStatus Copy(std::vector flags, MeshData* source, MeshData* dest) + template + static TaskStatus Copy(std::vector flags, T* source, T* dest) { - return Update::WeightedSumData, MeshData>(flags, source, source, 1., 0., dest); + return Update::WeightedSumData, T>(flags, source, source, 1., 0., dest); } /** diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 8e990ad4..504e8c93 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -105,6 +105,8 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // At the end of the step, updating "mbd_sub_step_final" updates the base // So we have to keep a copy at the beginning to calculate jcon pmb->meshblock_data.Add("preserve", base); + // Above only copies on allocate -- ensure we copy every step + Copy>({}, base.get(), pmb->meshblock_data.Get("preserve").get()); } } } @@ -178,7 +180,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // on adjacent ranks are seeded with the same value, which keeps them (more) similar auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index a7bd46d8..a639a006 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -113,7 +113,7 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s // UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } From 21102e4125f37db8610288e2c080a95cf66a96fd Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 Jul 2023 15:38:08 -0500 Subject: [PATCH 103/219] Delta: use default NVHPC stack and clean up compiler warnings --- kharma/b_cleanup/b_cleanup.cpp | 2 +- kharma/boundaries/boundaries.cpp | 2 +- kharma/boundaries/dirichlet.cpp | 2 +- kharma/driver/kharma_driver.cpp | 2 +- kharma/driver/kharma_driver.hpp | 2 +- kharma/driver/simple_step.cpp | 2 +- kharma/floors/floors_functions.hpp | 2 +- kharma/flux/flux.cpp | 2 +- kharma/inverter/invert_template.hpp | 2 +- kharma/inverter/inverter.hpp | 2 +- kharma/inverter/onedw.hpp | 2 +- kharma/kharma_package.hpp | 2 +- kharma/prob/bondi.hpp | 4 ++-- kharma/prob/elec/driven_turbulence.hpp | 2 +- kharma/prob/elec/gaussian.cpp | 2 +- kharma/prob/elec/hubble.hpp | 2 +- machines/delta.sh | 8 ++++++-- make.sh | 5 +++++ 18 files changed, 28 insertions(+), 19 deletions(-) diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 16baa1db..fc704e51 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -377,4 +377,4 @@ TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_v return TaskStatus::complete; } -#endif \ No newline at end of file +#endif diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 52f9eff8..d30d8cb7 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -364,4 +364,4 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) } return TaskStatus::complete; -} \ No newline at end of file +} diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 56858b03..7e6408f8 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -166,4 +166,4 @@ void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain } } ); -} \ No newline at end of file +} diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index c6ed1a4f..73027ac4 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -263,4 +263,4 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst throw std::invalid_argument("Unsupported reconstruction algorithm!"); } return t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; -} \ No newline at end of file +} diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 61973da7..28a61cd4 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -142,4 +142,4 @@ class KHARMADriver : public MultiStageDriver { return Update::WeightedSumData, MeshBlockData>(flags, source, source, norm, 0., source); } -}; \ No newline at end of file +}; diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index a639a006..80cf1020 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -163,4 +163,4 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); return tc; -} \ No newline at end of file +} diff --git a/kharma/floors/floors_functions.hpp b/kharma/floors/floors_functions.hpp index 3b61c1b0..2e089ea3 100644 --- a/kharma/floors/floors_functions.hpp +++ b/kharma/floors/floors_functions.hpp @@ -405,4 +405,4 @@ KOKKOS_INLINE_FUNCTION int apply_geo_floors(const GRCoordinates& G, Global& P, c return fflag; } -} // Floors \ No newline at end of file +} // Floors diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 3e038e3d..feb21b91 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -225,4 +225,4 @@ void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) VLOOP dUdt(b, m_u.U1 + v, k, j, i) += new_du[1 + v]; } ); -} \ No newline at end of file +} diff --git a/kharma/inverter/invert_template.hpp b/kharma/inverter/invert_template.hpp index 6f511dd4..6d938a11 100644 --- a/kharma/inverter/invert_template.hpp +++ b/kharma/inverter/invert_template.hpp @@ -86,4 +86,4 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const VariablePack< const Real& gam, const int& k, const int& j, const int& i, const VariablePack& P, const VarMap& m_p, const Loci loc); -} // namespace Inverter \ No newline at end of file +} // namespace Inverter diff --git a/kharma/inverter/inverter.hpp b/kharma/inverter/inverter.hpp index 2b484cbb..78c1503d 100644 --- a/kharma/inverter/inverter.hpp +++ b/kharma/inverter/inverter.hpp @@ -78,4 +78,4 @@ TaskStatus FixUtoP(MeshBlockData *rc); */ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md); -} \ No newline at end of file +} diff --git a/kharma/inverter/onedw.hpp b/kharma/inverter/onedw.hpp index dea8f05d..160d6625 100644 --- a/kharma/inverter/onedw.hpp +++ b/kharma/inverter/onedw.hpp @@ -233,4 +233,4 @@ KOKKOS_INLINE_FUNCTION Status u_to_p(const GRCoordinates &G, const return Status::success; } -} // namespace Inverter \ No newline at end of file +} // namespace Inverter diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index 4ddaf79a..7c43bbc7 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -163,4 +163,4 @@ void UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin); void PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); void PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); void PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); -} \ No newline at end of file +} diff --git a/kharma/prob/bondi.hpp b/kharma/prob/bondi.hpp index 5ee84d26..87b54f8e 100644 --- a/kharma/prob/bondi.hpp +++ b/kharma/prob/bondi.hpp @@ -91,8 +91,8 @@ KOKKOS_INLINE_FUNCTION Real get_T(const GReal r, const Real C1, const Real C2, c f0 = get_Tfunc(T0, r, C1, C2, n); T1 = Tmax; f1 = get_Tfunc(T1, r, C1, C2, n); - // TODO(BSP) find a way to throw/communicate this - //if (f0 * f1 > 0) throw std::runtime_error("Cannot solve temperature!"); + // TODO(BSP) where does this trigger an error? Can we make it clearer? + if (f0 * f1 > 0) return -1.; Th = (T0 + T1) / 2.; // a simple bisection method which is stable and fast fh = get_Tfunc(Th, r, C1, C2, n); diff --git a/kharma/prob/elec/driven_turbulence.hpp b/kharma/prob/elec/driven_turbulence.hpp index a80e7dee..37403a2d 100644 --- a/kharma/prob/elec/driven_turbulence.hpp +++ b/kharma/prob/elec/driven_turbulence.hpp @@ -201,4 +201,4 @@ void ApplyDrivingTurbulence(MeshBlockData *rc) printf("%.32f\n", (finl_e-init_e)/dt_kick); free(dv0); free(dv1); } -} \ No newline at end of file +} diff --git a/kharma/prob/elec/gaussian.cpp b/kharma/prob/elec/gaussian.cpp index b58527ce..9e6898db 100644 --- a/kharma/prob/elec/gaussian.cpp +++ b/kharma/prob/elec/gaussian.cpp @@ -119,4 +119,4 @@ void create_grf(int Nx1, int Nx2, double lx1, double lx2, { throw std::runtime_error("Attempted to use an FFT to generate a Gaussian random field, but KHARMA was compiled without FFT support!"); } -#endif \ No newline at end of file +#endif diff --git a/kharma/prob/elec/hubble.hpp b/kharma/prob/elec/hubble.hpp index 1c13ed83..e6de723e 100644 --- a/kharma/prob/elec/hubble.hpp +++ b/kharma/prob/elec/hubble.hpp @@ -65,4 +65,4 @@ TaskStatus SetHubble(std::shared_ptr>& rc, bool coarse=false /** * Apply the source term. Registered as ApplyPrimSource to run at end of step, once per step operator-split */ -void ApplyHubbleHeating(MeshBlockData *mbase); \ No newline at end of file +void ApplyHubbleHeating(MeshBlockData *mbase); diff --git a/machines/delta.sh b/machines/delta.sh index fbec7160..292388d6 100644 --- a/machines/delta.sh +++ b/machines/delta.sh @@ -26,14 +26,18 @@ then # Load common GPU modules module load modtree/gpu hdf5 cmake - if [[ $ARGS == *"nvhpc"* ]]; then + if [[ $ARGS == *"latest"* ]]; then # nvhpc only on request, MPI crashes module load nvhpc_latest openmpi-5.0_beta C_NATIVE=nvc CXX_NATIVE=nvc++ - else # TODO NVHPC not-latest + elif [[ $ARGS == *"gcc"* ]]; then C_NATIVE=gcc CXX_NATIVE=g++ + else + module load nvhpc + #C_NATIVE=nvc + #CXX_NATIVE=nvc++ fi else # CPU Compile diff --git a/make.sh b/make.sh index 58fd7179..82261b1e 100755 --- a/make.sh +++ b/make.sh @@ -87,6 +87,7 @@ fi ### Enivoronment Prep ### if [[ "$(which python3 2>/dev/null)" == *"conda"* ]]; then + echo echo "It looks like you have Anaconda loaded." echo "Anaconda forces a serial version of HDF5 which may make this compile impossible." echo "If you run into trouble, deactivate your environment with 'conda deactivate'" @@ -272,6 +273,10 @@ fi # If we're doing a clean build, prep the source and # delete the build directory if [[ "$ARGS" == *"clean"* ]]; then + echo + echo "Patching Parthenon to use KHARMA coordinates." + echo "You may see patch errors here, this is normal." + cd external/parthenon git apply ../patches/parthenon-*.patch cd - From 30dc2251e527ca57edb466904a2f681a97e1e45b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 10 Aug 2023 16:37:55 -0600 Subject: [PATCH 104/219] Jump to Parthenon develop, build stuff * Parthenon develop (+pep1 +solvers +non-cell AMR) * Include git commit version/hash in parameters * Print version header, control prints a bit * Build script touchups and docs (not done yet) BREAKS resizing for the moment, until labeled resize vars are in Parthenon proper --- .gitmodules | 4 +- CMakeLists.txt | 17 +- cmake/GetGitRevisionDescription.cmake | 284 +++++++++++++++++++++++ cmake/GetGitRevisionDescription.cmake.in | 46 ++++ external/parthenon | 2 +- kharma/CMakeLists.txt | 6 + kharma/b_cleanup/b_cleanup.cpp | 5 +- kharma/boundaries/boundaries.cpp | 2 +- kharma/boundaries/dirichlet.cpp | 4 +- kharma/coordinates/gr_coordinates.cpp | 7 +- kharma/driver/imex_step.cpp | 11 +- kharma/driver/kharma_driver.cpp | 2 +- kharma/driver/kharma_step.cpp | 11 +- kharma/kharma.cpp | 6 + kharma/main.cpp | 20 +- kharma/prob/fm_torus.cpp | 4 +- kharma/prob/problem.cpp | 4 +- kharma/version.cpp.in | 8 + kharma/version.hpp | 46 ++++ make.sh | 94 ++++---- 20 files changed, 502 insertions(+), 81 deletions(-) create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in create mode 100644 kharma/version.cpp.in create mode 100644 kharma/version.hpp diff --git a/.gitmodules b/.gitmodules index d5ec6b1b..6fd205a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "external/parthenon"] path = external/parthenon - url = https://github.com/parthenon-hpc-lab/parthenon.git - branch = bprather/backport-bicgstab + url = https://github.com/AFD-Illinois/parthenon.git + branch = kharma [submodule "external/variant"] path = external/variant url = https://github.com/mpark/variant.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 524aae85..a36fc94c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,10 @@ cmake_minimum_required(VERSION 3.10) project(kharma LANGUAGES C CXX) # We follow Parthenon in requiring C++17 going forward -#set(CMAKE_CXX_STANDARD 17) -#set(CMAKE_CXX_STANDARD_REQUIRED ON) -#set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") -#set(PARTHENON_ENABLE_CPP17 ON CACHE BOOL "KHARMA Override") -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17") +set(PARTHENON_ENABLE_CPP17 ON CACHE BOOL "KHARMA Override") # Set the path to include cmake/ dir list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -27,8 +26,6 @@ set(PARTHENON_DISABLE_SPARSE ON CACHE BOOL "KHARMA Override") # Parthenon internal build options set(BUILD_TESTING OFF CACHE BOOL "KHARMA Override") set(ENABLE_COMPILER_WARNINGS OFF CACHE BOOL "KHARMA Override") -# TODO upstream Parthenon needs support before we do this -#set(COORDINATE_TYPE GRCoordinates) # Always use static HDF5 set(HDF5_USE_STATIC_LIBRARIES ON CACHE BOOL "KHARMA Override") @@ -39,9 +36,9 @@ set(Kokkos_ENABLE_CUDA_CONSTEXPR ON CACHE BOOL "KHARMA Override") set(Kokkos_ENABLE_HWLOC OFF CACHE BOOL "KHARMA Override") # Possible speed improvement? set(Kokkos_ENABLE_AGGRESSIVE_VECTORIZATION ON CACHE BOOL "KHARMA Override") -# If we build KokkosKernels at all, disable the extras -set(KokkosKernels_ENABLE_TPL_CUSPARSE OFF CACHE BOOL "KHARMA Override") -set(KokkosKernels_ENABLE_TPL_CUBLAS OFF CACHE BOOL "KHARMA Override") +include(GetGitRevisionDescription) +get_git_head_revision(GIT_REFSPEC GIT_SHA1) +git_describe_working_tree(GIT_VERSION --tags) # Offer a KHARMA option to disable the MPI requirement # The only difference from setting PARTHENON_DISABLE_MPI is that diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 00000000..4fbd90db --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,284 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_describe_working_tree( [ ...]) +# +# Returns the results of git describe on the working tree (--dirty option), +# and adjusting the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2013, Iowa State University. +# Copyright 2013-2020, Ryan Pavlik +# Copyright 2013-2020, Contributors +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +# Function _git_find_closest_git_dir finds the next closest .git directory +# that is part of any directory in the path defined by _start_dir. +# The result is returned in the parent scope variable whose name is passed +# as variable _git_dir_var. If no .git directory can be found, the +# function returns an empty string via _git_dir_var. +# +# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and +# neither foo nor bar contain a file/directory .git. This wil return +# C:/bla/.git +# +function(_git_find_closest_git_dir _start_dir _git_dir_var) + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(git_previous_parent "${cur_dir}") + get_filename_component(cur_dir "${cur_dir}" DIRECTORY) + if(cur_dir STREQUAL git_previous_parent) + # We have reached the root directory, we are not in git + set(${_git_dir_var} + "" + PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + set(${_git_dir_var} + "${git_dir}" + PARENT_SCOPE) +endfunction() + +function(get_git_head_revision _refspecvar _hashvar) + _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) + + if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) + else() + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) + endif() + if(NOT "${GIT_DIR}" STREQUAL "") + file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" + "${GIT_DIR}") + if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) + # We've gone above the CMake root dir. + set(GIT_DIR "") + endif() + endif() + if("${GIT_DIR}" STREQUAL "") + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # Check if the current source dir is a git submodule or a worktree. + # In both cases .git is a file instead of a directory. + # + if(NOT IS_DIRECTORY ${GIT_DIR}) + # The following git command will return a non empty string that + # points to the super project working tree if the current + # source dir is inside a git submodule. + # Otherwise the command will return an empty string. + # + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse + --show-superproject-working-tree + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT "${out}" STREQUAL "") + # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE + ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree + file(READ ${GIT_DIR} worktree_ref) + # The .git directory contains a path to the worktree information directory + # inside the parent git repo of the worktree. + # + string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir + ${worktree_ref}) + string(STRIP ${git_worktree_dir} git_worktree_dir) + _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + else() + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_describe_working_tree _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 00000000..aee042f3 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,46 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright 2009-2012, Iowa State University +# Copyright 2011-2015, Contributors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + if(EXISTS "@GIT_DIR@/packed-refs") + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() + diff --git a/external/parthenon b/external/parthenon index de25712e..07516a2e 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit de25712e6f24b15ae2d1b1a8fc2db851b633b3a6 +Subproject commit 07516a2efcc7684a72c2883e6f182ac78403eaf0 diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index 20e10ef6..68e36502 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -55,6 +55,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/reductions) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/emhd) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/wind) +# generate version.cpp file from current git commit in a way that +# 1. Re-generates if hash changes +# 2. Does not re-build everything when hash changes +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/version.cpp" @ONLY) +list(APPEND EXE_NAME_SRC "${CMAKE_CURRENT_BINARY_DIR}/version.cpp" version.hpp) + add_executable(${EXE_NAME} ${EXE_NAME_SRC}) target_link_libraries(${EXE_NAME} PUBLIC kokkos) diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index fc704e51..e9d445c3 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -103,7 +103,8 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // RHS. Must not just be "divB" as that field does not sync boundaries pkg->AddParam("rhs_name", "divB_RHS"); // Construct a solver. We don't need the template parameter, so we use 'int' - BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}, {Metadata::GetUserFlag("B_Cleanup")}); + // TODO TODO + BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}); //, {Metadata::GetUserFlag("B_Cleanup")}); // Set callback solver.user_MatVec = B_Cleanup::CornerLaplacian; @@ -294,7 +295,7 @@ TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) for (auto& pmb : blocks) { auto rc_s = pmb->meshblock_data.Get(); for (auto varlabel : {"pk0", "res0", "temp0", "divB_RHS", "p"}) { - if (rc_s->HasCellVariable(varlabel)) + if (rc_s->HasVariable(varlabel)) rc_s->Remove(varlabel); } } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index d30d8cb7..1d810e91 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -267,7 +267,7 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom // Inflow check // Iterate over zones w/p=0 pmb->par_for_bndry( - "Outflow_check_inflow", IndexRange{0, 0}, domain, coarse, + "Outflow_check_inflow", IndexRange{0, 0}, domain, TopologicalElement::CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 7e6408f8..a8545cbc 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -74,7 +74,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda // printf("Freezing bounds:\n"); const auto domain = BoundaryDomain(bface); pmb->par_for_bndry( - "dirichlet_boundary", vars, domain, coarse, + "dirichlet_boundary", vars, domain, TopologicalElement::CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { if (right) { q(p, k, j, i) = bound(p, k - ke, j - je, i - ie); @@ -157,7 +157,7 @@ void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain const auto &G = pmb->coords; pmb->par_for_bndry( - "dirichlet_boundary", vars, domain, coarse, + "dirichlet_boundary", vars, domain, TopologicalElement::CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { if (right) { bound(p, k - ke, j - je, i - ie) = q(p, k, j, i); diff --git a/kharma/coordinates/gr_coordinates.cpp b/kharma/coordinates/gr_coordinates.cpp index cf8e42f0..18ad3057 100644 --- a/kharma/coordinates/gr_coordinates.cpp +++ b/kharma/coordinates/gr_coordinates.cpp @@ -64,9 +64,10 @@ void init_GRCoordinates(GRCoordinates& G); GRCoordinates::GRCoordinates(const RegionSize &rs, ParameterInput *pin): UniformCartesian(rs, pin), coords(pin) { - n1 = rs.nx1 + 2*Globals::nghost; - n2 = rs.nx2 > 1 ? rs.nx2 + 2*Globals::nghost : 1; - n3 = rs.nx3 > 1 ? rs.nx3 + 2*Globals::nghost : 1; + // TODO use new .symmetric? + n1 = rs.nx(X1DIR) + 2*Globals::nghost; + n2 = rs.nx(X2DIR) > 1 ? rs.nx(X2DIR) + 2*Globals::nghost : 1; + n3 = rs.nx(X3DIR) > 1 ? rs.nx(X3DIR) + 2*Globals::nghost : 1; //cout << "Initialized coordinates with nghost " << Globals::nghost << std::endl; connection_average_points = pin->GetOrAddInteger("coordinates", "connection_average_points", 1); diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index cce50844..8d86489a 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -128,11 +128,10 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta std::shared_ptr> &md_solver = (use_implicit) ? pmesh->mesh_data.GetOrAdd("solver", i) : md_sub_step_final; // Start receiving flux corrections and ghost cells - namespace cb = parthenon::cell_centered_bvars; - auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; if (pmesh->multilevel) - t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face // This reconstructs the primitives (P) at faces and uses them to calculate fluxes @@ -143,9 +142,9 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; if (pmesh->multilevel) { - tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 73027ac4..679e44f0 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -160,7 +160,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: t_start_sync = t_ptou_final; } - auto t_sync_done = parthenon::cell_centered_bvars::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); + auto t_sync_done = parthenon::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); auto t_bounds = t_sync_done; // TODO(BSP) careful about how AMR interacts with below diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 504e8c93..6f48582c 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -129,11 +129,10 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); // Start receiving flux corrections and ghost cells - namespace cb = parthenon::cell_centered_bvars; - auto t_start_recv_bound = tl.AddTask(t_none, cb::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; if (pmesh->multilevel) - t_start_recv_flux = tl.AddTask(t_none, cb::StartReceiveFluxCorrections, md_sub_step_init); + t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face // This reconstructs the primitives (P) at faces and uses them to calculate fluxes @@ -144,9 +143,9 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; if (pmesh->multilevel) { - tl.AddTask(t_fluxes, cb::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, cb::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, cb::SetFluxCorrections, md_sub_step_init); + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index cbd4d949..a1023b1e 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -38,6 +38,7 @@ #include #include "decs.hpp" +#include "version.hpp" // Packages #include "b_flux_ct.hpp" @@ -87,6 +88,11 @@ std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, st std::string problem_name = pin->GetString("parthenon/job", "problem_id"); params.Add("problem", problem_name); + // Finally, the code version. Recorded so it gets passed to output files & for printing + params.Add("version", KHARMA::Version::GIT_VERSION); + params.Add("SHA1", KHARMA::Version::GIT_SHA1); + params.Add("branch", KHARMA::Version::GIT_REFSPEC); + // Update the times with callbacks pkg->MeshPreStepUserWorkInLoop = KHARMA::MeshPreStepUserWorkInLoop; pkg->MeshPostStepUserWorkInLoop = KHARMA::MeshPostStepUserWorkInLoop; diff --git a/kharma/main.cpp b/kharma/main.cpp index 218e9b88..2abc93a8 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -143,14 +143,32 @@ int main(int argc, char *argv[]) signal(SIGSEGV, print_backtrace); #endif + // Begin code block to ensure driver is cleaned up { auto pin = pman.pinput.get(); // All parameters in the input file or command line auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc auto papp = pman.app_input.get(); // The list of callback functions specified above if(MPIRank0()) { + const int &verbose = pmesh->packages.Get("Globals")->Param("verbose"); + // Always print the version header, because it's fun + // TODO(someone) proper banner w/refs, names + const std::string &version = pmesh->packages.Get("Globals")->Param("version"); + const std::string &branch = pmesh->packages.Get("Globals")->Param("branch"); + const std::string &sha1 = pmesh->packages.Get("Globals")->Param("SHA1"); + std::cout << std::endl; + std::cout << "Starting KHARMA, version " << version << std::endl; + if (verbose > 0) std::cout << "Branch " << branch << ", commit hash: " << sha1 << std::endl; + std::cout << std::endl; + std::cout << "KHARMA is released under the BSD 3-clause license." << std::endl; + std::cout << "Source code for this program is available at https://github.com/AFD-Illinois/kharma/" << std::endl; + std::cout << std::endl; + // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation - if (pmesh->packages.Get("Globals")->Param("verbose") > 0) { + if (verbose > 0) { + // Print a list of variables as Parthenon used to (still does) + std::cout << "#Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; + // Print a list of all loaded packages. Surprisingly useful for debugging init logic std::cout << "Packages in use: " << std::endl; for (auto package : pmesh->packages.AllPackages()) { diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index b4f11eaf..a1c2adfb 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -139,8 +139,8 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter // Done device-side for speed (for large 2D meshes this may get bad) but may work fine in HostSpace // Note this covers the full domain on each rank: it doesn't need a grid so it's not a memory problem, // and an MPI synch as is done for beta_min would be a headache - GReal x1min = pmb->pmy_mesh->mesh_size.x1min; - GReal x1max = pmb->pmy_mesh->mesh_size.x1max; + GReal x1min = pmb->pmy_mesh->mesh_size.xmin(X1DIR); // TODO probably could get domain from GRCoords + GReal x1max = pmb->pmy_mesh->mesh_size.xmax(X1DIR); // Add back 2D if torus solution may not be largest in midplane (before tilt ofc) //GReal x2min = pmb->pmy_mesh->mesh_size.x2min; //GReal x2max = pmb->pmy_mesh->mesh_size.x2max; diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 435cd87e..db19c618 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -77,7 +77,9 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) Flag("ProblemGenerator_"+prob); // Also just print this, it's important if (MPIRank0()) { - std::cout << "Initializing problem: " << prob << std::endl; + static bool printed_msg = false; + if (!printed_msg) std::cout << "Initializing problem: " << prob << std::endl; + printed_msg = true; } // Breakout to call the appropriate initialization function, diff --git a/kharma/version.cpp.in b/kharma/version.cpp.in new file mode 100644 index 00000000..7774ebba --- /dev/null +++ b/kharma/version.cpp.in @@ -0,0 +1,8 @@ + +#include "version.hpp" + +using namespace KHARMA; + +const std::string Version::GIT_SHA1 = "@GIT_SHA1@"; +const std::string Version::GIT_VERSION = "@GIT_VERSION@"; +const std::string Version::GIT_REFSPEC = "@GIT_REFSPEC@"; diff --git a/kharma/version.hpp b/kharma/version.hpp new file mode 100644 index 00000000..929b6b45 --- /dev/null +++ b/kharma/version.hpp @@ -0,0 +1,46 @@ +/* + * File: version.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include + +namespace KHARMA +{ + struct Version + { + static const std::string GIT_SHA1; + static const std::string GIT_VERSION; + static const std::string GIT_REFSPEC; + }; +} diff --git a/make.sh b/make.sh index 82261b1e..c95e3f79 100755 --- a/make.sh +++ b/make.sh @@ -8,33 +8,35 @@ # clean: BUILD by re-running cmake, restarting the make process from nothing. # That is, "./make.sh clean" == "make clean" + "make" # Always use 'clean' when switching Release<->Debug or OpenMP<->CUDA -# cuda: Build for GPU with CUDA. Must have 'nvcc' in path -# sycl: Build for GPU with SYCL. Must have 'icpx' in path +# cuda: Build for GPU with CUDA +# sycl: Build for GPU with SYCL +# hip: Build for GPU with HIP # debug: Configure with debug flags: mostly array bounds checks # Note, though, many sanity checks during the run are # actually *runtime* parameters e.g. verbose, flag_verbose, etc # trace: Configure with execution tracing: print at the beginning and end # of most host-side function calls during a step -# See files in machines/ for machine-specific options -# Processors to use. When not specified, will use all. Be a good citizen. +# Disabling features at compile-time: +# nompi: Disable MPI and don't search/link it +# noimplicit: Disable implicit solver, avoids pulling in Kokkos-kernels +# nocleanup: Disable magnetic field cleaning code for resizing, avoids +# pulling in some unofficial Parthenon code. + +# Processors to use. Define this in the machine file. #NPROC=8 ### Load machine-specific configurations ### # This segment sources a series of machine-specific # definitions from the machines/ directory. -# If the current machine isn't listed, this script -# and/or Kokkos will attempt to guess the host architecture, -# which should suffice to compile but may not provide optimal -# performance. - -# See e.g. tacc.sh for an example to get started writing one, +# If the host isn't listed, the CPU & GPU arch will be guessed +# See e.g. tacc.sh for an example to get started writing a machine file, # or specify any options you need manually below # Example Kokkos_ARCH options: -# CPUs: WSM, HSW, BDW, SKX, KNL, AMDAVX, ZEN2, ZEN3, POWER9 +# CPUs: BDW, SKX, KNL, AMDAVX, ZEN2, ZEN3, POWER9 # ARM: ARMV80, ARMV81, ARMV8_THUNDERX2, A64FX -# GPUs: KEPLER35, VOLTA70, TURING75, AMPERE80, INTEL_GEN +# GPUs: VOLTA70, TURING75, AMPERE80, HOPPER90, VEGA90A, INTEL_GEN # HOST_ARCH= # DEVICE_ARCH= @@ -111,40 +113,47 @@ SCRIPT_DIR=$PWD # Generally best to set CXX_NATIVE yourself if you want to be sure, # but we try to be smart about loading the most specific/advanced/ # capable compiler available in PATH. -# Note selection is overridden in HIP, SYCL, and clanggpu modes if [[ -z "$CXX_NATIVE" ]]; then - # If we loaded xlC on Summit, we obviously want to use it - if which xlC >/dev/null 2>&1; then - CXX_NATIVE=xlC - C_NATIVE=xlc - # If Cray environment is loaded (Chicoma), use their wrappers - elif which CC >/dev/null 2>&1; then + # If Cray environment is loaded, use their wrappers + if which CC >/dev/null 2>&1; then CXX_NATIVE=CC C_NATIVE=cc + OMP_FLAG="-homp" # Prefer Intel oneAPI compiler over legacy, both over generic elif which icpx >/dev/null 2>&1; then CXX_NATIVE=icpx C_NATIVE=icx + OMP_FLAG="-fopenmp" elif which icpc >/dev/null 2>&1; then CXX_NATIVE=icpc C_NATIVE=icc + OMP_FLAG="-qopenmp" # Prefer NVHPC over generic compilers elif which nvc++ >/dev/null 2>&1; then CXX_NATIVE=nvc++ C_NATIVE=nvc + OMP_FLAG="-mp" # Maybe we overwrote 'c++' to point to something # Usually this is GCC on Linux systems, which is fine elif which cpp >/dev/null 2>&1; then CXX_NATIVE=c++ C_NATIVE=cc + OMP_FLAG="-fopenmp" # Otherwise, trusty system GCC else CXX_NATIVE=g++ C_NATIVE=gcc + OMP_FLAG="-fopenmp" fi # clang/++ will never be used automatically; # blame Apple, who don't support OpenMP fi +export CXXFLAGS="$OMP_FLAG $CXXFLAGS" + +# Set compilers +# Options are named different so we can override w/wrapper for CUDA +export CXX="$CXX_NATIVE" +export CC="$C_NATIVE" # CUDA loop options: MANUAL1D_LOOP > MDRANGE_LOOP, TPTTR_LOOP & TPTTRTVR_LOOP don't compile # Inner loop must be TVR_INNER_LOOP @@ -152,8 +161,6 @@ fi # Outer: SIMDFOR_LOOP;MANUAL1D_LOOP;MDRANGE_LOOP;TPTTR_LOOP;TPTVR_LOOP;TPTTRTVR_LOOP # Inner: SIMDFOR_INNER_LOOP;TVR_INNER_LOOP if [[ "$ARGS" == *"sycl"* ]]; then - export CXX=icpx - export CC=icx OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -161,9 +168,6 @@ if [[ "$ARGS" == *"sycl"* ]]; then ENABLE_SYCL="ON" ENABLE_HIP="OFF" elif [[ "$ARGS" == *"hip"* ]]; then - export CXX=hipcc - # Is there a hipc? - export CC="$C_NATIVE" OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -171,7 +175,6 @@ elif [[ "$ARGS" == *"hip"* ]]; then ENABLE_SYCL="OFF" ENABLE_HIP="ON" elif [[ "$ARGS" == *"cuda"* ]]; then - export CC="$C_NATIVE" export CXX="$SCRIPT_DIR/bin/nvcc_wrapper" if [[ "$ARGS" == *"wrapper_dryrun"* ]]; then export CXXFLAGS="-dryrun $CXXFLAGS" @@ -188,9 +191,7 @@ elif [[ "$ARGS" == *"cuda"* ]]; then ENABLE_CUDA="ON" ENABLE_SYCL="OFF" ENABLE_HIP="OFF" -elif [[ "$ARGS" == *"clanggpu"* ]]; then - export CXX="clang++" - export CC="clang" +elif [[ "$ARGS" == *"cudahpc"* ]]; then OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -198,8 +199,6 @@ elif [[ "$ARGS" == *"clanggpu"* ]]; then ENABLE_SYCL="OFF" ENABLE_HIP="OFF" else - export CXX="$CXX_NATIVE" - export CC="$C_NATIVE" OUTER_LAYOUT="MDRANGE_LOOP" INNER_LAYOUT="SIMDFOR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -231,6 +230,9 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then H5VER=1.12.2 H5VERU=1_12_2 cd external + if [[ "$ARGS" == *"cleanhdf5"* ]]; then + rm -rf hdf5-${H5VER}/ + fi if [ ! -d hdf5-${H5VER}/ ]; then curl https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${H5VERU}/source/hdf5-${H5VER}.tar.gz -o hdf5-${H5VER}.tar.gz tar xf hdf5-${H5VER}.tar.gz @@ -245,25 +247,34 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then HDF_CC=mpiicc HDF_EXTRA="--enable-parallel" else - HDF_CC=mpicc + # Cray wrappers include MPI + if [[ "$C_NATIVE" == "cc" ]]; then + HDF_CC=cc + else + HDF_CC=mpicc + fi HDF_EXTRA="--enable-parallel" fi fi -set -x + + echo Configuring HDF5... + CC=$HDF_CC sh configure -C $HDF_EXTRA --prefix=$SOURCE_DIR/external/hdf5 --enable-build-mode=production \ - --disable-dependency-tracking --disable-hl --disable-tests --disable-tools --disable-shared --disable-deprecated-symbols -set +x - wait 1 + --disable-dependency-tracking --disable-hl --disable-tests --disable-tools --disable-shared --disable-deprecated-symbols > build-hdf5.log + sleep 1 + echo "Building HDF5 (probably 30s-2min)" # Compiling C takes less memory if [[ -v $NPROC ]]; then - make -j$(( $NPROC * 2 )) + make -j$(( $NPROC * 2 )) >> build-hdf5.log 2>&1 else - make -j + make -j >> build-hdf5.log 2>&1 fi - make install - make clean + make install >> build-hdf5.log 2>&1 + make clean >> build-hdf5.log 2>&1 cd ../.. + + echo Built HDF5 fi if [[ "$ARGS" == *"hdf5"* ]]; then PREFIX_PATH="$SOURCE_DIR/external/hdf5;$PREFIX_PATH" @@ -273,12 +284,9 @@ fi # If we're doing a clean build, prep the source and # delete the build directory if [[ "$ARGS" == *"clean"* ]]; then - echo - echo "Patching Parthenon to use KHARMA coordinates." - echo "You may see patch errors here, this is normal." cd external/parthenon - git apply ../patches/parthenon-*.patch + git apply --quiet ../patches/parthenon-*.patch cd - rm -rf build From 5a5c580695280e3409480e71191964269fc34c42 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 11 Aug 2023 14:02:17 -0600 Subject: [PATCH 105/219] Update for current Parthenon, make script updates. --- external/parthenon | 2 +- kharma/driver/kharma_driver.hpp | 10 ++++-- machines/README.md | 28 +++++++++++++++++ make.sh | 55 +++++++++++++++------------------ 4 files changed, 62 insertions(+), 33 deletions(-) create mode 100644 machines/README.md diff --git a/external/parthenon b/external/parthenon index 07516a2e..a92df6e4 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 07516a2efcc7684a72c2883e6f182ac78403eaf0 +Subproject commit a92df6e4163291963b5d84136362782d56427484 diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 28a61cd4..1001bfb1 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -56,6 +56,9 @@ class KHARMADriver : public MultiStageDriver { static std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + // Eliminate Parthenon's print statements when starting up the driver, we have a bunch of our own + void PreExecute() { timer_main.reset(); } + /** * A Driver object orchestrates everything that has to be done to a mesh to take a step. * The function MakeTaskCollection outlines everything to be done in one sub-step, @@ -88,11 +91,14 @@ class KHARMADriver : public MultiStageDriver { TaskCollection MakeImExTaskCollection(BlockList_t &blocks, int stage); /** - * A simple step for experimentation. Does NOT support MPI, + * A simple step for experimentation/new implementations. Does NOT support MPI, or much of anything optional. */ TaskCollection MakeSimpleTaskCollection(BlockList_t &blocks, int stage); - + /** + * Add the flux calculations in each direction. Since the flux functions are templated on which + * reconstruction is being used, this amounts to a lot of shared lines. + */ static TaskID AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md); /** diff --git a/machines/README.md b/machines/README.md new file mode 100644 index 00000000..2e3ee9d5 --- /dev/null +++ b/machines/README.md @@ -0,0 +1,28 @@ +# Machine files + +## Writing a machine file + +`make.sh` sources a series of machine-specific +definitions from the machines/ directory. + +If the host isn't listed, the CPU & GPU arch will be guessed + +Example Kokkos_ARCH options: +CPUs: BDW, SKX, KNL, AMDAVX, ZEN2, ZEN3, POWER9 +ARM: ARMV80, ARMV81, ARMV8_THUNDERX2, A64FX +HOST_ARCH= + +GPUs: VOLTA70, TURING75, AMPERE80, HOPPER90, VEGA90A, INTEL_GEN +DEVICE_ARCH= + +Compilers to use. +C_NATIVE= +CXX_NATIVE= + +Less common options: +PREFIX_PATH= + +EXTRA_FLAGS + +CXXFLAGS +CFLAGS diff --git a/make.sh b/make.sh index c95e3f79..66279406 100755 --- a/make.sh +++ b/make.sh @@ -1,10 +1,10 @@ #!/bin/bash # Make script for KHARMA -# Used to decide flags and call cmake +# Used to set sensible default flags and call cmake/make # Usage: # ./make.sh [option1] [option2] - +# # clean: BUILD by re-running cmake, restarting the make process from nothing. # That is, "./make.sh clean" == "make clean" + "make" # Always use 'clean' when switching Release<->Debug or OpenMP<->CUDA @@ -16,36 +16,20 @@ # actually *runtime* parameters e.g. verbose, flag_verbose, etc # trace: Configure with execution tracing: print at the beginning and end # of most host-side function calls during a step - -# Disabling features at compile-time: +# hdf5: Download & compile HDF5, rather than looking for a system version +# cleanhdf5: Reconfigure HDF5 from scratch, rather than just recompiling # nompi: Disable MPI and don't search/link it # noimplicit: Disable implicit solver, avoids pulling in Kokkos-kernels # nocleanup: Disable magnetic field cleaning code for resizing, avoids # pulling in some unofficial Parthenon code. +# Many machine files have additional options, check machines/machinename.sh -# Processors to use. Define this in the machine file. -#NPROC=8 +# Make processes to use +# Set conservatively as nvcc/nvc++ uses a *lot* of memory +# Set in environment or override in machine file +NPROC=${NPROC:-8} ### Load machine-specific configurations ### -# This segment sources a series of machine-specific -# definitions from the machines/ directory. -# If the host isn't listed, the CPU & GPU arch will be guessed -# See e.g. tacc.sh for an example to get started writing a machine file, -# or specify any options you need manually below - -# Example Kokkos_ARCH options: -# CPUs: BDW, SKX, KNL, AMDAVX, ZEN2, ZEN3, POWER9 -# ARM: ARMV80, ARMV81, ARMV8_THUNDERX2, A64FX -# GPUs: VOLTA70, TURING75, AMPERE80, HOPPER90, VEGA90A, INTEL_GEN - -# HOST_ARCH= -# DEVICE_ARCH= -# C_NATIVE= -# CXX_NATIVE= - -# Less common options: -# PREFIX_PATH= - HOST=$(hostname -f) if [ -z $HOST ]; then HOST=$(hostname) @@ -91,10 +75,11 @@ fi if [[ "$(which python3 2>/dev/null)" == *"conda"* ]]; then echo echo "It looks like you have Anaconda loaded." - echo "Anaconda forces a serial version of HDF5 which may make this compile impossible." + echo "Anaconda loads a serial version of HDF5 which may make this compile impossible." echo "If you run into trouble, deactivate your environment with 'conda deactivate'" fi # Save arguments if we've changed them +# Used in run.sh for loading the same modules/etc. if [[ "$ARGS" == *"clean"* ]]; then echo "$ARGS" > $SOURCE_DIR/make_args fi @@ -118,7 +103,8 @@ if [[ -z "$CXX_NATIVE" ]]; then if which CC >/dev/null 2>&1; then CXX_NATIVE=CC C_NATIVE=cc - OMP_FLAG="-homp" + # In case this isn't Cray, use the more common flag + OMP_FLAG="-fopenomp" # Prefer Intel oneAPI compiler over legacy, both over generic elif which icpx >/dev/null 2>&1; then CXX_NATIVE=icpx @@ -148,7 +134,11 @@ if [[ -z "$CXX_NATIVE" ]]; then # clang/++ will never be used automatically; # blame Apple, who don't support OpenMP fi -export CXXFLAGS="$OMP_FLAG $CXXFLAGS" +# Disable OpenMP for HIP compiles, it gets confused +# and thinks we want to use OMP 5.0 offload stuff +if [[ "$ARGS" != *"hip"* ]]; then + export CXXFLAGS="$OMP_FLAG $CXXFLAGS" +fi # Set compilers # Options are named different so we can override w/wrapper for CUDA @@ -191,7 +181,7 @@ elif [[ "$ARGS" == *"cuda"* ]]; then ENABLE_CUDA="ON" ENABLE_SYCL="OFF" ENABLE_HIP="OFF" -elif [[ "$ARGS" == *"cudahpc"* ]]; then +elif [[ "$ARGS" == *"nvc++"* ]]; then OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" ENABLE_OPENMP="ON" @@ -230,11 +220,16 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then H5VER=1.12.2 H5VERU=1_12_2 cd external + # Allow complete reconfigure (for switching compilers, takes longer) if [[ "$ARGS" == *"cleanhdf5"* ]]; then rm -rf hdf5-${H5VER}/ fi - if [ ! -d hdf5-${H5VER}/ ]; then + # Download if needed + if [ ! -f hdf5-${H5VER}.tar.gz ]; then curl https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${H5VERU}/source/hdf5-${H5VER}.tar.gz -o hdf5-${H5VER}.tar.gz + fi + # Unpack if needed (or deleted) + if [ ! -d hdf5-${H5VER}/ ]; then tar xf hdf5-${H5VER}.tar.gz fi cd hdf5-${H5VER}/ From 4a4b493ab25fe649ab0372d32a4689cf032fd740 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 15 Aug 2023 13:54:21 -0600 Subject: [PATCH 106/219] Pull in new DataCollection machinery, remove overrides. Compiles but likely bugs --- external/parthenon | 2 +- .../parthenon-use-gr-coordinates.patch | 27 +------------------ kharma/coordinates/coordinate_utils.hpp | 1 + 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/external/parthenon b/external/parthenon index a92df6e4..3020f6c5 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit a92df6e4163291963b5d84136362782d56427484 +Subproject commit 3020f6c59e4ca354c9e066252ccdc6848b4ced14 diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index 3b4b816c..fbb4bb3b 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -32,29 +32,4 @@ index d1290dee..50bfc840 100644 namespace parthenon { -diff --git a/src/interface/data_collection.cpp b/src/interface/data_collection.cpp -index 6a1d72c9..b5ba609b 100644 ---- a/src/interface/data_collection.cpp -+++ b/src/interface/data_collection.cpp -@@ -48,7 +48,7 @@ std::shared_ptr DataCollection::Add(const std::string &name, - if (it != containers_.end()) { - // check to make sure they are the same - if (!(*src == *(it->second))) { -- PARTHENON_THROW("Error attempting to add a Container to a Collection"); -+ //PARTHENON_THROW("Error attempting to add a Container to a Collection"); - } - return it->second; - } -diff --git a/src/interface/meshblock_data.cpp b/src/interface/meshblock_data.cpp -index 8d5dca57..0ab7dad8 100644 ---- a/src/interface/meshblock_data.cpp -+++ b/src/interface/meshblock_data.cpp -@@ -440,7 +440,7 @@ MeshBlockData::GetVariablesByUid(const std::vector &uids) { - - template - void MeshBlockData::Remove(const std::string &label) { -- throw std::runtime_error("MeshBlockData::Remove not yet implemented"); -+ varMap_.erase(label); - } - - template + diff --git a/kharma/coordinates/coordinate_utils.hpp b/kharma/coordinates/coordinate_utils.hpp index 98e6a54e..79e95fec 100644 --- a/kharma/coordinates/coordinate_utils.hpp +++ b/kharma/coordinates/coordinate_utils.hpp @@ -34,6 +34,7 @@ #pragma once #include "decs.hpp" +#include "matrix.hpp" /** * Rotate a set of coordinates 'Xin' by 'angle' about the *y-axis* From db690b3ce16f12ddaa8a76800b3a6f60d1178ed3 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 17 Aug 2023 12:33:23 -0500 Subject: [PATCH 107/219] Fix IL compile by default-disabling linker tricks, bump IL NPROC --- machines/illinois.sh | 2 ++ make.sh | 11 +++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/machines/illinois.sh b/machines/illinois.sh index f0355603..66506a85 100644 --- a/machines/illinois.sh +++ b/machines/illinois.sh @@ -13,9 +13,11 @@ elif [[ $HOST == *".astro.illinois.edu" ]]; then HOST_ARCH="ZEN2" # BH29 benefits from using just 1 thread/core export OMP_NUM_THREADS=64 + NPROC=64 else # Other machines are Skylake HOST_ARCH="SKX" + NPROC=36 fi # Compile our own HDF5 by default diff --git a/make.sh b/make.sh index 66279406..eec24df6 100755 --- a/make.sh +++ b/make.sh @@ -109,7 +109,7 @@ if [[ -z "$CXX_NATIVE" ]]; then elif which icpx >/dev/null 2>&1; then CXX_NATIVE=icpx C_NATIVE=icx - OMP_FLAG="-fopenmp" + OMP_FLAG="-fiopenmp" elif which icpc >/dev/null 2>&1; then CXX_NATIVE=icpc C_NATIVE=icc @@ -200,9 +200,10 @@ fi # Allow for a custom linker program, but use CXX by # default as system linker may be older/incompatible if [[ -v LINKER ]]; then - LINKER="$LINKER" -else - LINKER="$CXX" + EXTRA_FLAGS="-DCMAKE_LINKER=$LINKER" +fi +if [[ "$ARGS" == *"special_link_line"* ]]; then + EXTRA_FLAGS="-DCMAKE_CXX_LINK_EXECUTABLE=' -o '" fi # Avoid warning on nvcc pragmas Intel doesn't like @@ -298,8 +299,6 @@ if [[ "$ARGS" == *"clean"* ]]; then cmake ..\ -DCMAKE_C_COMPILER="$CC" \ -DCMAKE_CXX_COMPILER="$CXX" \ - -DCMAKE_LINKER="$LINKER" \ - -DCMAKE_CXX_LINK_EXECUTABLE=' -o ' \ -DCMAKE_PREFIX_PATH="$PREFIX_PATH;$CMAKE_PREFIX_PATH" \ -DCMAKE_BUILD_TYPE=$TYPE \ -DPAR_LOOP_LAYOUT=$OUTER_LAYOUT \ From 2f9fbdddac329d41896c8f9b365db7c35b0d3758 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 17 Aug 2023 21:39:20 -0600 Subject: [PATCH 108/219] The reductions commit For Frontier (not to mention vectorization), I had to make some updates to the reductions code. It's now "polymorphic" via templating on an Enum, much like the reconstruction schemes. I foresee expanding the pattern to the fluxes code soon. The idea is to generate a different host function & kernel for each device function you want to run, rather than putting a giant switch statement into a kernel switching between all the different things it could do. Simpler kernel -> less registers -> fast. I took the opportunity to also fix the issue with Reducer objects, which were causing errors on exit as they were usually declared static. I provided a wrapper that hosts a central registry of all the Reducers in use, stored in a new Reductions package along side the per-block reduction code. Finally, I consolidated some reductions to single kernels so flag_verbose=2 should no longer be much of a performance hit, and should print implicit solver failure states along with floors and primitive recovery failures. The new reductions stuff is flexible enough to replace debug.cpp, which was fun. --- kharma/b_cd/b_cd.cpp | 30 +- kharma/b_cleanup/b_cleanup.cpp | 39 +-- kharma/b_cleanup/b_cleanup.hpp | 10 +- kharma/b_ct/b_ct.cpp | 14 +- kharma/b_ct/b_ct.hpp | 18 +- kharma/b_flux_ct/b_flux_ct.cpp | 17 +- kharma/b_flux_ct/b_flux_ct.hpp | 20 +- kharma/boundaries/boundaries.cpp | 4 +- kharma/debug.cpp | 167 ----------- kharma/debug.hpp | 51 ---- kharma/driver/imex_step.cpp | 54 ++-- kharma/driver/kharma_driver.cpp | 32 +- kharma/driver/kharma_driver.hpp | 26 +- kharma/driver/kharma_step.cpp | 7 - kharma/floors/floors.cpp | 20 +- kharma/floors/floors.hpp | 3 +- kharma/flux/flux.cpp | 35 ++- kharma/flux/flux.hpp | 5 +- kharma/grmhd/grmhd.cpp | 48 +-- kharma/grmhd/grmhd_reductions.hpp | 6 +- kharma/implicit/{fixup.cpp => fix_solve.cpp} | 8 +- kharma/implicit/implicit.cpp | 77 ++--- kharma/implicit/implicit.hpp | 15 +- kharma/inverter/invert_template.hpp | 4 +- kharma/inverter/inverter.cpp | 85 +++--- kharma/kharma.cpp | 18 +- kharma/kharma.hpp | 45 +-- kharma/prob/post_initialize.cpp | 55 ++-- kharma/prob/problem.cpp | 1 - kharma/prob/resize_restart.cpp | 1 - kharma/reductions/reductions.cpp | 277 ++++++----------- kharma/reductions/reductions.hpp | 86 ++++-- kharma/reductions/reductions_impl.hpp | 296 +++++++++++++++++++ kharma/reductions/reductions_types.hpp | 121 ++++++++ kharma/reductions/reductions_variables.hpp | 249 ++++++++++++++++ kharma/types.hpp | 1 + machines/bp.sh | 8 + make.sh | 1 + 38 files changed, 1195 insertions(+), 759 deletions(-) delete mode 100644 kharma/debug.cpp delete mode 100644 kharma/debug.hpp rename kharma/implicit/{fixup.cpp => fix_solve.cpp} (96%) create mode 100644 kharma/reductions/reductions_impl.hpp create mode 100644 kharma/reductions/reductions_types.hpp create mode 100644 kharma/reductions/reductions_variables.hpp diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index a89a0a6d..bb7d8f7e 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -223,20 +223,7 @@ Real MaxDivB(MeshData *md) TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) { - auto pmesh = md->GetMeshPointer(); - - // Print this unless we quash everything - int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - if (verbose >= 0) { - static Reduce max_divb; - max_divb.val = B_CD::MaxDivB(md); - max_divb.StartReduce(0, MPI_MAX); - while (max_divb.CheckReduce() == TaskStatus::incomplete); - - if(MPIRank0()) { - std::cout << "Max DivB: " << max_divb.val << std::endl; - } - } + // TODO. Unify w/other B? return TaskStatus::complete; } @@ -280,16 +267,17 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin) void UpdateCtopMax(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { + // TODO use new Reductions stuff for this // Reduce and record the maximum sound speed on the grid, to propagate // phi at that speed next step. // Just needs to run after every step, so we use the KHARMA callback at that point. - auto& params = pmesh->packages.Get("B_CD")->AllParams(); - static AllReduce ctop_max_last_r; - ctop_max_last_r.val = params.Get("ctop_max"); - ctop_max_last_r.StartReduce(MPI_MAX); - while (ctop_max_last_r.CheckReduce() == TaskStatus::incomplete); - params.Update("ctop_max_last", ctop_max_last_r.val); - params.Update("ctop_max", 0.0); // Reset for next max calculation + // auto& params = pmesh->packages.Get("B_CD")->AllParams(); + // static AllReduce ctop_max_last_r; + // ctop_max_last_r.val = params.Get("ctop_max"); + // ctop_max_last_r.StartReduce(MPI_MAX); + // while (ctop_max_last_r.CheckReduce() == TaskStatus::incomplete); + // params.Update("ctop_max_last", ctop_max_last_r.val); + // params.Update("ctop_max", 0.0); // Reset for next max calculation } } // namespace B_CD diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index e9d445c3..23de26ef 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -38,6 +38,7 @@ #include "boundaries.hpp" #include "decs.hpp" +#include "kharma.hpp" #include "kharma_driver.hpp" #include "grmhd.hpp" #include "kharma.hpp" @@ -217,7 +218,7 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) } // Calculate/print inital max divB exactly as we would during run - const double divb_start = B_FluxCT::GlobalMaxDivB(md.get()); + const double divb_start = B_FluxCT::GlobalMaxDivB(md.get(), true); if (divb_start < rel_tolerance && !always_solve) { // If divB is "pretty good" and we allow not solving... if (MPIRank0()) @@ -236,22 +237,8 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) KHARMADriver::SyncAllBounds(md); // Add a solver container and associated MeshData - for (auto& pmb : pmesh->block_list) { - auto &base = pmb->meshblock_data.Get(); - pmb->meshblock_data.Add("solve", base); - } - // The "solve" container really only needs the RHS, the solution, and the scratch array dB - // This does not affect the main container, but saves a *lot* of time not syncing - // static variables. - // There's no MeshData-wide 'Remove' so we go block-by-block - for (auto& pmb : pmesh->block_list) { - auto rc_s = pmb->meshblock_data.Get("solve"); - auto vars = rc_s->GetVariablesByFlag({Metadata::GetUserFlag("MHD")}).vars(); - for (auto var : vars) { - rc_s->Remove(var->label()); - } - } - auto &msolve = pmesh->mesh_data.GetOrAdd("solve", 0); + std::vector names = KHARMA::GetVariableNames(&pmesh->packages, Metadata::GetUserFlag("B_Cleanup")); + auto &msolve = pmesh->mesh_data.Add("solve", names); // Create a TaskCollection of just the solve, // execute it to perform BiCGStab iteration @@ -285,24 +272,6 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) return TaskStatus::complete; } -TaskStatus B_Cleanup::RemoveExtraFields(BlockList_t &blocks) -{ - // If we aren't needed to clean anything... - if (! (blocks[0]->packages.Get("B_Cleanup")->Param("cleanup_interval") > 0)) { - // remove the internal BiCGStab variables by name, - // to prevent them weighing down MPI exchanges - // TODO anything FillGhost & not Conserved or Primitive - for (auto& pmb : blocks) { - auto rc_s = pmb->meshblock_data.Get(); - for (auto varlabel : {"pk0", "res0", "temp0", "divB_RHS", "p"}) { - if (rc_s->HasVariable(varlabel)) - rc_s->Remove(varlabel); - } - } - } - return TaskStatus::complete; -} - TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) { // Apply on physical zones only, we'll be syncing/updating ghosts diff --git a/kharma/b_cleanup/b_cleanup.hpp b/kharma/b_cleanup/b_cleanup.hpp index ebb4037f..b75652d8 100644 --- a/kharma/b_cleanup/b_cleanup.hpp +++ b/kharma/b_cleanup/b_cleanup.hpp @@ -67,14 +67,8 @@ TaskStatus CleanupDivergence(std::shared_ptr>& md); bool CleanupThisStep(Mesh* pmesh, int nstep); /** - * Remove the extra solver fields which B_Cleanup added during initialization. - * Must be run before every step as the meshblocks are reconstructed per-step from - * package variable lists. - */ -TaskStatus RemoveExtraFields(BlockList_t &blocks); - -/** - * Calculate the laplacian using divergence at corners + * Calculate the laplacian using divergence at corners. + * Extra MeshData arg is just to satisfy Parthenon solver calling convention */ TaskStatus CornerLaplacian(MeshData* md, const std::string& p_var, MeshData* md_again, const std::string& lap_var); diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 2b91ed72..2440bfd6 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -383,13 +383,15 @@ double B_CT::BlockMaxDivB(MeshBlockData *rc) return max_divb; } -double B_CT::GlobalMaxDivB(MeshData *md) +double B_CT::GlobalMaxDivB(MeshData *md, bool all_reduce) { - static AllReduce max_divb; - max_divb.val = MaxDivB(md); - max_divb.StartReduce(MPI_MAX); - while (max_divb.CheckReduce() == TaskStatus::incomplete); - return max_divb.val; + if (all_reduce) { + Reductions::StartToAll(md, 2, MaxDivB(md), MPI_MAX); + return Reductions::CheckOnAll(md, 2); + } else { + Reductions::Start(md, 2, MaxDivB(md), MPI_MAX); + return Reductions::Check(md, 2); + } } TaskStatus B_CT::PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index b5b51dbb..0d45a5d3 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -98,7 +98,7 @@ double BlockMaxDivB(MeshBlockData *rc); /** * Returns the global maximum value, rather than the maximum over this rank's MeshData */ -double GlobalMaxDivB(MeshData *md); +double GlobalMaxDivB(MeshData *md, bool all_reduce=false); /** * Diagnostics printed/computed after each step @@ -124,22 +124,6 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin); */ void CalcDivB(MeshData *md, std::string divb_field_name="divB"); -// Reductions: FOR LATER -// KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) -// { -// // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 -// return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B -// } - -// inline Real ReducePhi0(MeshData *md) -// { -// return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); -// } -// inline Real ReducePhi5(MeshData *md) -// { -// return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); -// } - // Device functions template KOKKOS_INLINE_FUNCTION Real face_div(const GRCoordinates &G, Global &v, const int &ndim, const int &k, const int &j, const int &i) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index af2f93e6..6f846c02 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -478,7 +478,8 @@ double MaxDivB(MeshData *md) const IndexRange kb = IndexRange{kbl.s, kbl.e + (ndim > 2)}; const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; - // TODO Keep zone of max! Also applies to ctop. + // TODO Keep zone of max! See timestep calc + // Will need to translate them back to KS to make them useful though // This is one kernel call per block, because each block will have different bounds. // Could consolidate at the cost of lots of bounds checking. @@ -504,13 +505,15 @@ double MaxDivB(MeshData *md) return max_divb; } -double GlobalMaxDivB(MeshData *md) +double GlobalMaxDivB(MeshData *md, bool all_reduce) { - static AllReduce max_divb; - max_divb.val = MaxDivB(md); - max_divb.StartReduce(MPI_MAX); - while (max_divb.CheckReduce() == TaskStatus::incomplete); - return max_divb.val; + if (all_reduce) { + Reductions::StartToAll(md, 2, MaxDivB(md), MPI_MAX); + return Reductions::CheckOnAll(md, 2); + } else { + Reductions::Start(md, 2, MaxDivB(md), MPI_MAX); + return Reductions::Check(md, 2); + } } TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 0fa89728..ffdd4c3f 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -110,8 +110,10 @@ double MaxDivB(MeshData *md); /** * Returns the global maximum value, rather than the maximum over this rank's MeshData + * + * By default, only returns the correct value on rank 0 for printing */ -double GlobalMaxDivB(MeshData *md); +double GlobalMaxDivB(MeshData *md, bool all_reduce=false); /** * Diagnostics printed/computed after each step @@ -124,7 +126,7 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb=false) */ inline TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) { - auto& params = md->GetMeshPointer()->block_list[0]->packages.Get("B_FluxCT")->AllParams(); + auto& params = md->GetMeshPointer()->packages.Get("B_FluxCT")->AllParams(); return PrintGlobalMaxDivB(md, params.Get("kill_on_large_divb")); } @@ -133,25 +135,17 @@ inline TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) */ void FillOutput(MeshBlock *pmb, ParameterInput *pin); /** - * Fill field "name" with divB + * Fill the field 'divb_field_name' with divB */ void CalcDivB(MeshData *md, std::string divb_field_name="divB"); -// Reductions: phi uses global machinery, but divB is too -// Can also sum the hemispheres independently to be fancy (TODO?) -KOKKOS_INLINE_FUNCTION Real phi(REDUCE_FUNCTION_ARGS_EH) -{ - // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 - return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B -} - inline Real ReducePhi0(MeshData *md) { - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 0); + return Reductions::EHReduction(md, UserHistoryOperation::sum, 0); } inline Real ReducePhi5(MeshData *md) { - return Reductions::EHReduction(md, UserHistoryOperation::sum, phi, 5); + return Reductions::EHReduction(md, UserHistoryOperation::sum, 5); } /** diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 20561573..e3ac6c15 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -76,8 +76,8 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: Metadata m_x1, m_x2, m_x3; { - // We can't use GetVariablesByFlag yet, so walk through and count manually - int nvar = KHARMA::CountVars(packages.get(), Metadata::FillGhost); + // We can't use GetVariablesByFlag yet, so ask the packages + int nvar = KHARMA::PackDimension(packages.get(), Metadata::FillGhost); // We also don't know the mesh size, since it's not constructed. We infer. const int ng = pin->GetInteger("parthenon/mesh", "nghost"); diff --git a/kharma/debug.cpp b/kharma/debug.cpp deleted file mode 100644 index be3aa6e7..00000000 --- a/kharma/debug.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * File: debug.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "debug.hpp" - -#include "decs.hpp" - -#include "floors.hpp" -#include "grmhd_functions.hpp" -#include "types.hpp" - -// TODO make this a DomainReduce, and add better verbosity options -// TODO - -TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain) -{ - Flag("CheckNaN"); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - - // Pack variables - auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); - auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); - - // Get sizes - IndexRange ib = md->GetBoundsI(IndexDomain::interior); - IndexRange jb = md->GetBoundsJ(IndexDomain::interior); - IndexRange kb = md->GetBoundsK(IndexDomain::interior); - IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; - - // TODO these two kernels can be one with some Kokkos magic - int nzero = 0, nnan = 0; - Kokkos::Sum zero_reducer(nzero); - Kokkos::Sum nan_reducer(nnan); - pmb0->par_reduce("ctop_zeros", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (m::max(cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i)) <= 0.) { - ++local_result; - } - } - , zero_reducer); - pmb0->par_reduce("ctop_nans", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (m::isnan(m::max(cmax(b, dir-1, k, j, i), cmin(b, dir-1, k, j, i)))) { - ++local_result; - printf("ctop NaN at %d %d %d along dir %d\n", i, j, k, dir); // EDIT - } - } - , nan_reducer); - - // Reductions in parallel - // Only need to reduce to head node, saves time - static Reduce nzero_tot, nnan_tot; - nzero_tot.val = nzero; - nnan_tot.val = nnan; - nzero_tot.StartReduce(0, MPI_SUM); - nnan_tot.StartReduce(0, MPI_SUM); - while (nzero_tot.CheckReduce() == TaskStatus::incomplete); - while (nnan_tot.CheckReduce() == TaskStatus::incomplete); - nzero = nzero_tot.val; - nnan = nnan_tot.val; - - if (MPIRank0() && (nzero > 0 || nnan > 0)) { - // TODO string formatting in C++ that doesn't suck - fprintf(stderr, "Max signal speed ctop was 0 or NaN, direction %d (%d zero, %d NaN)", dir, nzero, nnan); - throw std::runtime_error("Bad ctop!"); - } - - // TODO reimplement printing *where* these values were hit? - // May not even be that useful, as the cause is usually much earlier - - EndFlag(); - return TaskStatus::complete; -} - -TaskStatus CheckNegative(MeshData *md, IndexDomain domain) -{ - Flag("CheckNegative"); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - // Pack variables - auto rho_p = md->PackVariables(std::vector{"prims.rho"}); - auto u_p = md->PackVariables(std::vector{"prims.u"}); - auto rho_c = md->PackVariables(std::vector{"cons.rho"}); - // Get sizes - IndexRange ib = md->GetBoundsI(domain); - IndexRange jb = md->GetBoundsJ(domain); - IndexRange kb = md->GetBoundsK(domain); - IndexRange block = IndexRange{0, rho_p.GetDim(5)-1}; - - // Check for negative values in the conserved vars - int nless = 0; - Kokkos::Sum sum_reducer(nless); - pmb0->par_reduce("count_negative_U", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (rho_c(b, 0, k, j, i) < 0.) ++local_result; - } - , sum_reducer); - - int nless_rho = 0, nless_u = 0; - Kokkos::Sum sum_reducer_rho(nless_rho); - Kokkos::Sum sum_reducer_u(nless_u); - pmb0->par_reduce("count_negative_RHO", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (rho_p(b, 0, k, j, i) < 0.) ++local_result; - } - , sum_reducer_rho); - pmb0->par_reduce("count_negative_UU", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if (u_p(b, 0, k, j, i) < 0.) ++local_result; - } - , sum_reducer_u); - - // Reductions in parallel - static Reduce nless_tot, nless_rho_tot, nless_u_tot; - nless_tot.val = nless; - nless_rho_tot.val = nless_rho; - nless_u_tot.val = nless_u; - nless_tot.StartReduce(0, MPI_SUM); - nless_rho_tot.StartReduce(0, MPI_SUM); - nless_u_tot.StartReduce(0, MPI_SUM); - while (nless_tot.CheckReduce() == TaskStatus::incomplete); - while (nless_rho_tot.CheckReduce() == TaskStatus::incomplete); - while (nless_u_tot.CheckReduce() == TaskStatus::incomplete); - nless = nless_tot.val; - nless_rho = nless_rho_tot.val; - nless_u = nless_u_tot.val; - - if (MPIRank0() && nless > 0) { - std::cout << "Number of negative conserved rho: " << nless << std::endl; - } - if (MPIRank0() && (nless_rho > 0 || nless_u > 0)) { - std::cout << "Number of negative primitive rho, u: " << nless_rho << "," << nless_u << std::endl; - } - - EndFlag(); - return TaskStatus::complete; -} diff --git a/kharma/debug.hpp b/kharma/debug.hpp deleted file mode 100644 index ec792836..00000000 --- a/kharma/debug.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * File: debug.hpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include "decs.hpp" -#include "types.hpp" - -// TODO TODO Namespace - -/** - * Check the max signal speed (ctop) for 0-values or NaNs. - * This is a final warning that something is very wrong and we should crash. - */ -TaskStatus CheckNaN(MeshData *md, int dir, IndexDomain domain=IndexDomain::interior); - -/** - * Check the primitive and conserved variables for negative values that definitely shouldn't be negative - * That is: primitive rho, u, conserved rho*u^t - */ -TaskStatus CheckNegative(MeshData *md, IndexDomain domain=IndexDomain::interior); diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 8d86489a..3928a2c5 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -44,7 +44,6 @@ #include "wind.hpp" // Other headers #include "boundaries.hpp" -#include "debug.hpp" #include "flux.hpp" #include "resize_restart.hpp" #include "implicit.hpp" @@ -70,37 +69,28 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta const bool use_jcon = pkgs.count("Current"); const bool use_linesearch = (use_implicit) ? pkgs.at("Implicit")->Param("linesearch") : false; - // If we cleaned up, this added other fields marked FillDerived - // Remove them before we allocate the space - if (use_b_cleanup) { - B_Cleanup::RemoveExtraFields(blocks); - } - - // Allocate the fluid states ("containers") we need for each block - for (auto& pmb : blocks) { - // first make other useful containers - auto &base = pmb->meshblock_data.Get(); - if (stage == 1) { - pmb->meshblock_data.Add("dUdt", base); - for (int i = 1; i < integrator->nstages; i++) - pmb->meshblock_data.Add(integrator->stage_name[i], base); - - if (use_jcon) { - // At the end of the step, updating "mbd_sub_step_final" updates the base - // So we have to keep a copy at the beginning to calculate jcon - pmb->meshblock_data.Add("preserve", base); - // Above only copies on allocate -- ensure we copy every step - Copy>({}, base.get(), pmb->meshblock_data.Get("preserve").get()); - } - - if (use_implicit) { - // When solving, we need a temporary copy with any explicit updates, - // but not overwriting the beginning- or mid-step values - pmb->meshblock_data.Add("solver", base); - if (use_linesearch) { - // Need an additional state for linesearch - pmb->meshblock_data.Add("linesearch", base); - } + // Allocate/copy the things we need + // TODO these can now be reduced by including the var lists/flags which actually need to be allocated + // TODO except the Copy they can be run on step 1 only + if (stage == 1) { + auto &base = pmesh->mesh_data.Get(); + // Fluxes + pmesh->mesh_data.Add("dUdt"); + for (int i = 1; i < integrator->nstages; i++) + pmesh->mesh_data.Add(integrator->stage_name[i]); + // Preserve state for time derivatives if we need to output current + if (use_jcon) { + pmesh->mesh_data.Add("preserve"); + // Above only copies on allocate -- ensure we copy every step + Copy>({}, base.get(), pmesh->mesh_data.Get("preserve").get()); + } + if (use_implicit) { + // When solving, we need a temporary copy with any explicit updates, + // but not overwriting the beginning- or mid-step values + pmesh->mesh_data.Add("solver"); + if (use_linesearch) { + // Need an additional state for linesearch + pmesh->mesh_data.Add("linesearch"); } } } diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index d60ed54d..37a28dec 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -266,5 +266,35 @@ TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconst << "donor_cell, linear_mc, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } - return t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; + auto t_calc_fluxes = t_calculate_flux1 | t_calculate_flux2 | t_calculate_flux3; + + auto t_ctop = t_calc_fluxes; + if (md->GetMeshPointer()->packages.Get("Globals")->Param("extra_checks") > 0) { + auto t_ctop = tl.AddTask(t_calc_fluxes, Flux::CheckCtop, md); + } + + return t_ctop; +} + +void KHARMADriver::SetGlobalTimeStep() +{ + // TODO TODO apply the limits from GRMHD package here + if (tm.dt < 0.1 * std::numeric_limits::max()) { + tm.dt *= 2.0; + } + Real big = std::numeric_limits::max(); + for (auto const &pmb : pmesh->block_list) { + tm.dt = std::min(tm.dt, pmb->NewDt()); + pmb->SetAllowedDt(big); + } + + // TODO start reduce at the end of the per-meshblock stuff, check here +#ifdef MPI_PARALLEL + PARTHENON_MPI_CHECK(MPI_Allreduce(MPI_IN_PLACE, &tm.dt, 1, MPI_PARTHENON_REAL, MPI_MIN, + MPI_COMM_WORLD)); +#endif + + if (tm.time < tm.tlim && + (tm.tlim - tm.time) < tm.dt) // timestep would take us past desired endpoint + tm.dt = tm.tlim - tm.time; } diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index e5a2c64e..6c695ea4 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -59,6 +59,9 @@ class KHARMADriver : public MultiStageDriver { // Eliminate Parthenon's print statements when starting up the driver, we have a bunch of our own void PreExecute() { timer_main.reset(); } + // Also override the timestep calculation, so we can start moving options etc out of GRMHD package + void SetGlobalTimeStep(); + /** * A Driver object orchestrates everything that has to be done to a mesh to take a step. * The function MakeTaskCollection outlines everything to be done in one sub-step, @@ -74,19 +77,22 @@ class KHARMADriver : public MultiStageDriver { * 4. Recover primtive variables * 4a. Apply any stability limits (floors) * 4b. Fix any errors in recovering the primitives, re-apply floors - * 5. Apply any source terms (KEL), or calculate outputs (jcon) which require the change in primitive values + * 5. Apply any source terms (KEL), or calculate outputs (jcon) which use the primitive variables * * This is before any synchronization between different blocks, etc, etc. - * Both task lists proceed roughly in this order, and you'll see the same broad outlines in both. + * All task lists proceed roughly in this order, but differ in which variables they synchronize via MPI, + * or whether they synchronize at all. */ TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); + + /** + * The default step, synchronizing conserved variables and then recovering primitive variables in the ghost zones. + */ TaskCollection MakeDefaultTaskCollection(BlockList_t &blocks, int stage); /** - * This "TaskCollection" (step) - * ImexDriver syncs primitive variables and treats them as fundamental, whereas HARMDriver syncs conserved variables. - * This allows ImexDriver to optionally use a semi-implicit step, adding a per-zone implicit solve via the 'Implicit' - * package, instead of just explicit RK2 time-stepping. This driver also allows explicit-only RK2 operation + * This step syncs primitive variables and treats them as fundamental + * This accommodates semi-implicit stepping, allowing evolving theories with implicit source terms such as extended MHD */ TaskCollection MakeImExTaskCollection(BlockList_t &blocks, int stage); @@ -95,6 +101,8 @@ class KHARMADriver : public MultiStageDriver { */ TaskCollection MakeSimpleTaskCollection(BlockList_t &blocks, int stage); + // The different drivers share substantially similar portions of the full task list, which we gather into + /** * Add the flux calculations in each direction. Since the flux functions are templated on which * reconstruction is being used, this amounts to a lot of shared lines. @@ -102,10 +110,10 @@ class KHARMADriver : public MultiStageDriver { static TaskID AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md); /** - * Add just the synchronization step to a task list tl, dependent upon taskID t_start, syncing mesh mc1 + * Add a synchronization retion to an existing TaskCollection tc. + * Since the region is self-contained, does not return a TaskID * - * This sequence is used identically in several places, so it makes sense - * to define once and use elsewhere. + * This function polls the 'integrator' member or it would be static too */ void AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage); diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 8bb2591b..19a6992c 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -44,7 +44,6 @@ #include "wind.hpp" // Other headers #include "boundaries.hpp" -#include "debug.hpp" #include "flux.hpp" #include "resize_restart.hpp" #include "implicit.hpp" @@ -88,12 +87,6 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const bool use_electrons = pkgs.count("Electrons"); const bool use_jcon = pkgs.count("Current"); - // If we cleaned up, this added other fields marked FillDerived - // Remove them before we allocate the space - if (use_b_cleanup) { - B_Cleanup::RemoveExtraFields(blocks); - } - // Allocate the fluid states ("containers") we need for each block for (auto& pmb : blocks) { // first make other useful containers diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 97f2412c..1dd4e557 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -34,7 +34,6 @@ #include "floors.hpp" #include "floors_functions.hpp" -#include "debug.hpp" #include "grmhd.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" @@ -43,7 +42,7 @@ int CountFFlags(MeshData *md) { - return Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, 0, true); + return Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, true)[0]; } std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shared_ptr& packages) @@ -225,7 +224,7 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain) { - auto pmb = mbd->GetBlockPointer(); + auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); @@ -286,6 +285,9 @@ TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain } ); + //if (flag_verbose) + //Reductions::StartFlagReduce(md, "fflag", FFlag::flag_names, IndexDomain::interior, true, 0); + return TaskStatus::complete; } @@ -297,9 +299,15 @@ TaskStatus Floors::PostStepDiagnostics(const SimTime& tm, MeshData *md) const auto& pars = pmesh->packages.Get("Globals")->AllParams(); const int flag_verbose = pars.Get("flag_verbose"); - // Debugging/diagnostic info about floor and inversion flags - if (flag_verbose >= 1) { - Reductions::CountFlags(md, "fflag", FFlag::flag_names, IndexDomain::interior, flag_verbose, true); + // Debugging/diagnostic info about floor flags + if (flag_verbose > 0) { + // TODO this should move to ApplyGRMHDFloors when everything goes MeshData + Reductions::StartFlagReduce(md, "fflag", FFlag::flag_names, IndexDomain::interior, true, 0); + // Debugging/diagnostic info about floor and inversion flags + Reductions::CheckFlagReduceAndPrintHits(md, "fflag", FFlag::flag_names, IndexDomain::interior, true, 0); } + + // Anything else (energy conservation? Added material stats?) + return TaskStatus::complete; } diff --git a/kharma/floors/floors.hpp b/kharma/floors/floors.hpp index 3e3d9edc..4325680c 100644 --- a/kharma/floors/floors.hpp +++ b/kharma/floors/floors.hpp @@ -80,7 +80,8 @@ static const std::map flag_names = { {TEMP, "TEMPERATURE"}, {KTOT, "ENTROPY"}, {GEOM_RHO_FLUX, "GEOM_RHO_ON_RECON"}, - {GEOM_U_FLUX, "GEOM_U_ON_RECON"}}; + {GEOM_U_FLUX, "GEOM_U_ON_RECON"} +}; } namespace Floors { diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 68bc6196..a49d704a 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -50,7 +50,7 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared // We can't just use GetVariables or something since there's no mesh yet. // That's what this function is for. - int nvar = KHARMA::CountVars(packages.get(), Metadata::WithFluxes); + int nvar = KHARMA::PackDimension(packages.get(), Metadata::WithFluxes); std::cout << "Allocating fluxes with nvar: " << nvar << std::endl; std::vector s_flux({nvar}); // TODO optionally move all these to faces? Not important yet, no output, more memory @@ -266,3 +266,36 @@ void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) } ); } + +TaskStatus Flux::CheckCtop(MeshData *md) +{ + Reductions::DomainReduction(md, UserHistoryOperation::sum, 0); + Reductions::DomainReduction(md, UserHistoryOperation::sum, 1); + return TaskStatus::complete; +} + +TaskStatus Flux::PostStepDiagnostics(const SimTime& tm, MeshData *md) +{ + auto pmesh = md->GetMeshPointer(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + // Options + const auto& pars = pmesh->packages.Get("Globals")->AllParams(); + const int extra_checks = pars.Get("extra_checks"); + + // Check for a soundspeed (ctop) of 0 or NaN + // This functions as a "last resort" check to stop a + // simulation on obviously bad data + if (extra_checks >= 1) { + int nnan = Reductions::Check(md, 0); + int nzero = Reductions::Check(md, 1); + + if (MPIRank0() && (nzero > 0 || nnan > 0)) { + // TODO string formatting in C++ that doesn't suck + fprintf(stderr, "Max signal speed ctop of 0 or NaN (%d zero, %d NaN)", nzero, nnan); + throw std::runtime_error("Bad ctop!"); + } + + } + + return TaskStatus::complete; +} diff --git a/kharma/flux/flux.hpp b/kharma/flux/flux.hpp index 289aa43d..d8907c8c 100644 --- a/kharma/flux/flux.hpp +++ b/kharma/flux/flux.hpp @@ -37,7 +37,6 @@ #include -#include "debug.hpp" #include "floors.hpp" #include "flux_functions.hpp" #include "pack.hpp" @@ -48,6 +47,10 @@ namespace Flux { std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); +TaskStatus CheckCtop(MeshData *md); + +TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md); + /** * Add the geometric source term present in the covariant derivative of the stress-energy tensor, * S_nu = sqrt(-g) T^kap_lam Gamma^lam_nu_kap diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 82b464d9..9c9f1c41 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -41,7 +41,6 @@ #include "boundaries.hpp" #include "current.hpp" -#include "debug.hpp" #include "floors.hpp" #include "flux.hpp" #include "gr_coordinates.hpp" @@ -50,7 +49,6 @@ #include - /** * GRMHD package. Global operations on General Relativistic Magnetohydrodynamic systems. */ @@ -258,26 +256,32 @@ Real EstimateTimestep(MeshBlockData *rc) return globals.Get("dt_light"); } - typename Kokkos::MinMax::value_type minmax; + Reductions::Reduce3v minmax; pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int k, const int j, const int i, - typename Kokkos::MinMax::value_type &lminmax) { + Reductions::Reduce3v &lminmax) { double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / m::max(cmax(0, k, j, i), cmin(0, k, j, i))) + 1 / (G.Dxc<2>(j) / m::max(cmax(1, k, j, i), cmin(1, k, j, i))) + 1 / (G.Dxc<3>(k) / m::max(cmax(2, k, j, i), cmin(2, k, j, i)))); // Effective "max speed" used for the timestep double ctop_max_zone = m::min(G.Dxc<1>(i), m::min(G.Dxc<2>(j), G.Dxc<3>(k))) / ndt_zone; - if (!m::isnan(ndt_zone) && (ndt_zone < lminmax.min_val)) + if (!m::isnan(ndt_zone) && (ndt_zone < lminmax.min_val)) { lminmax.min_val = ndt_zone; - if (!m::isnan(ctop_max_zone) && (ctop_max_zone > lminmax.max_val)) + lminmax.min_loc = std::tuple{i, j, k}; + } + if (!m::isnan(ctop_max_zone) && (ctop_max_zone > lminmax.max_val)) { lminmax.max_val = ctop_max_zone; + lminmax.max_loc = std::tuple{i, j, k}; + } } - , Kokkos::MinMax(minmax)); + , Reductions::Reduce3(minmax)); // Keep dt to do some checks below const double min_ndt = minmax.min_val; const double nctop = minmax.max_val; + // TODO print tuples + // Apply limits const double cfl = grmhd_pars.Get("cfl"); const double dt_min = grmhd_pars.Get("dt_min"); @@ -401,19 +405,27 @@ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *md) const auto& pars = pmesh->packages.Get("Globals")->AllParams(); const int extra_checks = pars.Get("extra_checks"); - // Check for a soundspeed (ctop) of 0 or NaN - // This functions as a "last resort" check to stop a - // simulation on obviously bad data - if (extra_checks >= 1) { - CheckNaN(md, X1DIR); - if (pmesh->ndim > 1) CheckNaN(md, X2DIR); - if (pmesh->ndim > 2) CheckNaN(md, X3DIR); - } - - // Further checking for any negative values. Floors should + // Checking for any negative values. Floors should // prevent this, so we save it for dire debugging if (extra_checks >= 2) { - CheckNegative(md, IndexDomain::interior); + // Not sure when I'd do the check to hide latency, it's a step-end sort of deal + // Just as well it's behind extra_checks 2 + // This may happen while ch0-1 are in flight from floors, but ch2-4 are now reusable + Reductions::DomainReduction(md, UserHistoryOperation::sum, 2); + Reductions::DomainReduction(md, UserHistoryOperation::sum, 3); + Reductions::DomainReduction(md, UserHistoryOperation::sum, 4); + int nless_rho = Reductions::Check(md, 2); + int nless_u = Reductions::Check(md, 3); + int nless_rhout = Reductions::Check(md, 4); + + if (MPIRank0()) { + if (nless_rhout > 0) { + std::cout << "Number of negative conserved rho: " << nless_rhout << std::endl; + } + if (nless_rho > 0 || nless_u > 0) { + std::cout << "Number of negative primitive rho, u: " << nless_rho << "," << nless_u << std::endl; + } + } } return TaskStatus::complete; diff --git a/kharma/grmhd/grmhd_reductions.hpp b/kharma/grmhd/grmhd_reductions.hpp index 2db2cad8..a98f40a3 100644 --- a/kharma/grmhd/grmhd_reductions.hpp +++ b/kharma/grmhd/grmhd_reductions.hpp @@ -59,7 +59,7 @@ KOKKOS_INLINE_FUNCTION Real edot(REDUCE_FUNCTION_ARGS_EH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); // \dot{E} == \int - T^1_0 * gdet * dx2 * dx3 return -T1[X0DIR] * G.gdet(Loci::center, j, i); } @@ -68,7 +68,7 @@ KOKKOS_INLINE_FUNCTION Real ldot(REDUCE_FUNCTION_ARGS_EH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(G, P, m_p, Dtmp, gam, k, j, i, X1DIR, T1); // \dot{L} == \int T^1_3 * gdet * dx2 * dx3 return T1[X3DIR] * G.gdet(Loci::center, j, i); } @@ -121,7 +121,7 @@ KOKKOS_INLINE_FUNCTION Real jet_lum(REDUCE_FUNCTION_ARGS_MESH) FourVectors Dtmp; Real T1[GR_DIM]; GRMHD::calc_4vecs(G, P(b), m_p, k, j, i, Loci::center, Dtmp); - Flux::calc_tensor(P(b), m_p, Dtmp, gam, k, j, i, X1DIR, T1); + Flux::calc_tensor(G, P(b), m_p, Dtmp, gam, k, j, i, X1DIR, T1); // If sigma > 1... if ((dot(Dtmp.bcon, Dtmp.bcov) / P(b, m_p.RHO, k, j, i)) > 1.) { // Energy flux, like at EH. 2D integral jacobian, so we have to take X1 off of auto-applied dV diff --git a/kharma/implicit/fixup.cpp b/kharma/implicit/fix_solve.cpp similarity index 96% rename from kharma/implicit/fixup.cpp rename to kharma/implicit/fix_solve.cpp index 81871696..9c9d7104 100644 --- a/kharma/implicit/fixup.cpp +++ b/kharma/implicit/fix_solve.cpp @@ -1,5 +1,5 @@ /* - * File: fixup.cpp + * File: fix_solve.cpp * * BSD 3-Clause License * @@ -86,7 +86,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { sum_x(ip, k, j, i) = 0.; } // Fix only bad zones - if ((solve_fail(k, j, i)) == SolverStatus::fail) { + if (failed(solve_fail(k, j, i))) { //printf("Fixing zone %d %d %d!\n", i, j, k); double wsum = 0., wsum_x = 0.; // double sum[nfvar] = {0.}, sum_x[nfvar] = {0.}; @@ -102,7 +102,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { double w = 1./(m::abs(l) + m::abs(m) + m::abs(n) + 1); // Count only the good cells, if we can - if ((solve_fail(kk, jj, ii)) != SolverStatus::fail) { + if (!failed(solve_fail(kk, jj, ii))) { // Weight by distance. Note interpolated "fixed" cells stay flagged wsum += w; FLOOP sum(ip, k, j, i) += w * P(ip, kk, jj, ii); @@ -140,7 +140,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { pmb->par_for("fix_solver_failures_PtoU", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& k, const int& j, const int& i) { - if (solve_fail(k, j, i) == SolverStatus::fail) + if (failed(solve_fail(k, j, i))) Flux::p_to_u(G, P_all, m_p, emhd_params, gam, k, j, i, U_all, m_u); } ); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 6254055a..826a4665 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -34,7 +34,6 @@ #include "implicit.hpp" -#include "debug.hpp" #include "grmhd.hpp" #include "grmhd_functions.hpp" #include "kharma.hpp" @@ -125,7 +124,7 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh bool save_residual = pin->GetOrAddBoolean("implicit", "save_residual", false); params.Add("save_residual", save_residual); if (save_residual) { - int nvars_implicit = KHARMA::CountVars(packages.get(), Metadata::GetUserFlag("Implicit")); + int nvars_implicit = KHARMA::PackDimension(packages.get(), Metadata::GetUserFlag("Implicit")); std::vector s_vars_implicit({nvars_implicit}); Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vars_implicit); @@ -153,7 +152,9 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const Real delta = implicit_par.Get("jacobian_delta"); const Real rootfind_tol = implicit_par.Get("rootfind_tol"); const bool use_qr = implicit_par.Get("use_qr"); - const int verbose = pmb_full_step_init->packages.Get("Globals")->Param("verbose"); + const auto& globals = pmb_full_step_init->packages.Get("Globals")->AllParams(); + const int verbose = globals.Get("verbose"); + const int flag_verbose = globals.Get("flag_verbose"); const Real gam = pmb_full_step_init->packages.Get("GRMHD")->Param("gamma"); const bool linesearch = implicit_par.Get("linesearch"); @@ -296,7 +297,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ ScratchPad2D P_linesearch_s(member.team_scratch(scratch_level), n1, nvar); // Scratchpads for solver performance diagnostics ScratchPad1D solve_norm_s(member.team_scratch(scratch_level), n1); - ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); + ScratchPad1D solve_fail_s(member.team_scratch(scratch_level), n1); // Copy some file contents to scratchpads, so we can slice them for(int ip=0; ip < nvar; ++ip) { @@ -321,7 +322,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ } else { // Need this to check if the zone had failed in any of the previous iterations. // If so, we don't attempt to update it again in the implicit solver. - solve_fail_s(i) = solve_fail_all(b, 0, k, j, i); + solve_fail_s(i) = (SolverStatus) solve_fail_all(b, 0, k, j, i); } } ); @@ -534,7 +535,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Did we converge to required tolerance? If not, update solve_fail accordingly if (solve_norm() > rootfind_tol) { - solve_fail() += SolverStatus::beyond_tol; + solve_fail() = SolverStatus::beyond_tol; // TODO was changed from +=. Valid? } } } @@ -555,7 +556,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ parthenon::par_for_inner(member, ib.s, ib.e, [&](const int& i) { solve_norm_all(b, 0, k, j, i) = solve_norm_s(i); - solve_fail_all(b, 0, k, j, i) = solve_fail_s(i); + solve_fail_all(b, 0, k, j, i) = (Real) solve_fail_s(i); } ); } @@ -564,42 +565,44 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // If we need to print or exit on the max norm... if (iter >= iter_min || verbose >= 1) { // Take the maximum L2 norm on this rank - static AllReduce max_norm; - Kokkos::Max norm_max(max_norm.val); + Real lmax_norm = 0.0; pmb_sub_step_init->par_reduce("max_norm", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& b, const int& k, const int& j, const int& i, Real& local_result) { if (solve_norm_all(b, 0, k, j, i) > local_result) local_result = solve_norm_all(b, 0, k, j, i); } - , norm_max); - // Then MPI reduce AllReduce to copy the global max to every rank - max_norm.StartReduce(MPI_MAX); - while (max_norm.CheckReduce() == TaskStatus::incomplete); - if (verbose >= 1 && MPIRank0()) printf("Iteration %d max L2 norm: %g\n", iter, max_norm.val); - - // Count total number of solver fails - // TODO move reductions like this to PostStep - int nfails = 0; - Kokkos::Sum sum_reducer(nfails); - pmb_sub_step_init->par_reduce("count_solver_fails", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int& b, const int& k, const int& j, const int& i, int& local_result) { - if (solve_fail_all(b, 0, k, j, i) == SolverStatus::fail) ++local_result; + , Kokkos::Max(lmax_norm)); + // Then MPI AllReduce to copy the global max to every rank + Reductions::StartToAll(md_solver, 4, lmax_norm, MPI_MAX); + Real max_norm = Reductions::CheckOnAll(md_solver, 4); + + if (verbose >= 1) { + // Count total number of solver fails + int lnfails = 0; + pmb_sub_step_init->par_reduce("count_solver_fails", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& b, const int& k, const int& j, const int& i, int& local_result) { + if ((SolverStatus) solve_fail_all(b, 0, k, j, i) == SolverStatus::fail) ++local_result; + } + , Kokkos::Sum(lnfails)); + // Then reduce to rank 0 to print the iteration by iteration + Reductions::Start(md_solver, 5, lnfails, MPI_SUM); + int nfails = Reductions::Check(md_solver, 5); + if (MPIRank0()) { + printf("Iteration %d max L2 norm: %g, failed zones: %d\n", iter, max_norm, nfails); } - , sum_reducer); - // Then MPI reduce AllReduce to copy the global max to every rank - static AllReduce nfails_tot; - nfails_tot.val = nfails; - nfails_tot.StartReduce(MPI_SUM); - while (nfails_tot.CheckReduce() == TaskStatus::incomplete); - if (verbose >= 1 && MPIRank0()) printf("Number of failed zones: %d\n", nfails_tot.val); - - // Break if max_norm is less than the total tolerance we set. TODO per-zone version of this? - if (iter >= iter_min && max_norm.val < rootfind_tol) break; + } + + // Finally, break if max_norm is less than the total tolerance we set + // TODO per-zone tolerance with masks? + if (iter >= iter_min && max_norm < rootfind_tol) break; } EndFlag(); } - EndFlag(); + if (flag_verbose > 0) { + Reductions::CheckFlagReduceAndPrintHits(md_solver, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); + } + EndFlag(); return TaskStatus::complete; } @@ -613,11 +616,9 @@ TaskStatus Implicit::PostStepDiagnostics(const SimTime& tm, MeshData *md) const int flag_verbose = pars.Get("flag_verbose"); // Debugging/diagnostic info about implicit solver - // TODO status names - // if (flag_verbose >= 1) { - // int nflags = Reductions::CountFlags(md, "solve_fail", Implicit::status_names, IndexDomain::interior, flag_verbose, false); - // // TODO TODO yell here if there are too many flags - // } + if (flag_verbose > 0) { + Reductions::CheckFlagReduceAndPrintHits(md, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); + } return TaskStatus::complete; } diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index 7004cd9f..91b4413b 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -65,7 +65,20 @@ namespace Implicit // `fail`: manual backtracking wasn't good enough. FixSolve will be called // `beyond_tol`: solver didn't converge to prescribed tolerance but didn't fail // `backtrack`: step length of 1 gave negative rho/uu, but manual backtracking (0.1) sufficed -enum SolverStatus{converged=0, fail, beyond_tol, backtrack}; +enum class SolverStatus{converged=0, fail, beyond_tol, backtrack}; + +static const std::map status_names = { + {(int) SolverStatus::fail, "failed"}, + {(int) SolverStatus::beyond_tol, "beyond tolerance"}, + {(int) SolverStatus::backtrack, "backtrack"} +}; + +template +KOKKOS_INLINE_FUNCTION bool failed(T status_flag) +{ + // Return only zones which outright failed + return static_cast(status_flag) == static_cast(SolverStatus::fail); +} /** * Initialization. Set parameters. diff --git a/kharma/inverter/invert_template.hpp b/kharma/inverter/invert_template.hpp index 38a99239..163ee8d7 100644 --- a/kharma/inverter/invert_template.hpp +++ b/kharma/inverter/invert_template.hpp @@ -58,7 +58,9 @@ static const std::map status_names = { {(int) Status::bad_gamma, "Gamma invalid"}, {(int) Status::neg_rho, "Negative rho"}, {(int) Status::neg_u, "Negative U"}, - {(int) Status::neg_rhou, "Negative rho & U"}}; + {(int) Status::neg_rhou, "Negative rho & U"} +}; + template KOKKOS_INLINE_FUNCTION bool failed(T status_flag) { diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index c120d448..62f70e84 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -39,45 +39,6 @@ #include "domain.hpp" #include "reductions.hpp" -/** - * Internal inversion fn, templated on inverter type. Calls through to templated u_to_p - * This is called with the correct template argument from BlockUtoP - */ -template -inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - auto pmb = rc->GetBlockPointer(); - const auto& G = pmb->coords; - - PackIndexMap prims_map, cons_map; - auto U = GRMHD::PackMHDCons(rc, cons_map); - auto P = GRMHD::PackHDPrims(rc, prims_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - - GridScalar pflag = rc->Get("pflag").data; - - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - - const Real err_tol = pmb->packages.Get("Inverter")->Param("err_tol"); - const int iter_max = pmb->packages.Get("Inverter")->Param("iter_max"); - const Real stepsize = pmb->packages.Get("Inverter")->Param("stepsize"); - - // Get the primitives from our conserved versions - // Notice we recover variables for only the physical (interior or MPI-boundary) - // zones! These are the only ones which are filled at our point in the step - auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const IndexRange3 b = KDomain::GetPhysicalRange(rc); - - pmb->par_for("U_to_P", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - if (KDomain::inside(k, j, i, b)) { - // Run over all interior zones and any initialized ghosts - pflag(k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); - } - } - ); -} - std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::shared_ptr& packages) { auto pkg = std::make_shared("Inverter"); @@ -126,6 +87,45 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh return pkg; } +/** + * Internal inversion fn, templated on inverter type. Calls through to templated u_to_p + * This is called with the correct template argument from BlockUtoP + */ +template +inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + auto pmb = rc->GetBlockPointer(); + const auto& G = pmb->coords; + + PackIndexMap prims_map, cons_map; + auto U = GRMHD::PackMHDCons(rc, cons_map); + auto P = GRMHD::PackHDPrims(rc, prims_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + + GridScalar pflag = rc->Get("pflag").data; + + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + + const Real err_tol = pmb->packages.Get("Inverter")->Param("err_tol"); + const int iter_max = pmb->packages.Get("Inverter")->Param("iter_max"); + const Real stepsize = pmb->packages.Get("Inverter")->Param("stepsize"); + + // Get the primitives from our conserved versions + // Notice we recover variables for only the physical (interior or MPI-boundary) + // zones! These are the only ones which are filled at our point in the step + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange3 b = KDomain::GetPhysicalRange(rc); + + pmb->par_for("U_to_P", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (KDomain::inside(k, j, i, b)) { + // Run over all interior zones and any initialized ghosts + pflag(k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); + } + } + ); +} + void Inverter::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { // This only chooses an implementation. See BlockPerformInversion and implementations e.g. onedw.hpp @@ -137,6 +137,7 @@ void Inverter::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coars case Type::none: break; } + //Reductions::StartFlagReduce(md, "pflag", Inverter::status_names, IndexDomain::interior, false, 1); } TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) @@ -148,9 +149,11 @@ TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) const int flag_verbose = pars.Get("flag_verbose"); // Debugging/diagnostic info about floor and inversion flags + // TODO grab the total and die on too many if (flag_verbose >= 1) { - int nflags = Reductions::CountFlags(md, "pflag", Inverter::status_names, IndexDomain::interior, flag_verbose, false); - // TODO TODO yell here if there are too many flags + // TODO this should move into BlockUtoP when everything goes MeshData + Reductions::StartFlagReduce(md, "pflag", Inverter::status_names, IndexDomain::interior, false, 1); + Reductions::CheckFlagReduceAndPrintHits(md, "pflag", Inverter::status_names, IndexDomain::interior, false, 1); } return TaskStatus::complete; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index d7fc2ffe..6bc4d950 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -301,6 +301,8 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) auto t_grmhd = tl.AddTask(t_globals | t_driver, KHARMA::AddPackage, packages, GRMHD::Initialize, pin.get()); // Inverter (TODO: split out fixups, then don't load this when GRMHD isn't loaded) auto t_inverter = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Inverter::Initialize, pin.get()); + // Reductions, needed for most other packages + auto t_reductions = tl.AddTask(t_none, KHARMA::AddPackage, packages, Reductions::Initialize, pin.get()); // B field solvers, to ensure divB ~= 0. // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference @@ -335,12 +337,8 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) if (t_b_field == t_none) t_b_field = t_b_cleanup; } - // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it). - // Since it is never required to restart, this is the only time we'd write (hence, need) it - if (FieldIsOutput(pin.get(), "jcon") && t_b_field != t_none) { - auto t_current = tl.AddTask(t_b_field, KHARMA::AddPackage, packages, Current::Initialize, pin.get()); - } - // Electrons are usually boring but not impossible without a B field (TODO add a test?) + // Optional standalone packages + // Electrons are boring but not impossible without a B field (TODO add a test?) if (pin->GetOrAddBoolean("electrons", "on", false)) { auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Electrons::Initialize, pin.get()); } @@ -350,6 +348,11 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) if (pin->GetOrAddBoolean("wind", "on", false)) { auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Wind::Initialize, pin.get()); } + // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it). + // Since it is never required to restart, this is the only time we'd write (hence, need) it + if (FieldIsOutput(pin.get(), "jcon") && t_b_field != t_none) { + auto t_current = tl.AddTask(t_b_field, KHARMA::AddPackage, packages, Current::Initialize, pin.get()); + } // Execute the whole collection (just in case we do something fancy?) while (!tr.Execute()); // TODO this will inf-loop on error @@ -365,7 +368,8 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); // Load the implicit package last, and only if there are any variables which need implicit evolution - int n_implicit = CountVars(packages.get(), Metadata::GetUserFlag("Implicit")); + auto all_implicit = Metadata::FlagCollection(Metadata::GetUserFlag("Implicit")); + int n_implicit = PackDimension(packages.get(), Metadata::GetUserFlag("Implicit")); if (n_implicit > 0) { KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); } diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index 5928ec3a..3e30e7a2 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -31,6 +31,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once #include "decs.hpp" #include "types.hpp" @@ -109,32 +110,36 @@ inline bool FieldIsOutput(ParameterInput *pin, std::string name) * This fn calculates the size a VariablePack *would* be, without making one -- * it uses only the package list, and counts through each variable in each package. * Mostly useful for initialization. - * TODO can this take flagcollections? Move to Parthenon... */ -inline int CountVars(Packages_t* packages, MetadataFlag flag) +inline int PackDimension(Packages_t* packages, Metadata::FlagCollection fc) { + // We want to exclude anything specific to B field cleanup & not used elsewhere + // (confusingly, this isn't *necessarily* everything in the B_Cleanup package) + if (packages->AllPackages().count("B_Cleanup")) + fc = fc - Metadata::GetUserFlag("B_Cleanup"); + + // Count dimensions (1 for scalars + vector lengths) of each package's variables int nvar = 0; for (auto pkg : packages->AllPackages()) { - for (auto field : pkg.second->AllFields()) { - // Specifically ignore the B_Cleanup variables, we'll never want them separately like this - bool is_not_cleanup = packages->AllPackages().count("B_Cleanup") - ? !field.second.IsSet(Metadata::GetUserFlag("B_Cleanup")) - : true; - if (field.second.IsSet(flag) && is_not_cleanup) { - int var_len = 0; - if (field.second.IsSet(Metadata::Face)) { - var_len = 3; // TODO non-scalar face fields? - } else if (field.second.Shape().size() < 1) { - var_len = 1; - } else { - var_len = field.second.Shape()[0]; - } - //std::cout << "flag: " << flag << " var: " << field.first.label() << " size: " << var_len << std::endl; - nvar += var_len; - } - } + nvar += pkg.second->GetPackDimension(fc); } return nvar; } +/** + * This fn calculates the size a VariablePack *would* be, without making one -- + * it uses only the package list, and counts through each variable in each package. + * Mostly useful for initialization. + */ +inline std::vector GetVariableNames(Packages_t* packages, Metadata::FlagCollection fc) +{ + // Count dimensions (1 for scalars + vector lengths) of each package's variables + std::vector names; + for (auto pkg : packages->AllPackages()) { + std::vector pnames = pkg.second->GetVariableNames(fc); + names.insert(names.end(), pnames.begin(), pnames.end()); + } + return names; +} + } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 7a09c3dc..6b338019 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -41,7 +41,6 @@ #include "b_field_tools.hpp" #include "blob.hpp" #include "boundaries.hpp" -#include "debug.hpp" #include "floors.hpp" #include "flux.hpp" #include "gr_coordinates.hpp" @@ -56,6 +55,7 @@ * Should only be used in initialization code, as the * reducer object & MPI comm are created on entry & * cleaned on exit + * TODO use Reductions stuff? */ template inline T MPIReduce_once(T f, MPI_Op O) @@ -69,35 +69,18 @@ inline T MPIReduce_once(T f, MPI_Op O) return reduction.val; } -// Define reductions we need just for PostInitialize code. -// TODO namespace... -KOKKOS_INLINE_FUNCTION Real bsq(REDUCE_FUNCTION_ARGS_MESH) -{ - FourVectors Dtmp; - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - return dot(Dtmp.bcon, Dtmp.bcov); -} -KOKKOS_INLINE_FUNCTION Real gas_pres(REDUCE_FUNCTION_ARGS_MESH) -{ - return (gam - 1) * P(m_p.UU, k, j, i); -} -KOKKOS_INLINE_FUNCTION Real gas_beta(REDUCE_FUNCTION_ARGS_MESH) -{ - FourVectors Dtmp; - GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); - return ((gam - 1) * P(m_p.UU, k, j, i))/(0.5*(dot(Dtmp.bcon, Dtmp.bcov) + SMALL)); -} +// Shorter names for the reductions we use here Real MaxBsq(MeshData *md) { - return Reductions::DomainReduction(md, UserHistoryOperation::max, bsq, 0.0); + return Reductions::DomainReduction(md, UserHistoryOperation::max); } Real MaxPressure(MeshData *md) { - return Reductions::DomainReduction(md, UserHistoryOperation::max, gas_pres, 0.0); + return Reductions::DomainReduction(md, UserHistoryOperation::max); } Real MinBeta(MeshData *md) { - return Reductions::DomainReduction(md, UserHistoryOperation::min, gas_beta, 0.0); + return Reductions::DomainReduction(md, UserHistoryOperation::min); } void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr> md) @@ -109,7 +92,7 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrpackages.AllPackages().count("B_CD"); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - // TODO this should be restructured... + fprintf(stderr, "0.5"); Flag("SeedBField"); // Seed the magnetic field on each block @@ -125,6 +108,8 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrGetString("parthenon/job", "problem_id"); @@ -142,9 +127,11 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrmesh_data.GetOrAdd("base", 0); + fprintf(stderr, "0.0"); + auto &md = pmesh->mesh_data.Get(); auto& pkgs = pmesh->packages.AllPackages(); + fprintf(stderr, "0.1"); // Magnetic field operations if (pin->GetString("b_field", "solver") != "none") { // If we need to seed a field based on the problem's fluid initialization... if (pin->GetOrAddString("b_field", "type", "none") != "none" && !is_restart) { // B field init is not stencil-1, needs boundaries sync'd. // FreezeDirichlet ensures any Dirichlet conditions aren't overwritten by zeros + fprintf(stderr, "0.2"); KBoundaries::FreezeDirichlet(md); KHARMADriver::SyncAllBounds(md); + fprintf(stderr, "0.3"); // Then init B field on each block... KHARMA::SeedAndNormalizeB(pin, md); } + fprintf(stderr, "4"); // Regardless, if evolving a field we should print max(divB) // divB is not stencil-1 and we may not have run the above. @@ -231,6 +222,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KBoundaries::FreezeDirichlet(md); KHARMADriver::SyncAllBounds(md); + fprintf(stderr, "5"); + if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); } else if (pkgs.count("B_CT")) { @@ -240,6 +233,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } } + fprintf(stderr, "6"); + // Add any hotspots. // Note any other modifications made when restarting should be made around here if (pin->GetOrAddBoolean("blob", "add_blob", false)) { @@ -250,6 +245,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } } + fprintf(stderr, "7"); + // Any extra cleanup & init especially when restarting if (is_restart) { // Parthenon restores all parameters (global vars) when restarting, @@ -257,6 +254,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMA::ResetGlobals(pin, pmesh); } + fprintf(stderr, "8"); + // Clean the B field if we've introduced a divergence somewhere // We call this function any time the package is loaded: // if we decided to load it in kharma.cpp, we need to clean. @@ -269,10 +268,10 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // This does its own MPI syncs B_Cleanup::CleanupDivergence(md); - - B_Cleanup::RemoveExtraFields(pmesh->block_list); } + fprintf(stderr, "9"); + // Finally, synchronize boundary values. // Freeze any Dirichlet physical boundaries as they are now, after cleanup/sync/etc. KBoundaries::FreezeDirichlet(md); diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index db19c618..b6e57c6e 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -36,7 +36,6 @@ #include "b_field_tools.hpp" #include "boundaries.hpp" -#include "debug.hpp" #include "electrons.hpp" #include "floors.hpp" #include "flux.hpp" diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 0a60caaf..07add691 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -35,7 +35,6 @@ #include "resize_restart.hpp" #include "b_flux_ct.hpp" -#include "debug.hpp" #include "hdf5_utils.h" #include "kharma_utils.hpp" #include "interpolation.hpp" diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index 07f7f1f7..ebf754b8 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -36,138 +36,39 @@ #include +// TODO none of this machinery preserves zone locations, +// which we pretty often would like... -#pragma hd_warning_disable -Real Reductions::EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone) +std::shared_ptr Reductions::Initialize(ParameterInput *pin, std::shared_ptr& packages) { - Flag("EHReduction"); - auto pmesh = md->GetMeshPointer(); - - Real result = 0.; - for (auto &pmb : pmesh->block_list) { - // If we're on the inner edge - if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { - const auto& pars = pmb->packages.Get("GRMHD")->AllParams(); - const Real gam = pars.Get("gamma"); - - auto& rc = pmb->meshblock_data.Get(); - PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - - IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::interior); - IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::interior); - IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::interior); - const auto& G = pmb->coords; - - Real block_result; - switch(op) { - case UserHistoryOperation::sum: { - Kokkos::Sum sum_reducer(block_result); - pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { - local_result += fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); - } - , sum_reducer); - result += block_result; - break; - } - case UserHistoryOperation::max: { - Kokkos::Max max_reducer(block_result); - pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { - const Real val = fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); - if (val > local_result) local_result = val; - } - , max_reducer); - if (block_result > result) result = block_result; - break; - } - case UserHistoryOperation::min: { - Kokkos::Min min_reducer(block_result); - pmb->par_reduce("accretion_sum", kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i, double &local_result) { - const Real val = fn(G, P, m_p, U, m_u, gam, k, j, i) * G.Dxc<3>(k) * G.Dxc<2>(j); - if (val < local_result) local_result = val; - } - , min_reducer); - if (block_result < result) result = block_result; - break; - } - } - } - } - - EndFlag(); - return result; -} - -#pragma hd_warning_disable -Real Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg) -{ - Flag("DomainReduction"); - auto pmesh = md->GetMeshPointer(); - - // TODO TODO MESHDATA THIS - Real result = 0.; - const auto& pars = pmesh->packages.Get("GRMHD")->AllParams(); - const Real gam = pars.Get("gamma"); - - PackIndexMap prims_map, cons_map; - const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); - const VarMap m_u(cons_map, true), m_p(prims_map, false); - - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - IndexRange ib = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); - IndexRange jb = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); - IndexRange kb = pmb0->cellbounds.GetBoundsK(IndexDomain::interior); - IndexRange block = IndexRange{0, U.GetDim(5) - 1}; - - switch(op) { - case UserHistoryOperation::sum: { - Kokkos::Sum sum_reducer(result); - pmb0->par_reduce("domain_sum", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { - const auto& G = U.GetCoords(b); - local_result += fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); - } - , sum_reducer); - break; - } - case UserHistoryOperation::max: { - Kokkos::Max max_reducer(result); - pmb0->par_reduce("domain_max", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { - const auto& G = U.GetCoords(b); - const Real val = fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); - if (val > local_result) local_result = val; - } - , max_reducer); - break; - } - case UserHistoryOperation::min: { - Kokkos::Min min_reducer(result); - pmb0->par_reduce("domain_min", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, double &local_result) { - const auto& G = U.GetCoords(b); - const Real val = fn(G, P(b), m_p, U(b), m_u, gam, k, j, i, arg) * G.Dxc<3>(k) * G.Dxc<2>(j) * G.Dxc<1>(i); - if (val < local_result) local_result = val; - } - , min_reducer); - break; - } - } - - EndFlag(); - return result; + auto pkg = std::make_shared("Reductions"); + Params ¶ms = pkg->AllParams(); + + // These pools are vectors of Reducers which operate on vectors (or scalars) + // They exist to allow several reductions to be in-flight at once to hide latency + // (even reductions over vectors, as with the different flags) + std::vector>> vector_int_reduce_pool; + params.Add("vector_int_reduce_pool", vector_int_reduce_pool, true); + std::vector>> vector_reduce_pool; + params.Add("vector_reduce_pool", vector_reduce_pool, true); + std::vector> int_reduce_pool; + params.Add("int_reduce_pool", int_reduce_pool, true); + std::vector> reduce_pool; + params.Add("reduce_pool", reduce_pool, true); + + std::vector>> vector_int_allreduce_pool; + params.Add("vector_int_allreduce_pool", vector_int_allreduce_pool, true); + std::vector>> vector_allreduce_pool; + params.Add("vector_allreduce_pool", vector_allreduce_pool, true); + std::vector> int_allreduce_pool; + params.Add("int_allreduce_pool", int_allreduce_pool, true); + std::vector> allreduce_pool; + params.Add("allreduce_pool", allreduce_pool, true); + + return pkg; } -/** - * Counts occurrences of a particular flag value - * - */ +// Flag reductions: local int Reductions::CountFlag(MeshData *md, std::string field_name, const int& flag_val, IndexDomain domain, bool is_bitflag) { auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); @@ -193,10 +94,11 @@ int Reductions::CountFlag(MeshData *md, std::string field_name, const int& return n_flag; } -int Reductions::CountFlags(MeshData *md, std::string field_name, std::map flag_values, IndexDomain domain, int verbose, bool is_bitflag) +#define MAX_NFLAGS 20 + +std::vector Reductions::CountFlags(MeshData *md, std::string field_name, const std::map &flag_values, IndexDomain domain, bool is_bitflag) { Flag("CountFlags_"+field_name); - int nflags = 0; auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Pack variables @@ -209,85 +111,82 @@ int Reductions::CountFlags(MeshData *md, std::string field_name, std::map< IndexRange kb = md->GetBoundsK(domain); IndexRange block = IndexRange{0, flag.GetDim(5) - 1}; - // Count all nonzero (technically, >0) values + const int n_of_flags = flag_values.size(); + int flag_val_list[MAX_NFLAGS]; + int f=0; + for (auto &flag : flag_values) { + flag_val_list[f] = flag.first; + f++; + } + + // Count all nonzero (technically, >0) values, + // and all values of each // This works for pflags or fflags, so long as they're separate // We don't count negative pflags as they denote zones that shouldn't be fixed - Kokkos::Sum sum_reducer(nflags); + Reductions::array_type flag_reducer; pmb0->par_reduce("count_flags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, int &local_result) { - if ((int) flag(b, 0, k, j, i) > 0) ++local_result; + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, + Reductions::array_type &local_result) { + if ((int) flag(b, 0, k, j, i) > 0) ++local_result.my_array[0]; + for (int f=0; f(flag(b, 0, k, j, i)) & flag_val_list[f]) || + (!is_bitflag && static_cast(flag(b, 0, k, j, i)) == flag_val_list[f])) + ++local_result.my_array[f+1]; } - , sum_reducer); - - // TODO TODO REPLACE ABOVE WITH SOMETHING LIKE: - // array_sum::array_type res; - // parthenon::par_reduce(parthenon::loop_pattern_mdrange_tag, "RadiationResidual1", - // DevExecSpace(), 0, mout->NumBlocks()-1, - // 0, nang1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, - // KOKKOS_LAMBDA(const int b, const int n, const int k, const int j, const int i, - // array_sum::array_type& dsum) { - // dsum.my_array[0] += m::abs(iiter(b,n,k,j,i) - iout(b,n,k,j,i)); - // dsum.my_array[1] += iout(b,n,k,j,i); - // }, array_sum::GlobalSum(res)); + , Reductions::ArraySum(flag_reducer)); + + std::vector n_each_flag; + for (int f=0; f n_tot; - n_tot.val = nflags; - n_tot.StartReduce(MPI_SUM); - while (n_tot.CheckReduce() == TaskStatus::incomplete); - nflags = n_tot.val; +// Flag reductions: global +void Reductions::StartFlagReduce(MeshData *md, std::string field_name, const std::map &flag_values, IndexDomain domain, bool is_bitflag, int channel) +{ + Start>(md, channel, CountFlags(md, field_name, flag_values, domain, is_bitflag), MPI_SUM); +} - // If necessary, count each flag - // This is slow, but it can be slow: it's not called for normal operation - if (verbose > 0 && nflags > 0) { - // Overlap reductions to save time - // ...at the cost of considerable complexity... +std::vector Reductions::CheckFlagReduceAndPrintHits(MeshData *md, std::string field_name, const std::map &flag_values, + IndexDomain domain, bool is_bitflag, int channel) +{ + Flag("CheckFlagReduce"); + const auto& pmesh = md->GetMeshPointer(); + const auto& verbose = pmesh->packages.Get("Globals")->Param("flag_verbose"); - // TODO TODO eliminate static reducers, they crash the program after it finishes - static Reduce n_cells_r; - n_cells_r.val = (block.e - block.s + 1) * (kb.e - kb.s + 1) * (jb.e - jb.s + 1) * (ib.e - ib.s + 1); - n_cells_r.StartReduce(0, MPI_SUM); + // Get the relevant reducer and result + auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); + auto *vector_int_reduce_pool = pars.GetMutable>>>("vector_int_reduce_pool"); + auto& vector_int_reduce = (*vector_int_reduce_pool)[channel]; - static std::vector>> reducers; - // Initialize reducers if they haven't been - if (reducers.size() == 0) { - for (auto& status : flag_values) { - std::shared_ptr> reducer = std::make_shared>(); - reducers.push_back(reducer); - } - } - // Count occurrences of each flag value, assign to a reducer in order - int i = 0; - for (auto& status : flag_values) { - reducers[i]->val = CountFlag(md, field_name, (int) status.first, domain, is_bitflag); - reducers[i]->StartReduce(0, MPI_SUM); - ++i; - } - while (n_cells_r.CheckReduce() == TaskStatus::incomplete); - const int n_cells = n_cells_r.val; - // Check each reducer in order, add to a vector - std::vector n_status_present; - for (std::shared_ptr> reducer : reducers) { - while (reducer->CheckReduce() == TaskStatus::incomplete); - n_status_present.push_back(reducer->val); - } + while (vector_int_reduce.CheckReduce() == TaskStatus::incomplete); + const std::vector &total_flag_counts = vector_int_reduce.val; + // Print flags + if (total_flag_counts[0] > 0 && verbose > 0) { if (MPIRank0()) { + // Always our domain size times total number of blocks + IndexRange ib = md->GetBoundsI(domain); + IndexRange jb = md->GetBoundsJ(domain); + IndexRange kb = md->GetBoundsK(domain); + int n_cells = pmesh->nbtotal * (kb.e - kb.s + 1) * (jb.e - jb.s + 1) * (ib.e - ib.s + 1); + + int nflags = total_flag_counts[0]; std::cout << field_name << ": " << nflags << " (" << (int)(((double) nflags )/n_cells * 100) << "% of all cells)" << std::endl; if (verbose > 1) { // Print nonzero vector contents against flag names in order - int i = 0; + int i = 1; for (auto& status : flag_values) { - if (n_status_present[i] > 0) std::cout << status.second << ": " << n_status_present[i] << std::endl; + if (total_flag_counts[i] > 0) std::cout << status.second << ": " << total_flag_counts[i] << std::endl; ++i; } std::cout << std::endl; } } - - // TODO Print zone locations of bad inversions } EndFlag(); - return nflags; + return total_flag_counts; } diff --git a/kharma/reductions/reductions.hpp b/kharma/reductions/reductions.hpp index 11dd709f..b84a8518 100644 --- a/kharma/reductions/reductions.hpp +++ b/kharma/reductions/reductions.hpp @@ -33,26 +33,22 @@ */ #pragma once -#include "debug.hpp" +#include "reductions_variables.hpp" #include "flux_functions.hpp" #include "grmhd_functions.hpp" #include "types.hpp" -// This is for flux/accretion rate -#define REDUCE_FUNCTION_ARGS_EH const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, \ - const VariableFluxPack& U, const VarMap& m_u, const Real& gam, \ - const int& k, const int& j, const int& i +namespace Reductions { -// Notice this list also includes a generic Real-type argument: this is for denoting a radius or placement. -// Provided as argument in case reductions at/within/etc multiple places are desired -// (e.g., disk and jet, inner & outer, multiple radii) -// TODO take off 'b' from arg list and pass block contents? -#define REDUCE_FUNCTION_ARGS_MESH const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, \ - const VariableFluxPack& U, const VarMap& m_u, const Real& gam, \ - const int& k, const int& j, const int& i, const Real& arg +// Think about how to do channels as not ints +//constexpr enum class Channel{fflag, pflag, iflag, }; -namespace Reductions { +/** + * These, too, are a package. + * Mostly it exists to keep track of Reducers, so we can clean them up to keep MPI happy. + */ +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); /** * Perform a reduction using operation 'op' over a spherical shell at the given zone, measured from left side of @@ -60,15 +56,48 @@ namespace Reductions { * As this only runs on innermost blocks, this is intended for accretion/event horizon * measurements in black hole simulations. */ -Real EHReduction(MeshData *md, UserHistoryOperation op, std::function fn, int zone); +template +T EHReduction(MeshData *md, UserHistoryOperation op, int zone); /** - * Perform a reduction using operation 'op' over all zones. - * The extra 'arg' is passed as the last argument to the device-side function. - * It is generally used to denote a radius inside, outside, or at which the reduction should be taken. - * This should be used for 2D shell sums not at the EH: just divide the function result by the zone spacing dx1. + * Perform a reduction using operation 'op' over a given domain + * This should be used for all 2D shell sums not around the EH: + * Just set equal min/max, 2D slices are detected */ -Real DomainReduction(MeshData *md, UserHistoryOperation op, std::function fn, Real arg); +template +T DomainReduction(MeshData *md, UserHistoryOperation op, const GReal startx[3], const GReal stopx[3], int channel=-1); +template +T DomainReduction(MeshData *md, UserHistoryOperation op, int channel=-1) { + const GReal startx[3] = {std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()}; + const GReal stopx[3] = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; + return DomainReduction(md, op, startx, stopx, channel); +} + +/** + * Start reductions with a value you have on hand + */ +template +void Start(MeshData *md, int channel, T val, MPI_Op op); +template +void StartToAll(MeshData *md, int channel, T val, MPI_Op op); + +/** + * Check the results of reductions that have been started. + * Remember channels are COUNTED SEPARATELY between the 4 lists: + * Real/default, int, vector and vector (i.e. Flags) + */ +template +T Check(MeshData *md, int channel); +template +T CheckOnAll(MeshData *md, int channel); + +/** + * Check the results of reductions that have been started. + * Remember channels are COUNTED SEPARATELY between the 4 lists: + * Real/default, int, vector and vector (i.e. Flags) + */ +template +T Check(MeshData *md, int channel); /** * Count instances of a particular flag value in the named field. @@ -78,11 +107,24 @@ Real DomainReduction(MeshData *md, UserHistoryOperation op, std::function< int CountFlag(MeshData *md, std::string field_name, const int& flag_val, IndexDomain domain, bool is_bitflag); /** - * Count instances of a particular flag value in the named field. + * Count instances of all flags in the named field. * is_bitflag specifies whether multiple flags may be present and will be orthogonal (e.g. FFlag), * or whether flags receive consecutive integer values. - * TODO could return numbers for all flags instead of just printing */ -int CountFlags(MeshData *md, std::string field_name, std::map flag_values, IndexDomain domain, int verbose, bool is_bitflag); +std::vector CountFlags(MeshData *md, std::string field_name, const std::map &flag_values, IndexDomain domain, bool is_bitflag); + +/** + * Determine number of local flags hit with CountFlags, and send the value over MPI reducer 'channel' + */ +void StartFlagReduce(MeshData *md, std::string field_name, const std::map &flag_values, IndexDomain domain, bool is_bitflag, int channel); + +/** + * Check a flag's MPI reduction and print any flags hit + */ +std::vector CheckFlagReduceAndPrintHits(MeshData *md, std::string field_name, const std::map &flag_values, + IndexDomain domain, bool is_bitflag, int channel); } // namespace Reductions + +// See the file for why we do this +#include "reductions_impl.hpp" diff --git a/kharma/reductions/reductions_impl.hpp b/kharma/reductions/reductions_impl.hpp new file mode 100644 index 00000000..0575cd88 --- /dev/null +++ b/kharma/reductions/reductions_impl.hpp @@ -0,0 +1,296 @@ +/* + * File: reductions_variables.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +// This is a weird header. It's included at the end of reductions.hpp, in order to provide +// the source of the below templates to other files to instantiate. +// Otherwise, it operates like a normal cpp (NOT hpp) file. + +// Satisfy IDE parsers who aren't wise to our schemes +#include "reductions.hpp" + +template +inline std::string GetPoolName() +{ + if constexpr (all_reduce) { + if constexpr (std::is_same::value) + return "allreduce_pool"; + if constexpr (std::is_same::value) + return "int_allreduce_pool"; + if constexpr (std::is_same>::value) + return "vector_allreduce_pool"; + if constexpr (std::is_same>::value) + return "vector_int_allreduce_pool"; + } else { + if constexpr (std::is_same::value) + return "reduce_pool"; + if constexpr (std::is_same::value) + return "int_reduce_pool"; + if constexpr (std::is_same>::value) + return "vector_reduce_pool"; + if constexpr (std::is_same>::value) + return "vector_int_reduce_pool"; + } +} + +// MPI reduction starts +template +void Reductions::Start(MeshData *md, int channel, T val, MPI_Op op) +{ + // Get the relevant reducer + const std::string pool_name = GetPoolName(); + auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); + auto *reduce_pool = pars.GetMutable>>(pool_name); + while (reduce_pool->size() <= channel) reduce_pool->push_back(Reduce()); + auto& vector_int_reduce = (*reduce_pool)[channel]; + // Fill with flags + vector_int_reduce.val = val; + vector_int_reduce.StartReduce(0, op); +} +template +void Reductions::StartToAll(MeshData *md, int channel, T val, MPI_Op op) +{ + // Get the relevant reducer + const std::string pool_name = GetPoolName(); + auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); + auto *allreduce_pool = pars.GetMutable>>(pool_name); + while (allreduce_pool->size() <= channel) allreduce_pool->push_back(AllReduce()); + auto& vector_int_reduce = (*allreduce_pool)[channel]; + // Fill with flags + vector_int_reduce.val = val; + vector_int_reduce.StartReduce(op); +} + +// MPI reduction checks +template +T Reductions::Check(MeshData *md, int channel) +{ + // Get the relevant reducer and result + const std::string pool_name = GetPoolName(); + auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); + auto *reduce_pool = pars.GetMutable>>(pool_name); + auto& reducer = (*reduce_pool)[channel]; + + while (reducer.CheckReduce() == TaskStatus::incomplete); + return reducer.val; +} +template +T Reductions::CheckOnAll(MeshData *md, int channel) +{ + // Get the relevant reducer and result + const std::string pool_name = GetPoolName(); + auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); + auto *reduce_pool = pars.GetMutable>>(pool_name); + auto& reducer = (*reduce_pool)[channel]; + + while (reducer.CheckReduce() == TaskStatus::incomplete); + return reducer.val; +} + +#define REDUCE_FUNCTION_CALL G, P(b), m_p, U(b), m_u, cmax(b), cmin(b), emhd_params, gam, k, j, i + +template +T Reductions::EHReduction(MeshData *md, UserHistoryOperation op, int zone) +{ + Flag("EHReduction"); + auto pmesh = md->GetMeshPointer(); + + const auto& pars = pmesh->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + const auto& emhd_params = EMHD::GetEMHDParameters(pmesh->packages); + + PackIndexMap prims_map, cons_map; + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); + const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + IndexRange ib = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); + IndexRange jb = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); + IndexRange kb = pmb0->cellbounds.GetBoundsK(IndexDomain::interior); + IndexRange block = IndexRange{0, U.GetDim(5) - 1}; + + T result(0); + int nb = pmesh->GetNumMeshBlocksThisRank(); + for (int iblock=0; iblock < nb; iblock++) { + const auto &pmb = pmesh->block_list[iblock]; + // Inner-edge blocks only for speed + if (pmb->boundary_flag[parthenon::BoundaryFace::inner_x1] == BoundaryFlag::user) { + const auto& G = pmb->coords; + T block_result; + switch(op) { + case UserHistoryOperation::sum: { + Kokkos::Sum sum_reducer(block_result); + pmb->par_reduce("accretion_sum", iblock, iblock, kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + local_result += reduction_var(REDUCE_FUNCTION_CALL) * G.Dxc<3>(k) * G.Dxc<2>(j); + } + , sum_reducer); + result += block_result; + break; + } + case UserHistoryOperation::max: { + Kokkos::Max max_reducer(block_result); + pmb->par_reduce("accretion_sum", iblock, iblock, kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + const T val = reduction_var(REDUCE_FUNCTION_CALL) * G.Dxc<3>(k) * G.Dxc<2>(j); + if (val > local_result) local_result = val; + } + , max_reducer); + if (block_result > result) result = block_result; + break; + } + case UserHistoryOperation::min: { + Kokkos::Min min_reducer(block_result); + pmb->par_reduce("accretion_sum", iblock, iblock, kb.s, kb.e, jb.s, jb.e, ib.s+zone, ib.s+zone, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + const T val = reduction_var(REDUCE_FUNCTION_CALL) * G.Dxc<3>(k) * G.Dxc<2>(j); + if (val < local_result) local_result = val; + } + , min_reducer); + if (block_result < result) result = block_result; + break; + } + } + } + } + + EndFlag(); + return result; +} + +#define INSIDE (x[1] > startx[0] && x[2] > startx[1] && x[3] > startx[2]) && \ + (trivial[0] ? x[1] < startx[0] + G.Dxc<1>(i) : x[1] < stopx[0]) && \ + (trivial[1] ? x[2] < startx[1] + G.Dxc<2>(j) : x[2] < stopx[1]) && \ + (trivial[2] ? x[3] < startx[2] + G.Dxc<3>(k) : x[3] < stopx[2]) + +// TODO additionally template on return type to avoid counting flags with Reals +template +T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const GReal startx[3], const GReal stopx[3], int channel) +{ + Flag("DomainReduction"); + auto pmesh = md->GetMeshPointer(); + + const auto& pars = pmesh->packages.Get("GRMHD")->AllParams(); + const Real gam = pars.Get("gamma"); + const auto& emhd_params = EMHD::GetEMHDParameters(pmesh->packages); + + // Just pass in everything we might want. Probably slow? + PackIndexMap prims_map, cons_map; + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const VarMap m_u(cons_map, true), m_p(prims_map, false); + const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); + const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + IndexRange ib = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); + IndexRange jb = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); + IndexRange kb = pmb0->cellbounds.GetBoundsK(IndexDomain::interior); + IndexRange block = IndexRange{0, U.GetDim(5) - 1}; + + bool trivial_tmp[3] = {false, false, false}; + VLOOP if(startx[v] == stopx[v]) { + trivial_tmp[v] = true; + } + const bool trivial[3] = {trivial_tmp[0], trivial_tmp[1], trivial_tmp[2]}; + + T result = 0.; + MPI_Op mop; + switch(op) { + case UserHistoryOperation::sum: { + Kokkos::Sum sum_reducer(result); + pmb0->par_reduce("domain_sum", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + const auto& G = U.GetCoords(b); + GReal x[4]; + G.coord_embed(k, j, i, Loci::center, x); + if(INSIDE) { + local_result += reduction_var(REDUCE_FUNCTION_CALL) * + (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + } + } + , sum_reducer); + mop = MPI_SUM; + break; + } + case UserHistoryOperation::max: { + Kokkos::Max max_reducer(result); + pmb0->par_reduce("domain_max", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + const auto& G = U.GetCoords(b); + GReal x[4]; + G.coord_embed(k, j, i, Loci::center, x); + if(INSIDE) { + const Real val = reduction_var(REDUCE_FUNCTION_CALL) * + (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + if (val > local_result) local_result = val; + } + } + , max_reducer); + mop = MPI_MAX; + break; + } + case UserHistoryOperation::min: { + Kokkos::Min min_reducer(result); + pmb0->par_reduce("domain_min", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, T &local_result) { + const auto& G = U.GetCoords(b); + GReal x[4]; + G.coord_embed(k, j, i, Loci::center, x); + if(INSIDE) { + const Real val = reduction_var(REDUCE_FUNCTION_CALL) * + (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + if (val < local_result) local_result = val; + } + } + , min_reducer); + mop = MPI_MIN; + break; + } + } + + // Optionally start an MPI reducer w/given index, so the mesh-wide result is ready when we want it + if (channel >= 0) { + Start(md, channel, result, mop); + } + + EndFlag(); + return result; +} + +#undef INSIDE +#undef REDUCE_FUNCTION_CALL diff --git a/kharma/reductions/reductions_types.hpp b/kharma/reductions/reductions_types.hpp new file mode 100644 index 00000000..7ebca674 --- /dev/null +++ b/kharma/reductions/reductions_types.hpp @@ -0,0 +1,121 @@ +/* + * File: reductions_variables.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +// This file is included with types.hpp, +// so that all files have access to the extra Kokkos reduction machinery + +#include "decs.hpp" + +// Reduction types: teach Kokkos to keep a 3-int index, make it usable +// See grmhd.cpp timestep calc for example +namespace Kokkos { +template <> +struct reduction_identity> { + KOKKOS_FORCEINLINE_FUNCTION constexpr static std::tuple min() { + int max = std::numeric_limits::max(); + return std::tuple{max, max, max}; + } +}; +} +namespace Reductions { +// Types for 3-index reduction +typedef Kokkos::MinMaxLoc> Reduce3; +typedef Reduce3::value_type Reduce3v; + +// Array type for reducing arbitrary numbers of reals +template +struct array_type { + ScalarType my_array[N]; + + KOKKOS_INLINE_FUNCTION + array_type() { init(); } + + KOKKOS_INLINE_FUNCTION + array_type(const array_type& rhs) { + for (int i = 0; i < N; i++) { + my_array[i] = rhs.my_array[i]; + } + } + + KOKKOS_INLINE_FUNCTION void init() { + for (int i = 0; i < N; i++) { + my_array[i] = 0; + } + } + + KOKKOS_INLINE_FUNCTION array_type& + operator+=(const array_type& src) { + for (int i = 0; i < N; i++) { + my_array[i] += src.my_array[i]; + } + return *this; + } +}; + +template +struct ArraySum { + public: + // Required + typedef ArraySum reducer; + typedef array_type value_type; + typedef Kokkos::View + result_view_type; + + private: + value_type& value; + + public: + KOKKOS_INLINE_FUNCTION + ArraySum(value_type& value_) : value(value_) {} + + // Required + KOKKOS_INLINE_FUNCTION + void join(value_type& dest, const value_type& src) const { + dest += src; + } + + KOKKOS_INLINE_FUNCTION + void init(value_type& val) const { val.init(); } + + KOKKOS_INLINE_FUNCTION + value_type& reference() const { return value; } + + KOKKOS_INLINE_FUNCTION + result_view_type view() const { return result_view_type(&value, 1); } + + KOKKOS_INLINE_FUNCTION + bool references_scalar() const { return true; } +}; +} \ No newline at end of file diff --git a/kharma/reductions/reductions_variables.hpp b/kharma/reductions/reductions_variables.hpp new file mode 100644 index 00000000..118ae5a9 --- /dev/null +++ b/kharma/reductions/reductions_variables.hpp @@ -0,0 +1,249 @@ +/* + * File: reductions_variables.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "types.hpp" + +#include "emhd.hpp" +#include "flux_functions.hpp" + +using namespace parthenon; + +#define REDUCE_FUNCTION_ARGS const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, \ + const VariableFluxPack& U, const VarMap& m_u, \ + const VariablePack& cmax, const VariablePack& cmin,\ + const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i + +namespace Reductions { + +// Add any new reduction variables to this list, then implementations below +// Not elegant, but fast & portable. +// HIPCC doesn't like passing function pointers as we used to do, +// and it doesn't vectorize anyway. Look forward to more of this pattern in the code +enum class Var{phi, bsq, gas_pressure, mag_pressure, beta, + mdot, edot, ldot, mdot_flux, edot_flux, ldot_flux, eht_lum, jet_lum, + nan_ctop, zero_ctop, neg_rho, neg_u, neg_rhout}; + +// Function template for all reductions. +template +Real reduction_var(REDUCE_FUNCTION_ARGS); + +// Can also sum the hemispheres independently to be fancy (TODO?) +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + // \Phi == \int |*F^1^0| * gdet * dx2 * dx3 == \int |B1| * gdet * dx2 * dx3 + return 0.5 * m::abs(U(m_u.B1, k, j, i)); // factor of gdet already in cons.B +} + +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + return dot(Dtmp.bcon, Dtmp.bcov); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + return (gam - 1) * P(m_p.UU, k, j, i); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + return ((gam - 1) * P(m_p.UU, k, j, i))/(0.5*(dot(Dtmp.bcon, Dtmp.bcov) + SMALL)); +} + +// Accretion rates: return a zone's contribution to the surface integral +// forming each rate measurement. +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real ucon[GR_DIM]; + GRMHD::calc_ucon(G, P, m_p, k, j, i, Loci::center, ucon); + // \dot{M} == \int rho * u^1 * gdet * dx2 * dx3 + return -P(m_p.RHO, k, j, i) * ucon[X1DIR] * G.gdet(Loci::center, j, i); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(P, m_p, Dtmp, emhd_params, gam, k, j, i, X1DIR, T1); + // \dot{E} == \int - T^1_0 * gdet * dx2 * dx3 + return -T1[X0DIR] * G.gdet(Loci::center, j, i); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(P, m_p, Dtmp, emhd_params, gam, k, j, i, X1DIR, T1); + // \dot{L} == \int T^1_3 * gdet * dx2 * dx3 + return T1[X3DIR] * G.gdet(Loci::center, j, i); +} + +// Then we can define the same with fluxes. +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + return -U.flux(X1DIR, m_u.RHO, k, j, i); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + return (U.flux(X1DIR, m_u.UU, k, j, i) - U.flux(X1DIR, m_u.RHO, k, j, i)); +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + return U.flux(X1DIR, m_u.U3, k, j, i); +} + +// Luminosity proxy from (for example) Porth et al 2019. +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Real rho = P(m_p.RHO, k, j, i); + Real Pg = (gam - 1.) * P(m_p.UU, k, j, i); + Real Bmag = m::sqrt(dot(Dtmp.bcon, Dtmp.bcov)); + Real j_eht = rho*rho*rho/Pg/Pg * m::exp(-0.2 * m::cbrt(rho * rho / (Bmag * Pg * Pg))); + return j_eht; +} + +// Example of checking extra conditions before adding local results: +// sums total jet power only at exactly r=radius, for areas with sig > 1 +// TODO version w/E&M power only. Needs "calc_tensor_EM" +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + FourVectors Dtmp; + Real T1[GR_DIM]; + GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); + Flux::calc_tensor(P, m_p, Dtmp, emhd_params, gam, k, j, i, X1DIR, T1); + // If sigma > 1... + if ((dot(Dtmp.bcon, Dtmp.bcov) / P(m_p.RHO, k, j, i)) > 1.) { + // Energy flux, like at EH + return -T1[X0DIR]; + } else { + return 0.; + } +} + +// Diagnostics. Still have to return Real so we get creative. +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real is_zero = 0; + VLOOP { + if(m::max(cmax(v, k, j, i), cmin(v, k, j, i)) <= 0.) { + is_zero = 1.; // once per zone +#if DEBUG +#ifndef KOKKOS_ENABLE_SYCL + printf("ctop zero at %d %d %d along dir %d\n", i, j, k, v+1); +#endif +#endif + } + } + + return is_zero; +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real is_nan = 0.; + VLOOP { + if(m::isnan(m::max(cmax(v, k, j, i), cmin(v, k, j, i)))) { + is_nan = 1.; +#if DEBUG +#ifndef KOKKOS_ENABLE_SYCL + printf("ctop NaN at %d %d %d along dir %d\n", i, j, k, v+1); +#endif +#endif + } + } + + return is_nan; +} + +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real is_neg = 0.; + if (U(m_u.RHO, k, j, i) < 0.) { + is_neg = 1.; +#if DEBUG +#ifndef KOKKOS_ENABLE_SYCL + printf("Negative rho*u^0 (cons.rho) at %d %d %d\n", i, j, k); +#endif +#endif + } +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real is_neg = 0.; + if (P(m_p.UU, k, j, i) < 0.) { + is_neg = 1.; +#if DEBUG +#ifndef KOKKOS_ENABLE_SYCL + printf("Negative internal energy (prims.u) at %d %d %d\n", i, j, k); +#endif +#endif + } +} +template <> +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) +{ + Real is_neg = 0.; + if (P(m_p.RHO, k, j, i) < 0.) { + is_neg = 1.; +#if DEBUG +#ifndef KOKKOS_ENABLE_SYCL + printf("Negative density (prims.rho) at %d %d %d\n", i, j, k); +#endif +#endif + } +} + +} + +#undef REDUCE_FUNCTION_ARGS \ No newline at end of file diff --git a/kharma/types.hpp b/kharma/types.hpp index adeee780..c8eafeab 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -37,6 +37,7 @@ #include "boundary_types.hpp" #include "kharma_package.hpp" +#include "reductions_types.hpp" #include diff --git a/machines/bp.sh b/machines/bp.sh index 9eed4109..f7997917 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -27,6 +27,14 @@ if [[ $METAL_HOSTNAME == "fermium" ]]; then DEVICE_ARCH="TURING75" # Nvidia MPI hangs unless I do this MPI_EXE=mpirun + + if [[ "$ARGS" == *"cuda"* ]]; then + echo "Nothing special for cuda" + else + # AMD for CPUs + CXX_NATIVE=clang++ + C_NATIVE=clang + fi fi if [[ $METAL_HOSTNAME == "ferrum" ]]; then diff --git a/make.sh b/make.sh index eec24df6..f5ebd175 100755 --- a/make.sh +++ b/make.sh @@ -255,6 +255,7 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then echo Configuring HDF5... + export CFLAGS="-fPIC $CFLAGS" CC=$HDF_CC sh configure -C $HDF_EXTRA --prefix=$SOURCE_DIR/external/hdf5 --enable-build-mode=production \ --disable-dependency-tracking --disable-hl --disable-tests --disable-tools --disable-shared --disable-deprecated-symbols > build-hdf5.log sleep 1 From 5ba8f0dc6fe4c9b59b8b5dbf67a168b7ed261e9e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 17 Aug 2023 22:49:21 -0600 Subject: [PATCH 109/219] Fixes for the obvious reduction bugs, & a new name collision with package names --- kharma/emhd/emhd.cpp | 11 ++++++----- kharma/grmhd/grmhd.hpp | 2 +- kharma/prob/post_initialize.cpp | 22 ---------------------- kharma/reductions/reductions_impl.hpp | 14 +++++++------- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 34153ce0..3150a198 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -122,15 +122,16 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

GetBlockPointer(); // PackIndexMap prims_map, cons_map; -// auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); +// auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); // auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); // const VarMap m_p(prims_map, false), m_u(cons_map, true); @@ -216,7 +217,7 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); diff --git a/kharma/grmhd/grmhd.hpp b/kharma/grmhd/grmhd.hpp index 51736a2f..53878118 100644 --- a/kharma/grmhd/grmhd.hpp +++ b/kharma/grmhd/grmhd.hpp @@ -74,7 +74,7 @@ void FillOutput(MeshBlock *pmb, ParameterInput *pin); /** * Diagnostics performed after each step. - * Currently finds any negative flags or 0/NaN values in ctop + * Currently just looks for negative density/internal energy */ TaskStatus PostStepDiagnostics(const SimTime& tm, MeshData *rc); } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 6b338019..5944bbe1 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -92,8 +92,6 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrpackages.AllPackages().count("B_CD"); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - fprintf(stderr, "0.5"); - Flag("SeedBField"); // Seed the magnetic field on each block for (auto &pmb : pmesh->block_list) { @@ -108,8 +106,6 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrGetString("parthenon/job", "problem_id"); @@ -127,11 +123,8 @@ void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptrmesh_data.Get(); auto& pkgs = pmesh->packages.AllPackages(); - fprintf(stderr, "0.1"); // Magnetic field operations if (pin->GetString("b_field", "solver") != "none") { // If we need to seed a field based on the problem's fluid initialization... if (pin->GetOrAddString("b_field", "type", "none") != "none" && !is_restart) { // B field init is not stencil-1, needs boundaries sync'd. // FreezeDirichlet ensures any Dirichlet conditions aren't overwritten by zeros - fprintf(stderr, "0.2"); KBoundaries::FreezeDirichlet(md); KHARMADriver::SyncAllBounds(md); - fprintf(stderr, "0.3"); // Then init B field on each block... KHARMA::SeedAndNormalizeB(pin, md); } - fprintf(stderr, "4"); // Regardless, if evolving a field we should print max(divB) // divB is not stencil-1 and we may not have run the above. @@ -222,8 +210,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KBoundaries::FreezeDirichlet(md); KHARMADriver::SyncAllBounds(md); - fprintf(stderr, "5"); - if (pkgs.count("B_FluxCT")) { B_FluxCT::PrintGlobalMaxDivB(md.get()); } else if (pkgs.count("B_CT")) { @@ -233,8 +219,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } } - fprintf(stderr, "6"); - // Add any hotspots. // Note any other modifications made when restarting should be made around here if (pin->GetOrAddBoolean("blob", "add_blob", false)) { @@ -245,8 +229,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } } - fprintf(stderr, "7"); - // Any extra cleanup & init especially when restarting if (is_restart) { // Parthenon restores all parameters (global vars) when restarting, @@ -254,8 +236,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMA::ResetGlobals(pin, pmesh); } - fprintf(stderr, "8"); - // Clean the B field if we've introduced a divergence somewhere // We call this function any time the package is loaded: // if we decided to load it in kharma.cpp, we need to clean. @@ -270,8 +250,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) B_Cleanup::CleanupDivergence(md); } - fprintf(stderr, "9"); - // Finally, synchronize boundary values. // Freeze any Dirichlet physical boundaries as they are now, after cleanup/sync/etc. KBoundaries::FreezeDirichlet(md); diff --git a/kharma/reductions/reductions_impl.hpp b/kharma/reductions/reductions_impl.hpp index 0575cd88..df42b1b8 100644 --- a/kharma/reductions/reductions_impl.hpp +++ b/kharma/reductions/reductions_impl.hpp @@ -73,10 +73,10 @@ void Reductions::Start(MeshData *md, int channel, T val, MPI_Op op) auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); auto *reduce_pool = pars.GetMutable>>(pool_name); while (reduce_pool->size() <= channel) reduce_pool->push_back(Reduce()); - auto& vector_int_reduce = (*reduce_pool)[channel]; + auto& reduce = (*reduce_pool)[channel]; // Fill with flags - vector_int_reduce.val = val; - vector_int_reduce.StartReduce(0, op); + reduce.val = val; + reduce.StartReduce(0, op); } template void Reductions::StartToAll(MeshData *md, int channel, T val, MPI_Op op) @@ -86,10 +86,10 @@ void Reductions::StartToAll(MeshData *md, int channel, T val, MPI_Op op) auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); auto *allreduce_pool = pars.GetMutable>>(pool_name); while (allreduce_pool->size() <= channel) allreduce_pool->push_back(AllReduce()); - auto& vector_int_reduce = (*allreduce_pool)[channel]; + auto& reduce = (*allreduce_pool)[channel]; // Fill with flags - vector_int_reduce.val = val; - vector_int_reduce.StartReduce(op); + reduce.val = val; + reduce.StartReduce(op); } // MPI reduction checks @@ -111,7 +111,7 @@ T Reductions::CheckOnAll(MeshData *md, int channel) // Get the relevant reducer and result const std::string pool_name = GetPoolName(); auto& pars = md->GetMeshPointer()->packages.Get("Reductions")->AllParams(); - auto *reduce_pool = pars.GetMutable>>(pool_name); + auto *reduce_pool = pars.GetMutable>>(pool_name); auto& reducer = (*reduce_pool)[channel]; while (reducer.CheckReduce() == TaskStatus::incomplete); From a479a50a65c3e600aaad4c5d7dbddc3d162e9b55 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 18 Aug 2023 08:34:09 -0600 Subject: [PATCH 110/219] Update Parthenon module w/proposed fns --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index 3020f6c5..437e02bf 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 3020f6c59e4ca354c9e066252ccdc6848b4ced14 +Subproject commit 437e02bf62734f7a7962b9e5d0fae0ab36a34dfc From 43772f5b38d181f7e00414a25f7ed527aafd33bc Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 18 Aug 2023 21:44:16 -0600 Subject: [PATCH 111/219] Prolongation operator. Arrange flags to split physics conserved vs. code conserved vars. (Better flag organization forthcoming which will hide some silliness here.) In particular, we want the code to conserve the face B, but the cell-centered version is still "conserved" in the physical sense. Add the prolongation operator from Olivares et al. 2019 in generality, but currently hardcoded to alpha values matching the operator in Toth & Roe. Boundaries between refinement levels are still a problem, but overall this is pretty close for a day of fiddling. --- kharma/b_cd/b_cd.cpp | 9 +- kharma/b_cd/b_cd.hpp | 2 +- kharma/b_cleanup/b_cleanup.cpp | 6 +- kharma/b_ct/b_ct.cpp | 31 +++--- kharma/b_ct/b_ct.hpp | 118 +++++++++++++++++++++ kharma/b_flux_ct/b_flux_ct.cpp | 6 +- kharma/b_flux_ct/b_flux_ct.hpp | 2 +- kharma/domain.hpp | 2 +- kharma/driver/imex_step.cpp | 4 +- kharma/driver/kharma_driver.hpp | 2 +- kharma/driver/kharma_step.cpp | 2 +- kharma/driver/simple_step.cpp | 2 +- kharma/electrons/electrons.cpp | 20 ++-- kharma/emhd/emhd.cpp | 16 +-- kharma/emhd/emhd_limits.hpp | 4 +- kharma/floors/floors.cpp | 8 +- kharma/flux/flux.cpp | 16 +-- kharma/flux/get_flux.hpp | 4 +- kharma/grmhd/grmhd.cpp | 11 +- kharma/grmhd/pack.hpp | 16 +-- kharma/implicit/fix_solve.cpp | 6 +- kharma/implicit/implicit.cpp | 6 +- kharma/prob/emhd/conducting_atmosphere.cpp | 2 +- kharma/prob/kelvin_helmholtz.hpp | 61 +++++++++-- kharma/reductions/reductions_impl.hpp | 8 +- kharma/wind/wind.cpp | 2 +- machines/bp.sh | 1 + pars/kelvin_helmholtz.par | 79 ++++++++++++++ 28 files changed, 347 insertions(+), 99 deletions(-) create mode 100644 pars/kelvin_helmholtz.par diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index bb7d8f7e..cced6a80 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -62,20 +62,21 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::Vector}, s_vector); + Metadata::Restart, Metadata::GetUserFlag("GRPrimitive"), Metadata::Vector}, s_vector); pkg->AddField("prims.B", m); // Constraint damping scalar field psi. Prim and cons forms correspond to B field forms, // i.e. differ by a factor of gdet. This is apparently marginally more stable in some // circumstances. m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, - Metadata::Restart, Metadata::Conserved, Metadata::WithFluxes}); + Metadata::Restart, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, Metadata::WithFluxes}); pkg->AddField("cons.psi_cd", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, Metadata::GetUserFlag("Primitive")}); + Metadata::Restart, Metadata::GetUserFlag("GRPrimitive")}); pkg->AddField("prims.psi_cd", m); // We only update the divB field for output diff --git a/kharma/b_cd/b_cd.hpp b/kharma/b_cd/b_cd.hpp index 93b70545..1bc70216 100644 --- a/kharma/b_cd/b_cd.hpp +++ b/kharma/b_cd/b_cd.hpp @@ -47,7 +47,7 @@ using namespace parthenon; * * This requires only the values at cell centers, and preserves a cell-centered divergence representation * - * This implementation includes conversion from "primitive" to "conserved" B and back, + * This implementation includes conversion from "GRPrimitive" to "conserved" B and back, * i.e. between field strength and flux via multiplying by gdet. */ namespace B_CD { diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 23de26ef..cdbde016 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -146,10 +146,10 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s MetadataFlag areWeImplicit = (implicit_b) ? Metadata::GetUserFlag("Implicit") : Metadata::GetUserFlag("Explicit"); - // Flags for B fields. "Primitive" form is field, "conserved" is flux - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + // Flags for B fields. "GRPrimitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 2440bfd6..6e2845e5 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -41,6 +41,7 @@ #include "kharma_driver.hpp" #include +#include using namespace parthenon; @@ -77,22 +78,23 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // TODO maybe one day implicit? // Flags for B fields on faces. - // We don't mark these as "Primitive" and "Conserved" else they'd be bundled + // We don't mark these as "GRPrimitive" and "GRConserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::GetUserFlag("Explicit")}; - std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, + std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, Metadata::Conserved, Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; // TODO TODO Restart auto m = Metadata(flags_prim_f); pkg->AddField("prims.fB", m); m = Metadata(flags_cons_f); + m.RegisterRefinementOps(); pkg->AddField("cons.fB", m); // Cell-centered versions. Needed for BS, not for other schemes. // Probably will want to keep primitives for e.g. correct PtoU of MHD vars, but cons maybe can go - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::Conserved, Metadata::WithFluxes, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRConserved"), Metadata::WithFluxes, Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; std::vector s_vector({NVEC}); m = Metadata(flags_prim, s_vector); @@ -102,7 +104,7 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // EMF on edges. // TODO only sync when needed - std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}; + std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_emf); pkg->AddField("B_CT.emf", m); @@ -125,7 +127,6 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // Register the other callbacks pkg->PostStepDiagnosticsMesh = B_CT::PostStepDiagnostics; - // TODO TODO prolongation/restriction will be registered here too // The definition of MaxDivB we care about actually changes per-transport, // so calculating it is handled by the transport package @@ -138,15 +139,15 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // List (vector) of HistoryOutputVars that will all be enrolled as output variables // LATER - // parthenon::HstVar_list hst_vars = {}; - // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_CT::MaxDivB, "MaxDivB")); - // // Event horizon magnetization. Might be the same or different for different representations? - // if (pin->GetBoolean("coordinates", "spherical")) { - // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi0, "Phi_0")); - // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi5, "Phi_EH")); - // } - // // add callbacks for HST output to the Params struct, identified by the `hist_param_key` - // pkg->AddParam<>(parthenon::hist_param_key, hst_vars); + parthenon::HstVar_list hst_vars = {}; + hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::max, B_CT::MaxDivB, "MaxDivB")); + // Event horizon magnetization. Might be the same or different for different representations? + if (pin->GetBoolean("coordinates", "spherical")) { + // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi0, "Phi_0")); + // hst_vars.emplace_back(parthenon::HistoryOutputVar(UserHistoryOperation::sum, ReducePhi5, "Phi_EH")); + } + // add callbacks for HST output to the Params struct, identified by the `hist_param_key` + pkg->AddParam<>(parthenon::hist_param_key, hst_vars); return pkg; } diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index 0d45a5d3..5891bf01 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -210,4 +210,122 @@ KOKKOS_INLINE_FUNCTION Real upwind_diff(const VariableFluxPack& B_U, const } } +// Only by formatting may the following be made even a little comprehensible. + +template +KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, int l, int m, int n, int fk, int fj, int fi) +{ + // TODO compile-time error on misuse? + constexpr int df_is_k = 2*(diff_face == V3 && DIM > 2); + constexpr int df_is_j = 2*(diff_face == V2 && DIM > 1); + constexpr int df_is_i = 2*(diff_face == V1 && DIM > 0); + constexpr int ds_is_k = (diff_side == V3 && DIM > 2); + constexpr int ds_is_j = (diff_side == V2 && DIM > 1); + constexpr int ds_is_i = (diff_side == V1 && DIM > 0); + constexpr int of_is_k = (offset == V3 && DIM > 2); + constexpr int of_is_j = (offset == V2 && DIM > 1); + constexpr int of_is_i = (offset == V1 && DIM > 0); + // if(fi == 10 && fj == 10 && fk == 0) { + // fprintf(stderr, "F facediff dir %d sidediff dirr %d off dir %d\nadding terms %d %d %d, -%d %d %d, -%d %d %d, %d %d %d\n", + // diff_face, diff_side, offset, + // df_is_i+ds_is_i+of_is_i, df_is_j+ds_is_j+of_is_j, df_is_k+ds_is_k+of_is_k, + // ds_is_i+of_is_i, ds_is_j+of_is_j, ds_is_k+of_is_k, + // df_is_i+of_is_i, df_is_j+of_is_j, df_is_k+of_is_k, + // of_is_i , of_is_j , of_is_k); + // } + return fine(diff_face, l, m, n, fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) + - fine(diff_face, l, m, n, fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) + - fine(diff_face, l, m, n, fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) + + fine(diff_face, l, m, n, fk+of_is_k , fj+of_is_j , fi+of_is_i); +} + +struct ProlongateInternalOlivares { + static constexpr bool OperationRequired(TopologicalElement fel, + TopologicalElement cel) { + return fel == cel; + } + + template + KOKKOS_FORCEINLINE_FUNCTION static void + Do(const int l, const int m, const int n, const int k, const int j, const int i, + const IndexRange &ckb, const IndexRange &cjb, const IndexRange &cib, + const IndexRange &kb, const IndexRange &jb, const IndexRange &ib, + const Coordinates_t &coords, const Coordinates_t &coarse_coords, + const ParArrayND *, + const ParArrayND *pfine) { + + // Definitely exit on what we can't handle + if constexpr (el != TE::F1 && el != TE::F2 && el != TE::F3) + return; + + // Handle permutations "naturally." + // Olivares et al. is fond of listing x1 versions which permute, + // this makes translating/checking those easier + constexpr int me = static_cast(el) % 3; + constexpr int next = (me+1) % 3; + constexpr int third = (me+2) % 3; + + // Exit if we're computing a trivial direction + if constexpr ((me == V3 && !(DIM > 2)) || (me == V2 && !(DIM > 1)) || (me == V1 && !(DIM > 0))) + return; + + // Fine array, indices + auto &fine = *pfine; + const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; + const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; + const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; + + // Coefficients selecting a particular formula (see Olivares et al. 2019) + // TODO options here. This corresponds to Cunningham, but we could have: + // 1. differences of squares of zone dimesnions (Toth) + // 2. heuristic based on flux difference of top vs bottom halves (Olivares) + //constexpr Real a[3] = {0., 0., 0.}; + const Real a[3] = {(SQR(coords.Dxc<2>(j)) - SQR(coords.Dxc<3>(k)))/(SQR(coords.Dxc<2>(j)) + SQR(coords.Dxc<3>(k))), + (SQR(coords.Dxc<3>(k)) - SQR(coords.Dxc<1>(i)))/(SQR(coords.Dxc<3>(k)) + SQR(coords.Dxc<1>(i))), + (SQR(coords.Dxc<1>(i)) - SQR(coords.Dxc<2>(j)))/(SQR(coords.Dxc<1>(i)) + SQR(coords.Dxc<2>(j)))}; + + // Coefficients for each term evaluating the four sub-faces + const Real coeff[4][4] = {{3 + a[next], 1 - a[next], 3 - a[third], 1 + a[third]}, + {3 + a[next], 1 - a[next], 1 + a[third], 3 - a[third]}, + {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, + {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; + + constexpr int diff_k = (me == V3), diff_j = (me == V2), diff_i = (me == V1); + // if(fi == 10 && fj == 10 && fk == 0) { + // fprintf(stderr, "Prolongating %d %d %d EL %d, DIM %d\n", fi, fj, fk, static_cast(el), DIM); + // fprintf(stderr, "Differencing %d %d %d\n", diff_i, diff_j, diff_k); + // } + + // Iterate through the 4 sub-faces + for (int elem=0; elem < 4; elem++) { + // Make sure we can offset in other directions before doing so, though + // TODO eliminate redundant work or template these so the compiler can? + const int off_i = (DIM > 0) ? elem%2*(me == V2) + elem/2*(me == V3) + (me == V1) : 0; + const int off_j = (DIM > 1) ? elem%2*(me == V3) + elem/2*(me == V1) + (me == V2) : 0; + const int off_k = (DIM > 2) ? elem%2*(me == V1) + elem/2*(me == V2) + (me == V3) : 0; + + fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = + // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) + 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + + fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + + 1./16*(coeff[elem][0]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][1]*F(fine, l, m, n, fk, fj, fi) + + coeff[elem][2]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); + + // if(fi == 10 && fj == 10 && fk == 0 && me == V1) { + // fprintf(stderr, "Elem %d Offset %d %d %d set %g\n", elem, off_i, off_j, off_k, fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i)); + // fprintf(stderr, "Averaging faces %d %d %d and %d %d %d (%g & %g)\n", fi+off_i-diff_i, fj+off_j-diff_j, fk+off_k-diff_k, + // fi+off_i+diff_i, fj+off_j+diff_j, fk+off_k+diff_k, + // fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i), + // fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)); + // fprintf(stderr, "Coeffs %g %g %g %g\n", coeff[elem][0]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][1]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][2]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); + // } + + } + } +}; + } diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 6f846c02..2a958427 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -102,10 +102,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + // Flags for B fields. "GRPrimitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index ffdd4c3f..1a6560f2 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -46,7 +46,7 @@ * * This requires only the values at cell centers * - * This implementation includes conversion from "primitive" to "conserved" B and back + * This implementation includes conversion from "GRPrimitive" to "conserved" B and back */ namespace B_FluxCT { /** diff --git a/kharma/domain.hpp b/kharma/domain.hpp index d26ceec1..a15f2d6a 100644 --- a/kharma/domain.hpp +++ b/kharma/domain.hpp @@ -42,7 +42,6 @@ namespace KDomain { /** * Functions for checking boundaries in 3D. - * Uses IndexRange objects, or this would be in kharma_utils.hpp */ KOKKOS_INLINE_FUNCTION bool outside(const int& k, const int& j, const int& i, const IndexRange& kb, const IndexRange& jb, const IndexRange& ib) @@ -66,6 +65,7 @@ KOKKOS_INLINE_FUNCTION bool inside(const int& k, const int& j, const int& i, con } // TODO(BSP) these really should be in Parthenon +// There's a templated way to do it I forget, but this would be easier template inline const int& GetNDim(MeshBlockData* rc) { return rc->GetBlockPointer()->pmy_mesh->ndim; } diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 3928a2c5..4f58eafc 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -171,7 +171,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), md_sub_step_init.get(), md_solver.get()); } @@ -199,7 +199,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Copy the primitives to the `linesearch` MeshData object if linesearch was enabled. auto t_copy_linesearch = t_guess_ready; if (use_linesearch) { - t_copy_linesearch = tl.AddTask(t_guess_ready, Copy>, std::vector({Metadata::GetUserFlag("Primitive")}), + t_copy_linesearch = tl.AddTask(t_guess_ready, Copy>, std::vector({Metadata::GetUserFlag("GRPrimitive")}), md_solver.get(), md_linesearch.get()); } diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 6c695ea4..e9508ee2 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -68,7 +68,7 @@ class KHARMADriver : public MultiStageDriver { * so that the driver can repeat calls to create a predictor-corrector, RK2/4, etc. * * Unlike MHD, GRMHD must keep two forms of the variables: the conserved variables, and a set of - * "primitive" variables more amenable to reconstruction. To evolve the fluid, the code must: + * "GRPrimitive" variables more amenable to reconstruction. To evolve the fluid, the code must: * 1. Reconstruct the right- and left-going components at zone faces, given the primitive variables * 2. Calculate the fluxes of conserved quantities through the faces * 2a. Apply any fixes to fluxes (e.g., for the magnetic field) diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 19a6992c..e3275769 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -197,7 +197,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // on adjacent ranks are seeded with the same value, which keeps them (more) similar auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index 80cf1020..ca1e7b79 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -113,7 +113,7 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s // UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index 0d03fe27..d52f6beb 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -126,9 +126,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, areWeImplicit, Metadata::GetUserFlag("Electrons")}; - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), Metadata::Restart, areWeImplicit, Metadata::GetUserFlag("Electrons")}; // Total entropy, used to track changes @@ -201,7 +201,7 @@ TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInpu // Need to distinguish KTOT from the other variables, so we record which it is PackIndexMap prims_map; - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}, prims_map); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRPrimitive")}, prims_map); const int ktot_index = prims_map["prims.Ktot"].first; // Just need these two from the rest of Prims GridScalar rho = rc->Get("prims.rho").data; @@ -238,8 +238,8 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); // No need for a "map" here, we just want everything that fits these - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}); - auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::Conserved}); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRPrimitive")}); + auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRConserved")}); // And then the local density GridScalar rho_U = rc->Get("cons.rho").data; @@ -261,8 +261,8 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); // And then the local density GridScalar rho_P = rc->Get("cons.rho").data; @@ -287,9 +287,9 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // so we only bother with one map of the primitives // TODO Parthenon can definitely build a pack from a map, though PackIndexMap prims_map, cons_map; - auto& P = rc_old->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto& P_new = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto& U_new = rc->PackVariables({Metadata::Conserved}, cons_map); + auto& P = rc_old->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto& P_new = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto& U_new = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); auto pmb = rc->GetBlockPointer(); diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 3150a198..4b235b83 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -129,9 +129,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); -// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); +// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); // const VarMap m_p(prims_map, false), m_u(cons_map, true); // const auto& G = pmb->coords; @@ -217,8 +217,8 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); - auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::GetUserFlag("GRConserved")}, cons_map); + auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); const auto& G = pmb->coords; @@ -263,9 +263,9 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap prims_map, cons_map, source_map; - auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = md->PackVariables(std::vector{Metadata::Conserved}, cons_map); - auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, source_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto U = md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, source_map); const VarMap m_p(prims_map, false), m_u(cons_map, true), m_s(source_map, true); // Get temporary ucov, Theta for gradients diff --git a/kharma/emhd/emhd_limits.hpp b/kharma/emhd/emhd_limits.hpp index 8da45c8a..0da0692d 100644 --- a/kharma/emhd/emhd_limits.hpp +++ b/kharma/emhd/emhd_limits.hpp @@ -130,8 +130,8 @@ inline void ApplyEMHDLimits(MeshBlockData *mbd, IndexDomain domain) auto packages = pmb->packages; PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 1dd4e557..120ee04b 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -161,8 +161,8 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; @@ -227,8 +227,8 @@ TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index a49d704a..f048d4d1 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -94,8 +94,8 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; @@ -126,8 +126,8 @@ TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coa // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::Conserved, Metadata::Cell}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const int nvar = U.GetDim(4); @@ -167,8 +167,8 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; @@ -229,8 +229,8 @@ void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap prims_map, cons_map; - auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); // EMHD params diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 534c27c4..1fa4062a 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -107,8 +107,8 @@ inline TaskStatus GetFlux(MeshData *md) const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); // TODO maybe all WithFluxes vars, split into cell & face? - const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::Cell}, prims_map); + const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::Cell}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& Pl_all = md->PackVariables(std::vector{"Flux.Pl"}); diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 9c9f1c41..a5474e7a 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -127,8 +127,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, - Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + Metadata::Restart, Metadata::GetUserFlag("GRPrimitive"), + Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, - Metadata::WithFluxes, Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + Metadata::WithFluxes, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, + Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; bool sync_prims = packages->Get("Driver")->Param("sync_prims"); if (!sync_prims) { // Normal operation diff --git a/kharma/grmhd/pack.hpp b/kharma/grmhd/pack.hpp index dac61a3d..50063167 100644 --- a/kharma/grmhd/pack.hpp +++ b/kharma/grmhd/pack.hpp @@ -50,29 +50,29 @@ namespace GRMHD { */ inline VariablePack PackMHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); } inline MeshBlockPack> PackMHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); } inline VariablePack PackMHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { - return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("MHD")}, cons_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("MHD")}, cons_map, coarse); } inline MeshBlockPack> PackMHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("MHD")}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("MHD")}, cons_map, coarse); } inline VariablePack PackHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); } inline MeshBlockPack> PackHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); } // Version without template @@ -81,11 +81,11 @@ inline VariablePack PackHDPrims(T data) { PackIndexMap nop; return PackHDP inline VariablePack PackHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { auto pmb = rc->GetBlockPointer(); - return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("HD")}, cons_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("HD")}, cons_map, coarse); } inline MeshBlockPack> PackHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("HD")}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("HD")}, cons_map, coarse); } diff --git a/kharma/implicit/fix_solve.cpp b/kharma/implicit/fix_solve.cpp index 9c9d7104..3703b5ec 100644 --- a/kharma/implicit/fix_solve.cpp +++ b/kharma/implicit/fix_solve.cpp @@ -46,7 +46,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { // Get number of implicit variables PackIndexMap implicit_prims_map; - auto implicit_vars = Implicit::GetOrderedNames(mbd, Metadata::GetUserFlag("Primitive"), true); + auto implicit_vars = Implicit::GetOrderedNames(mbd, Metadata::GetUserFlag("GRPrimitive"), true); auto& P = mbd->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P.GetDim(4); @@ -131,8 +131,8 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { // Since floors were applied earlier, we assume the zones obtained by averaging the neighbors also respect the floors. // Compute new conserved variables PackIndexMap prims_map, cons_map; - auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - auto& U_all = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto& U_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); // Need emhd_params object diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 826a4665..7d0851fc 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -186,8 +186,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // just the residual & Jacobian we care about, which makes the solve faster. auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); - auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::Conserved); + auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRPrimitive")); + auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRConserved")); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -209,7 +209,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const int nblock = U_full_step_init_all.GetDim(5); const int nvar = U_full_step_init_all.GetDim(4); // Get number of implicit variables - auto implicit_vars = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive"), true); + auto implicit_vars = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRPrimitive"), true); //std::cerr << "Ordered implicit:"; for(auto var: implicit_vars) std::cerr << " " << var; std::cerr << std::endl; PackIndexMap implicit_prims_map; diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 170cd914..ff1ac667 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -63,7 +63,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame // Get all primitive variables (GRMHD+EMHD if in use) PackIndexMap prims_map; - auto P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); VarMap m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 1e2bdb34..5832403d 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -35,6 +35,10 @@ #pragma once #include "decs.hpp" +#include "domain.hpp" +#include "types.hpp" + +#include "b_ct.hpp" #include @@ -59,18 +63,17 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P const Real uflow = pin->GetOrAddReal("kelvin_helmholtz", "uflow", 1.); const Real a = pin->GetOrAddReal("kelvin_helmholtz", "a", 0.05); const Real sigma = pin->GetOrAddReal("kelvin_helmholtz", "sigma", 0.2); - const Real A = pin->GetOrAddReal("kelvin_helmholtz", "A", 0.01); + const Real amp = pin->GetOrAddReal("kelvin_helmholtz", "amp", 0.01); const Real z1 = pin->GetOrAddReal("kelvin_helmholtz", "z1", 0.5); const Real z2 = pin->GetOrAddReal("kelvin_helmholtz", "z2", 1.5); + const Real added_b = pin->GetOrAddReal("kelvin_helmholtz", "added_b", 0.0); const auto& G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - IndexDomain domain = IndexDomain::interior; - IndexRange ib = pmb->cellbounds.GetBoundsI(domain); - IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); - IndexRange kb = pmb->cellbounds.GetBoundsK(domain); - pmb->par_for("kh_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + IndexDomain domain = IndexDomain::entire; + IndexRange3 b = KDomain::GetRange(rc, domain, 0, 0); + pmb->par_for("kh_init", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); @@ -83,17 +86,59 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P rho0 + Drho * 0.5 * (tanh((z - z1) / a) - tanh((z - z2) / a)); u(k, j, i) = P0 / (gam - 1.); uvec(0, k, j, i) = uflow * (tanh((z - z1) / a) - tanh((z - z2) / a) - 1.); - uvec(1, k, j, i) = A * sin(2. * M_PI * x) * + uvec(1, k, j, i) = amp * sin(2. * M_PI * x) * (m::exp(-(z - z1) * (z - z1) / (sigma * sigma)) + m::exp(-(z - z2) * (z - z2) / (sigma * sigma))); uvec(2, k, j, i) = 0; } ); + + if (pmb->packages.AllPackages().count("B_CT")) { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // Halo one zone right for faces + // We don't need any more than that, since curls never take d1dx1 + IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); + IndexSize3 s = KDomain::GetBlockSize(rc); + GridVector A("A", NVEC, s.n3, s.n2, s.n1); + pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real Xembed[GR_DIM]; + G.coord(k, j, i, Loci::corner, Xembed); + A(V3, k, j, i) = added_b * (Xembed[1] + Xembed[2]) * tscale; + } + ); + // This fills a couple zones outside the exact interior with bad data + IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); + pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + B_CT::curl_2D(G, A, B_Uf, k, j, i); + } + ); + B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); + double max_divb = B_CT::BlockMaxDivB(rc.get()); + std::cout << "Block max DivB: " << max_divb << std::endl; + + } else if (pmb->packages.AllPackages().count("B_FluxCT") || + pmb->packages.AllPackages().count("B_CD")) { + GridVector B_P = rc->Get("prims.B").data; + pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + Real X[GR_DIM]; + G.coord(k, j, i, Loci::center, X); + B_P(V1, k, j, i) = added_b * tscale; + B_P(V2, k, j, i) = added_b * tscale; + B_P(V3, k, j, i) = 0.; + } + ); + B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); + } + // Rescale primitive velocities by tscale, and internal energy by the square. - pmb->par_for("kh_renorm", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("kh_renorm", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { u(k, j, i) *= tscale * tscale; VLOOP uvec(v, k, j, i) *= tscale; + //VLOOP B_P(v, k, j, i) *= tscale; //already done } ); diff --git a/kharma/reductions/reductions_impl.hpp b/kharma/reductions/reductions_impl.hpp index df42b1b8..bb2f0081 100644 --- a/kharma/reductions/reductions_impl.hpp +++ b/kharma/reductions/reductions_impl.hpp @@ -131,8 +131,8 @@ T Reductions::EHReduction(MeshData *md, UserHistoryOperation op, int zone) const auto& emhd_params = EMHD::GetEMHDParameters(pmesh->packages); PackIndexMap prims_map, cons_map; - const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); @@ -210,8 +210,8 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const // Just pass in everything we might want. Probably slow? PackIndexMap prims_map, cons_map; - const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); diff --git a/kharma/wind/wind.cpp b/kharma/wind/wind.cpp index 1a560fa1..bc3a77d2 100644 --- a/kharma/wind/wind.cpp +++ b/kharma/wind/wind.cpp @@ -79,7 +79,7 @@ TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap cons_map; - auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); const VarMap m_u(cons_map, true); // Get sizes const IndexRange ib = mdudt->GetBoundsI(IndexDomain::interior); diff --git a/machines/bp.sh b/machines/bp.sh index f7997917..5688072b 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -32,6 +32,7 @@ if [[ $METAL_HOSTNAME == "fermium" ]]; then echo "Nothing special for cuda" else # AMD for CPUs + module load aocc-compiler-4.1.0 mpi CXX_NATIVE=clang++ C_NATIVE=clang fi diff --git a/pars/kelvin_helmholtz.par b/pars/kelvin_helmholtz.par new file mode 100644 index 00000000..aa3a91ba --- /dev/null +++ b/pars/kelvin_helmholtz.par @@ -0,0 +1,79 @@ +# GRMHD Modes problem +# Try to propagate several analytically-amenable linear modes of the MHD equations + + +problem_id = kelvin_helmholtz + + +refinement = adaptive +numlevel = 3 + +nx1 = 128 +x1min = 0.0 +x1max = 1.0 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 256 +x2min = 0.0 +x2max = 2.0 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +method = derivative_order_1 +field = prims.rho +refine_tol = 0.01 +derefine_tol = 0.001 + + +base = cartesian_minkowski +transform = null + + +tlim = 200.0 +integrator = rk2 + + +tscale = 0.01 +added_b = 1 + + +u_jitter = 0.01 + + +type = kharma + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = linear_mc + + +#solver = flux_ct # TODO warn on using flux_ct when AMR +solver = face_ct +kill_on_large_divb = true +#ct_scheme = bs99 +ct_scheme = sg09 + + +verbose = 1 +flag_verbose = 0 +extra_checks = 0 + + +file_type = hdf5 +dt = 0.5 +variables = prims.rho, prims.u, prims.uvec, prims.B, divB + From 60f933bd7e23dd79e32233ca2570706bee767da7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 22 Aug 2023 10:59:47 -0600 Subject: [PATCH 112/219] Make make.sh fallback gracefully w/old git --- machines/darwin.sh | 33 ++++++++++++++++++++++++++------- make.sh | 8 +++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/machines/darwin.sh b/machines/darwin.sh index 4bf58f07..ba3c8858 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -10,10 +10,12 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then module purge module load cmake - # Always our own HDF5 - # Run ""./make.sh hdf5" to build it - PREFIX_PATH="$SOURCE_DIR/external/hdf5" + # Help Darwin find the right modules in automated jobs + if [[ "$ARGS" == *"cuda"* ]]; then + export MODULEPATH="/projects/darwin-nv/modulefiles/rhel8/aarch64:/projects/darwin-nv/modulefiles/rhel8/aarch64" + fi + # Load modules based on first argument... if [[ "$ARGS" == *"cuda"* ]]; then if [[ "$ARGS" == *"gcc12"* ]]; then module load cuda/12.0.0 openmpi gcc/12.1.0 @@ -24,13 +26,19 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then C_NATIVE=gcc CXX_NATIVE=g++ else - module load nvhpc/23.3 cuda/11.7.0 + module load nvhpc/22.1 cuda/12.0.0 C_NATIVE="nvc" CXX_NATIVE="nvc++" # New NVHPC doesn't like CUDA_HOME export NVHPC_CUDA_HOME="$CUDA_HOME" unset CUDA_HOME fi + elif [[ "$ARGS" == *"hip"* ]]; then + module load rocm/5.4.3 #openmpi/5.0.0rc11-gcc_13.1.0 + source ~/libs/env.sh + C_NATIVE=hipcc + CXX_NATIVE=hipcc + export CXXFLAGS="-fopenmp $CXXFLAGS" else if [[ "$ARGS" == *"gcc"* ]]; then module load openmpi gcc/10.2.0 @@ -44,14 +52,20 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then fi fi - # These are orthogonal to above. - # Just don't compile for an nv arch without "cuda" + # ...and set architecture according to second. + # These are orthogonal to above, so long as the hardware + # supports the paradigm NPROC=$(($(nproc) / 2)) - if [[ "$ARGS" == *"arm-nv"* ]]; then + if [[ "$ARGS" == *"arm-ampere"* ]]; then HOST_ARCH="ARMV81" DEVICE_ARCH="AMPERE80" MPI_NUM_PROCS=2 NODE_SLICE=2 + elif [[ "$ARGS" == *"arm-hopper"* ]]; then + HOST_ARCH="ARMV81" + DEVICE_ARCH="HOPPER90" + MPI_NUM_PROCS=1 + NODE_SLICE=1 elif [[ "$ARGS" == *"ampere"* ]]; then HOST_ARCH="ZEN3" DEVICE_ARCH="AMPERE80" @@ -72,6 +86,11 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then HOST_ARCH="HSW" MPI_NUM_PROCS=1 NODE_SLICE=1 + elif [[ "$ARGS" == *"mi250"* ]]; then + HOST_ARCH=ZEN3 + DEVICE_ARCH=VEGA90A + MPI_NUM_PROCS=8 + NODE_SLICE=16 else echo "Must specify an architecture on Darwin!" exit diff --git a/make.sh b/make.sh index 66279406..a891a6bc 100755 --- a/make.sh +++ b/make.sh @@ -74,6 +74,7 @@ fi ### Enivoronment Prep ### if [[ "$(which python3 2>/dev/null)" == *"conda"* ]]; then echo + echo "make.sh note:" echo "It looks like you have Anaconda loaded." echo "Anaconda loads a serial version of HDF5 which may make this compile impossible." echo "If you run into trouble, deactivate your environment with 'conda deactivate'" @@ -281,7 +282,12 @@ fi if [[ "$ARGS" == *"clean"* ]]; then cd external/parthenon - git apply --quiet ../patches/parthenon-*.patch + if [[ $(( $(git --version | cut -d '.' -f 2) > 35 )) ]]; then + git apply --quiet ../patches/parthenon-*.patch + else + echo "make.sh note: You may see errors applying patches below. These are normal." + git apply ../patches/parthenon-*.patch + fi cd - rm -rf build From e472f63ecf4698421d1582d3b4366213fd95df78 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 23 Aug 2023 22:11:16 -0600 Subject: [PATCH 113/219] Working CT/AMR After a fix to parthenon and an index check, AMR/re-meshing fully works. Also discovered/tried/demonstrated static meshing. Several drive-by fixes for compiler warnings etc, and switch to the new split ParthenonInit to call FixParameters a little more naturally. Just need polar/outflow boundaries (maybe?) and initialization and we've got working 2D SANE AMR sims. Note the EMF prolongation operator WILL NEED SMALL CHANGES FOR 3D. --- external/parthenon | 2 +- kharma/b_ct/b_ct.cpp | 40 +++- kharma/b_ct/b_ct.hpp | 264 ++++++++++++++++----- kharma/boundaries/boundaries.cpp | 10 +- kharma/driver/kharma_driver.cpp | 5 +- kharma/driver/kharma_driver.hpp | 8 +- kharma/driver/kharma_step.cpp | 22 +- kharma/floors/floors.cpp | 3 - kharma/flux/flux.cpp | 3 +- kharma/kharma.cpp | 19 +- kharma/kharma.hpp | 2 +- kharma/main.cpp | 121 +++++----- kharma/prob/b_field_tools.hpp | 24 +- kharma/prob/fm_torus.cpp | 2 +- kharma/prob/resize_restart.cpp | 2 +- kharma/prob/resize_restart.hpp | 2 +- kharma/prob/resize_restart_kharma.cpp | 2 +- kharma/prob/resize_restart_kharma.hpp | 2 +- kharma/reductions/reductions_variables.hpp | 3 + kharma/types.hpp | 4 +- pars/kelvin_helmholtz.par | 30 ++- pars/sane2d_refined.par | 92 +++++++ run.sh | 4 + 23 files changed, 475 insertions(+), 191 deletions(-) create mode 100644 pars/sane2d_refined.par diff --git a/external/parthenon b/external/parthenon index 437e02bf..374d08c6 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 437e02bf62734f7a7962b9e5d0fae0ab36a34dfc +Subproject commit 374d08c66d1137951816a017c28a201392d46310 diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 6e2845e5..bd79bc7e 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -44,6 +44,8 @@ #include using namespace parthenon; +using parthenon::refinement_ops::ProlongateSharedMinMod; +using parthenon::refinement_ops::RestrictAverage; std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared_ptr& packages) { @@ -87,7 +89,7 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared auto m = Metadata(flags_prim_f); pkg->AddField("prims.fB", m); m = Metadata(flags_cons_f); - m.RegisterRefinementOps(); + m.RegisterRefinementOps(); pkg->AddField("cons.fB", m); // Cell-centered versions. Needed for BS, not for other schemes. @@ -104,8 +106,9 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // EMF on edges. // TODO only sync when needed - std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy}; + std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}; m = Metadata(flags_emf); + m.RegisterRefinementOps(); pkg->AddField("B_CT.emf", m); if (ct_scheme == "sg09") { @@ -117,8 +120,8 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // CALLBACKS // We implement a source term replacement, rather than addition, - // but same difference, really - //pkg->AddSource = B_CT::AddSource; + // but same difference really + pkg->AddSource = B_CT::AddSource; // Also ensure that prims get filled, both during step and on boundaries //pkg->MeshUtoP = B_CT::MeshUtoP; @@ -180,7 +183,6 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) B_Pf(F3, 0, k, j, i) = B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i); } ); - Kokkos::fence(); // Average the primitive vals for zone centers (TODO right?) const IndexRange3 bc = KDomain::GetRange(rc, domain, coarse); pmb->par_for("UtoP_B_center", bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, @@ -192,22 +194,21 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) : B_Pf(F3, 0, k, j, i); } ); - Kokkos::fence(); pmb->par_for("UtoP_B_centerPtoU", 0, NVEC-1, bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { B_U(v, k, j, i) = B_P(v, k, j, i) * G.gdet(Loci::center, j, i); } ); - Kokkos::fence(); } -// TODO this isn't really a source... it's a replacement of the -// face-centered fields according to constrained transport rules -TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_ptr>& mdudt) +TaskStatus B_CT::CalculateEMF(MeshData *md) { auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; + //md->GetMeshPointer()->mesh_data.Add("emf", md, std::vector{"B_CT.emf"}); + //KHARMADriver::Copy(); + // EMF temporary auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); @@ -296,6 +297,23 @@ TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_pt } else { throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, sg09"); } + return TaskStatus::complete; +} + +TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) +{ + auto pmesh = md->GetMeshPointer(); + const int ndim = pmesh->ndim; + + // EMF temporary + auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); + + // Figure out indices + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); + const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; + + auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); // This is what we're replacing auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); @@ -331,8 +349,6 @@ TaskStatus B_CT::UpdateFaces(std::shared_ptr>& md, std::shared_pt return TaskStatus::complete; } - - double B_CT::MaxDivB(MeshData *md) { auto pmesh = md->GetMeshPointer(); diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index 5891bf01..7e056b34 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -39,6 +39,8 @@ #include "reductions.hpp" #include "types.hpp" +#include "kharma_driver.hpp" + #include #include @@ -69,20 +71,26 @@ TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); * input: Conserved B = sqrt(-gdet) * B^i * output: Primitive B = B^i */ -void BlockUtoP(MeshBlockData *md, IndexDomain domain, bool coarse=false); +void BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** - * Reverse of the above. Only used alone during initialization. + * Reverse of the above. Only used by itself during initialization. * Generally, use Flux::BlockPtoU or Flux::BlockPtoUExceptMHD. */ void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); /** - * Replace conserved face B field components with versions calculated - * by constrained transport. + * Calculate the EMF around edges of faces caused by the flux of B field + * through each face. + */ +TaskStatus CalculateEMF(MeshData *md); + +/** + * Calculate the change in magnetic field on faces for this step, + * from the EMFs at edges. */ -TaskStatus UpdateFaces(std::shared_ptr>& md, std::shared_ptr>& mdudt); +TaskStatus AddSource(MeshData *md, MeshData *mdudt); // TODO UNIFY ALL THE FOLLOWING @@ -242,7 +250,7 @@ KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, struct ProlongateInternalOlivares { static constexpr bool OperationRequired(TopologicalElement fel, TopologicalElement cel) { - return fel == cel; + return fel == cel && (fel == F1 || fel == F2 || fel == F3); } template *, const ParArrayND *pfine) { - // Definitely exit on what we can't handle - if constexpr (el != TE::F1 && el != TE::F2 && el != TE::F3) - return; + // Definitely exit on what we can't handle + if constexpr (el != TE::F1 && el != TE::F2 && el != TE::F3) + return; + + // Handle permutations "naturally." + // Olivares et al. is fond of listing x1 versions which permute, + // this makes translating/checking those easier + constexpr int me = static_cast(el) % 3; + constexpr int next = (me+1) % 3; + constexpr int third = (me+2) % 3; + + // Exit if we're computing a trivial direction + if constexpr ((me == V3 && !(DIM > 2)) || (me == V2 && !(DIM > 1)) || (me == V1 && !(DIM > 0))) + return; + + // Fine array, indices + auto &fine = *pfine; + const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; + const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; + const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; + + // TODO can we handle this in Parthenon instead? + if ((el == TE::F1 && fi+2 > ib.s) || (el == TE::F2 && fj+2 > jb.s) || (el == TE::F3 && fk+2 > kb.s)) + return; + + // Coefficients selecting a particular formula (see Olivares et al. 2019) + // TODO options here. This corresponds to Cunningham, but we could have: + // 1. differences of squares of zone dimesnions (Toth) + // 2. heuristic based on flux difference of top vs bottom halves (Olivares) + //constexpr Real a[3] = {0., 0., 0.}; + const Real a[3] = {(SQR(coords.Dxc<2>(j)) - SQR(coords.Dxc<3>(k)))/(SQR(coords.Dxc<2>(j)) + SQR(coords.Dxc<3>(k))), + (SQR(coords.Dxc<3>(k)) - SQR(coords.Dxc<1>(i)))/(SQR(coords.Dxc<3>(k)) + SQR(coords.Dxc<1>(i))), + (SQR(coords.Dxc<1>(i)) - SQR(coords.Dxc<2>(j)))/(SQR(coords.Dxc<1>(i)) + SQR(coords.Dxc<2>(j)))}; + + // Coefficients for each term evaluating the four sub-faces + const Real coeff[4][4] = {{3 + a[next], 1 - a[next], 3 - a[third], 1 + a[third]}, + {3 + a[next], 1 - a[next], 1 + a[third], 3 - a[third]}, + {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, + {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; + + constexpr int diff_k = (me == V3), diff_j = (me == V2), diff_i = (me == V1); + // if(fi == 10 && fj == 10 && fk == 0) { + // fprintf(stderr, "Prolongating %d %d %d EL %d, DIM %d\n", fi, fj, fk, static_cast(el), DIM); + // fprintf(stderr, "Differencing %d %d %d\n", diff_i, diff_j, diff_k); + // } - // Handle permutations "naturally." - // Olivares et al. is fond of listing x1 versions which permute, - // this makes translating/checking those easier - constexpr int me = static_cast(el) % 3; - constexpr int next = (me+1) % 3; - constexpr int third = (me+2) % 3; + // Iterate through the 4 sub-faces + for (int elem=0; elem < 4; elem++) { + // Make sure we can offset in other directions before doing so, though + // TODO eliminate redundant work or template these so the compiler can? + const int off_i = (DIM > 0) ? elem%2*(me == V2) + elem/2*(me == V3) + (me == V1) : 0; + const int off_j = (DIM > 1) ? elem%2*(me == V3) + elem/2*(me == V1) + (me == V2) : 0; + const int off_k = (DIM > 2) ? elem%2*(me == V1) + elem/2*(me == V2) + (me == V3) : 0; + + fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = + // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) + 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + + fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + + 1./16*(coeff[elem][0]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][1]*F(fine, l, m, n, fk, fj, fi) + + coeff[elem][2]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); + + // if(fi == 10 && fj == 10 && fk == 0 && me == V1) { + // fprintf(stderr, "Elem %d Offset %d %d %d set %g\n", elem, off_i, off_j, off_k, fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i)); + // fprintf(stderr, "Averaging faces %d %d %d and %d %d %d (%g & %g)\n", fi+off_i-diff_i, fj+off_j-diff_j, fk+off_k-diff_k, + // fi+off_i+diff_i, fj+off_j+diff_j, fk+off_k+diff_k, + // fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i), + // fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)); + // fprintf(stderr, "Coeffs %g %g %g %g\n", coeff[elem][0]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][1]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][2]*F(fine, l, m, n, fk, fj, fi), + // coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); + // } + } + } +}; - // Exit if we're computing a trivial direction - if constexpr ((me == V3 && !(DIM > 2)) || (me == V2 && !(DIM > 1)) || (me == V1 && !(DIM > 0))) - return; +struct RestrictNearest { + static constexpr bool OperationRequired(TopologicalElement fel, + TopologicalElement cel) { + return fel == cel && (fel == E1 || fel == E2 || fel == E3); + } - // Fine array, indices + template + KOKKOS_FORCEINLINE_FUNCTION static void + Do(const int l, const int m, const int n, const int ck, const int cj, const int ci, + const IndexRange &ckb, const IndexRange &cjb, const IndexRange &cib, + const IndexRange &kb, const IndexRange &jb, const IndexRange &ib, + const Coordinates_t &coords, const Coordinates_t &coarse_coords, + const ParArrayND *pcoarse, + const ParArrayND *pfine) { + + auto &coarse = *pcoarse; + auto &fine = *pfine; + + constexpr int element_idx = static_cast(el) % 3; + const int i = (DIM > 0) ? (ci - cib.s) * 2 + ib.s : ib.s; + const int j = (DIM > 1) ? (cj - cjb.s) * 2 + jb.s : jb.s; + const int k = (DIM > 2) ? (ck - ckb.s) * 2 + kb.s : kb.s; + + coarse(element_idx, l, m, n, ck, cj, ci) = 0.5*fine(element_idx, l, m, n, k, j, i); + } +}; + +struct ProlongateSharedMinMod2 { + static constexpr bool OperationRequired(TopologicalElement fel, + TopologicalElement cel) { + return fel == cel && (fel == E1 || fel == E2 || fel == E3); + } + + template + KOKKOS_FORCEINLINE_FUNCTION static void + Do(const int l, const int m, const int n, const int k, const int j, const int i, + const IndexRange &ckb, const IndexRange &cjb, const IndexRange &cib, + const IndexRange &kb, const IndexRange &jb, const IndexRange &ib, + const Coordinates_t &coords, const Coordinates_t &coarse_coords, + const ParArrayND *pcoarse, + const ParArrayND *pfine) { + using namespace parthenon::refinement_ops::util; + auto &coarse = *pcoarse; auto &fine = *pfine; + + constexpr int element_idx = static_cast(el) % 3; + const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; - // Coefficients selecting a particular formula (see Olivares et al. 2019) - // TODO options here. This corresponds to Cunningham, but we could have: - // 1. differences of squares of zone dimesnions (Toth) - // 2. heuristic based on flux difference of top vs bottom halves (Olivares) - //constexpr Real a[3] = {0., 0., 0.}; - const Real a[3] = {(SQR(coords.Dxc<2>(j)) - SQR(coords.Dxc<3>(k)))/(SQR(coords.Dxc<2>(j)) + SQR(coords.Dxc<3>(k))), - (SQR(coords.Dxc<3>(k)) - SQR(coords.Dxc<1>(i)))/(SQR(coords.Dxc<3>(k)) + SQR(coords.Dxc<1>(i))), - (SQR(coords.Dxc<1>(i)) - SQR(coords.Dxc<2>(j)))/(SQR(coords.Dxc<1>(i)) + SQR(coords.Dxc<2>(j)))}; - - // Coefficients for each term evaluating the four sub-faces - const Real coeff[4][4] = {{3 + a[next], 1 - a[next], 3 - a[third], 1 + a[third]}, - {3 + a[next], 1 - a[next], 1 + a[third], 3 - a[third]}, - {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, - {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; - - constexpr int diff_k = (me == V3), diff_j = (me == V2), diff_i = (me == V1); - // if(fi == 10 && fj == 10 && fk == 0) { - // fprintf(stderr, "Prolongating %d %d %d EL %d, DIM %d\n", fi, fj, fk, static_cast(el), DIM); - // fprintf(stderr, "Differencing %d %d %d\n", diff_i, diff_j, diff_k); - // } + constexpr bool INCLUDE_X1 = + (DIM > 0) && (el == TE::CC || el == TE::F2 || el == TE::F3 || el == TE::E1); + constexpr bool INCLUDE_X2 = + (DIM > 1) && (el == TE::CC || el == TE::F3 || el == TE::F1 || el == TE::E2); + constexpr bool INCLUDE_X3 = + (DIM > 2) && (el == TE::CC || el == TE::F1 || el == TE::F2 || el == TE::E3); + + const Real fc = coarse(element_idx, l, m, n, k, j, i); + + Real dx1fm = 0; + [[maybe_unused]] Real dx1fp = 0; + Real gx1c = 0; + if constexpr (INCLUDE_X1) { + Real dx1m, dx1p; + GetGridSpacings<1, el>(coords, coarse_coords, cib, ib, i, fi, &dx1m, &dx1p, &dx1fm, + &dx1fp); + gx1c = GradMinMod(fc, coarse(element_idx, l, m, n, k, j, i - 1), + coarse(element_idx, l, m, n, k, j, i + 1), dx1m, dx1p); + } - // Iterate through the 4 sub-faces - for (int elem=0; elem < 4; elem++) { - // Make sure we can offset in other directions before doing so, though - // TODO eliminate redundant work or template these so the compiler can? - const int off_i = (DIM > 0) ? elem%2*(me == V2) + elem/2*(me == V3) + (me == V1) : 0; - const int off_j = (DIM > 1) ? elem%2*(me == V3) + elem/2*(me == V1) + (me == V2) : 0; - const int off_k = (DIM > 2) ? elem%2*(me == V1) + elem/2*(me == V2) + (me == V3) : 0; - - fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = - // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) - 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + - fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + - 1./16*(coeff[elem][0]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][1]*F(fine, l, m, n, fk, fj, fi) - + coeff[elem][2]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); - - // if(fi == 10 && fj == 10 && fk == 0 && me == V1) { - // fprintf(stderr, "Elem %d Offset %d %d %d set %g\n", elem, off_i, off_j, off_k, fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i)); - // fprintf(stderr, "Averaging faces %d %d %d and %d %d %d (%g & %g)\n", fi+off_i-diff_i, fj+off_j-diff_j, fk+off_k-diff_k, - // fi+off_i+diff_i, fj+off_j+diff_j, fk+off_k+diff_k, - // fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i), - // fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)); - // fprintf(stderr, "Coeffs %g %g %g %g\n", coeff[elem][0]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][1]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][2]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); - // } + Real dx2fm = 0; + [[maybe_unused]] Real dx2fp = 0; + Real gx2c = 0; + if constexpr (INCLUDE_X2) { + Real dx2m, dx2p; + GetGridSpacings<2, el>(coords, coarse_coords, cjb, jb, j, fj, &dx2m, &dx2p, &dx2fm, + &dx2fp); + gx2c = GradMinMod(fc, coarse(element_idx, l, m, n, k, j - 1, i), + coarse(element_idx, l, m, n, k, j + 1, i), dx2m, dx2p); + } + Real dx3fm = 0; + [[maybe_unused]] Real dx3fp = 0; + Real gx3c = 0; + if constexpr (INCLUDE_X3) { + Real dx3m, dx3p; + GetGridSpacings<3, el>(coords, coarse_coords, ckb, kb, k, fk, &dx3m, &dx3p, &dx3fm, + &dx3fp); + gx3c = GradMinMod(fc, coarse(element_idx, l, m, n, k - 1, j, i), + coarse(element_idx, l, m, n, k + 1, j, i), dx3m, dx3p); } + + // KGF: add the off-centered quantities first to preserve FP symmetry + // JMM: Extraneous quantities are zero + fine(element_idx, l, m, n, fk, fj, fi) = + (fc - (gx1c * dx1fm + gx2c * dx2fm + gx3c * dx3fm))*2; + if constexpr (INCLUDE_X1) + fine(element_idx, l, m, n, fk, fj, fi + 1) = + (fc + (gx1c * dx1fp - gx2c * dx2fm - gx3c * dx3fm))*2; + if constexpr (INCLUDE_X2) + fine(element_idx, l, m, n, fk, fj + 1, fi) = + (fc - (gx1c * dx1fm - gx2c * dx2fp + gx3c * dx3fm))*2; + if constexpr (INCLUDE_X2 && INCLUDE_X1) + fine(element_idx, l, m, n, fk, fj + 1, fi + 1) = + (fc + (gx1c * dx1fp + gx2c * dx2fp - gx3c * dx3fm))*2; + if constexpr (INCLUDE_X3) + fine(element_idx, l, m, n, fk + 1, fj, fi) = + (fc - (gx1c * dx1fm + gx2c * dx2fm - gx3c * dx3fp))*2; + if constexpr (INCLUDE_X3 && INCLUDE_X1) + fine(element_idx, l, m, n, fk + 1, fj, fi + 1) = + (fc + (gx1c * dx1fp - gx2c * dx2fm + gx3c * dx3fp))*2; + if constexpr (INCLUDE_X3 && INCLUDE_X2) + fine(element_idx, l, m, n, fk + 1, fj + 1, fi) = + (fc - (gx1c * dx1fm - gx2c * dx2fp - gx3c * dx3fp))*2; + if constexpr (INCLUDE_X3 && INCLUDE_X2 && INCLUDE_X1) + fine(element_idx, l, m, n, fk + 1, fj + 1, fi + 1) = + (fc + (gx1c * dx1fp + gx2c * dx2fp + gx3c * dx3fp))*2; } }; diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index e3ac6c15..6c06f746 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -158,6 +158,8 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: case BoundaryFace::outer_x3: pkg->KBoundaries[bface] = KBoundaries::Dirichlet; break; + default: + break; } } else if (btype == "reflecting") { switch (bface) { @@ -179,6 +181,8 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: case BoundaryFace::outer_x3: pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX3; break; + default: + break; } } else if (btype == "outflow") { switch (bface) { @@ -200,6 +204,8 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: case BoundaryFace::outer_x3: pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX3; break; + default: + break; } } } @@ -233,7 +239,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // Prevent inflow of material by changing fluid speeds, // anywhere we've specified. if (params.Get("check_inflow_" + bname)) { - Flag("CheckInflow"); + Flag("CheckInflow_"+bname); CheckInflow(rc, domain, coarse); EndFlag(); } @@ -267,7 +273,7 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom // Inflow check // Iterate over zones w/p=0 pmb->par_for_bndry( - "Outflow_check_inflow", IndexRange{0, 0}, domain, CC, coarse, + "check_inflow", IndexRange{0, 0}, domain, CC, coarse, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 37a28dec..b5ad9e17 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -189,13 +189,13 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: return t_bounds; } -void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) +TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) { Flag("SyncAllBounds"); TaskID t_none(0); // 1. PtoU on the interior to ensure we're up-to-date - Flux::MeshPtoU(md.get(), IndexDomain::interior, false); + //Flux::MeshPtoU(md.get(), IndexDomain::interior, false); // 2. Sync MPI bounds // This call syncs the primitive variables when using the ImEx driver, and cons @@ -216,6 +216,7 @@ void KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_ } EndFlag(); + return TaskStatus::complete; } TaskID KHARMADriver::AddFluxCalculations(TaskID& t_start, TaskList& tl, KReconstruction::Type recon, MeshData *md) diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index e9508ee2..48b8ff93 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -36,7 +36,7 @@ #include "decs.hpp" #include "types.hpp" -#include "reconstruction.hpp" +#include "flux/reconstruction.hpp" using namespace parthenon; @@ -68,7 +68,7 @@ class KHARMADriver : public MultiStageDriver { * so that the driver can repeat calls to create a predictor-corrector, RK2/4, etc. * * Unlike MHD, GRMHD must keep two forms of the variables: the conserved variables, and a set of - * "GRPrimitive" variables more amenable to reconstruction. To evolve the fluid, the code must: + * "primitive" variables more amenable to reconstruction. To evolve the fluid, the code must: * 1. Reconstruct the right- and left-going components at zone faces, given the primitive variables * 2. Calculate the fluxes of conserved quantities through the faces * 2a. Apply any fixes to fluxes (e.g., for the magnetic field) @@ -133,8 +133,10 @@ class KHARMADriver : public MultiStageDriver { /** * Single call to sync all boundary conditions (MPI/internal and domain/physical boundaries) * Used anytime boundary sync is needed outside the usual loop of steps. + * + * Only use this as a task each step when debugging! */ - static void SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds=true); + static TaskStatus SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds=true); // TODO swapped versions of these /** diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index e3275769..baef3a12 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -133,12 +133,21 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // This reconstructs the primitives (P) at faces and uses them to calculate fluxes // of the conserved variables (U) through each face. const KReconstruction::Type& recon = driver_pkg.Get("recon"); - auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_bound, tl, recon, md_sub_step_init.get()); + auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_flux, tl, recon, md_sub_step_init.get()); // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; if (pmesh->multilevel || use_b_ct) { - tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_emf = t_fluxes; + // TODO this MPI sync should be bundled into fluxcorr + if (use_b_ct) { + // Pull out a container of only EMF to synchronize + auto &base = pmesh->mesh_data.Get(); + auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition + auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); + auto t_emf = KHARMADriver::AddMPIBoundarySync(t_emf_local, tl, md_emf_only); + } + tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } @@ -153,18 +162,13 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); // Add any source terms: geometric \Gamma * T, wind, damping, etc etc + // Also where CT sets the change in face fields auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); - // CT Update step (needs another boundary sync) - auto t_ct_update = t_sources; - if (use_b_ct) { - t_ct_update = tl.AddTask(t_sources, B_CT::UpdateFaces, md_sub_step_init, md_flux_src); - } - // Perform the update using the source term // Add any proportion of the step start required by the integrator (e.g., RK2) // TODO splitting this is stupid, dig into Parthenon & fix - auto t_avg_data_c = tl.AddTask(t_ct_update, Update::WeightedSumData, MeshData>, + auto t_avg_data_c = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, std::vector({Metadata::Independent, Metadata::Cell}), md_sub_step_init.get(), md_full_step_init.get(), integrator->gam0[stage-1], integrator->gam1[stage-1], diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 120ee04b..d3ee0640 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -201,9 +201,6 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); - fprintf(stderr, "%d %d %d %d %d\n", m_p.RHO, m_p.UU, m_p.U1, m_p.U2, m_p.U3); - fprintf(stderr, "%d %d %d %d %d\n", m_u.RHO, m_u.UU, m_u.U1, m_u.U2, m_u.U3); - // Apply floors over the same zones we just updated with UtoP // This selects the entire domain, but we then require pflag >= 0, // which keeps us from covering completely uninitialized zones diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index f048d4d1..23160b42 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -51,7 +51,6 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared // We can't just use GetVariables or something since there's no mesh yet. // That's what this function is for. int nvar = KHARMA::PackDimension(packages.get(), Metadata::WithFluxes); - std::cout << "Allocating fluxes with nvar: " << nvar << std::endl; std::vector s_flux({nvar}); // TODO optionally move all these to faces? Not important yet, no output, more memory std::vector flags_flux = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; @@ -78,7 +77,7 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared pkg->AddField("Flux.vl", m); } - Flag("Initialized"); + EndFlag(); return pkg; } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 6bc4d950..1ac830bb 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -138,7 +138,7 @@ void KHARMA::MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const globals.Update("time", tm.time); } -void KHARMA::FixParameters(std::unique_ptr& pin) +void KHARMA::FixParameters(ParameterInput *pin) { Flag("Fixing parameters"); // Parthenon sets 2 ghost zones as a default. @@ -162,7 +162,7 @@ void KHARMA::FixParameters(std::unique_ptr& pin) } // Construct a CoordinateEmbedding object. See coordinate_embedding.hpp for supported systems/tags - CoordinateEmbedding tmp_coords(pin.get()); + CoordinateEmbedding tmp_coords(pin); // Record whether we're in spherical as we'll need that pin->SetBoolean("coordinates", "spherical", tmp_coords.is_spherical()); @@ -275,10 +275,6 @@ TaskStatus KHARMA::AddPackage(std::shared_ptr& packages, Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) { - // See above. Only run if - //if () - FixParameters(pin); - Flag("ProcessPackages"); // Allocate the packages list as a shared pointer, to be updated in various tasks @@ -305,11 +301,14 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) auto t_reductions = tl.AddTask(t_none, KHARMA::AddPackage, packages, Reductions::Initialize, pin.get()); // B field solvers, to ensure divB ~= 0. - // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference + // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference: + // 1. Prefer B_CT if AMR since it's compatible + // 2. Prefer B_Flux_CT otherwise since it's well-tested auto t_b_field = t_none; - std::string b_field_solver = pin->GetOrAddString("b_field", "solver", "flux_ct"); + bool multilevel = pin->GetOrAddString("parthenon/mesh", "refinement", "none") != "none"; + std::string b_field_solver = pin->GetOrAddString("b_field", "solver", multilevel ? "face_ct" : "flux_ct"); if (b_field_solver == "none" || b_field_solver == "cleanup" || b_field_solver == "b_cleanup") { - // Don't add a B field + // Don't add a B field here } else if (b_field_solver == "constrained_transport" || b_field_solver == "face_ct") { t_b_field = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, B_CT::Initialize, pin.get()); } else if (b_field_solver == "constraint_damping" || b_field_solver == "cd") { @@ -374,8 +373,6 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); } - // TODO print full package list as soon as we know it, up here - #if DEBUG // Carry the ParameterInput with us, for generating outputs whenever we want packages->Get("Globals")->AllParams().Add("pin", pin.get()); diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index 3e30e7a2..323c533e 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -76,7 +76,7 @@ TaskStatus AddPackage(std::shared_ptr& packages, * This includes boundaries in spherical coordinates, coordinate system translations, etc. * This function also handles setting parameters from restart files */ -void FixParameters(std::unique_ptr& pin); +void FixParameters(ParameterInput *pin); /** * Load any packages specified in the input parameters diff --git a/kharma/main.cpp b/kharma/main.cpp index 0356a583..fa3d384b 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -41,6 +41,7 @@ #include "post_initialize.hpp" #include "problem.hpp" #include "emhd/conducting_atmosphere.hpp" +#include "version.hpp" // Parthenon headers #include @@ -122,10 +123,24 @@ int main(int argc, char *argv[]) pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x3] = KBoundaries::ApplyBoundaryTemplate; pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x3] = KBoundaries::ApplyBoundaryTemplate; - // Parthenon init includes Kokkos, MPI, parses parameters & cmdline, - // then calls ProcessPackages and ProcessProperties, then constructs the Mesh + if(MPIRank0()) { + // Always print the version header, because it's fun + // TODO(BSP) proper banner w/refs, names + const std::string &version = KHARMA::Version::GIT_VERSION; + const std::string &branch = KHARMA::Version::GIT_REFSPEC; + const std::string &sha1 = KHARMA::Version::GIT_SHA1; + std::cout << std::endl; + std::cout << "Starting KHARMA, version " << version << std::endl; + std::cout << "Branch " << branch << ", commit hash: " << sha1 << std::endl; + std::cout << std::endl; + std::cout << "KHARMA is released under the BSD 3-clause license." << std::endl; + std::cout << "Source code is available at https://github.com/AFD-Illinois/kharma/" << std::endl; + std::cout << std::endl; + } + + // Parthenon init includes Kokkos, MPI, parses parameters & cmdline Flag("ParthenonInit"); - auto manager_status = pman.ParthenonInit(argc, argv); + auto manager_status = pman.ParthenonInitEnv(argc, argv); if (manager_status == ParthenonStatus::complete) { pman.ParthenonFinalize(); return 0; @@ -134,6 +149,14 @@ int main(int argc, char *argv[]) pman.ParthenonFinalize(); return 1; } + auto pin = pman.pinput.get(); // All parameters in the input file or command line + // Modify input parameters as we need + KHARMA::FixParameters(pin); + // InitPackagesEtc calls ProcessPackages, then constructs the Mesh + pman.ParthenonInitPackagesAndMesh(); + // Now pull out the mesh and app_input as well for below + auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc + auto papp = pman.app_input.get(); // The list of callback functions specified above EndFlag(); #if DEBUG @@ -143,65 +166,53 @@ int main(int argc, char *argv[]) signal(SIGSEGV, print_backtrace); #endif - // Begin code block to ensure driver is cleaned up - { - auto pin = pman.pinput.get(); // All parameters in the input file or command line - auto pmesh = pman.pmesh.get(); // The mesh, with list of blocks & locations, size, etc - auto papp = pman.app_input.get(); // The list of callback functions specified above - - if(MPIRank0()) { - const int &verbose = pmesh->packages.Get("Globals")->Param("verbose"); - // Always print the version header, because it's fun - // TODO(someone) proper banner w/refs, names - const std::string &version = pmesh->packages.Get("Globals")->Param("version"); - const std::string &branch = pmesh->packages.Get("Globals")->Param("branch"); - const std::string &sha1 = pmesh->packages.Get("Globals")->Param("SHA1"); - std::cout << std::endl; - std::cout << "Starting KHARMA, version " << version << std::endl; - if (verbose > 0) std::cout << "Branch " << branch << ", commit hash: " << sha1 << std::endl; - std::cout << std::endl; - std::cout << "KHARMA is released under the BSD 3-clause license." << std::endl; - std::cout << "Source code for this program is available at https://github.com/AFD-Illinois/kharma/" << std::endl; - std::cout << std::endl; - - // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation - if (verbose > 0) { - // Print a list of variables as Parthenon used to (still does) - std::cout << "#Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; - - // Print a list of all loaded packages. Surprisingly useful for debugging init logic - std::cout << "Packages in use: " << std::endl; - for (auto package : pmesh->packages.AllPackages()) { - std::cout << package.first << std::endl; - } - std::cout << std::endl; - } - std::cout << "Running post-initialization tasks..." << std::endl; - } - - // PostInitialize: Add magnetic field to the problem, initialize ghost zones. - // Any init which may be run even when restarting, or requires all - // MeshBlocks to be initialized already - auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); - Flag("PostInitialize"); - KHARMA::PostInitialize(pin, pmesh, is_restart); - EndFlag(); + // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation + const int &verbose = pmesh->packages.Get("Globals")->Param("verbose"); + if(MPIRank0() && verbose > 0) { + // Print a list of variables as Parthenon used to (still does by default) + std::cout << "#Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; - std::string driver_type = pmesh->packages.Get("Driver")->Param("type"); - std::cerr << "Initializing and running " << driver_type << " driver" << std::endl; - // Construct a temporary driver purely for parameter parsing - KHARMADriver driver(pin, papp, pmesh); + // Print a list of all loaded packages. Surprisingly useful for debugging init logic + std::cout << "Packages in use: " << std::endl; + for (auto package : pmesh->packages.AllPackages()) { + std::cout << package.first << std::endl; + } + std::cout << std::endl; - // Write parameters to console if we should be wordy - if ((pmesh->packages.Get("Globals")->Param("verbose") > 0) && MPIRank0()) { + // Write all parameters etc. to console if we should be especially wordy + if ((verbose > 1) && MPIRank0()) { // This dumps the full Kokkos config, useful for double-checking // that the compile did what we wanted - ShowConfig(); + parthenon::ShowConfig(); pin->ParameterDump(std::cout); } - // Then execute the driver. This is a Parthenon function inherited by our HARMDriver object, + // This is for the next bit + std::cout << "Running post-initialization tasks..." << std::endl; + } + + // PostInitialize: Add magnetic field to the problem, initialize ghost zones. + // Any init which may be run even when restarting, or requires all + // MeshBlocks to be initialized already. + // TODO(BSP) split to package hooks + auto prob = pin->GetString("parthenon/job", "problem_id"); + bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); + Flag("PostInitialize"); + KHARMA::PostInitialize(pin, pmesh, is_restart); + EndFlag(); + + // Begin code block to ensure driver is cleaned up + { + std::string driver_type = pmesh->packages.Get("Driver")->Param("type"); + std::cout << "Initializing and running " << driver_type << " driver" << std::endl; + + // Pull out things we need to give the driver + auto pin = pman.pinput.get(); // All parameters in the input file or command line + + // We now have just one driver package, with different TaskLists for different modes + KHARMADriver driver(pin, papp, pmesh); + + // Then execute the driver. This is a Parthenon function inherited by our KHARMADriver object, // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion // of each step until a stop criterion is reached. Flag("driver.Execute"); diff --git a/kharma/prob/b_field_tools.hpp b/kharma/prob/b_field_tools.hpp index e09406bf..7e9f5902 100644 --- a/kharma/prob/b_field_tools.hpp +++ b/kharma/prob/b_field_tools.hpp @@ -74,18 +74,18 @@ inline BSeedType ParseBSeedType(std::string b_field_type) } } -/** - * Initializer for magnetic fields directly: value of a divergence-free configuration at a point - */ -KOKKOS_INLINE_FUNCTION double BSeed_A(BSeedType type, GReal Xembed[GR_DIM]) -{ +// /** +// * Initializer for magnetic fields directly: value of a divergence-free configuration at a point +// */ +// KOKKOS_INLINE_FUNCTION double BSeed_A(BSeedType type, GReal Xembed[GR_DIM]) +// { -} +// } -/** - * - */ -KOKKOS_INLINE_FUNCTION double BSeed_B(BSeedType type, GReal Xembed[GR_DIM]) -{ +// /** +// * +// */ +// KOKKOS_INLINE_FUNCTION double BSeed_B(BSeedType type, GReal Xembed[GR_DIM]) +// { -} \ No newline at end of file +// } \ No newline at end of file diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index a1c2adfb..57e0c802 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -218,7 +218,7 @@ TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pi // to get a new sequence for every block const int rng_seed = pin->GetOrAddInteger("perturbation", "rng_seed", 31337); // Print real seed used for all blocks, to ensure they're different - if (pmb->packages.Get("Globals")->Param("verbose") > 0) { + if (pmb->packages.Get("Globals")->Param("verbose") > 1) { std::cout << "Seeding RNG in block " << pmb->gid << " with value " << rng_seed + pmb->gid << std::endl; } const bool serial = pin->GetOrAddInteger("perturbation", "serial", false); diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 07add691..6b3747f5 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -58,7 +58,7 @@ hsize_t static_max(int i, int n) { return static_cast(m::max(i, n)); } hsize_t static_min(int i, int n) { return static_cast(m::min(i, n)); } -void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin) +void ReadIharmRestartHeader(std::string fname, ParameterInput *pin) { // Read the restart file and set parameters that need to be specified at early loading hdf5_open(fname.c_str()); diff --git a/kharma/prob/resize_restart.hpp b/kharma/prob/resize_restart.hpp index a62dc062..c934ef34 100644 --- a/kharma/prob/resize_restart.hpp +++ b/kharma/prob/resize_restart.hpp @@ -8,7 +8,7 @@ * Read the header of an iharm3d HDF5 restart file, and set appropriate parameters * Call this before mesh creation! */ -void ReadIharmRestartHeader(std::string fname, std::unique_ptr& pin); +void ReadIharmRestartHeader(std::string fname, ParameterInput *pin); /** * Read data from an iharm3d restart file. Does not support >1 meshblock in Parthenon diff --git a/kharma/prob/resize_restart_kharma.cpp b/kharma/prob/resize_restart_kharma.cpp index 9ccdcd8a..25d722e2 100644 --- a/kharma/prob/resize_restart_kharma.cpp +++ b/kharma/prob/resize_restart_kharma.cpp @@ -43,7 +43,7 @@ // Reads in KHARMA restart file but at a different simulation size -void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin) +void ReadKharmaRestartHeader(std::string fname, ParameterInput *pin) { bool use_dt = pin->GetOrAddBoolean("resize_restart", "use_dt", true); bool use_tf = pin->GetOrAddBoolean("resize_restart", "use_tf", false); diff --git a/kharma/prob/resize_restart_kharma.hpp b/kharma/prob/resize_restart_kharma.hpp index 18729681..a2be9f6b 100644 --- a/kharma/prob/resize_restart_kharma.hpp +++ b/kharma/prob/resize_restart_kharma.hpp @@ -13,7 +13,7 @@ * Read the header of an KHARMA HDF5 restart file, and set appropriate parameters * Call this before mesh creation! */ -void ReadKharmaRestartHeader(std::string fname, std::unique_ptr& pin); +void ReadKharmaRestartHeader(std::string fname, ParameterInput *pin); /** * Read data from an KHARMA restart file. Does not support >1 meshblock in Parthenon diff --git a/kharma/reductions/reductions_variables.hpp b/kharma/reductions/reductions_variables.hpp index 118ae5a9..0ebe3681 100644 --- a/kharma/reductions/reductions_variables.hpp +++ b/kharma/reductions/reductions_variables.hpp @@ -216,6 +216,7 @@ KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) #endif #endif } + return is_neg; } template <> KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) @@ -229,6 +230,7 @@ KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) #endif #endif } + return is_neg; } template <> KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) @@ -242,6 +244,7 @@ KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) #endif #endif } + return is_neg; } } diff --git a/kharma/types.hpp b/kharma/types.hpp index c8eafeab..8670a44e 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -35,9 +35,9 @@ #include "decs.hpp" -#include "boundary_types.hpp" +#include "boundaries/boundary_types.hpp" #include "kharma_package.hpp" -#include "reductions_types.hpp" +#include "reductions/reductions_types.hpp" #include diff --git a/pars/kelvin_helmholtz.par b/pars/kelvin_helmholtz.par index aa3a91ba..93ba263c 100644 --- a/pars/kelvin_helmholtz.par +++ b/pars/kelvin_helmholtz.par @@ -5,7 +5,7 @@ problem_id = kelvin_helmholtz -refinement = adaptive +refinement = static numlevel = 3 nx1 = 128 @@ -31,11 +31,29 @@ nx1 = 64 nx2 = 64 nx3 = 1 - -method = derivative_order_1 -field = prims.rho -refine_tol = 0.01 -derefine_tol = 0.001 +# +#method = derivative_order_1 +#field = prims.rho +#refine_tol = 0.01 +#derefine_tol = 0.001 + + +x1min = 0.4 +x1max = 0.6 +x2min = 0.9 +x2max = 1.1 +x3min = 0.0 +x3max = 0.0 +level = 1 + + +x1min = 0.2 +x1max = 0.3 +x2min = 0.0 +x2max = 0.1 +x3min = 0.0 +x3max = 0.0 +level = 2 base = cartesian_minkowski diff --git a/pars/sane2d_refined.par b/pars/sane2d_refined.par new file mode 100644 index 00000000..e83013b0 --- /dev/null +++ b/pars/sane2d_refined.par @@ -0,0 +1,92 @@ +# SANE model mirroring the simulation library +# Overall simulation size 50M, to allow +# running at small scale on e.g. a laptop +# Uses MKS coordinates, not Funky variant + + +problem_id = torus + + +refinement = static +numlevel = 3 +nx1 = 128 +nx2 = 128 +nx3 = 1 + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +x1min = 1.0 +x1max = 3.0 +x2min = 0.45 +x2max = 0.55 +level = 1 + + +base = spherical_ks +transform = eks +r_out = 50 +a = 0.9375 + + +tlim = 3000.0 +nlim = -1 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +type = sane +beta_min = 100. + + + +rho_min_geom = 1e-5 +u_min_geom = 1e-7 +ktot_max = 1500 +u_over_rho_max = 100 +bsq_over_rho_max = 100 + + +on = false +howes = true +kawazura = true +werner = true +rowan = true +sharma = true + + +on = false + + +file_type = hdf5 +dt = 10.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B + +# Can't until face field output is enabled +# +#file_type = rst +#dt = 100.0 +#ghost_zones = true + + +file_type = hst +dt = 0.1 diff --git a/run.sh b/run.sh index fa15df5d..b94d5798 100755 --- a/run.sh +++ b/run.sh @@ -54,6 +54,10 @@ do source $machine done +if [[ "$1" == "trace" ]]; then + export KOKKOS_TOOLS_LIBS=$KHARMA_DIR/../kokkos-tools/kp_kernel_logger.so + shift +fi # Override MPI_NUM_PROCS at user option "-n" # and OMP_NUM_THREADS at option "-nt" if [[ "$1" == "-n" ]]; then From 0a93b0ffa4cd161274d729108a1c9e4ae4d4c1bb Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 29 Aug 2023 14:57:04 -0600 Subject: [PATCH 114/219] Working Rename flags back as Parthenon doesn't treat Metadata::Conserved as special so it can mean the fluid version only. Overhaul B field init to use the same functions for cell- and face-centered fields, just changing how A->B happens. Fix for face CT in spherical systems: set \Delta B2 = 0 on the zero-size faces at poles explicitly. --- .gitignore | 1 + kharma/b_cd/b_cd.cpp | 8 +- kharma/b_cd/b_cd.hpp | 13 +- kharma/b_cd/seed_B_cd.cpp | 171 ------------ kharma/b_cleanup/b_cleanup.cpp | 6 +- kharma/b_ct/b_ct.cpp | 71 +++-- kharma/b_ct/b_ct.hpp | 189 +++---------- kharma/b_ct/seed_B_ct.cpp | 62 ----- kharma/b_flux_ct/b_flux_ct.cpp | 6 +- kharma/b_flux_ct/b_flux_ct.hpp | 9 +- kharma/b_flux_ct/seed_B_flux_ct.cpp | 296 --------------------- kharma/boundaries/boundaries.cpp | 18 +- kharma/driver/imex_step.cpp | 9 +- kharma/driver/kharma_driver.cpp | 56 ++-- kharma/driver/kharma_driver.hpp | 9 +- kharma/driver/kharma_step.cpp | 36 ++- kharma/driver/simple_step.cpp | 7 +- kharma/electrons/electrons.cpp | 20 +- kharma/emhd/emhd.cpp | 16 +- kharma/emhd/emhd_limits.hpp | 4 +- kharma/floors/floors.cpp | 8 +- kharma/flux/flux.cpp | 16 +- kharma/flux/get_flux.hpp | 4 +- kharma/grmhd/grmhd.cpp | 36 ++- kharma/grmhd/pack.hpp | 16 +- kharma/implicit/fix_solve.cpp | 6 +- kharma/implicit/implicit.cpp | 6 +- kharma/kharma_package.cpp | 2 +- kharma/prob/b_field_tools.hpp | 91 ------- kharma/prob/emhd/conducting_atmosphere.cpp | 2 +- kharma/prob/fm_torus.cpp | 5 - kharma/prob/fm_torus.hpp | 8 +- kharma/prob/kelvin_helmholtz.hpp | 2 +- kharma/prob/post_initialize.cpp | 139 +--------- kharma/prob/problem.cpp | 2 +- kharma/prob/seed_B.cpp | 194 ++++++++++++++ kharma/prob/seed_B.hpp | 141 ++++++++++ kharma/prob/seed_B_impl.hpp | 295 ++++++++++++++++++++ kharma/reductions/reductions.cpp | 31 ++- kharma/reductions/reductions_impl.hpp | 33 ++- kharma/reductions/reductions_types.hpp | 31 +-- kharma/wind/wind.cpp | 2 +- machines/bp.sh | 5 +- make.sh | 10 +- pars/kelvin_helmholtz.par | 8 +- pars/orszag_tang.par | 8 +- pars/sane2d_refined.par | 42 ++- pars/sane3d_refined.par | 85 ++++++ 48 files changed, 1059 insertions(+), 1176 deletions(-) delete mode 100644 kharma/b_cd/seed_B_cd.cpp delete mode 100644 kharma/b_ct/seed_B_ct.cpp delete mode 100644 kharma/b_flux_ct/seed_B_flux_ct.cpp delete mode 100644 kharma/prob/b_field_tools.hpp create mode 100644 kharma/prob/seed_B.cpp create mode 100644 kharma/prob/seed_B.hpp create mode 100644 kharma/prob/seed_B_impl.hpp create mode 100644 pars/sane3d_refined.par diff --git a/.gitignore b/.gitignore index 80ec56b0..59b06881 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ convergence.txt core.* frames_*/ logs/ +*.log # KHARMA/Parthenon outputs *.phdf diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index cced6a80..e008c1af 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -62,21 +62,21 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, Metadata::GetUserFlag("GRPrimitive"), Metadata::Vector}, s_vector); + Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::Vector}, s_vector); pkg->AddField("prims.B", m); // Constraint damping scalar field psi. Prim and cons forms correspond to B field forms, // i.e. differ by a factor of gdet. This is apparently marginally more stable in some // circumstances. m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::FillGhost, - Metadata::Restart, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, Metadata::WithFluxes}); + Metadata::Restart, Metadata::Conserved, Metadata::Conserved, Metadata::WithFluxes}); pkg->AddField("cons.psi_cd", m); m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, - Metadata::Restart, Metadata::GetUserFlag("GRPrimitive")}); + Metadata::Restart, Metadata::GetUserFlag("Primitive")}); pkg->AddField("prims.psi_cd", m); // We only update the divB field for output diff --git a/kharma/b_cd/b_cd.hpp b/kharma/b_cd/b_cd.hpp index 1bc70216..42014db0 100644 --- a/kharma/b_cd/b_cd.hpp +++ b/kharma/b_cd/b_cd.hpp @@ -47,7 +47,7 @@ using namespace parthenon; * * This requires only the values at cell centers, and preserves a cell-centered divergence representation * - * This implementation includes conversion from "GRPrimitive" to "conserved" B and back, + * This implementation includes conversion from "primitive" to "conserved" B and back, * i.e. between field strength and flux via multiplying by gdet. */ namespace B_CD { @@ -56,17 +56,6 @@ namespace B_CD { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); -/** - * Seed an axisymmetric initialization with magnetic field proportional to fluid density, - * or density and radius, to create a SANE or MAD flow - * Note this function expects a normalized P for which rho_max==1 - * - * @param rin is the interior radius of the torus - * @param min_rho_q is the minimum density at which there will be magnetic vector potential - * @param b_field_type is one of "sane" "ryan" "r3s3" or "gaussian", described below (TODO test or remove opts) - */ -TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); - /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". * Also applies floors to the calculated primitives, and fixes up any inversion errors. diff --git a/kharma/b_cd/seed_B_cd.cpp b/kharma/b_cd/seed_B_cd.cpp deleted file mode 100644 index 88fcaa7e..00000000 --- a/kharma/b_cd/seed_B_cd.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * File: seed_B_cd.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Seed a torus of some type with a magnetic field according to its density - -#include "b_cd.hpp" - -#include "b_field_tools.hpp" - -#include "grmhd_functions.hpp" - -using namespace parthenon; - -TaskStatus B_CD::SeedBField(MeshBlockData *rc, ParameterInput *pin) -{ - auto pmb = rc->GetBlockPointer(); - IndexDomain domain = IndexDomain::entire; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); - int n2 = pmb->cellbounds.ncellsj(IndexDomain::entire); - - const auto& G = pmb->coords; - GridScalar rho = rc->Get("prims.rho").data; - GridVector B_P = rc->Get("prims.B").data; - GridVector B_U = rc->Get("cons.B").data; - - Real min_rho_q = pin->GetOrAddReal("b_field", "min_rho_q", 0.2); - std::string b_field_type = pin->GetString("b_field", "type"); - - // Translate to an enum so we can avoid string comp inside, - // as well as for good errors, many->one maps, etc. - BSeedType b_field_flag = ParseBSeedType(b_field_type); - - // Require and load what we need if necessary - Real rin, b10, b20, b30; - switch (b_field_flag) - { - case BSeedType::constant: - b10 = pin->GetOrAddReal("b_field", "b10", 0.); - b20 = pin->GetOrAddReal("b_field", "b20", 0.); - b30 = pin->GetOrAddReal("b_field", "b30", 0.); - break; - case BSeedType::monopole: - b10 = pin->GetReal("b_field", "b10"); - break; - case BSeedType::sane: - break; - case BSeedType::ryan: - case BSeedType::r3s3: - case BSeedType::gaussian: - rin = pin->GetReal("torus", "rin"); - break; - default: - break; - } - - // Shortcut to field values for easy fields - if (b_field_flag == BSeedType::constant) { - pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Set B1 directly - B_P(0, k, j, i) = b10; - B_P(1, k, j, i) = b20; - B_P(2, k, j, i) = b30; - } - ); - return TaskStatus::complete; - } else if (b_field_flag == BSeedType::monopole) { - pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Set B1 directly by normalizing - B_P(0, k, j, i) = b10 / G.gdet(Loci::center, j, i); - B_P(1, k, j, i) = 0.; - B_P(2, k, j, i) = 0.; - } - ); - return TaskStatus::complete; - } - - // Find the cell-centered magnetic vector potential. In X3 symmetry only A_phi is non-zero, so we keep track of that. - ParArrayND A3("A", n2, n1); - // TODO figure out double vs Real here - pmb->par_for("B_field_A", js+1, je, is+1, ie, - KOKKOS_LAMBDA (const int& j, const int& i) { - GReal Xembed[GR_DIM]; - G.coord_embed(0, j, i, Loci::center, Xembed); - GReal r = Xembed[1], th = Xembed[2]; - - // Use rho at cell centers - Real rho_av = rho(ks, j, i); - - Real q; - switch (b_field_flag) - { - case BSeedType::sane: - q = rho_av - min_rho_q; - break; - case BSeedType::ryan: - // BR's smoothed poloidal in-torus - q = m::pow(m::sin(th), 3) * m::pow(r / rin, 3) * m::exp(-r / 400) * rho_av - min_rho_q; - break; - case BSeedType::r3s3: - // Just the r^3 sin^3 th term, proposed EHT standard MAD - // TODO split r3 here and r3s3 - q = m::pow(r / rin, 3) * rho_av - min_rho_q; - break; - case BSeedType::gaussian: - // Pure vertical threaded field of gaussian strength with FWHM 2*rin (i.e. HM@rin) - // centered at BH center - // Block is to avoid compiler whinging about initialization - { - Real x = (r / rin) * sin(th); - Real sigma = 2 / m::sqrt(2 * log(2)); - Real u = x / m::abs(sigma); - q = (1 / (m::sqrt(2 * M_PI) * m::abs(sigma))) * m::exp(-u * u / 2); - } - break; - default: - // This shouldn't be reached. Could squawk here? - break; - } - - A3(j, i) = m::max(q, 0.); - } - ); - - // Calculate B-field - pmb->par_for("B_field_B", ks, ke, js+1, je-1, is+1, ie-1, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Take the curl - B_P(0, k, j, i) = (A3(j + 1, i) - A3(j-1, i)) / (2 * G.Dxc<2>(j) * G.gdet(Loci::center, j, i)); - B_P(1, k, j, i) = -(A3(j, i + 1) - A3(j, i-1)) / (2 * G.Dxc<1>(i) * G.gdet(Loci::center, j, i)); - B_P(2, k, j, i) = 0.; - } - ); - - return TaskStatus::complete; -} diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index cdbde016..831b920b 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -146,10 +146,10 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s MetadataFlag areWeImplicit = (implicit_b) ? Metadata::GetUserFlag("Implicit") : Metadata::GetUserFlag("Explicit"); - // Flags for B fields. "GRPrimitive" form is field, "conserved" is flux - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), + // Flags for B fields. "primitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index bd79bc7e..eafb3aec 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -77,14 +77,12 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // FIELDS - // TODO maybe one day implicit? - // Flags for B fields on faces. - // We don't mark these as "GRPrimitive" and "GRConserved" else they'd be bundled + // We don't mark these as "Primitive" and "Conserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::GetUserFlag("Explicit")}; - std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, Metadata::Conserved, + std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; // TODO TODO Restart auto m = Metadata(flags_prim_f); pkg->AddField("prims.fB", m); @@ -94,9 +92,9 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // Cell-centered versions. Needed for BS, not for other schemes. // Probably will want to keep primitives for e.g. correct PtoU of MHD vars, but cons maybe can go - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRConserved"), Metadata::WithFluxes, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::Conserved, Metadata::WithFluxes, Metadata::GetUserFlag("MHD"), Metadata::GetUserFlag("Explicit"), Metadata::Vector}; std::vector s_vector({NVEC}); m = Metadata(flags_prim, s_vector); @@ -108,7 +106,6 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // TODO only sync when needed std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}; m = Metadata(flags_emf); - m.RegisterRefinementOps(); pkg->AddField("B_CT.emf", m); if (ct_scheme == "sg09") { @@ -173,6 +170,8 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto B_P = rc->PackVariables(std::vector{"prims.B"}); const auto& G = pmb->coords; + // TODO get rid of prims on faces probably + // Update the primitive B-fields on faces const IndexRange3 bf = KDomain::GetRange(rc, domain, 0, 1, coarse); pmb->par_for("UtoP_B", bf.ks, bf.ke, bf.js, bf.je, bf.is, bf.ie, @@ -206,9 +205,6 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; - //md->GetMeshPointer()->mesh_data.Add("emf", md, std::vector{"B_CT.emf"}); - //KHARMADriver::Copy(); - // EMF temporary auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); @@ -228,14 +224,14 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // TODO will we need gdet/cell length here? const auto& G = B_U.GetCoords(bl); if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = + emf_pack(bl, E1, 0, k, j, i) = G.Dxc<1>(i) * 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)); - emf_pack(bl, E2, 0, k, j, i) = + emf_pack(bl, E2, 0, k, j, i) = G.Dxc<2>(j) * 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)); } - emf_pack(bl, E3, 0, k, j, i) = + emf_pack(bl, E3, 0, k, j, i) = G.Dxc<3>(k) * 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)); } @@ -270,14 +266,14 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // ...then zone number... // and finally, a boolean indicating a leftward (e.g., i-3/4) vs rightward (i-1/4) position if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = + emf_pack(bl, E1, 0, k, j, i) = G.Dxc<1>(i) * 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)) + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); - emf_pack(bl, E2, 0, k, j, i) = + emf_pack(bl, E2, 0, k, j, i) = G.Dxc<2>(j) * 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)) + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) @@ -285,7 +281,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); } - emf_pack(bl, E3, 0, k, j, i) = + emf_pack(bl, E3, 0, k, j, i) = G.Dxc<3>(k) * 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)) + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) @@ -318,22 +314,21 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) // This is what we're replacing auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); // Circulation -> change in flux at face - // Note we *replace* whatever this term in the source term was "supposed" to be pmb0->par_for("B_CT_Circ_1", block.s, block.e, b.ks, b.ke, b.js, b.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F1, 0, k, j, i) = emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i); + dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); if (ndim > 2) { - dB_Uf_dt(bl, F1, 0, k, j, i) += -emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i); + dB_Uf_dt(bl, F1, 0, k, j, i) += (-emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j); } } ); pmb0->par_for("B_CT_Circ_2", block.s, block.e, b.ks, b.ke, b1.js, b1.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F2, 0, k, j, i) = -emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i); + dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); if (ndim > 2) { - dB_Uf_dt(bl, F2, 0, k, j, i) += emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i); + dB_Uf_dt(bl, F2, 0, k, j, i) += (emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); } } ); @@ -341,11 +336,41 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F3, 0, k, j, i) += emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i) - - emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i); + dB_Uf_dt(bl, F3, 0, k, j, i) += (emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j) + - (emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); } ); } + + // Explicitly zero polar faces + // In spherical, zero B2 on X2 face regardless of boundary condition + // This shouldn't interfere with divB since the face size is zero anyway + if (mdudt->GetBlockData(0)->GetBlockPointer()->coords.coords.is_spherical()) { + const IndexRange ib = mdudt->GetBoundsI(IndexDomain::entire); + const IndexRange kb = mdudt->GetBoundsK(IndexDomain::entire); + const int js = mdudt->GetBoundsJ(IndexDomain::interior).s; + const int je = mdudt->GetBoundsJ(IndexDomain::interior).e + 1; // Face + for (int i_block = 0; i_block < mdudt->NumBlocks(); i_block++) { + auto &rc = mdudt->GetBlockData(i_block); + auto pmb = rc->GetBlockPointer(); + auto& dB_Uf_dt_block = rc->PackVariables(std::vector{"cons.fB"}); + if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::inner_x2)) { + pmb->par_for("B_CT_zero_B2_in", kb.s, kb.e, js, js, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + dB_Uf_dt_block(F2, 0, k, j, i) = 0; + } + ); + } + if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::outer_x2)) { + pmb->par_for("B_CT_zero_B2_out", kb.s, kb.e, je, je, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + dB_Uf_dt_block(F2, 0, k, j, i) = 0; + } + ); + } + } + } + return TaskStatus::complete; } diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index 7e056b34..6e4ec96f 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -55,13 +55,6 @@ namespace B_CT { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); -/** - * Seed a divergence-free magnetic field of user's choice, optionally - * proportional to existing fluid density. - * Updates primitive and conserved variables. - */ -TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); - /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". * Also applies floors to the calculated primitives, and fixes up any inversion errors @@ -74,12 +67,6 @@ TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); void BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); -/** - * Reverse of the above. Only used by itself during initialization. - * Generally, use Flux::BlockPtoU or Flux::BlockPtoUExceptMHD. - */ -void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); - /** * Calculate the EMF around edges of faces caused by the flux of B field * through each face. @@ -146,34 +133,45 @@ KOKKOS_INLINE_FUNCTION Real face_div(const GRCoordinates &G, Global &v, const in return du / G.CellVolume(k, j, i); } -// KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, -// const int& k, const int& j, const int& i) -// { -// B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j);// A3,2 derivative -// B_U(F2, 0, k, j, i) =-(A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative -// B_U(F3, 0, k, j, i) = 0.; -// } - KOKKOS_INLINE_FUNCTION void curl_3D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, const int& k, const int& j, const int& i) { - // "CT" to faces from a cell-centered potential - - B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j) // A3,2 derivative - - (A(V2, k + 1, j, i) - A(V2, k, j, i)) / G.Dxc<3>(k);// A2,3 derivative - - B_U(F2, 0, k, j, i) = (A(V1, k + 1, j, i) - A(V1, k, j, i)) / G.Dxc<3>(k) // A1,3 derivative - - (A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative - - B_U(F3, 0, k, j, i) = (A(V2, k, j, i + 1) - A(V2, k, j, i)) / G.Dxc<1>(i) // A2,1 derivative - - (A(V1, k, j + 1, i) - A(V1, k, j, i)) / G.Dxc<2>(j);// A1,2 derivative + // Take a face-ct step from the corner potentials. + // This needs to be 3D because post-tilt A may not point in the phi direction only + + // A3,2 derivative + const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 2; + const Real A3c2b = (A(V3, k, j, i) + A(V3, k + 1, j, i)) / 2; + // A2,3 derivative + const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 2; + const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j + 1, i)) / 2; + B_U(F1, 0, k, j, i) = (A3c2f - A3c2b) - (A2c3f - A2c3b); + + // A1,3 derivative + const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 2; + const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1)) / 2; + // A3,1 derivative + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1)) / 2; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i)) / 2; + B_U(F2, 0, k, j, i) = (A1c3f - A1c3b) - (A3c1f - A3c1b); + + // A2,1 derivative + const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1)) / 2; + const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i)) / 2; + // A1,2 derivative + const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 2; + const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1)) / 2; + B_U(F3, 0, k, j, i) = (A2c1f - A2c1b) - (A1c2f - A1c2b); } KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, - const int& k, const int& j, const int& i) + const int& k, const int& j, const int& i) { - B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j);// A3,2 derivative - B_U(F2, 0, k, j, i) =-(A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i);// A3,1 derivative + // TODO why do these not need + // A3,2 derivative + B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)); + // A3,1 derivative + B_U(F2, 0, k, j, i) = - (A(V3, k, j, i + 1) - A(V3, k, j, i)); B_U(F3, 0, k, j, i) = 0.; } @@ -339,127 +337,4 @@ struct ProlongateInternalOlivares { } }; -struct RestrictNearest { - static constexpr bool OperationRequired(TopologicalElement fel, - TopologicalElement cel) { - return fel == cel && (fel == E1 || fel == E2 || fel == E3); - } - - template - KOKKOS_FORCEINLINE_FUNCTION static void - Do(const int l, const int m, const int n, const int ck, const int cj, const int ci, - const IndexRange &ckb, const IndexRange &cjb, const IndexRange &cib, - const IndexRange &kb, const IndexRange &jb, const IndexRange &ib, - const Coordinates_t &coords, const Coordinates_t &coarse_coords, - const ParArrayND *pcoarse, - const ParArrayND *pfine) { - - auto &coarse = *pcoarse; - auto &fine = *pfine; - - constexpr int element_idx = static_cast(el) % 3; - const int i = (DIM > 0) ? (ci - cib.s) * 2 + ib.s : ib.s; - const int j = (DIM > 1) ? (cj - cjb.s) * 2 + jb.s : jb.s; - const int k = (DIM > 2) ? (ck - ckb.s) * 2 + kb.s : kb.s; - - coarse(element_idx, l, m, n, ck, cj, ci) = 0.5*fine(element_idx, l, m, n, k, j, i); - } -}; - -struct ProlongateSharedMinMod2 { - static constexpr bool OperationRequired(TopologicalElement fel, - TopologicalElement cel) { - return fel == cel && (fel == E1 || fel == E2 || fel == E3); - } - - template - KOKKOS_FORCEINLINE_FUNCTION static void - Do(const int l, const int m, const int n, const int k, const int j, const int i, - const IndexRange &ckb, const IndexRange &cjb, const IndexRange &cib, - const IndexRange &kb, const IndexRange &jb, const IndexRange &ib, - const Coordinates_t &coords, const Coordinates_t &coarse_coords, - const ParArrayND *pcoarse, - const ParArrayND *pfine) { - using namespace parthenon::refinement_ops::util; - auto &coarse = *pcoarse; - auto &fine = *pfine; - - constexpr int element_idx = static_cast(el) % 3; - - const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; - const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; - const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; - - constexpr bool INCLUDE_X1 = - (DIM > 0) && (el == TE::CC || el == TE::F2 || el == TE::F3 || el == TE::E1); - constexpr bool INCLUDE_X2 = - (DIM > 1) && (el == TE::CC || el == TE::F3 || el == TE::F1 || el == TE::E2); - constexpr bool INCLUDE_X3 = - (DIM > 2) && (el == TE::CC || el == TE::F1 || el == TE::F2 || el == TE::E3); - - const Real fc = coarse(element_idx, l, m, n, k, j, i); - - Real dx1fm = 0; - [[maybe_unused]] Real dx1fp = 0; - Real gx1c = 0; - if constexpr (INCLUDE_X1) { - Real dx1m, dx1p; - GetGridSpacings<1, el>(coords, coarse_coords, cib, ib, i, fi, &dx1m, &dx1p, &dx1fm, - &dx1fp); - gx1c = GradMinMod(fc, coarse(element_idx, l, m, n, k, j, i - 1), - coarse(element_idx, l, m, n, k, j, i + 1), dx1m, dx1p); - } - - Real dx2fm = 0; - [[maybe_unused]] Real dx2fp = 0; - Real gx2c = 0; - if constexpr (INCLUDE_X2) { - Real dx2m, dx2p; - GetGridSpacings<2, el>(coords, coarse_coords, cjb, jb, j, fj, &dx2m, &dx2p, &dx2fm, - &dx2fp); - gx2c = GradMinMod(fc, coarse(element_idx, l, m, n, k, j - 1, i), - coarse(element_idx, l, m, n, k, j + 1, i), dx2m, dx2p); - } - - Real dx3fm = 0; - [[maybe_unused]] Real dx3fp = 0; - Real gx3c = 0; - if constexpr (INCLUDE_X3) { - Real dx3m, dx3p; - GetGridSpacings<3, el>(coords, coarse_coords, ckb, kb, k, fk, &dx3m, &dx3p, &dx3fm, - &dx3fp); - gx3c = GradMinMod(fc, coarse(element_idx, l, m, n, k - 1, j, i), - coarse(element_idx, l, m, n, k + 1, j, i), dx3m, dx3p); - } - - // KGF: add the off-centered quantities first to preserve FP symmetry - // JMM: Extraneous quantities are zero - fine(element_idx, l, m, n, fk, fj, fi) = - (fc - (gx1c * dx1fm + gx2c * dx2fm + gx3c * dx3fm))*2; - if constexpr (INCLUDE_X1) - fine(element_idx, l, m, n, fk, fj, fi + 1) = - (fc + (gx1c * dx1fp - gx2c * dx2fm - gx3c * dx3fm))*2; - if constexpr (INCLUDE_X2) - fine(element_idx, l, m, n, fk, fj + 1, fi) = - (fc - (gx1c * dx1fm - gx2c * dx2fp + gx3c * dx3fm))*2; - if constexpr (INCLUDE_X2 && INCLUDE_X1) - fine(element_idx, l, m, n, fk, fj + 1, fi + 1) = - (fc + (gx1c * dx1fp + gx2c * dx2fp - gx3c * dx3fm))*2; - if constexpr (INCLUDE_X3) - fine(element_idx, l, m, n, fk + 1, fj, fi) = - (fc - (gx1c * dx1fm + gx2c * dx2fm - gx3c * dx3fp))*2; - if constexpr (INCLUDE_X3 && INCLUDE_X1) - fine(element_idx, l, m, n, fk + 1, fj, fi + 1) = - (fc + (gx1c * dx1fp - gx2c * dx2fm + gx3c * dx3fp))*2; - if constexpr (INCLUDE_X3 && INCLUDE_X2) - fine(element_idx, l, m, n, fk + 1, fj + 1, fi) = - (fc - (gx1c * dx1fm - gx2c * dx2fp - gx3c * dx3fp))*2; - if constexpr (INCLUDE_X3 && INCLUDE_X2 && INCLUDE_X1) - fine(element_idx, l, m, n, fk + 1, fj + 1, fi + 1) = - (fc + (gx1c * dx1fp + gx2c * dx2fp + gx3c * dx3fp))*2; - } -}; - } diff --git a/kharma/b_ct/seed_B_ct.cpp b/kharma/b_ct/seed_B_ct.cpp deleted file mode 100644 index c79f0bd2..00000000 --- a/kharma/b_ct/seed_B_ct.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * File: seed_B_ct.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Seed a torus of some type with a magnetic field according to its density - -#include "b_ct.hpp" - -#include "b_field_tools.hpp" -#include "coordinate_utils.hpp" -#include "fm_torus.hpp" -#include "grmhd_functions.hpp" - -using namespace parthenon; - -TaskStatus B_CT::SeedBField(MeshBlockData *rc, ParameterInput *pin) -{ - auto pmb = rc->GetBlockPointer(); - - const auto& G = pmb->coords; - GridScalar rho = rc->Get("prims.rho").data; - GridVector B_P = rc->Get("prims.B").data; - GridVector B_U = rc->Get("cons.B").data; - - // Orszag-Tang Vortex - - - // Finally, make sure we initialize the primitive field too - B_CT::BlockUtoP(rc, IndexDomain::entire, false); - - return TaskStatus::complete; -} diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 2a958427..1d17ce7c 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -102,10 +102,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), + // Flags for B fields. "primitive" form is field, "conserved" is flux + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 1a6560f2..7080b269 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -46,7 +46,7 @@ * * This requires only the values at cell centers * - * This implementation includes conversion from "GRPrimitive" to "conserved" B and back + * This implementation includes conversion from "primitive" to "conserved" B and back */ namespace B_FluxCT { /** @@ -54,13 +54,6 @@ namespace B_FluxCT { */ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); -/** - * Seed a divergence-free magnetic field of user's choice, optionally - * proportional to existing fluid density. - * Updates primitive and conserved variables. - */ -TaskStatus SeedBField(MeshBlockData *rc, ParameterInput *pin); - /** * Get the primitive variables, which in Parthenon's nomenclature are "derived". * Also applies floors to the calculated primitives, and fixes up any inversion errors diff --git a/kharma/b_flux_ct/seed_B_flux_ct.cpp b/kharma/b_flux_ct/seed_B_flux_ct.cpp deleted file mode 100644 index baeb67aa..00000000 --- a/kharma/b_flux_ct/seed_B_flux_ct.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * File: seed_B_flux_ct.cpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Seed a torus of some type with a magnetic field according to its density - -#include "b_flux_ct.hpp" - -#include "b_field_tools.hpp" -#include "boundaries.hpp" -#include "coordinate_utils.hpp" -#include "fm_torus.hpp" -#include "grmhd_functions.hpp" - -using namespace parthenon; - -TaskStatus B_FluxCT::SeedBField(MeshBlockData *rc, ParameterInput *pin) -{ - auto pmb = rc->GetBlockPointer(); - - const auto& G = pmb->coords; - GridScalar rho = rc->Get("prims.rho").data; - GridVector B_P = rc->Get("prims.B").data; - GridVector B_U = rc->Get("cons.B").data; - Real fx1min, fx1max, dx1, fx1min_ghost; - auto fname_fill = pin->GetOrAddString("resize_restart", "fname_fill", "none"); - const bool should_fill = !(fname_fill == "none"); - - Real min_rho_q = pin->GetOrAddReal("b_field", "min_rho_q", 0.2); - std::string b_field_type = pin->GetString("b_field", "type"); - // Translate the type to an enum so we can avoid string comp inside, - // as well as for good errors, many->one maps, etc. - BSeedType b_field_flag = ParseBSeedType(b_field_type); - - std::cout << "Seeding B field with type " << b_field_type << std::endl; - - // Other parameters we need - auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_torus = (prob == "torus"); - - // Require and load what we need if necessary - Real a, rin, rmax, gam, kappa, rho_norm; - Real tilt = 0; // Needs to be initialized - Real bz = 0; - switch (b_field_flag) - { - case BSeedType::sane: - case BSeedType::ryan: - case BSeedType::ryan_quadrupole: - case BSeedType::r3s3: - case BSeedType::steep: - case BSeedType::gaussian: - if (!is_torus) - throw std::invalid_argument("Magnetic field seed "+b_field_type+" supports only torus problems!"); - // Torus parameters - rin = pin->GetReal("torus", "rin"); - rmax = pin->GetReal("torus", "rmax"); - kappa = pin->GetReal("torus", "kappa"); - tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; - // Other things we need only for torus evaluation - gam = pmb->packages.Get("GRMHD")->Param("gamma"); - rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); - a = G.coords.get_a(); - break; - case BSeedType::bz_monopole: - break; - case BSeedType::vertical: - bz = pin->GetOrAddReal("b_field", "bz", 0.); - break; - default: - break; - } - - IndexDomain domain = IndexDomain::entire; - int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); - int n2 = pmb->cellbounds.ncellsj(IndexDomain::entire); - int n3 = pmb->cellbounds.ncellsk(IndexDomain::entire); - int ndim = pmb->pmy_mesh->ndim; - - // Shortcut to field values for easy fields - bool early_field = false; - if (b_field_flag == BSeedType::constant) { - const Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); - const Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); - const Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); - pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Set B1 directly - B_P(V1, k, j, i) = b10; - B_P(V2, k, j, i) = b20; - B_P(V3, k, j, i) = b30; - } - ); - early_field = true; - } - if (b_field_flag == BSeedType::monopole) { - const Real b10 = pin->GetReal("b_field", "b10"); // required - pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Set B1 directly by normalizing - B_P(V1, k, j, i) = b10 / G.gdet(Loci::center, j, i); - B_P(V2, k, j, i) = 0.; - B_P(V3, k, j, i) = 0.; - } - ); - early_field = true; - } - if (b_field_flag == BSeedType::monopole_cube) { - pmb->par_for("B_field_B", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // This ignores rin_bondi to keep divB consistent - // B \prop r^-3 - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, Loci::center, Xembed); - B_P(V1, k, j, i) = 1/(Xembed[1]*Xembed[1]*Xembed[1]); - B_P(V2, k, j, i) = 0.; - B_P(V3, k, j, i) = 0.; - } - ); - early_field = true; - } - // We still need to update conserved flux values, but then we're done - if (early_field) { - B_FluxCT::BlockPtoU(rc, IndexDomain::entire, false); - KBoundaries::FreezeDirichletBlock(rc); - return TaskStatus::complete; - } - - // For all other fields... - // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, - // But for tilted conditions we must keep track of all components - ParArrayND A("A", NVEC, n3+1, n2+1, n1+1); - pmb->par_for("B_field_A", ks, ke+1, js, je+1, is, ie+1, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - GReal Xnative[GR_DIM]; - GReal Xembed[GR_DIM], Xmidplane[GR_DIM]; - G.coord(k, j, i, Loci::corner, Xnative); - G.coord_embed(k, j, i, Loci::corner, Xembed); - // What are our corresponding "midplane" values for evaluating the function? - rotate_polar(Xembed, tilt, Xmidplane); - const GReal r = Xmidplane[1], th = Xmidplane[2]; - - // This is written under the assumption re-computed rho is more accurate than a bunch - // of averaging in a meaningful way. Just use the average if not. - Real rho_av; - if (is_torus) { - // Find rho (later u?) at corner directly for torii - rho_av = fm_torus_rho(a, rin, rmax, gam, kappa, r, th) / rho_norm; - } else { - // Use averages for anything else - // This loop runs over every corner. Centers do not exist before the first - // or after the last, so use the last (ghost) zones available. - const int ii = clip(i, is+1, ie); - const int jj = clip(j, js+1, je); - const int kk = clip(k, ks+1, ke); - if (ndim > 2) { - rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + - rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1) + - rho(kk - 1, jj, ii) + rho(kk - 1, jj, ii - 1) + - rho(kk - 1, jj - 1, ii) + rho(kk - 1, jj - 1, ii - 1)) / 8; - } else { - rho_av = (rho(ks, jj, ii) + rho(ks, jj, ii - 1) + - rho(ks, jj - 1, ii) + rho(ks, jj - 1, ii - 1)) / 4; - } - } - - Real q; - switch (b_field_flag) - { - case BSeedType::sane: - q = m::max(rho_av - min_rho_q, 0.); - break; - case BSeedType::bz_monopole: - // used in testing to exactly agree with harmpi - q = 1. - m::cos(th); - break; - case BSeedType::ryan: - // BR's smoothed poloidal in-torus, EHT standard MAD - q = m::max(m::pow(r / rin, 3) * m::pow(sin(th), 3) * m::exp(-r / 400) * rho_av - min_rho_q, 0.); - break; - case BSeedType::ryan_quadrupole: - // BR's smoothed poloidal in-torus, but turned into a quadrupole - q = m::max(pow(r / rin, 3) * m::pow(sin(th), 3) * m::exp(-r / 400) * rho_av - min_rho_q, 0.) * m::cos(th); - break; - case BSeedType::r3s3: - // Just the r^3 sin^3 th term - q = m::max(m::pow(r / rin, 3) * m::pow(m::sin(th), 3) * rho_av - min_rho_q, 0.); - break; - case BSeedType::steep: - // Bump power to r^5 sin^5 th term, quieter MAD - q = m::max(m::pow(r / rin, 5) * m::pow(m::sin(th), 5) * rho_av - min_rho_q, 0.); - break; - case BSeedType::gaussian: - // Pure vertical threaded field of gaussian strength with FWHM 2*rin (i.e. HM@rin) - // centered at BH center - // Block is to avoid compiler whinging about initialization - { - Real x = (r / rin) * m::sin(th); - Real sigma = 2 / m::sqrt(2 * m::log(2)); - Real u = x / m::abs(sigma); - q = (1 / (m::sqrt(2 * M_PI) * m::abs(sigma))) * m::exp(-u * u / 2); - } - break; - case BSeedType::vertical: - q = bz * r * m::sin(th) / 2.; - default: - // This shouldn't be reached. Squawk here? - break; - } - - if (tilt != 0.0) { - // This is *covariant* A_mu of an untilted disk - const double A_untilt_lower[GR_DIM] = {0., 0., 0., q}; - // Raise to contravariant vector, since rotate_polar_vec will need that. - // Note we have to do this in the midplane! - // The coord_to_native calculation involves an iterative solve for MKS/FMKS - GReal Xnative_midplane[GR_DIM] = {0}, gcon_midplane[GR_DIM][GR_DIM] = {0}; - G.coords.coord_to_native(Xmidplane, Xnative_midplane); - G.coords.gcon_native(Xnative_midplane, gcon_midplane); - double A_untilt[GR_DIM] = {0}; - DLOOP2 A_untilt[mu] += gcon_midplane[mu][nu] * A_untilt_lower[nu]; - - // Then rotate - double A_tilt[GR_DIM] = {0}; - double A_untilt_embed[GR_DIM] = {0}, A_tilt_embed[GR_DIM] = {0}; - G.coords.con_vec_to_embed(Xnative_midplane, A_untilt, A_untilt_embed); - rotate_polar_vec(Xmidplane, A_untilt_embed, -tilt, Xembed, A_tilt_embed); - G.coords.con_vec_to_native(Xnative, A_tilt_embed, A_tilt); - - // Lower the result as we need curl(A_mu). Done at local zone. - double A_tilt_lower[GR_DIM] = {0}; - G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); - VLOOP A(v, k, j, i) = A_tilt_lower[1+v]; - } else { - // Some problems rely on a very accurate A->B, which the rotation lacks. - // So, we preserve exact values in the no-tilt case. - A(V3, k, j, i) = q; - } - } - ); - - // Calculate B-field - if (ndim > 2) { - pmb->par_for("B_field_B_3D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - averaged_curl_3D(G, A, B_U, k, j, i); - } - ); - } else if (ndim > 1) { - pmb->par_for("B_field_B_2D", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - averaged_curl_2D(G, A, B_U, k, j, i); - } - ); - } else { - throw std::runtime_error("Must initialize 1D field directly!"); - } - - // Finally, make sure we initialize the primitive field too - B_FluxCT::BlockUtoP(rc, IndexDomain::entire, false); - - return TaskStatus::complete; -} diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 6c06f746..4adc1afd 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -227,6 +227,14 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD auto pkg = pmb->packages.Get("Boundaries"); auto& params = pkg->AllParams(); + // TODO canonize this as a function. Prints all variables in the current MBD/MD object, + // which can now be smaller than everything. + // std::cout << rc->GetVariableVector().size() << std::endl; + // for (auto &var : rc->GetVariableVector()) { + // std::cout << var->label() << " "; + // } + // std::cout << std::endl; + const auto bface = BoundaryFaceOf(domain); const auto bname = BoundaryName(bface); const auto btype_name = params.Get(bname); @@ -236,6 +244,12 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD pkg->KBoundaries[bface](rc, coarse); EndFlag(); + // Exit immediately if we're syncing emf alone + if (rc->GetVariableVector().size() == 1) { + EndFlag(); + return; + } + // Prevent inflow of material by changing fluid speeds, // anywhere we've specified. if (params.Get("check_inflow_" + bname)) { @@ -245,7 +259,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD } // If specified, fix corner values when applying X2 boundaries (see function) - if (params.Get("fix_corner") && bdir == X2DIR) { + if (bdir == X2DIR && params.Get("fix_corner")) { Flag("FixCorner"); FixCorner(rc, domain, coarse); EndFlag(); @@ -287,7 +301,7 @@ void KBoundaries::FixCorner(std::shared_ptr> &rc, IndexDomai return; // If we're on the interior edge, re-apply that edge for our block by calling - // exactly the same function that Parthenon does. This ensures we're applying + // whatever the X1 boundary is, again. This ensures we're applying // the same thing, just emulating calling it after X2. if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 4f58eafc..76392b28 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -171,7 +171,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_solver.get()); } @@ -199,7 +199,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Copy the primitives to the `linesearch` MeshData object if linesearch was enabled. auto t_copy_linesearch = t_guess_ready; if (use_linesearch) { - t_copy_linesearch = tl.AddTask(t_guess_ready, Copy>, std::vector({Metadata::GetUserFlag("GRPrimitive")}), + t_copy_linesearch = tl.AddTask(t_guess_ready, Copy>, std::vector({Metadata::GetUserFlag("Primitive")}), md_solver.get(), md_linesearch.get()); } @@ -298,7 +298,10 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // identical to their physical counterparts, now that they have been // modified on each rank. const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); - if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + if (two_sync) { + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], 0); + KHARMADriver::AddFullSyncRegion(tc, md_sub_step_final); + } return tc; } diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index b5ad9e17..d946e281 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -111,37 +111,30 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std return pkg; } -void KHARMADriver::AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage) +void KHARMADriver::AddFullSyncRegion(TaskCollection& tc, std::shared_ptr> &md_sync) { const TaskID t_none(0); - // Parthenon's call for bounds is MeshBlock, it sucks - int nblocks = pmesh->block_list.size(); - TaskRegion &async_region2 = tc.AddRegion(nblocks); - for (int i = 0; i < nblocks; i++) { - auto &pmb = pmesh->block_list[i]; - auto &tl = async_region2[i]; - auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); - tl.AddTask(t_none, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); - } + bool sync_prims = pmesh->packages.Get("Driver")->Param("sync_prims"); // MPI boundary exchange, done over MeshData objects/partitions at once + // Parthenon includes physical bounds const int num_partitions = pmesh->DefaultNumPartitions(); // Usually 1 TaskRegion &bound_sync = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { auto &tl = bound_sync[i]; - // This is a member function of KHARMADriver, so it inherits 'integrator' - auto &mbd_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); - AddMPIBoundarySync(t_none, tl, mbd_sub_step_final); + AddMPIBoundarySync(t_none, tl, md_sync, sync_prims, pmesh->multilevel); } } -TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> mc1) +// We take the extra bools to make this a static method, so SyncAllBounds can be static +TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &mc1, + bool sync_prims, bool multilevel) { + Flag("AddBoundarySync"); auto t_start_sync = t_start; - // TODO this is likely part of syncing cons of e.g. implicit vars, etc. - if (0) { //(mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { + if (sync_prims) { TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_ptou_final(0); int i_task = 0; @@ -160,14 +153,14 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: t_start_sync = t_ptou_final; } - auto t_sync_done = parthenon::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, mc1->GetMeshPointer()->multilevel); + // The Parthenon exchange tasks include applying physical boundary conditions + Flag("ParthenonAddSync"); + auto t_sync_done = parthenon::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, multilevel); auto t_bounds = t_sync_done; - - // TODO(BSP) careful about how AMR interacts with below - Kokkos::fence(); + EndFlag(); // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims - if (mc1->GetMeshPointer()->packages.Get("Driver")->Param("sync_prims")) { + if (sync_prims) { TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); int i_task = 0; @@ -186,35 +179,22 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: t_bounds = t_utop_final; } + EndFlag(); return t_bounds; } -TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds) +TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> &md, bool sync_prims, bool multilevel) { Flag("SyncAllBounds"); TaskID t_none(0); - // 1. PtoU on the interior to ensure we're up-to-date - //Flux::MeshPtoU(md.get(), IndexDomain::interior, false); - - // 2. Sync MPI bounds + // 1. Sync MPI bounds // This call syncs the primitive variables when using the ImEx driver, and cons - // TaskCollection tc; auto tr = tc.AddRegion(1); - AddMPIBoundarySync(t_none, tr[0], md); + AddMPIBoundarySync(t_none, tr[0], md, sync_prims, multilevel); while (!tr.Execute()); - if (apply_domain_bounds) { - // 3. Apply physical bounds block-by-block - // TODO clean this up when ApplyBoundaryConditions gets a MeshData version - for (auto &pmb : md->GetMeshPointer()->block_list) { - auto& rc = pmb->meshblock_data.Get(); - // Physical boundary conditions - parthenon::ApplyBoundaryConditions(rc); - } - } - EndFlag(); return TaskStatus::complete; } diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 48b8ff93..208c472a 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -112,10 +112,8 @@ class KHARMADriver : public MultiStageDriver { /** * Add a synchronization retion to an existing TaskCollection tc. * Since the region is self-contained, does not return a TaskID - * - * This function polls the 'integrator' member or it would be static too */ - void AddFullSyncRegion(Mesh* pmesh, TaskCollection& tc, int stage); + void AddFullSyncRegion(TaskCollection& tc, std::shared_ptr> &md); /** * Add just the synchronization step to a task list tl, dependent upon taskID t_start, syncing mesh mc1 @@ -123,7 +121,8 @@ class KHARMADriver : public MultiStageDriver { * This sequence is used identically in several places, so it makes sense * to define once and use elsewhere. */ - static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> mc1); + static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &md, + bool sync_prims=false, bool multilevel=false); /** * Calculate the fluxes in each direction @@ -136,7 +135,7 @@ class KHARMADriver : public MultiStageDriver { * * Only use this as a task each step when debugging! */ - static TaskStatus SyncAllBounds(std::shared_ptr> md, bool apply_domain_bounds=true); + static TaskStatus SyncAllBounds(std::shared_ptr> &md, bool sync_prims=false, bool multilevel=false); // TODO swapped versions of these /** diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index baef3a12..cdd0380d 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -45,8 +45,9 @@ // Other headers #include "boundaries.hpp" #include "flux.hpp" -#include "resize_restart.hpp" +#include "kharma.hpp" #include "implicit.hpp" +#include "resize_restart.hpp" #include #include @@ -80,7 +81,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const TaskID t_none(0); // Which packages we load affects which tasks we'll add to the list - auto& pkgs = blocks[0]->packages.AllPackages(); + auto& pkgs = pmesh->packages.AllPackages(); auto& driver_pkg = pkgs.at("Driver")->AllParams(); const bool use_b_cleanup = pkgs.count("B_Cleanup"); const bool use_b_ct = pkgs.count("B_CT"); @@ -106,7 +107,15 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int } } - //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); + Flag("MakeTaskCollection::fluxes"); + + // Build the list of variables we'll be syncing during "normal" boundary exchanges. + // This *excludes* anything related to divergence cleaning (which have their own syncs during the clean), + // and the EMF (or other edge variables) which are really part of the flux correction sync + using FC = Metadata::FlagCollection; + auto sync_flags = FC(Metadata::FillGhost) - FC(Metadata::Edge); + if (pkgs.count("B_Cleanup")) sync_flags = sync_flags - FC(Metadata::GetUserFlag("B_Cleanup")); + std::vector sync_vars = KHARMA::GetVariableNames(&(pmesh->packages), sync_flags); // Big packed region: get and apply new fluxes on all the zones we control const int num_partitions = pmesh->DefaultNumPartitions(); @@ -142,7 +151,6 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // TODO this MPI sync should be bundled into fluxcorr if (use_b_ct) { // Pull out a container of only EMF to synchronize - auto &base = pmesh->mesh_data.Get(); auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); auto t_emf = KHARMADriver::AddMPIBoundarySync(t_emf_local, tl, md_emf_only); @@ -201,13 +209,19 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // on adjacent ranks are seeded with the same value, which keeps them (more) similar auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } + // TODO the pointers here are weird + //auto &md_sync = pmesh->mesh_data.AddShallow("sync", md_sub_step_final, sync_vars); + //md_sync->SetMeshPointer(pmesh); KHARMADriver::AddMPIBoundarySync(t_copy_prims, tl, md_sub_step_final); } + EndFlag(); + Flag("MakeTaskCollection::fixes"); + // Smaller meshblock region. This gets touchy because we want to keep ghost zones updated, // so very commented TaskRegion &async_region = tc.AddRegion(blocks.size()); @@ -285,6 +299,9 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int } } + EndFlag(); + Flag("MakeTaskCollection::extras"); + // B Field cleanup: this is a separate solve so it's split out // It's also really slow when enabled so we don't care too much about limiting regions, etc. if (use_b_cleanup && (stage == integrator->nstages) && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { @@ -301,7 +318,14 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // identical to their physical counterparts, now that they have been // modified on each rank. const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); - if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + if (two_sync) { + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], 0); + // TODO this gets weird if we partition + //auto &md_sync = pmesh->mesh_data.AddShallow("sync", md_sub_step_final, sync_vars); + KHARMADriver::AddFullSyncRegion(tc, md_sub_step_final); + } + + EndFlag(); return tc; } diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index ca1e7b79..2d68b8f0 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -113,7 +113,7 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s // UtoP needs a guess in order to converge, so we copy in md_sub_step_init auto t_copy_prims = t_update; if (integrator->nstages > 1) { - t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("GRPrimitive")}), + t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_sub_step_final.get()); } @@ -160,7 +160,10 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s // identical to their physical counterparts, now that they have been // modified on each rank. const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); - if (two_sync) KHARMADriver::AddFullSyncRegion(pmesh, tc, stage); + if (two_sync) { + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], 0); + KHARMADriver::AddFullSyncRegion(tc, md_sub_step_final); + } return tc; } diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index d52f6beb..dd73df5c 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -126,9 +126,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, + std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, Metadata::Conserved, Metadata::WithFluxes, Metadata::FillGhost, areWeImplicit, Metadata::GetUserFlag("Electrons")}; - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("GRPrimitive"), + std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), Metadata::Restart, areWeImplicit, Metadata::GetUserFlag("Electrons")}; // Total entropy, used to track changes @@ -201,7 +201,7 @@ TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInpu // Need to distinguish KTOT from the other variables, so we record which it is PackIndexMap prims_map; - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}, prims_map); const int ktot_index = prims_map["prims.Ktot"].first; // Just need these two from the rest of Prims GridScalar rho = rc->Get("prims.rho").data; @@ -238,8 +238,8 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); // No need for a "map" here, we just want everything that fits these - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRPrimitive")}); - auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("GRConserved")}); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}); + auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::Conserved}); // And then the local density GridScalar rho_U = rc->Get("cons.rho").data; @@ -261,8 +261,8 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); + auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); // And then the local density GridScalar rho_P = rc->Get("cons.rho").data; @@ -287,9 +287,9 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData // so we only bother with one map of the primitives // TODO Parthenon can definitely build a pack from a map, though PackIndexMap prims_map, cons_map; - auto& P = rc_old->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto& P_new = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto& U_new = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); + auto& P = rc_old->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto& P_new = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto& U_new = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); auto pmb = rc->GetBlockPointer(); diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 4b235b83..02e73749 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -129,9 +129,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); -// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); +// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); // const VarMap m_p(prims_map, false), m_u(cons_map, true); // const auto& G = pmb->coords; @@ -217,8 +217,8 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::GetUserFlag("GRConserved")}, cons_map); - auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); + auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); const auto& G = pmb->coords; @@ -263,9 +263,9 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap prims_map, cons_map, source_map; - auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto U = md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); - auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, source_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = md->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, source_map); const VarMap m_p(prims_map, false), m_u(cons_map, true), m_s(source_map, true); // Get temporary ucov, Theta for gradients diff --git a/kharma/emhd/emhd_limits.hpp b/kharma/emhd/emhd_limits.hpp index 0da0692d..8da45c8a 100644 --- a/kharma/emhd/emhd_limits.hpp +++ b/kharma/emhd/emhd_limits.hpp @@ -130,8 +130,8 @@ inline void ApplyEMHDLimits(MeshBlockData *mbd, IndexDomain domain) auto packages = pmb->packages; PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index d3ee0640..94ab5db8 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -161,8 +161,8 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; @@ -224,8 +224,8 @@ TaskStatus Floors::ApplyGRMHDFloors(MeshBlockData *mbd, IndexDomain domain auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 23160b42..6f474d13 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -93,8 +93,8 @@ TaskStatus Flux::BlockPtoUMHD(MeshBlockData *rc, IndexDomain domain, bool // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; @@ -125,8 +125,8 @@ TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coa // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const int nvar = U.GetDim(4); @@ -166,8 +166,8 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo // Pack variables PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::GetUserFlag("GRConserved")}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; @@ -228,8 +228,8 @@ void Flux::AddGeoSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap prims_map, cons_map; - auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); // EMHD params diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 1fa4062a..b229cce1 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -107,8 +107,8 @@ inline TaskStatus GetFlux(MeshData *md) const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); // TODO maybe all WithFluxes vars, split into cell & face? - const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::Cell}, prims_map); - const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::Cell}, cons_map); + const auto& P_all = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::Cell}, prims_map); + const auto& U_all = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved, Metadata::Cell}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& Pl_all = md->PackVariables(std::vector{"Flux.Pl"}); diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index a5474e7a..8e82e224 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -128,8 +128,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, - Metadata::Restart, Metadata::GetUserFlag("GRPrimitive"), + Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, - Metadata::WithFluxes, Metadata::GetUserFlag("GRConserved"), Metadata::Conserved, + Metadata::WithFluxes, Metadata::Conserved, Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; bool sync_prims = packages->Get("Driver")->Param("sync_prims"); @@ -259,31 +258,28 @@ Real EstimateTimestep(MeshBlockData *rc) return globals.Get("dt_light"); } - Reductions::Reduce3v minmax; + ParArray1D min_loc("min_loc", 3); + + // TODO version preserving location, with switch to keep this fast one + // std::tuple doesn't work device-side, Kokkos::pair is 2D. pair of pairs? + Real min_ndt = 0.; pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int k, const int j, const int i, - Reductions::Reduce3v &lminmax) { + Real &local_result) { double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / m::max(cmax(0, k, j, i), cmin(0, k, j, i))) + 1 / (G.Dxc<2>(j) / m::max(cmax(1, k, j, i), cmin(1, k, j, i))) + 1 / (G.Dxc<3>(k) / m::max(cmax(2, k, j, i), cmin(2, k, j, i)))); - // Effective "max speed" used for the timestep - double ctop_max_zone = m::min(G.Dxc<1>(i), m::min(G.Dxc<2>(j), G.Dxc<3>(k))) / ndt_zone; - if (!m::isnan(ndt_zone) && (ndt_zone < lminmax.min_val)) { - lminmax.min_val = ndt_zone; - lminmax.min_loc = std::tuple{i, j, k}; - } - if (!m::isnan(ctop_max_zone) && (ctop_max_zone > lminmax.max_val)) { - lminmax.max_val = ctop_max_zone; - lminmax.max_loc = std::tuple{i, j, k}; + if (!m::isnan(ndt_zone) && (ndt_zone < local_result)) { + local_result = ndt_zone; } } - , Reductions::Reduce3(minmax)); - // Keep dt to do some checks below - const double min_ndt = minmax.min_val; - const double nctop = minmax.max_val; + , Kokkos::Min(min_ndt)); + // TODO(BSP) this would need work for non-rectangular grids. + const double nctop = m::min(G.Dxc<1>(0), m::min(G.Dxc<2>(0), G.Dxc<3>(0))) / min_ndt; - // TODO print tuples + // TODO print location + //std::cout << "New min timestep: " << min_ndt << std::endl; // Apply limits const double cfl = grmhd_pars.Get("cfl"); diff --git a/kharma/grmhd/pack.hpp b/kharma/grmhd/pack.hpp index 50063167..13ab53d1 100644 --- a/kharma/grmhd/pack.hpp +++ b/kharma/grmhd/pack.hpp @@ -50,29 +50,29 @@ namespace GRMHD { */ inline VariablePack PackMHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - return rc->PackVariables({Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD"), Metadata::Cell}, prims_map, coarse); } inline MeshBlockPack> PackMHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("MHD")}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("MHD"), Metadata::Cell}, prims_map, coarse); } inline VariablePack PackMHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { - return rc->PackVariables({Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("MHD")}, cons_map, coarse); + return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("MHD"), Metadata::Cell}, cons_map, coarse); } inline MeshBlockPack> PackMHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("MHD")}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("MHD"), Metadata::Cell}, cons_map, coarse); } inline VariablePack PackHDPrims(MeshBlockData *rc, PackIndexMap& prims_map, bool coarse=false) { - return rc->PackVariables({Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); + return rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::Cell}, prims_map, coarse); } inline MeshBlockPack> PackHDPrims(MeshData *md, PackIndexMap& prims_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive"), Metadata::GetUserFlag("HD")}, prims_map, coarse); + return md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::Cell}, prims_map, coarse); } // Version without template @@ -81,11 +81,11 @@ inline VariablePack PackHDPrims(T data) { PackIndexMap nop; return PackHDP inline VariablePack PackHDCons(MeshBlockData *rc, PackIndexMap& cons_map, bool coarse=false) { auto pmb = rc->GetBlockPointer(); - return rc->PackVariables({Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("HD")}, cons_map, coarse); + return rc->PackVariables({Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::Cell}, cons_map, coarse); } inline MeshBlockPack> PackHDCons(MeshData *md, PackIndexMap& cons_map, bool coarse=false) { - return md->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved"), Metadata::GetUserFlag("HD")}, cons_map, coarse); + return md->PackVariables(std::vector{Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::Cell}, cons_map, coarse); } diff --git a/kharma/implicit/fix_solve.cpp b/kharma/implicit/fix_solve.cpp index 3703b5ec..9c9d7104 100644 --- a/kharma/implicit/fix_solve.cpp +++ b/kharma/implicit/fix_solve.cpp @@ -46,7 +46,7 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { // Get number of implicit variables PackIndexMap implicit_prims_map; - auto implicit_vars = Implicit::GetOrderedNames(mbd, Metadata::GetUserFlag("GRPrimitive"), true); + auto implicit_vars = Implicit::GetOrderedNames(mbd, Metadata::GetUserFlag("Primitive"), true); auto& P = mbd->PackVariables(implicit_vars, implicit_prims_map); const int nfvar = P.GetDim(4); @@ -131,8 +131,8 @@ TaskStatus Implicit::FixSolve(MeshBlockData *mbd) { // Since floors were applied earlier, we assume the zones obtained by averaging the neighbors also respect the floors. // Compute new conserved variables PackIndexMap prims_map, cons_map; - auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); - auto& U_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto& P_all = mbd->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto& U_all = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); // Need emhd_params object diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 7d0851fc..826a4665 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -186,8 +186,8 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // just the residual & Jacobian we care about, which makes the solve faster. auto& mbd_full_step_init = md_full_step_init->GetBlockData(0); // MeshBlockData object, more member functions - auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRPrimitive")); - auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRConserved")); + auto ordered_prims = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive")); + auto ordered_cons = GetOrderedNames(mbd_full_step_init.get(), Metadata::Conserved); //std::cerr << "Ordered prims:"; for(auto prim: ordered_prims) std::cerr << " " << prim; std::cerr << std::endl; //std::cerr << "Ordered cons:"; for(auto con: ordered_cons) std::cerr << " " << con; std::cerr << std::endl; @@ -209,7 +209,7 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ const int nblock = U_full_step_init_all.GetDim(5); const int nvar = U_full_step_init_all.GetDim(4); // Get number of implicit variables - auto implicit_vars = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("GRPrimitive"), true); + auto implicit_vars = GetOrderedNames(mbd_full_step_init.get(), Metadata::GetUserFlag("Primitive"), true); //std::cerr << "Ordered implicit:"; for(auto var: implicit_vars) std::cerr << " " << var; std::cerr << std::endl; PackIndexMap implicit_prims_map; diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 7c1f04a1..594bc1c3 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -237,7 +237,7 @@ void Packages::PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTi // Parthenon's version of this has a bug, but I would probably subclass it anyway. // very useful to have a single per-step spot to control any routine print statements Flag("PostStepDiagnostics"); - const auto& md = pmesh->mesh_data.GetOrAdd("base", 0).get(); + const auto& md = pmesh->mesh_data.Get().get(); if (md->NumBlocks() > 0) { for (auto &package : pmesh->packages.AllPackages()) { if (package.second->PostStepDiagnosticsMesh != nullptr) { diff --git a/kharma/prob/b_field_tools.hpp b/kharma/prob/b_field_tools.hpp deleted file mode 100644 index 7e9f5902..00000000 --- a/kharma/prob/b_field_tools.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * File: b_field_tools.hpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include "decs.hpp" -#include "types.hpp" - - - -// Internal representation of the field initialization preference for quick switch -// Avoids string comparsion in kernels -enum BSeedType{constant, monopole, monopole_cube, sane, ryan, ryan_quadrupole, r3s3, steep, gaussian, bz_monopole, vertical}; - -/** - * Function to parse a string indicating desired field to a BSeedType - */ -inline BSeedType ParseBSeedType(std::string b_field_type) -{ - if (b_field_type == "constant") { - return BSeedType::constant; - } else if (b_field_type == "monopole") { - return BSeedType::monopole; - } else if (b_field_type == "monopole_cube") { - return BSeedType::monopole_cube; - } else if (b_field_type == "sane") { - return BSeedType::sane; - } else if (b_field_type == "mad" || b_field_type == "ryan") { - return BSeedType::ryan; - } else if (b_field_type == "mad_quadrupole" || b_field_type == "ryan_quadrupole") { - return BSeedType::ryan_quadrupole; - } else if (b_field_type == "r3s3") { - return BSeedType::r3s3; - } else if (b_field_type == "mad_steep" || b_field_type == "steep") { - return BSeedType::steep; - } else if (b_field_type == "gaussian") { - return BSeedType::gaussian; - } else if (b_field_type == "bz_monopole") { - return BSeedType::bz_monopole; - } else if (b_field_type == "vertical") { - return BSeedType::vertical; - } else { - throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); - } -} - -// /** -// * Initializer for magnetic fields directly: value of a divergence-free configuration at a point -// */ -// KOKKOS_INLINE_FUNCTION double BSeed_A(BSeedType type, GReal Xembed[GR_DIM]) -// { - -// } - -// /** -// * -// */ -// KOKKOS_INLINE_FUNCTION double BSeed_B(BSeedType type, GReal Xembed[GR_DIM]) -// { - -// } \ No newline at end of file diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index ff1ac667..170cd914 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -63,7 +63,7 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame // Get all primitive variables (GRMHD+EMHD if in use) PackIndexMap prims_map; - auto P = rc->PackVariables({Metadata::GetUserFlag("GRPrimitive")}, prims_map); + auto P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); VarMap m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index 57e0c802..e0441445 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -47,11 +47,6 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; - - // Are we using EMHD? - // TODO does anything really change? If so use packages.count - //const bool use_emhd = pin->GetOrAddBoolean("emhd", "on", false); const GReal rin = pin->GetOrAddReal("torus", "rin", 6.0); const GReal rmax = pin->GetOrAddReal("torus", "rmax", 12.0); diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index 7406ac32..987d33c5 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -37,8 +37,8 @@ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal ri Real SS = r2 + a2 * cth * cth; Real thin = M_PI / 2.; - Real sthin = sin(thin); - Real cthin = cos(thin); + Real sthin = m::sin(thin); + Real cthin = m::cos(thin); Real rin2 = m::pow(rin, 2); Real DDin = rin2 - 2. * rin + a2; @@ -48,7 +48,7 @@ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal ri if (r >= rin) { return 0.5 * - log((1. + + m::log((1. + m::sqrt(1. + 4. * (l * l * SS * SS) * DD / (AA * AA * sth * sth))) / (SS * DD / AA)) - @@ -57,7 +57,7 @@ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal ri (AA * AA * sth * sth)) - 2. * a * r * l / AA - (0.5 * - log((1. + + m::log((1. + m::sqrt(1. + 4. * (l * l * SSin * SSin) * DDin / (AAin * AAin * sthin * sthin))) / diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 5832403d..2012fe1d 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -104,7 +104,7 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real Xembed[GR_DIM]; G.coord(k, j, i, Loci::corner, Xembed); - A(V3, k, j, i) = added_b * (Xembed[1] + Xembed[2]) * tscale; + A(V3, k, j, i) = added_b * (Xembed[1]/G.Dxc<1>(i) + Xembed[2]/G.Dxc<2>(j)) * tscale; } ); // This fills a couple zones outside the exact interior with bad data diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 5944bbe1..cd2c0000 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -38,7 +38,6 @@ #include "b_cleanup.hpp" #include "b_ct.hpp" #include "b_flux_ct.hpp" -#include "b_field_tools.hpp" #include "blob.hpp" #include "boundaries.hpp" #include "floors.hpp" @@ -48,130 +47,9 @@ #include "kharma.hpp" #include "kharma_driver.hpp" #include "reductions.hpp" +#include "seed_B.hpp" #include "types.hpp" -/** - * Perform a Parthenon MPI reduction. - * Should only be used in initialization code, as the - * reducer object & MPI comm are created on entry & - * cleaned on exit - * TODO use Reductions stuff? - */ -template -inline T MPIReduce_once(T f, MPI_Op O) -{ - parthenon::AllReduce reduction; - reduction.val = f; - reduction.StartReduce(O); - // Wait on results - while (reduction.CheckReduce() == parthenon::TaskStatus::incomplete); - // TODO catch errors? - return reduction.val; -} - -// Shorter names for the reductions we use here -Real MaxBsq(MeshData *md) -{ - return Reductions::DomainReduction(md, UserHistoryOperation::max); -} -Real MaxPressure(MeshData *md) -{ - return Reductions::DomainReduction(md, UserHistoryOperation::max); -} -Real MinBeta(MeshData *md) -{ - return Reductions::DomainReduction(md, UserHistoryOperation::min); -} - -void KHARMA::SeedAndNormalizeB(ParameterInput *pin, std::shared_ptr> md) -{ - // Check which solver we'll be using - auto pmesh = md->GetMeshPointer(); - const bool use_b_flux_ct = pmesh->packages.AllPackages().count("B_FluxCT") - || pmesh->packages.AllPackages().count("B_Cleanup"); - const bool use_b_cd = pmesh->packages.AllPackages().count("B_CD"); - const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - - Flag("SeedBField"); - // Seed the magnetic field on each block - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - - // This initializes B_P & B_U - if (use_b_flux_ct) { - B_FluxCT::SeedBField(rc.get(), pin); - } else if (use_b_cd) { - B_CD::SeedBField(rc.get(), pin); - } - } - EndFlag(); - - // Then, if we're in a torus problem or we explicitly ask for it, - // normalize the magnetic field according to the density - auto prob = pin->GetString("parthenon/job", "problem_id"); - if (pin->GetOrAddBoolean("b_field", "norm", (prob == "torus"))) { - Flag("NormBField"); - // Default to the general literature beta_min of 100. - // As noted above, by default this uses the definition max(P)/max(P_B)! - Real desired_beta_min = pin->GetOrAddReal("b_field", "beta_min", 100.); - - // "Legacy" is the much more common normalization: - // It's the ratio of max values over the domain i.e. max(P) / max(P_B), - // not necessarily a local min(beta) - Real beta_calc_legacy = pin->GetOrAddBoolean("b_field", "legacy_norm", true); - - // Calculate current beta_min value - Real bsq_max, p_max, beta_min; - if (beta_calc_legacy) { - bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); - p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); - beta_min = p_max / (0.5 * bsq_max); - } else { - beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); - } - - if (MPIRank0() && verbose > 0) { - if (beta_calc_legacy) { - std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; - std::cout << "Pressure max pre-norm: " << p_max << std::endl; - } - std::cout << "Beta min pre-norm: " << beta_min << std::endl; - } - - // Then normalize B by sqrt(beta/beta_min) - if (beta_min > 0) { - Real norm = m::sqrt(beta_min/desired_beta_min); - for (auto &pmb : pmesh->block_list) { - auto& rc = pmb->meshblock_data.Get(); - KHARMADriver::Scale(std::vector{"prims.B"}, rc.get(), norm); - } - } - - // Measure again to check. We'll add divB too, later - if (verbose > 0) { - Real bsq_max, p_max, beta_min; - if (beta_calc_legacy) { - bsq_max = MPIReduce_once(MaxBsq(md.get()), MPI_MAX); - p_max = MPIReduce_once(MaxPressure(md.get()), MPI_MAX); - beta_min = p_max / (0.5 * bsq_max); - } else { - beta_min = MPIReduce_once(MinBeta(md.get()), MPI_MIN); - } - if (MPIRank0()) { - if (beta_calc_legacy) { - std::cout << "B^2 max post-norm: " << bsq_max << std::endl; - std::cout << "Pressure max post-norm: " << p_max << std::endl; - } - std::cout << "Beta min post-norm: " << beta_min << std::endl; - } - } - EndFlag(); //NormBField - } - - // We've been initializing/manipulating P - Flux::MeshPtoU(md.get(), IndexDomain::entire); -} - void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) { // This call: @@ -182,10 +60,10 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // 4. Resets a couple of incidental flags, if Parthenon read them from a restart file // 5. If necessary, cleans up any magnetic field divergence present on the grid - // Coming into this function, the *interior* regions should be initialized with a problem: + // Coming into this function, at least the *interior* regions should be initialized with a problem: // that is, at least rho, u, uvec on each physical zone. - // If your problem requires custom boundary conditions, these should be implemented - // with the problem and assigned to the relevant functions in the "Boundaries" package. + // If you need Dirichlet boundary conditions, the domain-edge *ghost* zones should also be initialized, + // as they will be "frozen in" during this function and applied thereafter. auto &md = pmesh->mesh_data.Get(); @@ -201,7 +79,14 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMADriver::SyncAllBounds(md); // Then init B field on each block... - KHARMA::SeedAndNormalizeB(pin, md); + SeedBField(md.get(), pin); + + // If we're doing a torus problem or explicitly ask for it, + // normalize the magnetic field according to the density + bool is_torus = pin->GetString("parthenon/job", "problem_id") == "torus"; + if (pin->GetOrAddBoolean("b_field", "norm", is_torus)) { + NormalizeBField(md.get(), pin); + } } // Regardless, if evolving a field we should print max(divB) diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index b6e57c6e..42f85a40 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -34,7 +34,6 @@ #include "problem.hpp" -#include "b_field_tools.hpp" #include "boundaries.hpp" #include "electrons.hpp" #include "floors.hpp" @@ -76,6 +75,7 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) Flag("ProblemGenerator_"+prob); // Also just print this, it's important if (MPIRank0()) { + // We have no way of tracking whether this is the first block we're initializing static bool printed_msg = false; if (!printed_msg) std::cout << "Initializing problem: " << prob << std::endl; printed_msg = true; diff --git a/kharma/prob/seed_B.cpp b/kharma/prob/seed_B.cpp new file mode 100644 index 00000000..5476331c --- /dev/null +++ b/kharma/prob/seed_B.cpp @@ -0,0 +1,194 @@ +/* + * File: seed_B.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "seed_B.hpp" + +#include "seed_B_impl.hpp" + +#include "boundaries.hpp" +#include "coordinate_utils.hpp" +#include "fm_torus.hpp" +#include "grmhd_functions.hpp" + +using namespace parthenon; + +/** + * Perform a Parthenon MPI reduction. + * Should only be used in initialization code, as the + * reducer object & MPI comm are created on entry & + * cleaned on exit + */ +template +inline T MPIReduce_once(T f, MPI_Op O) +{ + parthenon::AllReduce reduction; + reduction.val = f; + reduction.StartReduce(O); + // Wait on results + while (reduction.CheckReduce() == parthenon::TaskStatus::incomplete); + // TODO catch errors? + return reduction.val; +} + +// Shorter names for the reductions we use here +Real MaxBsq(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::max); +} +Real MaxPressure(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::max); +} +Real MinBeta(MeshData *md) +{ + return Reductions::DomainReduction(md, UserHistoryOperation::min); +} + +TaskStatus SeedBField(MeshData *md, ParameterInput *pin) +{ + Flag("SeedBField"); + std::string b_field_type = pin->GetString("b_field", "type"); + auto pmesh = md->GetMeshPointer(); + const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); + + if (verbose) { + std::cout << "Seeding B field with type " << b_field_type << std::endl; + } + + TaskStatus status = TaskStatus::incomplete; + for (int i=0; i < md->NumBlocks(); i++) { + auto *rc = md->GetBlockData(i).get(); + + // I could make this a map or something, + // but this is the only place I decode it. + // TODO could also save it to a package... + // TODO accumulate TaskStatus properly? + if (b_field_type == "constant") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "monopole") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "monopole_cube") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "sane") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "mad") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "mad_quadrupole") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "r3s3") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "steep" || b_field_type == "r5s5") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "gaussian") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "bz_monopole") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "vertical") { + status = SeedBFieldType(rc, pin); + } else { + throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); + } + } + + EndFlag(); + return status; +} + +TaskStatus NormalizeBField(MeshData *md, ParameterInput *pin) +{ + Flag("NormBField"); + // Check which solver we'll be using + auto pmesh = md->GetMeshPointer(); + const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); + + // Default to the general literature beta_min of 100. + // As noted above, by default this uses the definition max(P)/max(P_B)! + Real desired_beta_min = pin->GetOrAddReal("b_field", "beta_min", 100.); + + // "Legacy" is the much more common normalization: + // It's the ratio of max values over the domain i.e. max(P) / max(P_B), + // not necessarily a local min(beta) + Real beta_calc_legacy = pin->GetOrAddBoolean("b_field", "legacy_norm", true); + + // Calculate current beta_min value + Real bsq_max, p_max, beta_min; + if (beta_calc_legacy) { + bsq_max = MPIReduce_once(MaxBsq(md), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md), MPI_MAX); + beta_min = p_max / (0.5 * bsq_max); + } else { + beta_min = MPIReduce_once(MinBeta(md), MPI_MIN); + } + + if (MPIRank0() && verbose > 0) { + if (beta_calc_legacy) { + std::cout << "B^2 max pre-norm: " << bsq_max << std::endl; + std::cout << "Pressure max pre-norm: " << p_max << std::endl; + } + std::cout << "Beta min pre-norm: " << beta_min << std::endl; + } + + // Then normalize B by sqrt(beta/beta_min) + if (beta_min > 0) { + Real norm = m::sqrt(beta_min/desired_beta_min); + for (auto &pmb : pmesh->block_list) { + auto& rc = pmb->meshblock_data.Get(); + KHARMADriver::Scale(std::vector{"prims.B"}, rc.get(), norm); + } + } // else yell? + + // Measure again to check + if (verbose > 0) { + Real bsq_max, p_max, beta_min; + if (beta_calc_legacy) { + bsq_max = MPIReduce_once(MaxBsq(md), MPI_MAX); + p_max = MPIReduce_once(MaxPressure(md), MPI_MAX); + beta_min = p_max / (0.5 * bsq_max); + } else { + beta_min = MPIReduce_once(MinBeta(md), MPI_MIN); + } + if (MPIRank0()) { + if (beta_calc_legacy) { + std::cout << "B^2 max post-norm: " << bsq_max << std::endl; + std::cout << "Pressure max post-norm: " << p_max << std::endl; + } + std::cout << "Beta min post-norm: " << beta_min << std::endl; + } + } + + // We've been initializing/manipulating P + Flux::MeshPtoU(md, IndexDomain::entire); + + EndFlag(); //NormBField + return TaskStatus::complete; +} \ No newline at end of file diff --git a/kharma/prob/seed_B.hpp b/kharma/prob/seed_B.hpp new file mode 100644 index 00000000..b451cc90 --- /dev/null +++ b/kharma/prob/seed_B.hpp @@ -0,0 +1,141 @@ +/* + * File: seed_B.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" +#include "types.hpp" + +TaskStatus SeedBField(MeshData *md, ParameterInput *pin); + +TaskStatus NormalizeBField(MeshData *md, ParameterInput *pin); + +// Internal representation of the field initialization preference for quick switch +// Avoids string comparsion in kernels +enum BSeedType{constant, monopole, monopole_cube, sane, mad, mad_quadrupole, r3s3, r5s5, gaussian, bz_monopole, vertical}; + +#define SEEDA_ARGS GReal *x, double rho, double rin, double min_A, double A0 + +template +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) {} + +// EHT comparison SANE +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return m::max(rho - min_A, 0.); +} + +// used in testing to exactly agree with harmpi +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return 1. - m::cos(x[2]); +} + +// BR's smoothed poloidal in-torus, EHT standard MAD +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return m::max(m::pow(x[1] / rin, 3) * m::pow(sin(x[2]), 3) * + m::exp(-x[1] / 400) * rho - min_A, 0.); +} + +// MAD, but turned into a quadrupole +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return m::max(pow(x[1] / rin, 3) * m::pow(sin(x[2]), 3) * + m::exp(-x[1] / 400) * rho - min_A, 0.) * m::cos(x[2]); +} + +// Just the r^3 sin^3 th term +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return m::max(m::pow(x[1] / rin, 3) * m::pow(m::sin(x[2]), 3) * rho - min_A, 0.); +} + +// Bump power to r^5 sin^5 th term, quieter MAD +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return m::max(m::pow(x[1] / rin, 5) * m::pow(m::sin(x[2]), 5) * rho - min_A, 0.); +} + +// Pure vertical threaded field of gaussian strength with FWHM 2*rin (i.e. HM@rin) +// centered at BH center +// Block is to avoid compiler whinging about initialization +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + const Real xf = (x[1] / rin) * m::sin(x[2]); + const Real sigma = 2 / m::sqrt(2 * m::log(2)); + const Real u = xf / m::abs(sigma); + return (1 / (m::sqrt(2 * M_PI) * m::abs(sigma))) * m::exp(-u * u / 2); +} + +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return A0 * x[1] * m::sin(x[2]) / 2.; +} + +#define SEEDB_ARGS GReal *x, GReal gdet, double b10, double b20, double b30, double &B1, double &B2, double &B3 + +template +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) {} + +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + B1 = b10; + B2 = b20; + B3 = b30; +} + +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + B1 = b10 / gdet; + B2 = 0.; + B3 = 0.; +} + +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + B1 = 1 / (x[1]*x[1]*x[1]); + B2 = 0.; + B3 = 0.; +} diff --git a/kharma/prob/seed_B_impl.hpp b/kharma/prob/seed_B_impl.hpp new file mode 100644 index 00000000..bf7dbea8 --- /dev/null +++ b/kharma/prob/seed_B_impl.hpp @@ -0,0 +1,295 @@ +/* + * File: seed_B.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "seed_B.hpp" + +#include "b_flux_ct.hpp" +#include "b_ct.hpp" +#include "boundaries.hpp" +#include "domain.hpp" +#include "fm_torus.hpp" + +template +TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDomain domain = IndexDomain::entire) +{ + auto pmb = rc->GetBlockPointer(); + auto pkgs = pmb->packages.AllPackages(); + + // Fields + GridScalar rho = rc->Get("prims.rho").data; + const auto &G = pmb->coords; + + // Parameters + std::string b_field_type = pin->GetString("b_field", "type"); + auto prob = pin->GetString("parthenon/job", "problem_id"); + bool is_torus = (prob == "torus"); + + // Indices + IndexRange3 b = KDomain::GetRange(rc, domain); + int ndim = pmb->pmy_mesh->ndim; + + // Shortcut to field values for easy fields + if constexpr (Seed == BSeedType::constant || + Seed == BSeedType::monopole || + Seed == BSeedType::monopole_cube) + { + if (pkgs.count("B_CT")) + { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); + Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); + Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); + // Fill at 3 different locations + // TODO this would need to be extended for domain < entire + pmb->par_for( + "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, Loci::face1, Xembed); + GReal gdet = G.gdet(Loci::face1, j, i); + double tmp1, tmp2; + seed_b(Xembed, gdet, b10, b20, b30, + B_Uf(F1, 0, k, j, i), tmp1, tmp2); + + G.coord_embed(k, j, i, Loci::face2, Xembed); + gdet = G.gdet(Loci::face2, j, i); + seed_b(Xembed, gdet, b10, b20, b30, + tmp1, B_Uf(F2, 0, k, j, i), tmp2); + + G.coord_embed(k, j, i, Loci::face3, Xembed); + gdet = G.gdet(Loci::face3, j, i); + seed_b(Xembed, gdet, b10, b20, b30, + tmp1, tmp2, B_Uf(F3, 0, k, j, i)); + }); + // Update primitive variables + B_CT::BlockUtoP(rc, domain); + } + else if (pkgs.count("B_FluxCT")) + { + GridVector B_P = rc->Get("prims.B").data; + Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); + Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); + Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); + pmb->par_for( + "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, Xembed); + const GReal gdet = G.gdet(Loci::center, j, i); + seed_b(Xembed, gdet, b10, b20, b30, + B_P(V1, k, j, i), + B_P(V2, k, j, i), + B_P(V3, k, j, i)); + }); + // We still need to update conserved flux values, but then we're done + B_FluxCT::BlockPtoU(rc, domain); + } + return TaskStatus::complete; + } + + // Require and load what we need if necessary + // TODO this seems very inelegant. Also most of these should support non-FM-torii + // as long as we don't call fm_torus_rho below + Real a, rin, rmax, gam, kappa, rho_norm; + Real tilt = 0; // Needs to be initialized + switch (Seed) + { + case BSeedType::sane: + case BSeedType::mad: + case BSeedType::mad_quadrupole: + case BSeedType::r3s3: + case BSeedType::r5s5: + case BSeedType::gaussian: + if (!is_torus) + throw std::invalid_argument("Magnetic field seed " + b_field_type + " supports only torus problems!"); + // Torus parameters + rin = pin->GetReal("torus", "rin"); + rmax = pin->GetReal("torus", "rmax"); + kappa = pin->GetReal("torus", "kappa"); + tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; + // Other things we need only for torus evaluation + gam = pmb->packages.Get("GRMHD")->Param("gamma"); + rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); + a = G.coords.get_a(); + break; + default: + break; + } + + Real A0 = pin->GetOrAddReal("b_field", "A0", 0.); + Real min_A = pin->GetOrAddReal("b_field", "min_A", 0.2); // TODO back compat? Doubtful was used + + // For all other fields... + // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, + // But for tilted conditions we must keep track of all components + IndexSize3 sz = KDomain::GetBlockSize(rc); + ParArrayND A("A", NVEC, sz.n3, sz.n2, sz.n1); + pmb->par_for( + "B_field_A", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xnative[GR_DIM]; + GReal Xembed[GR_DIM], Xmidplane[GR_DIM]; + G.coord(k, j, i, Loci::corner, Xnative); + G.coord_embed(k, j, i, Loci::corner, Xembed); + // What are our corresponding "midplane" values for evaluating the function? + rotate_polar(Xembed, tilt, Xmidplane); + const GReal r = Xmidplane[1], th = Xmidplane[2]; + + // This is written under the assumption re-computed rho is more accurate than a bunch + // of averaging in a meaningful way. Just use the average if not. + Real rho_av; + if (is_torus) + { + // Find rho at corner directly for torii + rho_av = fm_torus_rho(a, rin, rmax, gam, kappa, r, th) / rho_norm; + } + else + { + // Use averages for anything else + // This loop runs over every corner. Centers do not exist before the first + // or after the last, so use the last (ghost) zones available. + const int ii = clip((uint)i, b.is + 1, b.ie); + const int jj = clip((uint)j, b.js + 1, b.je); + const int kk = clip((uint)k, b.ks + 1, b.ke); + if (ndim > 2) + { + rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + + rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1) + + rho(kk - 1, jj, ii) + rho(kk - 1, jj, ii - 1) + + rho(kk - 1, jj - 1, ii) + rho(kk - 1, jj - 1, ii - 1)) / + 8; + } + else + { + rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + + rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1)) / + 4; + } + } + + Real Aphi = seed_a(Xmidplane, rho_av, rin, min_A, A0); + + if (tilt != 0.0) + { + // This is *covariant* A_mu of an untilted disk + const double A_untilt_lower[GR_DIM] = {0., 0., 0., Aphi}; + // Raise to contravariant vector, since rotate_polar_vec will need that. + // Note we have to do this in the midplane! + // The coord_to_native calculation involves an iterative solve for MKS/FMKS + GReal Xnative_midplane[GR_DIM] = {0}, gcon_midplane[GR_DIM][GR_DIM] = {0}; + G.coords.coord_to_native(Xmidplane, Xnative_midplane); + G.coords.gcon_native(Xnative_midplane, gcon_midplane); + double A_untilt[GR_DIM] = {0}; + DLOOP2 A_untilt[mu] += gcon_midplane[mu][nu] * A_untilt_lower[nu]; + + // Then rotate + double A_tilt[GR_DIM] = {0}; + double A_untilt_embed[GR_DIM] = {0}, A_tilt_embed[GR_DIM] = {0}; + G.coords.con_vec_to_embed(Xnative_midplane, A_untilt, A_untilt_embed); + rotate_polar_vec(Xmidplane, A_untilt_embed, -tilt, Xembed, A_tilt_embed); + G.coords.con_vec_to_native(Xnative, A_tilt_embed, A_tilt); + + // Lower the result as we need curl(A_mu). Done at local zone. + double A_tilt_lower[GR_DIM] = {0}; + G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); + VLOOP A(v, k, j, i) = A_tilt_lower[1 + v]; + } + else + { + // Some problems rely on a very accurate A->B, which the rotation lacks. + // So, we preserve exact values in the no-tilt case. + A(V3, k, j, i) = Aphi; + } + }); + + if (pkgs.count("B_CT")) + { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // This fills a couple zones outside the exact interior with bad data + // Careful of that w/e.g. Dirichlet bounds. + IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); + if (ndim > 2) + { + pmb->par_for( + "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_CT::curl_3D(G, A, B_Uf, k, j, i); + }); + } + else if (ndim > 1) + { + pmb->par_for( + "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_CT::curl_2D(G, A, B_Uf, k, j, i); + }); + } + else + { + throw std::runtime_error("Must initialize 1D field directly!"); + } + B_CT::BlockUtoP(rc, domain); + } + else if (pkgs.count("B_FluxCT")) + { + // Calculate B-field + GridVector B_U = rc->Get("cons.B").data; + IndexRange3 bl = KDomain::GetRange(rc, domain, 0, -1); // TODO will need changes if domain < entire + if (ndim > 2) + { + pmb->par_for( + "B_field_B_3D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_FluxCT::averaged_curl_3D(G, A, B_U, k, j, i); + }); + } + else if (ndim > 1) + { + pmb->par_for( + "B_field_B_2D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_FluxCT::averaged_curl_2D(G, A, B_U, k, j, i); + }); + } + else + { + throw std::runtime_error("Must initialize 1D field directly!"); + } + // Finally, make sure we initialize the primitive field too + B_FluxCT::BlockUtoP(rc, domain); + } + + return TaskStatus::complete; +} \ No newline at end of file diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index ebf754b8..f69fa8b8 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -111,32 +111,39 @@ std::vector Reductions::CountFlags(MeshData *md, std::string field_na IndexRange kb = md->GetBoundsK(domain); IndexRange block = IndexRange{0, flag.GetDim(5) - 1}; + // Man, moving arrays is clunky. Oh well. const int n_of_flags = flag_values.size(); - int flag_val_list[MAX_NFLAGS]; - int f=0; + ParArray1D flag_val_list("flag_values", MAX_NFLAGS); + auto flag_val_list_h = flag_val_list.GetHostMirror(); + int f=1; for (auto &flag : flag_values) { - flag_val_list[f] = flag.first; + flag_val_list_h[f] = flag.first; f++; } + flag_val_list.DeepCopy(flag_val_list_h); + Kokkos::fence(); // Count all nonzero (technically, >0) values, - // and all values of each + // and all values which match each flag. // This works for pflags or fflags, so long as they're separate // We don't count negative pflags as they denote zones that shouldn't be fixed Reductions::array_type flag_reducer; pmb0->par_reduce("count_flags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, Reductions::array_type &local_result) { - if ((int) flag(b, 0, k, j, i) > 0) ++local_result.my_array[0]; - for (int f=0; f(flag(b, 0, k, j, i)) & flag_val_list[f]) || - (!is_bitflag && static_cast(flag(b, 0, k, j, i)) == flag_val_list[f])) - ++local_result.my_array[f+1]; + const int flag_int = static_cast(flag(b, 0, k, j, i)); + // First element is total count + if (flag_int > 0) ++local_result.my_array[0]; + // The rest of the list is individual flags + for (int f=1; f < n_of_flags; f++) + if ((is_bitflag && flag_int & flag_val_list(f)) || + (!is_bitflag && flag_int == flag_val_list(f))) + ++local_result.my_array[f]; } - , Reductions::ArraySum(flag_reducer)); - + , Reductions::ArraySum(flag_reducer)); + std::vector n_each_flag; - for (int f=0; f *md, UserHistoryOperation op, int zone) const auto& emhd_params = EMHD::GetEMHDParameters(pmesh->packages); PackIndexMap prims_map, cons_map; - const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); - const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); @@ -192,10 +192,10 @@ T Reductions::EHReduction(MeshData *md, UserHistoryOperation op, int zone) return result; } -#define INSIDE (x[1] > startx[0] && x[2] > startx[1] && x[3] > startx[2]) && \ - (trivial[0] ? x[1] < startx[0] + G.Dxc<1>(i) : x[1] < stopx[0]) && \ - (trivial[1] ? x[2] < startx[1] + G.Dxc<2>(j) : x[2] < stopx[1]) && \ - (trivial[2] ? x[3] < startx[2] + G.Dxc<3>(k) : x[3] < stopx[2]) +#define INSIDE (x[1] > startx1 && x[2] > startx2 && x[3] > startx3) && \ + (trivial1 ? x[1] < startx1 + G.Dxc<1>(i) : x[1] < stopx1) && \ + (trivial2 ? x[2] < startx2 + G.Dxc<2>(j) : x[2] < stopx2) && \ + (trivial3 ? x[3] < startx3 + G.Dxc<3>(k) : x[3] < stopx3) // TODO additionally template on return type to avoid counting flags with Reals template @@ -210,8 +210,8 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const // Just pass in everything we might want. Probably slow? PackIndexMap prims_map, cons_map; - const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("GRPrimitive")}, prims_map); - const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + const auto& P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const auto& U = md->PackVariablesAndFluxes(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& cmax = md->PackVariables(std::vector{"Flux.cmax"}); const auto& cmin = md->PackVariables(std::vector{"Flux.cmin"}); @@ -226,7 +226,16 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const VLOOP if(startx[v] == stopx[v]) { trivial_tmp[v] = true; } - const bool trivial[3] = {trivial_tmp[0], trivial_tmp[1], trivial_tmp[2]}; + // Pull values to pass to device, because passing views is cumbersome + const bool trivial1 = trivial_tmp[0]; + const bool trivial2 = trivial_tmp[1]; + const bool trivial3 = trivial_tmp[2]; + const GReal startx1 = startx[0]; + const GReal startx2 = startx[1]; + const GReal startx3 = startx[2]; + const GReal stopx1 = stopx[0]; + const GReal stopx2 = stopx[1]; + const GReal stopx3 = stopx[2]; T result = 0.; MPI_Op mop; @@ -240,7 +249,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { local_result += reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); } } , sum_reducer); @@ -256,7 +265,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { const Real val = reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); if (val > local_result) local_result = val; } } @@ -273,7 +282,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { const Real val = reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); if (val < local_result) local_result = val; } } diff --git a/kharma/reductions/reductions_types.hpp b/kharma/reductions/reductions_types.hpp index 7ebca674..b52a5cd2 100644 --- a/kharma/reductions/reductions_types.hpp +++ b/kharma/reductions/reductions_types.hpp @@ -38,23 +38,8 @@ #include "decs.hpp" -// Reduction types: teach Kokkos to keep a 3-int index, make it usable -// See grmhd.cpp timestep calc for example -namespace Kokkos { -template <> -struct reduction_identity> { - KOKKOS_FORCEINLINE_FUNCTION constexpr static std::tuple min() { - int max = std::numeric_limits::max(); - return std::tuple{max, max, max}; - } -}; -} namespace Reductions { -// Types for 3-index reduction -typedef Kokkos::MinMaxLoc> Reduce3; -typedef Reduce3::value_type Reduce3v; - -// Array type for reducing arbitrary numbers of reals +// Array type for reducing arbitrary numbers of reals or ints template struct array_type { ScalarType my_array[N]; @@ -75,13 +60,23 @@ struct array_type { } } - KOKKOS_INLINE_FUNCTION array_type& - operator+=(const array_type& src) { + // The kokkos example defines both of these, + // but we clearly can't. Guess. + KOKKOS_INLINE_FUNCTION + array_type& operator+=(const array_type& src) { for (int i = 0; i < N; i++) { my_array[i] += src.my_array[i]; } return *this; } + + // KOKKOS_INLINE_FUNCTION + // void operator+=(const array_type& src) { + // for (int i = 0; i < N; i++) { + // my_array[i] += src.my_array[i]; + // } + // } + }; template diff --git a/kharma/wind/wind.cpp b/kharma/wind/wind.cpp index bc3a77d2..1a560fa1 100644 --- a/kharma/wind/wind.cpp +++ b/kharma/wind/wind.cpp @@ -79,7 +79,7 @@ TaskStatus Wind::AddSource(MeshData *md, MeshData *mdudt) // Pack variables PackIndexMap cons_map; - auto dUdt = mdudt->PackVariables(std::vector{Metadata::GetUserFlag("GRConserved")}, cons_map); + auto dUdt = mdudt->PackVariables(std::vector{Metadata::Conserved}, cons_map); const VarMap m_u(cons_map, true); // Get sizes const IndexRange ib = mdudt->GetBoundsI(IndexDomain::interior); diff --git a/machines/bp.sh b/machines/bp.sh index 5688072b..366ea959 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -14,7 +14,7 @@ if [[ $HOST == "cheshire"* ]]; then module load compiler mpi/2021 fi - NPROC=16 + NPROC=8 MPI_EXE=mpirun fi @@ -29,7 +29,8 @@ if [[ $METAL_HOSTNAME == "fermium" ]]; then MPI_EXE=mpirun if [[ "$ARGS" == *"cuda"* ]]; then - echo "Nothing special for cuda" + # Container default is the wrong NVHPC package + module swap nvhpc-hpcx nvhpc else # AMD for CPUs module load aocc-compiler-4.1.0 mpi diff --git a/make.sh b/make.sh index 35e4df3d..976a356d 100755 --- a/make.sh +++ b/make.sh @@ -215,12 +215,16 @@ fi if [[ $CXX == "icpx" ]]; then export CXXFLAGS="-fno-fast-math $CXXFLAGS" fi +# Avoid NVC++ complaining constantly about one line in Kokkos +if [[ $CXX_NATIVE == "nvc++" ]]; then + export CXXFLAGS="-diag-suppress 68 $CXXFLAGS" +fi ### Build HDF5 ### # If we're building HDF5, do it after we set *all flags* if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then - H5VER=1.12.2 - H5VERU=1_12_2 + H5VER=1.14.2 + H5VERU=1_14_2 cd external # Allow complete reconfigure (for switching compilers, takes longer) if [[ "$ARGS" == *"cleanhdf5"* ]]; then @@ -228,7 +232,7 @@ if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then fi # Download if needed if [ ! -f hdf5-${H5VER}.tar.gz ]; then - curl https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${H5VERU}/source/hdf5-${H5VER}.tar.gz -o hdf5-${H5VER}.tar.gz + curl https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${H5VERU}/src/hdf5-${H5VER}.tar.gz -o hdf5-${H5VER}.tar.gz fi # Unpack if needed (or deleted) if [ ! -d hdf5-${H5VER}/ ]; then diff --git a/pars/kelvin_helmholtz.par b/pars/kelvin_helmholtz.par index 93ba263c..284a359d 100644 --- a/pars/kelvin_helmholtz.par +++ b/pars/kelvin_helmholtz.par @@ -1,5 +1,6 @@ -# GRMHD Modes problem -# Try to propagate several analytically-amenable linear modes of the MHD equations +# Kelvin-Helmholtz instability +# Basic K-H problem, usually used to test AMR +# since it gets refined in predictable places problem_id = kelvin_helmholtz @@ -79,10 +80,9 @@ gamma = 1.666667 reconstruction = linear_mc -#solver = flux_ct # TODO warn on using flux_ct when AMR +type = none solver = face_ct kill_on_large_divb = true -#ct_scheme = bs99 ct_scheme = sg09 diff --git a/pars/orszag_tang.par b/pars/orszag_tang.par index 8af0a5a6..d76df7d4 100644 --- a/pars/orszag_tang.par +++ b/pars/orszag_tang.par @@ -8,13 +8,13 @@ problem_id = orszag_tang refinement = none numlevel = 1 -nx1 = 1024 +nx1 = 512 x1min = -3.141592653589793 x1max = 3.141592653589793 ix1_bc = periodic ox1_bc = periodic -nx2 = 1024 +nx2 = 512 x2min = -3.141592653589793 x2max = 3.141592653589793 ix2_bc = periodic @@ -27,8 +27,8 @@ ix3_bc = periodic ox3_bc = periodic -nx1 = 512 -nx2 = 512 +nx1 = 256 +nx2 = 256 nx3 = 1 diff --git a/pars/sane2d_refined.par b/pars/sane2d_refined.par index e83013b0..bbb226a8 100644 --- a/pars/sane2d_refined.par +++ b/pars/sane2d_refined.par @@ -1,28 +1,26 @@ -# SANE model mirroring the simulation library -# Overall simulation size 50M, to allow -# running at small scale on e.g. a laptop -# Uses MKS coordinates, not Funky variant +# SANE model, but refined +# Needs new B field init problem_id = torus refinement = static -numlevel = 3 -nx1 = 128 -nx2 = 128 +numlevel = 2 +nx1 = 256 +nx2 = 256 nx3 = 1 -nx1 = 64 +nx1 = 128 nx2 = 64 nx3 = 1 x1min = 1.0 x1max = 3.0 -x2min = 0.45 -x2max = 0.55 +x2min = 1.50 +x2max = 1.60 level = 1 @@ -52,34 +50,26 @@ rmax = 12.0 u_jitter = 0.04 + +fix_corner = false + +solver = face_ct +ct_scheme = bs99 type = sane beta_min = 100. - -rho_min_geom = 1e-5 -u_min_geom = 1e-7 -ktot_max = 1500 +rho_min_geom = 1e-6 +u_min_geom = 1e-8 u_over_rho_max = 100 bsq_over_rho_max = 100 - -on = false -howes = true -kawazura = true -werner = true -rowan = true -sharma = true - - -on = false - file_type = hdf5 dt = 10.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B +variables = prims.rho, prims.u, prims.uvec, prims.B, divB # Can't until face field output is enabled # diff --git a/pars/sane3d_refined.par b/pars/sane3d_refined.par new file mode 100644 index 00000000..cf0ce2ec --- /dev/null +++ b/pars/sane3d_refined.par @@ -0,0 +1,85 @@ +# SANE model, but refined +# Needs new B field init + + +problem_id = torus + + +refinement = static +numlevel = 2 +nx1 = 64 +nx2 = 64 +nx3 = 64 + + +nx1 = 16 +nx2 = 16 +nx3 = 16 + + +x1min = 1.0 +x1max = 2.0 +x2min = 1.50 +x2max = 1.60 +x3min = 0.0 +x3max = 6.28 +level = 1 + + +base = spherical_ks +transform = eks +r_out = 20 +a = 0.9375 + + +tlim = 3000.0 +nlim = -1 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +fix_corner = false + + +solver = face_ct +ct_scheme = bs99 +type = sane +beta_min = 100. + + +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +u_over_rho_max = 100 +bsq_over_rho_max = 100 + + +file_type = hdf5 +dt = 0.0 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, divB +ghost_zones = true + +# Can't until face field output is enabled +# +#file_type = rst +#dt = 100.0 +#ghost_zones = true + + +file_type = hst +dt = 0.1 From 4105d83c017af56e396da00ac9dd7dcdb33f20e1 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 29 Aug 2023 20:11:46 -0600 Subject: [PATCH 115/219] Fix last couple typos for 3D CT --- kharma/b_ct/b_ct.cpp | 38 ++++++++++++++++++-------------------- make.sh | 4 ---- pars/sane.par | 3 +-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index eafb3aec..bf91ae63 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -269,25 +269,25 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) emf_pack(bl, E1, 0, k, j, i) = G.Dxc<1>(i) * 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); emf_pack(bl, E2, 0, k, j, i) = G.Dxc<2>(j) * 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, true)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); } emf_pack(bl, E3, 0, k, j, i) = G.Dxc<3>(k) * 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, true)) - + (1./4)*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, true)); + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, true)); } ); } else { @@ -317,27 +317,25 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) pmb0->par_for("B_CT_Circ_1", block.s, block.e, b.ks, b.ke, b.js, b.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); - if (ndim > 2) { + dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); + if (ndim > 2) dB_Uf_dt(bl, F1, 0, k, j, i) += (-emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j); - } } ); pmb0->par_for("B_CT_Circ_2", block.s, block.e, b.ks, b.ke, b1.js, b1.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); - if (ndim > 2) { + dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); + if (ndim > 2) dB_Uf_dt(bl, F2, 0, k, j, i) += (emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); - } } ); if (ndim > 2) { pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F3, 0, k, j, i) += (emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j) - - (emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); + dB_Uf_dt(bl, F3, 0, k, j, i) = (emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j) + + (-emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); } ); } diff --git a/make.sh b/make.sh index 976a356d..64b33f50 100755 --- a/make.sh +++ b/make.sh @@ -215,10 +215,6 @@ fi if [[ $CXX == "icpx" ]]; then export CXXFLAGS="-fno-fast-math $CXXFLAGS" fi -# Avoid NVC++ complaining constantly about one line in Kokkos -if [[ $CXX_NATIVE == "nvc++" ]]; then - export CXXFLAGS="-diag-suppress 68 $CXXFLAGS" -fi ### Build HDF5 ### # If we're building HDF5, do it after we set *all flags* diff --git a/pars/sane.par b/pars/sane.par index e4e0d39b..08d94b4e 100644 --- a/pars/sane.par +++ b/pars/sane.par @@ -36,7 +36,7 @@ cfl = 0.9 gamma = 1.666667 -type = imex +type = kharma two_sync = true reconstruction = weno5 @@ -50,7 +50,6 @@ u_jitter = 0.04 type = sane beta_min = 100. -norm = false rho_min_geom = 1e-6 From b7422620bf4a36252cfd0b3fff24c268e63f2807 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 1 Sep 2023 16:35:17 -0600 Subject: [PATCH 116/219] Add a package which preserves grid coordinates/metric as output variables --- kharma/CMakeLists.txt | 2 + kharma/coord_output/coord_output.cpp | 162 ++++++++++++++++++++ kharma/coord_output/coord_output.hpp | 52 +++++++ kharma/coordinates/coordinate_embedding.hpp | 79 +++++++++- kharma/coordinates/gr_coordinates.hpp | 149 +++++++++++++----- kharma/kharma.cpp | 5 + pars/sane2d.par | 13 ++ 7 files changed, 422 insertions(+), 40 deletions(-) create mode 100644 kharma/coord_output/coord_output.cpp create mode 100644 kharma/coord_output/coord_output.hpp diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index 86ce8e93..cc0ef4e8 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -21,6 +21,7 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_ct EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/boundaries EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/coord_output EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/current EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/driver EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/electrons EXE_NAME_SRC) @@ -45,6 +46,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_cleanup) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_ct) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/b_flux_ct) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/boundaries) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/coord_output) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/current) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/driver) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/electrons) diff --git a/kharma/coord_output/coord_output.cpp b/kharma/coord_output/coord_output.cpp new file mode 100644 index 00000000..c73d56d1 --- /dev/null +++ b/kharma/coord_output/coord_output.cpp @@ -0,0 +1,162 @@ +/* + * File: coord_output.cpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "coord_output.hpp" + +#include "domain.hpp" + +std::shared_ptr CoordinateOutput::Initialize(ParameterInput *pin, std::shared_ptr& packages) +{ + auto pkg = std::make_shared("CoordinateOutput"); + Params ¶ms = pkg->AllParams(); + + // Options + //Real n = pin->GetOrAddReal("wind", "ne", 2.e-4); + //params.Add("ne", n); + + // Fields: cell-center values for geometry only + // TODO test faces when available, optional lists of locations? + Metadata::AddUserFlag("Geometry"); + std::vector s_4vector({GR_DIM}); + std::vector s_4tensor({GR_DIM, GR_DIM}); + std::vector s_4conn({GR_DIM, GR_DIM, GR_DIM}); + std::vector flags_geom = {Metadata::Real, Metadata::Cell, Metadata::Derived, + Metadata::OneCopy, Metadata::GetUserFlag("Geometry")}; + std::vector flags_geom_face = {Metadata::Real, Metadata::Face, Metadata::Derived, + Metadata::OneCopy, Metadata::GetUserFlag("Geometry")}; + auto m0 = Metadata(flags_geom); + auto m1 = Metadata(flags_geom, s_4vector); + auto m1f = Metadata(flags_geom_face, s_4vector); + auto m2 = Metadata(flags_geom, s_4tensor); + auto m3 = Metadata(flags_geom, s_4conn); + + // Native coordinates, t/X1/X2/X3 + pkg->AddField("coords.Xnative", m1); + pkg->AddField("coords.X1", m0); + pkg->AddField("coords.X2", m0); + pkg->AddField("coords.X3", m0); + // Cartesian (or cartesianized KS) coordinates + pkg->AddField("coords.Xcart", m1); + pkg->AddField("coords.x", m0); + pkg->AddField("coords.y", m0); + pkg->AddField("coords.z", m0); + // Spherical KS coordinates + pkg->AddField("coords.Xks", m1); + pkg->AddField("coords.r", m0); + pkg->AddField("coords.th", m0); + pkg->AddField("coords.phi", m0); + + // Metric + pkg->AddField("coords.gcon", m2); + pkg->AddField("coords.gcov", m2); + pkg->AddField("coords.gdet", m0); + pkg->AddField("coords.lapse", m0); + pkg->AddField("coords.conn", m3); + + // Register our output. This will be called before *any* output, + // but we will only fill the fields before the first. + // This is all that's needed unless: + // 1. Someone wants geometry in an AMR sim with remeshing + // 2. Parthenon decides to include a way to delete fields, which we would want to do here + pkg->BlockUserWorkBeforeOutput = CoordinateOutput::BlockUserWorkBeforeOutput; + + return pkg; +} + +TaskStatus CoordinateOutput::BlockUserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin) +{ + auto& globals = pmb->packages.Get("Globals")->AllParams(); + if (!globals.Get("in_loop")) { + auto rc = pmb->meshblock_data.Get(); + + PackIndexMap geom_map; + auto Geom = rc->PackVariables({Metadata::GetUserFlag("Geometry")}, geom_map); + + const auto& G = pmb->coords; + + const int mXnative = geom_map["coords.Xnative"].first; + const int mX1 = geom_map["coords.X1"].first; + const int mX2 = geom_map["coords.X2"].first; + const int mX3 = geom_map["coords.X3"].first; + + const int mXcart = geom_map["coords.Xcart"].first; + const int mx = geom_map["coords.x"].first; + const int my = geom_map["coords.y"].first; + const int mz = geom_map["coords.z"].first; + + const int mXsph = geom_map["coords.Xsph"].first; + const int mr = geom_map["coords.r"].first; + const int mth = geom_map["coords.th"].first; + const int mphi = geom_map["coords.phi"].first; + + const int mgcov = geom_map["coords.gcov"].first; + const int mgcon = geom_map["coords.gcon"].first; + const int mgdet = geom_map["coords.gdet"].first; + const int mlapse = geom_map["coords.lapse"].first; + const int mconn = geom_map["coords.conn"].first; + + IndexRange3 b = KDomain::GetRange(rc, IndexDomain::entire); + pmb->par_for("set_geometry", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // Native + GReal Xnative[GR_DIM]; + G.coord(k, j, i, Loci::center, Xnative); + Geom(mXnative+1, k, j, i) = Geom(mX1, k, j, i) = Xnative[1]; + Geom(mXnative+2, k, j, i) = Geom(mX2, k, j, i) = Xnative[2]; + Geom(mXnative+3, k, j, i) = Geom(mX3, k, j, i) = Xnative[3]; + // Cartesian + Geom(mXcart+1, k, j, i) = Geom(mx, k, j, i) = G.x(k, j, i); + Geom(mXcart+2, k, j, i) = Geom(my, k, j, i) = G.y(k, j, i); + Geom(mXcart+3, k, j, i) = Geom(mz, k, j, i) = G.z(k, j, i); + // Spherical + Geom(mXsph+1, k, j, i) = Geom(mr, k, j, i) = G.r(k, j, i); + Geom(mXsph+2, k, j, i) = Geom(mth, k, j, i) = G.th(k, j, i); + Geom(mXsph+3, k, j, i) = Geom(mphi, k, j, i) = G.phi(k, j, i); + + // Metric + DLOOP2 Geom(mgcov+GR_DIM*mu+nu, k, j, i) = G.gcov(Loci::center, j, i, mu, nu); + DLOOP2 Geom(mgcon+GR_DIM*mu+nu, k, j, i) = G.gcon(Loci::center, j, i, mu, nu); + Geom(mgdet, k, j, i) = G.gdet(Loci::center, j, i); + Geom(mlapse, k, j, i) = 1. / m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + // shift? = G.gcon(Loci::center, j, i, 0, 1) * alpha * alpha; + // Connection + DLOOP3 Geom(mconn+GR_DIM*GR_DIM*mu+GR_DIM*nu+lam, k, j, i) = G.conn(j, i, mu, nu, lam); + + } + ); + + + } + + return TaskStatus::complete; +} diff --git a/kharma/coord_output/coord_output.hpp b/kharma/coord_output/coord_output.hpp new file mode 100644 index 00000000..10f20562 --- /dev/null +++ b/kharma/coord_output/coord_output.hpp @@ -0,0 +1,52 @@ +/* + * File: coord_output.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "grmhd_functions.hpp" + +#include + +namespace CoordinateOutput { + +/** + * Initialize the wind package with several options from the input deck + */ +std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); + +/** + * Fill the geometry output variables with quantities from the GRCoordinates object over a block + */ +TaskStatus BlockUserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin); + +} diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index b3ae341b..bec773fb 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -301,7 +301,9 @@ class CoordinateEmbedding { }, transform); } - // Convenience functions: only radial coordinate as others might be cylinderized + // Coordinate convenience functions: + // transform the radial coordinate alone without len-4 arrays + // ...at least not for the user. These are not fast KOKKOS_INLINE_FUNCTION GReal r_to_native(const GReal r) const { const GReal Xembed[GR_DIM] = {0., r, 0., 0.}; @@ -321,6 +323,81 @@ class CoordinateEmbedding { return Xembed[1]; } + // Get a particular coordinate from an array + // note these *aren't faster* or less memory, just convenient + KOKKOS_INLINE_FUNCTION GReal r_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (is_spherical()) { + return Xembed[1]; + } else { + return m::sqrt(SQR(Xembed[1]) + SQR(Xembed[2]) + SQR(Xembed[3])); + } + } + KOKKOS_INLINE_FUNCTION GReal th_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (is_spherical()) { + return Xembed[2]; + } else { + return m::atan2(m::sqrt(SQR(Xembed[1]) + SQR(Xembed[2])), Xembed[3]); + } + } + KOKKOS_INLINE_FUNCTION GReal phi_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (is_spherical()) { + return Xembed[3]; + } else { + return m::atan2(Xembed[2], Xembed[1]); + } + } + KOKKOS_INLINE_FUNCTION GReal x_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (!is_spherical()) { + return Xembed[1]; + } else { + return Xembed[1] * m::sin(Xembed[2]) * m::cos(Xembed[3]); + } + } + KOKKOS_INLINE_FUNCTION GReal y_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (!is_spherical()) { + return Xembed[2]; + } else { + return Xembed[1] * m::sin(Xembed[2]) * m::sin(Xembed[3]); + } + } + KOKKOS_INLINE_FUNCTION GReal z_of(const GReal Xnative[GR_DIM]) const + { + GReal Xembed[GR_DIM]; + mpark::visit( [&Xnative, &Xembed](const auto& self) { + self.coord_to_embed(Xnative, Xembed); + }, transform); + if (!is_spherical()) { + return Xembed[3]; + } else { + return Xembed[1] * m::cos(Xembed[2]); + } + } + // VECTOR TRANSFORMS // Contravariant vectors: KOKKOS_INLINE_FUNCTION void con_vec_to_embed(const GReal Xnative[GR_DIM], const GReal vcon_native[GR_DIM], GReal vcon_embed[GR_DIM]) const diff --git a/kharma/coordinates/gr_coordinates.hpp b/kharma/coordinates/gr_coordinates.hpp index 8a48ffd9..c8ea0e94 100644 --- a/kharma/coordinates/gr_coordinates.hpp +++ b/kharma/coordinates/gr_coordinates.hpp @@ -151,15 +151,19 @@ class GRCoordinates : public parthenon::UniformCartesian KOKKOS_INLINE_FUNCTION void coord(const int& k, const int& j, const int& i, const Loci& loc, GReal X[GR_DIM]) const; // Coordinates of the embedding system, usually r,th,phi[KS] or x1,x2,x3[Cartesian] KOKKOS_INLINE_FUNCTION void coord_embed(const int& k, const int& j, const int& i, const Loci& loc, GReal Xembed[GR_DIM]) const; - // Coordinates in a specific + // Coordinates in specific systems (slow!) + KOKKOS_INLINE_FUNCTION GReal r(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; + KOKKOS_INLINE_FUNCTION GReal th(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; + KOKKOS_INLINE_FUNCTION GReal phi(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; + KOKKOS_INLINE_FUNCTION GReal x(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; + KOKKOS_INLINE_FUNCTION GReal y(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; + KOKKOS_INLINE_FUNCTION GReal z(const int& k, const int& j, const int& i, const Loci& loc=Loci::center) const; // Transformations using the cached geometry KOKKOS_INLINE_FUNCTION void lower(const Real vcon[GR_DIM], Real vcov[GR_DIM], const int& k, const int& j, const int& i, const Loci loc) const; KOKKOS_INLINE_FUNCTION void raise(const Real vcov[GR_DIM], Real vcon[GR_DIM], const int& k, const int& j, const int& i, const Loci loc) const; - - // TODO Indexing functions and named slices to make it comfy }; /** @@ -198,24 +202,6 @@ KOKKOS_INLINE_FUNCTION void GRCoordinates::coord(const int& k, const int& j, con } } -#if FAST_CARTESIAN -KOKKOS_INLINE_FUNCTION void GRCoordinates::coord_embed(const int& k, const int& j, const int& i, const Loci& loc, GReal Xembed[GR_DIM]) const -{ - // Only supports null transform - coord(k, j, i, loc, Xembed); -} -#else -/** - * TODO Currently CANNOT be called from device-side code - */ -KOKKOS_INLINE_FUNCTION void GRCoordinates::coord_embed(const int& k, const int& j, const int& i, const Loci& loc, GReal Xembed[GR_DIM]) const -{ - GReal Xnative[GR_DIM]; - coord(k, j, i, loc, Xnative); - coords.coord_to_embed(Xnative, Xembed); -} -#endif - KOKKOS_INLINE_FUNCTION void GRCoordinates::lower(const Real vcon[GR_DIM], Real vcov[GR_DIM], const int& k, const int& j, const int& i, const Loci loc) const { @@ -235,24 +221,24 @@ KOKKOS_INLINE_FUNCTION void GRCoordinates::raise(const Real vcov[GR_DIM], Real v // NORMAL: Cache each zone center and return cached value thereafter #if FAST_CARTESIAN KOKKOS_INLINE_FUNCTION Real GRCoordinates::gcon(const Loci loc, const int& j, const int& i, const int mu, const int nu) const - {return -2*(mu == 0 && nu == 0) + (mu == nu);} +{ return -2*(mu == 0 && nu == 0) + (mu == nu); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gcov(const Loci loc, const int& j, const int& i, const int mu, const int nu) const - {return -2*(mu == 0 && nu == 0) + (mu == nu);} +{ return -2*(mu == 0 && nu == 0) + (mu == nu); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gdet(const Loci loc, const int& j, const int& i) const - {return 1;} +{ return 1; } KOKKOS_INLINE_FUNCTION Real GRCoordinates::conn(const int& j, const int& i, const int mu, const int nu, const int lam) const - {return 0;} +{ return 0; } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gdet_conn(const int& j, const int& i, const int mu, const int nu, const int lam) const - {return 0;} +{ return 0; } KOKKOS_INLINE_FUNCTION void GRCoordinates::gcon(const Loci loc, const int& j, const int& i, Real gcon[GR_DIM][GR_DIM]) const - {DLOOP2 gcon[mu][nu] = -2*(mu == 0 && nu == 0) + (mu == nu);} +{DLOOP2 gcon[mu][nu] = -2*(mu == 0 && nu == 0) + (mu == nu);} KOKKOS_INLINE_FUNCTION void GRCoordinates::gcov(const Loci loc, const int& j, const int& i, Real gcov[GR_DIM][GR_DIM]) const - {DLOOP2 gcov[mu][nu] = -2*(mu == 0 && nu == 0) + (mu == nu);} +{DLOOP2 gcov[mu][nu] = -2*(mu == 0 && nu == 0) + (mu == nu);} KOKKOS_INLINE_FUNCTION void GRCoordinates::conn(const int& j, const int& i, Real conn[GR_DIM][GR_DIM][GR_DIM]) const - {DLOOP3 conn[mu][nu][lam] = 0;} +{DLOOP3 conn[mu][nu][lam] = 0;} KOKKOS_INLINE_FUNCTION void GRCoordinates::gdet_conn(const int& j, const int& i, Real gdet_conn[GR_DIM][GR_DIM][GR_DIM]) const - {DLOOP3 gdet_conn[mu][nu][lam] = 0;} +{DLOOP3 gdet_conn[mu][nu][lam] = 0;} #elif NO_CACHE // TODO these are currently VERY SLOW. Rework them to generate just the desired component. (TODO gdet?...) // Except conn. We never need conn fast. @@ -304,22 +290,107 @@ KOKKOS_INLINE_FUNCTION void GRCoordinates::conn(const int& j, const int& i, Real } #else KOKKOS_INLINE_FUNCTION Real GRCoordinates::gcon(const Loci loc, const int& j, const int& i, const int mu, const int nu) const - {return gcon_direct(loc, j, i, mu, nu);} +{ return gcon_direct(loc, j, i, mu, nu); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gcov(const Loci loc, const int& j, const int& i, const int mu, const int nu) const - {return gcov_direct(loc, j, i, mu, nu);} +{ return gcov_direct(loc, j, i, mu, nu); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gdet(const Loci loc, const int& j, const int& i) const - {return gdet_direct(loc, j, i);} +{ return gdet_direct(loc, j, i); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::conn(const int& j, const int& i, const int mu, const int nu, const int lam) const - {return conn_direct(j, i, mu, nu, lam);} +{ return conn_direct(j, i, mu, nu, lam); } KOKKOS_INLINE_FUNCTION Real GRCoordinates::gdet_conn(const int& j, const int& i, const int mu, const int nu, const int lam) const - {return gdet_conn_direct(j, i, mu, nu, lam);} +{ return gdet_conn_direct(j, i, mu, nu, lam); } KOKKOS_INLINE_FUNCTION void GRCoordinates::gcon(const Loci loc, const int& j, const int& i, Real gcon[GR_DIM][GR_DIM]) const - {DLOOP2 gcon[mu][nu] = gcon_direct(loc, j, i, mu, nu);} +{ DLOOP2 gcon[mu][nu] = gcon_direct(loc, j, i, mu, nu); } KOKKOS_INLINE_FUNCTION void GRCoordinates::gcov(const Loci loc, const int& j, const int& i, Real gcov[GR_DIM][GR_DIM]) const - {DLOOP2 gcov[mu][nu] = gcov_direct(loc, j, i, mu, nu);} +{ DLOOP2 gcov[mu][nu] = gcov_direct(loc, j, i, mu, nu); } KOKKOS_INLINE_FUNCTION void GRCoordinates::conn(const int& j, const int& i, Real conn[GR_DIM][GR_DIM][GR_DIM]) const - {DLOOP3 conn[mu][nu][lam] = conn_direct(j, i, mu, nu, lam);} +{ DLOOP3 conn[mu][nu][lam] = conn_direct(j, i, mu, nu, lam); } KOKKOS_INLINE_FUNCTION void GRCoordinates::gdet_conn(const int& j, const int& i, Real gdet_conn[GR_DIM][GR_DIM][GR_DIM]) const - {DLOOP3 gdet_conn[mu][nu][lam] = gdet_conn_direct(j, i, mu, nu, lam);} +{ DLOOP3 gdet_conn[mu][nu][lam] = gdet_conn_direct(j, i, mu, nu, lam); } + +#endif + +// Two implementations: Fast Cartesian can skip some things +#if FAST_CARTESIAN +KOKKOS_INLINE_FUNCTION void GRCoordinates::coord_embed(const int& k, const int& j, const int& i, const Loci& loc, GReal Xembed[GR_DIM]) const +{ + // Only supports null transform + coord(k, j, i, loc, Xembed); +} + +// TODO this properly. We just never call r/th/phi in Cart yet anyway +KOKKOS_INLINE_FUNCTION void r(const int& k, const int& j, const int& i) const +{ return 0; } +KOKKOS_INLINE_FUNCTION void th(const int& k, const int& j, const int& i) const +{ return 0; } +KOKKOS_INLINE_FUNCTION void phi(const int& k, const int& j, const int& i) const +{ return 0; } +KOKKOS_INLINE_FUNCTION void x(const int& k, const int& j, const int& i) const +{ + GReal Xembed[GR_DIM]; + coord(k, j, i, loc, Xembed); + return Xembed[1]; +} +KOKKOS_INLINE_FUNCTION void y(const int& k, const int& j, const int& i) const +{ + GReal Xembed[GR_DIM]; + coord(k, j, i, loc, Xembed); + return Xembed[2]; +} +KOKKOS_INLINE_FUNCTION void z(const int& k, const int& j, const int& i) const +{ + GReal Xembed[GR_DIM]; + coord(k, j, i, loc, Xembed); + return Xembed[3]; +} + +#else + +KOKKOS_INLINE_FUNCTION void GRCoordinates::coord_embed(const int& k, const int& j, const int& i, const Loci& loc, GReal Xembed[GR_DIM]) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + coords.coord_to_embed(Xnative, Xembed); +} + +// These are basically just call-throughs with coord() +// TODO should we cache, esp for e.g. floors? +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::r(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.r_of(Xnative); +} +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::th(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.th_of(Xnative); +} +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::phi(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.phi_of(Xnative); +} +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::x(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.x_of(Xnative); +} +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::y(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.y_of(Xnative); +} +KOKKOS_INLINE_FUNCTION GReal GRCoordinates::z(const int& k, const int& j, const int& i, const Loci& loc) const +{ + GReal Xnative[GR_DIM]; + coord(k, j, i, loc, Xnative); + return coords.z_of(Xnative); +} + #endif diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 1ac830bb..03ef7d80 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -45,6 +45,7 @@ #include "b_cd.hpp" #include "b_cleanup.hpp" #include "b_ct.hpp" +#include "coord_output.hpp" #include "current.hpp" #include "kharma_driver.hpp" #include "electrons.hpp" @@ -287,6 +288,10 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) TaskID t_none(0); // The globals package will never have dependencies auto t_globals = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMA::InitializeGlobals, pin.get()); + // Neither will grid output, as any mesh will get GRCoordinates objects + // FieldIsOutput actually just checks for substring match, so this matches any coords. variable + if (FieldIsOutput(pin.get(), "coords.")) + auto t_coord_out = tl.AddTask(t_none, KHARMA::AddPackage, packages, CoordinateOutput::Initialize, pin.get()); // Driver package is the foundation auto t_driver = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMADriver::Initialize, pin.get()); // Floors package has no dependencies diff --git a/pars/sane2d.par b/pars/sane2d.par index 5d555836..0c24d995 100644 --- a/pars/sane2d.par +++ b/pars/sane2d.par @@ -90,3 +90,16 @@ ghost_zones = true file_type = hst dt = 0.1 + +# This outputs a geometry file, similar to iharm3d's old 'grid.h5' +# Not needed for pyharm analysis in Kerr metrics, but useful in +# a bunch of other contexts. + +file_type = hdf5 +dt = 1e20 +single_precision_output = false +# If you want the convenience and hate disk space, you can add these +# variables to "normal" dump files like output0 too. +variables = coords.Xnative, coords.Xsph, & + coords.gcon, coords.gcov, coords.gdet, coords.lapse, & + coords.conn From 7a2e7290c4471a4003b7523b9ce7aaf9de291f82 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 5 Sep 2023 13:28:39 -0600 Subject: [PATCH 117/219] WIP AMR commit trying to make 3D Newtonian/GR AMR both work --- kharma/b_ct/b_ct.cpp | 12 ++--- kharma/prob/kelvin_helmholtz.hpp | 76 ++++++++++++++++---------------- pars/kelvin_helmholtz.par | 10 +++-- pars/sane2d_refined.par | 12 ++--- 4 files changed, 57 insertions(+), 53 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index bf91ae63..dd5ba72c 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -231,9 +231,11 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)); } - emf_pack(bl, E3, 0, k, j, i) = G.Dxc<3>(k) * - 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) - - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)); + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(G.FaceArea<1>(k, j - 1, i) * B_U(bl).flux(X1DIR, V2, k, j - 1, i) / G.Dxc<2>(j-1) + + G.FaceArea<1>(k, j, i) * B_U(bl).flux(X1DIR, V2, k, j, i) / G.Dxc<2>(j) + - G.FaceArea<2>(k, j, i - 1) * B_U(bl).flux(X2DIR, V1, k, j, i - 1) / G.Dxc<1>(i-1) + - G.FaceArea<2>(k, j, i) * B_U(bl).flux(X2DIR, V1, k, j, i) / G.Dxc<1>(i)); } ); } else if (scheme == "sg09") { @@ -317,7 +319,7 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) pmb0->par_for("B_CT_Circ_1", block.s, block.e, b.ks, b.ke, b.js, b.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); + dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))*G.FaceArea<1>(k, j, i); if (ndim > 2) dB_Uf_dt(bl, F1, 0, k, j, i) += (-emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j); } @@ -325,7 +327,7 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) pmb0->par_for("B_CT_Circ_2", block.s, block.e, b.ks, b.ke, b1.js, b1.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))/G.Dxc<3>(k); + dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))*G.FaceArea<2>(k, j, i); if (ndim > 2) dB_Uf_dt(bl, F2, 0, k, j, i) += (emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); } diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 2012fe1d..7a7e9958 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -93,45 +93,45 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P } ); - if (pmb->packages.AllPackages().count("B_CT")) { - auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - // Halo one zone right for faces - // We don't need any more than that, since curls never take d1dx1 - IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); - IndexSize3 s = KDomain::GetBlockSize(rc); - GridVector A("A", NVEC, s.n3, s.n2, s.n1); - pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - Real Xembed[GR_DIM]; - G.coord(k, j, i, Loci::corner, Xembed); - A(V3, k, j, i) = added_b * (Xembed[1]/G.Dxc<1>(i) + Xembed[2]/G.Dxc<2>(j)) * tscale; - } - ); - // This fills a couple zones outside the exact interior with bad data - IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); - pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - B_CT::curl_2D(G, A, B_Uf, k, j, i); - } - ); - B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); - double max_divb = B_CT::BlockMaxDivB(rc.get()); - std::cout << "Block max DivB: " << max_divb << std::endl; + // if (pmb->packages.AllPackages().count("B_CT")) { + // auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // // Halo one zone right for faces + // // We don't need any more than that, since curls never take d1dx1 + // IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); + // IndexSize3 s = KDomain::GetBlockSize(rc); + // GridVector A("A", NVEC, s.n3, s.n2, s.n1); + // pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, + // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // Real Xembed[GR_DIM]; + // G.coord(k, j, i, Loci::corner, Xembed); + // A(V3, k, j, i) = added_b * (Xembed[1]/G.Dxc<1>(i) + Xembed[2]/G.Dxc<2>(j)) * tscale; + // } + // ); + // // This fills a couple zones outside the exact interior with bad data + // IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); + // pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // B_CT::curl_2D(G, A, B_Uf, k, j, i); + // } + // ); + // B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); + // double max_divb = B_CT::BlockMaxDivB(rc.get()); + // std::cout << "Block max DivB: " << max_divb << std::endl; - } else if (pmb->packages.AllPackages().count("B_FluxCT") || - pmb->packages.AllPackages().count("B_CD")) { - GridVector B_P = rc->Get("prims.B").data; - pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - Real X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - B_P(V1, k, j, i) = added_b * tscale; - B_P(V2, k, j, i) = added_b * tscale; - B_P(V3, k, j, i) = 0.; - } - ); - B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); - } + // } else if (pmb->packages.AllPackages().count("B_FluxCT") || + // pmb->packages.AllPackages().count("B_CD")) { + // GridVector B_P = rc->Get("prims.B").data; + // pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + // Real X[GR_DIM]; + // G.coord(k, j, i, Loci::center, X); + // B_P(V1, k, j, i) = added_b * tscale; + // B_P(V2, k, j, i) = added_b * tscale; + // B_P(V3, k, j, i) = 0.; + // } + // ); + // B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); + // } // Rescale primitive velocities by tscale, and internal energy by the square. pmb->par_for("kh_renorm", b.ks, b.ke, b.js, b.je, b.is, b.ie, diff --git a/pars/kelvin_helmholtz.par b/pars/kelvin_helmholtz.par index 284a359d..a2c5b970 100644 --- a/pars/kelvin_helmholtz.par +++ b/pars/kelvin_helmholtz.par @@ -15,7 +15,7 @@ x1max = 1.0 ix1_bc = periodic ox1_bc = periodic -nx2 = 256 +nx2 = 192 x2min = 0.0 x2max = 2.0 ix2_bc = periodic @@ -80,10 +80,12 @@ gamma = 1.666667 reconstruction = linear_mc -type = none +type = constant +b10 = 1 +b20 = 1 solver = face_ct kill_on_large_divb = true -ct_scheme = sg09 +ct_scheme = bs99 verbose = 1 @@ -92,6 +94,6 @@ extra_checks = 0 file_type = hdf5 -dt = 0.5 +dt = 0.0 variables = prims.rho, prims.u, prims.uvec, prims.B, divB diff --git a/pars/sane2d_refined.par b/pars/sane2d_refined.par index bbb226a8..ec7003d9 100644 --- a/pars/sane2d_refined.par +++ b/pars/sane2d_refined.par @@ -7,20 +7,20 @@ problem_id = torus refinement = static numlevel = 2 -nx1 = 256 -nx2 = 256 +nx1 = 192 +nx2 = 192 nx3 = 1 -nx1 = 128 +nx1 = 64 nx2 = 64 nx3 = 1 x1min = 1.0 x1max = 3.0 -x2min = 1.50 -x2max = 1.60 +x2min = 1.57 +x2max = 1.57 level = 1 @@ -67,7 +67,7 @@ bsq_over_rho_max = 100 file_type = hdf5 -dt = 10.0 +dt = 0.0 single_precision_output = true variables = prims.rho, prims.u, prims.uvec, prims.B, divB From d4fc51176ff40d2ccc27733b51f17af6e86f0047 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 6 Sep 2023 09:52:30 -0600 Subject: [PATCH 118/219] Fix a coordinate output bug by updating Parthenon --- external/parthenon | 2 +- kharma/kharma.hpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/external/parthenon b/external/parthenon index 374d08c6..f9e41049 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 374d08c66d1137951816a017c28a201392d46310 +Subproject commit f9e41049178586c26de0c0b069cf7d05075b019d diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index 323c533e..91c9a99c 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -89,19 +89,23 @@ Packages_t ProcessPackages(std::unique_ptr& pin); * Check whether a given field is anywhere in outputs. * Used to avoid calculating expensive fields (jcon, divB) if they * will not even be written. + * Note this compares the field name as a substring rather than + * an exact match to a vector element, so sub-names like `prims.` + * or `coords.` will match any field which contains them. */ inline bool FieldIsOutput(ParameterInput *pin, std::string name) { InputBlock *pib = pin->pfirst_block; while (pib != nullptr) { - if (pib->block_name.compare(0, 16, "parthenon/output") == 0 && + // For every output block with a 'variables' entry... + if (pib->block_name.find("parthenon/output") != std::string::npos && pin->DoesParameterExist(pib->block_name, "variables")) { std::string allvars = pin->GetString(pib->block_name, "variables"); if (allvars.find(name) != std::string::npos) { return true; } } - pib = pib->pnext; // move to next input block name + pib = pib->pnext; } return false; } From 5c1bbc7a303ac9e72044307153c4978cfcd4d61c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 6 Sep 2023 12:45:25 -0600 Subject: [PATCH 119/219] Add bach Chicoma machinefile, fix compile on Crays --- machines/chicoma.sh | 40 ++++++++++++++++++++++++++++++++++++++++ make.sh | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 machines/chicoma.sh diff --git a/machines/chicoma.sh b/machines/chicoma.sh new file mode 100644 index 00000000..41fc59b6 --- /dev/null +++ b/machines/chicoma.sh @@ -0,0 +1,40 @@ +# LANL Machines: HPC and IC + +# Chicoma +if [[ "$HOST" == "ch-fe"* ]]; then + HOST_ARCH="ZEN2" + + # Cray environments get confused easy + # Make things as simple as possible + # TODO version with Cray wrappers? + module purge + export CRAY_CPU_TARGET="x86-64" + if [[ "$ARGS" == *"cuda"* ]]; then + DEVICE_ARCH="AMPERE80" + # System HDF5 can't use compression + EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" + # Runtime + MPI_NUM_PROCS=4 + if [[ "$ARGS" == *"gnu"* ]]; then + module load PrgEnv-gnu cpe-cuda cuda + elif [[ "$ARGS" == *"intel"* ]]; then + module load PrgEnv-intel + elif [[ "$ARGS" == *"nvc++"* ]]; then + module load PrgEnv-nvhpc cray-hdf5-parallel + EXTRA_FLAGS="-DCMAKE_CUDA_COMPILER=$HOME/bin/nvc++-wrapper -DCMAKE_CUDA_COMPILER_ID=NVHPC -DCMAKE_CUDA_COMPILER_VERSION=11.6 $EXTRA_FLAGS" + else + module load PrgEnv-nvhpc cray-hdf5-parallel + fi + else + module load PrgEnv-aocc + fi + module load cmake + + # Runtime + MPI_NUM_PROCS=4 + MPI_EXE=srun + MPI_EXTRA_ARGS="--cpu-bind=mask_cpu:0x0*16,0x1*16,0x2*16,0x3*16 ~/bin/select-gpu" + unset OMP_NUM_THREADS + unset OMP_PROC_BIND + unset OMP_PLACES +fi diff --git a/make.sh b/make.sh index 64b33f50..e9b6329c 100755 --- a/make.sh +++ b/make.sh @@ -105,7 +105,7 @@ if [[ -z "$CXX_NATIVE" ]]; then CXX_NATIVE=CC C_NATIVE=cc # In case this isn't Cray, use the more common flag - OMP_FLAG="-fopenomp" + #OMP_FLAG="-fopenomp" # Prefer Intel oneAPI compiler over legacy, both over generic elif which icpx >/dev/null 2>&1; then CXX_NATIVE=icpx From ac048d6d9a1a83fcc5614de286861aeca820cd40 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 6 Sep 2023 14:52:40 -0400 Subject: [PATCH 120/219] Compile updates for Frontier, warning fixes --- .gitignore | 2 +- CMakeLists.txt | 6 +- external/patches/variant-hip.patch | 977 +++++++++++++++++++++++++++++ kharma/CMakeLists.txt | 2 + kharma/prob/seed_B.hpp | 5 +- machines/frontier.sh | 50 ++ make.sh | 2 +- run.sh | 2 + 8 files changed, 1040 insertions(+), 6 deletions(-) create mode 100644 external/patches/variant-hip.patch create mode 100644 machines/frontier.sh diff --git a/.gitignore b/.gitignore index 59b06881..8612874b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,7 +66,7 @@ make_args # Executables *.host* *.cuda* -*.rocm* +*.hip* *.sycl* *.exe *.out diff --git a/CMakeLists.txt b/CMakeLists.txt index a36fc94c..73010d2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,6 @@ set(ENABLE_COMPILER_WARNINGS OFF CACHE BOOL "KHARMA Override") set(HDF5_USE_STATIC_LIBRARIES ON CACHE BOOL "KHARMA Override") # Kokkos options -set(Kokkos_ENABLE_OPENMP ON CACHE BOOL "KHARMA Override") set(Kokkos_ENABLE_CUDA_LAMBDA ON CACHE BOOL "KHARMA Override") set(Kokkos_ENABLE_CUDA_CONSTEXPR ON CACHE BOOL "KHARMA Override") set(Kokkos_ENABLE_HWLOC OFF CACHE BOOL "KHARMA Override") # Possible speed improvement? @@ -51,8 +50,9 @@ else() include_directories(SYSTEM ${MPI_INCLUDE_PATH}) endif() -# OpenMP is strictly required -find_package(OpenMP REQUIRED) +# OpenMP is usually used host-side. We're letting Parthenon/Kokkos +# find it though, as sometimes we require disabling it fully +#find_package(OpenMP REQUIRED) # Build Parthenon add_subdirectory(external/parthenon) diff --git a/external/patches/variant-hip.patch b/external/patches/variant-hip.patch new file mode 100644 index 00000000..756dd09f --- /dev/null +++ b/external/patches/variant-hip.patch @@ -0,0 +1,977 @@ +diff --git a/include/mpark/variant.hpp b/include/mpark/variant.hpp +index 2fb2ac549..962ff3f59 100644 +--- a/include/mpark/variant.hpp ++++ b/include/mpark/variant.hpp +@@ -244,9 +244,11 @@ namespace mpark { + virtual const char *what() const noexcept override { return "bad_variant_access"; } + }; + +- [[noreturn]] inline void throw_bad_variant_access() { ++ //[[noreturn]] ++ KOKKOS_INLINE_FUNCTION void throw_bad_variant_access() { + #ifdef MPARK_EXCEPTIONS +- throw bad_variant_access{}; ++ //throw bad_variant_access{}; ++ return; + #else + std::terminate(); + MPARK_BUILTIN_UNREACHABLE; +@@ -310,7 +312,7 @@ namespace mpark { + + #ifdef MPARK_CPP14_CONSTEXPR + template +- inline constexpr std::size_t find_index() { ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t find_index() { + constexpr lib::array matches = { + {std::is_same::value...} + }; +@@ -326,13 +328,13 @@ namespace mpark { + return result; + } + #else +- inline constexpr std::size_t find_index_impl(std::size_t result, ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t find_index_impl(std::size_t result, + std::size_t) { + return result; + } + + template +- inline constexpr std::size_t find_index_impl(std::size_t result, ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t find_index_impl(std::size_t result, + std::size_t idx, + bool b, + Bs... bs) { +@@ -342,7 +344,7 @@ namespace mpark { + } + + template +- inline constexpr std::size_t find_index() { ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t find_index() { + return find_index_impl(not_found, 0, std::is_same::value...); + } + #endif +@@ -371,7 +373,7 @@ namespace mpark { + template class IsTriviallyAvailable, + template class IsAvailable> +- inline constexpr Trait trait() { ++ KOKKOS_INLINE_FUNCTION constexpr Trait trait() { + return IsTriviallyAvailable::value + ? Trait::TriviallyAvailable + : IsAvailable::value ? Trait::Available +@@ -380,7 +382,7 @@ namespace mpark { + + #ifdef MPARK_CPP14_CONSTEXPR + template +- inline constexpr Trait common_trait(Traits... traits_) { ++ KOKKOS_INLINE_FUNCTION constexpr Trait common_trait(Traits... traits_) { + Trait result = Trait::TriviallyAvailable; + lib::array traits = {{traits_...}}; + for (std::size_t i = 0; i < sizeof...(Traits); ++i) { +@@ -392,10 +394,10 @@ namespace mpark { + return result; + } + #else +- inline constexpr Trait common_trait_impl(Trait result) { return result; } ++ KOKKOS_INLINE_FUNCTION constexpr Trait common_trait_impl(Trait result) { return result; } + + template +- inline constexpr Trait common_trait_impl(Trait result, ++ KOKKOS_INLINE_FUNCTION constexpr Trait common_trait_impl(Trait result, + Trait t, + Traits... ts) { + return static_cast(t) > static_cast(result) +@@ -404,7 +406,7 @@ namespace mpark { + } + + template +- inline constexpr Trait common_trait(Traits... ts) { ++ KOKKOS_INLINE_FUNCTION constexpr Trait common_trait(Traits... ts) { + return common_trait_impl(Trait::TriviallyAvailable, ts...); + } + #endif +@@ -444,38 +446,38 @@ namespace mpark { + struct recursive_union { + #ifdef MPARK_RETURN_TYPE_DEDUCTION + template +- inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) { ++ KOKKOS_INLINE_FUNCTION static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) { + return lib::forward(v).head_; + } + + template +- inline static constexpr auto &&get_alt(V &&v, in_place_index_t) { ++ KOKKOS_INLINE_FUNCTION static constexpr auto &&get_alt(V &&v, in_place_index_t) { + return get_alt(lib::forward(v).tail_, in_place_index_t{}); + } + #else + template + struct get_alt_impl { + template +- inline constexpr AUTO_REFREF operator()(V &&v) const ++ KOKKOS_INLINE_FUNCTION constexpr AUTO_REFREF operator()(V &&v) const + AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v).tail_)) + }; + + template + struct get_alt_impl<0, Dummy> { + template +- inline constexpr AUTO_REFREF operator()(V &&v) const ++ KOKKOS_INLINE_FUNCTION constexpr AUTO_REFREF operator()(V &&v) const + AUTO_REFREF_RETURN(lib::forward(v).head_) + }; + + template +- inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t) ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t) + AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v))) + #endif + }; + + struct base { + template +- inline static constexpr AUTO_REFREF get_alt(V &&v) ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO_REFREF get_alt(V &&v) + #ifdef _MSC_VER + AUTO_REFREF_RETURN(recursive_union::get_alt( + lib::forward(v).data_, in_place_index_t{})) +@@ -487,7 +489,7 @@ namespace mpark { + + struct variant { + template +- inline static constexpr AUTO_REFREF get_alt(V &&v) ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO_REFREF get_alt(V &&v) + AUTO_REFREF_RETURN(base::get_alt(lib::forward(v).impl_)) + }; + +@@ -508,7 +510,7 @@ namespace mpark { + template + struct expected { + template +- inline static constexpr bool but_got() { ++ KOKKOS_INLINE_FUNCTION static constexpr bool but_got() { + return std::is_same::value; + } + }; +@@ -520,7 +522,7 @@ namespace mpark { + "`visit` requires the visitor to have a single return type"); + + template +- inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, + Alts &&... alts) + DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward(visitor), + lib::forward(alts)...)) +@@ -697,18 +699,18 @@ namespace mpark { + }; + #else + template +- inline static constexpr const T &at(const T &elem) noexcept { ++ KOKKOS_INLINE_FUNCTION static constexpr const T &at(const T &elem) noexcept { + return elem; + } + + template +- inline static constexpr const lib::remove_all_extents_t &at( ++ KOKKOS_INLINE_FUNCTION static constexpr const lib::remove_all_extents_t &at( + const lib::array &elems, std::size_t i, Is... is) noexcept { + return at(elems[i], is...); + } + + template +- inline static constexpr lib::array, sizeof...(Fs) + 1> ++ KOKKOS_INLINE_FUNCTION static constexpr lib::array, sizeof...(Fs) + 1> + make_farray(F &&f, Fs &&... fs) { + return {{lib::forward(f), lib::forward(fs)...}}; + } +@@ -717,7 +719,7 @@ namespace mpark { + struct make_fmatrix_impl { + + template +- inline static constexpr dispatch_result_t dispatch( ++ KOKKOS_INLINE_FUNCTION static constexpr dispatch_result_t dispatch( + F &&f, Vs &&... vs) { + using Expected = dispatch_result_t; + using Actual = decltype(lib::invoke( +@@ -730,12 +732,12 @@ namespace mpark { + + #ifdef MPARK_RETURN_TYPE_DEDUCTION + template +- inline static constexpr auto impl(lib::index_sequence) { ++ KOKKOS_INLINE_FUNCTION static constexpr auto impl(lib::index_sequence) { + return &dispatch; + } + + template +- inline static constexpr auto impl(Is, ++ KOKKOS_INLINE_FUNCTION static constexpr auto impl(Is, + lib::index_sequence, + Ls... ls) { + return make_farray(impl(lib::push_back_t{}, ls...)...); +@@ -746,13 +748,13 @@ namespace mpark { + + template + struct impl> { +- inline constexpr AUTO operator()() const ++ KOKKOS_INLINE_FUNCTION constexpr AUTO operator()() const + AUTO_RETURN(&dispatch) + }; + + template + struct impl, Ls...> { +- inline constexpr AUTO operator()() const ++ KOKKOS_INLINE_FUNCTION constexpr AUTO operator()() const + AUTO_RETURN( + make_farray(impl, Ls...>{}()...)) + }; +@@ -761,14 +763,14 @@ namespace mpark { + + #ifdef MPARK_RETURN_TYPE_DEDUCTION + template +- inline static constexpr auto make_fmatrix() { ++ KOKKOS_INLINE_FUNCTION static constexpr auto make_fmatrix() { + return make_fmatrix_impl::impl( + lib::index_sequence<>{}, + lib::make_index_sequence::size()>{}...); + } + #else + template +- inline static constexpr AUTO make_fmatrix() ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO make_fmatrix() + AUTO_RETURN( + typename make_fmatrix_impl::template impl< + lib::index_sequence<>, +@@ -778,7 +780,7 @@ namespace mpark { + template + struct make_fdiagonal_impl { + template +- inline static constexpr dispatch_result_t dispatch( ++ KOKKOS_INLINE_FUNCTION static constexpr dispatch_result_t dispatch( + F &&f, Vs &&... vs) { + using Expected = dispatch_result_t; + using Actual = decltype( +@@ -790,12 +792,12 @@ namespace mpark { + } + + template +- inline static constexpr AUTO impl(lib::index_sequence) ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO impl(lib::index_sequence) + AUTO_RETURN(make_farray(&dispatch...)) + }; + + template +- inline static constexpr auto make_fdiagonal() ++ KOKKOS_INLINE_FUNCTION static constexpr auto make_fdiagonal() + -> decltype(make_fdiagonal_impl::impl( + lib::make_index_sequence::size()>{})) { + static_assert(lib::all<(lib::decay_t::size() == +@@ -836,7 +838,7 @@ namespace mpark { + + struct alt { + template +- inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, + Vs &&... vs) + #ifdef MPARK_VARIANT_SWITCH_VISIT + DECLTYPE_AUTO_RETURN( +@@ -862,7 +864,7 @@ namespace mpark { + #endif + + template +- inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + #ifdef MPARK_VARIANT_SWITCH_VISIT +@@ -895,7 +897,7 @@ namespace mpark { + template + struct visitor { + template +- inline static constexpr bool does_not_handle() { ++ KOKKOS_INLINE_FUNCTION static constexpr bool does_not_handle() { + return lib::is_invocable::value; + } + }; +@@ -905,7 +907,7 @@ namespace mpark { + static_assert(visitor::template does_not_handle(), + "`visit` requires the visitor to be exhaustive."); + +- inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, + Values &&... values) + DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward(visitor), + lib::forward(values)...)) +@@ -916,7 +918,7 @@ namespace mpark { + Visitor &&visitor_; + + template +- inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const ++ KOKKOS_INLINE_FUNCTION constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const + DECLTYPE_AUTO_RETURN( + visit_exhaustiveness_check< + Visitor, +@@ -926,18 +928,18 @@ namespace mpark { + }; + + template +- inline static constexpr AUTO make_value_visitor(Visitor &&visitor) ++ KOKKOS_INLINE_FUNCTION static constexpr AUTO make_value_visitor(Visitor &&visitor) + AUTO_RETURN(value_visitor{lib::forward(visitor)}) + + public: + template +- inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward(visitor), + lib::forward(vs).impl_...)) + + template +- inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( +@@ -946,14 +948,14 @@ namespace mpark { + lib::forward(vs).impl_...)) + + template +- inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( + visit_alt(make_value_visitor(lib::forward(visitor)), + lib::forward(vs)...)) + + template +- inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index, ++ KOKKOS_INLINE_FUNCTION static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN( +@@ -973,7 +975,7 @@ namespace mpark { + #pragma warning(disable : 4244) + #endif + template +- inline explicit constexpr alt(in_place_t, Args &&... args) ++ KOKKOS_INLINE_FUNCTION explicit constexpr alt(in_place_t, Args &&... args) + : value(lib::forward(args)...) {} + #ifdef _MSC_VER + #pragma warning(pop) +@@ -992,16 +994,16 @@ namespace mpark { + template \ + union recursive_union { \ + public: \ +- inline explicit constexpr recursive_union(valueless_t) noexcept \ ++ KOKKOS_INLINE_FUNCTION explicit constexpr recursive_union(valueless_t) noexcept \ + : dummy_{} {} \ + \ + template \ +- inline explicit constexpr recursive_union(in_place_index_t<0>, \ ++ KOKKOS_INLINE_FUNCTION explicit constexpr recursive_union(in_place_index_t<0>, \ + Args &&... args) \ + : head_(in_place_t{}, lib::forward(args)...) {} \ + \ + template \ +- inline explicit constexpr recursive_union(in_place_index_t, \ ++ KOKKOS_INLINE_FUNCTION explicit constexpr recursive_union(in_place_index_t, \ + Args &&... args) \ + : tail_(in_place_index_t{}, lib::forward(args)...) {} \ + \ +@@ -1043,36 +1045,36 @@ namespace mpark { + template + class base { + public: +- inline explicit constexpr base(valueless_t tag) noexcept ++ KOKKOS_INLINE_FUNCTION explicit constexpr base(valueless_t tag) noexcept + : data_(tag), index_(static_cast>(-1)) {} + + template +- inline explicit constexpr base(in_place_index_t, Args &&... args) ++ KOKKOS_INLINE_FUNCTION explicit constexpr base(in_place_index_t, Args &&... args) + : data_(in_place_index_t{}, lib::forward(args)...), + index_(I) {} + +- inline constexpr bool valueless_by_exception() const noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool valueless_by_exception() const noexcept { + return index_ == static_cast>(-1); + } + +- inline constexpr std::size_t index() const noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t index() const noexcept { + return valueless_by_exception() ? variant_npos : index_; + } + + protected: + using data_t = recursive_union; + +- friend inline constexpr base &as_base(base &b) { return b; } +- friend inline constexpr const base &as_base(const base &b) { return b; } +- friend inline constexpr base &&as_base(base &&b) { return lib::move(b); } +- friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); } ++ friend KOKKOS_INLINE_FUNCTION constexpr base &as_base(base &b) { return b; } ++ friend KOKKOS_INLINE_FUNCTION constexpr const base &as_base(const base &b) { return b; } ++ friend KOKKOS_INLINE_FUNCTION constexpr base &&as_base(base &&b) { return lib::move(b); } ++ friend KOKKOS_INLINE_FUNCTION constexpr const base &&as_base(const base &&b) { return lib::move(b); } + +- friend inline constexpr data_t &data(base &b) { return b.data_; } +- friend inline constexpr const data_t &data(const base &b) { return b.data_; } +- friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; } +- friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; } ++ friend KOKKOS_INLINE_FUNCTION constexpr data_t &data(base &b) { return b.data_; } ++ friend KOKKOS_INLINE_FUNCTION constexpr const data_t &data(const base &b) { return b.data_; } ++ friend KOKKOS_INLINE_FUNCTION constexpr data_t &&data(base &&b) { return lib::move(b).data_; } ++ friend KOKKOS_INLINE_FUNCTION constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; } + +- inline static constexpr std::size_t size() { return sizeof...(Ts); } ++ KOKKOS_INLINE_FUNCTION static constexpr std::size_t size() { return sizeof...(Ts); } + + data_t data_; + index_t index_; +@@ -1087,7 +1089,7 @@ namespace mpark { + #pragma warning(disable : 4100) + #endif + template +- inline void operator()(Alt &alt) const noexcept { alt.~Alt(); } ++ KOKKOS_INLINE_FUNCTION void operator()(Alt &alt) const noexcept { alt.~Alt(); } + #ifdef _MSC_VER + #pragma warning(pop) + #endif +@@ -1098,7 +1100,7 @@ namespace mpark { + #else + #define MPARK_INHERITING_CTOR(type, base) \ + template \ +- inline explicit constexpr type(Args &&... args) \ ++ KOKKOS_INLINE_FUNCTION explicit constexpr type(Args &&... args) \ + : base(lib::forward(args)...) {} + #endif + +@@ -1128,14 +1130,14 @@ namespace mpark { + MPARK_VARIANT_DESTRUCTOR( + Trait::TriviallyAvailable, + ~destructor() = default;, +- inline void destroy() noexcept { ++ KOKKOS_INLINE_FUNCTION void destroy() noexcept { + this->index_ = static_cast>(-1); + }); + + MPARK_VARIANT_DESTRUCTOR( + Trait::Available, + ~destructor() { destroy(); }, +- inline void destroy() noexcept { ++ KOKKOS_INLINE_FUNCTION void destroy() noexcept { + if (!this->valueless_by_exception()) { + visitation::alt::visit_alt(dtor{}, *this); + } +@@ -1145,7 +1147,7 @@ namespace mpark { + MPARK_VARIANT_DESTRUCTOR( + Trait::Unavailable, + ~destructor() = delete;, +- inline void destroy() noexcept = delete;); ++ KOKKOS_INLINE_FUNCTION void destroy() noexcept = delete;); + + #undef MPARK_VARIANT_DESTRUCTOR + +@@ -1161,7 +1163,7 @@ namespace mpark { + #ifndef MPARK_GENERIC_LAMBDAS + struct ctor { + template +- inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const { ++ KOKKOS_INLINE_FUNCTION void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const { + constructor::construct_alt(lhs_alt, + lib::forward(rhs_alt).value); + } +@@ -1169,14 +1171,14 @@ namespace mpark { + #endif + + template +- inline static T &construct_alt(alt &a, Args &&... args) { ++ KOKKOS_INLINE_FUNCTION static T &construct_alt(alt &a, Args &&... args) { + auto *result = ::new (static_cast(lib::addressof(a))) + alt(in_place_t{}, lib::forward(args)...); + return result->value; + } + + template +- inline static void generic_construct(constructor &lhs, Rhs &&rhs) { ++ KOKKOS_INLINE_FUNCTION static void generic_construct(constructor &lhs, Rhs &&rhs) { + lhs.destroy(); + if (!rhs.valueless_by_exception()) { + visitation::alt::visit_alt_at( +@@ -1281,7 +1283,7 @@ namespace mpark { + using super::operator=; + + template +- inline /* auto & */ auto emplace(Args &&... args) ++ KOKKOS_INLINE_FUNCTION /* auto & */ auto emplace(Args &&... args) + -> decltype(this->construct_alt(access::base::get_alt(*this), + lib::forward(args)...)) { + this->destroy(); +@@ -1296,7 +1298,7 @@ namespace mpark { + template + struct assigner { + template +- inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const { ++ KOKKOS_INLINE_FUNCTION void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const { + self->assign_alt(this_alt, lib::forward(that_alt).value); + } + assignment *self; +@@ -1304,7 +1306,7 @@ namespace mpark { + #endif + + template +- inline void assign_alt(alt &a, Arg &&arg) { ++ KOKKOS_INLINE_FUNCTION void assign_alt(alt &a, Arg &&arg) { + if (this->index() == I) { + #ifdef _MSC_VER + #pragma warning(push) +@@ -1332,7 +1334,7 @@ namespace mpark { + } + + template +- inline void generic_assign(That &&that) { ++ KOKKOS_INLINE_FUNCTION void generic_assign(That &&that) { + if (this->valueless_by_exception() && that.valueless_by_exception()) { + // do nothing. + } else if (that.valueless_by_exception()) { +@@ -1447,12 +1449,12 @@ namespace mpark { + impl &operator=(impl &&) = default; + + template +- inline void assign(Arg &&arg) { ++ KOKKOS_INLINE_FUNCTION void assign(Arg &&arg) { + this->assign_alt(access::base::get_alt(*this), + lib::forward(arg)); + } + +- inline void swap(impl &that) { ++ KOKKOS_INLINE_FUNCTION void swap(impl &that) { + if (this->valueless_by_exception() && that.valueless_by_exception()) { + // do nothing. + } else if (this->index() == that.index()) { +@@ -1499,14 +1501,14 @@ namespace mpark { + #ifndef MPARK_GENERIC_LAMBDAS + struct swapper { + template +- inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { ++ KOKKOS_INLINE_FUNCTION void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { + using std::swap; + swap(this_alt.value, that_alt.value); + } + }; + #endif + +- inline constexpr bool move_nothrow() const { ++ KOKKOS_INLINE_FUNCTION constexpr bool move_nothrow() const { + return this->valueless_by_exception() || + lib::array{ + {std::is_nothrow_move_constructible::value...} +@@ -1612,7 +1614,7 @@ namespace mpark { + template < + typename Front = lib::type_pack_element_t<0, Ts...>, + lib::enable_if_t::value, int> = 0> +- inline constexpr variant() noexcept( ++ KOKKOS_INLINE_FUNCTION constexpr variant() noexcept( + std::is_nothrow_default_constructible::value) + : impl_(in_place_index_t<0>{}) {} + +@@ -1628,7 +1630,7 @@ namespace mpark { + std::size_t I = detail::best_match::value, + typename T = lib::type_pack_element_t, + lib::enable_if_t::value, int> = 0> +- inline constexpr variant(Arg &&arg) noexcept( ++ KOKKOS_INLINE_FUNCTION constexpr variant(Arg &&arg) noexcept( + std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, lib::forward(arg)) {} + +@@ -1637,7 +1639,7 @@ namespace mpark { + typename... Args, + typename T = lib::type_pack_element_t, + lib::enable_if_t::value, int> = 0> +- inline explicit constexpr variant( ++ KOKKOS_INLINE_FUNCTION explicit constexpr variant( + in_place_index_t, + Args &&... args) noexcept(std::is_nothrow_constructible::value) +@@ -1652,7 +1654,7 @@ namespace mpark { + std::initializer_list &, + Args...>::value, + int> = 0> +- inline explicit constexpr variant( ++ KOKKOS_INLINE_FUNCTION explicit constexpr variant( + in_place_index_t, + std::initializer_list il, + Args &&... args) noexcept(std:: +@@ -1667,7 +1669,7 @@ namespace mpark { + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + lib::enable_if_t::value, int> = 0> +- inline explicit constexpr variant( ++ KOKKOS_INLINE_FUNCTION explicit constexpr variant( + in_place_type_t, + Args &&... args) noexcept(std::is_nothrow_constructible::value) +@@ -1682,7 +1684,7 @@ namespace mpark { + std::initializer_list &, + Args...>::value, + int> = 0> +- inline explicit constexpr variant( ++ KOKKOS_INLINE_FUNCTION explicit constexpr variant( + in_place_type_t, + std::initializer_list il, + Args &&... args) noexcept(std:: +@@ -1705,7 +1707,7 @@ namespace mpark { + lib::enable_if_t<(std::is_assignable::value && + std::is_constructible::value), + int> = 0> +- inline variant &operator=(Arg &&arg) noexcept( ++ KOKKOS_INLINE_FUNCTION variant &operator=(Arg &&arg) noexcept( + (std::is_nothrow_assignable::value && + std::is_nothrow_constructible::value)) { + impl_.template assign(lib::forward(arg)); +@@ -1717,7 +1719,7 @@ namespace mpark { + typename... Args, + typename T = lib::type_pack_element_t, + lib::enable_if_t::value, int> = 0> +- inline T &emplace(Args &&... args) { ++ KOKKOS_INLINE_FUNCTION T &emplace(Args &&... args) { + return impl_.template emplace(lib::forward(args)...); + } + +@@ -1730,7 +1732,7 @@ namespace mpark { + std::initializer_list &, + Args...>::value, + int> = 0> +- inline T &emplace(std::initializer_list il, Args &&... args) { ++ KOKKOS_INLINE_FUNCTION T &emplace(std::initializer_list il, Args &&... args) { + return impl_.template emplace(il, lib::forward(args)...); + } + +@@ -1739,7 +1741,7 @@ namespace mpark { + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + lib::enable_if_t::value, int> = 0> +- inline T &emplace(Args &&... args) { ++ KOKKOS_INLINE_FUNCTION T &emplace(Args &&... args) { + return impl_.template emplace(lib::forward(args)...); + } + +@@ -1752,15 +1754,15 @@ namespace mpark { + std::initializer_list &, + Args...>::value, + int> = 0> +- inline T &emplace(std::initializer_list il, Args &&... args) { ++ KOKKOS_INLINE_FUNCTION T &emplace(std::initializer_list il, Args &&... args) { + return impl_.template emplace(il, lib::forward(args)...); + } + +- inline constexpr bool valueless_by_exception() const noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool valueless_by_exception() const noexcept { + return impl_.valueless_by_exception(); + } + +- inline constexpr std::size_t index() const noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr std::size_t index() const noexcept { + return impl_.index(); + } + +@@ -1772,7 +1774,7 @@ namespace mpark { + lib::dependent_type, + Dummy>::value)...>::value, + int> = 0> +- inline void swap(variant &that) noexcept( ++ KOKKOS_INLINE_FUNCTION void swap(variant &that) noexcept( + lib::all<(std::is_nothrow_move_constructible::value && + lib::is_nothrow_swappable::value)...>::value) { + impl_.swap(that.impl_); +@@ -1786,12 +1788,12 @@ namespace mpark { + }; + + template +- inline constexpr bool holds_alternative(const variant &v) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool holds_alternative(const variant &v) noexcept { + return v.index() == I; + } + + template +- inline constexpr bool holds_alternative(const variant &v) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool holds_alternative(const variant &v) noexcept { + return holds_alternative::value>(v); + } + +@@ -1806,60 +1808,60 @@ namespace mpark { + }; + + template +- inline constexpr AUTO_REFREF generic_get(V &&v) ++ KOKKOS_INLINE_FUNCTION constexpr AUTO_REFREF generic_get(V &&v) + AUTO_REFREF_RETURN(generic_get_impl( + holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))( + lib::forward(v))) + } // namespace detail + + template +- inline constexpr variant_alternative_t> &get( ++ KOKKOS_INLINE_FUNCTION constexpr variant_alternative_t> &get( + variant &v) { + return detail::generic_get(v); + } + + template +- inline constexpr variant_alternative_t> &&get( ++ KOKKOS_INLINE_FUNCTION constexpr variant_alternative_t> &&get( + variant &&v) { + return detail::generic_get(lib::move(v)); + } + + template +- inline constexpr const variant_alternative_t> &get( ++ KOKKOS_INLINE_FUNCTION constexpr const variant_alternative_t> &get( + const variant &v) { + return detail::generic_get(v); + } + + template +- inline constexpr const variant_alternative_t> &&get( ++ KOKKOS_INLINE_FUNCTION constexpr const variant_alternative_t> &&get( + const variant &&v) { + return detail::generic_get(lib::move(v)); + } + + template +- inline constexpr T &get(variant &v) { ++ KOKKOS_INLINE_FUNCTION constexpr T &get(variant &v) { + return get::value>(v); + } + + template +- inline constexpr T &&get(variant &&v) { ++ KOKKOS_INLINE_FUNCTION constexpr T &&get(variant &&v) { + return get::value>(lib::move(v)); + } + + template +- inline constexpr const T &get(const variant &v) { ++ KOKKOS_INLINE_FUNCTION constexpr const T &get(const variant &v) { + return get::value>(v); + } + + template +- inline constexpr const T &&get(const variant &&v) { ++ KOKKOS_INLINE_FUNCTION constexpr const T &&get(const variant &&v) { + return get::value>(lib::move(v)); + } + + namespace detail { + + template +- inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept ++ KOKKOS_INLINE_FUNCTION constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept + AUTO_RETURN(v && holds_alternative(*v) + ? lib::addressof(access::variant::get_alt(*v).value) + : nullptr) +@@ -1867,26 +1869,26 @@ namespace mpark { + } // namespace detail + + template +- inline constexpr lib::add_pointer_t>> ++ KOKKOS_INLINE_FUNCTION constexpr lib::add_pointer_t>> + get_if(variant *v) noexcept { + return detail::generic_get_if(v); + } + + template +- inline constexpr lib::add_pointer_t< ++ KOKKOS_INLINE_FUNCTION constexpr lib::add_pointer_t< + const variant_alternative_t>> + get_if(const variant *v) noexcept { + return detail::generic_get_if(v); + } + + template +- inline constexpr lib::add_pointer_t ++ KOKKOS_INLINE_FUNCTION constexpr lib::add_pointer_t + get_if(variant *v) noexcept { + return get_if::value>(v); + } + + template +- inline constexpr lib::add_pointer_t ++ KOKKOS_INLINE_FUNCTION constexpr lib::add_pointer_t + get_if(const variant *v) noexcept { + return get_if::value>(v); + } +@@ -1895,7 +1897,7 @@ namespace mpark { + template + struct convert_to_bool { + template +- inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const { + static_assert(std::is_convertible, + bool>::value, + "relational operators must return a type" +@@ -1907,7 +1909,7 @@ namespace mpark { + } // namespace detail + + template +- inline constexpr bool operator==(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator==(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using equal_to = detail::convert_to_bool; +@@ -1923,7 +1925,7 @@ namespace mpark { + } + + template +- inline constexpr bool operator!=(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator!=(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using not_equal_to = detail::convert_to_bool; +@@ -1939,7 +1941,7 @@ namespace mpark { + } + + template +- inline constexpr bool operator<(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator<(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using less = detail::convert_to_bool; +@@ -1958,7 +1960,7 @@ namespace mpark { + } + + template +- inline constexpr bool operator>(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator>(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using greater = detail::convert_to_bool; +@@ -1977,7 +1979,7 @@ namespace mpark { + } + + template +- inline constexpr bool operator<=(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator<=(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using less_equal = detail::convert_to_bool; +@@ -1997,7 +1999,7 @@ namespace mpark { + } + + template +- inline constexpr bool operator>=(const variant &lhs, ++ KOKKOS_INLINE_FUNCTION constexpr bool operator>=(const variant &lhs, + const variant &rhs) { + using detail::visitation::variant; + using greater_equal = detail::convert_to_bool; +@@ -2019,34 +2021,34 @@ namespace mpark { + + struct monostate {}; + +- inline constexpr bool operator<(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator<(monostate, monostate) noexcept { + return false; + } + +- inline constexpr bool operator>(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator>(monostate, monostate) noexcept { + return false; + } + +- inline constexpr bool operator<=(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator<=(monostate, monostate) noexcept { + return true; + } + +- inline constexpr bool operator>=(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator>=(monostate, monostate) noexcept { + return true; + } + +- inline constexpr bool operator==(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator==(monostate, monostate) noexcept { + return true; + } + +- inline constexpr bool operator!=(monostate, monostate) noexcept { ++ KOKKOS_INLINE_FUNCTION constexpr bool operator!=(monostate, monostate) noexcept { + return false; + } + + #ifdef MPARK_CPP14_CONSTEXPR + namespace detail { + +- inline constexpr bool any(std::initializer_list bs) { ++ KOKKOS_INLINE_FUNCTION constexpr bool any(std::initializer_list bs) { + for (bool b : bs) { + if (b) { + return true; +@@ -2058,7 +2060,7 @@ namespace mpark { + } // namespace detail + + template +- inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) { ++ KOKKOS_INLINE_FUNCTION constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) { + return (!detail::any({vs.valueless_by_exception()...}) + ? (void)0 + : throw_bad_variant_access()), +@@ -2069,20 +2071,20 @@ namespace mpark { + namespace detail { + + template +- inline constexpr bool all_impl(const lib::array &bs, ++ KOKKOS_INLINE_FUNCTION constexpr bool all_impl(const lib::array &bs, + std::size_t idx) { + return idx >= N || (bs[idx] && all_impl(bs, idx + 1)); + } + + template +- inline constexpr bool all(const lib::array &bs) { ++ KOKKOS_INLINE_FUNCTION constexpr bool all(const lib::array &bs) { + return all_impl(bs, 0); + } + + } // namespace detail + + template +- inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs) ++ KOKKOS_INLINE_FUNCTION constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN( + (detail::all( + lib::array{{!vs.valueless_by_exception()...}}) +@@ -2093,7 +2095,7 @@ namespace mpark { + #endif + + template +- inline auto swap(variant &lhs, ++ KOKKOS_INLINE_FUNCTION auto swap(variant &lhs, + variant &rhs) noexcept(noexcept(lhs.swap(rhs))) + -> decltype(lhs.swap(rhs)) { + lhs.swap(rhs); +@@ -2147,7 +2149,7 @@ namespace std { + using argument_type = mpark::variant; + using result_type = std::size_t; + +- inline result_type operator()(const argument_type &v) const { ++ KOKKOS_INLINE_FUNCTION result_type operator()(const argument_type &v) const { + using mpark::detail::visitation::variant; + std::size_t result = + v.valueless_by_exception() +@@ -2172,7 +2174,7 @@ namespace std { + #ifndef MPARK_GENERIC_LAMBDAS + struct hasher { + template +- inline std::size_t operator()(const Alt &alt) const { ++ KOKKOS_INLINE_FUNCTION std::size_t operator()(const Alt &alt) const { + using alt_type = mpark::lib::decay_t; + using value_type = + mpark::lib::remove_const_t; +@@ -2191,7 +2193,7 @@ namespace std { + using argument_type = mpark::monostate; + using result_type = std::size_t; + +- inline result_type operator()(const argument_type &) const noexcept { ++ KOKKOS_INLINE_FUNCTION result_type operator()(const argument_type &) const noexcept { + return 66740831; // return a fundamentally attractive random value. + } + }; diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index cc0ef4e8..2a9c53a9 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -5,6 +5,8 @@ if (Kokkos_ENABLE_CUDA) set(EXE_NAME "kharma.cuda") elseif(Kokkos_ENABLE_SYCL) set(EXE_NAME "kharma.sycl") +elseif(Kokkos_ENABLE_HIP) + set(EXE_NAME "kharma.hip") else() set(EXE_NAME "kharma.host") endif() diff --git a/kharma/prob/seed_B.hpp b/kharma/prob/seed_B.hpp index b451cc90..0f08487d 100644 --- a/kharma/prob/seed_B.hpp +++ b/kharma/prob/seed_B.hpp @@ -46,8 +46,11 @@ enum BSeedType{constant, monopole, monopole_cube, sane, mad, mad_quadrupole, r3s #define SEEDA_ARGS GReal *x, double rho, double rin, double min_A, double A0 +// This will also act as the default implementation for unspecified types, +// which should all be filled as B field by seed_b below. +// So, we want to set it to something dramatic. template -KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) {} +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) { return 0./0.;} // EHT comparison SANE template<> diff --git a/machines/frontier.sh b/machines/frontier.sh new file mode 100644 index 00000000..5f0df32b --- /dev/null +++ b/machines/frontier.sh @@ -0,0 +1,50 @@ + +# Config for OLCF Frontier + +if [[ $HOST == *".frontier.olcf.ornl.gov" ]] +then + HOST_ARCH=ZEN3 + DEVICE_ARCH=VEGA90A + + MPI_EXE=srun + NPROC=64 + + if [[ $ARGS == *"hip"* ]]; then + # HIP compile for AMD GPUs + + if [[ $ARGS == *"cray"* ]]; then + module load PrgEnv-cray + module load craype-accel-amd-gfx90a + module load amd-mixed + else + module load PrgEnv-amd + module load craype-accel-amd-gfx90a + fi + + module load cray-hdf5-parallel + + if [[ $ARGS == *"hipcc"* ]]; then + # TODO LINK MPI RIGHT + CXX_NATIVE=hipcc + C_NATIVE=hipcc + export CXXFLAGS="-I$CRAY_HDF5_PARALLEL_PREFIX/include -L$CRAY_HDF5_PARALLEL_PREFIX/lib -l:libhdf5_parallel.a" + #export PATH="$CRAY_HDF5_PARALLEL_PREFIX/bin:$PATH" + else + CXX_NATIVE=CC + C_NATIVE=cc + export CXXFLAGS="-noopenmp -mllvm -amdgpu-function-calls=false $CXXFLAGS" + fi + + # Runtime + MPI_NUM_PROCS=8 + MPI_EXTRA_ARGS="-c1 --gpus-per-node=8 --gpu-bind=closest" + export MPICH_GPU_SUPPORT_ENABLED=1 + + # Old workaround, for non-GPU MPI only! + #export MPICH_SMP_SINGLE_COPY_MODE=NONE + else + # CPU Compile + # TODO -c etc etc + MPI_NUM_PROCS=1 + fi +fi diff --git a/make.sh b/make.sh index e9b6329c..6235e789 100755 --- a/make.sh +++ b/make.sh @@ -161,7 +161,7 @@ if [[ "$ARGS" == *"sycl"* ]]; then elif [[ "$ARGS" == *"hip"* ]]; then OUTER_LAYOUT="MANUAL1D_LOOP" INNER_LAYOUT="TVR_INNER_LOOP" - ENABLE_OPENMP="ON" + ENABLE_OPENMP="OFF" ENABLE_CUDA="OFF" ENABLE_SYCL="OFF" ENABLE_HIP="ON" diff --git a/run.sh b/run.sh index b94d5798..bce8bc0e 100755 --- a/run.sh +++ b/run.sh @@ -34,6 +34,8 @@ if [ -f $KHARMA_DIR/kharma.cuda ]; then EXE_NAME=kharma.cuda elif [ -f $KHARMA_DIR/kharma.sycl ]; then EXE_NAME=kharma.sycl +elif [ -f $KHARMA_DIR/kharma.hip ]; then + EXE_NAME=kharma.hip elif [ -f $KHARMA_DIR/kharma.host ]; then EXE_NAME=kharma.host else From e3ffb0bb805a74348593824727c8884eb78fd169 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 12 Sep 2023 11:09:32 -0400 Subject: [PATCH 121/219] Fix a reductions HIP compile bug --- kharma/reductions/reductions_variables.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kharma/reductions/reductions_variables.hpp b/kharma/reductions/reductions_variables.hpp index 0ebe3681..46071eb2 100644 --- a/kharma/reductions/reductions_variables.hpp +++ b/kharma/reductions/reductions_variables.hpp @@ -58,7 +58,7 @@ enum class Var{phi, bsq, gas_pressure, mag_pressure, beta, // Function template for all reductions. template -Real reduction_var(REDUCE_FUNCTION_ARGS); +KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS); // Can also sum the hemispheres independently to be fancy (TODO?) template <> @@ -249,4 +249,4 @@ KOKKOS_INLINE_FUNCTION Real reduction_var(REDUCE_FUNCTION_ARGS) } -#undef REDUCE_FUNCTION_ARGS \ No newline at end of file +#undef REDUCE_FUNCTION_ARGS From b86820a93cdb801181b0da6cb761b5f1f930058e Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Fri, 22 Sep 2023 11:18:09 -0500 Subject: [PATCH 122/219] Fixes --- kharma/reductions/reductions.cpp | 31 ++++++++++++++++----------- kharma/reductions/reductions_impl.hpp | 26 +++++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/kharma/reductions/reductions.cpp b/kharma/reductions/reductions.cpp index ebf754b8..f69fa8b8 100644 --- a/kharma/reductions/reductions.cpp +++ b/kharma/reductions/reductions.cpp @@ -111,32 +111,39 @@ std::vector Reductions::CountFlags(MeshData *md, std::string field_na IndexRange kb = md->GetBoundsK(domain); IndexRange block = IndexRange{0, flag.GetDim(5) - 1}; + // Man, moving arrays is clunky. Oh well. const int n_of_flags = flag_values.size(); - int flag_val_list[MAX_NFLAGS]; - int f=0; + ParArray1D flag_val_list("flag_values", MAX_NFLAGS); + auto flag_val_list_h = flag_val_list.GetHostMirror(); + int f=1; for (auto &flag : flag_values) { - flag_val_list[f] = flag.first; + flag_val_list_h[f] = flag.first; f++; } + flag_val_list.DeepCopy(flag_val_list_h); + Kokkos::fence(); // Count all nonzero (technically, >0) values, - // and all values of each + // and all values which match each flag. // This works for pflags or fflags, so long as they're separate // We don't count negative pflags as they denote zones that shouldn't be fixed Reductions::array_type flag_reducer; pmb0->par_reduce("count_flags", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &b, const int &k, const int &j, const int &i, Reductions::array_type &local_result) { - if ((int) flag(b, 0, k, j, i) > 0) ++local_result.my_array[0]; - for (int f=0; f(flag(b, 0, k, j, i)) & flag_val_list[f]) || - (!is_bitflag && static_cast(flag(b, 0, k, j, i)) == flag_val_list[f])) - ++local_result.my_array[f+1]; + const int flag_int = static_cast(flag(b, 0, k, j, i)); + // First element is total count + if (flag_int > 0) ++local_result.my_array[0]; + // The rest of the list is individual flags + for (int f=1; f < n_of_flags; f++) + if ((is_bitflag && flag_int & flag_val_list(f)) || + (!is_bitflag && flag_int == flag_val_list(f))) + ++local_result.my_array[f]; } - , Reductions::ArraySum(flag_reducer)); - + , Reductions::ArraySum(flag_reducer)); + std::vector n_each_flag; - for (int f=0; f *md, UserHistoryOperation op, int zone) return result; } -#define INSIDE (x[1] > startx[0] && x[2] > startx[1] && x[3] > startx[2]) && \ - (trivial[0] ? x[1] < startx[0] + G.Dxc<1>(i) : x[1] < stopx[0]) && \ - (trivial[1] ? x[2] < startx[1] + G.Dxc<2>(j) : x[2] < stopx[1]) && \ - (trivial[2] ? x[3] < startx[2] + G.Dxc<3>(k) : x[3] < stopx[2]) +#define INSIDE (x[1] > startx1 && x[2] > startx2 && x[3] > startx3) && \ + (trivial1 ? x[1] < startx1 + G.Dxc<1>(i) : x[1] < stopx1) && \ + (trivial2 ? x[2] < startx2 + G.Dxc<2>(j) : x[2] < stopx2) && \ + (trivial3 ? x[3] < startx3 + G.Dxc<3>(k) : x[3] < stopx3) // TODO additionally template on return type to avoid counting flags with Reals template @@ -226,7 +226,17 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const VLOOP if(startx[v] == stopx[v]) { trivial_tmp[v] = true; } - const bool trivial[3] = {trivial_tmp[0], trivial_tmp[1], trivial_tmp[2]}; + + // Pull values to pass to device, because passing views is cumbersome + const bool trivial1 = trivial_tmp[0]; + const bool trivial2 = trivial_tmp[1]; + const bool trivial3 = trivial_tmp[2]; + const GReal startx1 = startx[0]; + const GReal startx2 = startx[1]; + const GReal startx3 = startx[2]; + const GReal stopx1 = stopx[0]; + const GReal stopx2 = stopx[1]; + const GReal stopx3 = stopx[2]; T result = 0.; MPI_Op mop; @@ -240,7 +250,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { local_result += reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); } } , sum_reducer); @@ -256,7 +266,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { const Real val = reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); if (val > local_result) local_result = val; } } @@ -273,7 +283,7 @@ T Reductions::DomainReduction(MeshData *md, UserHistoryOperation op, const G.coord_embed(k, j, i, Loci::center, x); if(INSIDE) { const Real val = reduction_var(REDUCE_FUNCTION_CALL) * - (!trivial[2]) * G.Dxc<3>(k) * (!trivial[1]) * G.Dxc<2>(j) * (!trivial[0]) * G.Dxc<1>(i); + (!trivial3) * G.Dxc<3>(k) * (!trivial2) * G.Dxc<2>(j) * (!trivial1) * G.Dxc<1>(i); if (val < local_result) local_result = val; } } From 8a16b518319baee6686aee7e53f95bd5318f4e10 Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Fri, 22 Sep 2023 13:02:04 -0500 Subject: [PATCH 123/219] Not all fixes carried through. This _should_ be all the fixes --- kharma/grmhd/grmhd.cpp | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 9c9f1c41..8e82e224 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -127,7 +127,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, - Metadata::Restart, Metadata::GetUserFlag("Primitive"), Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + Metadata::Restart, Metadata::GetUserFlag("Primitive"), + Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, - Metadata::WithFluxes, Metadata::Conserved, Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + Metadata::WithFluxes, Metadata::Conserved, Metadata::Conserved, + Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; bool sync_prims = packages->Get("Driver")->Param("sync_prims"); if (!sync_prims) { // Normal operation @@ -256,31 +258,28 @@ Real EstimateTimestep(MeshBlockData *rc) return globals.Get("dt_light"); } - Reductions::Reduce3v minmax; + ParArray1D min_loc("min_loc", 3); + + // TODO version preserving location, with switch to keep this fast one + // std::tuple doesn't work device-side, Kokkos::pair is 2D. pair of pairs? + Real min_ndt = 0.; pmb->par_reduce("ndt_min", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int k, const int j, const int i, - Reductions::Reduce3v &lminmax) { + Real &local_result) { double ndt_zone = 1 / (1 / (G.Dxc<1>(i) / m::max(cmax(0, k, j, i), cmin(0, k, j, i))) + 1 / (G.Dxc<2>(j) / m::max(cmax(1, k, j, i), cmin(1, k, j, i))) + 1 / (G.Dxc<3>(k) / m::max(cmax(2, k, j, i), cmin(2, k, j, i)))); - // Effective "max speed" used for the timestep - double ctop_max_zone = m::min(G.Dxc<1>(i), m::min(G.Dxc<2>(j), G.Dxc<3>(k))) / ndt_zone; - if (!m::isnan(ndt_zone) && (ndt_zone < lminmax.min_val)) { - lminmax.min_val = ndt_zone; - lminmax.min_loc = std::tuple{i, j, k}; - } - if (!m::isnan(ctop_max_zone) && (ctop_max_zone > lminmax.max_val)) { - lminmax.max_val = ctop_max_zone; - lminmax.max_loc = std::tuple{i, j, k}; + if (!m::isnan(ndt_zone) && (ndt_zone < local_result)) { + local_result = ndt_zone; } } - , Reductions::Reduce3(minmax)); - // Keep dt to do some checks below - const double min_ndt = minmax.min_val; - const double nctop = minmax.max_val; + , Kokkos::Min(min_ndt)); + // TODO(BSP) this would need work for non-rectangular grids. + const double nctop = m::min(G.Dxc<1>(0), m::min(G.Dxc<2>(0), G.Dxc<3>(0))) / min_ndt; - // TODO print tuples + // TODO print location + //std::cout << "New min timestep: " << min_ndt << std::endl; // Apply limits const double cfl = grmhd_pars.Get("cfl"); From 125d4948753980f370c116d7de7839b01a70f709 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 12:39:18 -0600 Subject: [PATCH 124/219] Darwin compile standarization/expansion --- machines/darwin.sh | 92 +++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/machines/darwin.sh b/machines/darwin.sh index ba3c8858..df526e35 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -1,60 +1,80 @@ # LANL Darwin. A little bit of everything -# Must list which node you're compiling for: -# ampere for AMD/NVIDIA A100 nodes -# volta for x86/volta of all kinds -# Not working yet: -# arm-nv to compile for devkit ARM/NVIDIA nodes +# Must list which node you're compiling for, +# from the options below if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then module purge module load cmake + # Where we're going, we don't need system libraries + ARGS="$ARGS hdf5" + # Help Darwin find the right modules in automated jobs - if [[ "$ARGS" == *"cuda"* ]]; then + if [[ "$ARGS" == *"cuda"* && "$ARGS" == *"arm-"* ]]; then export MODULEPATH="/projects/darwin-nv/modulefiles/rhel8/aarch64:/projects/darwin-nv/modulefiles/rhel8/aarch64" fi - # Load modules based on first argument... - if [[ "$ARGS" == *"cuda"* ]]; then - if [[ "$ARGS" == *"gcc12"* ]]; then - module load cuda/12.0.0 openmpi gcc/12.1.0 - C_NATIVE=gcc - CXX_NATIVE=g++ - elif [[ "$ARGS" == *"gcc"* ]]; then - module load cuda openmpi gcc/10.2.0 - C_NATIVE=gcc - CXX_NATIVE=g++ - else - module load nvhpc/22.1 cuda/12.0.0 + # Load compiler... + if [[ "$ARGS" == *"gcc12"* ]]; then + module load openmpi gcc/12.2.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + elif [[ "$ARGS" == *"gcc10"* ]]; then + module load openmpi gcc/10.4.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + elif [[ "$ARGS" == *"gcc"* ]]; then + # Default GCC + module load openmpi gcc/13.1.0 + C_NATIVE=gcc + CXX_NATIVE=g++ + elif [[ "$ARGS" == *"aocc"* ]]; then + module load aocc openmpi + C_NATIVE=clang + CXX_NATIVE=clang++ + elif [[ "$ARGS" == *"nvhpc"* ]]; then + module load nvhpc + C_NATIVE="nvc" + CXX_NATIVE="nvc++" + # New NVHPC doesn't like CUDA_HOME + export NVHPC_CUDA_HOME="$CUDA_HOME" + unset CUDA_HOME + elif [[ "$ARGS" == *"icc"* ]]; then + module load intel-classic/2021.3.0 openmpi + C_NATIVE=icc + CXX_NATIVE=icpc + else + # Default: NVHPC if cuda else IntelLLVM + if [[ "$ARGS" == *"cuda"* ]]; then + module load nvhpc C_NATIVE="nvc" CXX_NATIVE="nvc++" # New NVHPC doesn't like CUDA_HOME export NVHPC_CUDA_HOME="$CUDA_HOME" unset CUDA_HOME + else + module load intel openmpi + C_NATIVE=icx + CXX_NATIVE=icpx fi + fi + + # ...any accelerator libraries... + if [[ "$ARGS" == *"cuda"* ]]; then + module load cuda/12.0.0 elif [[ "$ARGS" == *"hip"* ]]; then module load rocm/5.4.3 #openmpi/5.0.0rc11-gcc_13.1.0 source ~/libs/env.sh C_NATIVE=hipcc CXX_NATIVE=hipcc export CXXFLAGS="-fopenmp $CXXFLAGS" - else - if [[ "$ARGS" == *"gcc"* ]]; then - module load openmpi gcc/10.2.0 - C_NATIVE=gcc - CXX_NATIVE=g++ - export CXXFLAGS="-fno-builtin-memset" - else - module load openmpi intel - C_NATIVE=icx - CXX_NATIVE=icpx - fi fi - # ...and set architecture according to second. + # ...and set architecture # These are orthogonal to above, so long as the hardware # supports the paradigm + # Note this also specifies cores to use for compiling NPROC=$(($(nproc) / 2)) if [[ "$ARGS" == *"arm-ampere"* ]]; then HOST_ARCH="ARMV81" @@ -86,6 +106,18 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then HOST_ARCH="HSW" MPI_NUM_PROCS=1 NODE_SLICE=1 + elif [[ "$ARGS" == *"skx"* ]]; then + HOST_ARCH="SKX" + MPI_NUM_PROCS=${MPI_NUM_PROCS:-$NPROC} + NODE_SLICE=${MPI_NUM_PROCS:-$NPROC} + elif [[ "$ARGS" == *"zen2"* ]]; then + HOST_ARCH=ZEN2 + MPI_NUM_PROCS=1 + NODE_SLICE=1 + elif [[ "$ARGS" == *"zen3"* ]]; then + HOST_ARCH=ZEN3 + MPI_NUM_PROCS=1 + NODE_SLICE=1 elif [[ "$ARGS" == *"mi250"* ]]; then HOST_ARCH=ZEN3 DEVICE_ARCH=VEGA90A From f2b4016ba8649e175818d3661c79866ec30236e1 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 12:43:52 -0600 Subject: [PATCH 125/219] Re-enable OpenMP runtime vars only for CPU runs --- run.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/run.sh b/run.sh index bce8bc0e..6a586da8 100755 --- a/run.sh +++ b/run.sh @@ -12,12 +12,6 @@ MPI_EXE=${MPI_EXE:-} MPI_NUM_PROCS=${MPI_NUM_PROCS:-1} MPI_EXTRA_ARGS=${MPI_EXTRA_ARGS:-} -# Default OpenMP directives: use all available threads -#export OMP_PROC_BIND=${OMP_PROC_BIND:-spread} -#export OMP_PLACES=${OMP_PLACES:-threads} -# Force a number of OpenMP threads if it doesn't autodetect -#export OMP_NUM_THREADS=28 - ### General run script # Map each MPI rank to one device with Kokkos @@ -38,6 +32,11 @@ elif [ -f $KHARMA_DIR/kharma.hip ]; then EXE_NAME=kharma.hip elif [ -f $KHARMA_DIR/kharma.host ]; then EXE_NAME=kharma.host + # Enable OpenMP to use all threads only where not counterproductive + export OMP_PROC_BIND=${OMP_PROC_BIND:-spread} + export OMP_PLACES=${OMP_PLACES:-threads} + # Force a number of OpenMP threads if it doesn't autodetect + #export OMP_NUM_THREADS=${OMP_NUM_THREADS:-28} else echo "KHARMA executable not found!" exit From ff836130ec585a96dc6ac62a44bd510a02dd5bc6 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 13:12:47 -0600 Subject: [PATCH 126/219] Chicoma compile/run touch-ups --- bin/select_gpu_chicoma | 3 +++ bin/{mpi_gpu_wrap => select_gpu_polaris} | 0 machines/chicoma.sh | 25 ++++++++++++------------ scripts/batch/polaris.qsub | 2 +- scripts/batch/scaling_polaris.qsub | 5 +++-- 5 files changed, 20 insertions(+), 15 deletions(-) create mode 100755 bin/select_gpu_chicoma rename bin/{mpi_gpu_wrap => select_gpu_polaris} (100%) diff --git a/bin/select_gpu_chicoma b/bin/select_gpu_chicoma new file mode 100755 index 00000000..e4033c85 --- /dev/null +++ b/bin/select_gpu_chicoma @@ -0,0 +1,3 @@ +#!/bin/bash +export CUDA_VISIBLE_DEVICES=$SLURM_LOCALID +exec $* diff --git a/bin/mpi_gpu_wrap b/bin/select_gpu_polaris similarity index 100% rename from bin/mpi_gpu_wrap rename to bin/select_gpu_polaris diff --git a/machines/chicoma.sh b/machines/chicoma.sh index 41fc59b6..8f6de402 100644 --- a/machines/chicoma.sh +++ b/machines/chicoma.sh @@ -1,7 +1,7 @@ # LANL Machines: HPC and IC # Chicoma -if [[ "$HOST" == "ch-fe"* ]]; then +if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then HOST_ARCH="ZEN2" # Cray environments get confused easy @@ -11,8 +11,6 @@ if [[ "$HOST" == "ch-fe"* ]]; then export CRAY_CPU_TARGET="x86-64" if [[ "$ARGS" == *"cuda"* ]]; then DEVICE_ARCH="AMPERE80" - # System HDF5 can't use compression - EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" # Runtime MPI_NUM_PROCS=4 if [[ "$ARGS" == *"gnu"* ]]; then @@ -20,21 +18,24 @@ if [[ "$HOST" == "ch-fe"* ]]; then elif [[ "$ARGS" == *"intel"* ]]; then module load PrgEnv-intel elif [[ "$ARGS" == *"nvc++"* ]]; then - module load PrgEnv-nvhpc cray-hdf5-parallel + module load PrgEnv-nvhpc EXTRA_FLAGS="-DCMAKE_CUDA_COMPILER=$HOME/bin/nvc++-wrapper -DCMAKE_CUDA_COMPILER_ID=NVHPC -DCMAKE_CUDA_COMPILER_VERSION=11.6 $EXTRA_FLAGS" else - module load PrgEnv-nvhpc cray-hdf5-parallel + module load PrgEnv-nvhpc fi + # GPU runtime opts + MPI_NUM_PROCS=4 + MPI_EXTRA_ARGS="--cpu-bind=mask_cpu:0x0*16,0x1*16,0x2*16,0x3*16 $SOURCE_DIR/bin/select-gpu" + unset OMP_NUM_THREADS + unset OMP_PROC_BIND + unset OMP_PLACES else module load PrgEnv-aocc fi - module load cmake + module load cray-hdf5-parallel cmake + # System HDF5 can't use compression + EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" - # Runtime - MPI_NUM_PROCS=4 + # Runtime opts MPI_EXE=srun - MPI_EXTRA_ARGS="--cpu-bind=mask_cpu:0x0*16,0x1*16,0x2*16,0x3*16 ~/bin/select-gpu" - unset OMP_NUM_THREADS - unset OMP_PROC_BIND - unset OMP_PLACES fi diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index e2cb07ec..5cb698d5 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -8,7 +8,7 @@ #PBS -l filesystems=home:grand KHARMA_DIR=~/kharma-dev -WRAPPER=$KHARMA_DIR/bin/mpi_gpu_wrap +WRAPPER=$KHARMA_DIR/bin/select_gpu_polaris KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" # Print ranks diff --git a/scripts/batch/scaling_polaris.qsub b/scripts/batch/scaling_polaris.qsub index efda6705..3e975aac 100755 --- a/scripts/batch/scaling_polaris.qsub +++ b/scripts/batch/scaling_polaris.qsub @@ -17,6 +17,7 @@ DO_STRONG=true DO_WEAK=true KHARMA_DIR=~/kharma-dev +WRAPPER=$KHARMA_DIR/bin/select_gpu_polaris # Gotta specify this inline since bsub doesn't do arguments PARFILE=~/kharma-dev/pars/scaling_torus.par @@ -78,7 +79,7 @@ if [[ $DO_STRONG == "true" ]]; then echo "cycle=100 Running ${size}x${size}x${size} cubed problem with KHARMA on $gpus GPUs (blocksize ${msize1}x${msize2}x${msize3})" - mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $KHARMA_DIR/bin/mpi_gpu_wrap \ + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $WRAPPER \ $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ parthenon/meshblock/nx1=$msize1 parthenon/meshblock/nx2=$msize2 parthenon/meshblock/nx3=$msize3 @@ -150,7 +151,7 @@ if [[ $DO_WEAK == "true" ]]; then nblock=$(( $mul1 * $mul2 * $mul3 )) echo "cycle=100 Running $size per node problem with KHARMA on $gpus GPUs (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" - mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $KHARMA_DIR/bin/mpi_gpu_wrap \ + mpiexec -n $gpus --ppn $NRANKS --depth 8 --cpu-bind depth --env OMP_NUM_THREADS=1 -env OMP_PLACES=threads $WRAPPER \ $KHARMA_DIR/kharma.cuda -i $PARFILE parthenon/time/nlim=102 \ parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size From a88c3135232c990a1ea8736c5c6f24395045b163 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 13:16:29 -0600 Subject: [PATCH 127/219] Stop printing code version on every rank --- kharma/main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kharma/main.cpp b/kharma/main.cpp index fa3d384b..c27fab91 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -123,6 +123,11 @@ int main(int argc, char *argv[]) pman.app_input->boundary_conditions[parthenon::BoundaryFace::inner_x3] = KBoundaries::ApplyBoundaryTemplate; pman.app_input->boundary_conditions[parthenon::BoundaryFace::outer_x3] = KBoundaries::ApplyBoundaryTemplate; + // Initialize Parthenon for MPI (also Kokkos, parses command line, etc.) + Flag("ParthenonInit"); + auto manager_status = pman.ParthenonInitEnv(argc, argv); + EndFlag(); + if(MPIRank0()) { // Always print the version header, because it's fun // TODO(BSP) proper banner w/refs, names @@ -138,9 +143,8 @@ int main(int argc, char *argv[]) std::cout << std::endl; } - // Parthenon init includes Kokkos, MPI, parses parameters & cmdline - Flag("ParthenonInit"); - auto manager_status = pman.ParthenonInitEnv(argc, argv); + // Check the Parthenon init return code, initialize packages/mesh + Flag("InitPackagesAndMesh"); if (manager_status == ParthenonStatus::complete) { pman.ParthenonFinalize(); return 0; From 43a32507b80acaaa573c5977d0b6066bab543965 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 14:19:29 -0600 Subject: [PATCH 128/219] Fix build w/NVHPC on Chicoma --- machines/chicoma.sh | 8 ++++---- run.sh | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/machines/chicoma.sh b/machines/chicoma.sh index 8f6de402..3e20d94a 100644 --- a/machines/chicoma.sh +++ b/machines/chicoma.sh @@ -11,10 +11,8 @@ if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then export CRAY_CPU_TARGET="x86-64" if [[ "$ARGS" == *"cuda"* ]]; then DEVICE_ARCH="AMPERE80" - # Runtime - MPI_NUM_PROCS=4 if [[ "$ARGS" == *"gnu"* ]]; then - module load PrgEnv-gnu cpe-cuda cuda + module load PrgEnv-gnu elif [[ "$ARGS" == *"intel"* ]]; then module load PrgEnv-intel elif [[ "$ARGS" == *"nvc++"* ]]; then @@ -23,9 +21,10 @@ if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then else module load PrgEnv-nvhpc fi + module load cpe-cuda cuda craype-accel-nvidia80 # GPU runtime opts MPI_NUM_PROCS=4 - MPI_EXTRA_ARGS="--cpu-bind=mask_cpu:0x0*16,0x1*16,0x2*16,0x3*16 $SOURCE_DIR/bin/select-gpu" + MPI_EXTRA_ARGS="--cpu-bind=mask_cpu:0x0*16,0x1*16,0x2*16,0x3*16 $SOURCE_DIR/bin/select_gpu_chicoma" unset OMP_NUM_THREADS unset OMP_PROC_BIND unset OMP_PLACES @@ -35,6 +34,7 @@ if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then module load cray-hdf5-parallel cmake # System HDF5 can't use compression EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" + export MPICH_GPU_SUPPORT_ENABLED=1 # Runtime opts MPI_EXE=srun diff --git a/run.sh b/run.sh index 6a586da8..275f5968 100755 --- a/run.sh +++ b/run.sh @@ -50,6 +50,7 @@ fi # Load environment from the same files as the compile process HOST=$(hostname -f) ARGS=$(cat $KHARMA_DIR/make_args) +SOURCE_DIR=$(dirname "$(readlink -f "$0")") for machine in $KHARMA_DIR/machines/*.sh do source $machine From 3c3115165292c697a60f70200c05ac2567db806d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 25 Sep 2023 15:09:58 -0600 Subject: [PATCH 129/219] Definitely the last Chicoma compile touch-ups --- bin/select_gpu_chicoma | 1 + machines/chicoma.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/bin/select_gpu_chicoma b/bin/select_gpu_chicoma index e4033c85..2e6ca0f0 100755 --- a/bin/select_gpu_chicoma +++ b/bin/select_gpu_chicoma @@ -1,3 +1,4 @@ #!/bin/bash +export OMP_NUM_THREADS=1 export CUDA_VISIBLE_DEVICES=$SLURM_LOCALID exec $* diff --git a/machines/chicoma.sh b/machines/chicoma.sh index 3e20d94a..3efe4e61 100644 --- a/machines/chicoma.sh +++ b/machines/chicoma.sh @@ -3,6 +3,7 @@ # Chicoma if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then HOST_ARCH="ZEN2" + NPROC=64 # Cray environments get confused easy # Make things as simple as possible From 6356e2cdb1675e79346e818877e382ea3fc46998 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 11:58:27 -0600 Subject: [PATCH 130/219] Allow more ghosts, fix compiler warnings, comments --- kharma/driver/kharma_driver.cpp | 11 +++++++++++ kharma/flux/get_flux.hpp | 4 ++++ kharma/kharma.cpp | 14 +++++--------- kharma/main.cpp | 2 +- kharma/prob/bondi.cpp | 2 +- kharma/prob/hdf5_utils.cpp | 10 +++++----- kharma/prob/resize_restart.cpp | 2 +- 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index d946e281..84387958 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -83,23 +83,34 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std std::string recon = pin->GetOrAddString("driver", "reconstruction", grmhd_recon_option); bool lower_edges = pin->GetOrAddBoolean("driver", "lower_edges", false); bool lower_poles = pin->GetOrAddBoolean("driver", "lower_poles", false); + int stencil = 0; if (recon == "donor_cell") { params.Add("recon", KReconstruction::Type::donor_cell); + stencil = 1; } else if (recon == "linear_vl") { params.Add("recon", KReconstruction::Type::linear_vl); + stencil = 3; } else if (recon == "linear_mc") { params.Add("recon", KReconstruction::Type::linear_mc); + stencil = 3; } else if (recon == "weno5_lower_edges" || (recon == "weno5" && lower_edges)) { params.Add("recon", KReconstruction::Type::weno5_lower_edges); + stencil = 5; } else if (recon == "weno5_lower_poles" || (recon == "weno5" && lower_poles)) { params.Add("recon", KReconstruction::Type::weno5_lower_poles); + stencil = 5; } else if (recon == "weno5") { params.Add("recon", KReconstruction::Type::weno5); + stencil = 5; } else { std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } + // Warn if using less than 3 ghost zones w/WENO etc, 2 w/Linear, etc. + if (Globals::nghost < (stencil/2 + 1)) { + throw std::runtime_error("Not enough ghost zones for specified reconstruction!"); + } // Field flags related to driver operation are defined outside any particular driver // When using the Implicit package we need to globally distinguish implicitly and explicitly-updated variables diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index b229cce1..fc77b63b 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -196,6 +196,10 @@ inline TaskStatus GetFlux(MeshData *md) ); } + // Now that this is split, we add the biggest TODO in KHARMA + // TODO per-package prim_to_flux? Is that slower? + // At least, we need to template on vchar/stress-energy T type + Flag("GetFlux_"+std::to_string(dir)+"_left"); parthenon::par_for_outer(DEFAULT_OUTER_LOOP_PATTERN, "calc_flux_left", pmb0->exec_space, flux_scratch_bytes, scratch_level, block.s, block.e, b.ks, b.ke, b.js, b.je, diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 03ef7d80..6d68279e 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -143,15 +143,11 @@ void KHARMA::FixParameters(ParameterInput *pin) { Flag("Fixing parameters"); // Parthenon sets 2 ghost zones as a default. - // We can't override that default while allowing a file-specified value. - // Fine for now because we crash with 2. (Flux CT) - // TODO add under different name? Better precedence/origin code? - pin->SetInteger("parthenon/mesh", "nghost", 4); - Globals::nghost = pin->GetInteger("parthenon/mesh", "nghost"); - // Warn if using less than 4 ghost zones in any circumstances, it's still not tested well - // if (Globals::nghost < 4) { - // std::cerr << "WARNING: Using less than 4 ghost zones is untested!" << std::endl; - // } + // We set a better default with our own parameter, and inform Parthenon. + // This means that ONLY driver/nghost will be respected + // Driver::Initialize will check we set enough for our reconstruction + Globals::nghost = pin->GetOrAddInteger("driver", "nghost", 4); + pin->SetInteger("parthenon/mesh", "nghost", Globals::nghost); // If we're restarting (not via Parthenon), read the restart file to get most parameters std::string prob = pin->GetString("parthenon/job", "problem_id"); diff --git a/kharma/main.cpp b/kharma/main.cpp index c27fab91..7052099a 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -208,7 +208,7 @@ int main(int argc, char *argv[]) // Begin code block to ensure driver is cleaned up { std::string driver_type = pmesh->packages.Get("Driver")->Param("type"); - std::cout << "Initializing and running " << driver_type << " driver" << std::endl; + if (MPIRank0()) std::cout << "Running " << driver_type << " driver" << std::endl; // Pull out things we need to give the driver auto pin = pman.pinput.get(); // All parameters in the input file or command line diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index 1cd0af60..d97d448b 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -92,7 +92,7 @@ TaskStatus InitializeBondi(std::shared_ptr>& rc, ParameterIn SetBondi(rc); } - // Default Bondi boundariy conditions: reset the outer boundary using our set function. + // Default Bondi boundary conditions: reset the outer boundary using our set function. // Register the callback to replace value from boundaries.cpp, & record the change in pin. auto bound_pkg = pmb->packages.Get("Boundaries"); if (pin->GetOrAddBoolean("bondi", "set_outer_bound", !outer_dirichlet)) { diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/hdf5_utils.cpp index a183beb2..6caaed7b 100644 --- a/kharma/prob/hdf5_utils.cpp +++ b/kharma/prob/hdf5_utils.cpp @@ -397,11 +397,11 @@ int hdf5_read_array(void *data, const char *name, size_t rank, if(DEBUG) { fprintf(stderr,"Reading arr %s:\n", path); - fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); - fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); - fprintf(stderr,"File read size: %llu %llu %llu %llu\n", fcount[0], fcount[1], fcount[2], fcount[3]); - fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); - fprintf(stderr,"Memory start: %llu %llu %llu %llu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); + fprintf(stderr,"Total file size: %lu %lu %lu %lu\n", fdims[0], fdims[1], fdims[2], fdims[3]); + fprintf(stderr,"File start: %lu %lu %lu %lu\n", fstart[0], fstart[1], fstart[2], fstart[3]); + fprintf(stderr,"File read size: %lu %lu %lu %lu\n", fcount[0], fcount[1], fcount[2], fcount[3]); + fprintf(stderr,"Total memory size: %lu %lu %lu %lu\n", mdims[0], mdims[1], mdims[2], mdims[3]); + fprintf(stderr,"Memory start: %lu %lu %lu %lu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index 6b3747f5..e4f4f636 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -262,7 +262,7 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI pin->GetInteger("parthenon/mesh", "nx2") != n2tot || pin->GetInteger("parthenon/mesh", "nx3") != n3tot) { printf("Mesh size does not match!\n"); - printf("[%d %d %d] vs [%llu %llu %llu]", + printf("[%d %d %d] vs [%lu %lu %lu]", pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), pin->GetInteger("parthenon/mesh", "nx3"), From f7fc036f046482c57d02b13890596b7f1ad5edf4 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 12:11:29 -0600 Subject: [PATCH 131/219] Face CT fixed & improved Still an issue when using div-preserving prolongation on faces when exchanging boundaries. Luckily this isn't necessary since we sync EMFs, so current default is Parthenon's averaging scheme. This means only SMR sims for now! Changes: DivB measurement fixed for non-symmetric face sizes dB update fixed to include geometry flux average -> emf fixed to exclude geometry Fixed GS'05 "c" algorithm (formerly "sg09"), corrections were too small Add GS'05 "0" algo, for 3: bs99, gs05_c, gs05_0 (default bs99 for now) Fixed magnetic field output to always show small numbers correctly --- kharma/b_ct/b_ct.cpp | 200 +++++++++++++++++++-------------- kharma/b_ct/b_ct.hpp | 150 ++++++++++++------------- kharma/b_flux_ct/b_flux_ct.cpp | 9 +- pars/orszag_tang_face_ct.par | 10 +- pars/orszag_tang_smr.par | 79 +++++++++++++ 5 files changed, 276 insertions(+), 172 deletions(-) create mode 100644 pars/orszag_tang_smr.par diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index dd5ba72c..485f349a 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -67,12 +67,18 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared Real kill_on_divb_over = pin->GetOrAddReal("b_field", "kill_on_divb_over", 1.e-3); params.Add("kill_on_divb_over", kill_on_divb_over); - // Currently bs99, sg09 - // TODO LDZ04, LDZ07, other GS? - std::string ct_scheme = pin->GetOrAddString("b_field", "ct_scheme", "sg09"); + // Currently bs99, gs05_c, gs05_0 + // TODO gs05_alpha, LDZ04 UCT1, LDZ07 UCT2 + std::string ct_scheme = pin->GetOrAddString("b_field", "ct_scheme", "bs99"); params.Add("ct_scheme", ct_scheme); - - // Add a reducer for divB to params + // Use the default Parthenon prolongation operator, rather than the divergence-preserving one + // This relies entirely on the EMF communication for preserving the divergence + bool lazy_prolongation = pin->GetOrAddBoolean("b_field", "lazy_prolongation", true); + // Need to preserve divergence if you refine/derefine during sim i.e. AMR + if (lazy_prolongation && pin->GetString("parthenon/mesh", "refinement") == "adaptive") + throw std::runtime_error("Cannot use non-preserving prolongation in AMR!"); + + // Add a reducer object (MPI communicator) for divB to params params.Add("divb_reducer", AllReduce()); // FIELDS @@ -87,7 +93,8 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared auto m = Metadata(flags_prim_f); pkg->AddField("prims.fB", m); m = Metadata(flags_cons_f); - m.RegisterRefinementOps(); + if (!lazy_prolongation) + m.RegisterRefinementOps(); pkg->AddField("cons.fB", m); // Cell-centered versions. Needed for BS, not for other schemes. @@ -103,12 +110,11 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared pkg->AddField("cons.B", m); // EMF on edges. - // TODO only sync when needed std::vector flags_emf = {Metadata::Real, Metadata::Edge, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}; m = Metadata(flags_emf); pkg->AddField("B_CT.emf", m); - if (ct_scheme == "sg09") { + if (ct_scheme != "bs99") { std::vector flags_emf_c = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_emf_c, s_vector); pkg->AddField("B_CT.cemf", m); @@ -210,90 +216,111 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // Figure out indices const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); std::string scheme = pmesh->packages.Get("B_CT")->Param("ct_scheme"); - if (scheme == "bs99") { - // Calculate circulation by averaging fluxes (BS88) - auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); - pmb0->par_for("B_CT_emf_BS", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // TODO will we need gdet/cell length here? - const auto& G = B_U.GetCoords(bl); - if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = G.Dxc<1>(i) * - 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) - - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)); - emf_pack(bl, E2, 0, k, j, i) = G.Dxc<2>(j) * - 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)); - } - emf_pack(bl, E3, 0, k, j, i) = - 0.25*(G.FaceArea<1>(k, j - 1, i) * B_U(bl).flux(X1DIR, V2, k, j - 1, i) / G.Dxc<2>(j-1) - + G.FaceArea<1>(k, j, i) * B_U(bl).flux(X1DIR, V2, k, j, i) / G.Dxc<2>(j) - - G.FaceArea<2>(k, j, i - 1) * B_U(bl).flux(X2DIR, V1, k, j, i - 1) / G.Dxc<1>(i-1) - - G.FaceArea<2>(k, j, i) * B_U(bl).flux(X2DIR, V1, k, j, i) / G.Dxc<1>(i)); + + // Calculate circulation by averaging fluxes + // This is the base of most other schemes, which make corrections + // It is the entirety of B&S '99 + auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); + pmb0->par_for("B_CT_emf_BS", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + // The basic EMF per length along edges is the B field flux + // We use this form rather than multiply by edge length here, + // since the default restriction op averages values + const auto& G = B_U.GetCoords(bl); + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) = + 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i) + B_U(bl).flux(X2DIR, V3, k, j, i) + - B_U(bl).flux(X3DIR, V2, k, j - 1, i) - B_U(bl).flux(X3DIR, V2, k, j, i)); + emf_pack(bl, E2, 0, k, j, i) = + 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1) + B_U(bl).flux(X3DIR, V1, k, j, i) + - B_U(bl).flux(X1DIR, V3, k - 1, j, i) - B_U(bl).flux(X1DIR, V3, k, j, i)); } - ); - } else if (scheme == "sg09") { - // Average fluxes and derivatives (SG09) + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); + } + ); + + if (scheme == "bs99") { + // Nothing more to do + } else if (scheme == "gs05_0" || scheme == "gs05_c") { + // Additional terms for Stone & Gardiner '09 + // Average fluxes and derivatives auto& uvec = md->PackVariables(std::vector{"prims.uvec"}); auto& emfc = md->PackVariables(std::vector{"B_CT.cemf"}); auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); auto& B_P = md->PackVariables(std::vector{"prims.B"}); // emf in center == -v x B - pmb0->par_for("B_CT_emf_GS09", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + const IndexRange3 bc = KDomain::GetRange(md, IndexDomain::entire); + pmb0->par_for("B_CT_emfc", block.s, block.e, bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { VLOOP emfc(bl, v, k, j, i) = 0.; VLOOP3 emfc(bl, x, k, j, i) -= antisym(v, w, x) * uvec(bl, v, k, j, i) * B_U(bl, w, k, j, i); } ); - // Get primitive velocity at face (on right side) (TODO do we need some average?) - auto& uvecf = md->PackVariables(std::vector{"Flux.vr"}); - - pmb0->par_for("B_CT_emf_GS09", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - // TODO will we need gdet/cell length here? - const auto& G = B_U.GetCoords(bl); - - // "simple" flux + upwinding method, Stone & Gardiner '09 but also in Stone+08 etc. - // Upwinded differences take in order (1-indexed): - // 1. EMF component direction to calculate - // 2. Direction of derivative - // 3. Direction of upwinding - // ...then zone number... - // and finally, a boolean indicating a leftward (e.g., i-3/4) vs rightward (i-1/4) position - if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = G.Dxc<1>(i) * - 0.25*(B_U(bl).flux(X2DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) + B_U(bl).flux(X2DIR, V3, k, j, i)/G.Dxc<3>(k) - - B_U(bl).flux(X3DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) - B_U(bl).flux(X3DIR, V2, k, j, i)/G.Dxc<2>(j)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); - emf_pack(bl, E2, 0, k, j, i) = G.Dxc<2>(j) * - 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) + B_U(bl).flux(X3DIR, V1, k, j, i)/G.Dxc<1>(i) - - B_U(bl).flux(X1DIR, V3, k - 1, j, i)/G.Dxc<3>(k-1) - B_U(bl).flux(X1DIR, V3, k, j, i)/G.Dxc<3>(k)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, true)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); + if (scheme == "gs05_0") { + pmb0->par_for("B_CT_emf_GS05_0", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = B_U.GetCoords(bl); + // Just subtract centered emf from twice the face version + // More stable for planar flows even without anything fancy + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) = 2 * emf_pack(bl, E1, 0, k, j, i) + - 0.25*(emfc(bl, V1, k, j, i) + emfc(bl, V1, k, j - 1, i) + + emfc(bl, V1, k, j - 1, i) + emfc(bl, V1, k - 1, j - 1, i)); + emf_pack(bl, E2, 0, k, j, i) = 2 * emf_pack(bl, E2, 0, k, j, i) + - 0.25*(emfc(bl, V2, k, j, i) + emfc(bl, V2, k, j, i - 1) + + emfc(bl, V2, k - 1, j, i) + emfc(bl, V2, k - 1, j, i - 1)); + } + emf_pack(bl, E3, 0, k, j, i) = 2 * emf_pack(bl, E3, 0, k, j, i) + - 0.25*(emfc(bl, V3, k, j, i) + emfc(bl, V3, k, j, i - 1) + + emfc(bl, V3, k, j - 1, i) + emfc(bl, V3, k, j - 1, i - 1)); } - emf_pack(bl, E3, 0, k, j, i) = G.Dxc<3>(k) * - 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i)/G.Dxc<2>(j-1) + B_U(bl).flux(X1DIR, V2, k, j, i)/G.Dxc<2>(j) - - B_U(bl).flux(X2DIR, V1, k, j, i - 1)/G.Dxc<1>(i-1) - B_U(bl).flux(X2DIR, V1, k, j, i)/G.Dxc<1>(i)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, true)) - + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, false) - - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, true)); - } - ); + ); + } else if (scheme == "gs05_c") { + // Get primitive velocity at face (on right side) (TODO do we need some average?) + auto& uvecf = md->PackVariables(std::vector{"Flux.vr"}); + + pmb0->par_for("B_CT_emf_GS05_c", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = B_U.GetCoords(bl); + + // "simple" flux + upwinding method, Stone & Gardiner '09 but also in Stone+08 etc. + // Upwinded differences take in order (1-indexed): + // 1. EMF component direction to calculate + // 2. Direction of derivative + // 3. Direction of upwinding + // ...then zone number... + // and finally, a boolean indicating a leftward (e.g., i-3/4) vs rightward (i-1/4) position + if (ndim > 2) { + emf_pack(bl, E1, 0, k, j, i) += + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 2, 3, k, j, i, true)); + emf_pack(bl, E2, 0, k, j, i) += + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 1, 3, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 2, 3, 1, k, j, i, true)); + } + emf_pack(bl, E3, 0, k, j, i) += + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 2, 1, k, j, i, true)) + + 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, false) + - upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 3, 1, 2, k, j, i, true)); + } + ); + } } else { - throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, sg09"); + throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, gs05_0, gs05_c!"); } return TaskStatus::complete; } @@ -319,25 +346,33 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) pmb0->par_for("B_CT_Circ_1", block.s, block.e, b.ks, b.ke, b.js, b.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F1, 0, k, j, i) = (emf_pack(bl, E3, 0, k, j + 1, i) - emf_pack(bl, E3, 0, k, j, i))*G.FaceArea<1>(k, j, i); + dB_Uf_dt(bl, F1, 0, k, j, i) = (G.Volume(k, j + 1, i) * emf_pack(bl, E3, 0, k, j + 1, i) + - G.Volume(k, j, i) * emf_pack(bl, E3, 0, k, j, i)); if (ndim > 2) - dB_Uf_dt(bl, F1, 0, k, j, i) += (-emf_pack(bl, E2, 0, k + 1, j, i) + emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j); + dB_Uf_dt(bl, F1, 0, k, j, i) += (-G.Volume(k + 1, j, i) * emf_pack(bl, E2, 0, k + 1, j, i) + + G.Volume(k, j, i) * emf_pack(bl, E2, 0, k, j, i)); + dB_Uf_dt(bl, F1, 0, k, j, i) /= G.Volume(k, j, i); } ); pmb0->par_for("B_CT_Circ_2", block.s, block.e, b.ks, b.ke, b1.js, b1.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F2, 0, k, j, i) = (-emf_pack(bl, E3, 0, k, j, i + 1) + emf_pack(bl, E3, 0, k, j, i))*G.FaceArea<2>(k, j, i); + dB_Uf_dt(bl, F2, 0, k, j, i) = (-G.Volume(k, j, i + 1) * emf_pack(bl, E3, 0, k, j, i + 1) + + G.Volume(k, j, i) * emf_pack(bl, E3, 0, k, j, i)); if (ndim > 2) - dB_Uf_dt(bl, F2, 0, k, j, i) += (emf_pack(bl, E1, 0, k + 1, j, i) - emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); + dB_Uf_dt(bl, F2, 0, k, j, i) += (G.Volume(k + 1, j, i) * emf_pack(bl, E1, 0, k + 1, j, i) + - G.Volume(k, j, i) * emf_pack(bl, E1, 0, k, j, i)); + dB_Uf_dt(bl, F2, 0, k, j, i) /= G.Volume(k, j, i); } ); if (ndim > 2) { pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F3, 0, k, j, i) = (emf_pack(bl, E2, 0, k, j, i + 1) - emf_pack(bl, E2, 0, k, j, i))/G.Dxc<2>(j) - + (-emf_pack(bl, E1, 0, k, j + 1, i) + emf_pack(bl, E1, 0, k, j, i))/G.Dxc<1>(i); + dB_Uf_dt(bl, F3, 0, k, j, i) = (G.Volume(k, j, i + 1) * emf_pack(bl, E2, 0, k, j, i + 1) + - G.Volume(k, j, i) * emf_pack(bl, E2, 0, k, j, i) + - G.Volume(k, j + 1, i) * emf_pack(bl, E1, 0, k, j + 1, i) + + G.Volume(k, j, i) * emf_pack(bl, E1, 0, k, j, i)) / G.Volume(k, j, i); } ); } @@ -442,12 +477,13 @@ TaskStatus B_CT::PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) // Since this is in the history file now, I don't bother printing it // unless we're being verbose. It's not costly to calculate though - if (pmb0->packages.Get("Globals")->Param("verbose") >= 1) { + const bool print = pmb0->packages.Get("Globals")->Param("verbose") >= 1; + if (print || kill_on_large_divb) { // Calculate the maximum from/on all nodes const double divb_max = B_CT::GlobalMaxDivB(md); // Print on rank zero - if (MPIRank0()) { - std::cout << "Max DivB: " << divb_max << std::endl; + if (MPIRank0() && print) { + printf("Max DivB: %g\n", divb_max); // someday I'll learn stream options } if (kill_on_large_divb) { if (divb_max > pmb0->packages.Get("B_CT")->Param("kill_on_divb_over")) diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index 6e4ec96f..319e455f 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -123,14 +123,12 @@ void CalcDivB(MeshData *md, std::string divb_field_name="divB"); template KOKKOS_INLINE_FUNCTION Real face_div(const GRCoordinates &G, Global &v, const int &ndim, const int &k, const int &j, const int &i) { - Real du = (v(F1, 0, k, j, i + 1) - v(F1, 0, k, j, i)); - if (ndim > 1) { - du += (v(F2, 0, k, j + 1, i) - v(F2, 0, k, j, i)); - } - if (ndim > 2) { - du += (v(F3, 0, k + 1, j, i) - v(F3, 0, k, j, i)); - } - return du / G.CellVolume(k, j, i); + Real du = (v(F1, 0, k, j, i + 1) * G.Volume(k, j, i + 1) - v(F1, 0, k, j, i) * G.Volume(k, j, i)); + if (ndim > 1) + du += (v(F2, 0, k, j + 1, i) * G.Volume(k, j + 1, i) - v(F2, 0, k, j, i) * G.Volume(k, j, i)); + if (ndim > 2) + du += (v(F3, 0, k + 1, j, i) * G.Volume(k + 1, j, i) - v(F3, 0, k, j, i) * G.Volume(k, j, i)); + return du / G.Volume(k, j, i); } KOKKOS_INLINE_FUNCTION void curl_3D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, @@ -138,6 +136,7 @@ KOKKOS_INLINE_FUNCTION void curl_3D(const GRCoordinates& G, const GridVector& A, { // Take a face-ct step from the corner potentials. // This needs to be 3D because post-tilt A may not point in the phi direction only + // TODO TODO averages probably not physical, think about it // A3,2 derivative const Real A3c2f = (A(V3, k, j + 1, i) + A(V3, k + 1, j + 1, i)) / 2; @@ -145,33 +144,30 @@ KOKKOS_INLINE_FUNCTION void curl_3D(const GRCoordinates& G, const GridVector& A, // A2,3 derivative const Real A2c3f = (A(V2, k + 1, j, i) + A(V2, k + 1, j + 1, i)) / 2; const Real A2c3b = (A(V2, k, j, i) + A(V2, k, j + 1, i)) / 2; - B_U(F1, 0, k, j, i) = (A3c2f - A3c2b) - (A2c3f - A2c3b); + B_U(F1, 0, k, j, i) = (A3c2f - A3c2b) / G.Dxc<2>(j) - (A2c3f - A2c3b) / G.Dxc<3>(k); // A1,3 derivative - const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 2; - const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1)) / 2; + const Real A1c3f = (A(V1, k + 1, j, i) + A(V1, k + 1, j, i + 1)) / 2; + const Real A1c3b = (A(V1, k, j, i) + A(V1, k, j, i + 1)) / 2; // A3,1 derivative - const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1)) / 2; - const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i)) / 2; - B_U(F2, 0, k, j, i) = (A1c3f - A1c3b) - (A3c1f - A3c1b); + const Real A3c1f = (A(V3, k, j, i + 1) + A(V3, k + 1, j, i + 1)) / 2; + const Real A3c1b = (A(V3, k, j, i) + A(V3, k + 1, j, i)) / 2; + B_U(F2, 0, k, j, i) = (A1c3f - A1c3b) / G.Dxc<3>(k) - (A3c1f - A3c1b) / G.Dxc<1>(i); // A2,1 derivative - const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1)) / 2; + const Real A2c1f = (A(V2, k, j, i + 1) + A(V2, k, j + 1, i + 1)) / 2; const Real A2c1b = (A(V2, k, j, i) + A(V2, k, j + 1, i)) / 2; // A1,2 derivative - const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 2; + const Real A1c2f = (A(V1, k, j + 1, i) + A(V1, k, j + 1, i + 1)) / 2; const Real A1c2b = (A(V1, k, j, i) + A(V1, k, j, i + 1)) / 2; - B_U(F3, 0, k, j, i) = (A2c1f - A2c1b) - (A1c2f - A1c2b); + B_U(F3, 0, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } KOKKOS_INLINE_FUNCTION void curl_2D(const GRCoordinates& G, const GridVector& A, const VariablePack& B_U, const int& k, const int& j, const int& i) { - // TODO why do these not need - // A3,2 derivative - B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)); - // A3,1 derivative - B_U(F2, 0, k, j, i) = - (A(V3, k, j, i + 1) - A(V3, k, j, i)); + B_U(F1, 0, k, j, i) = (A(V3, k, j + 1, i) - A(V3, k, j, i)) / G.Dxc<2>(j); // A3,2 derivative + B_U(F2, 0, k, j, i) = - (A(V3, k, j, i + 1) - A(V3, k, j, i)) / G.Dxc<1>(i); // A3,1 derivative; B_U(F3, 0, k, j, i) = 0.; } @@ -205,23 +201,26 @@ KOKKOS_INLINE_FUNCTION Real upwind_diff(const VariableFluxPack& B_U, const if (contact_vel > 0) { // Forward: difference at i - return return_sign * (emfc(0, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i)); + return return_sign * (emfc(comp-1, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i)); } else if (contact_vel < 0) { // Back: twice difference at i-1 - return return_sign * (emfc(0, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); + return return_sign * (emfc(comp-1, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); } else { // Half and half - return return_sign*0.5*(emfc(0, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i) + - emfc(0, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); + return return_sign*0.5*(emfc(comp-1, k_cent, j_cent, i_cent) - emf_sign * B_U.flux(dir, vdir-1, k, j, i) + + emfc(comp-1, k_cent_up, j_cent_up, i_cent_up) - emf_sign * B_U.flux(dir, vdir-1, k_up, j_up, i_up)); } } -// Only by formatting may the following be made even a little comprehensible. +// Only through formatting has the following been made even a little comprehensible. template -KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, int l, int m, int n, int fk, int fj, int fi) +KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, const Coordinates_t &coords, int l, int m, int n, int fk, int fj, int fi) { - // TODO compile-time error on misuse? + // Trivial directions + if constexpr (diff_face+1 > DIM) + return 0.; + // TODO compile-time error on misuse? (diff_face == diff_side etc) constexpr int df_is_k = 2*(diff_face == V3 && DIM > 2); constexpr int df_is_j = 2*(diff_face == V2 && DIM > 1); constexpr int df_is_i = 2*(diff_face == V1 && DIM > 0); @@ -231,18 +230,20 @@ KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, constexpr int of_is_k = (offset == V3 && DIM > 2); constexpr int of_is_j = (offset == V2 && DIM > 1); constexpr int of_is_i = (offset == V1 && DIM > 0); - // if(fi == 10 && fj == 10 && fk == 0) { - // fprintf(stderr, "F facediff dir %d sidediff dirr %d off dir %d\nadding terms %d %d %d, -%d %d %d, -%d %d %d, %d %d %d\n", - // diff_face, diff_side, offset, - // df_is_i+ds_is_i+of_is_i, df_is_j+ds_is_j+of_is_j, df_is_k+ds_is_k+of_is_k, - // ds_is_i+of_is_i, ds_is_j+of_is_j, ds_is_k+of_is_k, - // df_is_i+of_is_i, df_is_j+of_is_j, df_is_k+of_is_k, - // of_is_i , of_is_j , of_is_k); - // } - return fine(diff_face, l, m, n, fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) - - fine(diff_face, l, m, n, fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) - - fine(diff_face, l, m, n, fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) - + fine(diff_face, l, m, n, fk+of_is_k , fj+of_is_j , fi+of_is_i); + // if (fi == 56 && fj == 70) + // printf("I used dir %d offset %d %d %d, %d %d %d, %d %d %d, %d %d %d\n", diff_face+1, + // df_is_k+ds_is_k+of_is_k, df_is_j+ds_is_j+of_is_j, df_is_i+ds_is_i+of_is_i, + // ds_is_k+of_is_k , ds_is_j+of_is_j , ds_is_i+of_is_i, + // df_is_k+of_is_k , df_is_j+of_is_j , df_is_i+of_is_i, + // of_is_k , of_is_j , of_is_i); + return fine(diff_face, l, m, n, fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) + * coords.FaceArea(fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) + - fine(diff_face, l, m, n, fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) + * coords.FaceArea(fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) + - fine(diff_face, l, m, n, fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) + * coords.FaceArea(fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) + + fine(diff_face, l, m, n, fk+of_is_k , fj+of_is_j , fi+of_is_i) + * coords.FaceArea(fk+of_is_k , fj+of_is_j , fi+of_is_i); } struct ProlongateInternalOlivares { @@ -264,6 +265,9 @@ struct ProlongateInternalOlivares { // Definitely exit on what we can't handle if constexpr (el != TE::F1 && el != TE::F2 && el != TE::F3) return; + // Exit if we're computing a trivial direction + if constexpr ((el == TE::F3 && (DIM < 3)) || (el == TE::F2 && (DIM < 2))) + return; // Handle permutations "naturally." // Olivares et al. is fond of listing x1 versions which permute, @@ -272,67 +276,53 @@ struct ProlongateInternalOlivares { constexpr int next = (me+1) % 3; constexpr int third = (me+2) % 3; - // Exit if we're computing a trivial direction - if constexpr ((me == V3 && !(DIM > 2)) || (me == V2 && !(DIM > 1)) || (me == V1 && !(DIM > 0))) - return; - // Fine array, indices auto &fine = *pfine; const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; - // TODO can we handle this in Parthenon instead? - if ((el == TE::F1 && fi+2 > ib.s) || (el == TE::F2 && fj+2 > jb.s) || (el == TE::F3 && fk+2 > kb.s)) - return; - // Coefficients selecting a particular formula (see Olivares et al. 2019) // TODO options here. This corresponds to Cunningham, but we could have: // 1. differences of squares of zone dimesnions (Toth) // 2. heuristic based on flux difference of top vs bottom halves (Olivares) - //constexpr Real a[3] = {0., 0., 0.}; - const Real a[3] = {(SQR(coords.Dxc<2>(j)) - SQR(coords.Dxc<3>(k)))/(SQR(coords.Dxc<2>(j)) + SQR(coords.Dxc<3>(k))), - (SQR(coords.Dxc<3>(k)) - SQR(coords.Dxc<1>(i)))/(SQR(coords.Dxc<3>(k)) + SQR(coords.Dxc<1>(i))), - (SQR(coords.Dxc<1>(i)) - SQR(coords.Dxc<2>(j)))/(SQR(coords.Dxc<1>(i)) + SQR(coords.Dxc<2>(j)))}; + // constexpr Real a[3] = {0., 0., 0.}; + const Real a[3] = {(SQR(coords.Dxc<2>(fj)) - SQR(coords.Dxc<3>(fk))) / (SQR(coords.Dxc<2>(fj)) + SQR(coords.Dxc<3>(fk))), + (SQR(coords.Dxc<3>(fk)) - SQR(coords.Dxc<1>(fi))) / (SQR(coords.Dxc<3>(fk)) + SQR(coords.Dxc<1>(fi))), + (SQR(coords.Dxc<1>(fi)) - SQR(coords.Dxc<2>(fj))) / (SQR(coords.Dxc<1>(fi)) + SQR(coords.Dxc<2>(fj)))}; // Coefficients for each term evaluating the four sub-faces const Real coeff[4][4] = {{3 + a[next], 1 - a[next], 3 - a[third], 1 + a[third]}, - {3 + a[next], 1 - a[next], 1 + a[third], 3 - a[third]}, - {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, - {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; + {3 + a[next], 1 - a[next], 1 + a[third], 3 - a[third]}, + {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, + {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; constexpr int diff_k = (me == V3), diff_j = (me == V2), diff_i = (me == V1); - // if(fi == 10 && fj == 10 && fk == 0) { - // fprintf(stderr, "Prolongating %d %d %d EL %d, DIM %d\n", fi, fj, fk, static_cast(el), DIM); - // fprintf(stderr, "Differencing %d %d %d\n", diff_i, diff_j, diff_k); - // } // Iterate through the 4 sub-faces for (int elem=0; elem < 4; elem++) { // Make sure we can offset in other directions before doing so, though // TODO eliminate redundant work or template these so the compiler can? - const int off_i = (DIM > 0) ? elem%2*(me == V2) + elem/2*(me == V3) + (me == V1) : 0; - const int off_j = (DIM > 1) ? elem%2*(me == V3) + elem/2*(me == V1) + (me == V2) : 0; - const int off_k = (DIM > 2) ? elem%2*(me == V1) + elem/2*(me == V2) + (me == V3) : 0; + const int off_i = (DIM > 0) ? (elem%2)*(me == V2) + (elem/2)*(me == V3) + (me == V1) : 0; + const int off_j = (DIM > 1) ? (elem%2)*(me == V3) + (elem/2)*(me == V1) + (me == V2) : 0; + const int off_k = (DIM > 2) ? (elem%2)*(me == V1) + (elem/2)*(me == V2) + (me == V3) : 0; - fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = + fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = ( // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) - 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + - fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + - 1./16*(coeff[elem][0]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][1]*F(fine, l, m, n, fk, fj, fi) - + coeff[elem][2]*F(fine, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); - - // if(fi == 10 && fj == 10 && fk == 0 && me == V1) { - // fprintf(stderr, "Elem %d Offset %d %d %d set %g\n", elem, off_i, off_j, off_k, fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i)); - // fprintf(stderr, "Averaging faces %d %d %d and %d %d %d (%g & %g)\n", fi+off_i-diff_i, fj+off_j-diff_j, fk+off_k-diff_k, - // fi+off_i+diff_i, fj+off_j+diff_j, fk+off_k+diff_k, - // fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i), - // fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)); - // fprintf(stderr, "Coeffs %g %g %g %g\n", coeff[elem][0]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][1]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][2]*F(fine, l, m, n, fk, fj, fi), - // coeff[elem][3]*F(fine, l, m, n, fk, fj, fi)); - // } + 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + * coords.Volume(fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + + fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i) + * coords.Volume(fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + + 1./16*(coeff[elem][0]*F(fine, coords, l, m, n, fk, fj, fi) + + coeff[elem][1]*F(fine, coords, l, m, n, fk, fj, fi) + + coeff[elem][2]*F(fine, coords, l, m, n, fk, fj, fi) + + coeff[elem][3]*F(fine, coords, l, m, n, fk, fj, fi)) + ) / coords.Volume(fk+off_k, fj+off_j, fi+off_i); + //printf("%d %d\n", fi, fj); + // if (fi == 56 && fj == 70) + // printf("I used dir %d offset %d %d %d, %d %d %d\n", me+1, + // off_k-diff_k, off_j-diff_j, off_i-diff_i, + // off_k+diff_k, off_j+diff_j, off_i+diff_i); } } }; diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 1d17ce7c..9665e2f2 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -522,12 +522,15 @@ TaskStatus PrintGlobalMaxDivB(MeshData *md, bool kill_on_large_divb) // Since this is in the history file now, I don't bother printing it // unless we're being verbose. It's not costly to calculate though - if (pmb0->packages.Get("Globals")->Param("verbose") >= 1) { + const bool print = pmb0->packages.Get("Globals")->Param("verbose") >= 1; + if (print || kill_on_large_divb) { // Calculate the maximum from/on all nodes const double divb_max = B_FluxCT::GlobalMaxDivB(md); // Print on rank zero - if (MPIRank0()) { - std::cout << "Max DivB: " << divb_max << std::endl; + if (MPIRank0() && print) { + // someday I'll learn stream options + // for now this is more consistent in #digits/scientific + printf("Max DivB: %g\n", divb_max); } if (kill_on_large_divb) { if (divb_max > pmb0->packages.Get("B_FluxCT")->Param("kill_on_divb_over")) diff --git a/pars/orszag_tang_face_ct.par b/pars/orszag_tang_face_ct.par index bd669cde..3a7eca6d 100644 --- a/pars/orszag_tang_face_ct.par +++ b/pars/orszag_tang_face_ct.par @@ -9,7 +9,7 @@ nx1 = 256 x1min = -3.141592653589793 x1max = 3.141592653589793 -nx2 = 256 +nx2 = 128 x2min = -3.141592653589793 x2max = 3.141592653589793 @@ -35,14 +35,10 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 - -flux = hlle - solver = face_ct kill_on_large_divb = true -#ct_scheme = bs99 -ct_scheme = sg09 +ct_scheme = bs99 verbose = 1 @@ -54,7 +50,7 @@ disable_floors = true file_type = hdf5 -dt = 1.0 +dt = 1 single_precision_output = true # TODO just prims when face fields supported variables = prims.rho, prims.u, prims.uvec, prims.B, divB, jcon diff --git a/pars/orszag_tang_smr.par b/pars/orszag_tang_smr.par new file mode 100644 index 00000000..e6665b56 --- /dev/null +++ b/pars/orszag_tang_smr.par @@ -0,0 +1,79 @@ +# Orszag-Tang Vortex problem: +# Generate current sheets on short timescales + + +problem_id = orszag_tang + + +refinement = static +numlevel = 2 +nx1 = 192 +x1min = -3.141592653589793 +x1max = 3.141592653589793 + +nx2 = 192 +x2min = -3.141592653589793 +x2max = 3.141592653589793 + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +x1min = 0.0 +x1max = 0.0 +x2min = 0.0 +x2max = 0.0 +level = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 100.0 +integrator = rk2 + + +type = kharma +nghost = 6 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +solver = face_ct +ct_scheme = bs99 +#ct_scheme = gs05_0 + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +disable_floors = true + + +file_type = hdf5 +dt = 1 +single_precision_output = true +# TODO just prims when face fields supported +variables = prims.rho, prims.u, prims.uvec, prims.B, divB, jcon + + +file_type = hst +dt = 0.1 + +# This problem is generally much too short to need +# checkpointing. However, we have a test which uses it. +# +#file_type = rst +#dt = 10.0 From 20804a5a17428df74fe82a27d2cc6b47cc833347 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 12:12:42 -0600 Subject: [PATCH 132/219] Add CT operations to ImEx driver; tested lightly --- kharma/driver/imex_step.cpp | 45 +++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 76392b28..ae74be89 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -36,9 +36,10 @@ #include "decs.hpp" //Packages -#include "b_flux_ct.hpp" #include "b_cd.hpp" #include "b_cleanup.hpp" +#include "b_ct.hpp" +#include "b_flux_ct.hpp" #include "electrons.hpp" #include "grmhd.hpp" #include "wind.hpp" @@ -65,6 +66,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta auto& driver_pkg = pkgs.at("Driver")->AllParams(); const bool use_electrons = pkgs.count("Electrons"); const bool use_b_cleanup = pkgs.count("B_Cleanup"); + const bool use_b_ct = pkgs.count("B_CT"); const bool use_implicit = pkgs.count("Implicit"); const bool use_jcon = pkgs.count("Current"); const bool use_linesearch = (use_implicit) ? pkgs.at("Implicit")->Param("linesearch") : false; @@ -120,7 +122,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Start receiving flux corrections and ghost cells auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); auto t_start_recv_flux = t_start_recv_bound; - if (pmesh->multilevel) + if (pmesh->multilevel || use_b_ct) t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); // Calculate the flux of each variable through each face @@ -131,8 +133,16 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fluxes; - if (pmesh->multilevel) { - tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + if (pmesh->multilevel || use_b_ct) { + auto t_emf = t_fluxes; + // TODO this MPI sync should be bundled into fluxcorr + if (use_b_ct) { + // Pull out a container of only EMF to synchronize + auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition + auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); + auto t_emf = KHARMADriver::AddMPIBoundarySync(t_emf_local, tl, md_emf_only); + } + tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } @@ -150,26 +160,43 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); // UPDATE VARIABLES + // TODO abstract this since the drivers share it // This block is designed to intelligently update a set of variables partially marked "Implicit" // and partially "Explicit," by first doing any explicit updates, then using them as elements // of the "guess" for the implicit solve // Update the explicitly-evolved variables using the source term // Add any proportion of the step start required by the integrator (e.g., RK2) - auto t_avg_data = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, - std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent}), + auto t_avg_data_c = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent, Metadata::Cell}), + md_sub_step_init.get(), md_full_step_init.get(), + integrator->gam0[stage-1], integrator->gam1[stage-1], + md_solver.get()); + auto t_avg_data = t_avg_data_c; + if (use_b_ct) { + t_avg_data = tl.AddTask(t_avg_data_c, WeightedSumDataFace, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent, Metadata::Face}), md_sub_step_init.get(), md_full_step_init.get(), integrator->gam0[stage-1], integrator->gam1[stage-1], md_solver.get()); + } // apply du/dt to the result - auto t_update = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, - std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent}), + auto t_update_c = tl.AddTask(t_sources, Update::WeightedSumData, MeshData>, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent, Metadata::Cell}), md_solver.get(), md_flux_src.get(), 1.0, integrator->beta[stage-1] * integrator->dt, md_solver.get()); + auto t_update = t_update_c; + if (use_b_ct) { + t_update = tl.AddTask(t_update_c, WeightedSumDataFace, + std::vector({Metadata::GetUserFlag("Explicit"), Metadata::Independent, Metadata::Face}), + md_solver.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_solver.get()); + } // If evolving GRMHD explicitly, UtoP needs a guess in order to converge, so we copy in md_sub_step_init - auto t_copy_prims = t_none; + auto t_copy_prims = t_update; if (!pkgs.at("GRMHD")->Param("implicit")) { t_copy_prims = tl.AddTask(t_none, Copy>, std::vector({Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("Primitive")}), md_sub_step_init.get(), md_solver.get()); From c9a1860c8cd2aed6162898e63f9ba6479e769d53 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 12:31:52 -0600 Subject: [PATCH 133/219] Fix zeroing fluxes on non-X2 boundaries & 1D runs --- kharma/boundaries/boundaries.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 4adc1afd..41ef993e 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -331,17 +331,17 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) const IndexRange jbf = IndexRange{jbs.s, jbs.e + (ndim > 1)}; const IndexRange kbf = IndexRange{kbs.s, kbs.e + (ndim > 2)}; - for (auto &pmb : pmesh->block_list) - { + for (auto &pmb : pmesh->block_list) { auto &rc = pmb->meshblock_data.Get(); - for (int i = 0; i < BOUNDARY_NFACES; i++) - { + for (int i = 0; i < BOUNDARY_NFACES; i++) { BoundaryFace bface = (BoundaryFace)i; auto bname = BoundaryName(bface); auto bdir = BoundaryDirection(bface); auto binner = BoundaryIsInner(bface); + if (bdir > ndim) continue; + // Set ranges based IndexRange ib = ibs, jb = jbs, kb = kbs; // Range for inner_x1 bounds is first face only, etc. @@ -364,7 +364,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) pmb->par_for( "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - F.flux(X1DIR, m_rho, k, j, i) = m::min(F.flux(X1DIR, m_rho, k, j, i), 0.); + F.flux(bdir, m_rho, k, j, i) = m::min(F.flux(bdir, m_rho, k, j, i), 0.); }); } } @@ -376,7 +376,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) pmb->par_for( "zero_flux_" + bname, 0, F.GetDim(4) - 1, kb.s, kb.e, jb.s, jb.s, ib.s, ib.e, KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { - F.flux(X2DIR, p, k, j, i) = 0.; + F.flux(bdir, p, k, j, i) = 0.; }); } } From b859ad939c548d43a71f02a28ee950abd2687fdd Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 12:42:51 -0600 Subject: [PATCH 134/219] Parthenon vbump related to the CT work --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index f9e41049..eede5cd0 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit f9e41049178586c26de0c0b069cf7d05075b019d +Subproject commit eede5cd09f4d669d4fc97923d51eeca35f4dcd29 From c2343375252ffd9b95044027aacaf6e487af3a95 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 14:22:19 -0600 Subject: [PATCH 135/219] Get rid of a Kokkos warning about All_t --- external/kokkos-kernels/KokkosBatched_Util.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/external/kokkos-kernels/KokkosBatched_Util.hpp b/external/kokkos-kernels/KokkosBatched_Util.hpp index 46b97ee0..ae71a606 100644 --- a/external/kokkos-kernels/KokkosBatched_Util.hpp +++ b/external/kokkos-kernels/KokkosBatched_Util.hpp @@ -772,8 +772,8 @@ KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, } template KOKKOS_INLINE_FUNCTION auto subview_wrapper(ViewType v, IdxType1 i1, - Kokkos::Impl::ALL_t i2, - Kokkos::Impl::ALL_t i3, + Kokkos::ALL_t i2, + Kokkos::ALL_t i3, const BatchLayout::Left &layout_tag, const Trans::Transpose) { auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); @@ -805,7 +805,7 @@ KOKKOS_INLINE_FUNCTION auto subview_wrapper( } template KOKKOS_INLINE_FUNCTION auto subview_wrapper( - ViewType v, IdxType1 i1, Kokkos::Impl::ALL_t i2, Kokkos::Impl::ALL_t i3, + ViewType v, IdxType1 i1, Kokkos::ALL_t i2, Kokkos::ALL_t i3, const BatchLayout::Right &layout_tag, const Trans::Transpose &) { auto sv_nt = subview_wrapper(v, i1, i3, i2, layout_tag); From f723196c655010184865df7a77d7b8201a75c18a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 28 Sep 2023 14:24:07 -0600 Subject: [PATCH 136/219] Compile fixes for using custom linker & for personal machines --- external/patches/kokkos-rx7800.patch | 13 ++++++++ machines/bp.sh | 48 +++++++++++++++------------- make.sh | 8 ++--- 3 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 external/patches/kokkos-rx7800.patch diff --git a/external/patches/kokkos-rx7800.patch b/external/patches/kokkos-rx7800.patch new file mode 100644 index 00000000..80415188 --- /dev/null +++ b/external/patches/kokkos-rx7800.patch @@ -0,0 +1,13 @@ +diff --git a/cmake/kokkos_arch.cmake b/cmake/kokkos_arch.cmake +index 244881187..869b0962b 100644 +--- a/cmake/kokkos_arch.cmake ++++ b/cmake/kokkos_arch.cmake +@@ -105,7 +105,7 @@ LIST(APPEND SUPPORTED_AMD_ARCHS VEGA906 AMD_GFX906) + LIST(APPEND CORRESPONDING_AMD_FLAGS gfx906 gfx906) + LIST(APPEND SUPPORTED_AMD_GPUS RX7900XTX RX7900XTX V620/W6800 V620/W6800) + LIST(APPEND SUPPORTED_AMD_ARCHS NAVI1100 AMD_GFX1100 NAVI1030 AMD_GFX1030) +-LIST(APPEND CORRESPONDING_AMD_FLAGS gfx1100 gfx1100 gfx1030 gfx1030) ++LIST(APPEND CORRESPONDING_AMD_FLAGS gfx1100 gfx1101 gfx1030 gfx1030) + + #FIXME CAN BE REPLACED WITH LIST_ZIP IN CMAKE 3.17 + FOREACH(ARCH IN LISTS SUPPORTED_AMD_ARCHS) diff --git a/machines/bp.sh b/machines/bp.sh index 366ea959..c746b042 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -9,12 +9,13 @@ if [[ $HOST == "cheshire"* ]]; then if [[ "$ARGS" == *"cuda"* ]]; then # NVHPC. Compiler is chosen automatically now module load nvhpc + NPROC=8 # so much memory else # Intel oneAPI module load compiler mpi/2021 + NPROC=24 fi - - NPROC=8 + # Even CPU kharma is unkillable without this MPI_EXE=mpirun fi @@ -24,13 +25,16 @@ fi if [[ $METAL_HOSTNAME == "fermium" ]]; then HOST_ARCH="AMDAVX" - DEVICE_ARCH="TURING75" - # Nvidia MPI hangs unless I do this - MPI_EXE=mpirun + # We patch Kokkos to make this gfx1101==rx7800xt + DEVICE_ARCH="AMD_GFX1100" + # MPI & Kokkos separately dislike running the bin alone + #MPI_EXE=mpirun + NPROC=24 - if [[ "$ARGS" == *"cuda"* ]]; then - # Container default is the wrong NVHPC package - module swap nvhpc-hpcx nvhpc + if [[ "$ARGS" == *"hip"* ]]; then + # AMD for GPUs (this will be run in container, no modules) + C_NATIVE=hipcc + CXX_NATIVE=hipcc else # AMD for CPUs module load aocc-compiler-4.1.0 mpi @@ -41,7 +45,6 @@ fi if [[ $METAL_HOSTNAME == "ferrum" ]]; then HOST_ARCH="HSW" - DEVICE_ARCH="INTEL_GEN" NPROC=6 if [[ "$ARGS" == *"gcc"* ]]; then @@ -52,12 +55,12 @@ if [[ $METAL_HOSTNAME == "ferrum" ]]; then # Intel compiler module purge module load compiler mpi - PREFIX_PATH="$HOME/libs/hdf5-oneapi" + C_NATIVE="icc" + CXX_NATIVE="icpc" else # Intel SYCL implementation "DPC++" module purge module load compiler mpi - PREFIX_PATH="$HOME/libs/hdf5-oneapi" C_NATIVE="icx" CXX_NATIVE="icpx" fi @@ -69,15 +72,18 @@ if [[ $HOST == "cinnabar"* ]]; then module purge # Handle modules inside this script HOST_ARCH="HSW" # This won't change + DEVICE_ARCH="TURING75" + + # Runtime + MPI_NUM_PROCS=1 + + # TODO container: + # module swap nvhpc-hpcx nvhpc if [[ "$ARGS" == *"cuda"* ]]; then - # Use NVHPC libraries (GPU-aware OpenMPI!) - DEVICE_ARCH="KEPLER35" - MPI_NUM_PROCS=1 + # Runtime MPI_EXTRA_ARGS="--map-by ppr:1:numa:pe=14" - # Quash warning about my old gpus - export NVCC_WRAPPER_CUDA_EXTRA_FLAGS="-Wno-deprecated-gpu-targets" # System CUDA path EXTRA_FLAGS="-DCUDAToolkit_INCLUDE_DIR=/usr/include/cuda $EXTRA_FLAGS" @@ -86,28 +92,24 @@ if [[ $HOST == "cinnabar"* ]]; then module load mpi/mpich-x86_64 nvhpc-nompi C_NATIVE="gcc" CXX_NATIVE="g++" - # Uses system GCC, which is old - EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" else module load nvhpc PREFIX_PATH="$HOME/libs/hdf5-nvhpc" C_NATIVE="nvc" CXX_NATIVE="nvc++" - export CXXFLAGS="-mp" + #export CXXFLAGS="-mp" fi else - MPI_NUM_PROCS=1 if [[ "$ARGS" == *"gcc"* ]]; then # GCC module load mpi/mpich-x86_64 C_NATIVE="gcc" CXX_NATIVE="g++" - # Uses system GCC, which is old - EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" else # Intel by default module load compiler mpi - PREFIX_PATH="$HOME/libs/hdf5-oneapi" + C_NATIVE="icx" + CXX_NATIVE="icpx" fi fi fi diff --git a/make.sh b/make.sh index 6235e789..6dc9e585 100755 --- a/make.sh +++ b/make.sh @@ -201,10 +201,10 @@ fi # Allow for a custom linker program, but use CXX by # default as system linker may be older/incompatible if [[ -v LINKER ]]; then - EXTRA_FLAGS="-DCMAKE_LINKER=$LINKER" + EXTRA_FLAGS="$EXTRA_FLAGS -DCMAKE_LINKER=$LINKER" fi if [[ "$ARGS" == *"special_link_line"* ]]; then - EXTRA_FLAGS="-DCMAKE_CXX_LINK_EXECUTABLE=' -o '" + EXTRA_FLAGS="$EXTRA_FLAGS -DCMAKE_CXX_LINK_EXECUTABLE=' -o '" fi # Avoid warning on nvcc pragmas Intel doesn't like @@ -284,7 +284,7 @@ fi if [[ "$ARGS" == *"clean"* ]]; then cd external/parthenon - if [[ $(( $(git --version | cut -d '.' -f 2) > 35 )) ]]; then + if [[ $(( $(git --version | cut -d '.' -f 2) > 35 )) == "1" ]]; then git apply --quiet ../patches/parthenon-*.patch else echo "make.sh note: You may see errors applying patches below. These are normal." @@ -314,7 +314,7 @@ if [[ "$ARGS" == *"clean"* ]]; then -DKokkos_ENABLE_CUDA=$ENABLE_CUDA \ -DKokkos_ENABLE_SYCL=$ENABLE_SYCL \ -DKokkos_ENABLE_HIP=$ENABLE_HIP \ - $EXTRA_FLAGS + "$EXTRA_FLAGS" if [[ "$ARGS" == *"dryrun"* ]]; then set +x From b99b16077165c58ce1fd7d1a54e4a4c40e0e67ce Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 29 Sep 2023 10:45:40 -0600 Subject: [PATCH 137/219] Parthenon bump, callbacks Rename two very Athena-sounding callbacks more inline, add 'PostExecute' --- external/parthenon | 2 +- kharma/b_cd/b_cd.cpp | 2 +- kharma/driver/kharma_driver.cpp | 6 ++++++ kharma/driver/kharma_driver.hpp | 10 +++++++--- kharma/implicit/implicit.cpp | 5 ++++- kharma/kharma.cpp | 12 ++++++------ kharma/kharma.hpp | 5 +++-- kharma/kharma_package.cpp | 33 +++++++++++++++++++++++---------- kharma/kharma_package.hpp | 16 ++++++++++++---- kharma/main.cpp | 4 ++-- 10 files changed, 65 insertions(+), 30 deletions(-) diff --git a/external/parthenon b/external/parthenon index eede5cd0..4b5f5026 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit eede5cd09f4d669d4fc97923d51eeca35f4dcd29 +Subproject commit 4b5f5026e79fa81ff61a1806c1a4dbe09e673269 diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index e008c1af..7d37399f 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -88,7 +88,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

BlockUtoP = B_CD::BlockUtoP; pkg->PostStepDiagnosticsMesh = B_CD::PostStepDiagnostics; - pkg->MeshPostStepUserWorkInLoop = B_CD::UpdateCtopMax; + pkg->PostStepWork = B_CD::UpdateCtopMax; // List (vector) of HistoryOutputVar that will all be enrolled as output variables parthenon::HstVar_list hst_vars = {}; diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 84387958..e66fed60 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -290,3 +290,9 @@ void KHARMADriver::SetGlobalTimeStep() (tm.tlim - tm.time) < tm.dt) // timestep would take us past desired endpoint tm.dt = tm.tlim - tm.time; } + +void KHARMADriver::PostExecute(DriverStatus status) +{ + Packages::PostExecute(pmesh, pinput, tm); + EvolutionDriver::PostExecute(status); +} diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 208c472a..7bcc8d56 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -57,11 +57,14 @@ class KHARMADriver : public MultiStageDriver { static std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages); // Eliminate Parthenon's print statements when starting up the driver, we have a bunch of our own - void PreExecute() { timer_main.reset(); } + void PreExecute() override { timer_main.reset(); } // Also override the timestep calculation, so we can start moving options etc out of GRMHD package void SetGlobalTimeStep(); + // And the PostExecute, so we can add a package callback here + void PostExecute(DriverStatus status) override; + /** * A Driver object orchestrates everything that has to be done to a mesh to take a step. * The function MakeTaskCollection outlines everything to be done in one sub-step, @@ -83,7 +86,7 @@ class KHARMADriver : public MultiStageDriver { * All task lists proceed roughly in this order, but differ in which variables they synchronize via MPI, * or whether they synchronize at all. */ - TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage); + TaskCollection MakeTaskCollection(BlockList_t &blocks, int stage) override; /** * The default step, synchronizing conserved variables and then recovering primitive variables in the ghost zones. @@ -158,7 +161,8 @@ class KHARMADriver : public MultiStageDriver { } static TaskStatus WeightedSumDataFace(const std::vector &flags, MeshData *in1, MeshData *in2, const Real w1, const Real w2, - MeshData *out) { + MeshData *out) + { Kokkos::Profiling::pushRegion("Task_WeightedSumData"); const auto &x = in1->PackVariables(flags); const auto &y = in2->PackVariables(flags); diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index 826a4665..f0ddd5d4 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -593,7 +593,10 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ // Finally, break if max_norm is less than the total tolerance we set // TODO per-zone tolerance with masks? - if (iter >= iter_min && max_norm < rootfind_tol) break; + if (iter >= iter_min && max_norm < rootfind_tol) { + EndFlag(); + break; + } } EndFlag(); } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 6d68279e..07ec8d42 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -96,8 +96,8 @@ std::shared_ptr KHARMA::InitializeGlobals(ParameterInput *pin, st params.Add("branch", KHARMA::Version::GIT_REFSPEC); // Update the times with callbacks - pkg->MeshPreStepUserWorkInLoop = KHARMA::MeshPreStepUserWorkInLoop; - pkg->MeshPostStepUserWorkInLoop = KHARMA::MeshPostStepUserWorkInLoop; + pkg->PreStepWork = KHARMA::PreStepWork; + pkg->PostStepWork = KHARMA::PostStepWork; return pkg; } @@ -116,7 +116,7 @@ void KHARMA::ResetGlobals(ParameterInput *pin, Mesh *pmesh) // to be restored by Parthenon } -void KHARMA::MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +void KHARMA::PreStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { auto& globals = pmesh->packages.Get("Globals")->AllParams(); if (!globals.Get("in_loop")) { @@ -126,11 +126,11 @@ void KHARMA::MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const S globals.Update("time", tm.time); } -void KHARMA::MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +void KHARMA::PostStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - // Knowing this works took a little digging into Parthenon's EvolutionDriver. + // Knowing that this works took a little digging into Parthenon's EvolutionDriver. // The order of operations after calling Step() is: - // 1. Call PostStepUserWorkInLoop and PostStepDiagnostics (this function and following) + // 1. Call PostStepWork and PostStepDiagnostics (this function and following) // 2. Set the timestep tm.dt to the minimum from the EstimateTimestep calls // 3. Generate any outputs, e.g. jcon // Thus we preserve tm.dt (which has not yet been reset) as dt_last for Current::FillOutput diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index 91c9a99c..a772e9f5 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -56,11 +56,11 @@ void ResetGlobals(ParameterInput *pin, Mesh *pmesh); /** * Update variables in Globals package based on Parthenon state incl. SimTime struct */ -void MeshPreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PreStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); /** * Update variables in Globals package based on Parthenon state incl. SimTime struct */ -void MeshPostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PostStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); /** * Task to add a package. Lets us queue up all the packages we want in a task list, *then* load them @@ -126,6 +126,7 @@ inline int PackDimension(Packages_t* packages, Metadata::FlagCollection fc) int nvar = 0; for (auto pkg : packages->AllPackages()) { nvar += pkg.second->GetPackDimension(fc); + std::cout << pkg.first << " variables: " << pkg.second->GetPackDimension(fc) << std::endl; } return nvar; } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 594bc1c3..68dcb66e 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -204,28 +204,28 @@ void Packages::UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin) EndFlag(); } -void Packages::PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +void Packages::PreStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("PreStepUserWorkInLoop"); + Flag("PreStepWork"); auto kpackages = pmesh->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage.second->MeshPreStepUserWorkInLoop != nullptr) { - Flag("PreStepUserWorkInLoop_"+kpackage.first); - kpackage.second->MeshPreStepUserWorkInLoop(pmesh, pin, tm); + if (kpackage.second->PreStepWork != nullptr) { + Flag("PreStepWork_"+kpackage.first); + kpackage.second->PreStepWork(pmesh, pin, tm); EndFlag(); } } EndFlag(); } -void Packages::PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +void Packages::PostStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) { - Flag("PostStepUserWorkInLoop"); + Flag("PostStepWork"); auto kpackages = pmesh->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage.second->MeshPostStepUserWorkInLoop != nullptr) { - Flag("PostStepUserWorkInLoop_"+kpackage.first); - kpackage.second->MeshPostStepUserWorkInLoop(pmesh, pin, tm); + if (kpackage.second->PostStepWork != nullptr) { + Flag("PostStepWork_"+kpackage.first); + kpackage.second->PostStepWork(pmesh, pin, tm); EndFlag(); } } @@ -250,3 +250,16 @@ void Packages::PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTi EndFlag(); } +void Packages::PostExecute(Mesh *pmesh, ParameterInput *pin, const SimTime &tm) +{ + Flag("KHARMAPostExecute"); + auto kpackages = pmesh->packages.AllPackagesOfType(); + for (auto kpackage : kpackages) { + if (kpackage.second->PostExecute != nullptr) { + Flag("PostExecute_"+kpackage.first); + kpackage.second->PostExecute(pmesh, pin, tm); + EndFlag(); + } + } + EndFlag(); +} diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index 7c43bbc7..a3f02620 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -52,6 +52,9 @@ using namespace parthenon; class KHARMAPackage : public StateDescriptor { public: KHARMAPackage(std::string name) : StateDescriptor(name) {} +#if TRACE + ~KHARMAPackage() { std::cerr << "Destroying package " << label_ << std::endl; } +#endif // PHYSICS // Recovery of primitive variables from conserved. @@ -88,15 +91,19 @@ class KHARMAPackage : public StateDescriptor { // CONVENIENCE // Anything to be done before each step begins -- currently just updating global "in_loop" - std::function MeshPreStepUserWorkInLoop = nullptr; + std::function PreStepWork = nullptr; // Anything to be done after every step is fully complete -- usually reductions or preservation of variables - std::function MeshPostStepUserWorkInLoop = nullptr; + // Note that most diagnostics should go in "PostStepDiagnosticsMesh" instead + std::function PostStepWork = nullptr; // Anything to be done just before any outputs (dump files, restarts, history files) are made // Usually for filling output-only variables // TODO Add MeshUserWorkBeforeOutput to Parthenon std::function BlockUserWorkBeforeOutput = nullptr; + // Anything at the very end of simulation. Cleanup, summaries, outputs if you're brave + std::function PostExecute = nullptr; + // BOUNDARIES // Currently only used by the "boundaries" package // Note these functions take the boundary IndexDomain as an argument, so you can assign the same function to multiple boundaries. @@ -160,7 +167,8 @@ TaskStatus MeshApplyFloors(MeshData *md, IndexDomain domain); // These are already Parthenon global callbacks -- see their documentation // I define them here so I can pass them on to packages void UserWorkBeforeOutput(MeshBlock *pmb, ParameterInput *pin); -void PreStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); -void PostStepUserWorkInLoop(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PreStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PostStepWork(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); void PostStepDiagnostics(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); +void PostExecute(Mesh *pmesh, ParameterInput *pin, const SimTime &tm); } diff --git a/kharma/main.cpp b/kharma/main.cpp index 7052099a..724057d0 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -109,8 +109,8 @@ int main(int argc, char *argv[]) pman.app_input->ProblemGenerator = KHARMA::ProblemGenerator; // A few are passed on to be implemented by packages as they see fit pman.app_input->MeshBlockUserWorkBeforeOutput = Packages::UserWorkBeforeOutput; - pman.app_input->PreStepMeshUserWorkInLoop = Packages::PreStepUserWorkInLoop; - pman.app_input->PostStepMeshUserWorkInLoop = Packages::PostStepUserWorkInLoop; + pman.app_input->PreStepMeshUserWorkInLoop = Packages::PreStepWork; + pman.app_input->PostStepMeshUserWorkInLoop = Packages::PostStepWork; pman.app_input->PostStepDiagnosticsInLoop = Packages::PostStepDiagnostics; // Registering KHARMA's boundary functions here doesn't mean they will *always* run: From c1c940e9e8206a15f4c04e0e4abe54a78854ebd8 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 29 Sep 2023 10:47:29 -0600 Subject: [PATCH 138/219] Remove unused reducers, add comments --- kharma/b_ct/b_ct.cpp | 3 --- kharma/b_flux_ct/b_flux_ct.cpp | 2 -- kharma/boundaries/boundaries.cpp | 1 + kharma/boundaries/dirichlet.cpp | 2 +- kharma/emhd/emhd.cpp | 5 ++++- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 485f349a..02659f1c 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -78,9 +78,6 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared if (lazy_prolongation && pin->GetString("parthenon/mesh", "refinement") == "adaptive") throw std::runtime_error("Cannot use non-preserving prolongation in AMR!"); - // Add a reducer object (MPI communicator) for divB to params - params.Add("divb_reducer", AllReduce()); - // FIELDS // Flags for B fields on faces. diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 9665e2f2..fd3fbeb3 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -92,8 +92,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("b_field", "implicit", false); params.Add("implicit", implicit_b); - params.Add("divb_reducer", AllReduce()); - // FIELDS // Vector size: 3x[grid shape] std::vector s_vector({NVEC}); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 41ef993e..5aaeb2d7 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -245,6 +245,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD EndFlag(); // Exit immediately if we're syncing emf alone + // TODO can we check name? if (rc->GetVariableVector().size() == 1) { EndFlag(); return; diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index e90d5c47..c3b63f8f 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -56,7 +56,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda auto bound = rc->Get("bounds." + BoundaryName(bface)).data; if (q.GetDim(4) != bound.GetDim(4)) { - std::cerr << "Boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; + std::cerr << "Boundary cache mismatch! boundaries: " << bound.GetDim(4) << " vs pack: " << q.GetDim(4) << std::endl; std::cerr << "Variables with ghost zones:" << std::endl; ghostmap.print(); } diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 02e73749..80da5f4c 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -73,6 +73,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("emhd", "viscosity", true); params.Add("viscosity", viscosity); + // TODO consider erroring when (the correct subset of) these aren't present, + // rather than have defaults that won't work well Real tau = pin->GetOrAddReal("emhd", "tau", 1.0); Real conduction_alpha = pin->GetOrAddReal("emhd", "conduction_alpha", 1.0); params.Add("conduction_alpha", conduction_alpha); @@ -122,7 +124,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Date: Fri, 29 Sep 2023 10:50:20 -0600 Subject: [PATCH 139/219] Organize prob/, make problems request B fields The idea is to have every problem capable of either B field transport, but this requires defining a function for A or B rather than setting it, since you don't know where it lives. This solution is not ideal, but I think it'll work okay --- kharma/CMakeLists.txt | 2 + kharma/prob/emhd/emhdmodes.hpp | 2 + kharma/prob/fm_torus.cpp | 59 ----- kharma/prob/fm_torus.hpp | 10 - kharma/prob/kelvin_helmholtz.hpp | 69 +---- kharma/prob/mhdmodes.hpp | 95 ++++--- kharma/prob/orszag_tang.hpp | 53 +--- kharma/prob/post_initialize.cpp | 4 +- kharma/prob/problem.cpp | 8 +- kharma/prob/seed_B.cpp | 274 +++++++++++++++++++- kharma/prob/seed_B.hpp | 63 +++-- kharma/prob/seed_B_impl.hpp | 295 ---------------------- kharma/prob/{ => utils}/blob.hpp | 0 kharma/prob/{ => utils}/hdf5_utils.cpp | 0 kharma/prob/{ => utils}/hdf5_utils.h | 0 kharma/prob/{ => utils}/interpolation.hpp | 0 kharma/prob/utils/perturbation.hpp | 102 ++++++++ 17 files changed, 495 insertions(+), 541 deletions(-) delete mode 100644 kharma/prob/seed_B_impl.hpp rename kharma/prob/{ => utils}/blob.hpp (100%) rename kharma/prob/{ => utils}/hdf5_utils.cpp (100%) rename kharma/prob/{ => utils}/hdf5_utils.h (100%) rename kharma/prob/{ => utils}/interpolation.hpp (100%) create mode 100644 kharma/prob/utils/perturbation.hpp diff --git a/kharma/CMakeLists.txt b/kharma/CMakeLists.txt index 2a9c53a9..aec167cc 100644 --- a/kharma/CMakeLists.txt +++ b/kharma/CMakeLists.txt @@ -15,6 +15,7 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob/elec EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob/emhd EXE_NAME_SRC) +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/prob/utils EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/coordinates EXE_NAME_SRC) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/flux EXE_NAME_SRC) @@ -40,6 +41,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob/elec) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob/emhd) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/prob/utils) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/coordinates) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/flux) diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 2848075f..7c9c7f9d 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -89,6 +89,8 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet const Real k2 = 4. * M_PI; // END POSSIBLE ARGS + // TODO SET B PARAMS HERE + IndexDomain domain = IndexDomain::interior; IndexRange ib = pmb->cellbounds.GetBoundsI(domain); IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); diff --git a/kharma/prob/fm_torus.cpp b/kharma/prob/fm_torus.cpp index e0441445..879c1b34 100644 --- a/kharma/prob/fm_torus.cpp +++ b/kharma/prob/fm_torus.cpp @@ -38,9 +38,6 @@ #include "coordinate_utils.hpp" #include "types.hpp" -#include -#include "Kokkos_Random.hpp" - TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin) { auto pmb = rc->GetBlockPointer(); @@ -198,59 +195,3 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter return TaskStatus::complete; } - -// TODO move this to a different file -TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin) -{ - auto pmb = rc->GetBlockPointer(); - auto rho = rc->Get("prims.rho").data; - auto u = rc->Get("prims.u").data; - - const Real u_jitter = pin->GetReal("perturbation", "u_jitter"); - // Don't jitter values set by floors - const Real jitter_above_rho = pin->GetReal("floors", "rho_min_geom") + 1e-10; - // Note we add the MeshBlock gid to this value when seeding RNG, - // to get a new sequence for every block - const int rng_seed = pin->GetOrAddInteger("perturbation", "rng_seed", 31337); - // Print real seed used for all blocks, to ensure they're different - if (pmb->packages.Get("Globals")->Param("verbose") > 1) { - std::cout << "Seeding RNG in block " << pmb->gid << " with value " << rng_seed + pmb->gid << std::endl; - } - const bool serial = pin->GetOrAddInteger("perturbation", "serial", false); - - // Should we jitter ghosts? If first boundary sync doesn't work it's marginally less disruptive - IndexDomain domain = IndexDomain::interior; - const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); - const int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); - const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); - - if (serial) { - // Serial version - // Probably guarantees better determinism, but CPU single-thread only - std::mt19937 gen(rng_seed + pmb->gid); - std::uniform_real_distribution dis(-u_jitter/2, u_jitter/2); - - auto u_host = u.GetHostMirrorAndCopy(); - for(int k=ks; k <= ke; k++) - for(int j=js; j <= je; j++) - for(int i=is; i <= ie; i++) - u_host(k, j, i) *= 1. + dis(gen); - u.DeepCopy(u_host); - } else { - // Kokkos version - typedef typename Kokkos::Random_XorShift64_Pool<> RandPoolType; - RandPoolType rand_pool(rng_seed + pmb->gid); - typedef typename RandPoolType::generator_type gen_type; - pmb->par_for("perturb_u", ks, ke, js, je, is, ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - if (rho(k, j, i) > jitter_above_rho) { - gen_type rgen = rand_pool.get_state(); - u(k, j, i) *= 1. + Kokkos::rand::draw(rgen, -u_jitter/2, u_jitter/2); - rand_pool.free_state(rgen); - } - } - ); - } - - return TaskStatus::complete; -} diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index 987d33c5..211fe1e7 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -12,16 +12,6 @@ */ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, ParameterInput *pin); -/** - * Perturb the internal energy by a uniform random proportion per cell. - * Resulting internal energies will be between u \pm u*u_jitter/2 - * i.e. u_jitter=0.1 -> \pm 5% randomization, 0.95u to 1.05u - * - * @param u_jitter see description - * @param rng_seed is added to the MPI rank to seed the GSL RNG - */ -TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin); - /** * Torus solution for ln h, See Fishbone and Moncrief eqn. 3.6. */ diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 7a7e9958..25c79033 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -66,7 +66,6 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P const Real amp = pin->GetOrAddReal("kelvin_helmholtz", "amp", 0.01); const Real z1 = pin->GetOrAddReal("kelvin_helmholtz", "z1", 0.5); const Real z2 = pin->GetOrAddReal("kelvin_helmholtz", "z2", 1.5); - const Real added_b = pin->GetOrAddReal("kelvin_helmholtz", "added_b", 0.0); const auto& G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); @@ -77,68 +76,18 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { GReal X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); - // Lecoanet's x <-> x1; z <-> x2 - GReal x = X[1]; - GReal z = X[2]; + GReal zdist1 = X[2] - z1; + GReal zdist2 = X[2] - z2; rho(k, j, i) = - rho0 + Drho * 0.5 * (tanh((z - z1) / a) - tanh((z - z2) / a)); - u(k, j, i) = P0 / (gam - 1.); - uvec(0, k, j, i) = uflow * (tanh((z - z1) / a) - tanh((z - z2) / a) - 1.); - uvec(1, k, j, i) = amp * sin(2. * M_PI * x) * - (m::exp(-(z - z1) * (z - z1) / (sigma * sigma)) + - m::exp(-(z - z2) * (z - z2) / (sigma * sigma))); - uvec(2, k, j, i) = 0; - } - ); - - // if (pmb->packages.AllPackages().count("B_CT")) { - // auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - // // Halo one zone right for faces - // // We don't need any more than that, since curls never take d1dx1 - // IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); - // IndexSize3 s = KDomain::GetBlockSize(rc); - // GridVector A("A", NVEC, s.n3, s.n2, s.n1); - // pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, - // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Real Xembed[GR_DIM]; - // G.coord(k, j, i, Loci::corner, Xembed); - // A(V3, k, j, i) = added_b * (Xembed[1]/G.Dxc<1>(i) + Xembed[2]/G.Dxc<2>(j)) * tscale; - // } - // ); - // // This fills a couple zones outside the exact interior with bad data - // IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); - // pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, - // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // B_CT::curl_2D(G, A, B_Uf, k, j, i); - // } - // ); - // B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); - // double max_divb = B_CT::BlockMaxDivB(rc.get()); - // std::cout << "Block max DivB: " << max_divb << std::endl; - - // } else if (pmb->packages.AllPackages().count("B_FluxCT") || - // pmb->packages.AllPackages().count("B_CD")) { - // GridVector B_P = rc->Get("prims.B").data; - // pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, - // KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // Real X[GR_DIM]; - // G.coord(k, j, i, Loci::center, X); - // B_P(V1, k, j, i) = added_b * tscale; - // B_P(V2, k, j, i) = added_b * tscale; - // B_P(V3, k, j, i) = 0.; - // } - // ); - // B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); - // } - - // Rescale primitive velocities by tscale, and internal energy by the square. - pmb->par_for("kh_renorm", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - u(k, j, i) *= tscale * tscale; - VLOOP uvec(v, k, j, i) *= tscale; - //VLOOP B_P(v, k, j, i) *= tscale; //already done + rho0 + Drho * 0.5 * (m::tanh(zdist1 / a) - m::tanh(zdist2 / a)); + u(k, j, i) = P0 / (gam - 1.) * tscale * tscale; + uvec(0, k, j, i) = uflow * (m::tanh(zdist1 / a) - m::tanh(zdist2 / a) - 1.) * tscale; + uvec(1, k, j, i) = amp * m::sin(2. * M_PI * X[1]) * + (m::exp(-(zdist1 * zdist1) / (sigma * sigma)) + + m::exp(-(zdist2 * zdist2) / (sigma * sigma))) * tscale; + uvec(2, k, j, i) = 0.; } ); diff --git a/kharma/prob/mhdmodes.hpp b/kharma/prob/mhdmodes.hpp index c12e4255..4a8167f0 100644 --- a/kharma/prob/mhdmodes.hpp +++ b/kharma/prob/mhdmodes.hpp @@ -68,51 +68,38 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete const auto& G = pmb->coords; const int nmode = pin->GetOrAddInteger("mhdmodes", "nmode", 1); - const int dir = pin->GetOrAddInteger("mhdmodes", "dir", 0); const bool one_period = pin->GetOrAddBoolean("mhdmodes", "one_period", true); - // if (pin->GetInteger("parthenon/mesh", "nx1")) { - // dir = 3; - // } - - // START POSSIBLE ARGS: take all these as parameters in pin? // Mean state - Real rho0 = 1.; - Real u0 = 1.; - Real u10 = 0.; - Real u20 = 0.; - Real u30 = 0.; - - // Wavevector - Real k1 = 2. * M_PI; - Real k2 = 2. * M_PI; - Real k3 = 2. * M_PI; - // "Faux-2D" plane orientation - // Set to 0 for "full" 3D wave - if (dir == 1) - k1 = 0; - if (dir == 2) - k2 = 0; - if (dir == 3) - k3 = 0; - - Real amp = 1.e-4; - // END POSSIBLE ARGS - - // B is set later, see below - Real B10 = 0.; - Real B20 = 0.; - Real B30 = 0.; + const Real rho0 = pin->GetOrAddReal("mhdmodes", "rho0", 1.); + const Real u0 = pin->GetOrAddReal("mhdmodes", "u0", 1.); + const Real u10 = pin->GetOrAddReal("mhdmodes", "u10", 0.); + const Real u20 = pin->GetOrAddReal("mhdmodes", "u20", 0.); + const Real u30 = pin->GetOrAddReal("mhdmodes", "u30", 0.); + + // Wave parameters + // dir sets "Faux-2D" plane orientation, good for asymmetry bugs + // Set to 0 for "full" 3D wave. + const int dir = pin->GetOrAddInteger("mhdmodes", "dir", 0); + const Real amp = pin->GetOrAddReal("mhdmodes", "amp", 1.e-4); + + // Note the modes below don't work right if you manually set these + // TODO generate modes on the fly for any k values + const Real k1 = pin->GetOrAddReal("mhdmodes", "k1", (dir == 1) ? 0. : 2. * M_PI); + const Real k2 = pin->GetOrAddReal("mhdmodes", "k2", (dir == 2) ? 0. : 2. * M_PI); + const Real k3 = pin->GetOrAddReal("mhdmodes", "k3", (dir == 3) ? 0. : 2. * M_PI); + // Likewise + const Real B10 = pin->GetOrAddReal("mhdmodes", "B10", (dir == 0 || dir == 3) ? 1.0 : 0. ); + const Real B20 = pin->GetOrAddReal("mhdmodes", "B20", (dir == 1) ? 1.0 : 0. ); + const Real B30 = pin->GetOrAddReal("mhdmodes", "B30", (dir == 2) ? 1.0 : 0. ); std::complex omega; Real drho = 0, du = 0; Real du1 = 0, du2 = 0, du3 = 0; Real dB1 = 0, dB2 = 0, dB3 = 0; - // Eigenmode definitions if (dir == 0) { // 3D (1,1,1) wave - B10 = 1.; if (nmode == 0) { // Entropy drho = 1.; } else if (nmode == 1) { // Slow @@ -146,15 +133,6 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete else { // 2D (1,1,0), (1,0,1), (0,1,1) wave - // Constant field direction - if (dir == 1) { - B20 = 1.; - } else if (dir == 2) { - B30 = 1.; - } else if (dir == 3) { - B10 = 1.; - } - if (nmode == 0) { // Entropy drho = 1.; } else if (nmode == 1) { // Slow @@ -212,6 +190,31 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete } } + // Record the parameters + // This might be useful to read when checking, too... + // TODO + pin->SetReal("mhdmodes", "omega_real", omega.real()); + pin->SetReal("mhdmodes", "omega_imag", omega.imag()); + + pin->SetReal("mhdmodes", "drho", drho); + pin->SetReal("mhdmodes", "du", du); + pin->SetReal("mhdmodes", "du1", du1); + pin->SetReal("mhdmodes", "du2", du2); + pin->SetReal("mhdmodes", "du3", du3); + pin->SetReal("mhdmodes", "dB1", dB1); + pin->SetReal("mhdmodes", "dB2", dB2); + pin->SetReal("mhdmodes", "dB3", dB3); + + // Set B field parameters for our mode + pin->GetOrAddString("b_field", "type", "wave"); + pin->GetOrAddReal("b_field", "b10", B10); + pin->GetOrAddReal("b_field", "b20", B20); + pin->GetOrAddReal("b_field", "b30", B30); + pin->GetOrAddReal("b_field", "amp_B1", amp*dB1); + pin->GetOrAddReal("b_field", "amp_B2", amp*dB2); + pin->GetOrAddReal("b_field", "amp_B3", amp*dB3); + pin->GetOrAddReal("b_field", "phase", 0.); + IndexDomain domain = IndexDomain::interior; IndexRange ib = pmb->cellbounds.GetBoundsI(domain); IndexRange jb = pmb->cellbounds.GetBoundsJ(domain); @@ -220,16 +223,12 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); - - Real mode = amp * cos(k1 * X[1] + k2 * X[2] + k3 * X[3]); + Real mode = amp * m::cos(k1 * X[1] + k2 * X[2] + k3 * X[3]); rho(k, j, i) = rho0 + drho * mode; u(k, j, i) = u0 + du * mode; uvec(0, k, j, i) = u10 + du1 * mode; uvec(1, k, j, i) = u20 + du2 * mode; uvec(2, k, j, i) = u30 + du3 * mode; - B_P(0, k, j, i) = B10 + dB1 * mode; - B_P(1, k, j, i) = B20 + dB2 * mode; - B_P(2, k, j, i) = B30 + dB3 * mode; } ); diff --git a/kharma/prob/orszag_tang.hpp b/kharma/prob/orszag_tang.hpp index 41fd17f0..099edbec 100644 --- a/kharma/prob/orszag_tang.hpp +++ b/kharma/prob/orszag_tang.hpp @@ -36,14 +36,20 @@ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, Parame // Default phase puts the current sheet in the middle of the domain const Real phase = pin->GetOrAddReal("orszag_tang", "phase", M_PI); - // TODO coord_embed for snake coords? - + // Set parameters for B field, which will get added differently for flux vs face + // In a questionable decision, we allow overriding these + pin->GetOrAddString("b_field", "type", "orszag_tang_a"); + pin->GetOrAddReal("b_field", "amp_B1", tscale); + pin->GetOrAddReal("b_field", "amp_B2", tscale); + pin->GetOrAddReal("b_field", "phase", phase); + + // Set the non-B values IndexDomain domain = IndexDomain::entire; IndexRange3 b = KDomain::GetRange(rc, domain); pmb->par_for("ot_init", b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); + G.coord_embed(k, j, i, Loci::center, X); rho(k, j, i) = 25./9.; u(k, j, i) = 5./(3.*(gam - 1.)) * tscale * tscale; uvec(0, k, j, i) = -m::sin(X[2] + phase) * tscale; @@ -52,46 +58,5 @@ TaskStatus InitializeOrszagTang(std::shared_ptr>& rc, Parame } ); - if (pmb->packages.AllPackages().count("B_CT")) { - auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - // Halo one zone right for faces - // We don't need any more than that, since curls never take d1dx1 - IndexRange3 bA = KDomain::GetRange(rc, IndexDomain::entire, 0, 0); - IndexSize3 s = KDomain::GetBlockSize(rc); - GridVector A("A", NVEC, s.n3, s.n2, s.n1); - pmb->par_for("ot_A", bA.ks, bA.ke, bA.js, bA.je, bA.is, bA.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - Real Xembed[GR_DIM]; - G.coord(k, j, i, Loci::corner, Xembed); - A(V3, k, j, i) = (-0.5*std::cos(2*Xembed[1] + phase) - + std::cos(Xembed[2] + phase)) * tscale; - } - ); - // This fills a couple zones outside the exact interior with bad data - IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); - pmb->par_for("ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - B_CT::curl_2D(G, A, B_Uf, k, j, i); - } - ); - B_CT::BlockUtoP(rc.get(), IndexDomain::entire, false); - double max_divb = B_CT::BlockMaxDivB(rc.get()); - std::cout << "Block max DivB: " << max_divb << std::endl; - - } else if (pmb->packages.AllPackages().count("B_FluxCT") || - pmb->packages.AllPackages().count("B_CD")) { - GridVector B_P = rc->Get("prims.B").data; - pmb->par_for("ot_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - Real X[GR_DIM]; - G.coord(k, j, i, Loci::center, X); - B_P(V1, k, j, i) = -m::sin(X[2] + phase) * tscale; - B_P(V2, k, j, i) = m::sin(2.*(X[1] + phase)) * tscale; - B_P(V3, k, j, i) = 0.; - } - ); - B_FluxCT::BlockPtoU(rc.get(), IndexDomain::entire, false); - } - return TaskStatus::complete; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index cd2c0000..1fc7a790 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -82,7 +82,7 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) SeedBField(md.get(), pin); // If we're doing a torus problem or explicitly ask for it, - // normalize the magnetic field according to the density + // normalize the magnetic field according to the max density bool is_torus = pin->GetString("parthenon/job", "problem_id") == "torus"; if (pin->GetOrAddBoolean("b_field", "norm", is_torus)) { NormalizeBField(md.get(), pin); @@ -142,4 +142,6 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMADriver::SyncAllBounds(md); // And make sure the trivial primitive values are up-to-date //Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); + + // TODO output parsed parameters now we have *everything* including any problem configs for B field } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 42f85a40..2c0a8b11 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -41,6 +41,7 @@ #include "gr_coordinates.hpp" #include "grmhd.hpp" #include "grmhd_functions.hpp" +#include "perturbation.hpp" #include "types.hpp" // Problem initialization headers @@ -99,8 +100,6 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // GRMHD } else if (prob == "bondi") { status = InitializeBondi(rc, pin); - } else if (prob == "bz_monopole") { - status = InitializeBZMonopole(rc, pin); // Electrons } else if (prob == "noh") { status = InitializeNoh(rc, pin); @@ -122,10 +121,13 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) status = InitializeFMTorus(rc, pin); } else if (prob == "resize_restart") { status = ReadIharmRestart(rc, pin); - } else if (prob == "resize_restart_kharma") { // Hyerin + } else if (prob == "resize_restart_kharma") { status = ReadKharmaRestart(rc, pin); } else if (prob == "gizmo") { status = InitializeGIZMO(rc, pin); + } else if (prob == "vacuum") { + // No need for a separate initializer, just seed w/floors + status = Floors::ApplyInitialFloors(pin, rc.get(), IndexDomain::interior); } // If we didn't initialize a problem, yell diff --git a/kharma/prob/seed_B.cpp b/kharma/prob/seed_B.cpp index 5476331c..c85b5fd9 100644 --- a/kharma/prob/seed_B.cpp +++ b/kharma/prob/seed_B.cpp @@ -33,10 +33,12 @@ */ #include "seed_B.hpp" -#include "seed_B_impl.hpp" +#include "b_ct.hpp" +#include "b_flux_ct.hpp" #include "boundaries.hpp" #include "coordinate_utils.hpp" +#include "domain.hpp" #include "fm_torus.hpp" #include "grmhd_functions.hpp" @@ -74,6 +76,268 @@ Real MinBeta(MeshData *md) return Reductions::DomainReduction(md, UserHistoryOperation::min); } + +template +TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDomain domain = IndexDomain::entire) +{ + auto pmb = rc->GetBlockPointer(); + auto pkgs = pmb->packages.AllPackages(); + + // Fields + GridScalar rho = rc->Get("prims.rho").data; + const auto &G = pmb->coords; + + // Parameters + std::string b_field_type = pin->GetString("b_field", "type"); + auto prob = pin->GetString("parthenon/job", "problem_id"); + bool is_torus = (prob == "torus"); + + // Indices + // TODO handle filling faces with domain < entire more gracefully + IndexRange3 b = KDomain::GetRange(rc, domain); + int ndim = pmb->pmy_mesh->ndim; + + // Shortcut to field values for easy fields + if constexpr (Seed == BSeedType::constant || + Seed == BSeedType::monopole || + Seed == BSeedType::monopole_cube || + Seed == BSeedType::orszag_tang) + { + // All custom B fields should set what they need of these + const Real b10 = pin->GetOrAddReal("b_field", "B10", 0.); + const Real b20 = pin->GetOrAddReal("b_field", "B20", 0.); + const Real b30 = pin->GetOrAddReal("b_field", "B30", 0.); + const Real k1 = pin->GetOrAddReal("b_field", "k1", 0.); + const Real k2 = pin->GetOrAddReal("b_field", "k2", 0.); + const Real k3 = pin->GetOrAddReal("b_field", "k3", 0.); + const Real phase = pin->GetOrAddReal("b_field", "phase", 0.); + const Real amp_B1 = pin->GetOrAddReal("b_field", "amp_B1", 0.); + const Real amp_B2 = pin->GetOrAddReal("b_field", "amp_B2", 0.); + const Real amp_B3 = pin->GetOrAddReal("b_field", "amp_B3", 0.); + const Real amp2_B1 = pin->GetOrAddReal("b_field", "amp2_B1", 0.); + const Real amp2_B2 = pin->GetOrAddReal("b_field", "amp2_B2", 0.); + const Real amp2_B3 = pin->GetOrAddReal("b_field", "amp2_B3", 0.); + + if (pkgs.count("B_CT")) { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // Fill at 3 different locations + pmb->par_for( + "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xembed[GR_DIM]; + double null1, null2; + double B_Pf1, B_Pf2, B_Pf3; + G.coord_embed(k, j, i, Loci::face1, Xembed); + GReal gdet = G.gdet(Loci::face1, j, i); + B_Pf1 = b10; + seed_b(Xembed, gdet, k1, k2, k3, phase, + amp_B1, amp_B2, amp_B3, + amp2_B1, amp2_B2, amp2_B3, + B_Pf1, null1, null2); + B_Uf(F1, 0, k, j, i) = B_Pf1 * gdet; + + G.coord_embed(k, j, i, Loci::face2, Xembed); + gdet = G.gdet(Loci::face2, j, i); + B_Pf2 = b20; + seed_b(Xembed, gdet, k1, k2, k3, phase, + amp_B1, amp_B2, amp_B3, + amp2_B1, amp2_B2, amp2_B3, + null1, B_Pf2, null2); + B_Uf(F2, 0, k, j, i) = B_Pf2; + + G.coord_embed(k, j, i, Loci::face3, Xembed); + gdet = G.gdet(Loci::face3, j, i); + B_Pf3 = b30; + seed_b(Xembed, gdet, k1, k2, k3, phase, + amp_B1, amp_B2, amp_B3, + amp2_B1, amp2_B2, amp2_B3, + null1, null2, B_Pf3); + B_Uf(F3, 0, k, j, i) = B_Pf3 * gdet; + } + ); + // Update primitive variables + B_CT::BlockUtoP(rc, domain); + } else if (pkgs.count("B_FluxCT")) { + GridVector B_P = rc->Get("prims.B").data; + pmb->par_for( + "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, Xembed); + const GReal gdet = G.gdet(Loci::center, j, i); + seed_b(Xembed, gdet, k1, k2, k3, phase, + amp_B1, amp_B2, amp_B3, + amp2_B1, amp2_B2, amp2_B3, + B_P(V1, k, j, i), + B_P(V2, k, j, i), + B_P(V3, k, j, i)); + } + ); + // We still need to update conserved flux values, but then we're done + B_FluxCT::BlockPtoU(rc, domain); + } + return TaskStatus::complete; + } else { // Seed with vector potential A otherwise + // Require and load what we need if necessary + Real A0 = pin->GetOrAddReal("b_field", "A0", 0.); + Real min_A = pin->GetOrAddReal("b_field", "min_A", 0.2); + // Init-specific loads + Real a, rin, rmax, gam, kappa, rho_norm, arg1; + Real tilt = 0; // Needs to be initialized + switch (Seed) { + case BSeedType::sane: + case BSeedType::mad: + case BSeedType::mad_quadrupole: + case BSeedType::r3s3: + case BSeedType::r5s5: + case BSeedType::gaussian: + // Torus parameters + rin = pin->GetReal("torus", "rin"); + rmax = pin->GetReal("torus", "rmax"); + kappa = pin->GetReal("torus", "kappa"); + tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; + // Other things we need only for torus evaluation + gam = pmb->packages.Get("GRMHD")->Param("gamma"); + rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); + a = G.coords.get_a(); + break; + case BSeedType::orszag_tang_a: + A0 = pin->GetReal("orszag_tang", "tscale"); + arg1 = pin->GetReal("orszag_tang", "phase"); + break; + default: + break; + } + + // For all other fields... + // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, + // But for tilted conditions we must keep track of all components + IndexSize3 sz = KDomain::GetBlockSize(rc); + ParArrayND A("A", NVEC, sz.n3, sz.n2, sz.n1); + pmb->par_for( + "B_field_A", b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + GReal Xnative[GR_DIM]; + GReal Xembed[GR_DIM], Xmidplane[GR_DIM]; + G.coord(k, j, i, Loci::corner, Xnative); + G.coord_embed(k, j, i, Loci::corner, Xembed); + // What are our corresponding "midplane" values for evaluating the function? + rotate_polar(Xembed, tilt, Xmidplane); + const GReal r = Xmidplane[1], th = Xmidplane[2]; + + // In case we need zone sizes + const GReal dxc[GR_DIM] = {0., G.Dxc<1>(i), G.Dxc<2>(j), G.Dxc<3>(k)}; + + // This is written under the assumption re-computed rho is more accurate than a bunch + // of averaging in a meaningful way. Just use the average if not. + Real rho_av; + if (is_torus) { + // Find rho at corner directly for torii + rho_av = fm_torus_rho(a, rin, rmax, gam, kappa, r, th) / rho_norm; + } else { + // Use averages for anything else + // This loop runs over every corner. Centers do not exist before the first + // or after the last, so use the last (ghost) zones available. + const int ii = clip((uint)i, b.is + 1, b.ie); + const int jj = clip((uint)j, b.js + 1, b.je); + const int kk = clip((uint)k, b.ks + 1, b.ke); + if (ndim > 2) + { + rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + + rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1) + + rho(kk - 1, jj, ii) + rho(kk - 1, jj, ii - 1) + + rho(kk - 1, jj - 1, ii) + rho(kk - 1, jj - 1, ii - 1)) / + 8; + } + else + { + rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + + rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1)) / + 4; + } + } + + Real Aphi = seed_a(Xmidplane, dxc, rho_av, rin, min_A, A0, arg1); + + if (tilt != 0.0) { + // This is *covariant* A_mu of an untilted disk + const double A_untilt_lower[GR_DIM] = {0., 0., 0., Aphi}; + // Raise to contravariant vector, since rotate_polar_vec will need that. + // Note we have to do this in the midplane! + // The coord_to_native calculation involves an iterative solve for MKS/FMKS + GReal Xnative_midplane[GR_DIM] = {0}, gcon_midplane[GR_DIM][GR_DIM] = {0}; + G.coords.coord_to_native(Xmidplane, Xnative_midplane); + G.coords.gcon_native(Xnative_midplane, gcon_midplane); + double A_untilt[GR_DIM] = {0}; + DLOOP2 A_untilt[mu] += gcon_midplane[mu][nu] * A_untilt_lower[nu]; + + // Then rotate + double A_tilt[GR_DIM] = {0}; + double A_untilt_embed[GR_DIM] = {0}, A_tilt_embed[GR_DIM] = {0}; + G.coords.con_vec_to_embed(Xnative_midplane, A_untilt, A_untilt_embed); + rotate_polar_vec(Xmidplane, A_untilt_embed, -tilt, Xembed, A_tilt_embed); + G.coords.con_vec_to_native(Xnative, A_tilt_embed, A_tilt); + + // Lower the result as we need curl(A_mu). Done at local zone. + double A_tilt_lower[GR_DIM] = {0}; + G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); + VLOOP A(v, k, j, i) = A_tilt_lower[1 + v]; + } else { + // Some problems rely on a very accurate A->B, which the rotation lacks. + // So, we preserve exact values in the no-tilt case. + A(V3, k, j, i) = Aphi; + } + }); + + if (pkgs.count("B_CT")) + { + auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); + // This fills a couple zones outside the exact interior with bad data + // Careful of that w/e.g. Dirichlet bounds. + IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); + if (ndim > 2) { + pmb->par_for( + "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_CT::curl_3D(G, A, B_Uf, k, j, i); + }); + } else if (ndim > 1) { + pmb->par_for( + "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_CT::curl_2D(G, A, B_Uf, k, j, i); + }); + } else { + throw std::runtime_error("Must initialize 1D field directly!"); + } + B_CT::BlockUtoP(rc, domain); + } else if (pkgs.count("B_FluxCT")) { + // Calculate B-field + GridVector B_U = rc->Get("cons.B").data; + IndexRange3 bl = KDomain::GetRange(rc, domain, 0, -1); // TODO will need changes if domain < entire + if (ndim > 2) { + pmb->par_for( + "B_field_B_3D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_FluxCT::averaged_curl_3D(G, A, B_U, k, j, i); + }); + } else if (ndim > 1) { + pmb->par_for( + "B_field_B_2D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + B_FluxCT::averaged_curl_2D(G, A, B_U, k, j, i); + }); + } else { + throw std::runtime_error("Must initialize 1D field directly!"); + } + // Finally, make sure we initialize the primitive field too + B_FluxCT::BlockUtoP(rc, domain); + } + + return TaskStatus::complete; + } +} + TaskStatus SeedBField(MeshData *md, ParameterInput *pin) { Flag("SeedBField"); @@ -81,7 +345,7 @@ TaskStatus SeedBField(MeshData *md, ParameterInput *pin) auto pmesh = md->GetMeshPointer(); const int verbose = pmesh->packages.Get("Globals")->Param("verbose"); - if (verbose) { + if (MPIRank0() && verbose) { std::cout << "Seeding B field with type " << b_field_type << std::endl; } @@ -115,6 +379,12 @@ TaskStatus SeedBField(MeshData *md, ParameterInput *pin) status = SeedBFieldType(rc, pin); } else if (b_field_type == "vertical") { status = SeedBFieldType(rc, pin); + } else if (b_field_type == "orszag_tang") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "orszag_tang_a") { + status = SeedBFieldType(rc, pin); + } else if (b_field_type == "wave") { + status = SeedBFieldType(rc, pin); } else { throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); } diff --git a/kharma/prob/seed_B.hpp b/kharma/prob/seed_B.hpp index 0f08487d..61537a2f 100644 --- a/kharma/prob/seed_B.hpp +++ b/kharma/prob/seed_B.hpp @@ -40,11 +40,11 @@ TaskStatus SeedBField(MeshData *md, ParameterInput *pin); TaskStatus NormalizeBField(MeshData *md, ParameterInput *pin); -// Internal representation of the field initialization preference for quick switch -// Avoids string comparsion in kernels -enum BSeedType{constant, monopole, monopole_cube, sane, mad, mad_quadrupole, r3s3, r5s5, gaussian, bz_monopole, vertical}; +// Internal representation of the field initialization preference, used for templating +enum BSeedType{constant, monopole, monopole_cube, orszag_tang, orszag_tang_a, wave, + sane, mad, mad_quadrupole, r3s3, r5s5, gaussian, bz_monopole, vertical}; -#define SEEDA_ARGS GReal *x, double rho, double rin, double min_A, double A0 +#define SEEDA_ARGS GReal *x, const GReal *dxc, double rho, double rin, double min_A, double A0, double arg1 // This will also act as the default implementation for unspecified types, // which should all be filled as B field by seed_b below. @@ -98,7 +98,6 @@ KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) // Pure vertical threaded field of gaussian strength with FWHM 2*rin (i.e. HM@rin) // centered at BH center -// Block is to avoid compiler whinging about initialization template<> KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) { @@ -114,31 +113,57 @@ KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) return A0 * x[1] * m::sin(x[2]) / 2.; } -#define SEEDB_ARGS GReal *x, GReal gdet, double b10, double b20, double b30, double &B1, double &B2, double &B3 +template<> +KOKKOS_INLINE_FUNCTION Real seed_a(SEEDA_ARGS) +{ + return A0 * (-0.5 * std::cos(2*x[1] + arg1) + + std::cos(x[2] + arg1)); +} + +#undef SEEDA_ARGS +#define SEEDB_ARGS GReal *x, GReal gdet, double k1, double k2, double k3, double phase, \ + double amp_B1, double amp_B2, double amp_B3, \ + double amp2_B1, double amp2_B2, double amp2_B3, \ + double &B1, double &B2, double &B3 template -KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) {} +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) { B1 = 0./0.; B2 = 0./0.; B3 = 0./0.; } +// Constant field of B10, B20, B30 is always set template<> -KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) -{ - B1 = b10; - B2 = b20; - B3 = b30; -} +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) {} +// Reduce radial component by gdet for constant flux template<> KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) { - B1 = b10 / gdet; - B2 = 0.; - B3 = 0.; + B1 /= gdet; } +// Reduce radial component by the cube of radius template<> KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) { - B1 = 1 / (x[1]*x[1]*x[1]); - B2 = 0.; - B3 = 0.; + B1 /= (x[1]*x[1]*x[1]); } + +// For mhdmodes or linear waves tests +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + const Real smode = m::cos(k1 * x[1] + k2 * x[2] + k3 * x[3] + phase); + const Real cmode = m::cos(k1 * x[1] + k2 * x[2] + k3 * x[3] + phase); + B1 += amp_B1 * cmode + amp2_B1 * smode; + B2 += amp_B2 * cmode + amp2_B2 * smode; + B3 += amp_B3 * cmode + amp2_B3 * smode; +} + +// For Orszag-Tang vortex +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + B1 -= amp_B1 * m::sin( x[2] + phase ); + B2 += amp_B2 * m::sin(2.*(x[1] + phase)); +} + +#undef SEEDB_ARGS \ No newline at end of file diff --git a/kharma/prob/seed_B_impl.hpp b/kharma/prob/seed_B_impl.hpp deleted file mode 100644 index bf7dbea8..00000000 --- a/kharma/prob/seed_B_impl.hpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * File: seed_B.hpp - * - * BSD 3-Clause License - * - * Copyright (c) 2020, AFD Group at UIUC - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once - -#include "seed_B.hpp" - -#include "b_flux_ct.hpp" -#include "b_ct.hpp" -#include "boundaries.hpp" -#include "domain.hpp" -#include "fm_torus.hpp" - -template -TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDomain domain = IndexDomain::entire) -{ - auto pmb = rc->GetBlockPointer(); - auto pkgs = pmb->packages.AllPackages(); - - // Fields - GridScalar rho = rc->Get("prims.rho").data; - const auto &G = pmb->coords; - - // Parameters - std::string b_field_type = pin->GetString("b_field", "type"); - auto prob = pin->GetString("parthenon/job", "problem_id"); - bool is_torus = (prob == "torus"); - - // Indices - IndexRange3 b = KDomain::GetRange(rc, domain); - int ndim = pmb->pmy_mesh->ndim; - - // Shortcut to field values for easy fields - if constexpr (Seed == BSeedType::constant || - Seed == BSeedType::monopole || - Seed == BSeedType::monopole_cube) - { - if (pkgs.count("B_CT")) - { - auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); - Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); - Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); - // Fill at 3 different locations - // TODO this would need to be extended for domain < entire - pmb->par_for( - "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, Loci::face1, Xembed); - GReal gdet = G.gdet(Loci::face1, j, i); - double tmp1, tmp2; - seed_b(Xembed, gdet, b10, b20, b30, - B_Uf(F1, 0, k, j, i), tmp1, tmp2); - - G.coord_embed(k, j, i, Loci::face2, Xembed); - gdet = G.gdet(Loci::face2, j, i); - seed_b(Xembed, gdet, b10, b20, b30, - tmp1, B_Uf(F2, 0, k, j, i), tmp2); - - G.coord_embed(k, j, i, Loci::face3, Xembed); - gdet = G.gdet(Loci::face3, j, i); - seed_b(Xembed, gdet, b10, b20, b30, - tmp1, tmp2, B_Uf(F3, 0, k, j, i)); - }); - // Update primitive variables - B_CT::BlockUtoP(rc, domain); - } - else if (pkgs.count("B_FluxCT")) - { - GridVector B_P = rc->Get("prims.B").data; - Real b10 = pin->GetOrAddReal("b_field", "b10", 0.); - Real b20 = pin->GetOrAddReal("b_field", "b20", 0.); - Real b30 = pin->GetOrAddReal("b_field", "b30", 0.); - pmb->par_for( - "B_field_B", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - GReal Xembed[GR_DIM]; - G.coord_embed(k, j, i, Loci::center, Xembed); - const GReal gdet = G.gdet(Loci::center, j, i); - seed_b(Xembed, gdet, b10, b20, b30, - B_P(V1, k, j, i), - B_P(V2, k, j, i), - B_P(V3, k, j, i)); - }); - // We still need to update conserved flux values, but then we're done - B_FluxCT::BlockPtoU(rc, domain); - } - return TaskStatus::complete; - } - - // Require and load what we need if necessary - // TODO this seems very inelegant. Also most of these should support non-FM-torii - // as long as we don't call fm_torus_rho below - Real a, rin, rmax, gam, kappa, rho_norm; - Real tilt = 0; // Needs to be initialized - switch (Seed) - { - case BSeedType::sane: - case BSeedType::mad: - case BSeedType::mad_quadrupole: - case BSeedType::r3s3: - case BSeedType::r5s5: - case BSeedType::gaussian: - if (!is_torus) - throw std::invalid_argument("Magnetic field seed " + b_field_type + " supports only torus problems!"); - // Torus parameters - rin = pin->GetReal("torus", "rin"); - rmax = pin->GetReal("torus", "rmax"); - kappa = pin->GetReal("torus", "kappa"); - tilt = pin->GetReal("torus", "tilt") / 180. * M_PI; - // Other things we need only for torus evaluation - gam = pmb->packages.Get("GRMHD")->Param("gamma"); - rho_norm = pmb->packages.Get("GRMHD")->Param("rho_norm"); - a = G.coords.get_a(); - break; - default: - break; - } - - Real A0 = pin->GetOrAddReal("b_field", "A0", 0.); - Real min_A = pin->GetOrAddReal("b_field", "min_A", 0.2); // TODO back compat? Doubtful was used - - // For all other fields... - // Find the magnetic vector potential. In X3 symmetry only A_phi is non-zero, - // But for tilted conditions we must keep track of all components - IndexSize3 sz = KDomain::GetBlockSize(rc); - ParArrayND A("A", NVEC, sz.n3, sz.n2, sz.n1); - pmb->par_for( - "B_field_A", b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - GReal Xnative[GR_DIM]; - GReal Xembed[GR_DIM], Xmidplane[GR_DIM]; - G.coord(k, j, i, Loci::corner, Xnative); - G.coord_embed(k, j, i, Loci::corner, Xembed); - // What are our corresponding "midplane" values for evaluating the function? - rotate_polar(Xembed, tilt, Xmidplane); - const GReal r = Xmidplane[1], th = Xmidplane[2]; - - // This is written under the assumption re-computed rho is more accurate than a bunch - // of averaging in a meaningful way. Just use the average if not. - Real rho_av; - if (is_torus) - { - // Find rho at corner directly for torii - rho_av = fm_torus_rho(a, rin, rmax, gam, kappa, r, th) / rho_norm; - } - else - { - // Use averages for anything else - // This loop runs over every corner. Centers do not exist before the first - // or after the last, so use the last (ghost) zones available. - const int ii = clip((uint)i, b.is + 1, b.ie); - const int jj = clip((uint)j, b.js + 1, b.je); - const int kk = clip((uint)k, b.ks + 1, b.ke); - if (ndim > 2) - { - rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + - rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1) + - rho(kk - 1, jj, ii) + rho(kk - 1, jj, ii - 1) + - rho(kk - 1, jj - 1, ii) + rho(kk - 1, jj - 1, ii - 1)) / - 8; - } - else - { - rho_av = (rho(kk, jj, ii) + rho(kk, jj, ii - 1) + - rho(kk, jj - 1, ii) + rho(kk, jj - 1, ii - 1)) / - 4; - } - } - - Real Aphi = seed_a(Xmidplane, rho_av, rin, min_A, A0); - - if (tilt != 0.0) - { - // This is *covariant* A_mu of an untilted disk - const double A_untilt_lower[GR_DIM] = {0., 0., 0., Aphi}; - // Raise to contravariant vector, since rotate_polar_vec will need that. - // Note we have to do this in the midplane! - // The coord_to_native calculation involves an iterative solve for MKS/FMKS - GReal Xnative_midplane[GR_DIM] = {0}, gcon_midplane[GR_DIM][GR_DIM] = {0}; - G.coords.coord_to_native(Xmidplane, Xnative_midplane); - G.coords.gcon_native(Xnative_midplane, gcon_midplane); - double A_untilt[GR_DIM] = {0}; - DLOOP2 A_untilt[mu] += gcon_midplane[mu][nu] * A_untilt_lower[nu]; - - // Then rotate - double A_tilt[GR_DIM] = {0}; - double A_untilt_embed[GR_DIM] = {0}, A_tilt_embed[GR_DIM] = {0}; - G.coords.con_vec_to_embed(Xnative_midplane, A_untilt, A_untilt_embed); - rotate_polar_vec(Xmidplane, A_untilt_embed, -tilt, Xembed, A_tilt_embed); - G.coords.con_vec_to_native(Xnative, A_tilt_embed, A_tilt); - - // Lower the result as we need curl(A_mu). Done at local zone. - double A_tilt_lower[GR_DIM] = {0}; - G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); - VLOOP A(v, k, j, i) = A_tilt_lower[1 + v]; - } - else - { - // Some problems rely on a very accurate A->B, which the rotation lacks. - // So, we preserve exact values in the no-tilt case. - A(V3, k, j, i) = Aphi; - } - }); - - if (pkgs.count("B_CT")) - { - auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - // This fills a couple zones outside the exact interior with bad data - // Careful of that w/e.g. Dirichlet bounds. - IndexRange3 bB = KDomain::GetRange(rc, domain, 0, -1); - if (ndim > 2) - { - pmb->par_for( - "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - B_CT::curl_3D(G, A, B_Uf, k, j, i); - }); - } - else if (ndim > 1) - { - pmb->par_for( - "ot_B", bB.ks, bB.ke, bB.js, bB.je, bB.is, bB.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - B_CT::curl_2D(G, A, B_Uf, k, j, i); - }); - } - else - { - throw std::runtime_error("Must initialize 1D field directly!"); - } - B_CT::BlockUtoP(rc, domain); - } - else if (pkgs.count("B_FluxCT")) - { - // Calculate B-field - GridVector B_U = rc->Get("cons.B").data; - IndexRange3 bl = KDomain::GetRange(rc, domain, 0, -1); // TODO will need changes if domain < entire - if (ndim > 2) - { - pmb->par_for( - "B_field_B_3D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - B_FluxCT::averaged_curl_3D(G, A, B_U, k, j, i); - }); - } - else if (ndim > 1) - { - pmb->par_for( - "B_field_B_2D", bl.ks, bl.ke, bl.js, bl.je, bl.is, bl.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - B_FluxCT::averaged_curl_2D(G, A, B_U, k, j, i); - }); - } - else - { - throw std::runtime_error("Must initialize 1D field directly!"); - } - // Finally, make sure we initialize the primitive field too - B_FluxCT::BlockUtoP(rc, domain); - } - - return TaskStatus::complete; -} \ No newline at end of file diff --git a/kharma/prob/blob.hpp b/kharma/prob/utils/blob.hpp similarity index 100% rename from kharma/prob/blob.hpp rename to kharma/prob/utils/blob.hpp diff --git a/kharma/prob/hdf5_utils.cpp b/kharma/prob/utils/hdf5_utils.cpp similarity index 100% rename from kharma/prob/hdf5_utils.cpp rename to kharma/prob/utils/hdf5_utils.cpp diff --git a/kharma/prob/hdf5_utils.h b/kharma/prob/utils/hdf5_utils.h similarity index 100% rename from kharma/prob/hdf5_utils.h rename to kharma/prob/utils/hdf5_utils.h diff --git a/kharma/prob/interpolation.hpp b/kharma/prob/utils/interpolation.hpp similarity index 100% rename from kharma/prob/interpolation.hpp rename to kharma/prob/utils/interpolation.hpp diff --git a/kharma/prob/utils/perturbation.hpp b/kharma/prob/utils/perturbation.hpp new file mode 100644 index 00000000..7a453302 --- /dev/null +++ b/kharma/prob/utils/perturbation.hpp @@ -0,0 +1,102 @@ +/* + * File: perturbation.hpp + * + * BSD 3-Clause License + * + * Copyright (c) 2020, AFD Group at UIUC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include "decs.hpp" + +#include +#include "Kokkos_Random.hpp" + +/** + * Perturb the internal energy by a uniform random proportion per cell. + * Resulting internal energies will be between u \pm u*u_jitter/2 + * i.e. u_jitter=0.1 -> \pm 5% randomization, 0.95u to 1.05u + * + * @param u_jitter see description + * @param rng_seed is added to the MPI rank to seed the GSL RNG + */ +TaskStatus PerturbU(std::shared_ptr>& rc, ParameterInput *pin) +{ + auto pmb = rc->GetBlockPointer(); + auto rho = rc->Get("prims.rho").data; + auto u = rc->Get("prims.u").data; + + const Real u_jitter = pin->GetReal("perturbation", "u_jitter"); + // Don't jitter values set by floors + const Real jitter_above_rho = pin->GetReal("floors", "rho_min_geom") + 1e-10; + // Note we add the MeshBlock gid to this value when seeding RNG, + // to get a new sequence for every block + const int rng_seed = pin->GetOrAddInteger("perturbation", "rng_seed", 31337); + // Print real seed used for all blocks, to ensure they're different + if (pmb->packages.Get("Globals")->Param("verbose") > 1) { + std::cout << "Seeding RNG in block " << pmb->gid << " with value " << rng_seed + pmb->gid << std::endl; + } + const bool serial = pin->GetOrAddInteger("perturbation", "serial", false); + + // Should we jitter ghosts? If first boundary sync doesn't work it's marginally less disruptive + IndexDomain domain = IndexDomain::interior; + const int is = pmb->cellbounds.is(domain), ie = pmb->cellbounds.ie(domain); + const int js = pmb->cellbounds.js(domain), je = pmb->cellbounds.je(domain); + const int ks = pmb->cellbounds.ks(domain), ke = pmb->cellbounds.ke(domain); + + if (serial) { + // Serial version + // Probably guarantees better determinism, but CPU single-thread only + std::mt19937 gen(rng_seed + pmb->gid); + std::uniform_real_distribution dis(-u_jitter/2, u_jitter/2); + + auto u_host = u.GetHostMirrorAndCopy(); + for(int k=ks; k <= ke; k++) + for(int j=js; j <= je; j++) + for(int i=is; i <= ie; i++) + u_host(k, j, i) *= 1. + dis(gen); + u.DeepCopy(u_host); + } else { + // Kokkos version + typedef typename Kokkos::Random_XorShift64_Pool<> RandPoolType; + RandPoolType rand_pool(rng_seed + pmb->gid); + typedef typename RandPoolType::generator_type gen_type; + pmb->par_for("perturb_u", ks, ke, js, je, is, ie, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + if (rho(k, j, i) > jitter_above_rho) { + gen_type rgen = rand_pool.get_state(); + u(k, j, i) *= 1. + Kokkos::rand::draw(rgen, -u_jitter/2, u_jitter/2); + rand_pool.free_state(rgen); + } + } + ); + } + + return TaskStatus::complete; +} From bbb673306f5368c463b556b1804fe1e896fd868b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 29 Sep 2023 11:33:56 -0600 Subject: [PATCH 140/219] Fix compile error with Parthenon Kokkos vbump --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index eede5cd0..02898b68 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit eede5cd09f4d669d4fc97923d51eeca35f4dcd29 +Subproject commit 02898b683d2a33da5f7e912916e4ce367b733635 From ca3b994154c73997a6fce7aa42e7e886bdb00732 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Oct 2023 13:56:17 -0600 Subject: [PATCH 141/219] Reorganize parameter files This finally organizes the different parameter files into folders. Plenty of parfiles fit more than one category, feel free to move them. Better parfile documentation is planned as we bring up the rest of the tests. --- pars/{ => benchmark}/sane_perf.par | 0 pars/{ => benchmark}/scaling_torus.par | 0 pars/{ => bondi}/bondi.par | 0 pars/{ => bondi}/bondi_b.par | 0 pars/{ => bondi}/bondi_b_vertical.par | 0 pars/conducting_atmosphere.par | 98 ------------------- pars/{ => electrons}/driven_turbulence.par | 0 pars/{ => electrons}/hubble.par | 0 pars/{ => electrons}/noh.par | 12 +-- pars/{ => electrons}/rest_conserve.par | 2 +- pars/{ => emhd}/anisotropic_conduction.par | 0 pars/{ => emhd}/bondi_viscous.par | 16 +-- pars/{ => emhd}/emhdmodes.par | 0 pars/{ => emhd}/orszag_tang_viscous.par | 4 +- pars/emhdshock.par | 94 ------------------ pars/orszag_tang_face_ct.par | 67 ------------- pars/{ => restarts}/resize_restart.par | 0 pars/shocks/komissarov_collision.par | 2 +- pars/shocks/komissarov_fast.par | 2 +- pars/shocks/komissarov_shock_1.par | 2 +- pars/shocks/komissarov_shock_2.par | 2 +- pars/shocks/komissarov_slow.par | 2 +- pars/shocks/komissarov_switch_off.par | 2 +- pars/shocks/komissarov_switch_on.par | 2 +- .../orszag_tang_refined.par} | 0 pars/{ => smr}/sane2d_refined.par | 0 pars/{ => smr}/sane3d_refined.par | 0 pars/{ => tests}/bz_monopole.par | 2 +- pars/{ => tests}/explosion.par | 0 pars/{ => tests}/kelvin_helmholtz.par | 0 pars/{ => tests}/mhdmodes.par | 12 +-- pars/{ => tests}/orszag_tang.par | 0 pars/{ => tori_2d}/sane2d.par | 14 +-- pars/{ => tori_2d}/sane2d_cooling.par | 0 pars/{ => tori_2d}/sane_divb_2d.par | 0 pars/{ => tori_3d}/eht_comp.par | 0 pars/{ => tori_3d}/mad.par | 0 pars/{ => tori_3d}/mad_test.par | 0 pars/{ => tori_3d}/mad_tilt.par | 0 pars/{ => tori_3d}/sane.par | 0 pars/{ => tori_3d}/sane_emhd.par | 0 pars/{ => tori_3d}/sane_imex.par | 0 pars/{ => tori_3d}/sane_tilt.par | 0 scripts/batch/polaris.qsub | 2 +- scripts/batch/scaling_delta.sb | 4 +- scripts/batch/scaling_frontera.sb | 4 +- scripts/batch/scaling_longhorn.sb | 4 +- scripts/batch/scaling_polaris.qsub | 2 +- scripts/batch/scaling_summit.bsub | 2 +- tests/all_pars/run.sh | 10 +- tests/run_all.sh | 11 +++ 51 files changed, 64 insertions(+), 310 deletions(-) rename pars/{ => benchmark}/sane_perf.par (100%) rename pars/{ => benchmark}/scaling_torus.par (100%) rename pars/{ => bondi}/bondi.par (100%) rename pars/{ => bondi}/bondi_b.par (100%) rename pars/{ => bondi}/bondi_b_vertical.par (100%) delete mode 100644 pars/conducting_atmosphere.par rename pars/{ => electrons}/driven_turbulence.par (100%) rename pars/{ => electrons}/hubble.par (100%) rename pars/{ => electrons}/noh.par (90%) rename pars/{ => electrons}/rest_conserve.par (97%) rename pars/{ => emhd}/anisotropic_conduction.par (100%) rename pars/{ => emhd}/bondi_viscous.par (86%) rename pars/{ => emhd}/emhdmodes.par (100%) rename pars/{ => emhd}/orszag_tang_viscous.par (95%) delete mode 100644 pars/emhdshock.par delete mode 100644 pars/orszag_tang_face_ct.par rename pars/{ => restarts}/resize_restart.par (100%) rename pars/{orszag_tang_smr.par => smr/orszag_tang_refined.par} (100%) rename pars/{ => smr}/sane2d_refined.par (100%) rename pars/{ => smr}/sane3d_refined.par (100%) rename pars/{ => tests}/bz_monopole.par (96%) rename pars/{ => tests}/explosion.par (100%) rename pars/{ => tests}/kelvin_helmholtz.par (100%) rename pars/{ => tests}/mhdmodes.par (95%) rename pars/{ => tests}/orszag_tang.par (100%) rename pars/{ => tori_2d}/sane2d.par (79%) rename pars/{ => tori_2d}/sane2d_cooling.par (100%) rename pars/{ => tori_2d}/sane_divb_2d.par (100%) rename pars/{ => tori_3d}/eht_comp.par (100%) rename pars/{ => tori_3d}/mad.par (100%) rename pars/{ => tori_3d}/mad_test.par (100%) rename pars/{ => tori_3d}/mad_tilt.par (100%) rename pars/{ => tori_3d}/sane.par (100%) rename pars/{ => tori_3d}/sane_emhd.par (100%) rename pars/{ => tori_3d}/sane_imex.par (100%) rename pars/{ => tori_3d}/sane_tilt.par (100%) create mode 100755 tests/run_all.sh diff --git a/pars/sane_perf.par b/pars/benchmark/sane_perf.par similarity index 100% rename from pars/sane_perf.par rename to pars/benchmark/sane_perf.par diff --git a/pars/scaling_torus.par b/pars/benchmark/scaling_torus.par similarity index 100% rename from pars/scaling_torus.par rename to pars/benchmark/scaling_torus.par diff --git a/pars/bondi.par b/pars/bondi/bondi.par similarity index 100% rename from pars/bondi.par rename to pars/bondi/bondi.par diff --git a/pars/bondi_b.par b/pars/bondi/bondi_b.par similarity index 100% rename from pars/bondi_b.par rename to pars/bondi/bondi_b.par diff --git a/pars/bondi_b_vertical.par b/pars/bondi/bondi_b_vertical.par similarity index 100% rename from pars/bondi_b_vertical.par rename to pars/bondi/bondi_b_vertical.par diff --git a/pars/conducting_atmosphere.par b/pars/conducting_atmosphere.par deleted file mode 100644 index 694a8b1b..00000000 --- a/pars/conducting_atmosphere.par +++ /dev/null @@ -1,98 +0,0 @@ -# Hydrostatic conducting atmosphere -# Try to maintain the ODE solution that represnts hydrostatic equilibrium -# Checks the geometrical terms -# IMPORTANT: This test is different from the other tests in its initialization -# It reads in ".txt" files that correspond to the ODE solution (set input to "ODE" in ) -# Run it with a single MPI task - - -problem_id = conducting_atmosphere - - -refinement = none -numlevel = 1 -nx1 = 256 -nx2 = 256 -nx3 = 1 - - -nx1 = 256 -nx2 = 256 -nx3 = 1 - - - -base = ks -transform = mks -a = 0.0 -hslope = 1.0 -r_in = 200. -r_out = 300. - - -inner_x1 = dirichlet -outer_x1 = dirichlet -check_inflow_inner_x1 = false -check_inflow_outer_x1 = false - - -tlim = 150. - - -type = imex - - -implicit = true -cfl = 0.9 -gamma = 1.333333 -reconstruction = weno5 - - -implicit = false -initial_cleanup = false - - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 -linesearch = true -max_linesearch_iter = 3 -linesearch_eps = 1.e-4 - -# IMPORTANT: This block must be present and values filled in all EGRMHD simulations - -on = true -higher_order_terms = true -feedback = true -stability_limits = false - -conduction = true -viscosity = false - -closure_type = kappa_eta -tau = 10. -kappa = 0.1 -eta = 0.0 - - -input = ODE - - -disable_floors = true - - -verbose = 1 -flag_verbose = 2 -extra_checks = 1 - - -file_type = hdf5 -dt = 10 -single_precision_output = false -#variables = prims, cons, solve_norm, solve_fail -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, solve_norm, solve_fail -ghost_zones = true - - -file_type = hst -dt = 100 diff --git a/pars/driven_turbulence.par b/pars/electrons/driven_turbulence.par similarity index 100% rename from pars/driven_turbulence.par rename to pars/electrons/driven_turbulence.par diff --git a/pars/hubble.par b/pars/electrons/hubble.par similarity index 100% rename from pars/hubble.par rename to pars/electrons/hubble.par diff --git a/pars/noh.par b/pars/electrons/noh.par similarity index 90% rename from pars/noh.par rename to pars/electrons/noh.par index d7af06c7..015b9057 100644 --- a/pars/noh.par +++ b/pars/electrons/noh.par @@ -51,11 +51,11 @@ solver = none on = true constant = true -gamma_e = 1.333333 +gamma_e = 1.666667 fel_0 = 0. fel_constant = 0.5 -diss_sign = false -kel_min = false +enforce_positive_dissipation = false +limit_kel = false mach = 49. @@ -65,8 +65,8 @@ zero_ug = false set_tlim = true centered = false - -disable_floors = true +# +#disable_floors = true type = imex @@ -76,7 +76,7 @@ verbose = 0 file_type = hdf5 -dt = 0.1 +dt = 10 single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant diff --git a/pars/rest_conserve.par b/pars/electrons/rest_conserve.par similarity index 97% rename from pars/rest_conserve.par rename to pars/electrons/rest_conserve.par index 327b9b5c..86504eb3 100644 --- a/pars/rest_conserve.par +++ b/pars/electrons/rest_conserve.par @@ -2,7 +2,7 @@ # Try to propagate several analytically-amenable linear modes of the MHD equations -problem_id = rest_conserve +problem_id = hubble refinement = none diff --git a/pars/anisotropic_conduction.par b/pars/emhd/anisotropic_conduction.par similarity index 100% rename from pars/anisotropic_conduction.par rename to pars/emhd/anisotropic_conduction.par diff --git a/pars/bondi_viscous.par b/pars/emhd/bondi_viscous.par similarity index 86% rename from pars/bondi_viscous.par rename to pars/emhd/bondi_viscous.par index d7b10a2b..f7b15d23 100644 --- a/pars/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -4,9 +4,6 @@ problem_id = bondi -# Full mesh size, no refinement -refinement = none -numlevel = 1 nx1 = 128 nx2 = 128 nx3 = 1 @@ -21,10 +18,8 @@ base = ks transform = mks a = 0.0 hslope = 1.0 -r_out = 20 -# Needed to place 5 zones inside 3M, -# to match the analytic files. -Rhor = 3 +r_out = 20.0 +r_in = 5.0 tlim = 400.0 @@ -32,7 +27,6 @@ tlim = 400.0 cfl = 0.9 gamma = 1.666667 -reconstruction = weno5 implicit = true @@ -40,9 +34,6 @@ type = monopole_cube implicit = false initial_cleanup = false - -type = imex - min_nonlinear_iter = 1 max_nonlinear_iter = 3 @@ -75,7 +66,9 @@ disable_floors = true outer_x1 = dirichlet +inner_x1 = dirichlet check_inflow_outer_x1 = false +#check_inflow_inner_x1 = false verbose = 1 @@ -86,6 +79,7 @@ extra_checks = 1 file_type = hdf5 dt = 100.0 single_precision_output = false +ghost_zones = true variables = prims, solve_norm, solve_fail diff --git a/pars/emhdmodes.par b/pars/emhd/emhdmodes.par similarity index 100% rename from pars/emhdmodes.par rename to pars/emhd/emhdmodes.par diff --git a/pars/orszag_tang_viscous.par b/pars/emhd/orszag_tang_viscous.par similarity index 95% rename from pars/orszag_tang_viscous.par rename to pars/emhd/orszag_tang_viscous.par index d566e5ed..cdec24fc 100644 --- a/pars/orszag_tang_viscous.par +++ b/pars/emhd/orszag_tang_viscous.par @@ -44,8 +44,10 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 - + on = true +viscosity = true +conduction = false verbose = 0 diff --git a/pars/emhdshock.par b/pars/emhdshock.par deleted file mode 100644 index 26e191b0..00000000 --- a/pars/emhdshock.par +++ /dev/null @@ -1,94 +0,0 @@ -# EMHD Shock problem -# Try to maintain the BVP solution to a discontuinity -# Checks the higher order terms implementation in flat space -# IMPORTANT: This test is different from the other tests in its initialization -# It reads in ".txt" files that correspond to the BVP solution (set input to "BVP" in ) -# One, in principle, can run this problem with the usual ideal MHD jump conditions but this -# may not allow a quantitative check -# Run it with a single MPI task - - -problem_id = emhdshock - - -refinement = none -numlevel = 1 - -nx1 = 1024 -x1min = -0.5 -x1max = 1.5 -ix1_bc = outflow -ox1_bc = outflow - -nx2 = 1 -x2min = 0.0 -x2max = 1.0 -ix2_bc = periodic -ox2_bc = periodic - -nx3 = 1 -x3min = 0.0 -x3max = 1.0 -ix3_bc = periodic -ox3_bc = periodic - - -nx1 = 1024 -nx2 = 1 -nx3 = 1 - - -base = cartesian_minkowski -transform = null - - -# "RK2" is the only option for implicit solver -tlim = 0.5 -integrator = rk2 -dt_min = 1.e-6 - - -cfl = 0.25 -gamma = 1.333333 -reconstruction = linear_mc - - -implicit = true -initial_cleanup = false - -# IMPORTANT: This block must be present and values filled in all EGRMHD simulations - -on = true -higher_order_terms = true - -closure_type = soundspeed -tau = 0.1 -conduction_alpha = 5.0 -viscosity_alpha = 3.0 - - -max_nonlinear_iter = 3 -rootfind_tol = 1.e-20 -jacobian_delta = 4.e-8 - - -# The input can be the BVP solution or the ideal MHD Rankine-Hugoniot jump conditions -input = BVP - - -disable_floors = true - - -verbose = 1 -flag_verbose = 2 -extra_checks = 1 - - -file_type = hdf5 -dt = 0.05 -single_precision_output = false -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP - - -file_type = hst -dt = 0.1 \ No newline at end of file diff --git a/pars/orszag_tang_face_ct.par b/pars/orszag_tang_face_ct.par deleted file mode 100644 index 3a7eca6d..00000000 --- a/pars/orszag_tang_face_ct.par +++ /dev/null @@ -1,67 +0,0 @@ -# Orszag-Tang Vortex problem: -# Generate current sheets on short timescales - - -problem_id = orszag_tang - - -nx1 = 256 -x1min = -3.141592653589793 -x1max = 3.141592653589793 - -nx2 = 128 -x2min = -3.141592653589793 -x2max = 3.141592653589793 - -nx3 = 1 -x3min = -0.01 -x3max = 0.01 - - -nx1 = 128 -nx2 = 128 -nx3 = 1 - - -base = cartesian_minkowski -transform = null - - -tlim = 100.0 -integrator = rk2 - - -cfl = 0.9 -gamma = 1.666667 -reconstruction = weno5 - - -solver = face_ct -kill_on_large_divb = true -ct_scheme = bs99 - - -verbose = 1 -flag_verbose = 2 -extra_checks = 1 - - -disable_floors = true - - -file_type = hdf5 -dt = 1 -single_precision_output = true -# TODO just prims when face fields supported -variables = prims.rho, prims.u, prims.uvec, prims.B, divB, jcon -ghost_zones = true - - -file_type = hst -dt = 0.1 - -# This problem is generally much too short to need -# checkpointing. However, we have a test which uses it. -# -#file_type = rst -#dt = 10.0 diff --git a/pars/resize_restart.par b/pars/restarts/resize_restart.par similarity index 100% rename from pars/resize_restart.par rename to pars/restarts/resize_restart.par diff --git a/pars/shocks/komissarov_collision.par b/pars/shocks/komissarov_collision.par index fbefb3a8..9da07f16 100644 --- a/pars/shocks/komissarov_collision.par +++ b/pars/shocks/komissarov_collision.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1. diff --git a/pars/shocks/komissarov_fast.par b/pars/shocks/komissarov_fast.par index 22d25c40..4b1a3d55 100644 --- a/pars/shocks/komissarov_fast.par +++ b/pars/shocks/komissarov_fast.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.4 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1. diff --git a/pars/shocks/komissarov_shock_1.par b/pars/shocks/komissarov_shock_1.par index 29273e3d..8aa79aa7 100644 --- a/pars/shocks/komissarov_shock_1.par +++ b/pars/shocks/komissarov_shock_1.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1. diff --git a/pars/shocks/komissarov_shock_2.par b/pars/shocks/komissarov_shock_2.par index 72941ec0..afde2d88 100644 --- a/pars/shocks/komissarov_shock_2.par +++ b/pars/shocks/komissarov_shock_2.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1. diff --git a/pars/shocks/komissarov_slow.par b/pars/shocks/komissarov_slow.par index 6f319699..9fb91b67 100644 --- a/pars/shocks/komissarov_slow.par +++ b/pars/shocks/komissarov_slow.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1. diff --git a/pars/shocks/komissarov_switch_off.par b/pars/shocks/komissarov_switch_off.par index 17ea2977..1afa3ded 100644 --- a/pars/shocks/komissarov_switch_off.par +++ b/pars/shocks/komissarov_switch_off.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 0.1 diff --git a/pars/shocks/komissarov_switch_on.par b/pars/shocks/komissarov_switch_on.par index 4fbf9159..cd094493 100644 --- a/pars/shocks/komissarov_switch_on.par +++ b/pars/shocks/komissarov_switch_on.par @@ -43,7 +43,7 @@ dt_min = 0.0001 cfl = 0.5 gamma = 1.333333 -reconstruction = linear_vl +reconstruction = linear_mc rhoL = 1.78e-3 diff --git a/pars/orszag_tang_smr.par b/pars/smr/orszag_tang_refined.par similarity index 100% rename from pars/orszag_tang_smr.par rename to pars/smr/orszag_tang_refined.par diff --git a/pars/sane2d_refined.par b/pars/smr/sane2d_refined.par similarity index 100% rename from pars/sane2d_refined.par rename to pars/smr/sane2d_refined.par diff --git a/pars/sane3d_refined.par b/pars/smr/sane3d_refined.par similarity index 100% rename from pars/sane3d_refined.par rename to pars/smr/sane3d_refined.par diff --git a/pars/bz_monopole.par b/pars/tests/bz_monopole.par similarity index 96% rename from pars/bz_monopole.par rename to pars/tests/bz_monopole.par index cbf3b13f..5ed92c7f 100644 --- a/pars/bz_monopole.par +++ b/pars/tests/bz_monopole.par @@ -37,7 +37,7 @@ flag_verbose = 0 cfl = 0.7 gamma = 1.444444 -reconstruction = weno5 +reconstruction = linear_mc type = bz_monopole diff --git a/pars/explosion.par b/pars/tests/explosion.par similarity index 100% rename from pars/explosion.par rename to pars/tests/explosion.par diff --git a/pars/kelvin_helmholtz.par b/pars/tests/kelvin_helmholtz.par similarity index 100% rename from pars/kelvin_helmholtz.par rename to pars/tests/kelvin_helmholtz.par diff --git a/pars/mhdmodes.par b/pars/tests/mhdmodes.par similarity index 95% rename from pars/mhdmodes.par rename to pars/tests/mhdmodes.par index 02e01285..e8b7f5a7 100644 --- a/pars/mhdmodes.par +++ b/pars/tests/mhdmodes.par @@ -26,13 +26,13 @@ dir = 3 refinement = none numlevel = 1 -nx1 = 1024 +nx1 = 64 x1min = 0.0 x1max = 1.0 ix1_bc = periodic ox1_bc = periodic -nx2 = 1024 +nx2 = 64 x2min = 0.0 x2max = 1.0 ix2_bc = periodic @@ -48,8 +48,8 @@ ox3_bc = periodic # # of meshblocks must be >= the number of MPI ranks, # however there may be multiple blocks per rank -nx1 = 1024 -nx2 = 1024 +nx1 = 64 +nx2 = 64 nx3 = 1 # Set boring box coordinates. Explanations in bondi.par @@ -60,7 +60,7 @@ transform = null # tlim will be overridden depending on the problem tlim = 5.0 -integrator = vl2 +integrator = rk2 # Minimum is also the starting timestep dt_min = 0.0001 @@ -105,7 +105,7 @@ file_type = hdf5 # This is so as to output only the final state dt = 0.5 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B +variables = prims.rho, prims.u, prims.uvec, prims.B, divB # Text file with statistics (e.g. fluxes, floors hit) # recorded over time diff --git a/pars/orszag_tang.par b/pars/tests/orszag_tang.par similarity index 100% rename from pars/orszag_tang.par rename to pars/tests/orszag_tang.par diff --git a/pars/sane2d.par b/pars/tori_2d/sane2d.par similarity index 79% rename from pars/sane2d.par rename to pars/tori_2d/sane2d.par index 0c24d995..a2fa5b5e 100644 --- a/pars/sane2d.par +++ b/pars/tori_2d/sane2d.par @@ -31,6 +31,7 @@ poly_alpha = 14.0 tlim = 3000.0 nlim = -1 +integrator = vl2 verbose = 1 @@ -42,6 +43,9 @@ cfl = 0.9 gamma = 1.666667 reconstruction = weno5 + +type = kharma + rin = 6.0 rmax = 12.0 @@ -75,12 +79,10 @@ on = false file_type = hdf5 dt = 10.0 single_precision_output = true -# Any fields listed here which are not present (e.g. electrons if disabled) -# will be silently skipped. '&' character is a line continuation, like '\' -# Remember that the commas are still necessary, and unknown fields will silently fail! -variables = prims.rho, prims.u, prims.uvec, prims.B, prims.Ktot, & - prims.Kel_Howes, prims.Kel_Kawazura, prims.Kel_Werner, prims.Kel_Rowan, prims.Kel_Sharma, & - pflag, fflag +# Fields beginning with a specifier here will be included (e.g., all prims.XXX) +# Fields specified but not present are silently skipped +# If you need a line break, use '&' at line end +variables = prims, jcon, pflag, fflag file_type = rst diff --git a/pars/sane2d_cooling.par b/pars/tori_2d/sane2d_cooling.par similarity index 100% rename from pars/sane2d_cooling.par rename to pars/tori_2d/sane2d_cooling.par diff --git a/pars/sane_divb_2d.par b/pars/tori_2d/sane_divb_2d.par similarity index 100% rename from pars/sane_divb_2d.par rename to pars/tori_2d/sane_divb_2d.par diff --git a/pars/eht_comp.par b/pars/tori_3d/eht_comp.par similarity index 100% rename from pars/eht_comp.par rename to pars/tori_3d/eht_comp.par diff --git a/pars/mad.par b/pars/tori_3d/mad.par similarity index 100% rename from pars/mad.par rename to pars/tori_3d/mad.par diff --git a/pars/mad_test.par b/pars/tori_3d/mad_test.par similarity index 100% rename from pars/mad_test.par rename to pars/tori_3d/mad_test.par diff --git a/pars/mad_tilt.par b/pars/tori_3d/mad_tilt.par similarity index 100% rename from pars/mad_tilt.par rename to pars/tori_3d/mad_tilt.par diff --git a/pars/sane.par b/pars/tori_3d/sane.par similarity index 100% rename from pars/sane.par rename to pars/tori_3d/sane.par diff --git a/pars/sane_emhd.par b/pars/tori_3d/sane_emhd.par similarity index 100% rename from pars/sane_emhd.par rename to pars/tori_3d/sane_emhd.par diff --git a/pars/sane_imex.par b/pars/tori_3d/sane_imex.par similarity index 100% rename from pars/sane_imex.par rename to pars/tori_3d/sane_imex.par diff --git a/pars/sane_tilt.par b/pars/tori_3d/sane_tilt.par similarity index 100% rename from pars/sane_tilt.par rename to pars/tori_3d/sane_tilt.par diff --git a/scripts/batch/polaris.qsub b/scripts/batch/polaris.qsub index 5cb698d5..a2524d52 100644 --- a/scripts/batch/polaris.qsub +++ b/scripts/batch/polaris.qsub @@ -9,7 +9,7 @@ KHARMA_DIR=~/kharma-dev WRAPPER=$KHARMA_DIR/bin/select_gpu_polaris -KHARMA_ARGS="-i $KHARMA_DIR/pars/sane_perf.par" +KHARMA_ARGS="-i $KHARMA_DIR/pars/benchmark/sane_perf.par" # Print ranks NNODES=`wc -l < $PBS_NODEFILE` diff --git a/scripts/batch/scaling_delta.sb b/scripts/batch/scaling_delta.sb index d96e5212..3ff110bc 100755 --- a/scripts/batch/scaling_delta.sb +++ b/scripts/batch/scaling_delta.sb @@ -65,7 +65,7 @@ if [[ $DO_STRONG == "true" ]]; then echo "cycle=100 Running $size cubed problem with KHARMA on $nodes nodes with $tpn tasks each (blocksize $msize)" - srun -n $np $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + srun -n $np $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ parthenon/meshblock/nx1=$msize parthenon/meshblock/nx2=$msize parthenon/meshblock/nx3=$msize @@ -130,7 +130,7 @@ if [[ $DO_WEAK == "true" ]]; then nblock=$(( $mul1 * $mul2 * $mul3 )) echo "cycle=100 Running $size per node problem with KHARMA on $nodes nodes with $tpn tasks each (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" - srun -n $np $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + srun -n $np $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size diff --git a/scripts/batch/scaling_frontera.sb b/scripts/batch/scaling_frontera.sb index 17833c65..0ca0593c 100755 --- a/scripts/batch/scaling_frontera.sb +++ b/scripts/batch/scaling_frontera.sb @@ -54,7 +54,7 @@ if [[ $DO_STRONG == "true" ]]; then echo "cycle=100 Running $size cubed problem with KHARMA on $nodes nodes with $tpn tasks each (blocksize $msize)" - ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.host -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.host -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ parthenon/meshblock/nx1=$msize parthenon/meshblock/nx2=$msize parthenon/meshblock/nx3=$msize @@ -119,7 +119,7 @@ if [[ $DO_WEAK == "true" ]]; then nblock=$(( $mul1 * $mul2 * $mul3 )) echo "cycle=100 Running $size per node problem with KHARMA on $nodes nodes with $tpn tasks each (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" - ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.host -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.host -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size diff --git a/scripts/batch/scaling_longhorn.sb b/scripts/batch/scaling_longhorn.sb index 3e2b097b..8c4f41f8 100755 --- a/scripts/batch/scaling_longhorn.sb +++ b/scripts/batch/scaling_longhorn.sb @@ -52,7 +52,7 @@ if [[ $DO_STRONG == "true" ]]; then echo "cycle=100 Running $size cubed problem with KHARMA on $nodes nodes with $tpn tasks each (blocksize $msize)" - ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$size parthenon/mesh/nx2=$size parthenon/mesh/nx3=$size \ parthenon/meshblock/nx1=$msize parthenon/meshblock/nx2=$msize parthenon/meshblock/nx3=$msize @@ -117,7 +117,7 @@ if [[ $DO_WEAK == "true" ]]; then nblock=$(( $mul1 * $mul2 * $mul3 )) echo "cycle=100 Running $size per node problem with KHARMA on $nodes nodes with $tpn tasks each (total size ${tsize1}x${tsize2}x${tsize3}, $nblock blocks)" - ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/scaling_torus.par parthenon/time/nlim=102 \ + ibrun -np $np tacc_affinity $KHARMA_DIR/kharma.cuda -i $KHARMA_DIR/pars/benchmark/scaling_torus.par parthenon/time/nlim=102 \ parthenon/mesh/nx1=$tsize1 parthenon/mesh/nx2=$tsize2 parthenon/mesh/nx3=$tsize3 \ parthenon/meshblock/nx1=$size parthenon/meshblock/nx2=$size parthenon/meshblock/nx3=$size diff --git a/scripts/batch/scaling_polaris.qsub b/scripts/batch/scaling_polaris.qsub index 3e975aac..5c82de34 100755 --- a/scripts/batch/scaling_polaris.qsub +++ b/scripts/batch/scaling_polaris.qsub @@ -20,7 +20,7 @@ KHARMA_DIR=~/kharma-dev WRAPPER=$KHARMA_DIR/bin/select_gpu_polaris # Gotta specify this inline since bsub doesn't do arguments -PARFILE=~/kharma-dev/pars/scaling_torus.par +PARFILE=~/kharma-dev/pars/benchmark/scaling_torus.par # Allocate in full nodes, vs individual gpus min_nodes=1 min_gpus=1 #$(( $NRANKS * $min_nodes )) diff --git a/scripts/batch/scaling_summit.bsub b/scripts/batch/scaling_summit.bsub index d0ece6f9..9f0f45f8 100755 --- a/scripts/batch/scaling_summit.bsub +++ b/scripts/batch/scaling_summit.bsub @@ -19,7 +19,7 @@ DO_WEAK=true KHARMA_DIR=~/kharma # Gotta specify this inline since bsub doesn't do arguments -PARFILE=~/kharma/pars/scaling_torus.par +PARFILE=~/kharma/pars/benchmark/scaling_torus.par # Allocate in full nodes, vs individual gpus min_nodes=1 min_gpus=1 #$(( 6 * $min_nodes )) diff --git a/tests/all_pars/run.sh b/tests/all_pars/run.sh index 9b561044..d2e654c9 100755 --- a/tests/all_pars/run.sh +++ b/tests/all_pars/run.sh @@ -1,8 +1,12 @@ #!/bin/bash set -euo pipefail -for fil in ../../pars/*.par +# Skip testing the restarting & benchmark scripts +for folder in bondi electrons emhd shocks smr tests tori_2d tori_3d do - ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 - rm *.{hst,phdf,rhdf,xdmf} + for fil in ../../pars/$folder/*.par + do + ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 + rm -f *.{hst,phdf,rhdf,xdmf} + done done diff --git a/tests/run_all.sh b/tests/run_all.sh new file mode 100755 index 00000000..ea0da229 --- /dev/null +++ b/tests/run_all.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +for dir in */ +do + cd $dir + if [ -f ./run.sh ]; then + echo "Running $dir" + ./run.sh + fi + cd - +done From 0e416cdca04486daf2cbc194091ae57baf6882bc Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Oct 2023 14:13:52 -0600 Subject: [PATCH 142/219] Better boundaries, options * Try to make block and domain boundaries clear but flexible. Now supports, between blocks: sync cons sync prims sync prims (but under the hood sync cons for AMR) and for domain boundaries: prims are marked sync -> PtoU everything cons,GRHD prims are marked sync -> UtoP except PtoU on MHD cons,GRHD prims are marked sync -> UtoP everything * Dirichlet boundary fixes * More options to try inadvisable things on boundaries, & to record exactly what was applied * Drivers now have a type * Put in structure for limiting MPI sync vars * Clearly deprecate B_CD * Rename B_Cleanup -> general StartupOnly flag * Don't allocate in current calc --- kharma/b_cd/b_cd.cpp | 2 + kharma/b_cleanup/b_cleanup.cpp | 18 +++--- kharma/b_flux_ct/b_flux_ct.cpp | 17 +++-- kharma/b_flux_ct/b_flux_ct.hpp | 2 +- kharma/boundaries/boundaries.cpp | 107 +++++++++++++++++++++++------- kharma/boundaries/boundaries.hpp | 13 +--- kharma/boundaries/dirichlet.cpp | 108 ++++++++++++++++--------------- kharma/current/current.cpp | 24 ++++--- kharma/driver/kharma_driver.cpp | 89 +++++++++++++++++-------- kharma/driver/kharma_driver.hpp | 10 +-- kharma/driver/kharma_step.cpp | 76 +++++++++++----------- kharma/driver/simple_step.cpp | 2 +- kharma/electrons/electrons.cpp | 25 +++---- kharma/emhd/emhd.cpp | 96 ++++++++++++++------------- kharma/flux/flux.cpp | 8 ++- kharma/grmhd/grmhd.cpp | 58 ++++++----------- kharma/inverter/inverter.cpp | 6 +- kharma/kharma.cpp | 7 +- kharma/kharma.hpp | 10 +-- kharma/kharma_package.cpp | 18 +++--- kharma/kharma_package.hpp | 29 +++++---- kharma/main.cpp | 6 +- 22 files changed, 417 insertions(+), 314 deletions(-) diff --git a/kharma/b_cd/b_cd.cpp b/kharma/b_cd/b_cd.cpp index 7d37399f..4f69a6da 100644 --- a/kharma/b_cd/b_cd.cpp +++ b/kharma/b_cd/b_cd.cpp @@ -43,6 +43,8 @@ namespace B_CD std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr& packages) { + throw std::runtime_error("Constraint-damping transport is not functional with modern B field initialization!"); + auto pkg = std::make_shared("B_CD"); Params ¶ms = pkg->AllParams(); diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 831b920b..3d47e557 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -68,8 +68,7 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s auto pkg = std::make_shared("B_Cleanup"); Params ¶ms = pkg->AllParams(); - // The solver needs this flag - Metadata::AddUserFlag("B_Cleanup"); + // TODO also support face divB!! // Solver options // Allow setting tolerance relative to starting value. Off by default @@ -104,8 +103,9 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // RHS. Must not just be "divB" as that field does not sync boundaries pkg->AddParam("rhs_name", "divB_RHS"); // Construct a solver. We don't need the template parameter, so we use 'int' - // TODO TODO - BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}); //, {Metadata::GetUserFlag("B_Cleanup")}); + // The flag "StartupOnly" marks solver variables not to be sync'd later, + // even though they're also marked FillGhost + BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}, {Metadata::GetUserFlag("StartupOnly")}); // Set callback solver.user_MatVec = B_Cleanup::CornerLaplacian; @@ -113,7 +113,7 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // FIELDS std::vector s_vector({NVEC}); - std::vector cleanup_flags({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("B_Cleanup")}); + std::vector cleanup_flags({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("StartupOnly")}); auto cleanup_flags_ghost = cleanup_flags; cleanup_flags_ghost.push_back(Metadata::FillGhost); // Scalar potential, solution to del^2 p = div B @@ -135,10 +135,8 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // Declare fields if we're doing that if (manage_field) { - // Stolen verbatim from FluxCT, except we don't register the FixFlux step obvs - // Probably will crash due to not having the right parameters: add as needed. - // Best to crash, this mode is very not supported. - // TODO preserve an easier form of divB in this case? + // Stolen verbatim from FluxCT, will need updates to actually use + throw std::runtime_error("B field cleanup/projection is set as B field transport! If you really want this, disable this error in source!"); // Mark if we're evolving implicitly bool implicit_b = pin->GetOrAddBoolean("b_field", "implicit", false); @@ -237,7 +235,7 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) KHARMADriver::SyncAllBounds(md); // Add a solver container and associated MeshData - std::vector names = KHARMA::GetVariableNames(&pmesh->packages, Metadata::GetUserFlag("B_Cleanup")); + std::vector names = KHARMA::GetVariableNames(&pmesh->packages, {Metadata::GetUserFlag("B_Cleanup"), Metadata::GetUserFlag("StartupOnly")}); auto &msolve = pmesh->mesh_data.Add("solve", names); // Create a TaskCollection of just the solve, diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index fd3fbeb3..d4b4dd63 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -100,11 +100,14 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), - Metadata::Restart, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, Metadata::Conserved, - Metadata::WithFluxes, Metadata::FillGhost, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + // Flags for B fields + std::vector flags_b = {Metadata::Cell, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + + // "primitive" B field is field, "conserved" is flux + auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); + flags_prim.insert(flags_prim.end(), flags_b.begin(), flags_b.end()); + auto flags_cons = packages->Get("Driver")->Param>("cons_flags"); + flags_cons.insert(flags_cons.end(), flags_b.begin(), flags_b.end()); auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); @@ -112,7 +115,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("cons.B", m); // Declare EMF temporary variables, to avoid malloc/free during each step - // These are edge-centered but we only need the interior + 1-zone halo anyway + // Technically these are edge-centered but we only need the interior + 1-zone halo anyway, so we store as a vector std::vector flags_emf = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_emf, s_vector); pkg->AddField("emf", m); @@ -465,6 +468,7 @@ double MaxDivB(MeshData *md) { auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; + if (ndim < 2) return 0.; // Packing out here avoids frequent per-mesh packs. Do we need to? auto B_U = md->PackVariables(std::vector{"cons.B"}); @@ -545,6 +549,7 @@ void CalcDivB(MeshData *md, std::string divb_field_name) { auto pmesh = md->GetMeshPointer(); const int ndim = pmesh->ndim; + if (ndim < 2) return; // Packing out here avoids frequent per-mesh packs. Do we need to? auto B_U = md->PackVariables(std::vector{"cons.B"}); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 7080b269..2fc4c461 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -68,7 +68,7 @@ void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** * Reverse of the above. Only used alone during initialization. - * Generally, use Flux::BlockPtoU or Flux::BlockPtoUExceptMHD. + * Generally, use Flux::BlockPtoU */ void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 5aaeb2d7..a256dd1f 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -59,6 +59,11 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: bool zero_polar_flux = pin->GetOrAddBoolean("boundaries", "zero_polar_flux", spherical); params.Add("zero_polar_flux", zero_polar_flux); + // Apply physical boundaries to conserved GRMHD variables rho u^r, T^mu_nu + // Probably inadvisable? + bool domain_bounds_on_conserved = pin->GetOrAddBoolean("boundaries", "domain_bounds_on_conserved", false); + params.Add("domain_bounds_on_conserved", domain_bounds_on_conserved); + // Fix the X1/X2 corner by replacing the reflecting condition with the inflow // Never use this if not in spherical coordinates // Activates by default only with reflecting X2/outflow X1 and interior boundary inside EH @@ -71,8 +76,11 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: pin->GetString("boundaries", "inner_x1") == "outflow"); bool inside_eh = pin->GetBoolean("coordinates", "domain_intersects_eh"); fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner", correct_bounds && inside_eh); + // Allow overriding with specific name + fix_corner = pin->GetOrAddBoolean("boundaries", "fix_corner_inner", fix_corner); } - params.Add("fix_corner", fix_corner); + params.Add("fix_corner_inner", fix_corner); + params.Add("fix_corner_outer", pin->GetOrAddBoolean("boundaries", "fix_corner_outer", false)); Metadata m_x1, m_x2, m_x3; { @@ -99,8 +107,7 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: } // Set options for each boundary - for (int i = 0; i < BOUNDARY_NFACES; i++) - { + for (int i = 0; i < BOUNDARY_NFACES; i++) { const auto bface = (BoundaryFace) i; const auto bdomain = BoundaryDomain(bface); const auto bname = BoundaryName(bface); @@ -259,18 +266,57 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD EndFlag(); } - // If specified, fix corner values when applying X2 boundaries (see function) - if (bdir == X2DIR && params.Get("fix_corner")) { - Flag("FixCorner"); - FixCorner(rc, domain, coarse); - EndFlag(); + /* + * KHARMA is very particular about corner boundaries. + * In particular, we apply the outflow boundary over ALL X2 & X3. + * Then we apply the polar bound only where outflow is not applied, + * and periodic bounds only where neither other bound applies. + * The latter is accomplished regardless of Parthenon's definitions, + * since these functions are run after Parthenon's MPI boundary syncs & + * replace whatever they've done. + * However, the former must be added after the X2 boundary call, + * replacing the reflecting conditions in the X1/X2 corner (or in 3D, edge) + * with outflow conditions based on the updated ghost cells. + */ + if (bdir == X2DIR) { + // If we're on the interior edge, re-apply that edge for our block by calling + // exactly the same function that Parthenon does. This ensures we're applying + // the same thing, just emulating calling it after X2. + if (params.Get("fix_corner_inner")) { + if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) { + Flag("FixCorner"); + ApplyBoundary(rc, IndexDomain::inner_x1, coarse); + EndFlag(); + } + } + if (params.Get("fix_corner_outer")) { + if (pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { + Flag("FixCorner"); + ApplyBoundary(rc, IndexDomain::outer_x1, coarse); + EndFlag(); + } + } } - // Respect the fluid primitives on boundaries (does not include B) - // Also currently the EMHD extra variables q, dP - Packages::BoundaryPtoU(rc.get(), domain, coarse); - // For everything else, respect conserved variables - Packages::BoundaryUtoP(rc.get(), domain, coarse); + // If we applied the domain boundary to primitives (as we usually do)... + if (!params.Get("domain_bounds_on_conserved")) { + bool sync_prims = rc->GetBlockPointer()->packages.Get("Driver")->Param("sync_prims"); + // There are two modes of operation here: + if (sync_prims) { + // 1. ImEx w/o AMR: + // PRIMITIVE variables (only) are marked FillGhost + // So, run PtoU on EVERYTHING (and correct the B field) + CorrectBPrimitive(rc, domain, coarse); + Flux::BlockPtoU(rc.get(), domain, coarse); + } else { + // 2. Normal (KHARMA driver, ImEx w/AMR): + // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. + // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE + Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); + } + } else { + Packages::BlockUtoP(rc.get(), domain, coarse); + } EndFlag(); } @@ -295,19 +341,34 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom ); } -void KBoundaries::FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse) +void KBoundaries::CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse) { + Flag("CorrectBPrimitive"); std::shared_ptr pmb = rc->GetBlockPointer(); - if (pmb->pmy_mesh->ndim < 2) - return; + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - // If we're on the interior edge, re-apply that edge for our block by calling - // whatever the X1 boundary is, again. This ensures we're applying - // the same thing, just emulating calling it after X2. - if (pmb->boundary_flag[BoundaryFace::inner_x1] == BoundaryFlag::user) - { - ApplyBoundary(rc, IndexDomain::inner_x1, coarse); - } + auto B_P = rc->PackVariables(std::vector{"prims.B"}); + // Return if no field to correct + if (B_P.GetDim(4) == 0) return; + + const auto& G = pmb->coords; + + const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundaryDirection(domain); + const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) + : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) + : bounds.GetBoundsK(IndexDomain::interior)); + const int ref = BoundaryIsInner(domain) ? range.s : range.e; + + pmb->par_for_bndry( + "Correct_B_P", IndexRange{0,NVEC-1}, domain, CC, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) + / G.gdet(Loci::center, j, i); + } + ); + + EndFlag(); } TaskStatus KBoundaries::FixFlux(MeshData *md) diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index cf412551..dde70a60 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -85,18 +85,9 @@ TaskStatus FixFlux(MeshData *rc); void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse); /** - * KHARMA is very particular about corner boundaries. - * In particular, we apply the outflow boundary over ALL X2 & X3. - * Then we apply the polar bound only where outflow is not applied, - * and periodic bounds only where neither other bound applies. - * The latter is accomplished regardless of Parthenon's definitions, - * since these functions are run after Parthenon's MPI boundary syncs & - * replace whatever they've done. - * However, the former must be added after the X2 boundary call, - * replacing the reflecting conditions in the X1/X2 corner (or in 3D, edge) - * with outflow conditions based on the updated ghost cells. + * Correct for geometry when applying primitive B field boundaries */ -void FixCorner(std::shared_ptr> &rc, IndexDomain domain, bool coarse); +void CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse); /** * Check for velocity toward the simulation domain in a zone, and eliminate it. diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index c3b63f8f..809ae84e 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -40,6 +40,7 @@ using namespace parthenon; +// TODO can SetDirichlet be folded into this? void KBoundaries::DirichletImpl(std::shared_ptr> &rc, BoundaryFace bface, bool coarse) { std::shared_ptr pmb = rc->GetBlockPointer(); @@ -47,16 +48,18 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda // Get all ghosts, minus those in the B_Cleanup package if it is present using FC = Metadata::FlagCollection; - FC main_ghosts = pmb->packages.AllPackages().count("B_Cleanup") - ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) - : FC({Metadata::FillGhost}); + FC ghost_vars = FC({Metadata::FillGhost, Metadata::Conserved}) + + FC({Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}) + - FC({Metadata::GetUserFlag("StartupOnly")}); PackIndexMap ghostmap; - auto q = rc->PackVariables(main_ghosts, ghostmap, coarse); - const int q_index = ghostmap["prims.q"].first; + auto q = rc->PackVariables(ghost_vars, ghostmap, coarse); auto bound = rc->Get("bounds." + BoundaryName(bface)).data; + // We're sometimes called without any variables to sync (e.g. syncing flags, EMFs), just return + if (q.GetDim(4) == 0) return; + if (q.GetDim(4) != bound.GetDim(4)) { - std::cerr << "Boundary cache mismatch! boundaries: " << bound.GetDim(4) << " vs pack: " << q.GetDim(4) << std::endl; + std::cerr << "Dirichlet boundary mismatch! Boundary cache: " << bound.GetDim(4) << " for pack: " << q.GetDim(4) << std::endl; std::cerr << "Variables with ghost zones:" << std::endl; ghostmap.print(); } @@ -73,7 +76,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda const auto &G = pmb->coords; - // printf("Freezing bounds:\n"); + // const int q_index = ghostmap["prims.q"].first; const auto domain = BoundaryDomain(bface); pmb->par_for_bndry( "dirichlet_boundary", vars, domain, CC, coarse, @@ -86,8 +89,53 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda // if (p == q_index) printf("%g ", q(p, k, j, i)); } ); - // Kokkos::fence(); - // printf("\n\n"); +} + +void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + std::shared_ptr pmb = rc->GetBlockPointer(); + const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); + const BoundaryFace bface = BoundaryFaceOf(domain); + + using FC = Metadata::FlagCollection; + FC ghost_vars = FC({Metadata::FillGhost, Metadata::Conserved}) + FC({Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); + FC main_ghosts = ghost_vars - FC({Metadata::GetUserFlag("StartupOnly")}); + PackIndexMap ghostmap; + auto q = rc->PackVariables(main_ghosts, ghostmap, coarse); + const int q_index = ghostmap["prims.q"].first; + auto bound = rc->Get("bounds." + BoundaryName(bface)).data; + + // We're sometimes called without any variables to sync (e.g. syncing flags, EMFs), just return + if (q.GetDim(4) == 0) return; + + if (q.GetDim(4) != bound.GetDim(4)) { + std::cerr << "Dirichlet boundary mismatch! Boundary cache: " << bound.GetDim(4) << " for pack: " << q.GetDim(4) << std::endl; + std::cerr << "Variables with ghost zones:" << std::endl; + ghostmap.print(); + } + + const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; + const bool right = !BoundaryIsInner(domain); + + // Subtract off the starting index if we're on the right + const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundaryDirection(bface); + const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior) + 1 : 0; + const int je = (dir == 2) ? bounds.je(IndexDomain::interior) + 1 : 0; + const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior) + 1 : 0; + + const auto &G = pmb->coords; + + pmb->par_for_bndry( + "dirichlet_boundary", vars, domain, CC, coarse, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + if (right) { + bound(p, k - ke, j - je, i - ie) = q(p, k, j, i); + } else { + bound(p, k, j, i) = q(p, k, j, i); + } + } + ); } void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) @@ -127,45 +175,3 @@ void KBoundaries::FreezeDirichletBlock(MeshBlockData *rc) } } } - -void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain, bool coarse) -{ - std::shared_ptr pmb = rc->GetBlockPointer(); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - const BoundaryFace bface = BoundaryFaceOf(domain); - - using FC = Metadata::FlagCollection; - FC main_ghosts = pmb->packages.AllPackages().count("B_Cleanup") - ? FC({Metadata::FillGhost}) - FC({Metadata::GetUserFlag("B_Cleanup")}) - : FC({Metadata::FillGhost}); - auto q = rc->PackVariables(main_ghosts, coarse); - auto bound = rc->Get("bounds." + BoundaryName(bface)).data; - - // TODO error? - if (q.GetDim(4) != bound.GetDim(4)) { - std::cerr << "Dirichlet boundary cache mismatch! " << bound.GetDim(4) << " vs " << q.GetDim(4) << std::endl; - } - - const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; - const bool right = !BoundaryIsInner(domain); - - // Subtract off the starting index if we're on the right - const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundaryDirection(bface); - const int ie = (dir == 1) ? bounds.ie(IndexDomain::interior) + 1 : 0; - const int je = (dir == 2) ? bounds.je(IndexDomain::interior) + 1 : 0; - const int ke = (dir == 3) ? bounds.ke(IndexDomain::interior) + 1 : 0; - - const auto &G = pmb->coords; - - pmb->par_for_bndry( - "dirichlet_boundary", vars, domain, CC, coarse, - KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { - if (right) { - bound(p, k - ke, j - je, i - ie) = q(p, k, j, i); - } else { - bound(p, k, j, i) = q(p, k, j, i); - } - } - ); -} diff --git a/kharma/current/current.cpp b/kharma/current/current.cpp index a3e481fa..b29e9604 100644 --- a/kharma/current/current.cpp +++ b/kharma/current/current.cpp @@ -44,6 +44,12 @@ std::shared_ptr Current::Initialize(ParameterInput *pin, std::sha auto m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_fourvector); pkg->AddField("jcon", m); + // Temporaries + std::vector s_vector({NVEC}); + m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}, s_vector); + pkg->AddField("Current.uvec_c", m); + pkg->AddField("Current.B_P_c", m); + pkg->BlockUserWorkBeforeOutput = Current::FillOutput; return pkg; @@ -57,15 +63,12 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockDataGet("prims.uvec").data; GridVector B_P_new = rc1->Get("prims.B").data; GridVector jcon = rc1->Get("jcon").data; - const auto& G = pmb->coords; - int n1 = pmb->cellbounds.ncellsi(IndexDomain::entire); - int n2 = pmb->cellbounds.ncellsj(IndexDomain::entire); - int n3 = pmb->cellbounds.ncellsk(IndexDomain::entire); - const int ndim = pmb->pmy_mesh->ndim; + GridVector uvec_c = rc1->Get("Current.uvec_c").data; + GridVector B_P_c = rc1->Get("Current.B_P_c").data; - GridVector uvec_c("uvec_c", NVEC, n3, n2, n1); - GridVector B_P_c("B_P_c", NVEC, n3, n2, n1); + const auto& G = pmb->coords; + const int ndim = pmb->pmy_mesh->ndim; // Calculate time-centered primitives // We could pack, but we just need the vectors, U1,2,3 and B1,2,3 @@ -89,6 +92,7 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockDatapar_for("jcon_calc", n4v.s, n4v.e, kb_i.s, kb_i.e, jb_i.s, jb_i.e, ib_i.s, ib_i.e, KOKKOS_LAMBDA (const int &mu, const int &k, const int &j, const int &i) { // Get sqrt{-g}*F^{mu nu} at neighboring points + // TODO(BSP) this recalculates Fcon a lot... const Real gF0p = get_gdet_Fcon(G, uvec_new, B_P_new, 0, mu, k, j, i); const Real gF0m = get_gdet_Fcon(G, uvec_old, B_P_old, 0, mu, k, j, i); const Real gF1p = get_gdet_Fcon(G, uvec_c, B_P_c, 1, mu, k, j, i+1); @@ -101,9 +105,9 @@ TaskStatus Current::CalculateCurrent(MeshBlockData *rc0, MeshBlockData(i)) + - (gF2p - gF2m) / (2. * G.Dxc<2>(j)) + - (gF3p - gF3m) / (2. * G.Dxc<3>(k))); + (gF1p - gF1m) / (2 * G.Dxc<1>(i)) + + (gF2p - gF2m) / (2 * G.Dxc<2>(j)) + + (gF3p - gF3m) / (2 * G.Dxc<3>(k))); } ); diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index e66fed60..5c7b30b8 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -57,15 +57,19 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // driver (formerly HARM driver), and the latter supporting implicit stepping of some or all variables // Mostly, packages should react to e.g. the "sync_prims" option rather than the driver name bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); - std::string driver_type = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); - if (driver_type == "harm") driver_type = "kharma"; // TODO enum rather than strings? + std::string driver_type_s = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); + DriverType driver_type; + if (driver_type_s == "harm" || driver_type_s == "kharma") { + driver_type = DriverType::kharma; + } else if (driver_type_s == "imex") { + driver_type = DriverType::imex; + } else if (driver_type_s == "simple") { + driver_type = DriverType::simple; + } else { + throw std::invalid_argument("Driver type must be one of: simple, kharma, imex"); + } params.Add("type", driver_type); - - // Record whether we marked the prims or cons as "FillGhost." This also translates to whether we consider - // primitive or conserved state to be the ground truth when updating values in a step. - // Currently "imex" and "simple" drivers both sync primitive vars - bool sync_prims = !(driver_type == "kharma"); - params.Add("sync_prims", sync_prims); + params.Add("name", driver_type_s); // Synchronize boundary variables twice. Ensures KHARMA is agnostic to the breakdown // of meshblocks, at the cost of twice the MPI overhead, for potentially worse strong scaling. @@ -87,9 +91,9 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std if (recon == "donor_cell") { params.Add("recon", KReconstruction::Type::donor_cell); stencil = 1; - } else if (recon == "linear_vl") { - params.Add("recon", KReconstruction::Type::linear_vl); - stencil = 3; + // } else if (recon == "linear_vl") { + // params.Add("recon", KReconstruction::Type::linear_vl); + // stencil = 3; } else if (recon == "linear_mc") { params.Add("recon", KReconstruction::Type::linear_mc); stencil = 3; @@ -104,7 +108,7 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std stencil = 5; } else { std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, linear_vl, weno5" << std::endl; + std::cerr << "donor_cell, linear_mc, weno5, weno5_lower_edges, weno5_lower_poles (linear_vl coming back soon!)" << std::endl; throw std::invalid_argument("Unsupported reconstruction algorithm!"); } // Warn if using less than 3 ghost zones w/WENO etc, 2 w/Linear, etc. @@ -112,13 +116,42 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std throw std::runtime_error("Not enough ghost zones for specified reconstruction!"); } - // Field flags related to driver operation are defined outside any particular driver - // When using the Implicit package we need to globally distinguish implicitly and explicitly-updated variables + // When using the Implicit package we need to globally distinguish implicit & explicit vars // All independent variables should be marked one or the other, // so we define the flags here to avoid loading order issues Metadata::AddUserFlag("Implicit"); Metadata::AddUserFlag("Explicit"); + // 1. One flag to mark the primitive variables specifically + // (Parthenon has Metadata::Conserved already) + Metadata::AddUserFlag("Primitive"); + + // Finally, a flag for anything used (and possibly sync'd) during startup, + // but which should not be evolved (or more importantly, sync'd) during main stepping + Metadata::AddUserFlag("StartupOnly"); + + // This marks whether we consider primitive or conserved state to be + // the ground truth when updating values in a step. + // Currently "imex" and "simple" drivers both update primitive vars + bool prims_are_fundamental = driver_type != DriverType::kharma; + params.Add("prims_are_fundamental", prims_are_fundamental); + + // Finally, we set default flags for primitive and conserved variables + // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated + bool sync_prims = prims_are_fundamental && + (!pin->DoesParameterExist("parthenon/mesh", "numlevel") || + pin->GetInteger("parthenon/mesh", "numlevel") == 1); + params.Add("sync_prims", sync_prims); + if (sync_prims) { + // If we're not in AMR, we can sync primitive variables directly + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::Restart, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::WithFluxes, Metadata::Conserved}); + } else { + // If we're in AMR or using the KHARMA driver anyway, sync conserved vars + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::Restart, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); + } + return pkg; } @@ -126,26 +159,32 @@ void KHARMADriver::AddFullSyncRegion(TaskCollection& tc, std::shared_ptrpackages.Get("Driver")->Param("sync_prims"); - // MPI boundary exchange, done over MeshData objects/partitions at once // Parthenon includes physical bounds const int num_partitions = pmesh->DefaultNumPartitions(); // Usually 1 TaskRegion &bound_sync = tc.AddRegion(num_partitions); for (int i = 0; i < num_partitions; i++) { auto &tl = bound_sync[i]; - AddMPIBoundarySync(t_none, tl, md_sync, sync_prims, pmesh->multilevel); + AddMPIBoundarySync(t_none, tl, md_sync); } } -// We take the extra bools to make this a static method, so SyncAllBounds can be static -TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &mc1, - bool sync_prims, bool multilevel) +TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &mc1) { Flag("AddBoundarySync"); auto t_start_sync = t_start; - if (sync_prims) { + // Pull the mesh pointer from mc1 so we can be a static method + auto ¶ms = mc1->GetMeshPointer()->packages.Get("Driver")->AllParams(); + bool multilevel = mc1->GetMeshPointer()->multilevel; + + // If we're "syncing primitive variables" but must exchange conserved vars to prolong/restrict them, + // make sure to run P->U, then sync, then U->P + // Note this has the side effect of filling U in some zones, + // which must be replaced during e.g. startup code when primitive values should be truth + bool prims_are_fundamental = params.Get("prims_are_fundamental"); + bool sync_prims = params.Get("sync_prims"); + if (prims_are_fundamental && !sync_prims) { TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_ptou_final(0); int i_task = 0; @@ -171,7 +210,7 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: EndFlag(); // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims - if (sync_prims) { + if (prims_are_fundamental && !sync_prims) { TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); int i_task = 0; @@ -194,16 +233,14 @@ TaskID KHARMADriver::AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std: return t_bounds; } -TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> &md, bool sync_prims, bool multilevel) +TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> &md) { Flag("SyncAllBounds"); TaskID t_none(0); - // 1. Sync MPI bounds - // This call syncs the primitive variables when using the ImEx driver, and cons TaskCollection tc; auto tr = tc.AddRegion(1); - AddMPIBoundarySync(t_none, tr[0], md, sync_prims, multilevel); + AddMPIBoundarySync(t_none, tr[0], md); while (!tr.Execute()); EndFlag(); diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 7bcc8d56..18c943c5 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -40,6 +40,9 @@ using namespace parthenon; +// See Initialize() +enum class DriverType{kharma, imex, simple}; + /** * This is the "Driver" class for KHARMA. * A Driver object orchestrates everything that has to be done to a mesh to constitute a step. @@ -124,8 +127,7 @@ class KHARMADriver : public MultiStageDriver { * This sequence is used identically in several places, so it makes sense * to define once and use elsewhere. */ - static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &md, - bool sync_prims=false, bool multilevel=false); + static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &md); /** * Calculate the fluxes in each direction @@ -136,9 +138,9 @@ class KHARMADriver : public MultiStageDriver { * Single call to sync all boundary conditions (MPI/internal and domain/physical boundaries) * Used anytime boundary sync is needed outside the usual loop of steps. * - * Only use this as a task each step when debugging! + * Only use this during the run if you're debugging! */ - static TaskStatus SyncAllBounds(std::shared_ptr> &md, bool sync_prims=false, bool multilevel=false); + static TaskStatus SyncAllBounds(std::shared_ptr> &md); // TODO swapped versions of these /** diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index cdd0380d..86730060 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -55,15 +55,19 @@ TaskCollection KHARMADriver::MakeTaskCollection(BlockList_t &blocks, int stage) { - std::string driver_type = blocks[0]->packages.Get("Driver")->Param("type"); - Flag("MakeTaskCollection_"+driver_type); + DriverType driver_type = blocks[0]->packages.Get("Driver")->Param("type"); + Flag("MakeTaskCollection"); TaskCollection tc; - if (driver_type == "imex") { + switch (driver_type) { + case DriverType::kharma: + tc = MakeDefaultTaskCollection(blocks, stage); + break; + case DriverType::imex: tc = MakeImExTaskCollection(blocks, stage); - } else if (driver_type == "simple") { + break; + case DriverType::simple: tc = MakeSimpleTaskCollection(blocks, stage); - } else { - tc = MakeDefaultTaskCollection(blocks, stage); + break; } EndFlag(); return tc; @@ -100,22 +104,25 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int if (use_jcon) { // At the end of the step, updating "mbd_sub_step_final" updates the base // So we have to keep a copy at the beginning to calculate jcon - pmb->meshblock_data.Add("preserve", base); - // Above only copies on allocate -- ensure we copy every step - Copy>({}, base.get(), pmb->meshblock_data.Get("preserve").get()); + // We have to explicitly copy, since after the first step `Add`==`Get` + Copy>({}, base.get(), pmb->meshblock_data.Add("preserve").get()); } } } + //Copy>({}, pmesh->mesh_data.Get().get(), pmesh->mesh_data.Add("preserve").get()); Flag("MakeTaskCollection::fluxes"); - // Build the list of variables we'll be syncing during "normal" boundary exchanges. - // This *excludes* anything related to divergence cleaning (which have their own syncs during the clean), - // and the EMF (or other edge variables) which are really part of the flux correction sync - using FC = Metadata::FlagCollection; - auto sync_flags = FC(Metadata::FillGhost) - FC(Metadata::Edge); - if (pkgs.count("B_Cleanup")) sync_flags = sync_flags - FC(Metadata::GetUserFlag("B_Cleanup")); - std::vector sync_vars = KHARMA::GetVariableNames(&(pmesh->packages), sync_flags); + // TODO when we can make shallow copies work, copy based on this list for MPI syncs + // static std::vector sync_vars; + // if (sync_vars.size() == 0) { + // // Build the list of variables we'll be syncing during "normal" boundary exchanges. + // // This *excludes* anything related to divergence cleaning (which have their own syncs during the clean), + // // and the EMF (or other edge variables) which are really part of the flux correction sync + // using FC = Metadata::FlagCollection; + // auto sync_flags = FC(Metadata::FillGhost) - FC(Metadata::Edge) - FC(Metadata::GetUserFlag("StartupOnly")); + // sync_vars = KHARMA::GetVariableNames(&(pmesh->packages), sync_flags); + // } // Big packed region: get and apply new fluxes on all the zones we control const int num_partitions = pmesh->DefaultNumPartitions(); @@ -131,9 +138,11 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage - 1], i); auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); auto &md_flux_src = pmesh->mesh_data.GetOrAdd("dUdt", i); + // TODO this doesn't work still for some reason, even if the shallow copy has all variables + auto &md_sync = md_sub_step_final; //pmesh->mesh_data.AddShallow("sync", md_sub_step_final); // Start receiving flux corrections and ghost cells - auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); + auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sync); auto t_start_recv_flux = t_start_recv_bound; if (pmesh->multilevel || use_b_ct) t_start_recv_flux = tl.AddTask(t_none, parthenon::StartReceiveFluxCorrections, md_sub_step_init); @@ -148,7 +157,6 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_flux_bounds = t_fluxes; if (pmesh->multilevel || use_b_ct) { auto t_emf = t_fluxes; - // TODO this MPI sync should be bundled into fluxcorr if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition @@ -198,10 +206,10 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_update = t_update_c; if (use_b_ct) { t_update = tl.AddTask(t_update_c, WeightedSumDataFace, - std::vector({Metadata::Independent, Metadata::Face}), - md_sub_step_final.get(), md_flux_src.get(), - 1.0, integrator->beta[stage-1] * integrator->dt, - md_sub_step_final.get()); + std::vector({Metadata::Independent, Metadata::Face}), + md_sub_step_final.get(), md_flux_src.get(), + 1.0, integrator->beta[stage-1] * integrator->dt, + md_sub_step_final.get()); } // UtoP needs a guess in order to converge, so we copy in sc0 @@ -213,22 +221,17 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int md_sub_step_init.get(), md_sub_step_final.get()); } - // TODO the pointers here are weird - //auto &md_sync = pmesh->mesh_data.AddShallow("sync", md_sub_step_final, sync_vars); - //md_sync->SetMeshPointer(pmesh); - KHARMADriver::AddMPIBoundarySync(t_copy_prims, tl, md_sub_step_final); + KHARMADriver::AddMPIBoundarySync(t_copy_prims, tl, md_sync); } EndFlag(); Flag("MakeTaskCollection::fixes"); - // Smaller meshblock region. This gets touchy because we want to keep ghost zones updated, - // so very commented + // Smaller meshblock region. This gets touchy because we want to keep ghost zones updated, so it's very commented TaskRegion &async_region = tc.AddRegion(blocks.size()); for (int i = 0; i < blocks.size(); i++) { auto &pmb = blocks[i]; auto &tl = async_region[i]; - //auto &base = pmb->meshblock_data.Get(); auto &mbd_sub_step_init = pmb->meshblock_data.Get(integrator->stage_name[stage-1]); auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); @@ -305,12 +308,10 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // B Field cleanup: this is a separate solve so it's split out // It's also really slow when enabled so we don't care too much about limiting regions, etc. if (use_b_cleanup && (stage == integrator->nstages) && B_Cleanup::CleanupThisStep(pmesh, tm.ncycle)) { - TaskRegion &cleanup_region = tc.AddRegion(num_partitions); - for (int i = 0; i < num_partitions; i++) { - auto &tl = cleanup_region[i]; - auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); - tl.AddTask(t_none, B_Cleanup::CleanupDivergence, md_sub_step_final); - } + TaskRegion &cleanup_region = tc.AddRegion(1); + auto &tl = cleanup_region[0]; + auto &md_sub_step_final = pmesh->mesh_data.Get(integrator->stage_name[stage]); + tl.AddTask(t_none, B_Cleanup::CleanupDivergence, md_sub_step_final); } // Second boundary sync: @@ -320,9 +321,8 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); if (two_sync) { auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], 0); - // TODO this gets weird if we partition - //auto &md_sync = pmesh->mesh_data.AddShallow("sync", md_sub_step_final, sync_vars); - KHARMADriver::AddFullSyncRegion(tc, md_sub_step_final); + auto &md_sync = md_sub_step_final; //pmesh->mesh_data.AddShallow("sync", md_sub_step_final); + KHARMADriver::AddFullSyncRegion(tc, md_sync); } EndFlag(); diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index 2d68b8f0..ea30839e 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -60,7 +60,7 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s } } - //auto t_heating_test = tl.AddTask(t_none, Electrons::ApplyHeating, base.get()); + // Big synchronous region: get & apply fluxes to advance the fluid state // num_partitions is nearly always 1 diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index dd73df5c..ff4b2dff 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -34,6 +34,7 @@ #include "electrons.hpp" #include "decs.hpp" +#include "kharma_driver.hpp" #include "flux.hpp" #include "grmhd.hpp" #include "kharma.hpp" @@ -115,21 +116,21 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->AllParams(); - auto driver_type = driver.Get("type"); - bool grmhd_implicit = packages->Get("GRMHD")->Param("implicit"); // usually false - bool implicit_e = (driver_type == "imex" && pin->GetOrAddBoolean("electrons", "implicit", grmhd_implicit)); // so this false too + auto driver_type = driver.Get("type"); + bool implicit_e = (driver_type == DriverType::imex && pin->GetOrAddBoolean("electrons", "implicit", false)); params.Add("implicit", implicit_e); - Metadata::AddUserFlag("Electrons"); + Metadata::AddUserFlag("Elec"); MetadataFlag areWeImplicit = (implicit_e) ? Metadata::GetUserFlag("Implicit") : Metadata::GetUserFlag("Explicit"); + std::vector flags_elec = {Metadata::Cell, areWeImplicit, Metadata::GetUserFlag("Elec")}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, Metadata::Conserved, Metadata::Conserved, - Metadata::WithFluxes, Metadata::FillGhost, areWeImplicit, Metadata::GetUserFlag("Electrons")}; - std::vector flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::GetUserFlag("Primitive"), - Metadata::Restart, areWeImplicit, Metadata::GetUserFlag("Electrons")}; + auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); + flags_prim.insert(flags_prim.end(), flags_elec.begin(), flags_elec.end()); + auto flags_cons = packages->Get("Driver")->Param>("cons_flags"); + flags_cons.insert(flags_cons.end(), flags_elec.begin(), flags_elec.end()); // Total entropy, used to track changes int nKs = 1; @@ -201,7 +202,7 @@ TaskStatus InitElectrons(std::shared_ptr>& rc, ParameterInpu // Need to distinguish KTOT from the other variables, so we record which it is PackIndexMap prims_map; - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}, prims_map); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Elec"), Metadata::GetUserFlag("Primitive")}, prims_map); const int ktot_index = prims_map["prims.Ktot"].first; // Just need these two from the rest of Prims GridScalar rho = rc->Get("prims.rho").data; @@ -238,8 +239,8 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); // No need for a "map" here, we just want everything that fits these - auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::GetUserFlag("Primitive")}); - auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Electrons"), Metadata::Conserved}); + auto& e_P = rc->PackVariables({Metadata::GetUserFlag("Elec"), Metadata::GetUserFlag("Primitive")}); + auto& e_U = rc->PackVariables({Metadata::GetUserFlag("Elec"), Metadata::Conserved}); // And then the local density GridScalar rho_U = rc->Get("cons.rho").data; diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 80da5f4c..65ea482b 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -124,26 +124,28 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

emhd_flags = {Metadata::Cell, Metadata::GetUserFlag("Implicit"), Metadata::GetUserFlag("EMHD")}; + + auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); + flags_prim.insert(flags_prim.end(), emhd_flags.begin(), emhd_flags.end()); + auto flags_cons = packages->Get("Driver")->Param>("cons_flags"); + flags_cons.insert(flags_cons.end(), emhd_flags.begin(), emhd_flags.end()); + + Metadata m_cons = Metadata(flags_cons); + Metadata m_prim = Metadata(flags_prim); // Heat conduction if (conduction) { - pkg->AddField("cons.q", m_con); + pkg->AddField("cons.q", m_cons); pkg->AddField("prims.q", m_prim); } // Pressure anisotropy if (viscosity) { - pkg->AddField("cons.dP", m_con); + pkg->AddField("cons.dP", m_cons); pkg->AddField("prims.dP", m_prim); } @@ -156,19 +158,15 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

AddField("ucov", m_temp_vec); - // This works similarly to the fflag -- + // This works similarly to the fflag: // we register zones where limits on q and dP are hit Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("eflag", m); // Callbacks - // UtoP is *only* for boundary syncs and output, only register that function - // TODO support syncing cons someday - //pkg->BoundaryUtoP = EMHD::BlockUtoP; - - // For now, sync primitive variables & call PtoU on physical boundaries - pkg->BoundaryPtoU = EMHD::BlockPtoU; + // UtoP function specifically for boundary sync (KHARMA must sync cons for AMR) and output + pkg->BoundaryUtoP = EMHD::BlockUtoP; // Add all explicit source terms -- implicit terms are called from Implicit::Step pkg->AddSource = EMHD::AddSource; @@ -183,37 +181,37 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc, IndexDomain domain, bool coarse) -// { -// auto pmb = rc->GetBlockPointer(); - -// PackIndexMap prims_map, cons_map; -// auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); -// auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); -// const VarMap m_p(prims_map, false), m_u(cons_map, true); - -// const auto& G = pmb->coords; - -// auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; -// const IndexRange ib = bounds.GetBoundsI(domain); -// const IndexRange jb = bounds.GetBoundsJ(domain); -// const IndexRange kb = bounds.GetBoundsK(domain); - -// pmb->par_for("UtoP_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, -// KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { -// const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, Loci::center); -// const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); -// const Real ucon0 = gamma * inv_alpha; - -// // Update the primitive EMHD fields -// if (m_p.Q >= 0) -// P(m_p.Q, k, j, i) = U_E(m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); -// if (m_p.DP >= 0) -// P(m_p.DP, k, j, i) = U_E(m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); -// } -// ); -// Kokkos::fence(); -// } +void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +{ + auto pmb = rc->GetBlockPointer(); + + PackIndexMap prims_map, cons_map; + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); + auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + + const auto& G = pmb->coords; + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const IndexRange ib = bounds.GetBoundsI(domain); + const IndexRange jb = bounds.GetBoundsJ(domain); + const IndexRange kb = bounds.GetBoundsK(domain); + + pmb->par_for("UtoP_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, Loci::center); + const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + const Real ucon0 = gamma * inv_alpha; + + // Update the primitive EMHD fields + if (m_p.Q >= 0) + P(m_p.Q, k, j, i) = U_E(m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + if (m_p.DP >= 0) + P(m_p.DP, k, j, i) = U_E(m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + } + ); + Kokkos::fence(); +} void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { @@ -231,7 +229,7 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) const IndexRange jb = bounds.GetBoundsJ(domain); const IndexRange kb = bounds.GetBoundsK(domain); - pmb->par_for("UtoP_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb->par_for("PtoU_EMHD", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { const Real gamma = GRMHD::lorentz_calc(G, P, m_p, k, j, i, Loci::center); const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 6f474d13..c00e54b0 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -52,7 +52,7 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared // That's what this function is for. int nvar = KHARMA::PackDimension(packages.get(), Metadata::WithFluxes); std::vector s_flux({nvar}); - // TODO optionally move all these to faces? Not important yet, no output, more memory + // TODO optionally move all these to faces? Not important yet, & faces have no output, more memory std::vector flags_flux = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; Metadata m = Metadata(flags_flux, s_flux); pkg->AddField("Flux.Pr", m); @@ -62,7 +62,6 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared pkg->AddField("Flux.Fr", m); pkg->AddField("Flux.Fl", m); - // TODO could formally move this to face std::vector s_vector({NVEC}); std::vector flags_speed = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_speed, s_vector); @@ -70,13 +69,16 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared pkg->AddField("Flux.cmin", m); // Preserve all velocities at faces, for upwinded constrained transport - if (packages->AllPackages().count("B_CT")) { + if (packages->AllPackages().count("B_CT")) { // TODO & GS05_c std::vector flags_vel = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::OneCopy}; m = Metadata(flags_vel, s_vector); pkg->AddField("Flux.vr", m); pkg->AddField("Flux.vl", m); } + // We register the geometric (\Gamma*T) source here + pkg->AddSource = Flux::AddGeoSource; + EndFlag(); return pkg; } diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 8e82e224..153cc5a4 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -46,6 +46,7 @@ #include "gr_coordinates.hpp" #include "grmhd_functions.hpp" #include "kharma.hpp" +#include "kharma_driver.hpp" #include @@ -106,7 +107,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->AllParams(); - auto implicit_grmhd = (driver.Get("type") == "imex") && + auto implicit_grmhd = (driver.Get("type") == DriverType::imex) && (pin->GetBoolean("emhd", "on") || pin->GetOrAddBoolean("GRMHD", "implicit", false)); params.Add("implicit", implicit_grmhd); @@ -126,38 +127,25 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_prim = {Metadata::Real, Metadata::Cell, Metadata::Derived, areWeImplicit, - Metadata::Restart, Metadata::GetUserFlag("Primitive"), - Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; - std::vector flags_cons = {Metadata::Real, Metadata::Cell, Metadata::Independent, areWeImplicit, - Metadata::WithFluxes, Metadata::Conserved, Metadata::Conserved, - Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; - - bool sync_prims = packages->Get("Driver")->Param("sync_prims"); - if (!sync_prims) { // Normal operation - // As mentioned elsewhere, KHARMA treats the conserved variables as the independent ones, - // and the primitives as "Derived" - // Primitives are still used for reconstruction, physical boundaries, and output, and are - // generally the easier to understand quantities - // TODO can we not sync prims if we're using two_sync? - flags_cons.push_back(Metadata::FillGhost); - flags_prim.push_back(Metadata::FillGhost); - } else { // Treat primitive vars as fundamental - // When evolving (E)GRMHD implicitly, we just mark the primitive variables to be synchronized. - // This won't work for AMR, but it fits much better with the implicit solver, which expects - // primitive variable inputs and produces primitive variable results. + std::vector flags_grmhd = {Metadata::Cell, areWeImplicit, Metadata::GetUserFlag("HD"), Metadata::GetUserFlag("MHD")}; + + auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); + flags_prim.insert(flags_prim.end(), flags_grmhd.begin(), flags_grmhd.end()); + auto flags_cons = packages->Get("Driver")->Param>("cons_flags"); + flags_cons.insert(flags_cons.end(), flags_grmhd.begin(), flags_grmhd.end()); + + // We must additionally fill ghost zones of primitive variables in GRMHD, to seed the solver + // Only necessary to add here if syncing conserved vars + // Note some startup behavior relies on having the GRHD prims marked for syncing, + // so disable sync_utop_seed at your peril + if (!driver.Get("sync_prims") && pin->GetOrAddBoolean("GRMHD", "sync_utop_seed", true)) { flags_prim.push_back(Metadata::FillGhost); } @@ -189,16 +177,11 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

BlockUtoP // Taken care of by the inverter package since it's hard to do - // There's no "Flux" package, so we register the geometric (\Gamma*T) source here. I think it makes sense. - pkg->AddSource = Flux::AddGeoSource; // On physical boundaries, even if we've sync'd both, respect the application to primitive variables - pkg->BoundaryPtoU = Flux::BlockPtoUMHD; + pkg->DomainBoundaryPtoU = Flux::BlockPtoUMHD; - // Finally, the StateDescriptor/Package object determines the Callbacks Parthenon makes to - // a particular package -- that is, some portion of the things that the package needs done - // at each step, which must be done at specific times. - // See the header files defining each of these functions for their purpose and call context. + // AMR-related pkg->CheckRefinementBlock = GRMHD::CheckRefinement; pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; pkg->PostStepDiagnosticsMesh = GRMHD::PostStepDiagnostics; @@ -222,8 +205,9 @@ Real EstimateTimestep(MeshBlockData *rc) auto& cmax = rc->Get("Flux.cmax").data; auto& cmin = rc->Get("Flux.cmin").data; - // TODO: move timestep limiter into an override of SetGlobalTimestep - // TODO: keep location of the max, or be able to look it up in diagnostics + // TODO: move timestep limiters into KHARMADriver::SetGlobalTimestep + // TODO: option to keep location (in embedding coords) of zone which sets step. + // (this will likely be very slow, but we should do it anyway) auto& globals = pmb->packages.Get("Globals")->AllParams(); const auto& grmhd_pars = pmb->packages.Get("GRMHD")->AllParams(); diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index 62f70e84..e538350c 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -66,10 +66,10 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh // Flag denoting UtoP inversion failures // Only needed if we're actually calling UtoP, but always allocated as it's retrieved often // Needs boundary sync if treating primitive variables as fundamental - bool sync_prims = packages->Get("Driver")->Param("sync_prims"); + bool prims_are_fundamental = packages->Get("Driver")->Param("prims_are_fundamental"); bool implicit_grmhd = packages->Get("GRMHD")->Param("implicit"); Metadata m; - if (sync_prims && !implicit_grmhd) { + if (prims_are_fundamental && !implicit_grmhd) { m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); } else { m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); @@ -151,7 +151,7 @@ TaskStatus Inverter::PostStepDiagnostics(const SimTime& tm, MeshData *md) // Debugging/diagnostic info about floor and inversion flags // TODO grab the total and die on too many if (flag_verbose >= 1) { - // TODO this should move into BlockUtoP when everything goes MeshData + // TODO this should move into UtoP when everything goes MeshData Reductions::StartFlagReduce(md, "pflag", Inverter::status_names, IndexDomain::interior, false, 1); Reductions::CheckFlagReduceAndPrintHits(md, "pflag", Inverter::status_names, IndexDomain::interior, false, 1); } diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 07ec8d42..65748f00 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -209,6 +209,10 @@ void KHARMA::FixParameters(ParameterInput *pin) pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(x1min)); } } + } else { + // Add the coordinate versions if they don't exist (usually restarts) + pin->GetOrAddReal("coordinates", "r_in", tmp_coords.X1_to_embed(pin->GetReal("parthenon/mesh", "x1min"))); + pin->GetOrAddReal("coordinates", "r_out", tmp_coords.X1_to_embed(pin->GetReal("parthenon/mesh", "x1max"))); } // If the simulation domain extends inside the EH, we change some boundary options @@ -241,7 +245,8 @@ void KHARMA::FixParameters(ParameterInput *pin) // << tmp_coords.stopx(1) << " " // << tmp_coords.stopx(2) << " " // << tmp_coords.stopx(3) << std::endl; - // TODO(BSP) is this worth looping? I say probably no. + // In any coordinate system which sets boundaries (i.e. not Cartesian), + // stopx > startx > 0. In Cartesian xNmin/xNmax are required if (tmp_coords.startx(1) >= 0) pin->GetOrAddReal("parthenon/mesh", "x1min", tmp_coords.startx(1)); if (tmp_coords.stopx(1) >= 0) diff --git a/kharma/kharma.hpp b/kharma/kharma.hpp index a772e9f5..17e1deb3 100644 --- a/kharma/kharma.hpp +++ b/kharma/kharma.hpp @@ -117,16 +117,16 @@ inline bool FieldIsOutput(ParameterInput *pin, std::string name) */ inline int PackDimension(Packages_t* packages, Metadata::FlagCollection fc) { - // We want to exclude anything specific to B field cleanup & not used elsewhere - // (confusingly, this isn't *necessarily* everything in the B_Cleanup package) - if (packages->AllPackages().count("B_Cleanup")) - fc = fc - Metadata::GetUserFlag("B_Cleanup"); + // We want to exclude anything specific to startup processes e.g. B field cleanup, + // & not used elsewhere + if (packages->AllPackages().count("StartupOnly")) + fc = fc - Metadata::GetUserFlag("StartupOnly"); // Count dimensions (1 for scalars + vector lengths) of each package's variables int nvar = 0; for (auto pkg : packages->AllPackages()) { nvar += pkg.second->GetPackDimension(fc); - std::cout << pkg.first << " variables: " << pkg.second->GetPackDimension(fc) << std::endl; + // std::cout << pkg.first << " variables: " << pkg.second->GetPackDimension(fc) << std::endl; } return nvar; } diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 68dcb66e..bb0b7aea 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -35,7 +35,6 @@ #include "types.hpp" -// PHYSICS-RELATED // TODO take & accumulate TaskStatus? Useful for ::incomplete if we ever want to do that // TODO continue meshification until all is mesh @@ -57,8 +56,7 @@ TaskStatus Packages::FixFlux(MeshData *md) TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag("BlockUtoP"); - // Apply UtoP from B_CT, as this fills B primitive var for the GRMHD UtoP - // TODO could maybe call this in Inverter, or handle all ordering there, or something + // Apply UtoP from B_CT first, as this fills cons.B at cell centers auto pmb = rc->GetBlockPointer(); auto pkgs = pmb->packages.AllPackages(); if (pkgs.count("B_CT")) { @@ -105,14 +103,18 @@ TaskStatus Packages::BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, b return TaskStatus::complete; } -TaskStatus Packages::BoundaryPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus Packages::BoundaryPtoUElseUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { - Flag("BoundaryPtoU"); + Flag("DomainBoundaryLockstep"); auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); for (auto kpackage : kpackages) { - if (kpackage.second->BoundaryPtoU != nullptr) { - Flag("BoundaryPtoU_"+kpackage.first); - kpackage.second->BoundaryPtoU(rc, domain, coarse); + if (kpackage.second->DomainBoundaryPtoU != nullptr) { + Flag("DomainBoundaryPtoU_"+kpackage.first); + kpackage.second->DomainBoundaryPtoU(rc, domain, coarse); + EndFlag(); + } else if (kpackage.second->BoundaryUtoP != nullptr) { + Flag("DomainBoundaryUtoP_"+kpackage.first); + kpackage.second->BoundaryUtoP(rc, domain, coarse); EndFlag(); } } diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index a3f02620..6075ea4a 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -62,15 +62,16 @@ class KHARMAPackage : public StateDescriptor { // rather, they are called on zone center values once per step only. std::function*, IndexDomain, bool)> BlockUtoP = nullptr; std::function*, IndexDomain, bool)> MeshUtoP = nullptr; - // Allow applying UtoP only/separately for physical boundary domains after sync/prolong/restrict - // e.g., GRMHD does *not* register this as boundaries are applied to prims, - // whereas implicitly-evolved vars *only* register this. + // Allow applying UtoP only/separately for boundary domains after sync/prolong/restrict ops + // All packages with independent variables should register this for AMR std::function*, IndexDomain, bool)> BoundaryUtoP = nullptr; - // Same thing, the other way. For packages syncing primitives, e.g. GRMHD - std::function*, IndexDomain, bool)> BoundaryPtoU = nullptr; + // On domain boundaries, however, we sometimes need to respect the primitive variables. + // Currently only the GRMHD primitives (rho, u, uvec) do this + std::function*, IndexDomain, bool)> DomainBoundaryPtoU = nullptr; // Going the other way, however, is handled by Flux::PtoU. - // All PtoU implementations are device-side (called prim_to_flux) + // All PtoU implementations are device-side (called prim_to_flux), + // so we do not need something like //std::function*, IndexDomain, bool)> BlockPtoU = nullptr; // Source term to add to the conserved variables during each step @@ -85,7 +86,6 @@ class KHARMAPackage : public StateDescriptor { std::function*)> FixFlux = nullptr; // Apply any floors or limiters specific to the package (that is, on the package's variables) - // Called by Floors::*ApplyFloors std::function*, IndexDomain)> BlockApplyFloors = nullptr; std::function*, IndexDomain)> MeshApplyFloors = nullptr; @@ -129,18 +129,21 @@ TaskStatus BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=f TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** - * Version of UtoP specifically for boundaries. Some packages sync & apply boundaries to - * conserved variables, some to primitive variables. + * U to P specifically for boundaries (domain and MPI). + * All packages must define this, even if not using UtoP, as KHARMA must sync conserved + * variables in AMR mode. */ TaskStatus BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); /** - * P to U for boundaries. As it's internal to the flux updates, the "normal" PtoU is - * implemented device-side and called from the "Flux" package + * For each package, run DomainBoundaryPtoU if available, otherwise BoundaryUtoP. + * This is for domain boundaries: if we're syncing the conserved variables, we still + * want to apply domain boundaries to the GRHD primitive variables + * See KBoundaries::ApplyBoundary for details */ -TaskStatus BoundaryPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse=false); +TaskStatus BoundaryPtoUElseUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); /** - * Fill all conserved variables (U) from primitive variables (P), over a whole block + * Fill all conserved variables (U) from primitive variables (P), over a domain on a single block */ // TaskStatus BlockPtoU(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); diff --git a/kharma/main.cpp b/kharma/main.cpp index 724057d0..baf7f1a0 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -207,8 +207,10 @@ int main(int argc, char *argv[]) // Begin code block to ensure driver is cleaned up { - std::string driver_type = pmesh->packages.Get("Driver")->Param("type"); - if (MPIRank0()) std::cout << "Running " << driver_type << " driver" << std::endl; + if (MPIRank0()) { + std::string driver_name = pmesh->packages.Get("Driver")->Param("name"); + std::cout << "Running " << driver_name << " driver" << std::endl; + } // Pull out things we need to give the driver auto pin = pman.pinput.get(); // All parameters in the input file or command line From efcf77e8be86f5a8564446fcd3c7e44aacdbf5c9 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Oct 2023 14:21:08 -0600 Subject: [PATCH 143/219] Problem init & tests (round 1) This moves the rest of the problem initializations to using the unified field init. It also fixes some issues starting up problems when magnetic field has been added, which may have affected the starting internal energy of tori at times in the past (will check). It also moves (restores) a few very specific/brittle parameter files into their specific test dirs, rather than global 'pars' dir. --- kharma/prob/bondi.cpp | 26 ++ kharma/prob/bz_monopole.cpp | 1 - kharma/prob/emhd/anisotropic_conduction.hpp | 14 +- kharma/prob/emhd/conducting_atmosphere.cpp | 10 +- kharma/prob/emhd/emhdmodes.hpp | 27 +- kharma/prob/emhd/emhdshock.hpp | 23 +- kharma/prob/explosion.hpp | 1 - kharma/prob/fm_torus.hpp | 32 +-- kharma/prob/kelvin_helmholtz.hpp | 1 - kharma/prob/mhdmodes.hpp | 27 +- kharma/prob/post_initialize.cpp | 13 +- kharma/prob/problem.cpp | 18 +- kharma/prob/seed_B.cpp | 30 +- kharma/prob/seed_B.hpp | 24 +- kharma/prob/shock_tube.hpp | 20 +- .../bondi_analytic_128.txt | 128 --------- .../bondi_analytic_256.txt | 256 ------------------ .../bondi_analytic_64.txt | 64 ----- tests/bondi_viscous/check.py | 69 +++-- tests/bondi_viscous/run.sh | 6 +- tests/conducting_atmosphere/check.py | 15 +- .../conducting_atmosphere.par | 97 +++++++ tests/conducting_atmosphere/run.sh | 2 +- tests/emhdshock/emhdshock.par | 94 +++++++ tests/mhdmodes/check.py | 35 +-- tests/mhdmodes/run.sh | 63 +++-- tests/noh/check.py | 2 +- tests/noh/run.sh | 7 +- 28 files changed, 481 insertions(+), 624 deletions(-) delete mode 100644 tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt delete mode 100644 tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt delete mode 100644 tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt create mode 100644 tests/conducting_atmosphere/conducting_atmosphere.par create mode 100644 tests/emhdshock/emhdshock.par diff --git a/kharma/prob/bondi.cpp b/kharma/prob/bondi.cpp index d97d448b..4e0c11d7 100644 --- a/kharma/prob/bondi.cpp +++ b/kharma/prob/bondi.cpp @@ -187,5 +187,31 @@ TaskStatus SetBondiImpl(std::shared_ptr>& rc, IndexDomain do } ); + // Generally I avoid this, but the viscous Bondi test problem has very unique + // boundary requirements to converge. The GRMHD vars must be held constant, + // but the pressure anisotropy allowed to change as necessary with outflow conditions + if (pmb->packages.Get("Globals")->Param("problem") == "bondi_viscous") { + BoundaryFace bface = KBoundaries::BoundaryFaceOf(domain); + bool inner = KBoundaries::BoundaryIsInner(bface); + IndexRange ib_i = bounds.GetBoundsI(domain); + int ref = inner ? ib_i.s : ib_i.e; + pmb->par_for("bondi_viscous_boundary", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + GReal Xembed[GR_DIM]; + G.coord_embed(k, j, i, Loci::center, Xembed); + GReal r = Xembed[1]; + // TODO more general? + if (m_p.B1 >= 0) { + P(m_p.B1, k, j, i) = 1/(r*r*r); + P(m_p.B2, k, j, i) = 0.; + P(m_p.B3, k, j, i) = 0.; + } + if (m_p.DP >= 0) { + P(m_p.DP, k, j, i) = P(m_p.DP, k, j, ref); + } + } + ); + } + return TaskStatus::complete; } diff --git a/kharma/prob/bz_monopole.cpp b/kharma/prob/bz_monopole.cpp index c5c4ee0e..3a442091 100644 --- a/kharma/prob/bz_monopole.cpp +++ b/kharma/prob/bz_monopole.cpp @@ -46,7 +46,6 @@ TaskStatus InitializeBZMonopole(std::shared_ptr>& rc, Parame GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; Real bsq_o_rho_max = pin->GetOrAddReal("floors", "bsq_over_rho_max", 1.e2); Real rho_min_limit = pin->GetOrAddReal("floors", "rho_min_geom", 1.e-6); diff --git a/kharma/prob/emhd/anisotropic_conduction.hpp b/kharma/prob/emhd/anisotropic_conduction.hpp index b467efce..26220c7e 100644 --- a/kharma/prob/emhd/anisotropic_conduction.hpp +++ b/kharma/prob/emhd/anisotropic_conduction.hpp @@ -48,8 +48,7 @@ TaskStatus InitializeAnisotropicConduction(std::shared_ptr>& GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - // It is well and good this problem should cry if B/EMHD are disabled. - GridVector B_P = rc->Get("prims.B").data; + // It is well and good this problem should cry if EMHD is disabled. GridVector q = rc->Get("prims.q").data; GridVector dP = rc->Get("prims.dP").data; @@ -62,6 +61,14 @@ TaskStatus InitializeAnisotropicConduction(std::shared_ptr>& const Real R = m::sqrt(Rsq); + pin->GetOrAddString("b_field", "type", "wave"); + pin->GetOrAddReal("b_field", "phase", 0.); + // Constant B1 + pin->GetOrAddReal("b_field", "B10", B0); + // Amp & wavenumber of sin() for B2 + pin->GetOrAddReal("b_field", "amp2_B2", B0); + pin->GetOrAddReal("b_field", "k1", 2*M_PI*k0); + IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::entire); IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::entire); IndexRange kb = pmb->cellbounds.GetBoundsK(IndexDomain::entire); @@ -77,9 +84,6 @@ TaskStatus InitializeAnisotropicConduction(std::shared_ptr>& uvec(0, k, j, i) = 0.; uvec(1, k, j, i) = 0.; uvec(2, k, j, i) = 0.; - B_P(0, k, j, i) = B0; - B_P(1, k, j, i) = B0 * sin(2*M_PI*k0*X[1]); - B_P(2, k, j, i) = 0; q(k, j, i) = 0.; dP(k, j, i) = 0.; } diff --git a/kharma/prob/emhd/conducting_atmosphere.cpp b/kharma/prob/emhd/conducting_atmosphere.cpp index 170cd914..271b2111 100644 --- a/kharma/prob/emhd/conducting_atmosphere.cpp +++ b/kharma/prob/emhd/conducting_atmosphere.cpp @@ -71,6 +71,10 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame // Type of input to the problem const std::string input = pin->GetOrAddString("conducting_atmosphere", "input", "ODE"); + // Set default B field parameters + pin->GetOrAddString("b_field", "type", "monopole_cube"); + pin->GetOrAddReal("b_field", "B10", 1.); + // Bounds of the domain IndexRange ib = pmb->cellbounds.GetBoundsI(IndexDomain::entire); IndexRange jb = pmb->cellbounds.GetBoundsJ(IndexDomain::entire); @@ -104,13 +108,11 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; // Host side mirror of primitives auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); - auto B_host = B_P.GetHostMirror(); // Then for EMHD if enabled GridScalar q; @@ -167,9 +169,6 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame q_host(k, j, i) = q_temp; // Now the remaining primitives - B_host(V1, k, j, i) = 1./(Xembed[1]*Xembed[1]*Xembed[1]); - B_host(V2, k, j, i) = 0.; - B_host(V3, k, j, i) = 0.; if (use_emhd && emhd_params.viscosity) dP_host(k, j, i) = 0.; @@ -223,7 +222,6 @@ TaskStatus InitializeAtmosphere(std::shared_ptr>& rc, Parame rho.DeepCopy(rho_host); u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); - B_P.DeepCopy(B_host); if (use_emhd && emhd_params.conduction) q.DeepCopy(q_host); if (use_emhd && emhd_params.viscosity) diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 7c9c7f9d..371575d2 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -53,8 +53,7 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - // It is well and good this problem should cry if B/EMHD are disabled. - GridVector B_P = rc->Get("prims.B").data; + // It is well and good this problem should cry if EMHD is disabled. GridVector q = rc->Get("prims.q").data; GridVector dP = rc->Get("prims.dP").data; @@ -89,7 +88,19 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet const Real k2 = 4. * M_PI; // END POSSIBLE ARGS - // TODO SET B PARAMS HERE + // Set magnetic field parameters for our field transport package + pin->GetOrAddString("b_field", "type", "wave"); + pin->GetOrAddReal("b_field", "B10", B10); + pin->GetOrAddReal("b_field", "B20", B20); + pin->GetOrAddReal("b_field", "B30", B30); + pin->GetOrAddReal("b_field", "k1", k1); + pin->GetOrAddReal("b_field", "k2", k2); + + pin->GetOrAddReal("b_field", "amp_B1", amp * (-0.05973794979640743)); + pin->GetOrAddReal("b_field", "amp2_B1", amp * (0.03351707506150924)); + + pin->GetOrAddReal("b_field", "amp_B2", amp * (0.02986897489820372)); + pin->GetOrAddReal("b_field", "amp2_B2", amp * (-0.016758537530754618)); IndexDomain domain = IndexDomain::interior; IndexRange ib = pmb->cellbounds.GetBoundsI(domain); @@ -99,8 +110,8 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); - const Real cos_phi = cos(k1*X[1] + k2*X[2]); - const Real sin_phi = sin(k1*X[1] + k2*X[2]); + const Real cos_phi = m::cos(k1*X[1] + k2*X[2]); + const Real sin_phi = m::sin(k1*X[1] + k2*X[2]); // Perturbations: no higher-order terms const Real drho = amp * (((-0.518522524082246)*cos_phi) + ((0.1792647678001878)*sin_phi)); @@ -108,9 +119,6 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet const Real du1 = amp * (((0.008463122479547856)*cos_phi) + ((-0.011862022608466367)*sin_phi)); const Real du2 = amp * (((-0.16175466371870734)*cos_phi) + ((0.034828080823603294)*sin_phi)); const Real du3 = 0.; - const Real dB1 = amp * (((-0.05973794979640743)*cos_phi) + ((0.03351707506150924)*sin_phi)); - const Real dB2 = amp * (((0.02986897489820372)*cos_phi) - ((0.016758537530754618)*sin_phi)); - const Real dB3 = 0.; const Real dq = amp * (((0.5233486841539436)*cos_phi) - ((0.04767672501939603)*sin_phi)); const Real ddelta_p = amp * (((0.2909106062057657)*cos_phi) - ((0.02159452055336572)*sin_phi)); @@ -120,9 +128,6 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet uvec(V1, k, j, i) = u10 + du1; uvec(V2, k, j, i) = u20 + du2; uvec(V3, k, j, i) = u30 + du3; - B_P(V1, k, j, i) = B10 + dB1; - B_P(V2, k, j, i) = B20 + dB2; - B_P(V3, k, j, i) = B30 + dB3; q(k, j, i) = q0 + dq; dP(k, j, i) = delta_p0 + ddelta_p; diff --git a/kharma/prob/emhd/emhdshock.hpp b/kharma/prob/emhd/emhdshock.hpp index c61e07c1..5a55d8ae 100644 --- a/kharma/prob/emhd/emhdshock.hpp +++ b/kharma/prob/emhd/emhdshock.hpp @@ -61,7 +61,6 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; GridVector q = rc->Get("prims.q").data; GridVector dP = rc->Get("prims.dP").data; @@ -70,6 +69,11 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet // Type of input to the problem const std::string input = pin->GetOrAddString("emhdshock", "input", "BVP"); + // Both shocks do not have a B jump condition, just set B10 + // TODO take magnetization? + pin->GetOrAddString("b_field", "type", "constant"); + pin->GetOrAddReal("b_field", "B10", 1.e-5); + // Obtain EMHD params const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); // Obtain GRMHD params @@ -100,10 +104,13 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet fp_q = fopen(fbvp_q, "r"); fp_dP = fopen(fbvp_dP, "r"); + if (fp_rho == NULL || fp_u == NULL || fp_u1 == NULL || fp_q == NULL || fp_dP == NULL) { + throw std::runtime_error("Could not open conducting atmosphere solution!"); + } + auto rho_host = rho.GetHostMirror(); auto u_host = u.GetHostMirror(); auto uvec_host = uvec.GetHostMirror(); - auto B_host = B_P.GetHostMirror(); auto q_host = q.GetHostMirror(); auto dP_host = dP.GetHostMirror(); @@ -124,9 +131,6 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet // Now the remaining primitives uvec_host(1, k, j, i) = 0.; uvec_host(2, k, j, i) = 0.; - B_host(V1, k, j, i) = 1.e-5; - B_host(V2, k, j, i) = 0.; - B_host(V3, k, j, i) = 0.; if (emhd_params.higher_order_terms) { @@ -165,7 +169,6 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet rho.DeepCopy(rho_host); u.DeepCopy(u_host); uvec.DeepCopy(uvec_host); - B_P.DeepCopy(B_host); q.DeepCopy(q_host); dP.DeepCopy(dP_host); Kokkos::fence(); @@ -182,16 +185,13 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet double u1L = 1., u1R = 0.32434571; double u2L = 0., u2R = 0.; double u3L = 0., u3R = 0.; - double B1L = 1.e-5, B1R = 1.e-5; - double B2L = 0, B2R = 0.; - double B3L = 0., B3R = 0.; + const GReal x1_center = (x1min + x1max) / 2.; pmb->par_for("emhdshock_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; G.coord_embed(k, j, i, Loci::center, X); - const Real x1_center = (x1min + x1max) / 2.; bool lhs = X[1] < x1_center; @@ -201,9 +201,6 @@ TaskStatus InitializeEMHDShock(std::shared_ptr>& rc, Paramet uvec(V1, k, j, i) = (lhs) ? u1L : u1R; uvec(V2, k, j, i) = (lhs) ? u2L : u2R; uvec(V3, k, j, i) = (lhs) ? u3L : u3R; - B_P(V1, k, j, i) = (lhs) ? B1L : B1R; - B_P(V2, k, j, i) = (lhs) ? B2L : B2R; - B_P(V3, k, j, i) = (lhs) ? B3L : B3R; q(k ,j, i) = 0.; dP(k ,j, i) = 0.; diff --git a/kharma/prob/explosion.hpp b/kharma/prob/explosion.hpp index 65102d4f..5ed7a73c 100644 --- a/kharma/prob/explosion.hpp +++ b/kharma/prob/explosion.hpp @@ -55,7 +55,6 @@ TaskStatus InitializeExplosion(std::shared_ptr>& rc, Paramet GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto& G = pmb->coords; diff --git a/kharma/prob/fm_torus.hpp b/kharma/prob/fm_torus.hpp index 211fe1e7..326edf1e 100644 --- a/kharma/prob/fm_torus.hpp +++ b/kharma/prob/fm_torus.hpp @@ -17,11 +17,14 @@ TaskStatus InitializeFMTorus(std::shared_ptr>& rc, Parameter */ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal rin, const GReal r, const GReal th) { - Real sth = sin(th); - Real cth = cos(th); + // TODO this isn't faster than splitting into two evaluations of a sub-function, + // and it doesn't matter anyway. Make it clearer + Real sth = m::sin(th); + Real cth = m::cos(th); - Real r2 = m::pow(r, 2); - Real a2 = m::pow(a, 2); + Real r2 = r*r; + Real a2 = a*a; + // Metric Real DD = r2 - 2. * r + a2; Real AA = m::pow(r2 + a2, 2) - DD * a2 * sth * sth; Real SS = r2 + a2 * cth * cth; @@ -46,7 +49,7 @@ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal ri 4. * (l * l * SS * SS) * DD / (AA * AA * sth * sth)) - 2. * a * r * l / AA - - (0.5 * + (0.5 * m::log((1. + m::sqrt(1. + 4. * (l * l * SSin * SSin) * DDin / @@ -69,17 +72,14 @@ KOKKOS_INLINE_FUNCTION Real lnh_calc(const GReal a, const Real l, const GReal ri */ KOKKOS_INLINE_FUNCTION Real lfish_calc(const GReal a, const GReal r) { - return (((m::pow(a, 2) - 2. * a * m::sqrt(r) + m::pow(r, 2)) * - ((-2. * a * r * - (m::pow(a, 2) - 2. * a * m::sqrt(r) + - m::pow(r, - 2))) / - m::sqrt(2. * a * m::sqrt(r) + (-3. + r) * r) + - ((a + (-2. + r) * m::sqrt(r)) * (m::pow(r, 3) + m::pow(a, 2) * - (2. + r))) / + GReal sqtr = m::sqrt(r); + return ((a*a - 2. * a * sqtr + r*r) * + ((-2. * a * r * (a*a - 2. * a * sqtr + r*r)) / + m::sqrt(2. * a * sqtr + (-3. + r) * r) + + ((a + (-2. + r) * sqtr) * (r*r*r + a*a * (2. + r))) / m::sqrt(1 + (2. * a) / m::pow(r, 1.5) - 3. / r))) / - (m::pow(r, 3) * m::sqrt(2. * a * m::sqrt(r) + (-3. + r) * r) * - (m::pow(a, 2) + (-2. + r) * r))); + (r*r*r * m::sqrt(2. * a * sqtr + (-3. + r) * r) * + (a*a + (-2. + r) * r)); } /** @@ -88,7 +88,7 @@ KOKKOS_INLINE_FUNCTION Real lfish_calc(const GReal a, const GReal r) * This function is *not* used for the actual initialization (where rho is calculated * alongside the other primitive variables). Rather, it is for: * 1. Normalization, in which the max of this function over the domain is calculated. - * 2. B field initialization, which requires density the untilted disk for simplicity + * 2. B field initialization, which requires density of the untilted disk for simplicity */ KOKKOS_INLINE_FUNCTION Real fm_torus_rho(const GReal a, const GReal rin, const GReal rmax, const Real gam, const Real kappa, const GReal r, const GReal th) diff --git a/kharma/prob/kelvin_helmholtz.hpp b/kharma/prob/kelvin_helmholtz.hpp index 25c79033..921f97da 100644 --- a/kharma/prob/kelvin_helmholtz.hpp +++ b/kharma/prob/kelvin_helmholtz.hpp @@ -53,7 +53,6 @@ TaskStatus InitializeKelvinHelmholtz(std::shared_ptr>& rc, P GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; // follows notation of Lecoanet et al. eq. 8 et seq. const Real tscale = pin->GetOrAddReal("kelvin_helmholtz", "tscale", 0.05); diff --git a/kharma/prob/mhdmodes.hpp b/kharma/prob/mhdmodes.hpp index 4a8167f0..b7d49ce0 100644 --- a/kharma/prob/mhdmodes.hpp +++ b/kharma/prob/mhdmodes.hpp @@ -63,12 +63,11 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto& G = pmb->coords; const int nmode = pin->GetOrAddInteger("mhdmodes", "nmode", 1); - const bool one_period = pin->GetOrAddBoolean("mhdmodes", "one_period", true); + const bool one_period = pin->GetOrAddBoolean("mhdmodes", "one_period", nmode != 0); // Mean state const Real rho0 = pin->GetOrAddReal("mhdmodes", "rho0", 1.); @@ -82,6 +81,7 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete // Set to 0 for "full" 3D wave. const int dir = pin->GetOrAddInteger("mhdmodes", "dir", 0); const Real amp = pin->GetOrAddReal("mhdmodes", "amp", 1.e-4); + const Real phase = pin->GetOrAddReal("mhdmodes", "phase", 0.); // Note the modes below don't work right if you manually set these // TODO generate modes on the fly for any k values @@ -190,12 +190,10 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete } } - // Record the parameters + // Record the parameters we set via nmode // This might be useful to read when checking, too... - // TODO pin->SetReal("mhdmodes", "omega_real", omega.real()); pin->SetReal("mhdmodes", "omega_imag", omega.imag()); - pin->SetReal("mhdmodes", "drho", drho); pin->SetReal("mhdmodes", "du", du); pin->SetReal("mhdmodes", "du1", du1); @@ -207,13 +205,16 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete // Set B field parameters for our mode pin->GetOrAddString("b_field", "type", "wave"); - pin->GetOrAddReal("b_field", "b10", B10); - pin->GetOrAddReal("b_field", "b20", B20); - pin->GetOrAddReal("b_field", "b30", B30); + pin->GetOrAddReal("b_field", "B10", B10); + pin->GetOrAddReal("b_field", "B20", B20); + pin->GetOrAddReal("b_field", "B30", B30); pin->GetOrAddReal("b_field", "amp_B1", amp*dB1); pin->GetOrAddReal("b_field", "amp_B2", amp*dB2); pin->GetOrAddReal("b_field", "amp_B3", amp*dB3); - pin->GetOrAddReal("b_field", "phase", 0.); + pin->GetOrAddReal("b_field", "k1", k1); + pin->GetOrAddReal("b_field", "k2", k2); + pin->GetOrAddReal("b_field", "k3", k3); + pin->GetOrAddReal("b_field", "phase", phase); IndexDomain domain = IndexDomain::interior; IndexRange ib = pmb->cellbounds.GetBoundsI(domain); @@ -226,14 +227,14 @@ TaskStatus InitializeMHDModes(std::shared_ptr>& rc, Paramete Real mode = amp * m::cos(k1 * X[1] + k2 * X[2] + k3 * X[3]); rho(k, j, i) = rho0 + drho * mode; u(k, j, i) = u0 + du * mode; - uvec(0, k, j, i) = u10 + du1 * mode; - uvec(1, k, j, i) = u20 + du2 * mode; - uvec(2, k, j, i) = u30 + du3 * mode; + uvec(V1, k, j, i) = u10 + du1 * mode; + uvec(V2, k, j, i) = u20 + du2 * mode; + uvec(V3, k, j, i) = u30 + du3 * mode; } ); // Override end time to be exactly 1 period for moving modes, unless we set otherwise - if (nmode != 0 && one_period) { + if (one_period) { pin->SetReal("parthenon/time", "tlim", 2. * M_PI / m::abs(omega.imag())); } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 1fc7a790..8dae599c 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -104,8 +104,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } } - // Add any hotspots. - // Note any other modifications made when restarting should be made around here + // Add any hotspots *after* we've seeded fields, + // since seeding may be based on density if (pin->GetOrAddBoolean("blob", "add_blob", false)) { for (auto &pmb : pmesh->block_list) { auto rc = pmb->meshblock_data.Get(); @@ -121,7 +121,7 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KHARMA::ResetGlobals(pin, pmesh); } - // Clean the B field if we've introduced a divergence somewhere + // Clean the B field, generally for resizing/restarting // We call this function any time the package is loaded: // if we decided to load it in kharma.cpp, we need to clean. if (pkgs.count("B_Cleanup")) { @@ -135,13 +135,16 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) B_Cleanup::CleanupDivergence(md); } + // If PtoU was called before the B field was initialized or corrected, + // the total energy might be wrong. Now that we have the field, + // wipe away any temporary "totals" which may have omitted it + Flux::MeshPtoU(md.get(), IndexDomain::entire); + // Finally, synchronize boundary values. // Freeze any Dirichlet physical boundaries as they are now, after cleanup/sync/etc. KBoundaries::FreezeDirichlet(md); // This is the first sync if there is no B field KHARMADriver::SyncAllBounds(md); - // And make sure the trivial primitive values are up-to-date - //Packages::MeshUtoPExceptMHD(md.get(), IndexDomain::entire, false); // TODO output parsed parameters now we have *everything* including any problem configs for B field } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 2c0a8b11..01897edb 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -125,7 +125,7 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) status = ReadKharmaRestart(rc, pin); } else if (prob == "gizmo") { status = InitializeGIZMO(rc, pin); - } else if (prob == "vacuum") { + } else if (prob == "vacuum" || prob == "bz_monopole") { // No need for a separate initializer, just seed w/floors status = Floors::ApplyInitialFloors(pin, rc.get(), IndexDomain::interior); } @@ -153,20 +153,16 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) } } - // TODO blob here? - // Floors are NOT automatically applied at this point anymore. // If needed, they are applied within the problem-specific call. // See InitializeFMTorus in fm_torus.cpp for the details for torus problems. - // Fill the conserved variables U, - // which we'll usually treat as the independent/fundamental state. - // This will need to be repeated once magnetic field is seeded - // Note we do the whole domain, in case we're using Dirichlet conditions - Flux::BlockPtoU(rc.get(), IndexDomain::entire); - - // Finally, freeze in the current ghost zone values if using Dirichlet conditions - KBoundaries::FreezeDirichletBlock(rc.get()); + // Note we no longer call PtoU here either, as GRMHD variables' PtoU requires + // the magnetic field, which is added in PostInitialize, after all blocks + // are filled with other variables (it can be related to density averages which + // require correct ghost zones) + // ALL OTHER VARIABLES, however, must fill U if a magnetic field will depend on + // them in any way, as conserved variables are MPI-synchronized EndFlag(); } diff --git a/kharma/prob/seed_B.cpp b/kharma/prob/seed_B.cpp index c85b5fd9..a335dc26 100644 --- a/kharma/prob/seed_B.cpp +++ b/kharma/prob/seed_B.cpp @@ -101,12 +101,16 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom if constexpr (Seed == BSeedType::constant || Seed == BSeedType::monopole || Seed == BSeedType::monopole_cube || - Seed == BSeedType::orszag_tang) + Seed == BSeedType::orszag_tang || + Seed == BSeedType::wave || + Seed == BSeedType::shock_tube) { - // All custom B fields should set what they need of these - const Real b10 = pin->GetOrAddReal("b_field", "B10", 0.); - const Real b20 = pin->GetOrAddReal("b_field", "B20", 0.); - const Real b30 = pin->GetOrAddReal("b_field", "B30", 0.); + // All custom B fields should set what they need of these. + // We take the same names, but they may mean different things to the + // particular init function, check seed_B.hpp + const Real B10 = pin->GetOrAddReal("b_field", "B10", 0.); + const Real B20 = pin->GetOrAddReal("b_field", "B20", 0.); + const Real B30 = pin->GetOrAddReal("b_field", "B30", 0.); const Real k1 = pin->GetOrAddReal("b_field", "k1", 0.); const Real k2 = pin->GetOrAddReal("b_field", "k2", 0.); const Real k3 = pin->GetOrAddReal("b_field", "k3", 0.); @@ -127,9 +131,10 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom GReal Xembed[GR_DIM]; double null1, null2; double B_Pf1, B_Pf2, B_Pf3; + // TODO handle calling Seed() mid-run and adding field G.coord_embed(k, j, i, Loci::face1, Xembed); GReal gdet = G.gdet(Loci::face1, j, i); - B_Pf1 = b10; + B_Pf1 = B10; seed_b(Xembed, gdet, k1, k2, k3, phase, amp_B1, amp_B2, amp_B3, amp2_B1, amp2_B2, amp2_B3, @@ -138,7 +143,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom G.coord_embed(k, j, i, Loci::face2, Xembed); gdet = G.gdet(Loci::face2, j, i); - B_Pf2 = b20; + B_Pf2 = B20; seed_b(Xembed, gdet, k1, k2, k3, phase, amp_B1, amp_B2, amp_B3, amp2_B1, amp2_B2, amp2_B3, @@ -147,7 +152,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom G.coord_embed(k, j, i, Loci::face3, Xembed); gdet = G.gdet(Loci::face3, j, i); - B_Pf3 = b30; + B_Pf3 = B30; seed_b(Xembed, gdet, k1, k2, k3, phase, amp_B1, amp_B2, amp_B3, amp2_B1, amp2_B2, amp2_B3, @@ -165,6 +170,9 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom GReal Xembed[GR_DIM]; G.coord_embed(k, j, i, Loci::center, Xembed); const GReal gdet = G.gdet(Loci::center, j, i); + B_P(V1, k, j, i) = B10; + B_P(V2, k, j, i) = B20; + B_P(V3, k, j, i) = B30; seed_b(Xembed, gdet, k1, k2, k3, phase, amp_B1, amp_B2, amp_B3, amp2_B1, amp2_B2, amp2_B3, @@ -175,7 +183,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom ); // We still need to update conserved flux values, but then we're done B_FluxCT::BlockPtoU(rc, domain); - } + } // TODO B_CD!! return TaskStatus::complete; } else { // Seed with vector potential A otherwise // Require and load what we need if necessary @@ -332,7 +340,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom } // Finally, make sure we initialize the primitive field too B_FluxCT::BlockUtoP(rc, domain); - } + } // TODO B_CD!! return TaskStatus::complete; } @@ -385,6 +393,8 @@ TaskStatus SeedBField(MeshData *md, ParameterInput *pin) status = SeedBFieldType(rc, pin); } else if (b_field_type == "wave") { status = SeedBFieldType(rc, pin); + } else if (b_field_type == "shock_tube") { + status = SeedBFieldType(rc, pin); } else { throw std::invalid_argument("Magnetic field seed type not supported: " + b_field_type); } diff --git a/kharma/prob/seed_B.hpp b/kharma/prob/seed_B.hpp index 61537a2f..dea13857 100644 --- a/kharma/prob/seed_B.hpp +++ b/kharma/prob/seed_B.hpp @@ -40,8 +40,18 @@ TaskStatus SeedBField(MeshData *md, ParameterInput *pin); TaskStatus NormalizeBField(MeshData *md, ParameterInput *pin); +/* + * B field initializations. + * TO ADD A FIELD: + * 1. add its internal name to the enum below + * 2. Implement the template specialization for your field, either from seed_a<> or seed_b<> + * 3. Add your specialization to the `if` statements in SeedBField + * 4. If you used seed_b<>, add your case where SeedBFieldType<> selects direct initialization + * 5. If you added arguments, make sure the calls in SeedBFieldType<> are up-to-date + */ + // Internal representation of the field initialization preference, used for templating -enum BSeedType{constant, monopole, monopole_cube, orszag_tang, orszag_tang_a, wave, +enum BSeedType{constant, monopole, monopole_cube, orszag_tang, orszag_tang_a, wave, shock_tube, sane, mad, mad_quadrupole, r3s3, r5s5, gaussian, bz_monopole, vertical}; #define SEEDA_ARGS GReal *x, const GReal *dxc, double rho, double rin, double min_A, double A0, double arg1 @@ -151,13 +161,23 @@ KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) template<> KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) { - const Real smode = m::cos(k1 * x[1] + k2 * x[2] + k3 * x[3] + phase); + const Real smode = m::sin(k1 * x[1] + k2 * x[2] + k3 * x[3] + phase); const Real cmode = m::cos(k1 * x[1] + k2 * x[2] + k3 * x[3] + phase); B1 += amp_B1 * cmode + amp2_B1 * smode; B2 += amp_B2 * cmode + amp2_B2 * smode; B3 += amp_B3 * cmode + amp2_B3 * smode; } +// Shock tube init +template<> +KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) +{ + const bool lhs = x[1] < phase; + B1 += (lhs) ? amp_B1 : amp2_B1; + B2 += (lhs) ? amp_B2 : amp2_B2; + B3 += (lhs) ? amp_B3 : amp2_B3; +} + // For Orszag-Tang vortex template<> KOKKOS_INLINE_FUNCTION void seed_b(SEEDB_ARGS) diff --git a/kharma/prob/shock_tube.hpp b/kharma/prob/shock_tube.hpp index b41eaf6a..b9f5abcf 100644 --- a/kharma/prob/shock_tube.hpp +++ b/kharma/prob/shock_tube.hpp @@ -16,7 +16,6 @@ TaskStatus InitializeShockTube(std::shared_ptr>& rc, Paramet GridScalar rho = rc->Get("prims.rho").data; GridScalar u = rc->Get("prims.u").data; GridVector uvec = rc->Get("prims.uvec").data; - GridVector B_P = rc->Get("prims.B").data; const auto& G = pmb->coords; @@ -32,6 +31,7 @@ TaskStatus InitializeShockTube(std::shared_ptr>& rc, Paramet const Real u2R = pin->GetOrAddReal("shock", "u2R", 0.0); const Real u3L = pin->GetOrAddReal("shock", "u3L", 0.0); const Real u3R = pin->GetOrAddReal("shock", "u3R", 0.0); + const Real B1L = pin->GetOrAddReal("shock", "B1L", 0.0); const Real B1R = pin->GetOrAddReal("shock", "B1R", 0.0); const Real B2L = pin->GetOrAddReal("shock", "B2L", 0.0); @@ -48,6 +48,15 @@ TaskStatus InitializeShockTube(std::shared_ptr>& rc, Paramet const Real x1max = pin->GetReal("parthenon/mesh", "x1max"); const Real center = (x1min + x1max) / 2.; + pin->GetOrAddString("b_field", "type", "shock_tube"); + pin->GetOrAddReal("b_field", "phase", center); + pin->GetOrAddReal("b_field", "amp_B1", B1L); + pin->GetOrAddReal("b_field", "amp_B2", B2L); + pin->GetOrAddReal("b_field", "amp_B3", B3L); + pin->GetOrAddReal("b_field", "amp2_B1", B1R); + pin->GetOrAddReal("b_field", "amp2_B2", B2R); + pin->GetOrAddReal("b_field", "amp2_B3", B3R); + pmb->par_for("ot_init", kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { Real X[GR_DIM]; @@ -59,17 +68,8 @@ TaskStatus InitializeShockTube(std::shared_ptr>& rc, Paramet uvec(0, k, j, i) = (lhs) ? u1L : u1R; uvec(1, k, j, i) = (lhs) ? u2L : u2R; uvec(2, k, j, i) = (lhs) ? u3L : u3R; - B_P(0, k, j, i) = (lhs) ? B1L : B1R; - B_P(1, k, j, i) = (lhs) ? B2L : B2R; - B_P(2, k, j, i) = (lhs) ? B3L : B3R; } ); - if(pmb->packages.AllPackages().count("Electrons")) { - // Get e- starting parameters - - // Set e- starting state - } - return TaskStatus::complete; } diff --git a/tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt b/tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt deleted file mode 100644 index 97df8b68..00000000 --- a/tests/bondi_viscous/bondi_viscous_128_default/bondi_analytic_128.txt +++ /dev/null @@ -1,128 +0,0 @@ -1.804604567587375641e-02 3.334784880280494690e-03 -5.720360875129699707e-01 3.203265241319423057e-03 -1.769512519240379333e-02 3.227407578378915787e-03 -5.655881166458129883e-01 3.141545548595221261e-03 -1.735138520598411560e-02 3.123594913631677628e-03 -5.592013597488403320e-01 3.080914918348832011e-03 -1.701467670500278473e-02 3.023225814104080200e-03 -5.528752207756042480e-01 3.021340354083649083e-03 -1.668484508991241455e-02 2.926182234659790993e-03 -5.466093420982360840e-01 2.962831289634906308e-03 -1.636174879968166351e-02 2.832352416589856148e-03 -5.404032468795776367e-01 2.905403743434595114e-03 -1.604524627327919006e-02 2.741627395153045654e-03 -5.342563390731811523e-01 2.849007060224692609e-03 -1.573519408702850342e-02 2.653900301083922386e-03 -5.281683802604675293e-01 2.793589218016250420e-03 -1.543146185576915741e-02 2.569071482867002487e-03 -5.221387147903442383e-01 2.739166120096183776e-03 -1.513390894979238510e-02 2.487041056156158447e-03 -5.161669850349426270e-01 2.685726415972453356e-03 -1.484241150319576263e-02 2.407715423032641411e-03 -5.102526545524597168e-01 2.633225577159857556e-03 -1.455683726817369461e-02 2.331002615392208099e-03 -5.043954849243164062e-01 2.581643180119114721e-03 -1.427706424146890640e-02 2.256814856082201004e-03 -4.985947608947753906e-01 2.530984252484574926e-03 -1.400297041982412338e-02 2.185066696256399155e-03 -4.928501248359680176e-01 2.481241882113827645e-03 -1.373443286865949631e-02 2.115675015375018120e-03 -4.871611893177032471e-01 2.432399938156275775e-03 -1.347134262323379517e-02 2.048562280833721161e-03 -4.815274775028228760e-01 2.384406996040078046e-03 -1.321357581764459610e-02 1.983649795874953270e-03 -4.759485125541687012e-01 2.337276681629182895e-03 -1.296102628111839294e-02 1.920864568091928959e-03 -4.704238772392272949e-01 2.291014141894550434e-03 -1.271358411759138107e-02 1.860134885646402836e-03 -4.649530947208404541e-01 2.245568208908161906e-03 -1.247114315629005432e-02 1.801391597837209702e-03 -4.595358073711395264e-01 2.200939176100989974e-03 -1.223359536379575729e-02 1.744568115100264549e-03 -4.541715979576110840e-01 2.157122854302493363e-03 -1.200084201991558075e-02 1.689600176177918911e-03 -4.488598704338073730e-01 2.114060868088609314e-03 -1.177278161048889160e-02 1.636425382457673550e-03 -4.436002969741821289e-01 2.071770489416943726e-03 -1.154931634664535522e-02 1.584983663633465767e-03 -4.383924603462219238e-01 2.030269705892970289e-03 -1.133034937083721161e-02 1.535217510536313057e-03 -4.332359433174133301e-01 1.989515835773508854e-03 -1.111578755080699921e-02 1.487069996073842049e-03 -4.281302988529205322e-01 1.949498268381351677e-03 -1.090554054826498032e-02 1.440488267689943314e-03 -4.230750501155853271e-01 1.910206118756374105e-03 -1.069951709359884262e-02 1.395419007167220116e-03 -4.180698990821838379e-01 1.871588708969029518e-03 -1.049762964248657227e-02 1.351812505163252354e-03 -4.131143093109130859e-01 1.833661157484072326e-03 -1.029979344457387924e-02 1.309619750827550888e-03 -4.082079529762268066e-01 1.796440371627679427e-03 -1.010592095553874969e-02 1.268793246708810329e-03 -4.033503532409667969e-01 1.759924705383275612e-03 -9.915933012962341309e-03 1.229287940077483654e-03 -3.985411226749420166e-01 1.724055372964755596e-03 -9.729747660458087921e-03 1.191060058772563934e-03 -3.937799334526062012e-01 1.688805942792860528e-03 -9.547286666929721832e-03 1.154066878370940685e-03 -3.890663385391235352e-01 1.654217684394830015e-03 -9.368475526571273804e-03 1.118268002755939960e-03 -3.843997716903686523e-01 1.620231830373676908e-03 -9.193234145641326904e-03 1.083622919395565987e-03 -3.797800242900848389e-01 1.586873097588673833e-03 -9.021490812301635742e-03 1.050094026140868664e-03 -3.752066791057586670e-01 1.554134133672500570e-03 -8.853174746036529541e-03 1.017644419334828854e-03 -3.706792891025543213e-01 1.521989997213422149e-03 -8.688212372362613678e-03 9.862381266430020332e-04 -3.661974966526031494e-01 1.490461414923649258e-03 -8.526538498699665070e-03 9.558408055454492569e-04 -3.617607951164245605e-01 1.459449767754728532e-03 -8.368079550564289093e-03 9.264187538065016270e-04 -3.573690056800842285e-01 1.429012699391340969e-03 -8.212774991989135742e-03 8.979404228739440441e-04 -3.530215322971343994e-01 1.399152004113709885e-03 -8.060555905103683472e-03 8.703742059879004955e-04 -3.487181067466735840e-01 1.369818157141651982e-03 -7.911361753940582275e-03 8.436903008259832859e-04 -3.444583415985107422e-01 1.341002475825115196e-03 -7.765128277242183685e-03 8.178594871424138546e-04 -3.402418196201324463e-01 1.312704698153458046e-03 -7.621795870363712311e-03 7.928538252599537373e-04 -3.360682427883148193e-01 1.284932081461519736e-03 -7.481303531676530838e-03 7.686461904086172581e-04 -3.319371938705444336e-01 1.257708139553503045e-03 -7.343596313148736954e-03 7.452104473486542702e-04 -3.278482556343078613e-01 1.230985147337607805e-03 -7.208613213151693344e-03 7.225210429169237614e-04 -3.238011002540588379e-01 1.204750552347711497e-03 -7.076301146298646927e-03 7.005537627264857292e-04 -3.197953701019287109e-01 1.178989571961009449e-03 -6.946604233235120773e-03 6.792847416363656521e-04 -3.158307075500488281e-01 1.153691907111484326e-03 -6.819469388574361801e-03 6.586912204511463642e-04 -3.119067549705505371e-01 1.128875833546346850e-03 -6.694845389574766159e-03 6.387513712979853153e-04 -3.080230653285980225e-01 1.104517199013304923e-03 -6.572678219527006149e-03 6.194434245117008686e-04 -3.041794598102569580e-01 1.080599920285228461e-03 -6.452920846641063690e-03 6.007468327879905701e-04 -3.003753721714019775e-01 1.057094160332787370e-03 -6.335521582514047623e-03 5.826417473144829273e-04 -2.966106235980987549e-01 1.034038106400140579e-03 -6.220433861017227173e-03 5.651088431477546692e-04 -2.928847670555114746e-01 1.011439583019826472e-03 -6.107610184699296951e-03 5.481294938363134861e-04 -2.891975343227386475e-01 9.892395393871365652e-04 -5.997005384415388107e-03 5.316857132129371166e-04 -2.855485379695892334e-01 9.674366098181498737e-04 -5.888572428375482559e-03 5.157600389793515205e-04 -2.819374501705169678e-01 9.460302288950747087e-04 -5.782269407063722610e-03 5.003356491215527058e-04 -2.783638834953308105e-01 9.250196446214486827e-04 -5.678051151335239410e-03 4.853962745983153582e-04 -2.748275995254516602e-01 9.043954937958295333e-04 -5.575876682996749878e-03 4.709262284450232983e-04 -2.713282108306884766e-01 8.841678397662819508e-04 -5.475703626871109009e-03 4.569101438391953707e-04 -2.678653895854949951e-01 8.643277044767471405e-04 -5.377492401748895645e-03 4.433335852809250355e-04 -2.644387781620025635e-01 8.448558086077091528e-04 -5.281202495098114014e-03 4.301820881664752960e-04 -2.610480785369873047e-01 8.257276186450014958e-04 -5.186795257031917572e-03 4.174419445917010307e-04 -2.576930224895477295e-01 8.069410885907020870e-04 -5.094233434647321701e-03 4.051000869367271662e-04 -2.543732225894927979e-01 7.885034504824877614e-04 -5.003478843718767166e-03 3.931433602701872587e-04 -2.510883510112762451e-01 7.704145432179249336e-04 -4.914494697004556656e-03 3.815595991909503937e-04 -2.478381842374801636e-01 7.526604366432750786e-04 -4.827246069908142090e-03 3.703365800902247429e-04 -2.446223348379135132e-01 7.352219628433730517e-04 -4.741697106510400772e-03 3.594628069549798965e-04 -2.414404898881912231e-01 7.180950180851005575e-04 -4.657814744859933853e-03 3.489270166028290987e-04 -2.382924109697341919e-01 7.012796204896215623e-04 -4.575564526021480560e-03 3.387183242011815310e-04 -2.351776659488677979e-01 6.847753276983956430e-04 -4.494913853704929352e-03 3.288263396825641394e-04 -2.320961058139801025e-01 6.685767101385736407e-04 -4.415831528604030609e-03 3.192407602909952402e-04 -2.290472984313964844e-01 6.526807964704674789e-04 -4.338284023106098175e-03 3.099518071394413710e-04 -2.260310500860214233e-01 6.370788345620336445e-04 -4.262241534888744354e-03 3.009499632753431797e-04 -2.230470478534698486e-01 6.217579052427622778e-04 -4.187673795968294144e-03 2.922260900959372520e-04 -2.200949490070343018e-01 6.067054265421926518e-04 -4.114551935344934464e-03 2.837712818291038275e-04 -2.171744853258132935e-01 5.919184176374028307e-04 -4.042845685034990311e-03 2.755769528448581696e-04 -2.142854034900665283e-01 5.773955800511261331e-04 -3.972528036683797836e-03 2.676347503438591957e-04 -2.114273905754089355e-01 5.631334470306947459e-04 -3.903570352122187614e-03 2.599366707727313042e-04 -2.086001485586166382e-01 5.491285438773987025e-04 -3.835946321487426758e-03 2.524750307202339172e-04 -2.058034241199493408e-01 5.353795032711571684e-04 -3.769627306610345840e-03 2.452420594636350870e-04 -2.030369490385055542e-01 5.218729260418854743e-04 -3.704589325934648514e-03 2.382306265644729137e-04 -2.003004252910614014e-01 5.085891625483255404e-04 -3.640806069597601891e-03 2.314337907591834664e-04 -1.975935697555541992e-01 4.955169158844020556e-04 -3.578251926228404045e-03 2.248445816803723574e-04 -1.949161738157272339e-01 4.826513946301850405e-04 -3.516903379932045937e-03 2.184564800700172782e-04 -1.922678947448730469e-01 4.699861290170068043e-04 -3.456735517829656601e-03 2.122630685335025191e-04 -1.896485239267349243e-01 4.575215013801592996e-04 -3.397725289687514305e-03 2.062582352664321661e-04 -1.870577782392501831e-01 4.452655575464078866e-04 -3.339849645271897316e-03 2.004359848797321320e-04 -1.844954341650009155e-01 4.332181376867249483e-04 -3.283086465671658516e-03 1.947906275745481253e-04 -1.819611340761184692e-01 4.213643944662569946e-04 -3.227412700653076172e-03 1.893164298962801695e-04 -1.794546991586685181e-01 4.096883395634948470e-04 -3.172806696966290474e-03 1.840080803958699107e-04 -1.769759356975555420e-01 3.981777951074119779e-04 -3.119248431175947189e-03 1.788603694876655936e-04 -1.745244562625885010e-01 3.868214075353400305e-04 -3.066716017201542854e-03 1.738681894494220614e-04 -1.721000969409942627e-01 3.756189938886759843e-04 -3.015189897269010544e-03 1.690266799414530396e-04 -1.697025746107101440e-01 3.645674179774591704e-04 -2.964649349451065063e-03 1.643310824874788523e-04 -1.673316955566406250e-01 3.536379485230422984e-04 -2.915075747296214104e-03 1.597768568899482489e-04 -1.649871468544006348e-01 3.428143930642417870e-04 -2.866449300199747086e-03 1.553595502628013492e-04 -1.626687347888946533e-01 3.321145229563386052e-04 -2.818751381710171700e-03 1.510748406872153282e-04 -1.603762358427047729e-01 3.215299602465088581e-04 -2.771963831037282944e-03 1.469186099711805582e-04 -1.581093966960906982e-01 3.110228113625697322e-04 -2.726068720221519470e-03 1.428868272341787815e-04 -1.558679640293121338e-01 3.005641086759121999e-04 -2.681048586964607239e-03 1.389756216667592525e-04 -1.536517292261123657e-01 2.901593240276960708e-04 -2.636884804815053940e-03 1.351811370113864541e-04 -1.514604836702346802e-01 2.798175067280202970e-04 -2.593562472611665726e-03 1.314998662564903498e-04 -1.492939442396163940e-01 2.695193068518350848e-04 -2.551063895225524902e-03 1.279282296309247613e-04 -1.471519470214843750e-01 2.592452550406087108e-04 -2.509373473003506660e-03 1.244628365384414792e-04 -1.450342237949371338e-01 2.489698182455574453e-04 -2.468474442139267921e-03 1.211003036587499082e-04 -1.429405808448791504e-01 2.386650229364721586e-04 -2.428351901471614838e-03 1.178375096060335636e-04 -1.408708095550537109e-01 2.283028982002951708e-04 -2.388990484178066254e-03 1.146713257185183465e-04 -1.388246715068817139e-01 2.178570031434298368e-04 -2.350375289097428322e-03 1.115987906814552844e-04 -1.368019580841064453e-01 2.073025985383840049e-04 -2.312491647899150848e-03 1.086169941117987037e-04 -1.348024457693099976e-01 1.966037089493451401e-04 -2.275324892252683640e-03 1.057230911101214588e-04 -1.328259557485580444e-01 1.857279496639460388e-04 -2.238861285150051117e-03 1.029143968480639160e-04 -1.308722496032714844e-01 1.746309161373481398e-04 -2.203087555244565010e-03 1.001883065328001976e-04 -1.289411485195159912e-01 1.632670303924783634e-04 -2.167989034205675125e-03 9.754221537150442600e-05 -1.270324140787124634e-01 1.515961107366246938e-04 -2.133553382009267807e-03 9.497370047029107809e-05 -1.251458823680877686e-01 1.395781407012574595e-04 -2.099767560139298439e-03 9.248035348718985915e-05 -1.232813224196434021e-01 1.270995360625891857e-04 -2.066618530079722404e-03 9.005988249555230141e-05 -1.214385703206062317e-01 1.141313115277284139e-04 -2.034094184637069702e-03 8.771003194851800799e-05 -1.196173727512359619e-01 1.005667518635686771e-04 -2.002182183787226677e-03 8.542864088667556643e-05 -1.178175881505012512e-01 8.631312882876906220e-05 -1.970870885998010635e-03 8.321363566210493445e-05 -1.160390079021453857e-01 7.123526749421901360e-05 -1.940148184075951576e-03 8.106292079901322722e-05 -1.142814531922340393e-01 5.524471272396668599e-05 -1.910003134980797768e-03 7.897462637629359961e-05 -1.125446930527687073e-01 3.816948319780611462e-05 -1.880423631519079208e-03 7.694675150560215116e-05 -1.108285933732986450e-01 1.981819949089328931e-05 -1.851399429142475128e-03 7.497750630136579275e-05 -1.091329231858253479e-01 0.000000000000000000e+00 diff --git a/tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt b/tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt deleted file mode 100644 index 6961d76b..00000000 --- a/tests/bondi_viscous/bondi_viscous_256_default/bondi_analytic_256.txt +++ /dev/null @@ -1,256 +0,0 @@ -1.716369204223155975e-02 3.067483427003026009e-03 -5.556835532188415527e-01 3.049203811044207384e-03 -1.700004935264587402e-02 3.018894931301474571e-03 -5.525988340377807617e-01 3.020226009540082687e-03 -1.683804951608181000e-02 2.971101086586713791e-03 -5.495284795761108398e-01 2.991511476279015008e-03 -1.667767763137817383e-02 2.924087690189480782e-03 -5.464724898338317871e-01 2.963051834283930534e-03 -1.651891879737377167e-02 2.877843100577592850e-03 -5.434306263923645020e-01 2.934845993569251341e-03 -1.636174879968166351e-02 2.832352416589856148e-03 -5.404032468795776367e-01 2.906891689220341378e-03 -1.620615832507610321e-02 2.787604695186018944e-03 -5.373899340629577637e-01 2.879178593732568351e-03 -1.605212502181529999e-02 2.743586199358105659e-03 -5.343906879425048828e-01 2.851706132566705736e-03 -1.589963771402835846e-02 2.700285986065864563e-03 -5.314055085182189941e-01 2.824472806500916144e-03 -1.574867591261863708e-02 2.657691249623894691e-03 -5.284343957901000977e-01 2.797477775134330023e-03 -1.559922657907009125e-02 2.615790115669369698e-03 -5.254773497581481934e-01 2.770721421767756432e-03 -1.545127574354410172e-02 2.574571641162037849e-03 -5.225340723991394043e-01 2.744196954001513599e-03 -1.530480384826660156e-02 2.534023951739072800e-03 -5.196046829223632812e-01 2.717898689670209238e-03 -1.515979599207639694e-02 2.494135405868291855e-03 -5.166891217231750488e-01 2.691826628521836167e-03 -1.501624006778001785e-02 2.454895991832017899e-03 -5.137872695922851562e-01 2.665981145554064888e-03 -1.487411931157112122e-02 2.416294533759355545e-03 -5.108991861343383789e-01 2.640359626369303755e-03 -1.473341975361108780e-02 2.378320321440696716e-03 -5.080246925354003906e-01 2.614961421988014258e-03 -1.459412463009357452e-02 2.340962411835789680e-03 -5.051638484001159668e-01 2.589786532410197264e-03 -1.445621810853481293e-02 2.304211026057600975e-03 -5.023164749145507812e-01 2.564834852098720353e-03 -1.431969180703163147e-02 2.268056152388453484e-03 -4.994826614856719971e-01 2.540097515067705775e-03 -1.418452616780996323e-02 2.232488011941313744e-03 -4.966621398925781250e-01 2.515572079118275073e-03 -1.405070815235376358e-02 2.197495894506573677e-03 -4.938551485538482666e-01 2.491258925292234860e-03 -1.391822472214698792e-02 2.163071185350418091e-03 -4.910614490509033203e-01 2.467157288170522371e-03 -1.378706470131874084e-02 2.129204804077744484e-03 -4.882809519767761230e-01 2.443266254180473471e-03 -1.365720760077238083e-02 2.095885574817657471e-03 -4.855137765407562256e-01 2.419591966115367844e-03 -1.352864690124988556e-02 2.063106512650847435e-03 -4.827596843242645264e-01 2.396132757416540113e-03 -1.340136490762233734e-02 2.030857140198349953e-03 -4.800187945365905762e-01 2.372885027313342485e-03 -1.327534951269626617e-02 1.999129774048924446e-03 -4.772909283638000488e-01 2.349846284483063461e-03 -1.315058674663305283e-02 1.967914635315537453e-03 -4.745761156082153320e-01 2.327013727258641501e-03 -1.302706636488437653e-02 1.937204273417592049e-03 -4.718742370605468750e-01 2.304381027282849468e-03 -1.290477439761161804e-02 1.906990073621273041e-03 -4.691852331161499023e-01 2.281946551531343573e-03 -1.278369780629873276e-02 1.877263421192765236e-03 -4.665091931819915771e-01 2.259709375005557793e-03 -1.266382355242967606e-02 1.848016283474862576e-03 -4.638459086418151855e-01 2.237668918101621152e-03 -1.254514046013355255e-02 1.819240977056324482e-03 -4.611953496932983398e-01 2.215823963044098472e-03 -1.242763455957174301e-02 1.790929585695266724e-03 -4.585576057434082031e-01 2.194177055875227034e-03 -1.231129560619592667e-02 1.763074425980448723e-03 -4.559324085712432861e-01 2.172726156298820932e-03 -1.219611149281263351e-02 1.735668280161917210e-03 -4.533198475837707520e-01 2.151468464358420336e-03 -1.208206918090581894e-02 1.708702882751822472e-03 -4.507198333740234375e-01 2.130400177798243751e-03 -1.196915749460458755e-02 1.682171714492142200e-03 -4.481323659420013428e-01 2.109518053658852432e-03 -1.185736339539289474e-02 1.656067208386957645e-03 -4.455573558807373047e-01 2.088816329759587108e-03 -1.174667850136756897e-02 1.630382379516959190e-03 -4.429947435855865479e-01 2.068292396165747997e-03 -1.163708884268999100e-02 1.605110592208802700e-03 -4.404445290565490723e-01 2.047945549788011792e-03 -1.152858510613441467e-02 1.580244977958500385e-03 -4.379065036773681641e-01 2.027774760267228062e-03 -1.142115518450737000e-02 1.555778435431420803e-03 -4.353808760643005371e-01 2.007779310617372869e-03 -1.131478603929281235e-02 1.531704328954219818e-03 -4.328674077987670898e-01 1.987957540992423434e-03 -1.120947208255529404e-02 1.508017187006771564e-03 -4.303660392761230469e-01 1.968308505367164125e-03 -1.110519655048847198e-02 1.484709326177835464e-03 -4.278768301010131836e-01 1.948831054143558604e-03 -1.100195012986660004e-02 1.461774809285998344e-03 -4.253996908664703369e-01 1.929524618834849853e-03 -1.089972723275423050e-02 1.439208630472421646e-03 -4.229345023632049561e-01 1.910387720606013598e-03 -1.079851109534502029e-02 1.417003222741186619e-03 -4.204812943935394287e-01 1.891419803632207524e-03 -1.069829706102609634e-02 1.395153929479420185e-03 -4.180400073528289795e-01 1.872620546658754041e-03 -1.059906929731369019e-02 1.373653532937169075e-03 -4.156106412410736084e-01 1.853988453947166088e-03 -1.050082128494977951e-02 1.352497376501560211e-03 -4.131931066513061523e-01 1.835522756295321746e-03 -1.040354277938604355e-02 1.331679755821824074e-03 -4.107872545719146729e-01 1.817222387261382532e-03 -1.030722074210643768e-02 1.311194035224616528e-03 -4.083931446075439453e-01 1.799086790844584569e-03 -1.021185051649808884e-02 1.291036256588995457e-03 -4.060106873512268066e-01 1.781115911258556676e-03 -1.011741999536752701e-02 1.271200366318225861e-03 -4.036398530006408691e-01 1.763308319400915111e-03 -1.002391800284385681e-02 1.251680776476860046e-03 -4.012806415557861328e-01 1.745662050104283824e-03 -9.931336157023906708e-03 1.232472364790737629e-03 -3.989329934120178223e-01 1.728174306184958537e-03 -9.839666076004505157e-03 1.213570241816341877e-03 -3.965966999530792236e-01 1.710842846316475195e-03 -9.748898446559906006e-03 1.194969867356121540e-03 -3.942719101905822754e-01 1.693664638210693818e-03 -9.659022092819213867e-03 1.176665420643985271e-03 -3.919585049152374268e-01 1.676638252107266530e-03 -9.570030495524406433e-03 1.158652361482381821e-03 -3.896564245223999023e-01 1.659761580260549728e-03 -9.481912478804588318e-03 1.140926149673759937e-03 -3.873656392097473145e-01 1.643033320121943617e-03 -9.394660592079162598e-03 1.123482012189924717e-03 -3.850860595703125000e-01 1.626450455508494626e-03 -9.308265522122383118e-03 1.106315408833324909e-03 -3.828177452087402344e-01 1.610009999202180768e-03 -9.222717024385929108e-03 1.089421333745121956e-03 -3.805605769157409668e-01 1.593711806123459694e-03 -9.138009510934352875e-03 1.072795828804373741e-03 -3.783144652843475342e-01 1.577556590123219528e-03 -9.054132737219333649e-03 1.056434120982885361e-03 -3.760794401168823242e-01 1.561545057842212043e-03 -8.971079252660274506e-03 1.040332601405680180e-03 -3.738554120063781738e-01 1.545680374815922237e-03 -8.888838812708854675e-03 1.024486147798597813e-03 -3.716423511505126953e-01 1.529964152353707751e-03 -8.807404898107051849e-03 1.008890918456017971e-03 -3.694402277469635010e-01 1.514393849019976881e-03 -8.726768195629119873e-03 9.935431880876421928e-04 -3.672490417957305908e-01 1.498967165093053298e-03 -8.646920323371887207e-03 9.784383000805974007e-04 -3.650685548782348633e-01 1.483680988364736650e-03 -8.567855693399906158e-03 9.635728201828896999e-04 -3.628988564014434814e-01 1.468531124423920952e-03 -8.489564992487430573e-03 9.489428484812378883e-04 -3.607399463653564453e-01 1.453515324863183505e-03 -8.412038907408714294e-03 9.345440194010734558e-04 -3.585917353630065918e-01 1.438632721688875129e-03 -8.335271850228309631e-03 9.203731315210461617e-04 -3.564541637897491455e-01 1.423881745168323202e-03 -8.259254507720470428e-03 9.064262267202138901e-04 -3.543271720409393311e-01 1.409261041534629879e-03 -8.183981291949748993e-03 8.926997543312609196e-04 -3.522107601165771484e-01 1.394766584808358398e-03 -8.109441958367824554e-03 8.791898144409060478e-04 -3.501048386096954346e-01 1.380398176507262060e-03 -8.035630919039249420e-03 8.658931474201381207e-04 -3.480094075202941895e-01 1.366149015207681564e-03 -7.962542586028575897e-03 8.528066100552678108e-04 -3.459243476390838623e-01 1.352030190515267783e-03 -7.890164852142333984e-03 8.399261278100311756e-04 -3.438497781753540039e-01 1.338040051945830209e-03 -7.818494923412799835e-03 8.272488485090434551e-04 -3.417854011058807373e-01 1.324178164677941496e-03 -7.747523020952939987e-03 8.147713961079716682e-04 -3.397313654422760010e-01 1.310445994716974080e-03 -7.677243091166019440e-03 8.024902781471610069e-04 -3.376876115798950195e-01 1.296844702623601133e-03 -7.607648149132728577e-03 7.904025260359048843e-04 -3.356540501117706299e-01 1.283373860206222421e-03 -7.538730744272470474e-03 7.785049383528530598e-04 -3.336306214332580566e-01 1.270026869655093002e-03 -7.470485288649797440e-03 7.667945465072989464e-04 -3.316173851490020752e-01 1.256793546582370266e-03 -7.402903400361537933e-03 7.552680326625704765e-04 -3.296141326427459717e-01 1.243673998322951261e-03 -7.335981354117393494e-03 7.439230103045701981e-04 -3.276209831237792969e-01 1.230667811851342839e-03 -7.269707974046468735e-03 7.327557541429996490e-04 -3.256377875804901123e-01 1.217775194574115217e-03 -7.204080466181039810e-03 7.217639940790832043e-04 -3.236645162105560303e-01 1.204996402013013652e-03 -7.139089517295360565e-03 7.109445286914706230e-04 -3.217011690139770508e-01 1.192333423628720709e-03 -7.074732333421707153e-03 7.002949714660644531e-04 -3.197476863861083984e-01 1.179786118810683022e-03 -7.010999135673046112e-03 6.898121791891753674e-04 -3.178039789199829102e-01 1.167353694549179552e-03 -6.947885267436504364e-03 6.794935907237231731e-04 -3.158700764179229736e-01 1.155036166428871245e-03 -6.885385606437921524e-03 6.693368777632713318e-04 -3.139458596706390381e-01 1.142835260183226774e-03 -6.823491305112838745e-03 6.593388970941305161e-04 -3.120313882827758789e-01 1.130751647216891127e-03 -6.762197706848382950e-03 6.494973786175251007e-04 -3.101266026496887207e-01 1.118766102407034013e-03 -6.701497826725244522e-03 6.398097029887139797e-04 -3.082313239574432373e-01 1.106874679518922346e-03 -6.641387473791837692e-03 6.302733672782778740e-04 -3.063457012176513672e-01 1.095102677810591649e-03 -6.581859663128852844e-03 6.208861595951020718e-04 -3.044695258140563965e-01 1.083457693127233903e-03 -6.522908341139554977e-03 6.116455188021063805e-04 -3.026028871536254883e-01 1.071909760724144714e-03 -6.464528385549783707e-03 6.025490001775324345e-04 -3.007456958293914795e-01 1.060459203069826937e-03 -6.406713742762804031e-03 5.935943918302655220e-04 -2.988978624343872070e-01 1.049106255682725379e-03 -6.349458824843168259e-03 5.847794818691909313e-04 -2.970594167709350586e-01 1.037851560346049083e-03 -6.292757578194141388e-03 5.761018837802112103e-04 -2.952302694320678711e-01 1.026695617426719121e-03 -6.236605346202850342e-03 5.675595020875334740e-04 -2.934104204177856445e-01 1.015638266020308343e-03 -6.180995143949985504e-03 5.591499502770602703e-04 -2.915998399257659912e-01 1.004680043498215646e-03 -6.125923711806535721e-03 5.508714239113032818e-04 -2.897984087467193604e-01 9.938222184690970878e-04 -6.071383599191904068e-03 5.427215946838259697e-04 -2.880061566829681396e-01 9.830641840937958363e-04 -6.017371546477079391e-03 5.346985417418181896e-04 -2.862230241298675537e-01 9.724045250379492701e-04 -5.963879637420177460e-03 5.267999949865043163e-04 -2.844489514827728271e-01 9.618423363107919026e-04 -5.910905078053474426e-03 5.190241499803960323e-04 -2.826839387416839600e-01 9.513767083394826431e-04 -5.858441814780235291e-03 5.113690276630222797e-04 -2.809278964996337891e-01 9.410062396597822362e-04 -5.806484259665012360e-03 5.038327071815729141e-04 -2.791808545589447021e-01 9.307299890634868587e-04 -5.755027756094932556e-03 4.964131512679159641e-04 -2.774427235126495361e-01 9.205457889794694745e-04 -5.704067647457122803e-03 4.891086718998849392e-04 -2.757135033607482910e-01 9.104527456725462417e-04 -5.653598811477422714e-03 4.819173482246696949e-04 -2.739930748939514160e-01 9.004501095154510831e-04 -5.603616125881671906e-03 4.748373758047819138e-04 -2.722814679145812988e-01 8.905374972466838799e-04 -5.554114468395709991e-03 4.678669210989028215e-04 -2.705786228179931641e-01 8.807142550528743261e-04 -5.505089648067951202e-03 4.610042378772050142e-04 -2.688845098018646240e-01 8.709866752252799034e-04 -5.456537473946809769e-03 4.542477836366742849e-04 -2.671990990638732910e-01 8.613466821769578561e-04 -5.408450961112976074e-03 4.475955502130091190e-04 -2.655223309993743896e-01 8.517974887135010406e-04 -5.360827781260013580e-03 4.410461697261780500e-04 -2.638542056083679199e-01 8.423379009388863971e-04 -5.313663277775049210e-03 4.345979250501841307e-04 -2.621946036815643311e-01 8.329671765935310265e-04 -5.266950465738773346e-03 4.282489826437085867e-04 -2.605435848236083984e-01 8.236844223944004726e-04 -5.220687948167324066e-03 4.219980619382113218e-04 -2.589010298252105713e-01 8.144890184164349902e-04 -5.174869671463966370e-03 4.158435040153563023e-04 -2.572669684886932373e-01 8.053814647854373298e-04 -5.129490513354539871e-03 4.097837081644684076e-04 -2.556413114070892334e-01 7.963603765456862046e-04 -5.084547679871320724e-03 4.038171900901943445e-04 -2.540240585803985596e-01 7.874243208051845020e-04 -5.040036048740148544e-03 3.979424654971808195e-04 -2.524151802062988281e-01 7.785710220533770655e-04 -4.995950963348150253e-03 3.921581374015659094e-04 -2.508145868778228760e-01 7.697990500394148250e-04 -4.952289164066314697e-03 3.864626633003354073e-04 -2.492222487926483154e-01 7.611062041465502508e-04 -4.909045994281768799e-03 3.808547917287796736e-04 -2.476381957530975342e-01 7.524894314773492498e-04 -4.866217263042926788e-03 3.753329510800540447e-04 -2.460623532533645630e-01 7.439477227848116138e-04 -4.823798313736915588e-03 3.698958607856184244e-04 -2.444946318864822388e-01 7.354802405103347913e-04 -4.781785886734724045e-03 3.645421820692718029e-04 -2.429351061582565308e-01 7.270864102050059738e-04 -4.740175791084766388e-03 3.592705761548131704e-04 -2.413836866617202759e-01 7.187648658200237409e-04 -4.698963835835456848e-03 3.540797333698719740e-04 -2.398402690887451172e-01 7.105162996604768915e-04 -4.658146295696496964e-03 3.489684313535690308e-04 -2.383048981428146362e-01 7.023417542940500616e-04 -4.617718979716300964e-03 3.439353022258728743e-04 -2.367774844169616699e-01 6.942460084408869124e-04 -4.577678628265857697e-03 3.389791818335652351e-04 -2.352580875158309937e-01 6.862193925077211603e-04 -4.538020119071006775e-03 3.340988478157669306e-04 -2.337465882301330566e-01 6.782626330966285127e-04 -4.498741123825311661e-03 3.292930778115987778e-04 -2.322429716587066650e-01 6.703731151626043201e-04 -4.459837451577186584e-03 3.245607367716729641e-04 -2.307472229003906250e-01 6.625544620541861307e-04 -4.421305377036333084e-03 3.199006023351103067e-04 -2.292592525482177734e-01 6.548140987906500132e-04 -4.383140243589878082e-03 3.153115394525229931e-04 -2.277791053056716919e-01 6.471380672606776149e-04 -4.345340188592672348e-03 3.107925294898450375e-04 -2.263066619634628296e-01 6.395275755562245109e-04 -4.307900555431842804e-03 3.063423500861972570e-04 -2.248419523239135742e-01 6.319944922475439217e-04 -4.270818084478378296e-03 3.019599535036832094e-04 -2.233849167823791504e-01 6.245220323809081439e-04 -4.234088584780693054e-03 2.976442046929150820e-04 -2.219355702400207520e-01 6.171247693731173349e-04 -4.197709728032350540e-03 2.933942305389791727e-04 -2.204937487840652466e-01 6.098014397079750933e-04 -4.161676857620477676e-03 2.892088086809962988e-04 -2.190595716238021851e-01 6.025366092545816595e-04 -4.125987179577350616e-03 2.850869786925613880e-04 -2.176329195499420166e-01 5.953300076761356948e-04 -4.090637899935245514e-03 2.810277801472693682e-04 -2.162137776613235474e-01 5.881819441415299431e-04 -4.055623896420001984e-03 2.770301653072237968e-04 -2.148021310567855835e-01 5.810927401363955979e-04 -4.020944237709045410e-03 2.730932610575109720e-04 -2.133978605270385742e-01 5.740627171463639610e-04 -3.986593801528215408e-03 2.692160196602344513e-04 -2.120010554790496826e-01 5.670922067790906467e-04 -3.952570259571075439e-03 2.653975388966500759e-04 -2.106116265058517456e-01 5.601838904420140819e-04 -3.918869886547327042e-03 2.616368874441832304e-04 -2.092295140027999878e-01 5.533382330263037722e-04 -3.885489422827959061e-03 2.579331339802592993e-04 -2.078547477722167969e-01 5.465542640870832414e-04 -3.852426540106534958e-03 2.542854344937950373e-04 -2.064872235059738159e-01 5.398307946884053996e-04 -3.819677047431468964e-03 2.506928285583853722e-04 -2.051269561052322388e-01 5.331666358943238076e-04 -3.787239082157611847e-03 2.471546176820993423e-04 -2.037739306688308716e-01 5.265605174594351371e-04 -3.755108453333377838e-03 2.436697832308709621e-04 -2.024280577898025513e-01 5.200102419282768944e-04 -3.723283065482974052e-03 2.402375685051083565e-04 -2.010893523693084717e-01 5.135149841417092778e-04 -3.691758494824171066e-03 2.368570858379825950e-04 -1.997577548027038574e-01 5.070737409437619950e-04 -3.660533810034394264e-03 2.335276221856474876e-04 -1.984332650899887085e-01 5.006861234772110644e-04 -3.629604354500770569e-03 2.302482753293588758e-04 -1.971158385276794434e-01 4.943515375402611879e-04 -3.598967567086219788e-03 2.270182740176096559e-04 -1.958054006099700928e-01 4.880697455889978142e-04 -3.568622050806879997e-03 2.238369488622993231e-04 -1.945019662380218506e-01 4.818415107802307708e-04 -3.538563381880521774e-03 2.207034849561750889e-04 -1.932054907083511353e-01 4.756656405661530249e-04 -3.508788766339421272e-03 2.176170237362384796e-04 -1.919159442186355591e-01 4.695408860803005030e-04 -3.479296108707785606e-03 2.145770267816260457e-04 -1.906332969665527344e-01 4.634654208185696967e-04 -3.450082847848534584e-03 2.115826500812545419e-04 -1.893575340509414673e-01 4.574377992204147207e-04 -3.421144559979438782e-03 2.086331223836168647e-04 -1.880886107683181763e-01 4.514554198958551398e-04 -3.392480313777923584e-03 2.057278761640191078e-04 -1.868265122175216675e-01 4.455173169720519962e-04 -3.364087548106908798e-03 2.028661838266998529e-04 -1.855711638927459717e-01 4.396228530571216590e-04 -3.335962537676095963e-03 2.000473323278129101e-04 -1.843225508928298950e-01 4.337713907591806057e-04 -3.308102721348404884e-03 1.972706813830882311e-04 -1.830806583166122437e-01 4.279624486041275254e-04 -3.280506236478686333e-03 1.945355761563405395e-04 -1.818454861640930176e-01 4.221947602560124175e-04 -3.253170521929860115e-03 1.918413472594693303e-04 -1.806169450283050537e-01 4.164679452094941252e-04 -3.226092318072915077e-03 1.891873835120350122e-04 -1.793950349092483521e-01 4.107815452911755313e-04 -3.199269063770771027e-03 1.865730009740218520e-04 -1.781797409057617188e-01 4.051351023276596273e-04 -3.172699129208922386e-03 1.839976757764816284e-04 -1.769709885120391846e-01 3.995282361776110653e-04 -3.146379021927714348e-03 1.814606948755681515e-04 -1.757688075304031372e-01 3.939620477415104317e-04 -3.120307112112641335e-03 1.789615635061636567e-04 -1.745731234550476074e-01 3.884358706928163658e-04 -3.094481071457266808e-03 1.764996704878285527e-04 -1.733839064836502075e-01 3.829483301959528082e-04 -3.068897407501935959e-03 1.740743755362927914e-04 -1.722011715173721313e-01 3.774975337659158738e-04 -3.043555421754717827e-03 1.716852129902690649e-04 -1.710248142480850220e-01 3.720820830274438403e-04 -3.018451156094670296e-03 1.693315280135720968e-04 -1.698549091815948486e-01 3.666989791877355519e-04 -2.993583446368575096e-03 1.670128112891688943e-04 -1.686913520097732544e-01 3.613466050450033222e-04 -2.968949032947421074e-03 1.647285243961960077e-04 -1.675341129302978516e-01 3.560245038614123152e-04 -2.944546518847346306e-03 1.624781143618747592e-04 -1.663831919431686401e-01 3.507322188991276947e-04 -2.920373110100626945e-03 1.602610864210873842e-04 -1.652385741472244263e-01 3.454694601942537389e-04 -2.896426944062113762e-03 1.580769167048856616e-04 -1.641002148389816284e-01 3.402372239346000997e-04 -2.872705226764082909e-03 1.559250667924061418e-04 -1.629680842161178589e-01 3.350357685022379954e-04 -2.849206561222672462e-03 1.538051001261919737e-04 -1.618421077728271484e-01 3.298631060671725784e-04 -2.825927920639514923e-03 1.517164491815492511e-04 -1.607223302125930786e-01 3.247172487994090008e-04 -2.802868140861392021e-03 1.496587065048515797e-04 -1.596087068319320679e-01 3.195962088689523065e-04 -2.780024195089936256e-03 1.476313045714050531e-04 -1.585011929273605347e-01 3.144986389545947696e-04 -2.757394919171929359e-03 1.456338795833289623e-04 -1.573997586965560913e-01 3.094231595509212168e-04 -2.734976587817072868e-03 1.436658640159294009e-04 -1.563044041395187378e-01 3.043667975233969110e-04 -2.712769201025366783e-03 1.417268940713256598e-04 -1.552150845527648926e-01 2.993270540885746046e-04 -2.690769499167799950e-03 1.398164749843999743e-04 -1.541317850351333618e-01 2.943012703445045545e-04 -2.668975619599223137e-03 1.379341701976954937e-04 -1.530544459819793701e-01 2.892860938907345779e-04 -2.647385932505130768e-03 1.360795722575858235e-04 -1.519830375909805298e-01 2.842780121386620962e-04 -2.625998109579086304e-03 1.342522300546988845e-04 -1.509176045656204224e-01 2.792752394478310277e-04 -2.604810288175940514e-03 1.324517361354082823e-04 -1.498580425977706909e-01 2.742761881126264427e-04 -2.583820139989256859e-03 1.306776539422571659e-04 -1.488044112920761108e-01 2.692788494981652992e-04 -2.563026966527104378e-03 1.289296487811952829e-04 -1.477565765380859375e-01 2.642830869035813672e-04 -2.542427508160471916e-03 1.272072258871048689e-04 -1.467145979404449463e-01 2.592869564356481936e-04 -2.522020600736141205e-03 1.255100505659356713e-04 -1.456783860921859741e-01 2.542875410952232743e-04 -2.501803915947675705e-03 1.238377153640612960e-04 -1.446479707956314087e-01 2.492819238831642139e-04 -2.481776056811213493e-03 1.221898564836010337e-04 -1.436233073472976685e-01 2.442668749403794064e-04 -2.461935626342892647e-03 1.205661246785894036e-04 -1.426043212413787842e-01 2.392384546478730219e-04 -2.442279830574989319e-03 1.189660833915695548e-04 -1.415910869836807251e-01 2.341945958740678760e-04 -2.422806806862354279e-03 1.173893761006183922e-04 -1.405835002660751343e-01 2.291329466902329885e-04 -2.403516089543700218e-03 1.158357263193465769e-04 -1.395815759897232056e-01 2.240516312331043746e-04 -2.384404651820659637e-03 1.143046974902972579e-04 -1.385852396488189697e-01 2.189506539387458823e-04 -2.365471329540014267e-03 1.127959912992082536e-04 -1.375945359468460083e-01 2.138289358672349719e-04 -2.346714027225971222e-03 1.113092221203260124e-04 -1.366094052791595459e-01 2.086822238298576080e-04 -2.328131580725312233e-03 1.098440989153459668e-04 -1.356297880411148071e-01 2.035062605766995401e-04 -2.309722360223531723e-03 1.084002942661754787e-04 -1.346557140350341797e-01 1.982973030628560902e-04 -2.291484270244836807e-03 1.069774516508914530e-04 -1.336870938539505005e-01 1.930542857261014552e-04 -2.273415448144078255e-03 1.055752509273588657e-04 -1.327240318059921265e-01 1.877718818306050805e-04 -2.255514729768037796e-03 1.041934156091883779e-04 -1.317663341760635376e-01 1.824431088285549609e-04 -2.237780252471566200e-03 1.028315964504145086e-04 -1.308141052722930908e-01 1.770601590664857476e-04 -2.220210852101445198e-03 1.014895242406055331e-04 -1.298672854900360107e-01 1.716159733773617475e-04 -2.202804666012525558e-03 1.001668715616688132e-04 -1.289258003234863281e-01 1.661048447839135660e-04 -2.185559598729014397e-03 9.886331827146932483e-05 -1.279897093772888184e-01 1.605216576102571865e-04 -2.168474718928337097e-03 9.757863881532102823e-05 -1.270589232444763184e-01 1.548613459695540105e-04 -2.151548629626631737e-03 9.631252760300412774e-05 -1.261334717273712158e-01 1.491170823572880515e-04 -2.134779468178749084e-03 9.506466449238359928e-05 -1.252132654190063477e-01 1.432817942106481607e-04 -2.118165371939539909e-03 9.383480210090056062e-05 -1.242983415722846985e-01 1.373506795068198756e-04 -2.101705642417073250e-03 9.262267121812328696e-05 -1.233886554837226868e-01 1.313152452121739563e-04 -2.085398184135556221e-03 9.142798808170482516e-05 -1.224841699004173279e-01 1.251654102457772891e-04 -2.069242065772414207e-03 9.025049803312867880e-05 -1.215848773717880249e-01 1.188925280985824145e-04 -2.053235657513141632e-03 8.908996096579357982e-05 -1.206907629966735840e-01 1.124867771767234148e-04 -2.037377096712589264e-03 8.794607128947973251e-05 -1.198017895221710205e-01 1.059364497370102708e-04 -2.021665219217538834e-03 8.681863255333155394e-05 -1.189179345965385437e-01 9.922700588765054623e-05 -2.006098860874772072e-03 8.570733916712924838e-05 -1.180391684174537659e-01 9.234439165620919588e-05 -1.990676159039139748e-03 8.461198012810200453e-05 -1.171654835343360901e-01 8.528151142389726240e-05 -1.975396648049354553e-03 8.353232260560616851e-05 -1.162968501448631287e-01 7.801994594430422341e-05 -1.960257766768336296e-03 8.246811194112524390e-05 -1.154332458972930908e-01 7.054967210153930432e-05 -1.945258933119475842e-03 8.141912985593080521e-05 -1.145746633410453796e-01 6.284519422005574061e-05 -1.930398400872945786e-03 8.038512169150635600e-05 -1.137210652232170105e-01 5.489284034868858705e-05 -1.915675238706171513e-03 7.936589827295392752e-05 -1.128724217414855957e-01 4.669301142784747346e-05 -1.901088049635291100e-03 7.836121949367225170e-05 -1.120287403464317322e-01 3.822608322807355164e-05 -1.886635436676442623e-03 7.737085979897528887e-05 -1.111899539828300476e-01 2.944199581811238962e-05 -1.872316002845764160e-03 7.639460818609222770e-05 -1.103560924530029297e-01 2.032280144551531251e-05 -1.858128467574715614e-03 7.543223910033702850e-05 -1.095270961523056030e-01 1.085142089491031454e-05 -1.844071783125400543e-03 7.448357064276933670e-05 -1.087029650807380676e-01 9.999999999999999547e-07 diff --git a/tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt b/tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt deleted file mode 100644 index 095bf35c..00000000 --- a/tests/bondi_viscous/bondi_viscous_64_default/bondi_analytic_64.txt +++ /dev/null @@ -1,64 +0,0 @@ -2.009973116219043732e-02 3.990998491644859314e-03 -6.084101200103759766e-01 3.558629102739358906e-03 -1.928591914474964142e-02 3.725331742316484451e-03 -5.942628383636474609e-01 3.417428464649169099e-03 -1.850670017302036285e-02 3.477865131571888924e-03 -5.803928971290588379e-01 3.281391178847760642e-03 -1.776055805385112762e-02 3.247322514653205872e-03 -5.667958855628967285e-01 3.150485599348396611e-03 -1.704603619873523712e-02 3.032518550753593445e-03 -5.534673929214477539e-01 3.024416906864235005e-03 -1.636174879968166351e-02 2.832352416589856148e-03 -5.404032468795776367e-01 2.903056149472242956e-03 -1.570637151598930359e-02 2.645803149789571762e-03 -5.275989770889282227e-01 2.786120135274582337e-03 -1.507864054292440414e-02 2.471921965479850769e-03 -5.150505304336547852e-01 2.673418275879855741e-03 -1.447734516113996506e-02 2.309826202690601349e-03 -5.027536153793334961e-01 2.564948929317165376e-03 -1.390133798122406006e-02 2.158698625862598419e-03 -4.907041788101196289e-01 2.460588167217996058e-03 -1.334951352328062057e-02 2.017778344452381134e-03 -4.788980782032012939e-01 2.360069053203179938e-03 -1.282082404941320419e-02 1.886358717456459999e-03 -4.673311710357666016e-01 2.263244180108050195e-03 -1.231426373124122620e-02 1.763783046044409275e-03 -4.559995234012603760e-01 2.170060123178299723e-03 -1.182887796312570572e-02 1.649441663175821304e-03 -4.448990821838378906e-01 2.080273269952252931e-03 -1.136374846100807190e-02 1.542767276987433434e-03 -4.340259432792663574e-01 1.993762851774091415e-03 -1.091799978166818619e-02 1.443232176825404167e-03 -4.233760833740234375e-01 1.910557190014116214e-03 -1.049079839140176773e-02 1.350346719846129417e-03 -4.129458069801330566e-01 1.830522350370494995e-03 -1.008134800940752029e-02 1.263655489310622215e-03 -4.027311205863952637e-01 1.753453532136987555e-03 -9.688883088529109955e-03 1.182734384201467037e-03 -3.927283883094787598e-01 1.679298756798206860e-03 -9.312675334513187408e-03 1.107188872992992401e-03 -3.829338252544403076e-01 1.607920904118859086e-03 -8.952028118073940277e-03 1.036652945913374424e-03 -3.733437657356262207e-01 1.539189869474771313e-03 -8.606277406215667725e-03 9.707855642773211002e-04 -3.639545440673828125e-01 1.473051234712312915e-03 -8.274787105619907379e-03 9.092690888792276382e-04 -3.547626435756683350e-01 1.409389964401903646e-03 -7.956949062645435333e-03 8.518085232935845852e-04 -3.457643985748291016e-01 1.348190137276036871e-03 -7.652181200683116913e-03 7.981288945302367210e-04 -3.369564712047576904e-01 1.289244561101438903e-03 -7.359929848462343216e-03 7.479749619960784912e-04 -3.283352851867675781e-01 1.232524270093200876e-03 -7.079660892486572266e-03 7.011082489043474197e-04 -3.198975920677185059e-01 1.178063252421932775e-03 -6.810868624597787857e-03 6.573073915205895901e-04 -3.116399049758911133e-01 1.125660012524668484e-03 -6.553068757057189941e-03 6.163661601021885872e-04 -3.035589754581451416e-01 1.075158179971555340e-03 -6.305793765932321548e-03 5.780923529528081417e-04 -2.956515550613403320e-01 1.026517007545504163e-03 -6.068601738661527634e-03 5.423071561381220818e-04 -2.879144847393035889e-01 9.797906614376157477e-04 -5.841067060828208923e-03 5.088439211249351501e-04 -2.803445756435394287e-01 9.348581871019535049e-04 -5.622782744467258453e-03 4.775473498739302158e-04 -2.729387581348419189e-01 8.917221202554105131e-04 -5.413361359387636185e-03 4.482730582822114229e-04 -2.656939029693603516e-01 8.502178397566080093e-04 -5.212431773543357849e-03 4.208863538224250078e-04 -2.586071193218231201e-01 8.102325998417623382e-04 -5.019636359065771103e-03 3.952616243623197079e-04 -2.516754269599914551e-01 7.718161158785515844e-04 -4.834636114537715912e-03 3.712819598149508238e-04 -2.448958456516265869e-01 7.348557939475704182e-04 -4.657104611396789551e-03 3.488383081275969744e-04 -2.382656186819076538e-01 6.993182359984295542e-04 -4.486731719225645065e-03 3.278292715549468994e-04 -2.317818999290466309e-01 6.651178522425728540e-04 -4.323217086493968964e-03 3.081597969867289066e-04 -2.254419028759002686e-01 6.322289686902037671e-04 -4.166277591139078140e-03 2.897418162319809198e-04 -2.192430198192596436e-01 6.005251088069833623e-04 -4.015638027340173721e-03 2.724928781390190125e-04 -2.131824940443038940e-01 5.699934050505552465e-04 -3.871038323268294334e-03 2.563362650107592344e-04 -2.072577327489852905e-01 5.406458495437987781e-04 -3.732228418812155724e-03 2.412003523204475641e-04 -2.014662474393844604e-01 5.122905712814128983e-04 -3.598967567086219788e-03 2.270182740176096559e-04 -1.958054006099700928e-01 4.848685563758513219e-04 -3.471027826890349388e-03 2.137278206646442413e-04 -1.902728080749511719e-01 4.583627467159794955e-04 -3.348189173266291618e-03 2.012708428082987666e-04 -1.848660111427307129e-01 4.327539851236528678e-04 -3.230241360142827034e-03 1.895930763566866517e-04 -1.795825809240341187e-01 4.079802091157575406e-04 -3.116982523351907730e-03 1.786438806448131800e-04 -1.744202971458435059e-01 3.839021344878476550e-04 -3.008220810443162918e-03 1.683760638115927577e-04 -1.693767756223678589e-01 3.605152167153070496e-04 -2.903771121054887772e-03 1.587455190019682050e-04 -1.644497960805892944e-01 3.376953324809211566e-04 -2.803456503897905350e-03 1.497110642958432436e-04 -1.596371829509735107e-01 3.152200154006727694e-04 -2.707108389586210251e-03 1.412343262927606702e-04 -1.549367755651473999e-01 2.930779777823933952e-04 -2.614564495161175728e-03 1.332794199697673321e-04 -1.503463685512542725e-01 2.711579931693024474e-04 -2.525669289752840996e-03 1.258128322660923004e-04 -1.458639353513717651e-01 2.493602612763229083e-04 -2.440273761749267578e-03 1.188032765639945865e-04 -1.414874941110610962e-01 2.274038714995271575e-04 -2.358236582949757576e-03 1.122215835493989289e-04 -1.372149586677551270e-01 2.049739342898993020e-04 -2.279419684782624245e-03 1.060403810697607696e-04 -1.330444365739822388e-01 1.819409046091047017e-04 -2.203693147748708725e-03 1.002342178253456950e-04 -1.289739459753036499e-01 1.579125042445230236e-04 -2.130931708961725235e-03 9.477926505496725440e-05 -1.250016689300537109e-01 1.323912513433941364e-04 -2.061014994978904724e-03 8.965324377641081810e-05 -1.211257129907608032e-01 1.047926832720704832e-04 -1.993828220292925835e-03 8.483538113068789244e-05 -1.173442974686622620e-01 7.429226521448005635e-05 -1.929260906763374805e-03 8.030619937926530838e-05 -1.136556193232536316e-01 3.985325272669650325e-05 -1.867207814939320087e-03 7.604754500789567828e-05 -1.100579351186752319e-01 0.000000000000000000e+00 diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index 065220d7..bac34b83 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -1,14 +1,25 @@ +#!/usr/bin/env python3 + +import os, sys + import numpy as np -import os, glob, h5py, sys +from scipy.interpolate import splrep +from scipy.integrate import solve_ivp + import matplotlib matplotlib.use('Agg') import matplotlib as mpl import matplotlib.pyplot as plt import pyharm +import pyharm.grmhd.bondi as bondi +import pyharm.plots.plot_dumps as pplt +# Check that the computed Bondi solution matches +# the analytic Bondi solution in rho,u and the +# ODE results in dP -if __name__=='__main__': +if __name__ == '__main__': outputdir = './' kharmadir = '../../' @@ -22,39 +33,45 @@ fit = np.zeros([len(RES), NVAR]) for r, res in enumerate(RES): - - # load analytic result - fpath = os.path.join(os.curdir,'bondi_viscous_{}_default'.format(res), 'bondi_analytic_{}.txt'.format(res)) - rho_analytic, uu_analytic, dP_analytic = np.loadtxt(fpath, usecols=(0,1,3), unpack=True) + + # Load dump for parameters + dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res), cache_conn=True) + + # Compute analytic reference + mdot, rc, gam = dump['bondi']['mdot'], dump['bondi']['rs'], dump['gam'] + eta, tau = dump['emhd']['eta'], dump['emhd']['tau'] + state = bondi.get_bondi_fluid_state(mdot, rc, gam, dump.grid) + state.params['eta'] = eta + state.params['tau'] = tau + dP_check = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau) # load code data dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) - - params = dump.params - rho = np.squeeze(dump['RHO']) - uu = np.squeeze(dump['UU']) - dP_tilde = np.squeeze(dump['prims'][8,Ellipsis]) - - t = dump['t'] - gam = params['gam'] - tau = params['tau'] - eta = params['eta'] - higher_order_terms = params['higher_order_terms'] - - # compute dP - if higher_order_terms=="true": + + rho, uu, dP_tilde = dump['RHO'], dump['UU'], dump['dP'] + #rho, uu = dump['RHO'], dump['UU'] + + # compute dP + if dump['emhd']['higher_order_terms'] == "true": print("Res: "+str(res)+"; higher order terms enabled") - P = (gam - 1.) * uu - Theta = P / rho + Theta = (dump['gam'] - 1.) * uu / rho nu_emhd = eta / rho dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) else: dP = dP_tilde - + + # Plot + fig = plt.figure(figsize=(6,6)) + ax = fig.add_subplot(1,1,1) + pplt.plot_diff_xz(ax, dump, state, 'rho') + plt.legend() + fig.savefig("compare_rho_{}.png".format(res)) + plt.close(fig) + # compute L1 norm - L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) - L1[r,1] = np.mean(np.fabs(uu - uu_analytic[:,None])) - L1[r,2] = np.mean(np.fabs(dP - dP_analytic[:,None])[1:-1]) + L1[r,0] = np.mean(np.fabs(rho[:,0,0] - state['rho'][:,0,0])) + L1[r,1] = np.mean(np.fabs(uu[:,0,0] - state['u'][:,0,0])) + L1[r,2] = np.mean(np.fabs(dP[:,0,0] - dP_check)[1:-1]) # MEASURE CONVERGENCE L1 = np.array(L1) diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index b8320ee6..0da6979b 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -13,7 +13,7 @@ conv_2d() { do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 \ + $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 parthenon/time/tlim=400 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 @@ -22,9 +22,7 @@ conv_2d() { mv bondi.out0.final.phdf emhd_2d_${res}_end_${1}.phdf done check_code=0 - # pyharm-convert --double *.phdf python check.py $ALL_RES $1 2d || check_code=$? - # rm -r *.phdf rm -r *.xdmf rm -r *.out0* if [[ $check_code != 0 ]]; then @@ -35,7 +33,7 @@ conv_2d() { fi } -ALL_RES="64,128,256" +ALL_RES="8,16,32,64" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" exit $exit_code diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index 6f758167..655e0489 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -52,7 +52,20 @@ q = q_tilde * np.sqrt(chi_emhd * rho * Theta**2 / tau) else: q = q_tilde - + + fig = plt.figure(figsize=(8,8)) + plt.plot(np.mean(q, axis=-1)) + plt.plot(q_analytic) + plt.savefig("compare_{}.png".format(res)) + + fig = plt.figure(figsize=(8,8)) + plt.plot(np.mean(q - q_analytic[:,None], axis=-1)) + plt.savefig("diff_{}.png".format(res)) + + # fig, ax = plt.subplots(1,1,figsize=(8,8)) + # pplt.plot_xy(q - q_analytic[:,None], axis=-1)) + # plt.savefig("diff_{}.png".format(res)) + # compute L1 norm # compute L1 norm L1[r,0] = np.mean(np.fabs(rho - rho_analytic[:,None])) diff --git a/tests/conducting_atmosphere/conducting_atmosphere.par b/tests/conducting_atmosphere/conducting_atmosphere.par new file mode 100644 index 00000000..523b5fc5 --- /dev/null +++ b/tests/conducting_atmosphere/conducting_atmosphere.par @@ -0,0 +1,97 @@ +# Hydrostatic conducting atmosphere +# Try to maintain the ODE solution that represnts hydrostatic equilibrium +# Checks the geometrical terms +# IMPORTANT: This test is different from the other tests in its initialization +# It reads in ".txt" files that correspond to the ODE solution (set input to "ODE" in ) +# Run it with a single MPI task + + +problem_id = conducting_atmosphere + + +refinement = none +numlevel = 1 +nx1 = 256 +nx2 = 256 +nx3 = 1 + + +nx1 = 256 +nx2 = 256 +nx3 = 1 + + + +base = ks +transform = mks +a = 0.0 +hslope = 1.0 +r_in = 200. +r_out = 300. + + +inner_x1 = dirichlet +outer_x1 = dirichlet +check_inflow_inner_x1 = false +check_inflow_outer_x1 = false + + +tlim = 400. + + +type = imex + + +implicit = true +cfl = 0.9 +gamma = 1.333333 +reconstruction = weno5 + + +implicit = false +initial_cleanup = false + + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 +linesearch = true +max_linesearch_iter = 3 +linesearch_eps = 1.e-4 + +# IMPORTANT: This block must be present and values filled in all EGRMHD simulations + +on = true +higher_order_terms = true +feedback = true +stability_limits = false + +conduction = true +viscosity = false + +closure_type = kappa_eta +tau = 10. +kappa = 0.1 +eta = 0.0 + + +input = ODE + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 10 +single_precision_output = false +variables = prims, solve_norm, solve_fail +ghost_zones = true + + +file_type = hst +dt = 100 diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index ae70088b..964d4c3e 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -14,6 +14,7 @@ conv_2d() { do cp conducting_atmosphere_${res}_default/atmosphere_soln_*.txt . $BASE/run.sh -n 1 -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ + parthenon/time/tlim=200 parthenon/output0/dt=1000000 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ $2 >log_${1}_${res}.txt 2>&1 @@ -34,5 +35,4 @@ conv_2d() { } ALL_RES="64,128,256,512" -#ALL_RES="64,128" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" diff --git a/tests/emhdshock/emhdshock.par b/tests/emhdshock/emhdshock.par new file mode 100644 index 00000000..26e191b0 --- /dev/null +++ b/tests/emhdshock/emhdshock.par @@ -0,0 +1,94 @@ +# EMHD Shock problem +# Try to maintain the BVP solution to a discontuinity +# Checks the higher order terms implementation in flat space +# IMPORTANT: This test is different from the other tests in its initialization +# It reads in ".txt" files that correspond to the BVP solution (set input to "BVP" in ) +# One, in principle, can run this problem with the usual ideal MHD jump conditions but this +# may not allow a quantitative check +# Run it with a single MPI task + + +problem_id = emhdshock + + +refinement = none +numlevel = 1 + +nx1 = 1024 +x1min = -0.5 +x1max = 1.5 +ix1_bc = outflow +ox1_bc = outflow + +nx2 = 1 +x2min = 0.0 +x2max = 1.0 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = 0.0 +x3max = 1.0 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 1024 +nx2 = 1 +nx3 = 1 + + +base = cartesian_minkowski +transform = null + + +# "RK2" is the only option for implicit solver +tlim = 0.5 +integrator = rk2 +dt_min = 1.e-6 + + +cfl = 0.25 +gamma = 1.333333 +reconstruction = linear_mc + + +implicit = true +initial_cleanup = false + +# IMPORTANT: This block must be present and values filled in all EGRMHD simulations + +on = true +higher_order_terms = true + +closure_type = soundspeed +tau = 0.1 +conduction_alpha = 5.0 +viscosity_alpha = 3.0 + + +max_nonlinear_iter = 3 +rootfind_tol = 1.e-20 +jacobian_delta = 4.e-8 + + +# The input can be the BVP solution or the ideal MHD Rankine-Hugoniot jump conditions +input = BVP + + +disable_floors = true + + +verbose = 1 +flag_verbose = 2 +extra_checks = 1 + + +file_type = hdf5 +dt = 0.05 +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP + + +file_type = hst +dt = 0.1 \ No newline at end of file diff --git a/tests/mhdmodes/check.py b/tests/mhdmodes/check.py index 5b7ec02d..0dfffc21 100644 --- a/tests/mhdmodes/check.py +++ b/tests/mhdmodes/check.py @@ -60,19 +60,19 @@ dvar[6] = 0.0977545707307 dvar[7] = 0.0977545707307 if "alfven" in SHORT: - dvar[3] = -0.339683110243 - dvar[4] = 0.339683110243 - dvar[6] = 0.620173672946 - dvar[7] = -0.620173672946 + dvar[3] = -0.339683110243 + dvar[4] = 0.339683110243 + dvar[6] = 0.620173672946 + dvar[7] = -0.620173672946 if "fast" in SHORT: - dvar[0] = 0.481846076323 - dvar[1] = 0.642461435098 - dvar[2] = -0.0832240462505 - dvar[3] = -0.224080007379 - dvar[4] = -0.224080007379 - dvar[5] = 0.406380545676 - dvar[6] = -0.203190272838 - dvar[7] = -0.203190272838 + dvar[0] = 0.481846076323 + dvar[1] = 0.642461435098 + dvar[2] = -0.0832240462505 + dvar[3] = -0.224080007379 + dvar[4] = -0.224080007379 + dvar[5] = 0.406380545676 + dvar[6] = -0.203190272838 + dvar[7] = -0.203190272838 else: # EIGENMODES: 2D # We only *convergence check* dir = 3 i.e. X1/X2 plane runs @@ -113,9 +113,12 @@ dvar_code.append(dump['U1'] - var0[2]) dvar_code.append(dump['U2'] - var0[3]) dvar_code.append(dump['U3'] - var0[4]) - dvar_code.append(dump['B1'] - var0[5]) - dvar_code.append(dump['B2'] - var0[6]) - dvar_code.append(dump['B3'] - var0[7]) + try: + dvar_code.append(dump['B1'] - var0[5]) + dvar_code.append(dump['B2'] - var0[6]) + dvar_code.append(dump['B3'] - var0[7]) + except IOError: + NVAR = 5 dvar_sol = [] L1.append([]) @@ -155,7 +158,7 @@ plt.xscale('log', base=2); plt.yscale('log') plt.xlim([RES[0]/np.sqrt(2.), RES[-1]*np.sqrt(2.)]) plt.xlabel('N'); plt.ylabel('L1') -plt.title("MHD mode test convergence, {}".format(LONG)) +plt.title("{}".format(LONG)) plt.legend(loc=1) plt.savefig("convergence_modes_{}_{}.png".format(DIM,SHORT)) diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index 0b35f925..5426905e 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -2,18 +2,24 @@ BASE=../.. -# Most of the point of this one is exercising all 3D of transport -# TODO restore 2D test, use for codepath equivalence stuff (faster). +# This test confirms that all of the many transport options in KHARMA +# can converge when modeling each of the basic linearized modes: +# slow, fast, and alfven waves + +# It tests: +# 1. different reconstructions WENO vs linear +# 2. different drivers, simple, KHARMA, & ImEx +# 3. different B field transports, Flux-CT and Face-CT exit_code=0 conv_3d() { - ALL_RES="8,16,24,32,48,64" - for res in 8 16 24 32 48 64 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do # Eight blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/mhdmodes.par debug/verbose=2 \ + $BASE/run.sh -i $BASE/pars/tests/mhdmodes.par debug/verbose=2 mhdmodes/dir=0 \ parthenon/output0/single_precision_output=false parthenon/output0/dt=100. \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=$res \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=$half \ @@ -31,12 +37,12 @@ conv_3d() { fi } conv_2d() { - ALL_RES="16,24,32,48,64,96,128,256" - for res in 16 24 32 48 64 96 128 256 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/mhdmodes.par debug/verbose=1 mhdmodes/dir=3 \ + $BASE/run.sh -i $BASE/pars/tests/mhdmodes.par debug/verbose=2 mhdmodes/dir=3 \ parthenon/output0/single_precision_output=false parthenon/output0/dt=100. \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ @@ -55,30 +61,49 @@ conv_2d() { } # Normal MHD modes, 2D, defaults +ALL_RES="16,24,32,48,64" conv_2d slow mhdmodes/nmode=1 "slow mode in 2D" conv_2d alfven mhdmodes/nmode=2 "Alfven mode in 2D" conv_2d fast mhdmodes/nmode=3 "fast mode in 2D" # Entropy mode as reconstruction demo -#conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D, no B field" # TODO init currently requires B -conv_3d entropy mhdmodes/nmode=0 "entropy mode in 3D" -conv_3d entropy_mc "mhdmodes/nmode=0 GRMHD/reconstruction=linear_mc" "entropy mode in 3D, linear/MC reconstruction" -conv_3d entropy_vl "mhdmodes/nmode=0 GRMHD/reconstruction=linear_vl" "entropy mode in 3D, linear/VL reconstruction" +conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D, no B field" +conv_2d entropy mhdmodes/nmode=0 "entropy mode in 3D, WENO reconstruction" +conv_2d entropy_mc "mhdmodes/nmode=0 driver/reconstruction=linear_mc" "entropy mode in 2D, linear/MC reconstruction" +#conv_2d entropy_vl "mhdmodes/nmode=0 driver/reconstruction=linear_vl" "entropy mode in 2D, linear/VL reconstruction" +conv_2d entropy_donor "mhdmodes/nmode=0 driver/reconstruction=donor_cell" "entropy mode in 2D, Donor Cell reconstruction" +# KHARMA driver +conv_2d slow_kharma "mhdmodes/nmode=1 driver/type=kharma" "slow mode in 2D, KHARMA driver" +conv_2d alfven_kharma "mhdmodes/nmode=2 driver/type=kharma" "Alfven mode in 2D, KHARMA driver" +conv_2d fast_kharma "mhdmodes/nmode=3 driver/type=kharma" "fast mode in 2D, KHARMA driver" # ImEx driver -conv_2d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 3D, ImEx explicit" -conv_2d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 3D, ImEx explicit" -conv_2d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 3D, ImEx explicit" +conv_2d slow_imex "mhdmodes/nmode=1 driver/type=imex" "slow mode in 2D, ImEx explicit" +conv_2d alfven_imex "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 2D, ImEx explicit" +conv_2d fast_imex "mhdmodes/nmode=3 driver/type=imex" "fast mode in 2D, ImEx explicit" # B field totally explicit conv_2d slow_imex_semi "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "slow mode 3D, ImEx semi-implicit" conv_2d alfven_imex_semi "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "Alfven mode 3D, ImEx semi-implicit" conv_2d fast_imex_semi "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=false" "fast mode 3D, ImEx semi-implicit" -# All variables semi-implicit -conv_2d slow_imex_im "mhdmodes/nmode=1 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "slow mode 3D, ImEx implicit" -conv_2d alfven_imex_im "mhdmodes/nmode=2 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "Alfven mode 3D, ImEx implicit" -conv_2d fast_imex_im "mhdmodes/nmode=3 driver/type=imex GRMHD/implicit=true b_field/implicit=true b_field/kill_on_large_divb=false" "fast mode 3D, ImEx implicit" + +# KHARMA driver +conv_2d slow_kharma_ct "mhdmodes/nmode=1 driver/type=kharma b_field/solver=face_ct" "slow mode in 2D, KHARMA driver w/face CT" +conv_2d alfven_kharma_ct "mhdmodes/nmode=2 driver/type=kharma b_field/solver=face_ct" "Alfven mode in 2D, KHARMA driver w/face CT" +conv_2d fast_kharma_ct "mhdmodes/nmode=3 driver/type=kharma b_field/solver=face_ct" "fast mode in 2D, KHARMA driver w/face CT" +# ImEx driver +conv_2d slow_imex_ct "mhdmodes/nmode=1 driver/type=imex b_field/solver=face_ct" "slow mode in 2D, ImEx explicit w/face CT" +conv_2d alfven_imex_ct "mhdmodes/nmode=2 driver/type=imex b_field/solver=face_ct" "Alfven mode in 2D, ImEx explicit w/face CT" +conv_2d fast_imex_ct "mhdmodes/nmode=3 driver/type=imex b_field/solver=face_ct" "fast mode in 2D, ImEx explicit w/face CT" + + +# simple driver, high res +ALL_RES="16,24,32,48,64,96,128,192,256" +conv_2d slow_highres "mhdmodes/nmode=1 driver/type=imex" "slow mode in 2D, simple driver" +conv_2d alfven_highres "mhdmodes/nmode=2 driver/type=imex" "Alfven mode in 2D, simple driver" +conv_2d fast_highres "mhdmodes/nmode=3 driver/type=imex" "fast mode in 2D, simple driver" # 3D versions, basics only +ALL_RES="16,24,32" conv_3d slow "mhdmodes/nmode=1 mhdmodes/dir=3" "slow mode in 3D" conv_3d alfven "mhdmodes/nmode=2 mhdmodes/dir=3" "Alfven mode in 3D" conv_3d fast "mhdmodes/nmode=3 mhdmodes/dir=3" "fast mode in 3D" diff --git a/tests/noh/check.py b/tests/noh/check.py index bf2021d9..bb33fa88 100644 --- a/tests/noh/check.py +++ b/tests/noh/check.py @@ -50,7 +50,7 @@ powerfit = np.polyfit(np.log(resolutions), np.log(l1_norm), 1)[0] print("Power fit: {} {}".format(powerfit, l1_norm)) # These bounds were chosen heuristically - if powerfit < -1.9 and powerfit > -2.1: + if powerfit < -0.85 and powerfit > -1.15: fail = 0 else: fail = 1 diff --git a/tests/noh/run.sh b/tests/noh/run.sh index 3bc841d5..ba7a6ad5 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Bash script to run 1D (Noh) shock test +# Bash script to run 1D Noh shock test # Set paths KHARMADIR=../.. @@ -8,11 +8,12 @@ KHARMADIR=../.. exit_code=0 noh_test() { - ALL_RES="64,128,256,512,1024,2048" + ALL_RES="128,256,512,1024,2048" for res in 64 128 256 512 1024 2048 do eighth=$(($res / 8)) - $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par debug/verbose=1 \ #parthenon/output0/dt=1000 \ + $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par debug/verbose=1 parthenon/output0/dt=1000 \ + electrons/gamma_e=1.666667 \ parthenon/mesh/nx1=$res parthenon/meshblock/nx1=$eighth \ >log_noh_${res}.txt 2>&1 From 1303b9b1efafb3db9516e03da196d37e0a1a7c59 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Oct 2023 15:16:14 -0600 Subject: [PATCH 144/219] Fix propagating B3 in 2D with face CT, pass mhdmodes --- kharma/b_ct/b_ct.cpp | 60 ++++++++++++++++++++++++------------------- scripts/ci/cpu.yml | 20 ++++++--------- tests/mhdmodes/run.sh | 3 ++- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 02659f1c..85f08854 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -215,11 +215,12 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; + const int kd = ndim > 2 ? 1 : 0; + const int jd = ndim > 1 ? 1 : 0; + const int id = ndim > 0 ? 1 : 0; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); - std::string scheme = pmesh->packages.Get("B_CT")->Param("ct_scheme"); - // Calculate circulation by averaging fluxes // This is the base of most other schemes, which make corrections // It is the entirety of B&S '99 @@ -237,13 +238,24 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) emf_pack(bl, E2, 0, k, j, i) = 0.25*(B_U(bl).flux(X3DIR, V1, k, j, i - 1) + B_U(bl).flux(X3DIR, V1, k, j, i) - B_U(bl).flux(X1DIR, V3, k - 1, j, i) - B_U(bl).flux(X1DIR, V3, k, j, i)); + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); + } else if (ndim > 1) { + emf_pack(bl, E1, 0, k, j, i) = B_U(bl).flux(X2DIR, V3, k, j, i); + emf_pack(bl, E2, 0, k, j, i) = -B_U(bl).flux(X1DIR, V3, k, j, i); + emf_pack(bl, E3, 0, k, j, i) = + 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) + - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); + } else { + emf_pack(bl, E1, 0, k, j, i) = 0; + emf_pack(bl, E2, 0, k, j, i) = -B_U(bl).flux(X1DIR, V3, k, j, i); + emf_pack(bl, E3, 0, k, j, i) = B_U(bl).flux(X1DIR, V2, k, j, i); } - emf_pack(bl, E3, 0, k, j, i) = - 0.25*(B_U(bl).flux(X1DIR, V2, k, j - 1, i) + B_U(bl).flux(X1DIR, V2, k, j, i) - - B_U(bl).flux(X2DIR, V1, k, j, i - 1) - B_U(bl).flux(X2DIR, V1, k, j, i)); } ); + std::string scheme = pmesh->packages.Get("B_CT")->Param("ct_scheme"); if (scheme == "bs99") { // Nothing more to do } else if (scheme == "gs05_0" || scheme == "gs05_c") { @@ -268,17 +280,15 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) const auto& G = B_U.GetCoords(bl); // Just subtract centered emf from twice the face version // More stable for planar flows even without anything fancy - if (ndim > 2) { - emf_pack(bl, E1, 0, k, j, i) = 2 * emf_pack(bl, E1, 0, k, j, i) - - 0.25*(emfc(bl, V1, k, j, i) + emfc(bl, V1, k, j - 1, i) - + emfc(bl, V1, k, j - 1, i) + emfc(bl, V1, k - 1, j - 1, i)); - emf_pack(bl, E2, 0, k, j, i) = 2 * emf_pack(bl, E2, 0, k, j, i) - - 0.25*(emfc(bl, V2, k, j, i) + emfc(bl, V2, k, j, i - 1) - + emfc(bl, V2, k - 1, j, i) + emfc(bl, V2, k - 1, j, i - 1)); - } + emf_pack(bl, E1, 0, k, j, i) = 2 * emf_pack(bl, E1, 0, k, j, i) + - 0.25*(emfc(bl, V1, k, j, i) + emfc(bl, V1, k, j - jd, i) + + emfc(bl, V1, k, j - jd, i) + emfc(bl, V1, k - kd, j - jd, i)); + emf_pack(bl, E2, 0, k, j, i) = 2 * emf_pack(bl, E2, 0, k, j, i) + - 0.25*(emfc(bl, V2, k, j, i) + emfc(bl, V2, k, j, i - id) + + emfc(bl, V2, k - kd, j, i) + emfc(bl, V2, k - kd, j, i - id)); emf_pack(bl, E3, 0, k, j, i) = 2 * emf_pack(bl, E3, 0, k, j, i) - - 0.25*(emfc(bl, V3, k, j, i) + emfc(bl, V3, k, j, i - 1) - + emfc(bl, V3, k, j - 1, i) + emfc(bl, V3, k, j - 1, i - 1)); + - 0.25*(emfc(bl, V3, k, j, i) + emfc(bl, V3, k, j, i - id) + + emfc(bl, V3, k, j - jd, i) + emfc(bl, V3, k, j - jd, i - id)); } ); } else if (scheme == "gs05_c") { @@ -362,17 +372,15 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) dB_Uf_dt(bl, F2, 0, k, j, i) /= G.Volume(k, j, i); } ); - if (ndim > 2) { - pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { - const auto& G = dB_Uf_dt.GetCoords(bl); - dB_Uf_dt(bl, F3, 0, k, j, i) = (G.Volume(k, j, i + 1) * emf_pack(bl, E2, 0, k, j, i + 1) - - G.Volume(k, j, i) * emf_pack(bl, E2, 0, k, j, i) - - G.Volume(k, j + 1, i) * emf_pack(bl, E1, 0, k, j + 1, i) - + G.Volume(k, j, i) * emf_pack(bl, E1, 0, k, j, i)) / G.Volume(k, j, i); - } - ); - } + pmb0->par_for("B_CT_Circ_3", block.s, block.e, b1.ks, b1.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { + const auto& G = dB_Uf_dt.GetCoords(bl); + dB_Uf_dt(bl, F3, 0, k, j, i) = (G.Volume(k, j, i + 1) * emf_pack(bl, E2, 0, k, j, i + 1) + - G.Volume(k, j, i) * emf_pack(bl, E2, 0, k, j, i) + - G.Volume(k, j + 1, i) * emf_pack(bl, E1, 0, k, j + 1, i) + + G.Volume(k, j, i) * emf_pack(bl, E1, 0, k, j, i)) / G.Volume(k, j, i); + } + ); // Explicitly zero polar faces // In spherical, zero B2 on X2 face regardless of boundary condition diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index a5889edd..1f13629d 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -10,28 +10,24 @@ variables: OMPI_ALLOW_RUN_AS_ROOT: 1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 GIT_SUBMODULE_STRATEGY: recursive + MAMBA_ROOT_PREFIX: /mamba ### DEFAULT TEST BEHAVIOR ### default: tags: - public-kharma-runner - # Be default: install pyharm, then run test in cwd + # By default: install pyharm, then run test in cwd # For new tests, write one run.sh script which runs/verifies # interleaved, and prints a summary of results. before_script: - - dnf -y install hostname environment-modules git mpich fftw wget + - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - export PATH="$HOME/.local/bin:$PATH" - - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - - bash Miniforge3.sh -b -p "/home/conda" - - source "/home/conda/etc/profile.d/conda.sh" - - conda install h5py - - git clone https://github.com/AFD-Illinois/pyharm.git /home/pyharm - - conda activate - - cd /home/pyharm - - pip install --user . - - cd - + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba + - eval "$(./bin/micromamba shell hook -s posix)" + - micromamba create -y -f environment.yml + - micromamba activate pyharm + - ./install.sh # Tests can be executed in parallel stages: diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index 5426905e..b1647b97 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -71,7 +71,8 @@ conv_2d entropy_nob "mhdmodes/nmode=0 b_field/solver=none" "entropy mode in 2D, conv_2d entropy mhdmodes/nmode=0 "entropy mode in 3D, WENO reconstruction" conv_2d entropy_mc "mhdmodes/nmode=0 driver/reconstruction=linear_mc" "entropy mode in 2D, linear/MC reconstruction" #conv_2d entropy_vl "mhdmodes/nmode=0 driver/reconstruction=linear_vl" "entropy mode in 2D, linear/VL reconstruction" -conv_2d entropy_donor "mhdmodes/nmode=0 driver/reconstruction=donor_cell" "entropy mode in 2D, Donor Cell reconstruction" +# TODO doesn't converge? +#conv_2d entropy_donor "mhdmodes/nmode=0 driver/reconstruction=donor_cell" "entropy mode in 2D, Donor Cell reconstruction" # KHARMA driver conv_2d slow_kharma "mhdmodes/nmode=1 driver/type=kharma" "slow mode in 2D, KHARMA driver" From c817f1907b3aa39c0d53eee405a9798a4724b3ae Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Oct 2023 17:01:58 -0600 Subject: [PATCH 145/219] Pass bondi problem Fix run script, fix enabling flag_verbose in imex Support running in bare KS coordinates via new trivial transform (needed the check a little looser, turns out KS are not good coords) --- kharma/coordinates/coordinate_embedding.hpp | 6 +++- kharma/coordinates/coordinate_systems.hpp | 28 +++++++++++++++- kharma/implicit/implicit.cpp | 13 ++++++-- pars/bondi/bondi.par | 16 ++-------- tests/bondi/check.py | 2 +- tests/bondi/run.sh | 20 ++++++++---- .../bondi_analytic_32.txt | 32 ------------------- 7 files changed, 59 insertions(+), 58 deletions(-) delete mode 100644 tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt diff --git a/kharma/coordinates/coordinate_embedding.hpp b/kharma/coordinates/coordinate_embedding.hpp index bec773fb..eb0e6b08 100644 --- a/kharma/coordinates/coordinate_embedding.hpp +++ b/kharma/coordinates/coordinate_embedding.hpp @@ -153,7 +153,11 @@ class CoordinateEmbedding { bool spherical = is_spherical(); if (transform_str == "null" || transform_str == "none") { - transform.emplace(NullTransform()); + if (spherical) { + transform.emplace(SphNullTransform()); + } else { + transform.emplace(NullTransform()); + } } else if (transform_str == "exponential" || transform_str == "exp" || transform_str == "eks") { if (!spherical) throw std::invalid_argument("Transform is for spherical coordinates!"); transform.emplace(ExponentialTransform()); diff --git a/kharma/coordinates/coordinate_systems.hpp b/kharma/coordinates/coordinate_systems.hpp index b0361618..28d7717c 100644 --- a/kharma/coordinates/coordinate_systems.hpp +++ b/kharma/coordinates/coordinate_systems.hpp @@ -373,6 +373,32 @@ class NullTransform { DLOOP2 dXdx[mu][nu] = (mu == nu); } }; +// This only exists separately to define startx & stopx. Could fall back on base coords for these? +class SphNullTransform { + public: + static constexpr char name[] = "SphNullTransform"; + static constexpr GReal startx[3] = {-1, 0., 0.}; + static constexpr GReal stopx[3] = {-1, M_PI, 2*M_PI}; + // Coordinate transformations + // Any coordinate value protections (th < 0, th > pi, phi > 2pi) should be in the base system + KOKKOS_INLINE_FUNCTION void coord_to_embed(const GReal Xnative[GR_DIM], GReal Xembed[GR_DIM]) const + { + DLOOP1 Xembed[mu] = Xnative[mu]; + } + KOKKOS_INLINE_FUNCTION void coord_to_native(const GReal Xembed[GR_DIM], GReal Xnative[GR_DIM]) const + { + DLOOP1 Xnative[mu] = Xembed[mu]; + } + // Tangent space transformation matrices + KOKKOS_INLINE_FUNCTION void dxdX(const GReal X[GR_DIM], Real dxdX[GR_DIM][GR_DIM]) const + { + DLOOP2 dxdX[mu][nu] = (mu == nu); + } + KOKKOS_INLINE_FUNCTION void dXdx(const GReal X[GR_DIM], Real dXdx[GR_DIM][GR_DIM]) const + { + DLOOP2 dXdx[mu][nu] = (mu == nu); + } +}; /** * Just exponentiate the radial coordinate @@ -644,4 +670,4 @@ class FunkyTransform { // These act as a wannabe "interface" or "parent class" with the exception that access requires "mpark::visit" // See coordinate_embedding.hpp using SomeBaseCoords = mpark::variant; -using SomeTransform = mpark::variant; +using SomeTransform = mpark::variant; diff --git a/kharma/implicit/implicit.cpp b/kharma/implicit/implicit.cpp index f0ddd5d4..1b7ee4b6 100644 --- a/kharma/implicit/implicit.cpp +++ b/kharma/implicit/implicit.cpp @@ -131,6 +131,10 @@ std::shared_ptr Implicit::Initialize(ParameterInput *pin, std::sh pkg->AddField("residual", m); } + // The major call, to Step(), is done manually from the ImEx driver + // But, we just register the diagnostics function to print out solver failures + pkg->PostStepDiagnosticsMesh = Implicit::PostStepDiagnostics; + return pkg; } @@ -601,9 +605,11 @@ TaskStatus Implicit::Step(MeshData *md_full_step_init, MeshData *md_ EndFlag(); } - if (flag_verbose > 0) { - Reductions::CheckFlagReduceAndPrintHits(md_solver, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); - } + // if (flag_verbose > 0) { + // // Start the reduction as soon as we have the data + // // Dangerous, so commented + // Reductions::StartFlagReduce(md_solver, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); + // } EndFlag(); return TaskStatus::complete; @@ -620,6 +626,7 @@ TaskStatus Implicit::PostStepDiagnostics(const SimTime& tm, MeshData *md) // Debugging/diagnostic info about implicit solver if (flag_verbose > 0) { + Reductions::StartFlagReduce(md, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); Reductions::CheckFlagReduceAndPrintHits(md, "solve_fail", Implicit::status_names, IndexDomain::interior, false, 2); } diff --git a/pars/bondi/bondi.par b/pars/bondi/bondi.par index 67b22a36..ddcf77b6 100644 --- a/pars/bondi/bondi.par +++ b/pars/bondi/bondi.par @@ -31,7 +31,7 @@ hslope = 0.3 r_in = 3.0 r_out = 30.0 # If using "Funky" MKS later, where is "startx1"? -fmks_zero_point = 0.0 +fmks_zero_point = 1.0 tlim = 50.0 @@ -51,12 +51,6 @@ rs = 8.0 # Disable floors disable_floors = true -# If using B field, enable w/: -rho_min_geom = 1e-6 -u_min_geom = 1e-8 -bsq_over_rho_max = 100 -u_over_rho_max = 100 -gamma_max = 10 # We'll be adding material, and that's okay @@ -66,12 +60,6 @@ check_inflow_outer_x1 = false # No field type = none solver = none -# To add magnetic field -#type = monopole -#b10 = 1 -# Or -#type = vertical -#bz = 0.1 verbose = 0 @@ -83,7 +71,7 @@ file_type = hdf5 dt = 5.0 single_precision_output = true # Fields not present are silently ignored -variables = prims.rho, prims.u, prims.uvec, prims.B, pflag +variables = prims.rho, prims.u, prims.uvec, pflag file_type = hst diff --git a/tests/bondi/check.py b/tests/bondi/check.py index 898359ae..a34c3aab 100644 --- a/tests/bondi/check.py +++ b/tests/bondi/check.py @@ -52,7 +52,7 @@ L1[var] = np.array(L1[var]) powerfit = np.polyfit(np.log(RES), np.log(L1[var]), 1)[0] print("Powerfit: {} L1: {}".format(powerfit, L1[var])) - if powerfit < -2.2 or powerfit > -1.9: + if powerfit < -2.2 or powerfit > -1.85: fail = 1 # MAKE PLOTS diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index ff75ed2d..3f21749a 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -5,12 +5,12 @@ BASE=../.. exit_code=0 conv_2d() { - ALL_RES="16,32,48,64" - for res in 16 32 48 64 + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/bondi.par debug/verbose=1 debug/flag_verbose=2 parthenon/time/tlim=50 \ + $BASE/run.sh -i $BASE/pars/bondi/bondi.par debug/verbose=1 debug/flag_verbose=2 parthenon/time/tlim=50 \ parthenon/output0/dt=1000 parthenon/output0/single_precision_output=false \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ @@ -28,16 +28,20 @@ conv_2d() { fi } +# Test boundaries +ALL_RES="16,24,32,48,64" conv_2d dirichlet "boundaries/inner_x1=dirichlet boundaries/outer_x1=dirichlet" "in 2D, Dirichlet boundaries" # Test coordinates -#conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" conv_2d mks coordinates/transform=mks "in 2D, MKS coordinates" conv_2d eks coordinates/transform=eks "in 2D, EKS coordinates" -# TODO broken -#conv_2d ks coordinates/transform=null "in 2D, KS coordinates" +# Some coordinate systems do better/worse than 2o at low res +ALL_RES="48,64,96,128" +conv_2d fmks coordinates/transform=fmks "in 2D, FMKS coordinates" +conv_2d ks coordinates/transform=null "in 2D, KS coordinates" # Recon +ALL_RES="16,24,32,48,64" conv_2d linear_mc GRMHD/reconstruction=linear_mc "in 2D, linear recon with MC limiter" conv_2d linear_vl GRMHD/reconstruction=linear_vl "in 2D, linear recon with VL limiter" @@ -45,4 +49,8 @@ conv_2d linear_vl GRMHD/reconstruction=linear_vl "in 2D, linear recon with VL li conv_2d imex driver/type=imex "in 2D, with Imex driver" conv_2d imex_im "driver/type=imex GRMHD/implicit=true" "in 2D, semi-implicit stepping" +# TODO magnetized? + +# TODO 3D, esp magnetized + exit $exit_code diff --git a/tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt b/tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt deleted file mode 100644 index d8e2a9c2..00000000 --- a/tests/bondi_viscous/bondi_viscous_32_default/bondi_analytic_32.txt +++ /dev/null @@ -1,32 +0,0 @@ -2.584501169621944427e-02 6.068188231438398361e-03 -6.999706029891967773e-01 4.522162550684973893e-03 -2.356654405593872070e-02 5.203045438975095749e-03 -6.652408838272094727e-01 4.140434014011852570e-03 -2.149801887571811676e-02 4.464387428015470505e-03 -6.319671869277954102e-01 3.789233857803978815e-03 -1.961943879723548889e-02 3.833322320133447647e-03 -6.001013517379760742e-01 3.466035327603429750e-03 -1.791276969015598297e-02 3.293838584795594215e-03 -5.695956349372863770e-01 3.168579558245918169e-03 -1.636174879968166351e-02 2.832352416589856148e-03 -5.404032468795776367e-01 2.894997972590632879e-03 -1.495170872658491135e-02 2.437338000163435936e-03 -5.124777555465698242e-01 2.643221787914404146e-03 -1.366940420120954514e-02 2.099006203934550285e-03 -4.857742488384246826e-01 2.411604044596365147e-03 -1.250288169831037521e-02 1.809038803912699223e-03 -4.602482616901397705e-01 2.198377945378686849e-03 -1.144134253263473511e-02 1.560364384204149246e-03 -4.358564615249633789e-01 2.002143248357593670e-03 -1.047503110021352768e-02 1.346965902484953403e-03 -4.125564098358154297e-01 1.821718323887453698e-03 -9.595127776265144348e-03 1.163721084594726562e-03 -3.903068006038665771e-01 1.655920184890402862e-03 -8.793655782938003540e-03 1.006267266348004341e-03 -3.690673708915710449e-01 1.503540855322907160e-03 -8.063399232923984528e-03 8.708859095349907875e-04 -3.487989008426666260e-01 1.363545946702775496e-03 -7.397830486297607422e-03 7.544056279584765434e-04 -3.294633328914642334e-01 1.234987096634110760e-03 -6.791035179048776627e-03 6.541201728396117687e-04 -3.110238611698150635e-01 1.116773817419731670e-03 -6.237659603357315063e-03 5.677193985320627689e-04 -2.934447228908538818e-01 1.008369874502772559e-03 -5.732852034270763397e-03 4.932292504236102104e-04 -2.766912281513214111e-01 9.088907239085136256e-04 -5.272216163575649261e-03 4.289627831894904375e-04 -2.607300579547882080e-01 8.173766547032038435e-04 -4.851764533668756485e-03 3.734769416041672230e-04 -2.455290406942367554e-01 7.334003942844100716e-04 -4.467881284654140472e-03 3.255369665566831827e-04 -2.310569882392883301e-01 6.563852240762084326e-04 -4.117285367101430893e-03 2.840855740942060947e-04 -2.172840982675552368e-01 5.854909152618705589e-04 -3.796998877078294754e-03 2.482170821167528629e-04 -2.041816562414169312e-01 5.201248927715958637e-04 -3.504319349303841591e-03 2.171552332583814859e-04 -1.917218714952468872e-01 4.596615136618002577e-04 -3.236790420487523079e-03 1.902341609820723534e-04 -1.798784136772155762e-01 4.033137860565213931e-04 -2.992182737216353416e-03 1.668826007517054677e-04 -1.686256676912307739e-01 3.502789494691153460e-04 -2.768469508737325668e-03 1.466100511606782675e-04 -1.579393297433853149e-01 2.995213350777409657e-04 -2.563808113336563110e-03 1.289951469516381621e-04 -1.477960050106048584e-01 2.498668730583249718e-04 -2.376524033024907112e-03 1.136757491622120142e-04 -1.381733268499374390e-01 1.991955526768476121e-04 -2.205094322562217712e-03 1.003404613584280014e-04 -1.290498524904251099e-01 1.446416184657461858e-04 -2.048132708296179771e-03 8.872123726177960634e-05 -1.204050555825233459e-01 8.137099178784023786e-05 -1.904378994368016720e-03 7.858742901589721441e-05 -1.122193336486816406e-01 0.000000000000000000e+00 From c5d183f5aebe6f68ee51f49bd6229b7fa276dcda Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Oct 2023 08:36:07 -0600 Subject: [PATCH 146/219] Pass aniso_conduction, emhdmodes * Restores EMHD terms to stress-energy tensor (no, I do not remember why they were removed in this branch...) * Run script touch-ups for bondi_viscous but dP still not converging --- kharma/flux/flux_functions.hpp | 8 +++----- kharma/flux/get_flux.hpp | 1 + pars/emhd/bondi_viscous.par | 9 ++++----- tests/anisotropic_conduction/run.sh | 2 +- tests/bondi_viscous/check.py | 10 +++++----- tests/bondi_viscous/run.sh | 4 ++-- tests/emhdmodes/run.sh | 2 +- 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index b1de46ea..6c6d577a 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -73,7 +73,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const // GRMHD stress-energy tensor w/ first index up, second index down GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); } else { - // GRHD stress-energy tensor w/ first index up, second index down + // GRHD stress-energy tensor GRHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); } } @@ -125,8 +125,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, // Stress-energy tensor Real T[GR_DIM]; - //calc_tensor(P, m_p, D, emhd_params, gam, dir, T); - GRMHD::calc_tensor(P(m_p.RHO), P(m_p.UU), (gam - 1) * P(m_p.UU), D, dir, T); + calc_tensor(P, m_p, D, emhd_params, gam, dir, T); flux(m_u.UU) = T[0] * gdet + flux(m_u.RHO); flux(m_u.U1) = T[1] * gdet; flux(m_u.U2) = T[2] * gdet; @@ -190,8 +189,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P flux(m_u.RHO, k, j, i) = P(m_p.RHO, k, j, i) * D.ucon[dir] * gdet; Real T[GR_DIM]; - //calc_tensor(P, m_p, D, emhd_params, gam, k, j, i, dir, T); - GRMHD::calc_tensor(P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), (gam - 1) * P(m_p.UU, k, j, i), D, dir, T); + calc_tensor(P, m_p, D, emhd_params, gam, k, j, i, dir, T); flux(m_u.UU, k, j, i) = T[0] * gdet + flux(m_u.RHO, k, j, i); flux(m_u.U1, k, j, i) = T[1] * gdet; flux(m_u.U2, k, j, i) = T[2] * gdet; diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index fc77b63b..2c3bad88 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -340,6 +340,7 @@ inline TaskStatus GetFlux(MeshData *md) EndFlag(); // Save the face velocities for upwinding/CT later + // TODO only for certain GS'05 if (packages.AllPackages().count("B_CT")) { Flag("GetFlux_"+std::to_string(dir)+"_store_vel"); const auto& vl_all = md->PackVariables(std::vector{"Flux.vl"}); diff --git a/pars/emhd/bondi_viscous.par b/pars/emhd/bondi_viscous.par index f7b15d23..16b0fead 100644 --- a/pars/emhd/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -31,8 +31,7 @@ implicit = true type = monopole_cube -implicit = false -initial_cleanup = false +B10 = 1. min_nonlinear_iter = 1 @@ -65,8 +64,8 @@ rs = 8.0 disable_floors = true -outer_x1 = dirichlet -inner_x1 = dirichlet +#outer_x1 = dirichlet +#inner_x1 = dirichlet check_inflow_outer_x1 = false #check_inflow_inner_x1 = false @@ -79,7 +78,7 @@ extra_checks = 1 file_type = hdf5 dt = 100.0 single_precision_output = false -ghost_zones = true +ghost_zones = false variables = prims, solve_norm, solve_fail diff --git a/tests/anisotropic_conduction/run.sh b/tests/anisotropic_conduction/run.sh index 8e641822..a3bc836d 100755 --- a/tests/anisotropic_conduction/run.sh +++ b/tests/anisotropic_conduction/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -../../run.sh -i ../../pars/anisotropic_conduction.par +../../run.sh -i ../../pars/emhd/anisotropic_conduction.par parthenon/time/tlim=5 python make_plots.py . diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index bac34b83..c5a0095e 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -28,7 +28,7 @@ RES = [int(r) for r in sys.argv[1].split(",")] LONG = sys.argv[2] SHORT = sys.argv[3] - + L1 = np.zeros([len(RES), NVAR]) fit = np.zeros([len(RES), NVAR]) @@ -38,13 +38,13 @@ dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res), cache_conn=True) # Compute analytic reference - mdot, rc, gam = dump['bondi']['mdot'], dump['bondi']['rs'], dump['gam'] - eta, tau = dump['emhd']['eta'], dump['emhd']['tau'] + mdot, rc, gam = dump['bondi/mdot'], dump['bondi/rs'], dump['gam'] + eta, tau = dump['emhd/eta'], dump['emhd/tau'] state = bondi.get_bondi_fluid_state(mdot, rc, gam, dump.grid) state.params['eta'] = eta state.params['tau'] = tau dP_check = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau) - + # load code data dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) @@ -52,7 +52,7 @@ #rho, uu = dump['RHO'], dump['UU'] # compute dP - if dump['emhd']['higher_order_terms'] == "true": + if dump['emhd/higher_order_terms'] == "true": print("Res: "+str(res)+"; higher order terms enabled") Theta = (dump['gam'] - 1.) * uu / rho nu_emhd = eta / rho diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index 0da6979b..ad7a8722 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -13,7 +13,7 @@ conv_2d() { do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/bondi_viscous.par debug/verbose=1 parthenon/time/tlim=400 \ + $BASE/run.sh -i $BASE/pars/emhd/bondi_viscous.par debug/verbose=1 parthenon/time/tlim=400 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 @@ -34,6 +34,6 @@ conv_2d() { } ALL_RES="8,16,32,64" -conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" +conv_2d emhd2d_weno driver/reconstruction=weno5 "in 2D, WENO5" exit $exit_code diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index 231c54c8..cd5b6854 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -13,7 +13,7 @@ conv_2d() { do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/emhdmodes.par debug/verbose=1 \ + $BASE/run.sh -i $BASE/pars/emhd/emhdmodes.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ $2 >log_${1}_${res}.txt 2>&1 From 4229a12c008c39159c22c2fba166e1e50f0e78e7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 4 Oct 2023 08:36:31 -0600 Subject: [PATCH 147/219] Add back an old experimental option to cleanup --- kharma/b_cleanup/b_cleanup.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 3d47e557..9d38ae13 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -87,6 +87,8 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s params.Add("warn_without_convergence", warn_without_convergence); bool always_solve = pin->GetOrAddBoolean("b_cleanup", "always_solve", false); params.Add("always_solve", always_solve); + bool use_normalized_divb = pin->GetOrAddBoolean("b_cleanup", "use_normalized_divb", false); + params.Add("use_normalized_divb", use_normalized_divb); // Finally, initialize the solver // Translate parameters @@ -208,6 +210,7 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) auto always_solve = pkg->Param("always_solve"); auto solver = pkg->Param>("solver"); auto verbose = pmesh->packages.Get("Globals")->Param("verbose"); + auto use_normalized = pkg->Param("use_normalized_divb"); if (MPIRank0() && verbose > 0) { std::cout << "Cleaning divB to relative tolerance " << rel_tolerance << std::endl; @@ -232,6 +235,21 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) // and syncs ghost zones KHARMADriver::SyncAllBounds(md); B_FluxCT::CalcDivB(md.get(), "divB_RHS"); + if (use_normalized) { + // Normalize divB by local metric determinant for fairer weighting of errors + // Note that laplacian operator will also have to be normalized ofc + auto divb_rhs = md->PackVariables(std::vector{"divB_RHS"}); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + const IndexRange ib = md->GetBoundsI(IndexDomain::entire); + const IndexRange jb = md->GetBoundsJ(IndexDomain::entire); + const IndexRange kb = md->GetBoundsK(IndexDomain::entire); + pmb0->par_for("normalize_divB", 0, divb_rhs.GetDim(5)-1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { + const auto& G = divb_rhs.GetCoords(b); + divb_rhs(b, 0, k, j, i) /= G.gdet(Loci::corner, j, i); + } + ); + } KHARMADriver::SyncAllBounds(md); // Add a solver container and associated MeshData @@ -300,6 +318,9 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_var, MeshData* md_again, const std::string& lap_var) { + auto pkg = md->GetMeshPointer()->packages.Get("B_Cleanup"); + const auto use_normalized = pkg->Param("use_normalized_divb"); + // Cover ghost cells; maximize since both ops have stencil >1 const IndexRange ib = md->GetBoundsI(IndexDomain::entire); const IndexRange jb = md->GetBoundsJ(IndexDomain::entire); @@ -339,6 +360,9 @@ TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_v const auto& G = lap.GetCoords(b); // This is the inverse diagonal element of a fictional a_ij Laplacian operator lap(b, 0, k, j, i) = B_FluxCT::corner_div(G, dB, b, k, j, i, ndim > 2); + if (use_normalized) { + lap(b, 0, k, j, i) /= G.gdet(Loci::corner, j, i); + } } ); From c4588cfa87410d79e8a801e664879555b99815e0 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 5 Oct 2023 12:27:42 -0600 Subject: [PATCH 148/219] Test updates & restarting Restarts now record cons by default, GRMHD prims only Restarting is now binary-similar @5 steps Also add back strict 'set -euo pipefail' to kill tests on nonzero returns, and fix some more test scripts --- kharma/b_flux_ct/b_flux_ct.cpp | 22 +++++++ kharma/b_flux_ct/b_flux_ct.hpp | 8 +-- kharma/driver/kharma_driver.cpp | 8 +-- kharma/grmhd/grmhd.cpp | 3 + kharma/prob/post_initialize.cpp | 54 ++++++++++++----- pars/bondi/bondi_1d.par | 90 +++++++++++++++++++++++++++++ tests/anisotropic_conduction/run.sh | 1 + tests/bondi/run.sh | 1 + tests/bondi_viscous/run.sh | 2 +- tests/bz_monopole/run.sh | 2 +- tests/conducting_atmosphere/run.sh | 2 +- tests/emhdmodes/run.sh | 2 +- tests/emhdshock/run.sh | 4 +- tests/mhdmodes/run.sh | 1 + tests/multizone/run.sh | 19 +++--- tests/noh/run.sh | 1 + tests/regrid/run.sh | 11 ++-- tests/reinit/run.sh | 12 ++-- tests/resize/run.sh | 11 +++- tests/restart/run.sh | 12 +++- tests/tilt_init/run.sh | 1 + tests/torus_sanity/mad_test.par | 71 +++++++++++++++++++++++ tests/torus_sanity/run.sh | 3 +- 23 files changed, 286 insertions(+), 55 deletions(-) create mode 100644 pars/bondi/bondi_1d.par create mode 100644 tests/torus_sanity/mad_test.par diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index d4b4dd63..3be20c15 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -206,6 +206,28 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) ); } +void MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) +{ + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); + + const auto& B_U = md->PackVariables(std::vector{"cons.B"}); + const auto& B_P = md->PackVariables(std::vector{"prims.B"}); + + auto bounds = coarse ? pmb0->c_cellbounds : pmb0->cellbounds; + IndexRange ib = bounds.GetBoundsI(domain); + IndexRange jb = bounds.GetBoundsJ(domain); + IndexRange kb = bounds.GetBoundsK(domain); + IndexRange vec = IndexRange{0, B_U.GetDim(4)-1}; + IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; + + pmb0->par_for("UtoP_B", block.s, block.e, vec.s, vec.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& b, const int &mu, const int &k, const int &j, const int &i) { + const auto& G = B_U.GetCoords(b); + // Update the primitive B-fields + B_U(b, mu, k, j, i) = B_P(b, mu, k, j, i) * G.gdet(Loci::center, j, i); + } + ); +} void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 2fc4c461..7de3f6c9 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -68,9 +68,10 @@ void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** * Reverse of the above. Only used alone during initialization. - * Generally, use Flux::BlockPtoU + * Generally, use Flux::BlockPtoU/Flux::MeshPtoU */ void BlockPtoU(MeshBlockData *md, IndexDomain domain, bool coarse=false); +void MeshPtoU(MeshData *md, IndexDomain domain, bool coarse=false); /** * All flux corrections required by this package @@ -90,7 +91,6 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse); * Alternate B field fix for X1 boundary, keeps zero divergence while permitting flux * through the boundary, at the cost of a short non-local solve. */ -// added by Hyerin TaskStatus FixX1Flux(MeshData *md); /** @@ -205,14 +205,14 @@ KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, double& B1, double& B2, double& B3) { const double norm = (do_3D) ? 0.25 : 0.5; - // 2D divergence, averaging to corners + // 2D gradient, averaging to centers double term1 = P(b, 0, k, j+1, i+1) + P(b, 0, k, j, i+1) - P(b, 0, k, j+1, i) - P(b, 0, k, j, i); double term2 = P(b, 0, k, j+1, i+1) + P(b, 0, k, j+1, i) - P(b, 0, k, j, i+1) - P(b, 0, k, j, i); double term3 = 0.; if (do_3D) { - // Average to corners in 3D, add 3rd flux + // Average to centers in 3D, add 3rd flux term1 += P(b, 0, k+1, j+1, i+1) + P(b, 0, k+1, j, i+1) - P(b, 0, k+1, j+1, i) - P(b, 0, k+1, j, i); term2 += P(b, 0, k+1, j+1, i+1) + P(b, 0, k+1, j+1, i) diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 5c7b30b8..64a67c30 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -144,12 +144,12 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std params.Add("sync_prims", sync_prims); if (sync_prims) { // If we're not in AMR, we can sync primitive variables directly - params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::Restart, Metadata::GetUserFlag("Primitive")}); - params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::WithFluxes, Metadata::Conserved}); + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::WithFluxes, Metadata::Conserved}); } else { // If we're in AMR or using the KHARMA driver anyway, sync conserved vars - params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::Restart, Metadata::GetUserFlag("Primitive")}); - params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); } return pkg; diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index 153cc5a4..e1d7746a 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -141,6 +141,9 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->Param>("cons_flags"); flags_cons.insert(flags_cons.end(), flags_grmhd.begin(), flags_grmhd.end()); + // We must additionally save the primtive variables as the "seed" for the next U->P solve + flags_prim.push_back(Metadata::Restart); + // We must additionally fill ghost zones of primitive variables in GRMHD, to seed the solver // Only necessary to add here if syncing conserved vars // Note some startup behavior relies on having the GRHD prims marked for syncing, diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 8dae599c..3a9833d0 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -69,6 +69,8 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) auto& pkgs = pmesh->packages.AllPackages(); + auto prob_name = pin->GetString("parthenon/job", "problem_id"); + // Magnetic field operations if (pin->GetString("b_field", "solver") != "none") { // If we need to seed a field based on the problem's fluid initialization... @@ -83,25 +85,11 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // If we're doing a torus problem or explicitly ask for it, // normalize the magnetic field according to the max density - bool is_torus = pin->GetString("parthenon/job", "problem_id") == "torus"; + bool is_torus = prob_name == "torus"; if (pin->GetOrAddBoolean("b_field", "norm", is_torus)) { NormalizeBField(md.get(), pin); } } - - // Regardless, if evolving a field we should print max(divB) - // divB is not stencil-1 and we may not have run the above. - // If we did, we still need another sync, so it works out - KBoundaries::FreezeDirichlet(md); - KHARMADriver::SyncAllBounds(md); - - if (pkgs.count("B_FluxCT")) { - B_FluxCT::PrintGlobalMaxDivB(md.get()); - } else if (pkgs.count("B_CT")) { - B_CT::PrintGlobalMaxDivB(md.get()); - } else if (pkgs.count("B_CD")) { - //B_CD::PrintGlobalMaxDivB(md.get()); - } } // Add any hotspots *after* we've seeded fields, @@ -119,6 +107,40 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // Parthenon restores all parameters (global vars) when restarting, // but KHARMA needs a few (currently one) reset instead KHARMA::ResetGlobals(pin, pmesh); + + // We only record the conserved magnetic field in KHARMA restarts, + // but we record primitive field in iharm3d restarts + bool iharm3d_restart = prob_name == "resize_restart"; + if (!iharm3d_restart) { + if (pkgs.count("B_FluxCT")) { + B_FluxCT::MeshUtoP(md.get(), IndexDomain::entire); + } else if (pkgs.count("B_CT")) { + B_CT::MeshUtoP(md.get(), IndexDomain::entire); + } + } else { + if (pkgs.count("B_FluxCT")) { + B_FluxCT::MeshPtoU(md.get(), IndexDomain::entire); + } else if (pkgs.count("B_CT")) { + // TODO this is only true if not cleaning, amend when cleaning supports B_CT + throw std::runtime_error("Cannot restart face-centered field from iharm3d!"); + } + } + } + + if (pin->GetString("b_field", "solver") != "none") { + // Regardless of how we initialized, if evolving a field we should print max(divB) + // divB is not stencil-1, and we may or may not have initialized or read it + // Either way, we still need another sync, so it works out + KBoundaries::FreezeDirichlet(md); + KHARMADriver::SyncAllBounds(md); + + if (pkgs.count("B_FluxCT")) { + B_FluxCT::PrintGlobalMaxDivB(md.get()); + } else if (pkgs.count("B_CT")) { + B_CT::PrintGlobalMaxDivB(md.get()); + } else if (pkgs.count("B_CD")) { + //B_CD::PrintGlobalMaxDivB(md.get()); + } } // Clean the B field, generally for resizing/restarting @@ -131,7 +153,7 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); } - // This does its own MPI syncs + // Cleanup is applied to conserved variables B_Cleanup::CleanupDivergence(md); } diff --git a/pars/bondi/bondi_1d.par b/pars/bondi/bondi_1d.par new file mode 100644 index 00000000..c42434d0 --- /dev/null +++ b/pars/bondi/bondi_1d.par @@ -0,0 +1,90 @@ +# Bondi flow problem +# Model a spherically symmetric, unmagnetized inflow +# Uses more MeshBlocks than necessary, for debugging + + +problem_id = bondi + + +# Full mesh size, no refinement +# Don't bother with xN boundaries for spherical coordinate systems +# KHARMA will automatically place ~5 zones inside the EH +nx1 = 128 +nx2 = 1 +nx3 = 1 + + +nx1 = 128 +nx2 = 1 +nx3 = 1 + + +# Spherical Kerr-Schild coords +base = spherical_ks +# MKS of Gammie '03 +transform = mks +# BH spin +a = 0.0 +# MKS parameter +hslope = 0.3 +# Radial domain in r_g +r_in = 3.0 +r_out = 30.0 +# If using "Funky" MKS later, where is "startx1"? +fmks_zero_point = 0.0 + + +tlim = 50.0 + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = weno5 + + +# Bondi problem parameters: +# density scaling/accretion rate +mdot = 1.0 +# Sonic point +rs = 8.0 + + +# Disable floors +disable_floors = true +# If using B field, enable w/: +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 100 +gamma_max = 10 + + +# We'll be adding material, and that's okay +check_inflow_outer_x1 = false + + +# No field +type = none +solver = none +# To add magnetic field +#type = monopole +#B10 = 1 +# Or +#type = vertical +#bz = 0.1 + + +verbose = 0 +flag_verbose = 0 +extra_checks = 1 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = true +# Fields not present are silently ignored +variables = prims.rho, prims.u, prims.uvec, prims.B, pflag + + +file_type = hst +dt = 0.1 diff --git a/tests/anisotropic_conduction/run.sh b/tests/anisotropic_conduction/run.sh index a3bc836d..a5682232 100755 --- a/tests/anisotropic_conduction/run.sh +++ b/tests/anisotropic_conduction/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail ../../run.sh -i ../../pars/emhd/anisotropic_conduction.par parthenon/time/tlim=5 diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index 3f21749a..77cdaa9a 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail BASE=../.. diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index ad7a8722..8c0f882a 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -#set -euo pipefail +set -euo pipefail BASE=../.. diff --git a/tests/bz_monopole/run.sh b/tests/bz_monopole/run.sh index ad9ff9de..2854c763 100755 --- a/tests/bz_monopole/run.sh +++ b/tests/bz_monopole/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -#set -euo pipefail +set -euo pipefail BASE=../.. diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 964d4c3e..bb62569c 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -# set -euo pipefail +set -euo pipefail BASE=../.. diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index cd5b6854..0b37a067 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -#set -euo pipefail +set -euo pipefail BASE=../.. diff --git a/tests/emhdshock/run.sh b/tests/emhdshock/run.sh index 120201c7..9ba0aa89 100755 --- a/tests/emhdshock/run.sh +++ b/tests/emhdshock/run.sh @@ -1,7 +1,7 @@ #!/bin/bash set -euo pipefail -BASE=~/kharma +BASE=../.. # Extended MHD shock test convergence to exercise higher order terms # We'll use just 1 MPI rank to circumvent the somewhat annoying BVP initialization @@ -10,7 +10,7 @@ conv_1d() { for res in 256 512 1024 2048 do cp shock_soln_${res}_default/shock_soln_*.txt ./ - $BASE/run.sh -n 1 -i $BASE/pars/emhdshock.par debug/verbose=1 \ + $BASE/run.sh -n 1 -i ./emhdshock.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=1 parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=1 parthenon/meshblock/nx3=1 mv emhdshock.out0.00000.phdf emhd_1d_${res}_start.phdf diff --git a/tests/mhdmodes/run.sh b/tests/mhdmodes/run.sh index b1647b97..6864c337 100755 --- a/tests/mhdmodes/run.sh +++ b/tests/mhdmodes/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail BASE=../.. diff --git a/tests/multizone/run.sh b/tests/multizone/run.sh index 1500f82c..836131f9 100755 --- a/tests/multizone/run.sh +++ b/tests/multizone/run.sh @@ -1,7 +1,10 @@ -#!/bin/bash -# Hyerin (02/17/23) copied from Ben's code +#!/bin/bash +set -euo pipefail -# Bash script testing b_clean +# Test a "multizone" run, consisting of several runs in sequence +# Adapted from script by Hyerin Cho (02/17/23) + +# TODO simplify for single test. Replace with invocation of run.py? # User specified values here KERR=false @@ -41,10 +44,10 @@ do runtime=10 echo "Running for: " $runtime log_u_over_rho=-5.2915149 # test same vacuum conditions as r_shell when (rs=1e2.5) - start_time=$(($start_time+$runtime)) + start_time=$(($start_time+$runtime)) #parfilename="../../kharma/pars/bondi_multizone/bondi_multizone_$(printf %05d ${VAR}).par" # parameter file - + # set problem type and cleanup if [ $VAR -eq 0 ]; then prob="bondi" #"torus" # @@ -53,20 +56,20 @@ do prob="resize_restart_kharma" init_c=1 fi - + # set BH spin if [[ $KERR == "true" ]]; then spin=0.99 else spin=0.0 fi - + # output time steps output0_dt=$((${runtime}/10)) #output1_dt=$((${runtime}/20*10)) output1_dt=$((${runtime}/5)) output2_dt=$((${runtime}/10)) - + # dt, fname, fname_fill if [ $VAR -ne 0 ]; then # update dt from the previous run diff --git a/tests/noh/run.sh b/tests/noh/run.sh index ba7a6ad5..ebfe9fa7 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail # Bash script to run 1D Noh shock test diff --git a/tests/regrid/run.sh b/tests/regrid/run.sh index 4748290d..13dd4f4c 100755 --- a/tests/regrid/run.sh +++ b/tests/regrid/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail # Bash script testing a fresh Orszag-Tang vortex vs a version # re-gridded to 64^2 tiles in the middle of the run, @@ -42,11 +43,11 @@ $KHARMADIR/run.sh -i ./resize_orszag_tang.par >log_resize.txt 2>&1 # Check the final .rhdf file for sanity (i.e., divB small) check_code=0 pyharm-check-basics resize_restart.out1.final.rhdf || check_code=$? -if [[ $check_code != 0 ]]; then - echo Resize test FAIL: $check_code - exit_code=1 -else - echo Resize test success +if [[ $check_code != 0 ]]; then + echo Resize test FAIL: $check_code + exit_code=1 +else + echo Resize test success fi exit $exit_code diff --git a/tests/reinit/run.sh b/tests/reinit/run.sh index e6852c3e..62ec790c 100755 --- a/tests/reinit/run.sh +++ b/tests/reinit/run.sh @@ -1,21 +1,23 @@ #!/bin/bash +set -euo pipefail # Bash script testing determinism of problem initialization and first steps # Set paths KHARMADIR=../.. -$KHARMADIR/run.sh -i $KHARMADIR/pars/sane.par debug/archive_parameters=false perturbation/u_jitter=0 parthenon/time/nlim=5 \ +$KHARMADIR/run.sh -i $KHARMADIR/pars/tori_3d/sane.par perturbation/u_jitter=0 parthenon/time/nlim=5 \ >log_reinit_1.txt 2>&1 mv torus.out1.final.rhdf torus.out1.final.first.rhdf #$KHARMADIR/run.sh -r torus.out1.00000.rhdf parthenon/time/nlim=5 -$KHARMADIR/run.sh -i $KHARMADIR/pars/sane.par debug/archive_parameters=false perturbation/u_jitter=0 parthenon/time/nlim=5 \ - >log_reinit_1.txt 2>&1 +$KHARMADIR/run.sh -i $KHARMADIR/pars/tori_3d/sane.par perturbation/u_jitter=0 parthenon/time/nlim=5 \ + >log_reinit_2.txt 2>&1 mv torus.out1.final.rhdf torus.out1.final.second.rhdf -# This one's a clear case. Binary or bust +# This one's a clear case. Binary or bust, even the input params +# /Info includes walltime, which obvs can change h5diff --exclude-path=/Info torus.out1.final.first.rhdf torus.out1.final.second.rhdf -# And that's the exit code. One and done. \ No newline at end of file +# And that's the exit code. One and done. diff --git a/tests/resize/run.sh b/tests/resize/run.sh index 5b965bd0..f62a31f6 100755 --- a/tests/resize/run.sh +++ b/tests/resize/run.sh @@ -1,19 +1,24 @@ #!/bin/bash +set -euo pipefail # Bash script testing starting a simulation, then resizing it up # Set paths KHARMADIR=../.. -$KHARMADIR/run.sh -i $KHARMADIR/pars/sane.par parthenon/time/nlim=5 >log_resize_1.txt 2>&1 +# This at least stirs up the field slightly vs initialization +$KHARMADIR/run.sh -i $KHARMADIR/pars/tori_3d/sane.par parthenon/time/nlim=5 >log_resize_1.txt 2>&1 +# We can only resize/restart from iharm3d-format files pyharm convert --to_restart torus.out0.final.phdf sleep 1 -$KHARMADIR/run.sh -i ../../pars/resize_restart >log_resize_2.txt 2>&1 +$KHARMADIR/run.sh -i $KHARMADIR/pars/restarts/resize_restart.par resize_restart/fname=torus.out0.final.h5 \ + b_cleanup/always_solve=1 parthenon/time/nlim=5 \ + >log_resize_2.txt 2>&1 mv torus.out0.final.phdf torus.out0.final.restart.phdf # Check divB on the re-meshed output -pyharm-check-basics torus.out0.final.restart.phdf +pyharm check-basics torus.out0.final.restart.phdf diff --git a/tests/restart/run.sh b/tests/restart/run.sh index e3a36f2c..e5ec3215 100755 --- a/tests/restart/run.sh +++ b/tests/restart/run.sh @@ -1,12 +1,13 @@ #!/bin/bash +set -euo pipefail # Bash script testing initialization vs restart of a torus problem -# TODO this *really* should be binary now. +# Require binary similarity after 5 steps # Set paths KHARMADIR=../.. -$KHARMADIR/run.sh -i $KHARMADIR/pars/sane.par parthenon/time/nlim=5 >log_restart_1.txt 2>&1 +$KHARMADIR/run.sh -i $KHARMADIR/pars/tori_3d/sane.par parthenon/time/nlim=5 >log_restart_1.txt 2>&1 mv torus.out0.final.phdf torus.out0.final.init.phdf @@ -17,4 +18,9 @@ $KHARMADIR/run.sh -r torus.out1.00000.rhdf parthenon/time/nlim=5 >log_restart_2. mv torus.out0.final.phdf torus.out0.final.restart.phdf # compare.py allows for small (5e-10) difference -pyharm-diff torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart +#pyharm-diff torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart +# Compare binary +h5diff --exclude-path=/Info \ + --exclude-path=/Input \ + --exclude-path=/divB \ + torus.out0.final.init.phdf torus.out0.final.restart.phdf diff --git a/tests/tilt_init/run.sh b/tests/tilt_init/run.sh index bfc8b967..bb6ca62a 100755 --- a/tests/tilt_init/run.sh +++ b/tests/tilt_init/run.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail # Run default tilted problem to 5 steps ../../run.sh -i ../../pars/mad_tilt.par parthenon/time/nlim=5 debug/verbose=1 \ diff --git a/tests/torus_sanity/mad_test.par b/tests/torus_sanity/mad_test.par new file mode 100644 index 00000000..ce2c2898 --- /dev/null +++ b/tests/torus_sanity/mad_test.par @@ -0,0 +1,71 @@ +# MAD model for testing. Differences from mad.par: +# 1. Smaller, smaller meshblocks +# 2. No history or restart file output +# 3. Output in double, include divB, exclude jcon +# 4. Default to 10 steps + + +problem_id = torus + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 64 +nx3 = 64 + + +nx1 = 32 +nx2 = 32 +nx3 = 32 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 10000.0 +nlim = 10 + + +cfl = 0.7 +gamma = 1.666667 +reconstruction = weno5 + + +type = imex +two_sync = true + + +rin = 20.0 +rmax = 41.0 + + +u_jitter = 0.1 + + +type = mad +beta_min = 100. + + +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 2 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.B, cons.B, fflag, pflag, divB diff --git a/tests/torus_sanity/run.sh b/tests/torus_sanity/run.sh index e2212aa8..8f1131d2 100755 --- a/tests/torus_sanity/run.sh +++ b/tests/torus_sanity/run.sh @@ -1,11 +1,12 @@ #!/bin/bash +set -euo pipefail BASE=../.. exit_code=0 check_sanity() { # mad_test.par is basically only used for this, so common options are there. - $BASE/run.sh -i $BASE/pars/mad_test.par $2 >log_divb_${1}.txt 2>&1 #|| exit_code=$? + $BASE/run.sh -i ./mad_test.par $2 >log_divb_${1}.txt 2>&1 #|| exit_code=$? pyharm check-basics -d --allowed_divb=1e-10 torus.out0.final.phdf || exit_code=$? } From 802bdb211636d7bb8c2b0b603a05356b56a935a0 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 5 Oct 2023 13:16:50 -0600 Subject: [PATCH 149/219] Fix B_Cleanup As with the rest of KHARMA, B-field cleaning was ignoring the divergence on corners which fell on the the polar faces. This is because BiCGStab wasn't applying any physical boundary conditions on phi during solving. The easy way to get correct boundary conditions was to declare the scalar field and RHS to be explicitly defined at corners/nodes, and let Parthenon apply the default bounds for that case (as well as applying our own boundaries to the intermediate dB when calculating the Laplacian) With the new boundaries, the "physical" domain for phi is now larger, so bicgstab_solver had to be heavily modified. Since it's no longer going to be upstreamed, I just forked it. --- kharma/b_cleanup/b_cleanup.cpp | 135 +++--- kharma/b_cleanup/bicgstab_solver.hpp | 676 +++++++++++++++++++++++++++ kharma/boundaries/boundaries.cpp | 11 +- kharma/prob/post_initialize.cpp | 7 + pars/restarts/resize_restart.par | 12 +- tests/resize/run.sh | 10 +- 6 files changed, 785 insertions(+), 66 deletions(-) create mode 100644 kharma/b_cleanup/bicgstab_solver.hpp diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 9d38ae13..9cca1a7b 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -38,6 +38,7 @@ #include "boundaries.hpp" #include "decs.hpp" +#include "domain.hpp" #include "kharma.hpp" #include "kharma_driver.hpp" #include "grmhd.hpp" @@ -55,7 +56,8 @@ void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) {} #else #include -#include +// This is now part of KHARMA, but builds on some stuff not in all Parthenon versions +#include "bicgstab_solver.hpp" using namespace parthenon; using namespace parthenon::solvers; @@ -71,11 +73,11 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // TODO also support face divB!! // Solver options - // Allow setting tolerance relative to starting value. Off by default - Real rel_tolerance = pin->GetOrAddReal("b_cleanup", "rel_tolerance", 1.); + // Allow setting tolerance relative to starting value + // Parthenon's BiCGStab solver stops on abs || rel, so this disables rel + Real rel_tolerance = pin->GetOrAddReal("b_cleanup", "rel_tolerance", 1e-20); params.Add("rel_tolerance", rel_tolerance); - // TODO add an absolute tolerance to the Parthenon BiCGStab solver - Real abs_tolerance = pin->GetOrAddReal("b_cleanup", "abs_tolerance", 1e-11); + Real abs_tolerance = pin->GetOrAddReal("b_cleanup", "abs_tolerance", 1e-9); params.Add("abs_tolerance", abs_tolerance); int max_iterations = pin->GetOrAddInteger("b_cleanup", "max_iterations", 1e8); params.Add("max_iterations", max_iterations); @@ -103,11 +105,12 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // Solution pkg->AddParam("sol_name", "p"); // RHS. Must not just be "divB" as that field does not sync boundaries - pkg->AddParam("rhs_name", "divB_RHS"); - // Construct a solver. We don't need the template parameter, so we use 'int' + pkg->AddParam("rhs_name", "RHS_divB"); + // Construct a solver. We don't need the template parameter, so we use 'int'. // The flag "StartupOnly" marks solver variables not to be sync'd later, // even though they're also marked FillGhost - BiCGStabSolver solver(pkg.get(), rel_tolerance, SparseMatrixAccessor(), {}, {Metadata::GetUserFlag("StartupOnly")}); + BiCGStabSolver solver(pkg.get(), rel_tolerance, abs_tolerance, + SparseMatrixAccessor(), {}, {Metadata::GetUserFlag("StartupOnly")}); // Set callback solver.user_MatVec = B_Cleanup::CornerLaplacian; @@ -115,15 +118,19 @@ std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::s // FIELDS std::vector s_vector({NVEC}); - std::vector cleanup_flags({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::GetUserFlag("StartupOnly")}); - auto cleanup_flags_ghost = cleanup_flags; - cleanup_flags_ghost.push_back(Metadata::FillGhost); + std::vector cleanup_flags({Metadata::Real, Metadata::Derived, Metadata::OneCopy, + Metadata::GetUserFlag("StartupOnly")}); + auto cleanup_flags_node = cleanup_flags; + cleanup_flags_node.push_back(Metadata::FillGhost); + cleanup_flags_node.push_back(Metadata::Node); + auto cleanup_flags_cell = cleanup_flags; + cleanup_flags_cell.push_back(Metadata::Cell); // Scalar potential, solution to del^2 p = div B - pkg->AddField("p", Metadata(cleanup_flags_ghost)); + pkg->AddField("p", Metadata(cleanup_flags_node)); // Gradient of potential; temporary for gradient calc - pkg->AddField("dB", Metadata(cleanup_flags, s_vector)); + pkg->AddField("dB", Metadata(cleanup_flags_cell, s_vector)); // Field divergence as RHS, i.e. including boundary sync - pkg->AddField("divB_RHS", Metadata(cleanup_flags_ghost)); + pkg->AddField("RHS_divB", Metadata(cleanup_flags_node)); // Optionally take care of B field transport ourselves. Inadvisable. @@ -213,14 +220,15 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) auto use_normalized = pkg->Param("use_normalized_divb"); if (MPIRank0() && verbose > 0) { - std::cout << "Cleaning divB to relative tolerance " << rel_tolerance << std::endl; + std::cout << "Cleaning divB to absolute tolerance " << abs_tolerance << + " OR relative tolerance " << rel_tolerance << std::endl; if (warn_flag) std::cout << "Convergence failure will produce a warning." << std::endl; if (fail_flag) std::cout << "Convergence failure will produce an error." << std::endl; } // Calculate/print inital max divB exactly as we would during run const double divb_start = B_FluxCT::GlobalMaxDivB(md.get(), true); - if (divb_start < rel_tolerance && !always_solve) { + if ((divb_start < abs_tolerance || divb_start < rel_tolerance) && !always_solve) { // If divB is "pretty good" and we allow not solving... if (MPIRank0()) std::cout << "Magnetic field divergence of " << divb_start << " is below tolerance. Skipping B field cleanup." << std::endl; @@ -230,38 +238,38 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) std::cout << "Starting magnetic field divergence: " << divb_start << std::endl; } + // Add a solver container as a shallow copy on the default MeshData + // msolve is just a sub-set of vars we need from md, making MPI syncs etc faster + std::vector names = KHARMA::GetVariableNames(&pmesh->packages, {Metadata::GetUserFlag("B_Cleanup"), Metadata::GetUserFlag("StartupOnly")}); + auto &msolve = pmesh->mesh_data.AddShallow("solve", names); + // Initialize the divB variable, which we'll be solving against. // This gets signed divB on all physical corners (total (N+1)^3) - // and syncs ghost zones - KHARMADriver::SyncAllBounds(md); - B_FluxCT::CalcDivB(md.get(), "divB_RHS"); + B_FluxCT::CalcDivB(md.get(), "RHS_divB"); // this fn draws from cons.B, which is not in msolve if (use_normalized) { // Normalize divB by local metric determinant for fairer weighting of errors // Note that laplacian operator will also have to be normalized ofc - auto divb_rhs = md->PackVariables(std::vector{"divB_RHS"}); - auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); - const IndexRange ib = md->GetBoundsI(IndexDomain::entire); - const IndexRange jb = md->GetBoundsJ(IndexDomain::entire); - const IndexRange kb = md->GetBoundsK(IndexDomain::entire); + auto divb_rhs = msolve->PackVariables(std::vector{"RHS_divB"}); + auto pmb0 = msolve->GetBlockData(0)->GetBlockPointer(); + const IndexRange ib = msolve->GetBoundsI(IndexDomain::entire); + const IndexRange jb = msolve->GetBoundsJ(IndexDomain::entire); + const IndexRange kb = msolve->GetBoundsK(IndexDomain::entire); pmb0->par_for("normalize_divB", 0, divb_rhs.GetDim(5)-1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = divb_rhs.GetCoords(b); - divb_rhs(b, 0, k, j, i) /= G.gdet(Loci::corner, j, i); + divb_rhs(b, NN, 0, k, j, i) /= G.gdet(Loci::corner, j, i); } ); } - KHARMADriver::SyncAllBounds(md); - - // Add a solver container and associated MeshData - std::vector names = KHARMA::GetVariableNames(&pmesh->packages, {Metadata::GetUserFlag("B_Cleanup"), Metadata::GetUserFlag("StartupOnly")}); - auto &msolve = pmesh->mesh_data.Add("solve", names); + // make sure divB_RHS is sync'd + KHARMADriver::SyncAllBounds(msolve); // Create a TaskCollection of just the solve, // execute it to perform BiCGStab iteration TaskID t_none(0); TaskCollection tc; auto tr = tc.AddRegion(1); - auto t_solve_step = solver.CreateTaskList(t_none, 0, tr, md, msolve); + auto t_solve_step = solver.CreateTaskList(t_none, 0, tr, msolve, msolve); while (!tr.Execute()); // Make sure solution's ghost zones are sync'd KHARMADriver::SyncAllBounds(msolve); @@ -271,12 +279,10 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) std::cout << "Applying magnetic field correction" << std::endl; } // Update the (conserved) magnetic field on physical zones using our solution - B_Cleanup::ApplyP(msolve.get(), md.get()); - - // Synchronize to update ghost zones + B_Cleanup::ApplyP(md.get(), md.get()); + // Synchronize to update cons.B's ghost zones KHARMADriver::SyncAllBounds(md); - - // Make sure primitive B reflects solution + // Make sure prims.B reflects solution B_FluxCT::MeshUtoP(md.get(), IndexDomain::entire, false); // Recalculate divB max for one last check @@ -291,9 +297,7 @@ TaskStatus B_Cleanup::CleanupDivergence(std::shared_ptr>& md) TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) { // Apply on physical zones only, we'll be syncing/updating ghosts - const IndexRange ib = md->GetBoundsI(IndexDomain::interior); - const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); - const IndexRange kb = md->GetBoundsK(IndexDomain::interior); + const IndexRange3 b = KDomain::GetRange(msolve, IndexDomain::interior, 0, 1); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); auto P = msolve->PackVariables(std::vector{"p"}); @@ -302,7 +306,7 @@ TaskStatus B_Cleanup::ApplyP(MeshData *msolve, MeshData *md) const int ndim = P.GetNdim(); // dB = grad(p), defined at cell centers, subtract to make field divergence-free - pmb0->par_for("gradient_P", 0, P.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + pmb0->par_for("gradient_P", 0, P.GetDim(5) - 1, b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); double b1, b2, b3; @@ -321,10 +325,10 @@ TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_v auto pkg = md->GetMeshPointer()->packages.Get("B_Cleanup"); const auto use_normalized = pkg->Param("use_normalized_divb"); - // Cover ghost cells; maximize since both ops have stencil >1 - const IndexRange ib = md->GetBoundsI(IndexDomain::entire); - const IndexRange jb = md->GetBoundsJ(IndexDomain::entire); - const IndexRange kb = md->GetBoundsK(IndexDomain::entire); + // Updating interior is easier to follow -- BiCGStab will sync + const IndexRange ib = md->GetBoundsI(IndexDomain::interior); + const IndexRange jb = md->GetBoundsJ(IndexDomain::interior); + const IndexRange kb = md->GetBoundsK(IndexDomain::interior); auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); auto P = md->PackVariables(std::vector{p_var}); @@ -333,16 +337,17 @@ TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_v const int ndim = P.GetNdim(); - const IndexRange ib_l = IndexRange{ib.s, ib.e-1}; - const IndexRange jb_l = (ndim > 1) ? IndexRange{jb.s, jb.e-1} : jb; - const IndexRange kb_l = (ndim > 2) ? IndexRange{kb.s, kb.e-1} : kb; - const IndexRange ib_r = IndexRange{ib.s+1, ib.e-1}; - const IndexRange jb_r = (ndim > 1) ? IndexRange{jb.s+1, jb.e-1} : jb; - const IndexRange kb_r = (ndim > 2) ? IndexRange{kb.s+1, kb.e-1} : kb; + // P is defined on cell corners. We need enough to take + // grad -> center, then div -> corner, so one extra in each direction + const IndexRange ib_l = IndexRange{ib.s-1, ib.e+1}; + const IndexRange jb_l = (ndim > 1) ? IndexRange{jb.s-1, jb.e+1} : jb; + const IndexRange kb_l = (ndim > 2) ? IndexRange{kb.s-1, kb.e+1} : kb; + // The div computes corner i,j,k, so needs to be [0,N+1] to cover all physical corners + const IndexRange ib_r = IndexRange{ib.s, ib.e+1}; + const IndexRange jb_r = (ndim > 1) ? IndexRange{jb.s, jb.e+1} : jb; + const IndexRange kb_r = (ndim > 2) ? IndexRange{kb.s, kb.e+1} : kb; // dB = grad(p), defined at cell centers - // Need a halo one zone *left*, as corner_div will read that. - // Therefore B's ghosts need to be up to date! pmb0->par_for("gradient_P", 0, P.GetDim(5) - 1, kb_l.s, kb_l.e, jb_l.s, jb_l.e, ib_l.s, ib_l.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { const auto& G = P.GetCoords(b); @@ -354,6 +359,32 @@ TaskStatus B_Cleanup::CornerLaplacian(MeshData* md, const std::string& p_v } ); + // Replace ghost zone calculations with strict boundary conditions + // Only necessary in j so far, but there's no reason it shouldn't be done in i,k + for (int i=0; i < md->GetMeshPointer()->GetNumMeshBlocksThisRank(); i++) { + auto rc = md->GetBlockData(i); + auto pmb = rc->GetBlockPointer(); + auto dB_block = rc->PackVariables(std::vector{"dB"}); + if (pmb->boundary_flag[BoundaryFace::inner_x2] == BoundaryFlag::user) { + pmb->par_for("dB_boundary", kb_l.s, kb_l.e, ib_l.s, ib_l.e, + KOKKOS_LAMBDA (const int &k, const int &i) { + dB_block(V1, k, jb.s-1, i) = dB_block(V1, k, jb.s, i); + dB_block(V2, k, jb.s-1, i) = -dB_block(V2, k, jb.s, i); + dB_block(V3, k, jb.s-1, i) = dB_block(V3, k, jb.s, i); + } + ); + } + if (pmb->boundary_flag[BoundaryFace::outer_x2] == BoundaryFlag::user) { + pmb->par_for("dB_boundary", kb_l.s, kb_l.e, ib_l.s, ib_l.e, + KOKKOS_LAMBDA (const int &k, const int &i) { + dB_block(V1, k, jb.e+1, i) = dB_block(V1, k, jb.e, i); + dB_block(V2, k, jb.e+1, i) = -dB_block(V2, k, jb.e, i); + dB_block(V3, k, jb.e+1, i) = dB_block(V3, k, jb.e, i); + } + ); + } + } + // lap = div(dB), defined at cell corners pmb0->par_for("laplacian_dB", 0, lap.GetDim(5) - 1, kb_r.s, kb_r.e, jb_r.s, jb_r.e, ib_r.s, ib_r.e, KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { diff --git a/kharma/b_cleanup/bicgstab_solver.hpp b/kharma/b_cleanup/bicgstab_solver.hpp new file mode 100644 index 00000000..dc4fe559 --- /dev/null +++ b/kharma/b_cleanup/bicgstab_solver.hpp @@ -0,0 +1,676 @@ +//======================================================================================== +// (C) (or copyright) 2022. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== +#ifndef SOLVERS_BICGSTAB_SOLVER_HPP_ +#define SOLVERS_BICGSTAB_SOLVER_HPP_ + +#include +#include +#include + +#include "mesh/mesh.hpp" +#include "interface/mesh_data.hpp" +#include "interface/meshblock_data.hpp" +#include "interface/state_descriptor.hpp" +#include "kokkos_abstraction.hpp" +#include "solvers/solver_utils.hpp" +#include "tasks/task_id.hpp" +#include "tasks/task_list.hpp" + +namespace parthenon { + +namespace solvers { + +struct BiCGStabCounter { + static int global_num_bicgstab_solvers; +}; + +template +class BiCGStabSolver : BiCGStabCounter { + public: + BiCGStabSolver() = default; + BiCGStabSolver(StateDescriptor *pkg, const Real rel_error_tol_in, + const Real abs_error_tol_in, const SparseMatrixAccessor &sp, + const std::vector &aux_vars = {}, + std::vector user_flags={}) + : rel_error_tol(rel_error_tol_in), abs_error_tol(abs_error_tol_in), + sp_accessor(sp), max_iters(pkg->Param("bicgstab_max_iterations")), + check_interval(pkg->Param("bicgstab_check_interval")), + fail_flag(pkg->Param("bicgstab_abort_on_fail")), + warn_flag(pkg->Param("bicgstab_warn_on_fail")), aux_vars(aux_vars) { + Init(pkg, user_flags); + } + std::vector SolverState() const { + std::vector vars{spm_name, rhs_name, res, res0, vk, pk, tk, temp}; + vars.insert(vars.end(), aux_vars.begin(), aux_vars.end()); + return vars; + } + std::string label() const { + std::string lab; + for (const auto &s : SolverState()) + lab += s; + return lab; + } + + TaskID CreateTaskList(const TaskID &begin, const int i, TaskRegion &tr, + std::shared_ptr> md, + std::shared_ptr> mout) { + auto &solver = tr[i].AddIteration(solver_name); + solver.SetMaxIterations(max_iters); + solver.SetCheckInterval(check_interval); + solver.SetFailWithMaxIterations(fail_flag); + solver.SetWarnWithMaxIterations(warn_flag); + return CreateTaskList(begin, i, tr, solver, md, mout); + } + + using FMatVec = std::function *, const std::string &, + MeshData *, const std::string &)>; + using FScale = std::function *, const std::string &)>; + FMatVec user_MatVec; + FMatVec user_pre_fluxcor; + FMatVec user_precomm_MatVec; + FScale user_precomm_scale; + FScale user_postcomm_scale; + + std::vector aux_vars; + + private: + void Init(StateDescriptor *pkg, std::vector user_flags) { + // create vectors used internally by the solver + spm_name = pkg->Param("spm_name"); + sol_name = pkg->Param("sol_name"); + rhs_name = pkg->Param("rhs_name"); + + const std::string bicg_id(std::to_string(global_num_bicgstab_solvers)); + solver_name = "internal_bicgstab_" + bicg_id; + + res0 = "res_0" + bicg_id; + std::vector base_flags({Metadata::Node, Metadata::OneCopy}); + base_flags.insert(base_flags.end(), user_flags.begin(), user_flags.end()); + auto meta = Metadata(base_flags); + pkg->AddField(res0, meta); + + vk = "vk" + bicg_id; + tk = "tk" + bicg_id; + auto flux_flags = base_flags; + flux_flags.push_back(Metadata::WithFluxes); + meta = Metadata(flux_flags); + pkg->AddField(vk, meta); + pkg->AddField(tk, meta); + + res = "res" + bicg_id; + pk = "pk" + bicg_id; + temp = "temp" + bicg_id; + auto ghost_flags = base_flags; + ghost_flags.push_back(Metadata::FillGhost); + meta = Metadata(ghost_flags); + pkg->AddField(pk, meta); + pkg->AddField(res, meta); + pkg->AddField(temp, meta); + + global_num_bicgstab_solvers++; + } + + TaskID CreateTaskList(const TaskID &begin, const int i, TaskRegion &tr, + IterativeTasks &solver, std::shared_ptr> md, + std::shared_ptr> mout) { + using Solver_t = BiCGStabSolver; + using MD_t = MeshData; + TaskID none(0); + TaskList &tl = tr[i]; + RegionCounter reg(solver_name); + + // initialize some shared state + bicgstab_cntr = 0; + global_res0.val = 0.0; + global_res.val = 0.0; + rhoi.val = 0.0; + r0_dot_vk.val = 0.0; + t_dot_s.val = 0.0; + t_dot_t.val = 0.0; + + auto MatVec = [this](auto &task_list, const TaskID &init_depend, + std::shared_ptr> &spmd, + const std::string &name_in, const std::string &name_out) { + auto precom = init_depend; + auto vec_name = name_in; + if (this->user_precomm_MatVec) { + precom = task_list.AddTask(init_depend, this->user_precomm_MatVec, spmd.get(), + name_in, spmd.get(), this->temp); + vec_name = this->temp; + } + auto precom2 = precom; + if (this->user_precomm_scale) { + precom2 = + task_list.AddTask(precom, this->user_precomm_scale, spmd.get(), vec_name); + } + + // TODO(BSP) this is AddBoundaryExchangeTasks, would use that except it's not + // templated for special iterative lists + auto dependency = precom2; + auto &tl = task_list; + auto &md = spmd; + const auto any = BoundaryType::any; + auto send = tl.AddTask(dependency, SendBoundBufs, md); + auto recv = tl.AddTask(dependency, ReceiveBoundBufs, md); + auto set = tl.AddTask(recv, SetBounds, md); + + auto pro = set; + if (md->GetMeshPointer()->multilevel) { + auto cbound = tl.AddTask(set, ApplyBoundaryConditionsOnCoarseOrFineMD, md, true); + pro = tl.AddTask(cbound, ProlongateBounds, md); + } + auto fbound = tl.AddTask(pro, ApplyBoundaryConditionsOnCoarseOrFineMD, md, false); + auto boundaries = fbound; + + auto postcomm = boundaries; + if (this->user_postcomm_scale) { + postcomm = + task_list.AddTask(boundaries, this->user_postcomm_scale, spmd.get(), vec_name); + } + + auto update_rhs = postcomm; + if (this->user_MatVec) { + auto preflx = boundaries; + if (this->user_pre_fluxcor) { + auto calc_flx = task_list.AddTask(boundaries, this->user_pre_fluxcor, spmd.get(), + vec_name, spmd.get(), name_out); + auto send_flx = + task_list.AddTask(calc_flx, parthenon::LoadAndSendFluxCorrections, spmd); + auto recv_flx = + task_list.AddTask(calc_flx, parthenon::ReceiveFluxCorrections, spmd); + preflx = task_list.AddTask(recv_flx, parthenon::SetFluxCorrections, spmd); + } + update_rhs = task_list.AddTask(preflx, this->user_MatVec, spmd.get(), vec_name, + spmd.get(), name_out); + } else { + update_rhs = task_list.AddTask(boundaries, &Solver_t::MatVec, this, spmd.get(), + name_in, name_out); + } + return update_rhs; + }; + + auto get_init = MatVec(tl, begin, md, rhs_name, vk); + + auto init_bicgstab = tl.AddTask(get_init, &Solver_t::InitializeBiCGStab, this, + md.get(), mout.get(), &global_res0.val); + tr.AddRegionalDependencies(reg.ID(), i, init_bicgstab); + // global reduction for initial residual + auto start_global_res0 = + (i == 0 ? tl.AddTask(init_bicgstab, &AllReduce::StartReduce, &global_res0, + MPI_SUM) + : init_bicgstab); + auto finish_global_res0 = + tl.AddTask(start_global_res0, &AllReduce::CheckReduce, &global_res0); + tr.AddRegionalDependencies(reg.ID(), i, finish_global_res0); + + // 1. \hat{r}_0 \cdot r_{i-1} + auto get_rhoi = solver.AddTask(init_bicgstab, &Solver_t::DotProduct, this, + md.get(), res0, res, &rhoi.val); + tr.AddRegionalDependencies(reg.ID(), i, get_rhoi); + auto start_global_rhoi = + (i == 0 ? solver.AddTask(get_rhoi, &AllReduce::StartReduce, &rhoi, MPI_SUM) + : get_rhoi); + auto finish_global_rhoi = + solver.AddTask(start_global_rhoi, &AllReduce::CheckReduce, &rhoi); + + // 2. \beta = (rho_i/rho_{i-1}) (\alpha / \omega_{i-1}) + // 3. p_i = r_{i-1} + \beta (p_{i-1} - \omega_{i-1} v_{i-1}) + auto update_pk = + solver.AddTask(finish_global_rhoi, &Solver_t::Compute_pk, this, md.get()); + + // 4. v = A p + auto get_v = MatVec(solver, update_pk, md, pk, vk); + + // 5. alpha = rho_i / (\hat{r}_0 \cdot v_i) [Actually just calculate \hat{r}_0 \cdot + // v_i] + auto get_r0dotv = solver.AddTask(get_v, &Solver_t::DotProduct, this, md.get(), + res0, vk, &r0_dot_vk.val); + tr.AddRegionalDependencies(reg.ID(), i, get_r0dotv); + auto start_global_r0dotv = + (i == 0 ? solver.AddTask(get_r0dotv, &AllReduce::StartReduce, &r0_dot_vk, + MPI_SUM) + : get_r0dotv); + auto finish_global_r0dotv = + solver.AddTask(start_global_r0dotv, &AllReduce::CheckReduce, &r0_dot_vk); + // alpha is actually updated in this next task + + // 6. h = x_{i-1} + alpha p [Really updates x_i] + // 7. check for convergence [Not actually done] + // 8. s = r_{i-1} - alpha v + auto get_s = solver.AddTask(finish_global_r0dotv, &Solver_t::Update_h_and_s, + this, md.get(), mout.get()); + + // 9. t = A s + auto get_t = MatVec(solver, get_s, md, res, tk); + + // 10. omega = (t \cdot s) / (t \cdot t) + auto get_tdots = solver.AddTask(get_t, &Solver_t::OmegaDotProd, this, md.get(), + &t_dot_s.val, &t_dot_t.val); + tr.AddRegionalDependencies(reg.ID(), i, get_tdots); + auto start_global_tdots = + (i == 0 + ? solver.AddTask(get_tdots, &AllReduce::StartReduce, &t_dot_s, MPI_SUM) + : get_tdots); + auto finish_global_tdots = + solver.AddTask(start_global_tdots, &AllReduce::CheckReduce, &t_dot_s); + auto start_global_tdott = + (i == 0 + ? solver.AddTask(get_tdots, &AllReduce::StartReduce, &t_dot_t, MPI_SUM) + : get_tdots); + auto finish_global_tdott = + solver.AddTask(start_global_tdott, &AllReduce::CheckReduce, &t_dot_t); + // omega is actually updated in this next task + + // 11. update x and residual + auto update_x = solver.AddTask(finish_global_tdots | finish_global_tdott, + &Solver_t::Update_x_res, this, md.get(), + mout.get(), &global_res.val); + tr.AddRegionalDependencies(reg.ID(), i, update_x); + auto start_global_res = + (i == 0 ? solver.AddTask(update_x, &AllReduce::StartReduce, &global_res, + MPI_SUM) + : update_x); + auto finish_global_res = + solver.AddTask(start_global_res, &AllReduce::CheckReduce, &global_res); + + // 12. check for convergence + auto check = solver.SetCompletionTask(finish_global_res, &Solver_t::CheckConvergence, + this, i, true); + tr.AddGlobalDependencies(reg.ID(), i, check); + + return check; + } + + public: + template + TaskStatus InitializeBiCGStab(T *u, T *du, Real *gres0) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + std::vector vars({res, res0, vk, pk, rhs_name}); + const auto &v = u->PackVariables(vars, imap); + const int ires = imap[res].first; + const int ires0 = imap[res0].first; + const int ivk = imap[vk].first; + const int ipk = imap[pk].first; + const int irhs = imap[rhs_name].first; + + const auto &dv = du->PackVariables(std::vector({sol_name})); + + rhoi_old = 1.0; + alpha_old = 1.0; + omega_old = 1.0; + Real err(0); + const Real fac0 = 0.0; + const Real fac = 0.0; + par_reduce( + loop_pattern_mdrange_tag, "initialize bicgstab", DevExecSpace(), 0, + v.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lerr) { + // initialize guess for solution + dv(b, 0, k, j, i) = fac * v(b, irhs, k, j, i); + + v(b, ires, k, j, i) = v(b, irhs, k, j, i) - fac * v(b, ivk, k, j, i); + v(b, ires0, k, j, i) = v(b, irhs, k, j, i) - fac0 * v(b, ivk, k, j, i); + + v(b, ivk, k, j, i) = 0.0; + v(b, ipk, k, j, i) = 0.0; + + lerr += v(b, irhs, k, j, i) * v(b, irhs, k, j, i); + }, + Kokkos::Sum(err)); + *gres0 += err; + return TaskStatus::complete; + } + + template + TaskStatus update_r(T *u) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + std::vector vars({res, rhs_name}); + const auto &v = u->PackVariables(vars, imap); + const int ires = imap[res].first; + const int irhs = imap[rhs_name].first; + + par_for( + loop_pattern_mdrange_tag, "initialize bicgstab", DevExecSpace(), 0, + v.GetDim(5) - 1, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + v(b, ires, k, j, i) = v(b, irhs, k, j, i) - v(b, ires, k, j, i); + }); + return TaskStatus::complete; + } + + template + TaskStatus DotProduct(T *u, const std::string &vec1, const std::string &vec2, + Real *reduce_sum) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + auto &v = u->PackVariables(std::vector({vec1, vec2})); + + Real gsum(0); + par_reduce( + loop_pattern_mdrange_tag, "DotProduct", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, + kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lsum) { + lsum += v(b, 0, k, j, i) * v(b, 1, k, j, i); + }, + Kokkos::Sum(gsum)); + *reduce_sum += gsum; + // printf("DotProduct: %s dot %s = %e (%e)\n", vec1.c_str(), vec2.c_str(), + // *reduce_sum, gsum); + return TaskStatus::complete; + } + + template + TaskStatus Compute_pk(T *u) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + auto &v = u->PackVariables(std::vector({pk, res, vk, res0}), imap); + const int ipk = imap[pk].first; + const int ires = imap[res].first; + const int ires0 = imap[res0].first; + const int ivk = imap[vk].first; + + const Real beta = (rhoi.val / rhoi_old) * (alpha_old / omega_old); + bool reset = false; + // if (std::abs(rhoi.val) < 1.e-8) { + // // Reset + // printf("Resetting (r_{i-1}, r_0) = %e res = %e \n", rhoi.val, res_old); + // rhoi.val = res_old; // this should be the norm of the old residual, which we are + // resetting to reset = true; + // } + // printf("Compute_pk: rho_i = %e rho_{i-1} = %e alpha_old = %e omega_old = %e beta = + // %e\n", rhoi.val, rhoi_old, alpha_old, omega_old, beta); rhoi_old = rhoi.val; + const Real w_o = omega_old; + par_for( + DEFAULT_LOOP_PATTERN, "compute pk", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, + kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + v(b, ipk, k, j, i) = v(b, ires, k, j, i) + + beta * (v(b, ipk, k, j, i) - w_o * v(b, ivk, k, j, i)); + if (reset) { + v(b, ipk, k, j, i) = v(b, ires, k, j, i); + v(b, ires0, k, j, i) = v(b, ires, k, j, i); + } + }); + return TaskStatus::complete; + } + + template + TaskStatus MatVec(T *u, const std::string &in_vec, const std::string &out_vec) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + auto &v = + u->PackVariables(std::vector({in_vec, out_vec, spm_name}), imap); + const int iin = imap[in_vec].first; + const int iout = imap[out_vec].first; + const int isp_lo = imap[spm_name].first; + const int isp_hi = imap[spm_name].second; + SparseMatrixAccessor &r_sp_accessor = sp_accessor; + + par_for( + DEFAULT_LOOP_PATTERN, "MatVec", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, kb.e, + jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + v(b, iout, k, j, i) = + r_sp_accessor.MatVec(v, isp_lo, isp_hi, v, iin, b, k, j, i); + }); + // printf("MatVec: in_vec = %s out_vec = %s spm = %s\n", in_vec.c_str(), + // out_vec.c_str(), spm_name.c_str()); + return TaskStatus::complete; + } + + template + TaskStatus Update_h_and_s(T *u, T *du) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + auto &v = u->PackVariables(std::vector({res, pk, vk}), imap); + auto &dv = du->PackVariables(std::vector({sol_name})); + const int ires = imap[res].first; + const int ipk = imap[pk].first; + const int ivk = imap[vk].first; + + Real alpha = rhoi.val / r0_dot_vk.val; + // printf("alpha = %e rho = %e (v, r_0) = %e\n", alpha, rhoi.val, r0_dot_vk.val); + if (std::abs(r0_dot_vk.val) < 1.e-200) alpha = 0.0; + par_for( + DEFAULT_LOOP_PATTERN, "Update_h", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, kb.e, + jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + dv(b, 0, k, j, i) += alpha * v(b, ipk, k, j, i); + v(b, ires, k, j, i) -= alpha * v(b, ivk, k, j, i); + }); + return TaskStatus::complete; + } + + template + TaskStatus Update_h(T *u, T *du) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + auto &v = u->PackVariables(std::vector({pk})); + auto &dv = du->PackVariables(std::vector({sol_name})); + Real alpha = rhoi.val / r0_dot_vk.val; + // printf("Update_h: r0_dot_vk = %e rhoi = %e alpha = %e\n", r0_dot_vk.val, rhoi.val, + // alpha); + par_for( + DEFAULT_LOOP_PATTERN, "Update_h", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, kb.e, + jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + dv(b, 0, k, j, i) += alpha * v(b, 0, k, j, i); + }); + return TaskStatus::complete; + } + + template + TaskStatus Update_s(T *u) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + auto &v = u->PackVariables(std::vector({res, vk}), imap); + const int ires = imap[res].first; + const int ivk = imap[vk].first; + Real alpha = rhoi.val / r0_dot_vk.val; + // printf("Update_s: r0_dot_vk = %e rhoi = %e alpha = %e\n", r0_dot_vk.val, rhoi.val, + // alpha); + par_for( + DEFAULT_LOOP_PATTERN, "Update_s", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, kb.e, + jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i) { + v(b, ires, k, j, i) -= alpha * v(b, ivk, k, j, i); + }); + return TaskStatus::complete; + } + + template + TaskStatus OmegaDotProd(T *u, Real *t_dot_s, Real *t_dot_t) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + auto &v = u->PackVariables(std::vector({tk, res})); + + // TODO(JCD): these should probably be merged + Real ts_sum(0); + par_reduce( + loop_pattern_mdrange_tag, "tk dot sk", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, + kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lsum) { + lsum += v(b, 0, k, j, i) * v(b, 1, k, j, i); + }, + Kokkos::Sum(ts_sum)); + *t_dot_s += ts_sum; + + Real tt_sum(0); + par_reduce( + loop_pattern_mdrange_tag, "tk dot sk", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, + kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lsum) { + lsum += v(b, 0, k, j, i) * v(b, 0, k, j, i); + }, + Kokkos::Sum(tt_sum)); + *t_dot_t += tt_sum; + // printf("OmegaDotProd: t_dot_s = %e (%e) t_dot_t = %e (%e)\n", *t_dot_s, ts_sum, + // *t_dot_t, tt_sum); + return TaskStatus::complete; + } + + template + TaskStatus Update_x_res(T *u, T *du, Real *gres) { + const auto &ibi = u->GetBoundsI(IndexDomain::interior); + const auto &jbi = u->GetBoundsJ(IndexDomain::interior); + const auto &kbi = u->GetBoundsK(IndexDomain::interior); + const int ndim = u->GetMeshPointer()->ndim; + const auto ib = IndexRange{ibi.s, ibi.e + (ndim > 0)}; + const auto jb = IndexRange{jbi.s, jbi.e + (ndim > 1)}; + const auto kb = IndexRange{kbi.s, kbi.e + (ndim > 2)}; + + PackIndexMap imap; + auto &v = u->PackVariables(std::vector({res, tk}), imap); + const int ires = imap[res].first; + const int itk = imap[tk].first; + auto &dv = du->PackVariables(std::vector({sol_name})); + Real omega = t_dot_s.val / t_dot_t.val; + if (std::abs(t_dot_t.val) < 1.e-200) omega = 0.0; + Real err(0); + par_reduce( + loop_pattern_mdrange_tag, "Update_x", DevExecSpace(), 0, v.GetDim(5) - 1, kb.s, + kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA(const int b, const int k, const int j, const int i, Real &lerr) { + dv(b, 0, k, j, i) += omega * v(b, ires, k, j, i); + v(b, ires, k, j, i) -= omega * v(b, itk, k, j, i); + lerr += v(b, ires, k, j, i) * v(b, ires, k, j, i); + }, + Kokkos::Sum(err)); + *gres += err; + return TaskStatus::complete; + } + + TaskStatus CheckConvergence(const int &i, bool report) { + if (i != 0) return TaskStatus::complete; + bicgstab_cntr++; + global_res.val = std::sqrt(global_res.val); + if (bicgstab_cntr == 1) global_res0.val = std::sqrt(global_res0.val); + + // printf("rhoi: %e r0_dot_vk: %e t_dot_t: %e\n", rhoi.val, r0_dot_vk.val, + // t_dot_s.val); + // Update global scalars + rhoi_old = rhoi.val; + alpha_old = rhoi.val / r0_dot_vk.val; + omega_old = t_dot_s.val / t_dot_t.val; + res_old = global_res.val; + + bool converged = std::abs(global_res.val / global_res0.val) < rel_error_tol + || std::abs(global_res.val) < abs_error_tol; + + bool stop = bicgstab_cntr == max_iters; + if (std::abs(alpha_old) < 1.e-8 && std::abs(omega_old) < 1.e-8) stop = true; + if (bicgstab_cntr % check_interval == 0) { + if (Globals::my_rank == 0) { + std::cout << " its= " << bicgstab_cntr << " rho= " << rhoi_old + << " alpha= " << alpha_old << " omega= " << omega_old + << " relative-res: " << global_res.val / global_res0.val + << " absolute-res: " << global_res.val + << " absolute-res0: " << global_res0.val << " relerr-tol: " << rel_error_tol + << " abserr-tol: " << abs_error_tol + << std::endl; + } + } + + global_res.val = 0.0; + rhoi.val = 0.0; + r0_dot_vk.val = 0.0; + t_dot_s.val = 0.0; + t_dot_t.val = 0.0; + + return converged || stop ? TaskStatus::complete : TaskStatus::iterate; + } + + private: + Real rel_error_tol, abs_error_tol; + SparseMatrixAccessor sp_accessor; + int max_iters, check_interval, bicgstab_cntr; + bool fail_flag, warn_flag; + std::string spm_name, sol_name, rhs_name, res, res0, vk, pk, tk, temp, solver_name; + + Real rhoi_old, alpha_old, omega_old, res_old; + + AllReduce global_res0; + AllReduce global_res; + AllReduce rhoi; + AllReduce r0_dot_vk; + AllReduce t_dot_s; + AllReduce t_dot_t; +}; + +} // namespace solvers + +} // namespace parthenon + +#endif // SOLVERS_BICGSTAB_SOLVER_HPP_ diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index a256dd1f..cb529f32 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -34,6 +34,7 @@ #include "boundaries.hpp" #include "decs.hpp" +#include "domain.hpp" #include "kharma.hpp" #include "flux.hpp" #include "flux_functions.hpp" @@ -251,9 +252,13 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD pkg->KBoundaries[bface](rc, coarse); EndFlag(); - // Exit immediately if we're syncing emf alone - // TODO can we check name? - if (rc->GetVariableVector().size() == 1) { + // This will now be called in 2 places we might not expect, + // where we still may want to control the physical bounds: + // 1. Syncing only the EMF during runs with CT + // 2. Syncing boundaries while solving for B field + // this generally guards against anytime we can't do the below + PackIndexMap prims_map; + if (GRMHD::PackMHDPrims(rc.get(), prims_map).GetDim(4) == 0) { EndFlag(); return; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 3a9833d0..30e09004 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -155,6 +155,13 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) // Cleanup is applied to conserved variables B_Cleanup::CleanupDivergence(md); + + if (pin->GetOrAddBoolean("b_cleanup", "output_after_cleanup", false)) { + auto tm = SimTime(0., 0., 0, 0, 0, 0, 0.); + auto pouts = std::make_unique(pmesh, pin, &tm); + pouts->MakeOutputs(pmesh, pin, &tm, SignalHandler::OutputSignal::now); + } + } // If PtoU was called before the B field was initialized or corrected, diff --git a/pars/restarts/resize_restart.par b/pars/restarts/resize_restart.par index efdecd7d..6938a583 100644 --- a/pars/restarts/resize_restart.par +++ b/pars/restarts/resize_restart.par @@ -40,12 +40,12 @@ use_dt = false skip_b_cleanup = false -rel_tolerance = 1. -abs_tolerance = 1.e-14 -check_interval = 100 -max_iterations = 1000000 -# See b_cleanup.cpp -sor_factor = 20.3 +# Disable exiting on rel tolerance +rel_tolerance = 1.e-20 +# This tolerance is quite small, increase if no convergence +abs_tolerance = 1.e-9 +check_interval = 20 +max_iterations = 10000 rho_min_geom = 1e-6 diff --git a/tests/resize/run.sh b/tests/resize/run.sh index f62a31f6..07e237d8 100755 --- a/tests/resize/run.sh +++ b/tests/resize/run.sh @@ -15,10 +15,10 @@ pyharm convert --to_restart torus.out0.final.phdf sleep 1 $KHARMADIR/run.sh -i $KHARMADIR/pars/restarts/resize_restart.par resize_restart/fname=torus.out0.final.h5 \ - b_cleanup/always_solve=1 parthenon/time/nlim=5 \ - >log_resize_2.txt 2>&1 - -mv torus.out0.final.phdf torus.out0.final.restart.phdf + b_cleanup/abs_tolerance=1e-7 b_cleanup/always_solve=1 parthenon/time/nlim=1 \ + parthenon/output0/single_precision_output=false >log_resize_2.txt 2>&1 # Check divB on the re-meshed output -pyharm check-basics torus.out0.final.restart.phdf +# The tolerance is based on observed behavior with the high tolerance above +# production sims should set abs_tolerance of significantly less +pyharm check-basics --allowed_divb=2e-10 resize_restart.out0.00000.phdf From 26cf7f1e87075d9e1e915e1dc21b2e0d40d919a5 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 5 Oct 2023 14:45:54 -0600 Subject: [PATCH 150/219] Fix some EMHD stuff Not all flag names were updated "EMHD"->"EMHDVar", I suspect a merge somewhere regressed that. Also fix some more scripts --- kharma/emhd/emhd.cpp | 10 ++++++---- tests/bz_monopole/run.sh | 6 +++--- tests/conducting_atmosphere/run.sh | 4 ++-- tests/emhdshock/check.py | 14 +++++++------- tests/emhdshock/check.sh | 7 +------ 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index 65ea482b..b16fd7d2 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -128,7 +128,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

emhd_flags = {Metadata::Cell, Metadata::GetUserFlag("Implicit"), Metadata::GetUserFlag("EMHD")}; + std::vector emhd_flags = {Metadata::Cell, Metadata::GetUserFlag("Implicit"), Metadata::GetUserFlag("EMHDVar")}; auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); flags_prim.insert(flags_prim.end(), emhd_flags.begin(), emhd_flags.end()); @@ -185,8 +185,9 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); + // Get only relevant cons, but all prims as we need the Lorentz factor PackIndexMap prims_map, cons_map; - auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHD"), Metadata::Conserved}, cons_map); + auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); @@ -217,9 +218,10 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); + // Get only relevant cons, but all prims as we need the Lorentz factor PackIndexMap prims_map, cons_map; - auto U_E = rc->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); - auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + auto U_E = rc->PackVariables({Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); + auto P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); const auto& G = pmb->coords; diff --git a/tests/bz_monopole/run.sh b/tests/bz_monopole/run.sh index 2854c763..3ec7bfab 100755 --- a/tests/bz_monopole/run.sh +++ b/tests/bz_monopole/run.sh @@ -6,13 +6,13 @@ BASE=../.. exit_code=0 # Full run to test stability to completion -$BASE/run.sh -i $BASE/pars/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 #|| exit_code=$? +$BASE/run.sh -i $BASE/pars/tests/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 || exit_code=$? # At *least* check divB pyharm-check-basics bz_monopole.out0.final.phdf || exit_code=$? # Take 1 step to look for early signs of non-fatal instabilities -$BASE/run.sh -i $BASE/pars/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 #|| exit_code=$? +$BASE/run.sh -i $BASE/pars/tests/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 #|| exit_code=$? -# Check is for plots only! +# This just makes plots, it doesn't check anything python ./check.py diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index bb62569c..1f8b82f8 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -13,7 +13,7 @@ conv_2d() { for res in "${RES_LIST[@]}" do cp conducting_atmosphere_${res}_default/atmosphere_soln_*.txt . - $BASE/run.sh -n 1 -i $BASE/pars/conducting_atmosphere.par debug/verbose=1 \ + $BASE/run.sh -n 1 -i ./conducting_atmosphere.par debug/verbose=1 \ parthenon/time/tlim=200 parthenon/output0/dt=1000000 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$res parthenon/meshblock/nx2=$res parthenon/meshblock/nx3=1 \ @@ -35,4 +35,4 @@ conv_2d() { } ALL_RES="64,128,256,512" -conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "in 2D, WENO5" +conv_2d emhd2d_weno driver/reconstruction=weno5 "in 2D, WENO5" diff --git a/tests/emhdshock/check.py b/tests/emhdshock/check.py index 57678fcb..c557d6ad 100644 --- a/tests/emhdshock/check.py +++ b/tests/emhdshock/check.py @@ -9,7 +9,7 @@ if __name__=='__main__': outputdir = './' - kharmadir = '/home/vdhruv2/kharma' + kharmadir = '../../' RES = [int(r) for r in sys.argv[1].split(",")] CONDUCTION = 1 @@ -25,13 +25,13 @@ for r, res in enumerate(RES): # load analytic result - rho_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_rho.txt')) - u_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_u.txt')) - u1_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_u1.txt')) + rho_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_rho.txt')) + u_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_u.txt')) + u1_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_u1.txt')) if CONDUCTION: - q_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_q.txt')) - dP_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_dP.txt')) - x_analytic = np.loadtxt(os.path.join(kharmadir, 'kharma/prob/emhd/', 'shock_soln_{}_default'.format(res), 'shock_soln_xCoords.txt')) + q_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_q.txt')) + dP_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_dP.txt')) + x_analytic = np.loadtxt(os.path.join(outputdir, 'shock_soln_{}_default'.format(res), 'shock_soln_xCoords.txt')) # load code data dfile = h5py.File('emhd_1d_{}_end.h5'.format(res), 'r') diff --git a/tests/emhdshock/check.sh b/tests/emhdshock/check.sh index b62e1ae7..fbfa821d 100755 --- a/tests/emhdshock/check.sh +++ b/tests/emhdshock/check.sh @@ -2,16 +2,11 @@ # Run checks against analytic result for specified tests -. /home/vdhruv2/anaconda3/etc/profile.d/conda.sh -conda activate pyharm - # Very small amplitude by default, preserve double precision -~/pyHARM/scripts/pyharm-convert --double *.phdf +pyharm convert --double *.phdf RES1D="256,512,1024,2048" -conda activate base - fail=0 python3 check.py $RES1D "EMHD shock" emhd1d || fail=1 From 61de2e2ff2d167027bb68937269ebd452815918b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 11:27:08 -0500 Subject: [PATCH 151/219] CI: try to fix CPU build --- machines/bp.sh | 3 ++- scripts/ci/cpu.yml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/machines/bp.sh b/machines/bp.sh index c746b042..47b52cd1 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -73,6 +73,7 @@ if [[ $HOST == "cinnabar"* ]]; then module purge # Handle modules inside this script HOST_ARCH="HSW" # This won't change DEVICE_ARCH="TURING75" + NPROC=56 # Runtime MPI_NUM_PROCS=1 @@ -93,7 +94,7 @@ if [[ $HOST == "cinnabar"* ]]; then C_NATIVE="gcc" CXX_NATIVE="g++" else - module load nvhpc + module load nvhpc/23.7 PREFIX_PATH="$HOME/libs/hdf5-nvhpc" C_NATIVE="nvc" CXX_NATIVE="nvc++" diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 1f13629d..9d383895 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -38,15 +38,15 @@ stages: build: stage: build variables: - NPROC: 4 + NPROC: 8 HOST_ARCH: NATIVE before_script: - dnf -y groupinstall "Development Tools" - - dnf -y install hostname environment-modules cmake mpich-devel fftw-devel + - dnf -y install hostname environment-modules cmake mpich-devel hdf5-mpich-devel fftw-devel - source /etc/profile - module load mpi/mpich-x86_64 script: - - ./make.sh clean hdf5 + - ./make.sh clean artifacts: paths: - kharma.* From 0a6585f9b6183ee9a65bfe226bd06aba03521e4f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 13:52:41 -0500 Subject: [PATCH 152/219] CI+tests stuff --- scripts/ci/cpu.yml | 8 +++++--- tests/clean_tests.sh | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 9d383895..b8d92246 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -3,7 +3,7 @@ image: quay.io/centos/centos:stream9 variables: - OMP_NUM_THREADS: 4 + OMP_NUM_THREADS: 8 OMP_PROC_BIND: "false" MPI_EXE: mpirun MPI_NUM_PROCS: 2 @@ -40,13 +40,15 @@ build: variables: NPROC: 8 HOST_ARCH: NATIVE + C_NATIVE: gcc + CXX_NATIVE: g++ before_script: - dnf -y groupinstall "Development Tools" - - dnf -y install hostname environment-modules cmake mpich-devel hdf5-mpich-devel fftw-devel + - dnf -y install hostname environment-modules cmake mpich-devel fftw-devel - source /etc/profile - module load mpi/mpich-x86_64 script: - - ./make.sh clean + - ./make.sh clean hdf5 artifacts: paths: - kharma.* diff --git a/tests/clean_tests.sh b/tests/clean_tests.sh index 1a6c541c..df0084e4 100755 --- a/tests/clean_tests.sh +++ b/tests/clean_tests.sh @@ -2,4 +2,7 @@ # Cleans all temporary/gitignore files from tests TEST_DIR=$(dirname "$(readlink -f "$0")") -rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,h5,hst,txt,png} ${TEST_DIR}/tilt_init/mks ${TEST_DIR}/*/frames_* ${TEST_DIR}/*/kharma_parsed_parameters* +rm -rf ${TEST_DIR}/*/*.{phdf,xdmf,rhdf,h5,hst,txt,png} \ + ${TEST_DIR}/tilt_init/mks \ + ${TEST_DIR}/*/frames_* \ + ${TEST_DIR}/*/kharma_parsed_parameters* From c42db133b61775028f8fe6fad6e5520661e13084 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 14:27:05 -0500 Subject: [PATCH 153/219] CI: fix pyharm via mamba --- scripts/ci/cpu.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index b8d92246..ebf2ec9a 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -25,9 +25,8 @@ default: - module load mpi/mpich-x86_64 - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba - eval "$(./bin/micromamba shell hook -s posix)" - - micromamba create -y -f environment.yml - - micromamba activate pyharm - - ./install.sh + - git clone https://github.com/AFD-Illinois/pyharm.git /pyharm && cd /pyharm + - micromamba create -y -f environment.yml && micromamba activate pyharm && ./install.sh # Tests can be executed in parallel stages: From 184f65b4835e5a8d5894ca3799ca71ffc31f3ea7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 14:57:18 -0500 Subject: [PATCH 154/219] CI: pyharm install fixes --- scripts/ci/cpu.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index ebf2ec9a..c5742bb2 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -25,8 +25,10 @@ default: - module load mpi/mpich-x86_64 - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba - eval "$(./bin/micromamba shell hook -s posix)" - - git clone https://github.com/AFD-Illinois/pyharm.git /pyharm && cd /pyharm - - micromamba create -y -f environment.yml && micromamba activate pyharm && ./install.sh + - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm + - micromamba create -y -f /pyharm/environment.yml + - micromamba activate pyharm + - cd /pyharm && ./install.sh # Tests can be executed in parallel stages: From 15b40265c5195db2750af64965c7faa27ad255f4 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 15:13:33 -0500 Subject: [PATCH 155/219] CI again --- scripts/ci/cpu.yml | 1 + scripts/ci/nvhpc.yml | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index c5742bb2..d8b5781a 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -29,6 +29,7 @@ default: - micromamba create -y -f /pyharm/environment.yml - micromamba activate pyharm - cd /pyharm && ./install.sh + - cd - # Tests can be executed in parallel stages: diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 61a5be9c..9b49e58b 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -24,14 +24,12 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - wget -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - - bash Miniforge3.sh -b -p "/home/conda" - - source "/home/conda/etc/profile.d/conda.sh" - - conda install h5py - - git clone https://github.com/AFD-Illinois/pyharm.git /home/pyharm - - conda activate - - cd /home/pyharm - - pip install --user . + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba + - eval "$(./bin/micromamba shell hook -s posix)" + - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm + - micromamba create -y -f /pyharm/environment.yml + - micromamba activate pyharm + - cd /pyharm && ./install.sh - cd - # Tests can be executed in parallel, From dd1a566d37ae3a535731b76829bbfca47b4e60ac Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 21:56:10 -0500 Subject: [PATCH 156/219] Rename AddMPIBoundarySync to AddBoundarySync --- kharma/driver/imex_step.cpp | 4 ++-- kharma/driver/kharma_driver.cpp | 37 +++++++++++++++++++-------------- kharma/driver/kharma_driver.hpp | 2 +- kharma/driver/kharma_step.cpp | 4 ++-- kharma/driver/simple_step.cpp | 2 +- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index ae74be89..b68a8d62 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -140,7 +140,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); - auto t_emf = KHARMADriver::AddMPIBoundarySync(t_emf_local, tl, md_emf_only); + auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); @@ -250,7 +250,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // but hasn't been tested to do so yet. auto t_floors = tl.AddTask(t_implicit, Packages::MeshApplyFloors, md_sub_step_final.get(), IndexDomain::interior); - KHARMADriver::AddMPIBoundarySync(t_floors, tl, md_sub_step_final); + KHARMADriver::AddBoundarySync(t_floors, tl, md_sub_step_final); } // Async Region: Any post-sync tasks. Fixups, timestep & AMR tagging. diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 64a67c30..6e9a4fdc 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -81,12 +81,16 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std std::string flux = pin->GetOrAddString("driver", "flux", "llf"); params.Add("use_hlle", (flux == "hlle")); - // Reconstruction scheme: plm, weno5, ppm... - // Allow an old parameter location - std::string grmhd_recon_option = pin->GetOrAddString("GRMHD", "reconstruction", "weno5"); - std::string recon = pin->GetOrAddString("driver", "reconstruction", grmhd_recon_option); + // Reconstruction scheme. TODO bunch more here, PPM esp... + std::vector allowed_vals = {"donor_cell", "linear_mc", "weno5"}; + std::string recon = pin->GetOrAddString("driver", "reconstruction", "weno5", allowed_vals); bool lower_edges = pin->GetOrAddBoolean("driver", "lower_edges", false); bool lower_poles = pin->GetOrAddBoolean("driver", "lower_poles", false); + if (lower_edges && lower_poles) + throw std::runtime_error("Cannot enable lowered reconstruction on edges and poles!"); + if ((lower_edges || lower_poles) && recon != "weno5") + throw std::runtime_error("Lowered reconstructions can only be enabled with weno5!"); + int stencil = 0; if (recon == "donor_cell") { params.Add("recon", KReconstruction::Type::donor_cell); @@ -97,21 +101,18 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std } else if (recon == "linear_mc") { params.Add("recon", KReconstruction::Type::linear_mc); stencil = 3; - } else if (recon == "weno5_lower_edges" || (recon == "weno5" && lower_edges)) { + } else if (recon == "weno5" && lower_edges) { params.Add("recon", KReconstruction::Type::weno5_lower_edges); stencil = 5; - } else if (recon == "weno5_lower_poles" || (recon == "weno5" && lower_poles)) { + } else if (recon == "weno5" && lower_poles) { params.Add("recon", KReconstruction::Type::weno5_lower_poles); stencil = 5; } else if (recon == "weno5") { params.Add("recon", KReconstruction::Type::weno5); stencil = 5; - } else { - std::cerr << "Reconstruction type not supported! Supported reconstructions:" << std::endl; - std::cerr << "donor_cell, linear_mc, weno5, weno5_lower_edges, weno5_lower_poles (linear_vl coming back soon!)" << std::endl; - throw std::invalid_argument("Unsupported reconstruction algorithm!"); - } + } // we only allow these options // Warn if using less than 3 ghost zones w/WENO etc, 2 w/Linear, etc. + // SMR/AMR independently requires an even number of zones, so we usually use 4 if (Globals::nghost < (stencil/2 + 1)) { throw std::runtime_error("Not enough ghost zones for specified reconstruction!"); } @@ -136,12 +137,16 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std bool prims_are_fundamental = driver_type != DriverType::kharma; params.Add("prims_are_fundamental", prims_are_fundamental); - // Finally, we set default flags for primitive and conserved variables - // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated + // Which variables we *actually send* via Parthenon/MPI may differ, however. + // Prolongation/restriction should happen on conserved vars, so we must sync + // those in multilevel meshes. If prims are funcamental but not sync'd, + // we "emulate" syncing them with PtoU/UtoP on boundaries bool sync_prims = prims_are_fundamental && (!pin->DoesParameterExist("parthenon/mesh", "numlevel") || pin->GetInteger("parthenon/mesh", "numlevel") == 1); params.Add("sync_prims", sync_prims); + // Finally, we set default flags for primitive and conserved variables + // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated if (sync_prims) { // If we're not in AMR, we can sync primitive variables directly params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); @@ -165,11 +170,11 @@ void KHARMADriver::AddFullSyncRegion(TaskCollection& tc, std::shared_ptr> &mc1) +TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &mc1) { Flag("AddBoundarySync"); auto t_start_sync = t_start; @@ -240,7 +245,7 @@ TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> &md) TaskCollection tc; auto tr = tc.AddRegion(1); - AddMPIBoundarySync(t_none, tr[0], md); + AddBoundarySync(t_none, tr[0], md); while (!tr.Execute()); EndFlag(); diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 18c943c5..5fe825c4 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -127,7 +127,7 @@ class KHARMADriver : public MultiStageDriver { * This sequence is used identically in several places, so it makes sense * to define once and use elsewhere. */ - static TaskID AddMPIBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &md); + static TaskID AddBoundarySync(const TaskID t_start, TaskList &tl, std::shared_ptr> &md); /** * Calculate the fluxes in each direction diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 86730060..7b22c80b 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -161,7 +161,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); - auto t_emf = KHARMADriver::AddMPIBoundarySync(t_emf_local, tl, md_emf_only); + auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); @@ -221,7 +221,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int md_sub_step_init.get(), md_sub_step_final.get()); } - KHARMADriver::AddMPIBoundarySync(t_copy_prims, tl, md_sync); + KHARMADriver::AddBoundarySync(t_copy_prims, tl, md_sync); } EndFlag(); diff --git a/kharma/driver/simple_step.cpp b/kharma/driver/simple_step.cpp index ea30839e..a21a2a84 100644 --- a/kharma/driver/simple_step.cpp +++ b/kharma/driver/simple_step.cpp @@ -125,7 +125,7 @@ TaskCollection KHARMADriver::MakeSimpleTaskCollection(BlockList_t &blocks, int s auto t_floors = tl.AddTask(t_UtoP, Packages::MeshApplyFloors, md_sub_step_final.get(), IndexDomain::interior); // Boundary sync: neighbors must be available for FixUtoP below - KHARMADriver::AddMPIBoundarySync(t_floors, tl, md_sub_step_final); + KHARMADriver::AddBoundarySync(t_floors, tl, md_sub_step_final); } // Async Region: Any post-sync tasks. Fixups, timestep & AMR tagging. From 8ff938390a33e6d2eaf9c459f7b0e61e5b5c5e49 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 22:04:20 -0500 Subject: [PATCH 157/219] Get viscosity-only EMHD working again --- kharma/emhd/emhd.hpp | 20 +++++++++++++++++--- kharma/flux/flux.cpp | 2 ++ kharma/flux/flux_functions.hpp | 9 ++++----- kharma/flux/get_flux.hpp | 9 +++++++-- kharma/types.hpp | 18 +++++++++++++++++- tests/bondi/run.sh | 9 ++++----- tests/restart/run.sh | 16 ++++++++-------- 7 files changed, 59 insertions(+), 24 deletions(-) diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index ac043876..90b0505d 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -74,6 +74,16 @@ class EMHD_parameters { Real kappa; Real eta; + void print() const + { + printf("EMHD Parameters:\n"); + printf("higher order: %d feedback: %d conduction: %d viscosity: %d\n", + higher_order_terms, feedback, conduction, viscosity); + printf("kappa: %g eta: %g tau: %g conduction_a: %g viscosity_a: %g \n", + kappa, eta, tau, conduction_alpha, viscosity_alpha); + // TODO closuretype + } + }; /** @@ -109,7 +119,7 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse); */ inline EMHD_parameters GetEMHDParameters(Packages_t& packages) { - EMHD::EMHD_parameters emhd_params_tmp; + EMHD::EMHD_parameters emhd_params_tmp = {0}; if (packages.AllPackages().count("EMHD")) { emhd_params_tmp = packages.Get("EMHD")->Param("emhd_params"); } @@ -257,7 +267,9 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Local& FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, j, i, Loci::center, Dtmp); double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); - set_parameters(G, P(m_p.RHO), P(m_p.UU), P(m_p.Q), P(m_p.DP), + Real qtilde = (m_p.Q >= 0) ? P(m_p.Q) : 0.; + Real dPtilde = (m_p.DP >= 0) ? P(m_p.DP) : 0.; + set_parameters(G, P(m_p.RHO), P(m_p.UU), qtilde, dPtilde, bsq, emhd_params, gam, j, i, tau, chi_e, nu_e); } @@ -269,7 +281,9 @@ KOKKOS_INLINE_FUNCTION void set_parameters(const GRCoordinates& G, const Variabl FourVectors Dtmp; GRMHD::calc_4vecs(G, P, m_p, k, j, i, Loci::center, Dtmp); double bsq = m::max(dot(Dtmp.bcon, Dtmp.bcov), SMALL); - set_parameters(G, P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), P(m_p.Q, k, j, i), P(m_p.DP, k, j, i), + Real qtilde = (m_p.Q >= 0) ? P(m_p.Q, k, j, i) : 0.; + Real dPtilde = (m_p.DP >= 0) ? P(m_p.DP, k, j, i) : 0.; + set_parameters(G, P(m_p.RHO, k, j, i), P(m_p.UU, k, j, i), qtilde, dPtilde, bsq, emhd_params, gam, j, i, tau, chi_e, nu_e); } diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index c00e54b0..da47ecb5 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -52,6 +52,8 @@ std::shared_ptr Flux::Initialize(ParameterInput *pin, std::shared // That's what this function is for. int nvar = KHARMA::PackDimension(packages.get(), Metadata::WithFluxes); std::vector s_flux({nvar}); + if (packages->Get("Globals")->Param("verbose") > 2) + std::cout << "Allocating fluxes for " << nvar << " variables" << std::endl; // TODO optionally move all these to faces? Not important yet, & faces have no output, more memory std::vector flags_flux = {Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}; Metadata m = Metadata(flags_flux, s_flux); diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index 6c6d577a..c128960c 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -58,9 +58,9 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const if (m_p.Q >= 0 || m_p.DP >= 0) { // Apply higher-order terms conversion if necessary Real qtilde = 0., dPtilde = 0.; - if (emhd_params.conduction) + if (m_p.Q >= 0) qtilde = P(m_p.Q); - if (emhd_params.viscosity) + if (m_p.DP >= 0) dPtilde = P(m_p.DP); const Real Theta = (gam - 1) * P(m_p.UU) / P(m_p.RHO); const Real cs2 = gam * (gam - 1) * P(m_p.UU) / (P(m_p.RHO) + gam * P(m_p.UU)); @@ -85,12 +85,11 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Global& P, const VarMap& m_p, cons Real T[GR_DIM]) { if (m_p.Q >= 0 || m_p.DP >= 0) { - // Apply higher-order terms conversion if necessary Real qtilde = 0., dPtilde = 0.; - if (emhd_params.conduction) + if (m_p.Q >= 0) qtilde = P(m_p.Q, k, j, i); - if (emhd_params.viscosity) + if (m_p.DP >= 0) dPtilde = P(m_p.DP, k, j, i); const Real Theta = (gam - 1) * P(m_p.UU, k, j, i) / P(m_p.RHO, k, j, i); const Real cs2 = gam * (gam - 1) * P(m_p.UU, k, j, i) / (P(m_p.RHO, k, j, i) + gam * P(m_p.UU, k, j, i)); diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 2c3bad88..5f2edd77 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -124,8 +124,13 @@ inline TaskStatus GetFlux(MeshData *md) const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); const IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; const int nvar = U_all.GetDim(4); - //std::cout << "Calculating fluxes for " << cmax.GetDim(5) << " blocks, " - // << nvar << " variables (" << P_all.GetDim(4) << " primitives)" << std::endl; + + if (globals.Get("verbose") > 2) { + std::cout << "Calculating fluxes for " << cmax.GetDim(5) << " blocks, " + << nvar << " variables (" << P_all.GetDim(4) << " primitives)" << std::endl; + m_u.print(); m_p.print(); + emhd_params.print(); + } // Allocate scratch space const int scratch_level = 1; // 0 is actual scratch (tiny); 1 is HBM diff --git a/kharma/types.hpp b/kharma/types.hpp index 8670a44e..18e4a577 100644 --- a/kharma/types.hpp +++ b/kharma/types.hpp @@ -177,17 +177,33 @@ class VarMap { if (U1 >= 0) { U2 = U1 + 1; U3 = U1 + 2; + } else { + U2 = -1; + U3 = -1; } if (B1 >= 0) { B2 = B1 + 1; B3 = B1 + 2; + } else { + B2 = -1; + B3 = -1; } if (Bf1 >= 0) { Bf2 = Bf1 + 1; Bf3 = Bf1 + 2; + } else { + Bf2 = -1; + Bf3 = -1; } } - + + void print() const + { + printf("VAR MAP:\n"); + printf("prims: %d %d %d %d %d\n", RHO, UU, U1, U2, U3); + printf("B field cell: %d %d %d face: %d %d %d\n", B1, B2, B3, Bf1, Bf2, Bf3); + printf("EMHD q: %d dP: %d\n", Q, DP); + } }; #if DEBUG diff --git a/tests/bondi/run.sh b/tests/bondi/run.sh index 77cdaa9a..edbdeafb 100755 --- a/tests/bondi/run.sh +++ b/tests/bondi/run.sh @@ -15,7 +15,7 @@ conv_2d() { parthenon/output0/dt=1000 parthenon/output0/single_precision_output=false \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ - $2 >log_${1}_${res}.txt 2>&1 + $2 >log_${1}_${res}.txt 2>&1 || check_code=$? mv bondi.out0.00000.phdf bondi_2d_${res}_start_${1}.phdf mv bondi.out0.final.phdf bondi_2d_${res}_end_${1}.phdf done @@ -44,14 +44,13 @@ conv_2d ks coordinates/transform=null "in 2D, KS coordinates" # Recon ALL_RES="16,24,32,48,64" conv_2d linear_mc GRMHD/reconstruction=linear_mc "in 2D, linear recon with MC limiter" -conv_2d linear_vl GRMHD/reconstruction=linear_vl "in 2D, linear recon with VL limiter" +# TODO reintroduce +#conv_2d linear_vl GRMHD/reconstruction=linear_vl "in 2D, linear recon with VL limiter" # And the GRIM/classic driver conv_2d imex driver/type=imex "in 2D, with Imex driver" conv_2d imex_im "driver/type=imex GRMHD/implicit=true" "in 2D, semi-implicit stepping" -# TODO magnetized? - -# TODO 3D, esp magnetized +# TODO 3D, esp magnetized w/flux, face CT exit $exit_code diff --git a/tests/restart/run.sh b/tests/restart/run.sh index e5ec3215..66605a69 100755 --- a/tests/restart/run.sh +++ b/tests/restart/run.sh @@ -2,7 +2,7 @@ set -euo pipefail # Bash script testing initialization vs restart of a torus problem -# Require binary similarity after 5 steps +# Require similarity to round-off after 5 steps # Set paths KHARMADIR=../.. @@ -17,10 +17,10 @@ $KHARMADIR/run.sh -r torus.out1.00000.rhdf parthenon/time/nlim=5 >log_restart_2. mv torus.out0.final.phdf torus.out0.final.restart.phdf -# compare.py allows for small (5e-10) difference -#pyharm-diff torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart -# Compare binary -h5diff --exclude-path=/Info \ - --exclude-path=/Input \ - --exclude-path=/divB \ - torus.out0.final.init.phdf torus.out0.final.restart.phdf +# Compare to basic round-off +pyharm diff --rel_tol 1e-15 torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart +# Compare binary. Sometimes works but not worth keeping always +#h5diff --exclude-path=/Info \ +# --exclude-path=/Input \ +# --exclude-path=/divB \ +# torus.out0.final.init.phdf torus.out0.final.restart.phdf From 9b06281279f5c2ad986ef681d711523c884e975e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 6 Oct 2023 23:01:57 -0500 Subject: [PATCH 158/219] Bump Kokkos to fix CUDA bug --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index 02898b68..1a0597f9 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 02898b683d2a33da5f7e912916e4ce367b733635 +Subproject commit 1a0597f99b3aceafc98ba1eb23e94663bfaeb57e From ea701f99aff36f0c6e4204ca887c8e86b316d319 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sat, 7 Oct 2023 01:12:08 -0500 Subject: [PATCH 159/219] Bunch of test script fixes --- pars/electrons/hubble.par | 8 ++++---- pars/electrons/noh.par | 4 ++-- tests/all_pars/run.sh | 10 +++++++++- tests/emhdmodes/check.py | 2 +- tests/hubble_flow/make_plots.py | 12 ++++++------ tests/noh/check.py | 26 +++++++++++++++----------- tests/noh/run.sh | 6 +++--- tests/run_all.sh | 15 +++++++++++---- tests/tilt_init/run.sh | 2 +- 9 files changed, 52 insertions(+), 33 deletions(-) diff --git a/pars/electrons/hubble.par b/pars/electrons/hubble.par index b38e112f..55d94fab 100644 --- a/pars/electrons/hubble.par +++ b/pars/electrons/hubble.par @@ -32,6 +32,8 @@ nx2 = 1 nx3 = 1 +inner_x1 = dirichlet +outer_x1 = dirichlet check_inflow_inner_x1 = false check_inflow_outer_x1 = false @@ -79,8 +81,6 @@ type = imex file_type = hdf5 -# Once at the end -ghost_zones = true -dt = 1 -single_precision_output = false +dt = 10 +single_precision_output = true variables = prims.rho, prims.u, prims.uvec, prims.Ktot, prims.Kel_Constant, fflag diff --git a/pars/electrons/noh.par b/pars/electrons/noh.par index 015b9057..2b9d2a3e 100644 --- a/pars/electrons/noh.par +++ b/pars/electrons/noh.par @@ -10,7 +10,7 @@ problem_id = noh refinement = none numlevel = 1 -nx1 = 2000 +nx1 = 1024 x1min = 0.0 x1max = 1.0 @@ -23,7 +23,7 @@ x3min = 0.0 x3max = 1.0 -nx1 = 2000 +nx1 = 1024 nx2 = 1 nx3 = 1 diff --git a/tests/all_pars/run.sh b/tests/all_pars/run.sh index d2e654c9..ee9d480b 100755 --- a/tests/all_pars/run.sh +++ b/tests/all_pars/run.sh @@ -6,7 +6,15 @@ for folder in bondi electrons emhd shocks smr tests tori_2d tori_3d do for fil in ../../pars/$folder/*.par do - ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 + exit_code=0 + par=$(basename $fil) + prob=${par%.*} + ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 &>log_${prob}.txt || exit_code=$? rm -f *.{hst,phdf,rhdf,xdmf} + if [ $exit_code -ne 0 ]; then + echo $par FAIL + else + echo $par PASS + fi done done diff --git a/tests/emhdmodes/check.py b/tests/emhdmodes/check.py index 59991b57..7c577498 100644 --- a/tests/emhdmodes/check.py +++ b/tests/emhdmodes/check.py @@ -78,7 +78,7 @@ var_numerical = dump['prims'] - if higher_order_terms.lower() == "true": + if higher_order_terms: print("Higher order terms enabled") Theta = (gam - 1.) * dump['UU'] / dump['RHO'] cs2 = gam * (gam - 1.) * dump['UU'] / (dump['RHO'] + (gam * dump['UU']) ) diff --git a/tests/hubble_flow/make_plots.py b/tests/hubble_flow/make_plots.py index f0ab919e..4cda9061 100644 --- a/tests/hubble_flow/make_plots.py +++ b/tests/hubble_flow/make_plots.py @@ -17,24 +17,24 @@ x = np.linspace(0.0, 1.0, 128) kap = (gam - 2) * (game - 1) / (game - 2) * u0 / rho0**game * (1 + v0 * t)**(game - 2) -kap_dump = f['prims.Kel_Constant'][0,0,0,:,0] +kap_dump = f['prims.Kel_Constant'][0,0,0,:] fig, ax = plt.subplots(2,2, figsize=(10,10)) -ax[0, 0].plot(x,f['prims.uvec'][0,0,0,:,0]) +ax[0, 0].plot(x,f['prims.uvec'][0,0,0,0,:]) ax[0, 0].plot(x, v0*x / (1 + v0 * t)) ax[0, 0].set_title("vx") -ax[0, 1].plot(x,f['prims.rho'][0,0,0,:,0]) +ax[0, 1].plot(x,f['prims.rho'][0,0,0,:]) ax[0, 1].plot(x, rho0 / (1 + v0 * t) * np.ones_like(x)) ax[0, 1].set_title("rho") -ax[1, 0].plot(x,f['prims.u'][0,0,0,:,0]) +ax[1, 0].plot(x,f['prims.u'][0,0,0,:]) ax[1, 0].plot(x, ug0 / (1 + v0 * t)**2 * np.ones_like(x)) ax[1, 0].set_title("u") kap = (gam - 2) * (game - 1) / (game - 2) * u0 / rho0**game * (1 + v0 * t)**(game - 2) -ax[1, 1].plot(x, f['prims.Kel_Constant'][0,0,0,:,0]) +ax[1, 1].plot(x, f['prims.Kel_Constant'][0,0,0,:]) ax[1, 1].plot(x, kap*np.ones_like(x)) ax[1, 1].set_title("kappa_e") -plt.savefig("hubble.png") \ No newline at end of file +plt.savefig("hubble.png") diff --git a/tests/noh/check.py b/tests/noh/check.py index bb33fa88..0566f9c5 100644 --- a/tests/noh/check.py +++ b/tests/noh/check.py @@ -1,9 +1,12 @@ import numpy as np import os, sys, h5py + import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt +import pyharm + if __name__=='__main__': plotsdir = sys.argv[1] filesdir = sys.argv[2] @@ -17,17 +20,18 @@ # read data for r, resolution in enumerate(resolutions): - hfp = h5py.File(os.path.join(filesdir, 'noh.out0.final.res{:d}.h5'.format(resolution))) - gam = hfp['header/gam'][()] - gam_e = hfp['header/gamma_e'][()] - fel = hfp['header/fel_constant'][()] - rho = np.squeeze(hfp['prims'][Ellipsis,0][()]) - uu = np.squeeze(hfp['prims'][Ellipsis,1][()]) - kel = np.squeeze(hfp['prims'][Ellipsis,6][()]) - startx1 = hfp['header/geom/startx1'][()] - dx1 = hfp['header/geom/dx1'][()] - n1 = hfp['header/n1'][()] - hfp.close() + #hfp = h5py.File(os.path.join(filesdir, 'noh.out0.final.res{:d}.h5'.format(resolution))) + hfp = pyharm.load_dump('noh.out0.final.res{:d}.phdf'.format(resolution)) + gam = hfp['gam'] + gam_e = hfp['gam_e'] + fel = hfp['electrons/fel_constant'] + rho = np.squeeze(hfp['rho']) + uu = np.squeeze(hfp['u']) + kel = np.squeeze(hfp['Kel_Constant']) + startx1 = hfp['startx1'] + dx1 = hfp['dx1'] + n1 = hfp['n1'] + del hfp x1 = np.zeros(n1, dtype=float) for i in range(n1): diff --git a/tests/noh/run.sh b/tests/noh/run.sh index ebfe9fa7..f5a34a68 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -9,11 +9,11 @@ KHARMADIR=../.. exit_code=0 noh_test() { - ALL_RES="128,256,512,1024,2048" - for res in 64 128 256 512 1024 2048 + ALL_RES="128,256,512,1024" + for res in 64 128 256 512 1024 do eighth=$(($res / 8)) - $KHARMADIR/run.sh -i $KHARMADIR/pars/noh.par debug/verbose=1 parthenon/output0/dt=1000 \ + $KHARMADIR/run.sh -i $KHARMADIR/pars/electrons/noh.par debug/verbose=1 parthenon/output0/dt=1000 \ electrons/gamma_e=1.666667 \ parthenon/mesh/nx1=$res parthenon/meshblock/nx1=$eighth \ >log_noh_${res}.txt 2>&1 diff --git a/tests/run_all.sh b/tests/run_all.sh index ea0da229..cc789804 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -2,10 +2,17 @@ for dir in */ do - cd $dir + prob=${dir%?} + cd $prob &>/dev/null if [ -f ./run.sh ]; then - echo "Running $dir" - ./run.sh + echo Running $prob + exit_code=0 + ./run.sh >../log_${prob}.txt 2>&1 || exit_code=$? + if [ $exit_code -ne 0 ]; then + echo Test $prob FAIL + else + echo Test $prob PASS + fi fi - cd - + cd - &>/dev/null done diff --git a/tests/tilt_init/run.sh b/tests/tilt_init/run.sh index bb6ca62a..f7ccb04b 100755 --- a/tests/tilt_init/run.sh +++ b/tests/tilt_init/run.sh @@ -2,7 +2,7 @@ set -euo pipefail # Run default tilted problem to 5 steps -../../run.sh -i ../../pars/mad_tilt.par parthenon/time/nlim=5 debug/verbose=1 \ +../../run.sh -i ../../pars/tori_3d/mad_tilt.par parthenon/time/nlim=5 debug/verbose=1 \ parthenon/output0/single_precision_output=false \ >log_tilt_init.txt 2>&1 From de75557149e4e33b88140f30f1891cf3e49bb0f9 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sat, 7 Oct 2023 09:43:41 -0500 Subject: [PATCH 160/219] Add some tests that are ready, fix artifacts --- scripts/ci/cpu.yml | 9 ++++++++- scripts/ci/nvhpc.yml | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index d8b5781a..cdc93c6c 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -30,6 +30,12 @@ default: - micromamba activate pyharm - cd /pyharm && ./install.sh - cd - + # Always keep logs and plots. Results should be printed to console! + artifacts: + when: always + paths: + - tests/*/*.png + - tests/*/*.txt # Tests can be executed in parallel stages: @@ -64,4 +70,5 @@ tests: - ./run.sh parallel: matrix: - - TEST: [bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, restart, tilt_init, torus_sanity] + - TEST: [all_pars, anisotropic_conduction, bondi, bondi_viscous, bz_monopole, \ + emhdmodes, mhdmodes, noh, regrid, reinit, resize, restart, tilt_init, torus_sanity] diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 9b49e58b..0683c999 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -31,6 +31,12 @@ default: - micromamba activate pyharm - cd /pyharm && ./install.sh - cd - + # Always keep logs and plots. Results should be printed to console! + artifacts: + when: always + paths: + - tests/*/*.png + - tests/*/*.txt # Tests can be executed in parallel, # but be careful about GPU arch From ff49d37f2f5e916c34c810eb635cd453e026f500 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sat, 7 Oct 2023 10:28:33 -0500 Subject: [PATCH 161/219] CI fix --- scripts/ci/cpu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index cdc93c6c..37735eca 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -70,5 +70,5 @@ tests: - ./run.sh parallel: matrix: - - TEST: [all_pars, anisotropic_conduction, bondi, bondi_viscous, bz_monopole, \ + - TEST: [all_pars, anisotropic_conduction, bondi, bondi_viscous, bz_monopole, emhdmodes, mhdmodes, noh, regrid, reinit, resize, restart, tilt_init, torus_sanity] From a8b177d581b516bf26b9c43aa677010c7de488a2 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sat, 7 Oct 2023 12:02:17 -0500 Subject: [PATCH 162/219] Pass EMHDModes with Face CT. Clean up some other scripts. --- kharma/driver/imex_step.cpp | 7 ++-- pars/emhd/emhdmodes.par | 8 ++-- pars/tests/mhdmodes.par | 5 +-- tests/all_pars/run.sh | 10 ++++- tests/bz_monopole/bz_monopole.par | 69 +++++++++++++++++++++++++++++++ tests/bz_monopole/run.sh | 6 ++- tests/emhdmodes/run.sh | 12 +++--- 7 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 tests/bz_monopole/bz_monopole.par diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index b68a8d62..967461d2 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -237,11 +237,12 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta auto t_implicit_step = tl.AddTask(t_copy_linesearch, Implicit::Step, md_full_step_init.get(), md_sub_step_init.get(), md_flux_src.get(), md_linesearch.get(), md_solver.get(), integrator->beta[stage-1] * integrator->dt); - // Copy the entire solver state (everything defined on the grid, i.e. 'Cell') into the final state md_sub_step_final + // Copy the entire solver state (everything defined on the grid, incl. our new Face variables) into the final state md_sub_step_final // If we're entirely explicit, we just declare these equal - t_implicit = tl.AddTask(t_implicit_step, Copy>, std::vector({Metadata::Cell}), + auto t_implicit_c = tl.AddTask(t_implicit_step, Copy>, std::vector({Metadata::Cell}), md_solver.get(), md_sub_step_final.get()); - + t_implicit = tl.AddTask(t_implicit_step, WeightedSumDataFace, std::vector({Metadata::Face}), + md_solver.get(), md_solver.get(), 1.0, 0.0, md_sub_step_final.get()); } // Apply all floors & limits (GRMHD,EMHD,etc), but do *not* immediately correct UtoP failures with FixUtoP -- diff --git a/pars/emhd/emhdmodes.par b/pars/emhd/emhdmodes.par index 68f20974..f50e35e5 100644 --- a/pars/emhd/emhdmodes.par +++ b/pars/emhd/emhdmodes.par @@ -39,9 +39,7 @@ transform = null tlim = 2.0 nlim = -1 -# "RK2" is the only option for implicit solver -integrator = rk2 -use_dt_light = true +#use_dt_light = true cfl = 0.9 @@ -73,8 +71,10 @@ use_qr = true # General verbosity level: # 1: general archival info # 2: specific debugging logs +# 3: egregious/temporary notes verbose = 0 # Set to 1 to check each step for wavespeed of zero/NaN & exit +# 2 enables some rarely seen stuff extra_checks = 1 # Print summary of all flags hit during each step: # 1: Number of flags total @@ -99,7 +99,7 @@ viscosity_alpha = 1.0 file_type = hdf5 # Output only final state -dt = 100.0 +dt = 0.2 # Output in double due to low amplitude single_precision_output = false variables = prims.rho, prims.u, prims.uvec, prims.B, prims.q, prims.dP, solve_norm, solve_fail diff --git a/pars/tests/mhdmodes.par b/pars/tests/mhdmodes.par index e8b7f5a7..87166e23 100644 --- a/pars/tests/mhdmodes.par +++ b/pars/tests/mhdmodes.par @@ -19,9 +19,8 @@ nmode = 1 dir = 3 # Size and parameters of the full mesh -# KHARMA does not yet support AMR, -# so all mesh declarations will have -# the first two parameters +# First two parameters are the defaults, +# and specify no SMR/AMR refinement = none numlevel = 1 diff --git a/tests/all_pars/run.sh b/tests/all_pars/run.sh index ee9d480b..3b066e1e 100755 --- a/tests/all_pars/run.sh +++ b/tests/all_pars/run.sh @@ -1,6 +1,8 @@ #!/bin/bash set -euo pipefail +return_code=0 + # Skip testing the restarting & benchmark scripts for folder in bondi electrons emhd shocks smr tests tori_2d tori_3d do @@ -12,9 +14,13 @@ do ../../run.sh -n 1 -i $fil parthenon/time/nlim=2 &>log_${prob}.txt || exit_code=$? rm -f *.{hst,phdf,rhdf,xdmf} if [ $exit_code -ne 0 ]; then - echo $par FAIL + printf "%-40s %s\n" $par FAIL + return_code=1 else - echo $par PASS + printf "%-40s %s\n" $par PASS fi done done + +exit $return_code + diff --git a/tests/bz_monopole/bz_monopole.par b/tests/bz_monopole/bz_monopole.par new file mode 100644 index 00000000..a0b26367 --- /dev/null +++ b/tests/bz_monopole/bz_monopole.par @@ -0,0 +1,69 @@ +# Monopole in vacuum +# Specific parameters for integration test + + +problem_id = bz_monopole + + +refinement = none +numlevel = 1 +nx1 = 128 +nx2 = 128 +nx3 = 1 + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +base = spherical_ks +transform = fmks +r_out = 100. +a = 0.9375 +hslope = 0.3 +mks_smooth = 0.5 +poly_xt = 0.82 +poly_alpha = 14.0 + + +tlim = 100.0 +nlim = -1 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 + + +cfl = 0.7 +gamma = 1.444444 +reconstruction = linear_mc + + +type = bz_monopole +norm = false + + +bsq_over_rho_max = 100 +rho_min_geom = 1e-20 +u_min_geom = 1e-20 +gamma_max = 10 + + +on = false +ne = 1.e-4 +Tp = 100 +u1 = 0.4 +power = 40 + + +file_type = hdf5 +dt = 5.0 +single_precision_output = false +variables = prims.rho, prims.u, prims.uvec, prims.B, cons.B, divB +ghost_zones = true + + +file_type = hst +dt = 0.1 diff --git a/tests/bz_monopole/run.sh b/tests/bz_monopole/run.sh index 3ec7bfab..677410f2 100755 --- a/tests/bz_monopole/run.sh +++ b/tests/bz_monopole/run.sh @@ -6,13 +6,15 @@ BASE=../.. exit_code=0 # Full run to test stability to completion -$BASE/run.sh -i $BASE/pars/tests/bz_monopole.par debug/verbose=1 parthenon/output0/single_precision_output=false >log_bz_monopole_full.txt 2>&1 || exit_code=$? +$BASE/run.sh -i ./bz_monopole.par debug/verbose=1 >log_bz_monopole_full.txt 2>&1 || exit_code=$? # At *least* check divB pyharm-check-basics bz_monopole.out0.final.phdf || exit_code=$? # Take 1 step to look for early signs of non-fatal instabilities -$BASE/run.sh -i $BASE/pars/tests/bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 parthenon/output0/single_precision_output=false >log_bz_monopole_step.txt 2>&1 #|| exit_code=$? +$BASE/run.sh -i ./bz_monopole.par parthenon/time/nlim=1 parthenon/output0/dt=0.0 >log_bz_monopole_step.txt 2>&1 || exit_code=$? # This just makes plots, it doesn't check anything python ./check.py + +exit $exit_code diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index 0b37a067..c96ae5f3 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -23,10 +23,10 @@ conv_2d() { check_code=0 python check.py $ALL_RES "$3" $1 2d || check_code=$? if [[ $check_code != 0 ]]; then - echo EMHD modes test $3 FAIL: $check_code + echo $3 FAIL: $check_code exit_code=1 else - echo EMHD modes test $3 success + echo $3 success fi } @@ -34,10 +34,12 @@ conv_2d() { # Just one default mode ALL_RES="32,64,128" conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "EMHD mode in 2D, WENO5" -ALL_RES="16,32,64,128,256" -conv_2d emhd2d_mc GRMHD/reconstruction=linear_mc "EMHD mode in 2D, linear/MC reconstruction" - # Test that higher-order terms don't mess anything up conv_2d emhd2d_higher_order emhd/higher_order_terms=true "EMHD mode in 2D, higher order terms enabled" +# Test we can use imex/EMHD and face CT +conv_2d emhd2d_face_ct b_field/solver=face_ct "EMHD mode in 2D w/Face CT" + +ALL_RES="16,32,64,128,256" +conv_2d emhd2d_mc GRMHD/reconstruction=linear_mc "EMHD mode in 2D, linear/MC reconstruction" exit $exit_code From aea831f8734bff95ef2e1085df3bc5765b0db167 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sat, 7 Oct 2023 13:01:55 -0500 Subject: [PATCH 163/219] Tone down CI core usage a touch, use abs paths --- scripts/ci/cpu.yml | 10 +++++----- scripts/ci/nvhpc.yml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 37735eca..635a99d6 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -3,7 +3,7 @@ image: quay.io/centos/centos:stream9 variables: - OMP_NUM_THREADS: 8 + OMP_NUM_THREADS: 6 OMP_PROC_BIND: "false" MPI_EXE: mpirun MPI_NUM_PROCS: 2 @@ -23,8 +23,8 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba - - eval "$(./bin/micromamba shell hook -s posix)" + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj /mamba/micromamba + - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml - micromamba activate pyharm @@ -46,8 +46,8 @@ stages: build: stage: build variables: - NPROC: 8 - HOST_ARCH: NATIVE + NPROC: 12 + HOST_ARCH: HSW C_NATIVE: gcc CXX_NATIVE: g++ before_script: diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 0683c999..5d30a628 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -24,8 +24,8 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba - - eval "$(./bin/micromamba shell hook -s posix)" + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj /mamba/micromamba + - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml - micromamba activate pyharm From a3f43eb19cf4a76b66e18a38bbc80650135cf64d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 17:05:45 -0500 Subject: [PATCH 164/219] Fix the boundaries of EMHD problems All MHD variables in bondi_viscous now converge as expected, and boundaries are applied to dP as expected. Source term seems to be much, much too large for some reason. Also Vbump Kokkos to fix a CUDA segfault (again?) --- external/parthenon | 2 +- kharma/b_ct/b_ct.cpp | 7 ++++-- kharma/b_flux_ct/b_flux_ct.cpp | 8 +++++++ kharma/boundaries/boundaries.cpp | 37 ++++++++++++++++++++++++++++--- kharma/driver/imex_step.cpp | 2 +- kharma/emhd/emhd.cpp | 2 ++ kharma/flux/flux.cpp | 13 ++++++++--- tests/bondi_viscous/check.py | 38 +++++++++++++++++++++++--------- 8 files changed, 88 insertions(+), 21 deletions(-) diff --git a/external/parthenon b/external/parthenon index 1a0597f9..72a97564 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 1a0597f99b3aceafc98ba1eb23e94663bfaeb57e +Subproject commit 72a975647e5548fee643952a52f12a249fc2b325 diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 85f08854..2243487b 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -73,7 +73,7 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared params.Add("ct_scheme", ct_scheme); // Use the default Parthenon prolongation operator, rather than the divergence-preserving one // This relies entirely on the EMF communication for preserving the divergence - bool lazy_prolongation = pin->GetOrAddBoolean("b_field", "lazy_prolongation", true); + bool lazy_prolongation = pin->GetOrAddBoolean("b_field", "lazy_prolongation", false); // Need to preserve divergence if you refine/derefine during sim i.e. AMR if (lazy_prolongation && pin->GetString("parthenon/mesh", "refinement") == "adaptive") throw std::runtime_error("Cannot use non-preserving prolongation in AMR!"); @@ -83,6 +83,7 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // Flags for B fields on faces. // We don't mark these as "Primitive" and "Conserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want + // Also note we *always* sync B field conserved var std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, Metadata::GetUserFlag("Explicit")}; std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, @@ -172,6 +173,8 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto B_U = rc->PackVariables(std::vector{"cons.B"}); auto B_P = rc->PackVariables(std::vector{"prims.B"}); const auto& G = pmb->coords; + // Return if we're not syncing U & P at all (e.g. edges) + if (B_Uf.GetDim(4) == 0) return; // TODO get rid of prims on faces probably @@ -213,7 +216,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // Figure out indices const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 1); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 2); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; const int kd = ndim > 2 ? 1 : 0; const int jd = ndim > 1 ? 1 : 0; diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 3be20c15..c9ce22ba 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -109,6 +109,14 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->Param>("cons_flags"); flags_cons.insert(flags_cons.end(), flags_b.begin(), flags_b.end()); + // Always sync B field conserved var, for standardization with B_CT + if (!flags_cons.count(Metadata::FillGhost)) { + flags_cons.push_back(Metadata::FillGhost); + } + if (flags_prims.count(Metadata::FillGhost)) { + flags_prims.erase(std::remove(flags_prims.begin(), flags_prims.end(), Metadata::FillGhost), flags_prims.end()); + } + auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); m = Metadata(flags_cons, s_vector); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index cb529f32..af1bf345 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -128,6 +128,11 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: bool zero_flux = pin->GetOrAddBoolean("boundaries", "zero_flux_" + bname, zero_polar_flux && bdir == X2DIR); params.Add("zero_flux_" + bname, zero_flux); + // Allow specifically dP to outflow in otherwise Dirichlet conditions + // Only used for viscous_bondi problem + bool outflow_EMHD = pin->GetOrAddBoolean("boundaries", "outflow_EMHD_" + bname, false); + params.Add("outflow_EMHD_" + bname, outflow_EMHD); + // BOUNDARY TYPES // Get the boundary type we specified in kharma auto btype = pin->GetString("boundaries", bname); @@ -248,6 +253,13 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD const auto btype_name = params.Get(bname); const auto bdir = BoundaryDirection(bface); + // If we're pretending to sync primitives, but applying physical bounds + // to conserved variables, make sure we're up to date + if (pmb->packages.Get("Driver")->Param("prims_are_fundamental") && + params.Get("domain_bounds_on_conserved")) { + Flux::BlockPtoU_Send(rc.get(), domain, coarse); + } + Flag("Apply "+bname+" boundary: "+btype_name); pkg->KBoundaries[bface](rc, coarse); EndFlag(); @@ -271,6 +283,26 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD EndFlag(); } + // Allow specifically dP to outflow in otherwise Dirichlet conditions + // Only used for viscous_bondi problem + // TODO make this more general? + if (params.Get("outflow_EMHD_" + bname)) { + Flag("OutflowEMHD_"+bname); + auto EMHDg = rc->PackVariables({Metadata::GetUserFlag("EMHDVar"), Metadata::FillGhost}); + const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const auto &range = (bdir == 1) ? bounds.GetBoundsI(IndexDomain::interior) + : (bdir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) + : bounds.GetBoundsK(IndexDomain::interior)); + const int ref = BoundaryIsInner(domain) ? range.s : range.e; + pmb->par_for_bndry( + "outflow_EMHD", IndexRange{0,EMHDg.GetDim(4)-1}, domain, CC, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + EMHDg(v, k, j, i) = EMHDg(v, (bdir == 3) ? ref : k, (bdir == 2) ? ref : j, (bdir == 1) ? ref : i); + } + ); + EndFlag(); + } + /* * KHARMA is very particular about corner boundaries. * In particular, we apply the outflow boundary over ALL X2 & X3. @@ -320,7 +352,8 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); } } else { - Packages::BlockUtoP(rc.get(), domain, coarse); + // These get applied the same way regardless of driver + Packages::BoundaryUtoP(rc.get(), domain, coarse); } EndFlag(); @@ -350,8 +383,6 @@ void KBoundaries::CorrectBPrimitive(std::shared_ptr>& rc, In { Flag("CorrectBPrimitive"); std::shared_ptr pmb = rc->GetBlockPointer(); - const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); - auto B_P = rc->PackVariables(std::vector{"prims.B"}); // Return if no field to correct if (B_P.GetDim(4) == 0) return; diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 967461d2..5f947823 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -84,7 +84,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta if (use_jcon) { pmesh->mesh_data.Add("preserve"); // Above only copies on allocate -- ensure we copy every step - Copy>({}, base.get(), pmesh->mesh_data.Get("preserve").get()); + Copy>({Metadata::Cell}, base.get(), pmesh->mesh_data.Get("preserve").get()); } if (use_implicit) { // When solving, we need a temporary copy with any explicit updates, diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index b16fd7d2..a2db56e5 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -167,6 +167,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

BoundaryUtoP = EMHD::BlockUtoP; + // If we wanted to apply the domian boundaries to primitive EMHD variables + //pkg->DomainBoundaryPtoU = EMHD::BlockPtoU; // Add all explicit source terms -- implicit terms are called from Implicit::Step pkg->AddSource = EMHD::AddSource; diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index da47ecb5..79c482d7 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -168,12 +168,19 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo const EMHD::EMHD_parameters& emhd_params = EMHD::GetEMHDParameters(pmb->packages); - // Pack variables + // Pack variables. We never want to run this on the B field + using FC = Metadata::FlagCollection; + auto cons_flags = FC(Metadata::Conserved, Metadata::Cell, Metadata::GetUserFlag("HD")); + if (pmb->packages.AllPackages().count("EMHD")) + cons_flags = cons_flags + FC(Metadata::Conserved, Metadata::Cell, Metadata::GetUserFlag("EMHDVar")); PackIndexMap prims_map, cons_map; - const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - const auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + const auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::Cell}, prims_map); + const auto& U = rc->PackVariables(cons_flags, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); + // Return if we're not syncing U & P at all (e.g. edges) + if (P.GetDim(4) == 0) return TaskStatus::complete; + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; IndexRange ib = bounds.GetBoundsI(domain); IndexRange jb = bounds.GetBoundsJ(domain); diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index c5a0095e..baba6974 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -23,8 +23,8 @@ outputdir = './' kharmadir = '../../' - NVAR = 3 - VARS = ['rho', 'u', 'dP'] + NVAR = 4 + VARS = ['rho', 'u', 'dP', 'B'] RES = [int(r) for r in sys.argv[1].split(",")] LONG = sys.argv[2] SHORT = sys.argv[3] @@ -44,12 +44,15 @@ state.params['eta'] = eta state.params['tau'] = tau dP_check = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau) + state.cache['dP'] = dP_check # load code data dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) - rho, uu, dP_tilde = dump['RHO'], dump['UU'], dump['dP'] + # TODO iterate on names here + #rho, uu, dP_tilde = dump['RHO'], dump['UU'], dump['dP'] #rho, uu = dump['RHO'], dump['UU'] + rho, uu, dP_tilde, B1 = dump['RHO'], dump['UU'], dump['dP'], dump['B1'] # compute dP if dump['emhd/higher_order_terms'] == "true": @@ -61,17 +64,30 @@ dP = dP_tilde # Plot - fig = plt.figure(figsize=(6,6)) - ax = fig.add_subplot(1,1,1) - pplt.plot_diff_xz(ax, dump, state, 'rho') + for var in ['rho', 'u', 'B1', 'dP']: + fig = plt.figure(figsize=(6,6)) + ax = fig.add_subplot(1,1,1) + pplt.plot_diff_xz(ax, dump, state, var) + plt.legend() + fig.savefig("compare_{}_{}.png".format(var, res)) + plt.close(fig) + + r_start_ind = 1 + radius = np.mean(dump.grid['r'][r_start_ind:], axis=(1,2)) + plt.plot(radius, dP_check[r_start_ind:], label='dP ODE check') + plt.plot(radius, np.mean(dump['dP'][r_start_ind:], axis=(1,2)), label='dP0 ODE check') + plt.plot(radius, np.mean(state['ucon'][1][r_start_ind:], axis=(1,2)), label='ur') + #plt.plot(radius, np.mean(coeff[r_start_ind:], axis=(1,2)), label='coeff') plt.legend() - fig.savefig("compare_rho_{}.png".format(res)) - plt.close(fig) + plt.savefig('dP_soln_new.png') + plt.close() + # compute L1 norm - L1[r,0] = np.mean(np.fabs(rho[:,0,0] - state['rho'][:,0,0])) - L1[r,1] = np.mean(np.fabs(uu[:,0,0] - state['u'][:,0,0])) - L1[r,2] = np.mean(np.fabs(dP[:,0,0] - dP_check)[1:-1]) + L1[r,0] = np.mean(np.fabs(rho - state['rho'])[1:-1]) + L1[r,1] = np.mean(np.fabs(uu - state['u'])) + L1[r,2] = np.mean(np.fabs(dP - dP_check)[1:-1]) + L1[r,3] = np.mean(np.fabs(B1 - state['B1'])) # MEASURE CONVERGENCE L1 = np.array(L1) From 2a99504938f14b024fa216a3df906897e9906062 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 17:31:27 -0500 Subject: [PATCH 165/219] Fix compile w/new Flux_CT B flags --- kharma/b_flux_ct/b_flux_ct.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index c9ce22ba..c2169c82 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -110,11 +110,12 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Date: Mon, 9 Oct 2023 17:45:39 -0500 Subject: [PATCH 166/219] CI: tar fixes --- scripts/ci/cpu.yml | 2 +- scripts/ci/nvhpc.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 635a99d6..341268bc 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -23,7 +23,7 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj /mamba/micromamba + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xOvj bin/micromamba > /mamba/micromamba - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 5d30a628..800e3cd2 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -24,7 +24,7 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj /mamba/micromamba + - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xOvj bin/micromamba > /mamba/micromamba - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml From 5619ebb42c5574d2032a189827fcd9949a022b45 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 17:59:45 -0500 Subject: [PATCH 167/219] CI: more mamba installation --- scripts/ci/cpu.yml | 4 +++- scripts/ci/nvhpc.yml | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 341268bc..2330eff9 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -11,6 +11,7 @@ variables: OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 GIT_SUBMODULE_STRATEGY: recursive MAMBA_ROOT_PREFIX: /mamba + MAMBA_URL: https://micro.mamba.pm/api/micromamba/linux-64/latest ### DEFAULT TEST BEHAVIOR ### default: @@ -23,7 +24,8 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xOvj bin/micromamba > /mamba/micromamba + - mkdir $MAMBA_ROOT_PREFIX + - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba > $MAMBA_ROOT_PREFIX/micromamba - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 800e3cd2..27eda9f1 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -14,6 +14,8 @@ variables: OMPI_ALLOW_RUN_AS_ROOT: 1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1 GIT_SUBMODULE_STRATEGY: recursive + MAMBA_ROOT_PREFIX: /mamba + MAMBA_URL: https://micro.mamba.pm/api/micromamba/linux-64/latest ### DEFAULT TEST BEHAVIOR ### default: @@ -24,7 +26,8 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xOvj bin/micromamba > /mamba/micromamba + - mkdir $MAMBA_ROOT_PREFIX + - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba > $MAMBA_ROOT_PREFIX/micromamba - eval "$(/mamba/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml From b92750d716ff0b041fec22adbbda02922d41b4c3 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 18:17:42 -0500 Subject: [PATCH 168/219] CI: sometimes installation scripts are that way for a reason --- scripts/ci/cpu.yml | 5 ++--- scripts/ci/nvhpc.yml | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 2330eff9..4651440a 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -24,9 +24,8 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - mkdir $MAMBA_ROOT_PREFIX - - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba > $MAMBA_ROOT_PREFIX/micromamba - - eval "$(/mamba/micromamba shell hook -s posix)" + - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba + - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml - micromamba activate pyharm diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 27eda9f1..8ea630de 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -26,9 +26,8 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - mkdir $MAMBA_ROOT_PREFIX - - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba > $MAMBA_ROOT_PREFIX/micromamba - - eval "$(/mamba/micromamba shell hook -s posix)" + - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba + - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml - micromamba activate pyharm From b62fd1111859143acb452c4c95d05ae800054226 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 18:24:57 -0500 Subject: [PATCH 169/219] Fix Noh test by parsing e- gamma correctly in checker --- pars/electrons/noh.par | 4 ++-- tests/noh/check.py | 8 +++++++- tests/noh/run.sh | 2 -- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pars/electrons/noh.par b/pars/electrons/noh.par index 2b9d2a3e..17151db5 100644 --- a/pars/electrons/noh.par +++ b/pars/electrons/noh.par @@ -65,8 +65,8 @@ zero_ug = false set_tlim = true centered = false -# -#disable_floors = true + +disable_floors = true type = imex diff --git a/tests/noh/check.py b/tests/noh/check.py index 0566f9c5..08136cd3 100644 --- a/tests/noh/check.py +++ b/tests/noh/check.py @@ -39,7 +39,8 @@ u_e = (kel * rho**gam_e)/(gam_e - 1.) ratio_analytical = np.where(rho > 1.5, \ - fel/2. * (((gam + 1.)/(gam - 1.))**gam_e * (1. - gam/gam_e) + 1. + gam/gam_e) * ((gam**2 - 1.)/(gam_e**2 - 1.)), \ + fel/2. * (((gam + 1.)/(gam - 1.))**gam_e * (1. - gam/gam_e) + 1. + gam/gam_e) \ + * ((gam**2 - 1.)/(gam_e**2 - 1.)), \ 0.) plt.figure(figsize=(6,6)) @@ -48,6 +49,11 @@ plt.legend() plt.savefig("noh_results_{}.png".format(resolution)) + plt.figure(figsize=(6,6)) + plt.plot(x1, rho, label="Computed") + plt.legend() + plt.savefig("noh_rho_{}.png".format(resolution)) + l1_norm.append(np.mean(abs(u_e/uu - ratio_analytical))) l1_norm = np.array(l1_norm) diff --git a/tests/noh/run.sh b/tests/noh/run.sh index f5a34a68..65887234 100755 --- a/tests/noh/run.sh +++ b/tests/noh/run.sh @@ -20,7 +20,6 @@ noh_test() { cp noh.out0.final.phdf noh.out0.final.res$res.phdf done - pyharm-convert *.phdf check_code=0 python check.py . . $ALL_RES 1.666667 || check_code=$? if [[ $check_code != 0 ]]; then @@ -29,7 +28,6 @@ noh_test() { else echo Noh shock test success fi - rm *.phdf } noh_test From e4747ebc6dfe481f009a40cd1a0dd7d96ab4c09a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 18:26:19 -0500 Subject: [PATCH 170/219] CI: sigh --- scripts/ci/cpu.yml | 2 +- scripts/ci/nvhpc.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 4651440a..df2abecf 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -24,7 +24,7 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba + - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 8ea630de..589b9ed7 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -26,7 +26,7 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - curl -Ls $MAMBA_URL | tar -xOvj bin/micromamba + - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml From d7d61e79c420b1e9bff7d10d93ca4fed6b435dc7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 19:54:33 -0500 Subject: [PATCH 171/219] Get rid of sync_prims Supporting exchanging primitive vars only for ImEx driver in non-AMR had become a source of bugs, incl. last commit. Fix by simplifying. ImEx driver needs to be able to sync conserved variables anyway for AMR, so better to keep the same codepath even at the cost of the occasional UtoP/PtoU call. --- kharma/b_flux_ct/b_flux_ct.cpp | 9 ------ kharma/boundaries/boundaries.cpp | 49 ++++---------------------------- kharma/boundaries/boundaries.hpp | 5 ---- kharma/driver/kharma_driver.cpp | 30 +++++-------------- kharma/grmhd/grmhd.cpp | 6 ++-- kharma/inverter/inverter.cpp | 1 + 6 files changed, 15 insertions(+), 85 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index c2169c82..3be20c15 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -109,15 +109,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->Param>("cons_flags"); flags_cons.insert(flags_cons.end(), flags_b.begin(), flags_b.end()); - // Always sync B field conserved var, for standardization with B_CT - // god std::vector is verbose - if (std::find(flags_cons.begin(), flags_cons.end(), Metadata::FillGhost) == flags_cons.end()) { - flags_cons.push_back(Metadata::FillGhost); - } - if (std::find(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost) != flags_prim.end()) { - flags_prim.erase(std::remove(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost), flags_prim.end()); - } - auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); m = Metadata(flags_cons, s_vector); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index af1bf345..d7f52799 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -335,24 +335,13 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD } } - // If we applied the domain boundary to primitives (as we usually do)... + // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. + // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE if (!params.Get("domain_bounds_on_conserved")) { - bool sync_prims = rc->GetBlockPointer()->packages.Get("Driver")->Param("sync_prims"); - // There are two modes of operation here: - if (sync_prims) { - // 1. ImEx w/o AMR: - // PRIMITIVE variables (only) are marked FillGhost - // So, run PtoU on EVERYTHING (and correct the B field) - CorrectBPrimitive(rc, domain, coarse); - Flux::BlockPtoU(rc.get(), domain, coarse); - } else { - // 2. Normal (KHARMA driver, ImEx w/AMR): - // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. - // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE - Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); - } + // Only the GRMHD package defines a BoundaryPtoU + Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); } else { - // These get applied the same way regardless of driver + // Or, apply the boundary to the conserved GRMHD variables, too! Packages::BoundaryUtoP(rc.get(), domain, coarse); } @@ -379,34 +368,6 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom ); } -void KBoundaries::CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -{ - Flag("CorrectBPrimitive"); - std::shared_ptr pmb = rc->GetBlockPointer(); - auto B_P = rc->PackVariables(std::vector{"prims.B"}); - // Return if no field to correct - if (B_P.GetDim(4) == 0) return; - - const auto& G = pmb->coords; - - const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundaryDirection(domain); - const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) - : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) - : bounds.GetBoundsK(IndexDomain::interior)); - const int ref = BoundaryIsInner(domain) ? range.s : range.e; - - pmb->par_for_bndry( - "Correct_B_P", IndexRange{0,NVEC-1}, domain, CC, coarse, - KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { - B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) - / G.gdet(Loci::center, j, i); - } - ); - - EndFlag(); -} - TaskStatus KBoundaries::FixFlux(MeshData *md) { auto pmesh = md->GetMeshPointer(); diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index dde70a60..fb910995 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -84,11 +84,6 @@ TaskStatus FixFlux(MeshData *rc); */ void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse); -/** - * Correct for geometry when applying primitive B field boundaries - */ -void CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse); - /** * Check for velocity toward the simulation domain in a zone, and eliminate it. */ diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 6e9a4fdc..53d9382e 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -55,7 +55,7 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // Driver options // The two current drivers are "kharma" or "imex", with the former being the usual KHARMA // driver (formerly HARM driver), and the latter supporting implicit stepping of some or all variables - // Mostly, packages should react to e.g. the "sync_prims" option rather than the driver name + // Mostly, packages should react to options rather than the driver name bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); std::string driver_type_s = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); DriverType driver_type; @@ -137,25 +137,10 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std bool prims_are_fundamental = driver_type != DriverType::kharma; params.Add("prims_are_fundamental", prims_are_fundamental); - // Which variables we *actually send* via Parthenon/MPI may differ, however. - // Prolongation/restriction should happen on conserved vars, so we must sync - // those in multilevel meshes. If prims are funcamental but not sync'd, - // we "emulate" syncing them with PtoU/UtoP on boundaries - bool sync_prims = prims_are_fundamental && - (!pin->DoesParameterExist("parthenon/mesh", "numlevel") || - pin->GetInteger("parthenon/mesh", "numlevel") == 1); - params.Add("sync_prims", sync_prims); - // Finally, we set default flags for primitive and conserved variables - // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated - if (sync_prims) { - // If we're not in AMR, we can sync primitive variables directly - params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); - params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::WithFluxes, Metadata::Conserved}); - } else { - // If we're in AMR or using the KHARMA driver anyway, sync conserved vars - params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive")}); - params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); - } + // Now that we're an AMR code, though, we always *sync* conserved variables + // This means "emulating" syncing primitives in some cases, by running PtoU -> sync -> UtoP + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); return pkg; } @@ -188,8 +173,7 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh // Note this has the side effect of filling U in some zones, // which must be replaced during e.g. startup code when primitive values should be truth bool prims_are_fundamental = params.Get("prims_are_fundamental"); - bool sync_prims = params.Get("sync_prims"); - if (prims_are_fundamental && !sync_prims) { + if (prims_are_fundamental) { TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_ptou_final(0); int i_task = 0; @@ -215,7 +199,7 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh EndFlag(); // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims - if (prims_are_fundamental && !sync_prims) { + if (prims_are_fundamental) { TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); int i_task = 0; diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index e1d7746a..fedb683a 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -145,10 +145,8 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

("sync_prims") && pin->GetOrAddBoolean("GRMHD", "sync_utop_seed", true)) { + // Disabling this is not well tested regardless of how fancy the solver is, YMMV + if (pin->GetOrAddBoolean("GRMHD", "sync_utop_seed", true)) { flags_prim.push_back(Metadata::FillGhost); } diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index e538350c..b3281371 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -80,6 +80,7 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh // This package is still loaded because fixes if (!implicit_grmhd) { pkg->BlockUtoP = Inverter::BlockUtoP; + pkg->BoundaryUtoP = Inverter::BlockUtoP; } pkg->PostStepDiagnosticsMesh = Inverter::PostStepDiagnostics; From f1e1cb9d59f259b2c42670f2c8656da9dd8dacea Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 21:20:38 -0500 Subject: [PATCH 172/219] Repair some tests for always syncing cons These bugs would have appeared more inscrutably when we ran w/AMR anyway --- kharma/electrons/electrons.cpp | 1 + kharma/inverter/inverter.cpp | 7 +++++-- tests/restart/run.sh | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index ff4b2dff..b6ac1c17 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -186,6 +186,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

BlockUtoP = Electrons::BlockUtoP; + pkg->BoundaryUtoP = Electrons::BlockUtoP; return pkg; } diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index b3281371..a50ba646 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -103,7 +103,10 @@ inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, b auto P = GRMHD::PackHDPrims(rc, prims_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); - GridScalar pflag = rc->Get("pflag").data; + auto pflag = rc->PackVariables(std::vector{"pflag"}); + + if (U.GetDim(4) == 0 || pflag.GetDim(4) == 0) + return; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); @@ -121,7 +124,7 @@ inline void BlockPerformInversion(MeshBlockData *rc, IndexDomain domain, b KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { if (KDomain::inside(k, j, i, b)) { // Run over all interior zones and any initialized ghosts - pflag(k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); + pflag(0, k, j, i) = static_cast(Inverter::u_to_p(G, U, m_u, gam, k, j, i, P, m_p, Loci::center)); } } ); diff --git a/tests/restart/run.sh b/tests/restart/run.sh index 66605a69..966c516f 100755 --- a/tests/restart/run.sh +++ b/tests/restart/run.sh @@ -18,7 +18,7 @@ $KHARMADIR/run.sh -r torus.out1.00000.rhdf parthenon/time/nlim=5 >log_restart_2. mv torus.out0.final.phdf torus.out0.final.restart.phdf # Compare to basic round-off -pyharm diff --rel_tol 1e-15 torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart +pyharm diff --rel_tol 1e-11 torus.out0.final.init.phdf torus.out0.final.restart.phdf -o compare_restart # Compare binary. Sometimes works but not worth keeping always #h5diff --exclude-path=/Info \ # --exclude-path=/Input \ From 868b033c7558119df3a71c2ee7a4341064bf5672 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 9 Oct 2023 22:33:19 -0500 Subject: [PATCH 173/219] Test fixes, but not the important ones --- kharma/b_ct/b_ct.hpp | 4 ++++ kharma/boundaries/boundaries.cpp | 7 ++++++- kharma/driver/imex_step.cpp | 15 +++++++-------- kharma/driver/kharma_step.cpp | 12 ++++++------ pars/emhd/bondi_viscous.par | 13 ++++++++----- pars/smr/orszag_tang_refined.par | 1 - scripts/ci/cpu.yml | 3 ++- scripts/ci/nvhpc.yml | 4 ++-- tests/emhdmodes/run.sh | 2 +- 9 files changed, 36 insertions(+), 25 deletions(-) diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index 319e455f..ab2b291d 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -306,6 +306,10 @@ struct ProlongateInternalOlivares { const int off_i = (DIM > 0) ? (elem%2)*(me == V2) + (elem/2)*(me == V3) + (me == V1) : 0; const int off_j = (DIM > 1) ? (elem%2)*(me == V3) + (elem/2)*(me == V1) + (me == V2) : 0; const int off_k = (DIM > 2) ? (elem%2)*(me == V1) + (elem/2)*(me == V2) + (me == V3) : 0; + if (((el == TE::F1) && (fi + off_i > ib.e)) || + ((el == TE::F2) && (fj + off_j > jb.e)) || + ((el == TE::F3) && (fk + off_k > kb.e))) + return; fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = ( // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index d7f52799..23a415c1 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -86,7 +86,12 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: Metadata m_x1, m_x2, m_x3; { // We can't use GetVariablesByFlag yet, so ask the packages - int nvar = KHARMA::PackDimension(packages.get(), Metadata::FillGhost); + // These flags get anything that needs a physical boundary during the run + using FC = Metadata::FlagCollection; + FC ghost_vars = FC({Metadata::FillGhost, Metadata::Conserved}) + + FC({Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}) + - FC({Metadata::GetUserFlag("StartupOnly")}); + int nvar = KHARMA::PackDimension(packages.get(), ghost_vars); // We also don't know the mesh size, since it's not constructed. We infer. const int ng = pin->GetInteger("parthenon/mesh", "nghost"); diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 5f947823..7fb9db74 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -132,26 +132,25 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_bound, tl, recon, md_sub_step_init.get()); // If we're in AMR, correct fluxes from neighbors - auto t_flux_bounds = t_fluxes; + auto t_emf = t_fluxes; if (pmesh->multilevel || use_b_ct) { - auto t_emf = t_fluxes; - // TODO this MPI sync should be bundled into fluxcorr + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + auto t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); + auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition - auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); + auto t_emf_local = tl.AddTask(t_flux_bounds, B_CT::CalculateEMF, md_sub_step_init.get()); auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } - tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: // 1. CT calculations for B field transport // 2. Zero fluxes through poles // etc - auto t_fix_flux = tl.AddTask(t_flux_bounds, Packages::FixFlux, md_sub_step_init.get()); + auto t_fix_flux = tl.AddTask(t_emf, Packages::FixFlux, md_sub_step_init.get()); // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 7b22c80b..e5619dbd 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -154,25 +154,25 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_flux, tl, recon, md_sub_step_init.get()); // If we're in AMR, correct fluxes from neighbors - auto t_flux_bounds = t_fluxes; + auto t_emf = t_fluxes; if (pmesh->multilevel || use_b_ct) { - auto t_emf = t_fluxes; + tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); + auto t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); + auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } - tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Any package modifications to the fluxes. e.g.: // 1. Flux-CT calculations for B field transport // 2. Zero fluxes through poles // etc - auto t_fix_flux = tl.AddTask(t_flux_bounds, Packages::FixFlux, md_sub_step_init.get()); + auto t_fix_flux = tl.AddTask(t_emf, Packages::FixFlux, md_sub_step_init.get()); // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); diff --git a/pars/emhd/bondi_viscous.par b/pars/emhd/bondi_viscous.par index 16b0fead..efdd7c0a 100644 --- a/pars/emhd/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -64,10 +64,13 @@ rs = 8.0 disable_floors = true -#outer_x1 = dirichlet -#inner_x1 = dirichlet +outer_x1 = dirichlet +inner_x1 = dirichlet check_inflow_outer_x1 = false -#check_inflow_inner_x1 = false +check_inflow_inner_x1 = false +# Force outflow bounds for EMHD vars +outflow_EMHD_inner_x1 = true +outflow_EMHD_outer_x1 = true verbose = 1 @@ -76,9 +79,9 @@ extra_checks = 1 file_type = hdf5 -dt = 100.0 +dt = 10.0 single_precision_output = false -ghost_zones = false +ghost_zones = true variables = prims, solve_norm, solve_fail diff --git a/pars/smr/orszag_tang_refined.par b/pars/smr/orszag_tang_refined.par index e6665b56..6765b1e7 100644 --- a/pars/smr/orszag_tang_refined.par +++ b/pars/smr/orszag_tang_refined.par @@ -41,7 +41,6 @@ integrator = rk2 type = kharma -nghost = 6 cfl = 0.9 diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index df2abecf..272dbbd7 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -24,7 +24,6 @@ default: - dnf -y install hostname environment-modules git mpich fftw bzip2 - source /etc/profile - module load mpi/mpich-x86_64 - - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml @@ -56,12 +55,14 @@ build: - dnf -y install hostname environment-modules cmake mpich-devel fftw-devel - source /etc/profile - module load mpi/mpich-x86_64 + - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba script: - ./make.sh clean hdf5 artifacts: paths: - kharma.* - make_args + - bin/micromamba #Run all tests in parallel tests: diff --git a/scripts/ci/nvhpc.yml b/scripts/ci/nvhpc.yml index 589b9ed7..7cb2bc72 100644 --- a/scripts/ci/nvhpc.yml +++ b/scripts/ci/nvhpc.yml @@ -26,7 +26,6 @@ default: # interleaved, and prints a summary of results. before_script: - export PATH="$HOME/.local/bin:$PATH" - - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba - eval "$(bin/micromamba shell hook -s posix)" - git clone -b dev https://github.com/AFD-Illinois/pyharm.git /pyharm - micromamba create -y -f /pyharm/environment.yml @@ -53,7 +52,7 @@ build: NPROC: 8 HOST_ARCH: NATIVE before_script: - - echo "Skipping pyharm install in build." + - curl -Ls $MAMBA_URL | tar -xvj bin/micromamba script: - export PREFIX_PATH=$PWD/external/hdf5 - ./make.sh clean cuda hdf5 @@ -61,6 +60,7 @@ build: paths: - kharma.* - make_args + - bin/micromamba #Run all tests in parallel tests: diff --git a/tests/emhdmodes/run.sh b/tests/emhdmodes/run.sh index c96ae5f3..668f51ef 100755 --- a/tests/emhdmodes/run.sh +++ b/tests/emhdmodes/run.sh @@ -33,7 +33,7 @@ conv_2d() { # 2D modes use small blocks, could pick up some problems at MPI ranks >> 1 # Just one default mode ALL_RES="32,64,128" -conv_2d emhd2d_weno GRMHD/reconstruction=weno5 "EMHD mode in 2D, WENO5" +conv_2d emhd2d_weno driver/reconstruction=weno5 "EMHD mode in 2D, WENO5" # Test that higher-order terms don't mess anything up conv_2d emhd2d_higher_order emhd/higher_order_terms=true "EMHD mode in 2D, higher order terms enabled" # Test we can use imex/EMHD and face CT From 76d346e210bc3252f028dd7c08928cbaaadcaa89 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 10 Oct 2023 10:04:50 -0500 Subject: [PATCH 174/219] Make ordering of GRMHD/EMHD ops consistent, drive-by fixes --- kharma/electrons/electrons.cpp | 6 ++-- kharma/emhd/emhd.cpp | 4 +-- kharma/inverter/inverter.cpp | 3 +- kharma/kharma_package.cpp | 51 +++++++++++++++++++++++++++------ kharma/main.cpp | 2 ++ kharma/prob/emhd/emhdmodes.hpp | 12 +++----- kharma/prob/post_initialize.cpp | 2 -- kharma/prob/problem.cpp | 4 +-- pars/emhd/bondi_viscous.par | 2 +- 9 files changed, 56 insertions(+), 30 deletions(-) diff --git a/kharma/electrons/electrons.cpp b/kharma/electrons/electrons.cpp index b6ac1c17..2ec96d61 100644 --- a/kharma/electrons/electrons.cpp +++ b/kharma/electrons/electrons.cpp @@ -263,11 +263,9 @@ void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse) auto pmb = rc->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto& U = rc->PackVariables({Metadata::Conserved}, cons_map); + auto& P = rc->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::Cell}, prims_map); + auto& U = rc->PackVariables({Metadata::Conserved, Metadata::Cell}, cons_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); - // And then the local density - GridScalar rho_P = rc->Get("cons.rho").data; const auto& G = pmb->coords; diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index a2db56e5..c76272e5 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -181,8 +181,6 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); @@ -193,6 +191,8 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) auto P = rc->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); const VarMap m_p(prims_map, false), m_u(cons_map, true); + if (U_E.GetDim(4) == 0) return; + const auto& G = pmb->coords; auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index a50ba646..15e7f86f 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -76,8 +76,7 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh } pkg->AddField("pflag", m); - // Don't operate if GRMHD variables are being evolved implicitly - // This package is still loaded because fixes + // Don't operate at the usual time if GRMHD variables are being evolved implicitly if (!implicit_grmhd) { pkg->BlockUtoP = Inverter::BlockUtoP; pkg->BoundaryUtoP = Inverter::BlockUtoP; diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index bb0b7aea..96099ef3 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -35,8 +35,10 @@ #include "types.hpp" -// TODO take & accumulate TaskStatus? Useful for ::incomplete if we ever want to do that -// TODO continue meshification until all is mesh +// TODO clearly this needs a better concept of ordering. +// probably this means something that returns an ordered list of packages +// for the given operation, based on... declared dependencies? +// it could also use full meshification & return codes TaskStatus Packages::FixFlux(MeshData *md) { @@ -58,8 +60,8 @@ TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool Flag("BlockUtoP"); // Apply UtoP from B_CT first, as this fills cons.B at cell centers auto pmb = rc->GetBlockPointer(); - auto pkgs = pmb->packages.AllPackages(); - if (pkgs.count("B_CT")) { + auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + if (kpackages.count("B_CT")) { KHARMAPackage *pkpackage = pmb->packages.Get("B_CT"); if (pkpackage->BlockUtoP != nullptr) { Flag("BlockUtoP_B_CT"); @@ -67,9 +69,17 @@ TaskStatus Packages::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool EndFlag(); } } - auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + // Then GRMHD, as some packages require GRMHD prims in place for U->P + if (kpackages.count("Inverter")) { + KHARMAPackage *pkpackage = pmb->packages.Get("Inverter"); + if (pkpackage->BlockUtoP != nullptr) { + Flag("BlockUtoP_Inverter"); + pkpackage->BlockUtoP(rc, domain, coarse); + EndFlag(); + } + } for (auto kpackage : kpackages) { - if (kpackage.second->BlockUtoP != nullptr && kpackage.first != "B_CT") { + if (kpackage.second->BlockUtoP != nullptr && kpackage.first != "B_CT" && kpackage.first != "Inverter") { Flag("BlockUtoP_"+kpackage.first); kpackage.second->BlockUtoP(rc, domain, coarse); EndFlag(); @@ -91,9 +101,18 @@ TaskStatus Packages::MeshUtoP(MeshData *md, IndexDomain domain, bool coars TaskStatus Packages::BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag("BoundaryUtoP"); + auto pmb = rc->GetBlockPointer(); auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + if (kpackages.count("Inverter")) { + KHARMAPackage *pkpackage = pmb->packages.Get("Inverter"); + if (pkpackage->BoundaryUtoP != nullptr) { + Flag("BoundaryUtoP_Inverter"); + pkpackage->BoundaryUtoP(rc, domain, coarse); + EndFlag(); + } + } for (auto kpackage : kpackages) { - if (kpackage.second->BoundaryUtoP != nullptr) { + if (kpackage.second->BoundaryUtoP != nullptr && kpackage.first != "Inverter") { Flag("BoundaryUtoP_"+kpackage.first); kpackage.second->BoundaryUtoP(rc, domain, coarse); EndFlag(); @@ -106,13 +125,27 @@ TaskStatus Packages::BoundaryUtoP(MeshBlockData *rc, IndexDomain domain, b TaskStatus Packages::BoundaryPtoUElseUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { Flag("DomainBoundaryLockstep"); + auto pmb = rc->GetBlockPointer(); auto kpackages = rc->GetBlockPointer()->packages.AllPackagesOfType(); + // Some downstream UtoP rely on GRMHD prims, some cons + if (kpackages.count("GRMHD")) { + KHARMAPackage *pkpackage = pmb->packages.Get("GRMHD"); + if (pkpackage->DomainBoundaryPtoU != nullptr) { + Flag("DomainBoundaryPtoU_GRMHD"); + pkpackage->DomainBoundaryPtoU(rc, domain, coarse); + EndFlag(); + } else if (pkpackage->BoundaryUtoP != nullptr) { // This won't be called + Flag("DomainBoundaryUtoP_GRMHD"); + pkpackage->BoundaryUtoP(rc, domain, coarse); + EndFlag(); + } + } for (auto kpackage : kpackages) { - if (kpackage.second->DomainBoundaryPtoU != nullptr) { + if (kpackage.second->DomainBoundaryPtoU != nullptr && kpackage.first != "GRMHD") { Flag("DomainBoundaryPtoU_"+kpackage.first); kpackage.second->DomainBoundaryPtoU(rc, domain, coarse); EndFlag(); - } else if (kpackage.second->BoundaryUtoP != nullptr) { + } else if (kpackage.second->BoundaryUtoP != nullptr && kpackage.first != "GRMHD") { Flag("DomainBoundaryUtoP_"+kpackage.first); kpackage.second->BoundaryUtoP(rc, domain, coarse); EndFlag(); diff --git a/kharma/main.cpp b/kharma/main.cpp index baf7f1a0..4d319fdc 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -205,6 +205,8 @@ int main(int argc, char *argv[]) KHARMA::PostInitialize(pin, pmesh, is_restart); EndFlag(); + // TODO output parsed parameters *here*, now we have everything including any problem configs for B field + // Begin code block to ensure driver is cleaned up { if (MPIRank0()) { diff --git a/kharma/prob/emhd/emhdmodes.hpp b/kharma/prob/emhd/emhdmodes.hpp index 371575d2..ba0805a7 100644 --- a/kharma/prob/emhd/emhdmodes.hpp +++ b/kharma/prob/emhd/emhdmodes.hpp @@ -136,17 +136,13 @@ TaskStatus InitializeEMHDModes(std::shared_ptr>& rc, Paramet // Zeros are q, dP, and bsq, only needed for torus closure EMHD::set_parameters(G, rho(k, j, i), u(k, j, i), 0., 0., 0., emhd_params, gam, j, i, tau, chi_e, nu_e); Real Theta = (gam - 1) * u(k, j, i) / rho(k, j, i); - Real q_tilde = q(k, j, i); - Real dP_tilde = dP(k, j, i); - if (emhd_params.higher_order_terms) { - q_tilde *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho(k, j, i) * Theta * Theta)) : 0.; - dP_tilde *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho(k, j, i) * Theta)) : 0.; - } - q(k, j, i) = q_tilde; - dP(k, j, i) = dP_tilde; + q(k, j, i) *= (chi_e != 0) ? m::sqrt(tau / (chi_e * rho(k, j, i) * Theta * Theta)) : 0.; + dP(k, j, i) *= (nu_e != 0) ? m::sqrt(tau / (nu_e * rho(k, j, i) * Theta)) : 0.; } } ); + Flux::BlockPtoU(rc.get(), IndexDomain::interior, false); + return TaskStatus::complete; } diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 30e09004..16a007da 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -174,6 +174,4 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) KBoundaries::FreezeDirichlet(md); // This is the first sync if there is no B field KHARMADriver::SyncAllBounds(md); - - // TODO output parsed parameters now we have *everything* including any problem configs for B field } diff --git a/kharma/prob/problem.cpp b/kharma/prob/problem.cpp index 01897edb..67ab0354 100644 --- a/kharma/prob/problem.cpp +++ b/kharma/prob/problem.cpp @@ -161,8 +161,8 @@ void KHARMA::ProblemGenerator(MeshBlock *pmb, ParameterInput *pin) // the magnetic field, which is added in PostInitialize, after all blocks // are filled with other variables (it can be related to density averages which // require correct ghost zones) - // ALL OTHER VARIABLES, however, must fill U if a magnetic field will depend on - // them in any way, as conserved variables are MPI-synchronized + // If the B field will depend on the conserved variables (for some reason?) + // they must be computed by the particular problem. EndFlag(); } diff --git a/pars/emhd/bondi_viscous.par b/pars/emhd/bondi_viscous.par index efdd7c0a..8aa45d4a 100644 --- a/pars/emhd/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -81,7 +81,7 @@ extra_checks = 1 file_type = hdf5 dt = 10.0 single_precision_output = false -ghost_zones = true +ghost_zones = false variables = prims, solve_norm, solve_fail From 20c2a11cd42e949ad49096769ca8dcd53f4eaed1 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 10 Oct 2023 09:12:31 -0600 Subject: [PATCH 175/219] Revert "Get rid of sync_prims" This reverts commit d7d61e79c420b1e9bff7d10d93ca4fed6b435dc7. --- kharma/b_flux_ct/b_flux_ct.cpp | 9 ++++++ kharma/boundaries/boundaries.cpp | 49 ++++++++++++++++++++++++++++---- kharma/boundaries/boundaries.hpp | 5 ++++ kharma/driver/kharma_driver.cpp | 30 ++++++++++++++----- kharma/grmhd/grmhd.cpp | 6 ++-- kharma/inverter/inverter.cpp | 1 - 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 3be20c15..c2169c82 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -109,6 +109,15 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

Get("Driver")->Param>("cons_flags"); flags_cons.insert(flags_cons.end(), flags_b.begin(), flags_b.end()); + // Always sync B field conserved var, for standardization with B_CT + // god std::vector is verbose + if (std::find(flags_cons.begin(), flags_cons.end(), Metadata::FillGhost) == flags_cons.end()) { + flags_cons.push_back(Metadata::FillGhost); + } + if (std::find(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost) != flags_prim.end()) { + flags_prim.erase(std::remove(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost), flags_prim.end()); + } + auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); m = Metadata(flags_cons, s_vector); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 23a415c1..9d5ffcd4 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -340,13 +340,24 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD } } - // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. - // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE + // If we applied the domain boundary to primitives (as we usually do)... if (!params.Get("domain_bounds_on_conserved")) { - // Only the GRMHD package defines a BoundaryPtoU - Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); + bool sync_prims = rc->GetBlockPointer()->packages.Get("Driver")->Param("sync_prims"); + // There are two modes of operation here: + if (sync_prims) { + // 1. ImEx w/o AMR: + // PRIMITIVE variables (only) are marked FillGhost + // So, run PtoU on EVERYTHING (and correct the B field) + CorrectBPrimitive(rc, domain, coarse); + Flux::BlockPtoU(rc.get(), domain, coarse); + } else { + // 2. Normal (KHARMA driver, ImEx w/AMR): + // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. + // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE + Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); + } } else { - // Or, apply the boundary to the conserved GRMHD variables, too! + // These get applied the same way regardless of driver Packages::BoundaryUtoP(rc.get(), domain, coarse); } @@ -373,6 +384,34 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom ); } +void KBoundaries::CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse) +{ + Flag("CorrectBPrimitive"); + std::shared_ptr pmb = rc->GetBlockPointer(); + auto B_P = rc->PackVariables(std::vector{"prims.B"}); + // Return if no field to correct + if (B_P.GetDim(4) == 0) return; + + const auto& G = pmb->coords; + + const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + const int dir = BoundaryDirection(domain); + const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) + : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) + : bounds.GetBoundsK(IndexDomain::interior)); + const int ref = BoundaryIsInner(domain) ? range.s : range.e; + + pmb->par_for_bndry( + "Correct_B_P", IndexRange{0,NVEC-1}, domain, CC, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) + / G.gdet(Loci::center, j, i); + } + ); + + EndFlag(); +} + TaskStatus KBoundaries::FixFlux(MeshData *md) { auto pmesh = md->GetMeshPointer(); diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index fb910995..dde70a60 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -84,6 +84,11 @@ TaskStatus FixFlux(MeshData *rc); */ void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse); +/** + * Correct for geometry when applying primitive B field boundaries + */ +void CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse); + /** * Check for velocity toward the simulation domain in a zone, and eliminate it. */ diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 53d9382e..6e9a4fdc 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -55,7 +55,7 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // Driver options // The two current drivers are "kharma" or "imex", with the former being the usual KHARMA // driver (formerly HARM driver), and the latter supporting implicit stepping of some or all variables - // Mostly, packages should react to options rather than the driver name + // Mostly, packages should react to e.g. the "sync_prims" option rather than the driver name bool do_emhd = pin->GetOrAddBoolean("emhd", "on", false); std::string driver_type_s = pin->GetOrAddString("driver", "type", (do_emhd) ? "imex" : "kharma"); DriverType driver_type; @@ -137,10 +137,25 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std bool prims_are_fundamental = driver_type != DriverType::kharma; params.Add("prims_are_fundamental", prims_are_fundamental); - // Now that we're an AMR code, though, we always *sync* conserved variables - // This means "emulating" syncing primitives in some cases, by running PtoU -> sync -> UtoP - params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive")}); - params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); + // Which variables we *actually send* via Parthenon/MPI may differ, however. + // Prolongation/restriction should happen on conserved vars, so we must sync + // those in multilevel meshes. If prims are funcamental but not sync'd, + // we "emulate" syncing them with PtoU/UtoP on boundaries + bool sync_prims = prims_are_fundamental && + (!pin->DoesParameterExist("parthenon/mesh", "numlevel") || + pin->GetInteger("parthenon/mesh", "numlevel") == 1); + params.Add("sync_prims", sync_prims); + // Finally, we set default flags for primitive and conserved variables + // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated + if (sync_prims) { + // If we're not in AMR, we can sync primitive variables directly + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::WithFluxes, Metadata::Conserved}); + } else { + // If we're in AMR or using the KHARMA driver anyway, sync conserved vars + params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive")}); + params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved}); + } return pkg; } @@ -173,7 +188,8 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh // Note this has the side effect of filling U in some zones, // which must be replaced during e.g. startup code when primitive values should be truth bool prims_are_fundamental = params.Get("prims_are_fundamental"); - if (prims_are_fundamental) { + bool sync_prims = params.Get("sync_prims"); + if (prims_are_fundamental && !sync_prims) { TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_ptou_final(0); int i_task = 0; @@ -199,7 +215,7 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh EndFlag(); // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims - if (prims_are_fundamental) { + if (prims_are_fundamental && !sync_prims) { TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); int i_task = 0; diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index fedb683a..e1d7746a 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -145,8 +145,10 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

GetOrAddBoolean("GRMHD", "sync_utop_seed", true)) { + // Only necessary to add here if syncing conserved vars + // Note some startup behavior relies on having the GRHD prims marked for syncing, + // so disable sync_utop_seed at your peril + if (!driver.Get("sync_prims") && pin->GetOrAddBoolean("GRMHD", "sync_utop_seed", true)) { flags_prim.push_back(Metadata::FillGhost); } diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index 15e7f86f..d669ff64 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -79,7 +79,6 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh // Don't operate at the usual time if GRMHD variables are being evolved implicitly if (!implicit_grmhd) { pkg->BlockUtoP = Inverter::BlockUtoP; - pkg->BoundaryUtoP = Inverter::BlockUtoP; } pkg->PostStepDiagnosticsMesh = Inverter::PostStepDiagnostics; From 0541185765a21996e8c47f769c04d8c6ce68de2a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 10 Oct 2023 17:23:42 -0600 Subject: [PATCH 176/219] Sync primitive variables if they're fundamental A few commits back I removed sync_prims, reasoning conserved variables should be prolongated/restricted, and could always be recovered from each other. Both points were wrong: primitive vars can be prolongated and restricted on boundaries fine, though the latter is not ideal. And, in EMHD, it is not straightforward to recover P from U, as this is normally done inline with the computation of the next step's state. This commit switches to syncing primitive variables (sync_prims) anytime they're fundamental (ImEx and simple drivers, "prims_are_fundamental") Note the "conserved" B field is *always* what is sync'd, regardless of the other primitive or conserved variables. It also avoids loading the inverter package if GRMHD is implicitly evolved, and expands some computed domains related to B to work at the last prolongation operator bug before AMR. --- kharma/b_ct/b_ct.cpp | 36 +++++++++-------- kharma/b_ct/b_ct.hpp | 14 +++---- kharma/b_flux_ct/b_flux_ct.cpp | 24 ++++------- kharma/b_flux_ct/b_flux_ct.hpp | 2 +- kharma/boundaries/boundaries.cpp | 52 ++++++++++++------------ kharma/driver/kharma_driver.cpp | 68 ++++++++++---------------------- kharma/driver/kharma_step.cpp | 6 +-- kharma/flux/get_flux.hpp | 2 +- kharma/inverter/inverter.cpp | 16 ++++---- kharma/kharma.cpp | 7 ++-- 10 files changed, 96 insertions(+), 131 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 2243487b..a6f6076a 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -46,6 +46,7 @@ using namespace parthenon; using parthenon::refinement_ops::ProlongateSharedMinMod; using parthenon::refinement_ops::RestrictAverage; +using parthenon::refinement_ops::ProlongateInternalAverage; std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared_ptr& packages) { @@ -93,6 +94,8 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared m = Metadata(flags_cons_f); if (!lazy_prolongation) m.RegisterRefinementOps(); + else + m.RegisterRefinementOps(); pkg->AddField("cons.fB", m); // Cell-centered versions. Needed for BS, not for other schemes. @@ -164,7 +167,7 @@ TaskStatus B_CT::MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) return TaskStatus::complete; } -void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); const int ndim = pmb->pmy_mesh->ndim; @@ -204,6 +207,8 @@ void B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) B_U(v, k, j, i) = B_P(v, k, j, i) * G.gdet(Loci::center, j, i); } ); + + return TaskStatus::complete; } TaskStatus B_CT::CalculateEMF(MeshData *md) @@ -215,12 +220,9 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, -1, 2); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::entire, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::entire, 1, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; - const int kd = ndim > 2 ? 1 : 0; - const int jd = ndim > 1 ? 1 : 0; - const int id = ndim > 0 ? 1 : 0; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); @@ -269,8 +271,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) auto& B_U = md->PackVariablesAndFluxes(std::vector{"cons.B"}); auto& B_P = md->PackVariables(std::vector{"prims.B"}); // emf in center == -v x B - const IndexRange3 bc = KDomain::GetRange(md, IndexDomain::entire); - pmb0->par_for("B_CT_emfc", block.s, block.e, bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, + pmb0->par_for("B_CT_emfc", block.s, block.e, b.ks, b.ke, b.js, b.je, b.is, b.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { VLOOP emfc(bl, v, k, j, i) = 0.; VLOOP3 emfc(bl, x, k, j, i) -= antisym(v, w, x) * uvec(bl, v, k, j, i) * B_U(bl, w, k, j, i); @@ -278,19 +279,22 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) ); if (scheme == "gs05_0") { + const int kd = ndim > 2 ? 1 : 0; + const int jd = ndim > 1 ? 1 : 0; + const int id = ndim > 0 ? 1 : 0; pmb0->par_for("B_CT_emf_GS05_0", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(bl); // Just subtract centered emf from twice the face version // More stable for planar flows even without anything fancy emf_pack(bl, E1, 0, k, j, i) = 2 * emf_pack(bl, E1, 0, k, j, i) - - 0.25*(emfc(bl, V1, k, j, i) + emfc(bl, V1, k, j - jd, i) - + emfc(bl, V1, k, j - jd, i) + emfc(bl, V1, k - kd, j - jd, i)); + - 0.25*(emfc(bl, V1, k, j, i) + emfc(bl, V1, k, j - jd, i) + + emfc(bl, V1, k, j - jd, i) + emfc(bl, V1, k - kd, j - jd, i)); emf_pack(bl, E2, 0, k, j, i) = 2 * emf_pack(bl, E2, 0, k, j, i) - - 0.25*(emfc(bl, V2, k, j, i) + emfc(bl, V2, k, j, i - id) - + emfc(bl, V2, k - kd, j, i) + emfc(bl, V2, k - kd, j, i - id)); + - 0.25*(emfc(bl, V2, k, j, i) + emfc(bl, V2, k, j, i - id) + + emfc(bl, V2, k - kd, j, i) + emfc(bl, V2, k - kd, j, i - id)); emf_pack(bl, E3, 0, k, j, i) = 2 * emf_pack(bl, E3, 0, k, j, i) - - 0.25*(emfc(bl, V3, k, j, i) + emfc(bl, V3, k, j, i - id) + - 0.25*(emfc(bl, V3, k, j, i) + emfc(bl, V3, k, j, i - id) + emfc(bl, V3, k, j - jd, i) + emfc(bl, V3, k, j - jd, i - id)); } ); @@ -301,7 +305,6 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) pmb0->par_for("B_CT_emf_GS05_c", block.s, block.e, b1.ks, b1.ke, b1.js, b1.je, b1.is, b1.ie, KOKKOS_LAMBDA (const int &bl, const int &k, const int &j, const int &i) { const auto& G = B_U.GetCoords(bl); - // "simple" flux + upwinding method, Stone & Gardiner '09 but also in Stone+08 etc. // Upwinded differences take in order (1-indexed): // 1. EMF component direction to calculate @@ -309,6 +312,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // 3. Direction of upwinding // ...then zone number... // and finally, a boolean indicating a leftward (e.g., i-3/4) vs rightward (i-1/4) position + // TODO(BSP) This doesn't properly support 2D. Yell when it's chosen? if (ndim > 2) { emf_pack(bl, E1, 0, k, j, i) += 0.25*(upwind_diff(B_U(bl), emfc(bl), uvecf(bl), 1, 3, 2, k, j, i, false) @@ -344,8 +348,8 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::entire, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::entire, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index ab2b291d..b9db5649 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -64,7 +64,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*mbd, IndexDomain domain, bool coarse=false); +TaskStatus BlockUtoP(MeshBlockData *mbd, IndexDomain domain, bool coarse=false); TaskStatus MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** @@ -283,9 +283,10 @@ struct ProlongateInternalOlivares { const int fk = (DIM > 2) ? (k - ckb.s) * 2 + kb.s : kb.s; // Coefficients selecting a particular formula (see Olivares et al. 2019) - // TODO options here. This corresponds to Cunningham, but we could have: - // 1. differences of squares of zone dimesnions (Toth) - // 2. heuristic based on flux difference of top vs bottom halves (Olivares) + // TODO options here. There are 3 presented: + // 1. Zeros (Cunningham) + // 2. differences of squares of zone dimesnions (Toth) + // 3. heuristic based on flux difference of top vs bottom halves (Olivares) // constexpr Real a[3] = {0., 0., 0.}; const Real a[3] = {(SQR(coords.Dxc<2>(fj)) - SQR(coords.Dxc<3>(fk))) / (SQR(coords.Dxc<2>(fj)) + SQR(coords.Dxc<3>(fk))), (SQR(coords.Dxc<3>(fk)) - SQR(coords.Dxc<1>(fi))) / (SQR(coords.Dxc<3>(fk)) + SQR(coords.Dxc<1>(fi))), @@ -322,11 +323,6 @@ struct ProlongateInternalOlivares { + coeff[elem][2]*F(fine, coords, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, coords, l, m, n, fk, fj, fi)) ) / coords.Volume(fk+off_k, fj+off_j, fi+off_i); - //printf("%d %d\n", fi, fj); - // if (fi == 56 && fj == 70) - // printf("I used dir %d offset %d %d %d, %d %d %d\n", me+1, - // off_k-diff_k, off_j-diff_j, off_i-diff_i, - // off_k+diff_k, off_j+diff_j, off_i+diff_i); } } }; diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index c2169c82..11f14e3a 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -101,22 +101,11 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

flags_b = {Metadata::Cell, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; - - // "primitive" B field is field, "conserved" is flux - auto flags_prim = packages->Get("Driver")->Param>("prim_flags"); - flags_prim.insert(flags_prim.end(), flags_b.begin(), flags_b.end()); - auto flags_cons = packages->Get("Driver")->Param>("cons_flags"); - flags_cons.insert(flags_cons.end(), flags_b.begin(), flags_b.end()); - - // Always sync B field conserved var, for standardization with B_CT - // god std::vector is verbose - if (std::find(flags_cons.begin(), flags_cons.end(), Metadata::FillGhost) == flags_cons.end()) { - flags_cons.push_back(Metadata::FillGhost); - } - if (std::find(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost) != flags_prim.end()) { - flags_prim.erase(std::remove(flags_prim.begin(), flags_prim.end(), Metadata::FillGhost), flags_prim.end()); - } + // We always mark conserved B to be sync'd for consistency, since it's strictly required for B_CT/AMR + std::vector flags_prim = {Metadata::Real, Metadata::Derived, Metadata::GetUserFlag("Primitive"), + Metadata::Cell, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; + std::vector flags_cons = {Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::FillGhost, Metadata::WithFluxes, Metadata::Conserved, + Metadata::Cell, Metadata::GetUserFlag("MHD"), areWeImplicit, Metadata::Vector}; auto m = Metadata(flags_prim, s_vector); pkg->AddField("prims.B", m); @@ -192,7 +181,7 @@ void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse) } ); } -void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) +TaskStatus BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); @@ -213,6 +202,7 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) B_P(mu, k, j, i) = B_U(mu, k, j, i) / G.gdet(Loci::center, j, i); } ); + return TaskStatus::complete; } void MeshPtoU(MeshData *md, IndexDomain domain, bool coarse) diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index 7de3f6c9..bb266fbc 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -63,7 +63,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*md, IndexDomain domain, bool coarse=false); +TaskStatus BlockUtoP(MeshBlockData *md, IndexDomain domain, bool coarse=false); void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); /** diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 9d5ffcd4..87399e60 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -36,12 +36,15 @@ #include "decs.hpp" #include "domain.hpp" #include "kharma.hpp" -#include "flux.hpp" #include "flux_functions.hpp" #include "grmhd_functions.hpp" #include "pack.hpp" #include "types.hpp" +#include "b_ct.hpp" +#include "b_flux_ct.hpp" +#include "flux.hpp" + // Parthenon's boundaries #include @@ -258,13 +261,6 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD const auto btype_name = params.Get(bname); const auto bdir = BoundaryDirection(bface); - // If we're pretending to sync primitives, but applying physical bounds - // to conserved variables, make sure we're up to date - if (pmb->packages.Get("Driver")->Param("prims_are_fundamental") && - params.Get("domain_bounds_on_conserved")) { - Flux::BlockPtoU_Send(rc.get(), domain, coarse); - } - Flag("Apply "+bname+" boundary: "+btype_name); pkg->KBoundaries[bface](rc, coarse); EndFlag(); @@ -340,25 +336,31 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD } } - // If we applied the domain boundary to primitives (as we usually do)... - if (!params.Get("domain_bounds_on_conserved")) { - bool sync_prims = rc->GetBlockPointer()->packages.Get("Driver")->Param("sync_prims"); - // There are two modes of operation here: - if (sync_prims) { - // 1. ImEx w/o AMR: - // PRIMITIVE variables (only) are marked FillGhost - // So, run PtoU on EVERYTHING (and correct the B field) - CorrectBPrimitive(rc, domain, coarse); - Flux::BlockPtoU(rc.get(), domain, coarse); - } else { - // 2. Normal (KHARMA driver, ImEx w/AMR): - // CONSERVED variables are marked FillGhost, plus FLUID PRIMITIVES. - // So, run PtoU on FLUID, and UtoP on EVERYTHING ELSE - Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); + bool sync_prims = rc->GetBlockPointer()->packages.Get("Driver")->Param("sync_prims"); + // There are two modes of operation here: + if (sync_prims) { + // 1. Exchange/prolongate/restrict PRIMITIVE variables: (ImEx driver) + // Primitive variables and conserved B field are marked FillGhost + // Explicitly run UtoP on B field, then PtoU on everything + // TODO there should be a set of B field wrappers that dispatch this + auto pkgs = pmb->packages.AllPackages(); + if (pkgs.count("B_FluxCT")) { + B_FluxCT::BlockUtoP(rc.get(), IndexDomain::entire); + } else if (pkgs.count("B_CT")) { + B_CT::BlockUtoP(rc.get(), IndexDomain::entire); } + Flux::BlockPtoU(rc.get(), domain, coarse); } else { - // These get applied the same way regardless of driver - Packages::BoundaryUtoP(rc.get(), domain, coarse); + // 2. Exchange/prolongate/restrict CONSERVED variables: (KHARMA driver, maybe ImEx+AMR) + // Conserved variables are marked FillGhost, plus FLUID PRIMITIVES. + if (!params.Get("domain_bounds_on_conserved")) { + // To apply primitive boundaries to GRMHD, we run PtoU on that ONLY, + // and UtoP on EVERYTHING ELSE + Packages::BoundaryPtoUElseUtoP(rc.get(), domain, coarse); + } else { + // If we want to apply boundaries to conserved vars, just run UtoP on EVERYTHING + Packages::BoundaryUtoP(rc.get(), domain, coarse); + } } EndFlag(); diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 6e9a4fdc..b1c6e0f9 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -131,24 +131,13 @@ std::shared_ptr KHARMADriver::Initialize(ParameterInput *pin, std // but which should not be evolved (or more importantly, sync'd) during main stepping Metadata::AddUserFlag("StartupOnly"); - // This marks whether we consider primitive or conserved state to be - // the ground truth when updating values in a step. - // Currently "imex" and "simple" drivers both update primitive vars - bool prims_are_fundamental = driver_type != DriverType::kharma; - params.Add("prims_are_fundamental", prims_are_fundamental); - - // Which variables we *actually send* via Parthenon/MPI may differ, however. - // Prolongation/restriction should happen on conserved vars, so we must sync - // those in multilevel meshes. If prims are funcamental but not sync'd, - // we "emulate" syncing them with PtoU/UtoP on boundaries - bool sync_prims = prims_are_fundamental && - (!pin->DoesParameterExist("parthenon/mesh", "numlevel") || - pin->GetInteger("parthenon/mesh", "numlevel") == 1); + // Synchronize primitive variables unless we're using the KHARMA driver that specifically doesn't + // This includes for AMR w/ImEx driver + // Note the "conserved" B field is always sync'd. The "primitive" version only differs by sqrt(-g) + bool sync_prims = driver_type != DriverType::kharma; params.Add("sync_prims", sync_prims); - // Finally, we set default flags for primitive and conserved variables - // This first mode is only for simulations without AMR/SMR, as primitives shouldn't be prolongated if (sync_prims) { - // If we're not in AMR, we can sync primitive variables directly + // For ImEx/simple drivers, sync/prolongate/restrict primitive variables directly params.Add("prim_flags", std::vector{Metadata::Real, Metadata::Derived, Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}); params.Add("cons_flags", std::vector{Metadata::Real, Metadata::Independent, Metadata::Restart, Metadata::WithFluxes, Metadata::Conserved}); } else { @@ -180,42 +169,23 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh auto t_start_sync = t_start; // Pull the mesh pointer from mc1 so we can be a static method - auto ¶ms = mc1->GetMeshPointer()->packages.Get("Driver")->AllParams(); - bool multilevel = mc1->GetMeshPointer()->multilevel; - - // If we're "syncing primitive variables" but must exchange conserved vars to prolong/restrict them, - // make sure to run P->U, then sync, then U->P - // Note this has the side effect of filling U in some zones, - // which must be replaced during e.g. startup code when primitive values should be truth - bool prims_are_fundamental = params.Get("prims_are_fundamental"); - bool sync_prims = params.Get("sync_prims"); - if (prims_are_fundamental && !sync_prims) { - TaskID t_all_ptou[mc1->NumBlocks() * BOUNDARY_NFACES]; - TaskID t_ptou_final(0); - int i_task = 0; - for (int i_block = 0; i_block < mc1->NumBlocks(); i_block++) { - auto &rc = mc1->GetBlockData(i_block); - for (int i_bnd = 0; i_bnd < BOUNDARY_NFACES; i_bnd++) { - if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || - rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { - const auto bdomain = KBoundaries::BoundaryDomain((BoundaryFace) i_bnd); - t_all_ptou[i_task] = tl.AddTask(t_start, Flux::BlockPtoU_Send, rc.get(), bdomain, false); - t_ptou_final = t_ptou_final | t_all_ptou[i_task]; - i_task++; - } - } - } - t_start_sync = t_ptou_final; - } + auto pmesh = mc1->GetMeshPointer(); + auto ¶ms = pmesh->packages.Get("Driver")->AllParams(); + bool multilevel = pmesh->multilevel; + + // TODO PtoU for B field when sync_prims? - // The Parthenon exchange tasks include applying physical boundary conditions + // The Parthenon exchange tasks include applying physical boundary conditions now. + // We generally do not take advantage of this yet, but good to know when reasoning about initialization. Flag("ParthenonAddSync"); auto t_sync_done = parthenon::AddBoundaryExchangeTasks(t_start_sync, tl, mc1, multilevel); auto t_bounds = t_sync_done; EndFlag(); - // If we're "syncing primitive variables" but just exchanged conserved variables (B, implicit, etc), we need to recover the prims - if (prims_are_fundamental && !sync_prims) { + // We always just sync'd the "conserved" magnetic field + // Translate back to "primitive" (& cell-centered) field if that's what we'll be using + if (params.Get("sync_prims")) { + auto pkgs = pmesh->packages.AllPackages(); TaskID t_all_utop[mc1->NumBlocks() * BOUNDARY_NFACES]; TaskID t_utop_final(0); int i_task = 0; @@ -225,7 +195,11 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh if (rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::block || rc->GetBlockPointer()->boundary_flag[i_bnd] == BoundaryFlag::periodic) { const auto bdomain = KBoundaries::BoundaryDomain((BoundaryFace) i_bnd); - t_all_utop[i_task] = tl.AddTask(t_sync_done, Packages::BoundaryUtoP, rc.get(), bdomain, false); + if (pkgs.count("B_FluxCT")) { + t_all_utop[i_task] = tl.AddTask(t_sync_done, B_FluxCT::BlockUtoP, rc.get(), bdomain, false); + } else if (pkgs.count("B_CT")) { + t_all_utop[i_task] = tl.AddTask(t_sync_done, B_CT::BlockUtoP, rc.get(), bdomain, false); + } t_utop_final = t_utop_final | t_all_utop[i_task]; i_task++; } diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index e5619dbd..9adb64ec 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -162,16 +162,16 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize - auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition + auto &md_b_ct = pmesh->mesh_data.AddShallow("B_CT", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); - auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); + auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_b_ct); } } // Any package modifications to the fluxes. e.g.: // 1. Flux-CT calculations for B field transport // 2. Zero fluxes through poles - // etc + // etc auto t_fix_flux = tl.AddTask(t_emf, Packages::FixFlux, md_sub_step_init.get()); // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" diff --git a/kharma/flux/get_flux.hpp b/kharma/flux/get_flux.hpp index 5f2edd77..9f9da4f8 100644 --- a/kharma/flux/get_flux.hpp +++ b/kharma/flux/get_flux.hpp @@ -119,7 +119,7 @@ inline TaskStatus GetFlux(MeshData *md) const auto& Fr_all = md->PackVariables(std::vector{"Flux.Fr"}); // Get the domain size - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, -1, 1); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, -1, 2); // Get other sizes we need const int n1 = pmb0->cellbounds.ncellsi(IndexDomain::entire); const IndexRange block = IndexRange{0, cmax.GetDim(5) - 1}; diff --git a/kharma/inverter/inverter.cpp b/kharma/inverter/inverter.cpp index d669ff64..28919d35 100644 --- a/kharma/inverter/inverter.cpp +++ b/kharma/inverter/inverter.cpp @@ -64,22 +64,20 @@ std::shared_ptr Inverter::Initialize(ParameterInput *pin, std::sh // TODO add version attempting to recover from entropy, stuff like that // Flag denoting UtoP inversion failures - // Only needed if we're actually calling UtoP, but always allocated as it's retrieved often - // Needs boundary sync if treating primitive variables as fundamental - bool prims_are_fundamental = packages->Get("Driver")->Param("prims_are_fundamental"); - bool implicit_grmhd = packages->Get("GRMHD")->Param("implicit"); + // Needs boundary sync if treating primitive variables as fundamental, since we need to + // avoid failed neighbors when fixing. + bool sync_prims = packages->Get("Driver")->Param("sync_prims"); Metadata m; - if (prims_are_fundamental && !implicit_grmhd) { + if (sync_prims) { m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy, Metadata::FillGhost}); } else { m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); } pkg->AddField("pflag", m); - // Don't operate at the usual time if GRMHD variables are being evolved implicitly - if (!implicit_grmhd) { - pkg->BlockUtoP = Inverter::BlockUtoP; - } + // We exist basically to do this + pkg->BlockUtoP = Inverter::BlockUtoP; + pkg->BoundaryUtoP = Inverter::BlockUtoP; pkg->PostStepDiagnosticsMesh = Inverter::PostStepDiagnostics; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index 65748f00..fa03b9f0 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -301,8 +301,6 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) } // GRMHD needs globals to mark packages auto t_grmhd = tl.AddTask(t_globals | t_driver, KHARMA::AddPackage, packages, GRMHD::Initialize, pin.get()); - // Inverter (TODO: split out fixups, then don't load this when GRMHD isn't loaded) - auto t_inverter = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Inverter::Initialize, pin.get()); // Reductions, needed for most other packages auto t_reductions = tl.AddTask(t_none, KHARMA::AddPackage, packages, Reductions::Initialize, pin.get()); @@ -373,11 +371,14 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); // Load the implicit package last, and only if there are any variables which need implicit evolution - auto all_implicit = Metadata::FlagCollection(Metadata::GetUserFlag("Implicit")); int n_implicit = PackDimension(packages.get(), Metadata::GetUserFlag("Implicit")); if (n_implicit > 0) { KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); } + // Only load the inverter if GRMHD isn't being evolved implicitly + if (PackDimension(packages.get(), {Metadata::GetUserFlag("Implicit"), Metadata::GetUserFlag("MHD")}) < 5) { + KHARMA::AddPackage(packages, Inverter::Initialize, pin.get()); + } #if DEBUG // Carry the ParameterInput with us, for generating outputs whenever we want From 4fc472fc9c9e553b88d2aad20e71641c815ddf71 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 11 Oct 2023 09:05:06 -0600 Subject: [PATCH 177/219] Fix the build --- kharma/b_ct/b_ct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index a6f6076a..1627bf79 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -177,7 +177,7 @@ TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coa auto B_P = rc->PackVariables(std::vector{"prims.B"}); const auto& G = pmb->coords; // Return if we're not syncing U & P at all (e.g. edges) - if (B_Uf.GetDim(4) == 0) return; + if (B_Uf.GetDim(4) == 0) return TaskStatus::complete; // TODO get rid of prims on faces probably From 218a875fbab08a0ed386b2ceaccd0e38a0b88634 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 11 Oct 2023 10:04:35 -0600 Subject: [PATCH 178/219] Fix floors so we don't need to load Inverter pkg --- kharma/b_ct/b_ct.cpp | 2 +- kharma/floors/floors.cpp | 5 +++++ kharma/kharma.cpp | 31 ++++++++++++++++++------------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 1627bf79..3751bd20 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -191,7 +191,7 @@ TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coa B_Pf(F3, 0, k, j, i) = B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i); } ); - // Average the primitive vals for zone centers (TODO right?) + // Average the primitive vals for zone centers const IndexRange3 bc = KDomain::GetRange(rc, domain, coarse); pmb->par_for("UtoP_B_center", bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index 94ab5db8..e1a7ee76 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -138,6 +138,11 @@ std::shared_ptr Floors::Initialize(ParameterInput *pin, std::shar // Should switch these to "Integer" fields when Parthenon supports it Metadata m = Metadata({Metadata::Real, Metadata::Cell, Metadata::Derived, Metadata::OneCopy}); pkg->AddField("fflag", m); + // When not using UtoP, we still need a dummy copy of pflag, too + // TODO we shouldn't require pflag + if (!packages->AllPackages().count("Inverter")) { + pkg->AddField("pflag", m); + } pkg->BlockApplyFloors = Floors::ApplyGRMHDFloors; pkg->PostStepDiagnosticsMesh = Floors::PostStepDiagnostics; diff --git a/kharma/kharma.cpp b/kharma/kharma.cpp index fa03b9f0..0f13eb8a 100644 --- a/kharma/kharma.cpp +++ b/kharma/kharma.cpp @@ -291,21 +291,28 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) auto t_globals = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMA::InitializeGlobals, pin.get()); // Neither will grid output, as any mesh will get GRCoordinates objects // FieldIsOutput actually just checks for substring match, so this matches any coords. variable - if (FieldIsOutput(pin.get(), "coords.")) + if (FieldIsOutput(pin.get(), "coords.")) { auto t_coord_out = tl.AddTask(t_none, KHARMA::AddPackage, packages, CoordinateOutput::Initialize, pin.get()); + } // Driver package is the foundation auto t_driver = tl.AddTask(t_none, KHARMA::AddPackage, packages, KHARMADriver::Initialize, pin.get()); - // Floors package has no dependencies - if (!pin->GetOrAddBoolean("floors", "disable_floors", false)) { - auto t_floors = tl.AddTask(t_none, KHARMA::AddPackage, packages, Floors::Initialize, pin.get()); - } // GRMHD needs globals to mark packages auto t_grmhd = tl.AddTask(t_globals | t_driver, KHARMA::AddPackage, packages, GRMHD::Initialize, pin.get()); + // Only load the inverter if GRMHD/EMHD isn't being evolved implicitly + auto t_inverter = t_grmhd; + if (!pin->GetOrAddBoolean("GRMHD", "implicit", pin->GetOrAddBoolean("emhd", "on", false))) { + t_inverter = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Inverter::Initialize, pin.get()); + } + // Floors package depends on having pflag + if (!pin->GetOrAddBoolean("floors", "disable_floors", false)) { + auto t_floors = tl.AddTask(t_inverter, KHARMA::AddPackage, packages, Floors::Initialize, pin.get()); + } // Reductions, needed for most other packages auto t_reductions = tl.AddTask(t_none, KHARMA::AddPackage, packages, Reductions::Initialize, pin.get()); // B field solvers, to ensure divB ~= 0. // Bunch of logic here: basically we want to load <=1 solver with an encoded order of preference: + // 0. Anything user-specified // 1. Prefer B_CT if AMR since it's compatible // 2. Prefer B_Flux_CT otherwise since it's well-tested auto t_b_field = t_none; @@ -345,11 +352,11 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) if (pin->GetOrAddBoolean("electrons", "on", false)) { auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Electrons::Initialize, pin.get()); } - if (pin->GetOrAddBoolean("emhd", "on", false)) { - auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, EMHD::Initialize, pin.get()); + if (pin->GetBoolean("emhd", "on")) { + auto t_emhd = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, EMHD::Initialize, pin.get()); } if (pin->GetOrAddBoolean("wind", "on", false)) { - auto t_electrons = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Wind::Initialize, pin.get()); + auto t_wind = tl.AddTask(t_grmhd, KHARMA::AddPackage, packages, Wind::Initialize, pin.get()); } // Enable calculating jcon iff it is in any list of outputs (and there's even B to calculate it). // Since it is never required to restart, this is the only time we'd write (hence, need) it @@ -370,15 +377,13 @@ Packages_t KHARMA::ProcessPackages(std::unique_ptr &pin) // TODO avoid init if Parthenon will be handling all boundaries? KHARMA::AddPackage(packages, KBoundaries::Initialize, pin.get()); - // Load the implicit package last, and only if there are any variables which need implicit evolution + // Load the implicit package last, if there are *any* variables that need implicit evolution + // This lets us just count by flag, rather than checking all the possible parameters that would + // trigger this int n_implicit = PackDimension(packages.get(), Metadata::GetUserFlag("Implicit")); if (n_implicit > 0) { KHARMA::AddPackage(packages, Implicit::Initialize, pin.get()); } - // Only load the inverter if GRMHD isn't being evolved implicitly - if (PackDimension(packages.get(), {Metadata::GetUserFlag("Implicit"), Metadata::GetUserFlag("MHD")}) < 5) { - KHARMA::AddPackage(packages, Inverter::Initialize, pin.get()); - } #if DEBUG // Carry the ParameterInput with us, for generating outputs whenever we want From ac46a45adcb4bc7ae77ed5120596b9e26c4aab54 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 11 Oct 2023 17:13:28 -0600 Subject: [PATCH 179/219] Viscous Bondi test touchups, prototype SMR test --- .gitignore | 1 + pars/emhd/bondi_viscous.par | 4 +- pars/smr/mhdmodes_refined.par | 90 ++++++++++++++++++ tests/bondi_viscous/check.py | 44 ++++----- tests/mhdmodes_smr/check.py | 170 ++++++++++++++++++++++++++++++++++ tests/mhdmodes_smr/run.sh | 47 ++++++++++ 6 files changed, 327 insertions(+), 29 deletions(-) create mode 100644 pars/smr/mhdmodes_refined.par create mode 100644 tests/mhdmodes_smr/check.py create mode 100755 tests/mhdmodes_smr/run.sh diff --git a/.gitignore b/.gitignore index 8612874b..c2551928 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ logs/ # Archival files kharma_parsed_*.par log_*.txt +bondi_analytic_*.txt # Editor documents .project diff --git a/pars/emhd/bondi_viscous.par b/pars/emhd/bondi_viscous.par index 8aa45d4a..70d3b7ca 100644 --- a/pars/emhd/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -67,10 +67,10 @@ disable_floors = true outer_x1 = dirichlet inner_x1 = dirichlet check_inflow_outer_x1 = false -check_inflow_inner_x1 = false +check_inflow_inner_x1 = true # Force outflow bounds for EMHD vars outflow_EMHD_inner_x1 = true -outflow_EMHD_outer_x1 = true +outflow_EMHD_outer_x1 = false verbose = 1 diff --git a/pars/smr/mhdmodes_refined.par b/pars/smr/mhdmodes_refined.par new file mode 100644 index 00000000..159e9f3f --- /dev/null +++ b/pars/smr/mhdmodes_refined.par @@ -0,0 +1,90 @@ +# GRMHD Modes problem +# Try to propagate several analytically-amenable linear modes +# of the MHD equations +# Heavily commented as a likely first problem for new users + + +problem_id = mhdmodes + + +nmode = 1 +dir = 3 + + +refinement = static +numlevel = 2 + +nx1 = 96 +x1min = 0.0 +x1max = 1.0 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 96 +x2min = 0.0 +x2max = 1.0 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = 0.0 +x3max = 1.0 +ix3_bc = periodic +ox3_bc = periodic + +# Size of a mesh block +# # of meshblocks must be >= the number of MPI ranks, +# however there may be multiple blocks per rank + +nx1 = 32 +nx2 = 32 +nx3 = 1 + + +x1min = 0.5 +x1max = 0.5 +x2min = 0.5 +x2max = 0.5 +level = 1 + + +base = cartesian_minkowski +transform = null + + +tlim = 5.0 +integrator = rk2 +dt_min = 0.0001 + + +cfl = 0.9 +gamma = 1.333333 +implicit = false + + +solver = face_ct +lazy_prolongation = true +implicit = false + + +disable_floors = true + + +verbose = 0 +extra_checks = 1 +flag_verbose = 0 + + +type = kharma +reconstruction = weno5 + + +file_type = hdf5 +dt = 0.05 +single_precision_output = true +variables = prims.rho, prims.u, prims.uvec, prims.B, divB + + +file_type = hst +dt = 0.1 + diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index baba6974..5e4e4245 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -46,48 +46,38 @@ dP_check = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau) state.cache['dP'] = dP_check - # load code data - dump = pyharm.load_dump("emhd_2d_{}_end_emhd2d_weno.phdf".format(res)) - - # TODO iterate on names here - #rho, uu, dP_tilde = dump['RHO'], dump['UU'], dump['dP'] - #rho, uu = dump['RHO'], dump['UU'] - rho, uu, dP_tilde, B1 = dump['RHO'], dump['UU'], dump['dP'], dump['B1'] - # compute dP - if dump['emhd/higher_order_terms'] == "true": + if dump['emhd/higher_order_terms']: print("Res: "+str(res)+"; higher order terms enabled") - Theta = (dump['gam'] - 1.) * uu / rho - nu_emhd = eta / rho - dP = dP_tilde * np.sqrt(nu_emhd * rho * Theta / tau) + Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] + nu_emhd = eta / dump['rho'] + dP = dump['dP'] * np.sqrt(nu_emhd * dump['rho'] * Theta / tau) else: - dP = dP_tilde + dP = dump['dP'] + Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] + nu_emhd = eta / dump['rho'] + dP_check /= np.sqrt(nu_emhd * dump['rho'] * Theta / tau) # Plot - for var in ['rho', 'u', 'B1', 'dP']: + for var in ['rho', 'u', 'B1']: fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(1,1,1) pplt.plot_diff_xz(ax, dump, state, var) - plt.legend() fig.savefig("compare_{}_{}.png".format(var, res)) plt.close(fig) - r_start_ind = 1 - radius = np.mean(dump.grid['r'][r_start_ind:], axis=(1,2)) - plt.plot(radius, dP_check[r_start_ind:], label='dP ODE check') - plt.plot(radius, np.mean(dump['dP'][r_start_ind:], axis=(1,2)), label='dP0 ODE check') - plt.plot(radius, np.mean(state['ucon'][1][r_start_ind:], axis=(1,2)), label='ur') - #plt.plot(radius, np.mean(coeff[r_start_ind:], axis=(1,2)), label='coeff') + radius = np.mean(dump.grid['r'], axis=(1,2)) + plt.plot(radius, dP_check, label='dP ODE') + plt.plot(radius, np.mean(dP, axis=(1,2)), label='dP code') plt.legend() - plt.savefig('dP_soln_new.png') + plt.savefig('compare_dP_{}.png'.format(res)) plt.close() - # compute L1 norm - L1[r,0] = np.mean(np.fabs(rho - state['rho'])[1:-1]) - L1[r,1] = np.mean(np.fabs(uu - state['u'])) - L1[r,2] = np.mean(np.fabs(dP - dP_check)[1:-1]) - L1[r,3] = np.mean(np.fabs(B1 - state['B1'])) + L1[r,0] = np.mean(np.fabs(dump['rho'] - state['rho'])) + L1[r,1] = np.mean(np.fabs(dump['u'] - state['u'])) + L1[r,2] = np.mean(np.fabs(dP - dP_check)) + L1[r,3] = np.mean(np.fabs(dump['B1'] - state['B1'])) # MEASURE CONVERGENCE L1 = np.array(L1) diff --git a/tests/mhdmodes_smr/check.py b/tests/mhdmodes_smr/check.py new file mode 100644 index 00000000..777313c6 --- /dev/null +++ b/tests/mhdmodes_smr/check.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python + +# MHD linear modes convergence plots +import os,sys +from matplotlib.colors import LightSource +import numpy as np +import matplotlib.pyplot as plt + +import pyharm +import pyharm.plots as pplt + +RES = [int(x) for x in sys.argv[1].split(",")] +LONG = sys.argv[2] +SHORT = sys.argv[3] +if len(sys.argv) > 4: + DIM = sys.argv[4] +else: + DIM = "3d" +if len(sys.argv) > 5: + DIR = int(sys.argv[5]) +else: + DIR = 0 + +print(DIR) + +NVAR = 8 +VARS = ['rho', 'u', 'u1', 'u2', 'u3', 'B1', 'B2', 'B3'] + +amp = 1.e-4 +k1 = 2.*np.pi +k2 = 2.*np.pi +if DIM == "3d" and DIR == 0: + k3 = 2.*np.pi +else: + k3 = 0 +var0 = np.zeros(NVAR) + +# Background +var0[0] = 1. +var0[1] = 1. +# Magnetic field +var0[5] = 1. +var0[6] = 0. +var0[7] = 0. + +L1 = [] + +# EIGENMODES: 3D +dvar = np.zeros(NVAR) +if DIM == "3d" and DIR == 0: + if "entropy" in SHORT: + dvar[0] = 1. + if "slow" in SHORT: + dvar[0] = 0.556500332363 + dvar[1] = 0.742000443151 + dvar[2] = -0.282334999306 + dvar[3] = 0.0367010491491 + dvar[4] = 0.0367010491491 + dvar[5] = -0.195509141461 + dvar[6] = 0.0977545707307 + dvar[7] = 0.0977545707307 + if "alfven" in SHORT: + dvar[3] = -0.339683110243 + dvar[4] = 0.339683110243 + dvar[6] = 0.620173672946 + dvar[7] = -0.620173672946 + if "fast" in SHORT: + dvar[0] = 0.481846076323 + dvar[1] = 0.642461435098 + dvar[2] = -0.0832240462505 + dvar[3] = -0.224080007379 + dvar[4] = -0.224080007379 + dvar[5] = 0.406380545676 + dvar[6] = -0.203190272838 + dvar[7] = -0.203190272838 +else: + # EIGENMODES: 2D + # We only *convergence check* dir = 3 i.e. X1/X2 plane runs + # Other directions are useful for diagnosis but won't fail if 3D runs don't + if "entropy" in SHORT: + dvar[0] = 1. + if "slow" in SHORT: + dvar[0] = 0.558104461559 + dvar[1] = 0.744139282078 + dvar[2] = -0.277124827421 + dvar[3] = 0.0630348927707 + dvar[5] = -0.164323721928 + dvar[6] = 0.164323721928 + if "alfven" in SHORT: + dvar[4] = 0.480384461415 + dvar[7] = 0.877058019307 + if "fast" in SHORT: + dvar[0] = 0.476395427447 + dvar[1] = 0.635193903263 + dvar[2] = -0.102965815319 + dvar[3] = -0.316873207561 + dvar[5] = 0.359559114174 + dvar[6] = -0.359559114174 + +dvar *= amp + +for m, res in enumerate(RES): + dump = pyharm.load_dump("mhd_{}_{}_{}_end.phdf".format(DIM, SHORT, res)) + + X1 = dump['x'] + X2 = dump['y'] + X3 = dump['z'] + + dvar_code = [] + dvar_code.append(dump['RHO'] - var0[0]) + dvar_code.append(dump['UU'] - var0[1]) + dvar_code.append(dump['U1'] - var0[2]) + dvar_code.append(dump['U2'] - var0[3]) + dvar_code.append(dump['U3'] - var0[4]) + try: + dvar_code.append(dump['B1'] - var0[5]) + dvar_code.append(dump['B2'] - var0[6]) + dvar_code.append(dump['B3'] - var0[7]) + except IOError: + NVAR = 5 + + dvar_sol = [] + L1.append([]) + for k in range(NVAR): + dvar_sol.append(np.real(dvar[k])*np.cos(k1*X1 + k2*X2 + k3*X3)) + L1[m].append(np.mean(np.fabs(dvar_code[k] - dvar_sol[k]))) + + fig = plt.figure(figsize=(5,5)) + ax = fig.add_subplot(1,1,1) + pplt.plot_xz(ax, dump, dvar_code[k] - dvar_sol[k], native=True, window=[0,1,0,1]) + plt.savefig("compare_{}_{}_{}_{}.png".format(VARS[k], DIM, SHORT, res)) + + +# MEASURE CONVERGENCE +L1 = np.array(L1) +powerfits = [0.,]*NVAR +fail = 0 +for k in range(NVAR): + if abs(dvar[k]) != 0.: + powerfits[k] = np.polyfit(np.log(RES), np.log(L1[:,k]), 1)[0] + + print("Power fit {}: {} {}".format(VARS[k], powerfits[k], L1[:,k])) + # These bounds were chosen heuristically: fast u2/u3 converge fast + if powerfits[k] > -1.9 or ("entropy" not in SHORT and powerfits[k] < -2.1): + # Allow entropy wave to converge fast, otherwise everything is ~2 + fail = 1 + +# MAKE PLOTS +fig = plt.figure(figsize=(5,5)) + +ax = fig.add_subplot(1,1,1) +for k in range(NVAR): + if abs(dvar[k]) != 0.: + ax.plot(RES, L1[:,k], marker='s', label=VARS[k]) + +norm = L1[0,0]*RES[0]*RES[0] +if norm < 1e-4: + norm = L1[0,3]*RES[0]*RES[0] +xmin = RES[0]/2. +xmax = RES[-1]*2. +ax.plot([xmin, xmax], norm*np.asarray([xmin, xmax])**-2., color='k', linestyle='--', label='N^-2') + +plt.xscale('log', base=2); plt.yscale('log') +plt.xlim([RES[0]/np.sqrt(2.), RES[-1]*np.sqrt(2.)]) +plt.xlabel('N'); plt.ylabel('L1') +plt.title("{}".format(LONG)) +plt.legend(loc=1) +plt.savefig("convergence_modes_{}_{}.png".format(DIM,SHORT)) + +exit(fail) diff --git a/tests/mhdmodes_smr/run.sh b/tests/mhdmodes_smr/run.sh new file mode 100755 index 00000000..33b8e71b --- /dev/null +++ b/tests/mhdmodes_smr/run.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -euo pipefail + +BASE=../.. + +# This test confirms that all of the many transport options in KHARMA +# can converge when modeling each of the basic linearized modes: +# slow, fast, and alfven waves + +# It tests: +# 1. different reconstructions WENO vs linear +# 2. different drivers, simple, KHARMA, & ImEx +# 3. different B field transports, Flux-CT and Face-CT + +exit_code=0 + +conv_2d() { + IFS=',' read -ra RES_LIST <<< "$ALL_RES" + for res in "${RES_LIST[@]}" + do + # 3x3 & refine center + block=$(($res / 3)) + $BASE/run.sh -i $BASE/pars/smr/mhdmodes_refined.par debug/verbose=2 mhdmodes/dir=3 \ + parthenon/output0/single_precision_output=false parthenon/output0/dt=100. \ + parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ + parthenon/meshblock/nx1=$block parthenon/meshblock/nx2=$block parthenon/meshblock/nx3=1 \ + $2 >log_2d_${1}_${res}.txt 2>&1 + mv mhdmodes.out0.00000.phdf mhd_2d_${1}_${res}_start.phdf + mv mhdmodes.out0.final.phdf mhd_2d_${1}_${res}_end.phdf + done + check_code=0 + python check.py $ALL_RES "$3" $1 2d || check_code=$? + if [[ $check_code != 0 ]]; then + echo MHD modes test \"$3\" FAIL: $check_code + exit_code=1 + else + echo MHD modes test \"$3\" success + fi +} + +# Normal MHD modes, 2D, defaults +ALL_RES="24,48,96,192" +conv_2d slow mhdmodes/nmode=1 "slow mode in 2D" +conv_2d alfven mhdmodes/nmode=2 "Alfven mode in 2D" +conv_2d fast mhdmodes/nmode=3 "fast mode in 2D" + +exit $exit_code From f774756f9683c7b2a53909efb20e2598bf145ab7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 11 Oct 2023 18:20:42 -0600 Subject: [PATCH 180/219] Add & repair conducting atmo so it fails properly --- scripts/ci/cpu.yml | 2 +- tests/conducting_atmosphere/check.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/cpu.yml b/scripts/ci/cpu.yml index 272dbbd7..ca0299d5 100644 --- a/scripts/ci/cpu.yml +++ b/scripts/ci/cpu.yml @@ -72,5 +72,5 @@ tests: - ./run.sh parallel: matrix: - - TEST: [all_pars, anisotropic_conduction, bondi, bondi_viscous, bz_monopole, + - TEST: [all_pars, anisotropic_conduction, bondi, bondi_viscous, bz_monopole, conducting_atmosphere, emhdmodes, mhdmodes, noh, regrid, reinit, resize, restart, tilt_init, torus_sanity] diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index 655e0489..dd0e3f42 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -39,7 +39,7 @@ t = dfile['t'][()] gam = dfile['header/gam'][()] - higher_order_terms = dfile['header/higher_order_terms'][()].decode('UTF-8') + higher_order_terms = dfile['header/higher_order_terms'] # compute q if higher_order_terms=="TRUE": From 5eef03a75d2498c7c311f63885d7c9801c112e29 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 12 Oct 2023 11:56:31 -0500 Subject: [PATCH 181/219] Converge on last tests Viscous Bondi: use outflow conditions, solve ODE from outer edge value (converges to fixed condition, but not at 2o, probably due to limited runtime) Conducting atmo: actually return check result, converge by fixing detection of higher-order terms. --- .gitignore | 3 +- pars/emhd/bondi_viscous.par | 2 +- tests/bondi_viscous/check.py | 26 +- tests/bondi_viscous/check_ih3d.py | 459 ++++++++++++++++++ tests/bondi_viscous/run.sh | 2 +- tests/conducting_atmosphere/check.py | 3 +- .../conducting_atmosphere.par | 2 + tests/conducting_atmosphere/run.sh | 2 + 8 files changed, 481 insertions(+), 18 deletions(-) create mode 100644 tests/bondi_viscous/check_ih3d.py diff --git a/.gitignore b/.gitignore index c2551928..2e87022a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,10 +16,11 @@ logs/ *.rhdf *.xdmf *.hst -# Archival files +# Archival/test files kharma_parsed_*.par log_*.txt bondi_analytic_*.txt +atmosphere_soln_*.txt # Editor documents .project diff --git a/pars/emhd/bondi_viscous.par b/pars/emhd/bondi_viscous.par index 70d3b7ca..6e2d1330 100644 --- a/pars/emhd/bondi_viscous.par +++ b/pars/emhd/bondi_viscous.par @@ -70,7 +70,7 @@ check_inflow_outer_x1 = false check_inflow_inner_x1 = true # Force outflow bounds for EMHD vars outflow_EMHD_inner_x1 = true -outflow_EMHD_outer_x1 = false +outflow_EMHD_outer_x1 = true verbose = 1 diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index 5e4e4245..0a905ff7 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -43,23 +43,22 @@ state = bondi.get_bondi_fluid_state(mdot, rc, gam, dump.grid) state.params['eta'] = eta state.params['tau'] = tau - dP_check = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau) - state.cache['dP'] = dP_check - # compute dP + # compute dP either by adjusting dump to include higher-order terms, + # or the computed state to exclude them if dump['emhd/higher_order_terms']: print("Res: "+str(res)+"; higher order terms enabled") Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] - nu_emhd = eta / dump['rho'] - dP = dump['dP'] * np.sqrt(nu_emhd * dump['rho'] * Theta / tau) + # we're directly modifying the cache here. Inadvisable + dump.cache['dP'] = dump['dP'] * np.sqrt(eta * Theta / tau) else: - dP = dump['dP'] Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] - nu_emhd = eta / dump['rho'] - dP_check /= np.sqrt(nu_emhd * dump['rho'] * Theta / tau) + state.cache['dP'] /= np.sqrt(eta * Theta / tau) + + state.cache['dP'] = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau, start=np.mean(dump['dP'][-1])) # Plot - for var in ['rho', 'u', 'B1']: + for var in ['rho', 'u', 'B1', 'dP']: fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(1,1,1) pplt.plot_diff_xz(ax, dump, state, var) @@ -67,16 +66,17 @@ plt.close(fig) radius = np.mean(dump.grid['r'], axis=(1,2)) - plt.plot(radius, dP_check, label='dP ODE') - plt.plot(radius, np.mean(dP, axis=(1,2)), label='dP code') + plt.plot(radius, state['dP'], label='dP ODE') + plt.plot(radius, np.mean(dump['dP'], axis=(1,2)), label='dP code') + plt.plot(radius, np.mean(dump['dP'], axis=(1,2)) - state['dP'], label='dP diff') plt.legend() - plt.savefig('compare_dP_{}.png'.format(res)) + plt.savefig('compare_dP1d_{}.png'.format(res)) plt.close() # compute L1 norm L1[r,0] = np.mean(np.fabs(dump['rho'] - state['rho'])) L1[r,1] = np.mean(np.fabs(dump['u'] - state['u'])) - L1[r,2] = np.mean(np.fabs(dP - dP_check)) + L1[r,2] = np.mean(np.fabs(np.mean(dump['dP'], axis=(1,2)) - state['dP'])[2:]) L1[r,3] = np.mean(np.fabs(dump['B1'] - state['B1'])) # MEASURE CONVERGENCE diff --git a/tests/bondi_viscous/check_ih3d.py b/tests/bondi_viscous/check_ih3d.py new file mode 100644 index 00000000..517999cd --- /dev/null +++ b/tests/bondi_viscous/check_ih3d.py @@ -0,0 +1,459 @@ +import numpy as np +import os, sys, h5py, glob +from scipy import optimize +from scipy.interpolate import splrep, splev +from scipy.integrate import odeint, solve_ivp +import matplotlib +matplotlib.use('Agg') +from matplotlib import pyplot as plt + +import pyharm +import pyharm.io.gridfile as gridfile + +# Global dictionaries to store (i) fluid dump (ii) grid (iii) analytic solution data +dump = {} +grid = {} +soln = {} + +############### GEOMETRY FUNCTIONS ############### +# Compute gcov in BL from (r,th,phi) read from grid file +def gcov_bl(): + grid['gcov_bl'] = np.zeros_like(grid['gcov']) + + DD = 1 - 2./grid['r'] + grid['a']**2/grid['r']**2 + mu = 1 + grid['a']**2 * np.cos(grid['th'])**2 / grid['r']**2 + + grid['gcov_bl'][Ellipsis,0,0] = -(1 - 2./(grid['r'] * mu)) + grid['gcov_bl'][Ellipsis,0,3] = -2 * grid['a'] * np.sin(grid['th'])**2 / (grid['r'] * mu) + grid['gcov_bl'][Ellipsis,3,0] = grid['gcov_bl'][Ellipsis,0,3] + grid['gcov_bl'][Ellipsis,1,1] = mu / DD + grid['gcov_bl'][Ellipsis,2,2] = grid['r']**2 * mu + grid['gcov_bl'][Ellipsis,3,3] = grid['r']**2 * np.sin(grid['th'])**2 * (1 + grid['a']**2/grid['r']**2 \ + + 2 * grid['a']**2 * np.sin(grid['th'])**2 / (grid['r']**3 * mu)) + +# Compute gcov in KS from (r,th,phi) read from grid file +def gcov_ks(): + grid['gcov_ks'] = np.zeros_like(grid['gcov']) + sigma = grid['r']**2 + (grid['a']**2 * np.cos(grid['th'])**2) + + grid['gcov_ks'][Ellipsis,0,0] = -1 + 2*grid['r']/sigma + grid['gcov_ks'][Ellipsis,0,1] = 2*grid['r']/sigma + grid['gcov_ks'][Ellipsis,0,3] = -(2*grid['a']*grid['r']*np.sin(grid['th'])**2)/sigma + grid['gcov_ks'][Ellipsis,1,0] = 2*grid['r']/sigma + grid['gcov_ks'][Ellipsis,1,1] = 1 + 2*grid['r']/sigma + grid['gcov_ks'][Ellipsis,1,3] = -grid['a']*np.sin(grid['th'])**2 * (1 + 2*grid['r']/sigma) + grid['gcov_ks'][Ellipsis,2,2] = sigma + grid['gcov_ks'][Ellipsis,3,0] = -(2*grid['a']*grid['r']*np.sin(grid['th'])**2)/sigma + grid['gcov_ks'][Ellipsis,3,1] = -grid['a']*np.sin(grid['th'])**2 * (1 + 2*grid['r']/sigma) + grid['gcov_ks'][Ellipsis,3,3] = np.sin(grid['th'])**2 * (sigma + grid['a']**2*np.sin(grid['th'])**2 * (1 + 2*grid['r']/sigma)) + +# Compute gcov in KS from gcon_ks +def gcon_ks(): + grid['gcon_ks'] = np.linalg.inv(grid['gcov_ks']) + +# Compute transformation matrix from KS -> MKS / FMKS (for covariant indices) +def dxdX_KS_to_FMKS(): + dxdX = np.zeros((grid['n1'], grid['n2'], 4, 4), dtype=float) + + if grid['metric'] == 'mks': + dxdX[Ellipsis,0,0] = dxdX[Ellipsis,3,3] = 1 + dxdX[Ellipsis,1,1] = np.exp(grid['x1']) + dxdX[Ellipsis,2,2] = np.pi + (1 - grid['hslope']) * np.pi * np.cos(2 * np.pi * grid['x2']) + + else: + theta_g = (np.pi * grid['x2']) + ((1 - grid['hslope'])/2) * (np.sin(2*np.pi*grid['x2'])) + theta_j = grid['D'] * (2*grid['x2'] - 1) * (1 + (((2 * grid['x2'] - 1) / grid['poly_xt'])**grid['poly_alpha']) / (1 + grid['poly_alpha'])) + np.pi/2 + derv_theta_g = np.pi + (1 - grid['hslope']) * np.pi * np.cos(2 * np.pi * grid['x2']) + derv_theta_j = (2 * grid['poly_alpha'] * grid['D'] * (2 * grid['x2'] - 1)*((2 * grid['x2'] - 1) / grid['poly_xt'])**(grid['poly_alpha'] - 1)) / (grid['poly_xt'] * (grid['poly_alpha'] + 1)) + 2 * grid['D'] * (1 + (((2 * grid['x2'] - 1) / grid['poly_xt'])**grid['poly_alpha']) / (grid['poly_alpha'] + 1)) + dxdX[Ellipsis,0,0] = dxdX[Ellipsis,3,3] = 1 + dxdX[Ellipsis,1,1] = np.exp(grid['x1']) + dxdX[Ellipsis,2,1] = -grid['mks_smooth'] * np.exp(-grid['mks_smooth'] * grid['Dx1'][:,np.newaxis]) * (theta_j - theta_g) + dxdX[Ellipsis,2,2] = derv_theta_g + np.exp(-grid['mks_smooth'] * grid['Dx1'][:,np.newaxis]) * (derv_theta_j - derv_theta_g) + + return dxdX + +# Compute transformation matrix from MKS / FMKS -> KS (for covariant indices) +def dxdX_FMKS_to_KS(): + return (np.linalg.inv(dxdX_KS_to_FMKS())) + +# Compute quantities manually from x^mu +def bl_coords_from_x(grid_temp): + grid_temp['r'] = np.exp(grid_temp['x1']) + grid_temp['th'] = np.pi * grid_temp['x2'] + ((1 - grid['hslope'])/2.) * np.sin(2*np.pi*grid_temp['x2']) + +def gcov_ks_from_x(grid_temp): + bl_coords_from_x(grid_temp) + + grid_temp['gcov_ks'] = np.zeros_like(grid['gcov']) + sigma = grid_temp['r']**2 + (grid_temp['a']**2 * np.cos(grid_temp['th'])**2) + + grid_temp['gcov_ks'][Ellipsis,0,0] = -1 + 2*grid_temp['r']/sigma + grid_temp['gcov_ks'][Ellipsis,0,1] = 2*grid_temp['r']/sigma + grid_temp['gcov_ks'][Ellipsis,0,3] = -(2*grid_temp['a']*grid_temp['r']*np.sin(grid_temp['th'])**2)/sigma + grid_temp['gcov_ks'][Ellipsis,1,0] = 2*grid_temp['r']/sigma + grid_temp['gcov_ks'][Ellipsis,1,1] = 1 + 2*grid_temp['r']/sigma + grid_temp['gcov_ks'][Ellipsis,1,3] = -grid_temp['a']*np.sin(grid_temp['th'])**2 * (1 + 2*grid_temp['r']/sigma) + grid_temp['gcov_ks'][Ellipsis,2,2] = sigma + grid_temp['gcov_ks'][Ellipsis,3,0] = -(2*grid_temp['a']*grid_temp['r']*np.sin(grid_temp['th'])**2)/sigma + grid_temp['gcov_ks'][Ellipsis,3,1] = -grid_temp['a']*np.sin(grid_temp['th'])**2 * (1 + 2*grid_temp['r']/sigma) + grid_temp['gcov_ks'][Ellipsis,3,3] = np.sin(grid_temp['th'])**2 * (sigma + grid_temp['a']**2*np.sin(grid_temp['th'])**2 * (1 + 2*grid_temp['r']/sigma)) + +def dxdX_KS_to_MKS_from_x(grid_temp): + dxdX = np.zeros((grid['n1'], grid['n2'], 4, 4), dtype=float) + + dxdX[Ellipsis,0,0] = dxdX[Ellipsis,3,3] = 1 + dxdX[Ellipsis,1,1] = np.exp(grid_temp['x1']) + dxdX[Ellipsis,2,2] = np.pi + (1 - grid['hslope']) * np.pi * np.cos(2 * np.pi * grid_temp['x2']) + + return dxdX + +def dxdX_MKS_to_KS_from_x(grid_temp): + dxdX = dxdX_KS_to_MKS_from_x(grid_temp) + return np.linalg.inv(dxdX) + +def gcov_from_x(grid_temp): + gcov_ks_from_x(grid_temp) + dxdX = dxdX_KS_to_MKS_from_x(grid_temp) + + grid_temp['gcov'] = np.einsum('ijbn,ijmb->ijmn', dxdX, \ + np.einsum('ijam,ijab->ijmb', dxdX, grid_temp['gcov_ks'])) + + grid_temp['gcon'] = np.linalg.inv(grid_temp['gcov']) + +# Compute the Christoffel symbols in MKS/MMKS (like iharm3d/pyharm) +def conn_func(sigma, alpha, beta): + delta = 1.e-5 + conn = np.zeros((grid['n1'], grid['n2'], 4, 4, 4), dtype=float) + tmp = np.zeros_like(conn) + + x = np.zeros((grid['n1'], grid['n2'], 4), dtype=float) + x[Ellipsis,1] = grid['x1'] + x[Ellipsis,2] = grid['x2'] + x[Ellipsis,3] = grid['x3'] + + grid_h = {}; grid_h['a'] = grid['a'] + grid_l = {}; grid_l['a'] = grid['a'] + + for mu in range(4): + xh = np.copy(x) + xl = np.copy(x) + xh[Ellipsis,mu] += delta + xl[Ellipsis,mu] -= delta + + grid_h['x1'] = xh[Ellipsis,1] + grid_h['x2'] = xh[Ellipsis,2] + grid_l['x1'] = xl[Ellipsis,1] + grid_l['x2'] = xl[Ellipsis,2] + + gcov_from_x(grid_h) + gcov_from_x(grid_l) + + for lam in range(4): + for nu in range(4): + conn[Ellipsis,lam,nu,mu] = (grid_h['gcov'][Ellipsis,lam,nu] - grid_l['gcov'][Ellipsis,lam,nu]) \ + / (xh[Ellipsis,mu] - xl[Ellipsis,mu]) + + for lam in range(4): + for nu in range(4): + for mu in range(4): + tmp[Ellipsis,lam,nu,mu] = 0.5 * (conn[Ellipsis,nu,lam,mu] + conn[Ellipsis,mu,lam,nu] \ + - conn[Ellipsis,mu,nu,lam]) + + for lam in range(4): + for nu in range(4): + for mu in range(4): + conn[Ellipsis,lam,nu,mu] = 0 + for kap in range(4): + conn[Ellipsis,lam,nu,mu] += grid['gcon'][Ellipsis,lam,kap] * tmp[Ellipsis,kap,nu,mu] + + return conn[Ellipsis,sigma,alpha,beta] + + + +############### READ DATA ############### +# Read dump and/or grid file +def load_data(dumpsdir, dumpno, read_grid=False): + dfile = pyharm.load_dump(dumpsdir+'/emhd_2d_8_end_emhd2d_weno.phdf') + dump['rc'] = dfile['rs'] + dump['mdot'] = dfile['mdot'] + dump['gam'] = dfile['gam'] + dump['rEH'] = dfile['r_eh'] + + if read_grid: + gridfile.write_grid(dfile.grid, 'grid.h5') + gfile = h5py.File(os.path.join(dumpsdir, 'grid.h5'), 'r') + grid['r'] = np.squeeze(gfile['r']) + grid['th'] = np.squeeze(gfile['th']) + grid['phi'] = np.squeeze(gfile['phi']) + + grid['rEH_ind'] = np.argmin(np.fabs(grid['r'][:,0]-dump['rEH']) > 0.) + grid['n1'] = dfile['n1'] + grid['n2'] = dfile['n2'] + grid['n3'] = dfile['n3'] + grid['dx1'] = dfile['dx1'] + grid['dx2'] = dfile['dx2'] + + grid['x1'] = np.squeeze(gfile['X1']) + grid['x2'] = np.squeeze(gfile['X2']) + grid['x3'] = np.squeeze(gfile['X3']) + + grid['metric'] = dfile['coordinates'].lower() + grid['gcov'] = np.squeeze(gfile['gcov']) + grid['gcon'] = np.squeeze(gfile['gcon']) + grid['gdet'] = np.squeeze(gfile['gdet']) + grid['lapse'] = np.squeeze(gfile['lapse']) + + if grid['metric']=='mks' or grid['metric']=='mmks': + grid['a'] = dfile['a'] + grid['rEH'] = dfile['r_eh'] + grid['hslope'] = dfile['hslope'] + + if grid['metric']=='MMKS': + grid['mks_smooth'] = dfile['mks_smooth'] + grid['poly_alpha'] = dfile['poly_alpha'] + grid['poly_xt'] = dfile['poly_xt'] + grid['D'] = (np.pi*grid['poly_xt']**grid['poly_alpha'])/(2*grid['poly_xt']**grid['poly_alpha']+(2/(1+grid['poly_alpha']))) + + gfile.close() + + del dfile + + + +############### COMPUTE ANALYTIC IDEAL BONDI SOLUTION ############### +# Nonlinear expression to solve for T +def T_func(T, r, C3, C4, N): + return (1 + (1 + N/2)*T)**2 * (1 - 2./r + (C4**2/(r**4 * T**N))) - C3 + +# Obtain primitives for Bondi problem +def get_prim(): + N = 2./ (dump['gam'] - 1) + rc = dump['rc'] + mdot = dump['mdot'] + vc = np.sqrt(1. / (2 * rc)) + csc = np.sqrt(vc**2 / (1 - 3*vc**2)) + Tc = 2*N*csc**2 / ((N + 2)*(2 - N*csc**2)) + C4 = Tc**(N/2)*vc*rc**2 + C3 = (1 + (1 + N/2)*Tc)**2 * (1 - 2./rc + vc**2) + + # Root find T + T = np.zeros_like(grid['r'][:,0]) + for index, r in enumerate(grid['r'][:,0]): + T0 = Tc + sol = optimize.root(T_func, [T0], args=(r, C3, C4, N)) + T[index] = sol.x[0] + if (sol.success!=True): + print("Not converged at r = {:.2f}", r) + + # Compute remaining fluid variables + soln['T'] = T + soln['v'] = -C4 / (T**(N/2) * grid['r'][:,0]**2) + soln['K'] = (4*np.pi*C4 / mdot) ** (2./N) + + soln['rho'] = soln['K']**(-N/2) * T**(N/2) + soln['u'] = (N/2) * soln['K']**(-N/2) * T**(N/2 + 1) + + soln['mdot'] = mdot + soln['N'] = N + soln['rc'] = rc + +# Compute four vectors +def compute_ub(): + + # We have u^r in BL. We need to convert this to ucon in MKS + # First compute u^t in BL + ucon_bl = np.zeros((grid['n1'], grid['n2'], 4), dtype=float) + AA = grid['gcov_bl'][Ellipsis,0,0] + BB = 2. * grid['gcov_bl'][Ellipsis,0,1]*soln['v'][:,None] + CC = 1. + grid['gcov_bl'][Ellipsis,1,1]*soln['v'][:,None]**2 + + discr = BB*BB - 4.*AA*CC + ucon_bl[Ellipsis,0] = (-BB - np.sqrt(discr)) / (2.*AA) + ucon_bl[Ellipsis,1] = soln['v'][:,None] + + # Convert ucon(Bl) to ucon(KS) + dxdX = np.zeros((grid['n1'], grid['n2'], 4, 4), dtype=float) + dxdX[Ellipsis,0,0] = dxdX[Ellipsis,1,1] = dxdX[Ellipsis,2,2] = dxdX[Ellipsis,3,3] = 1. + dxdX[Ellipsis,0,1] = 2*grid['r'] / (grid['r']**2 - 2.*grid['r'] + grid['a']**2) + dxdX[Ellipsis,3,1] = grid['a']/(grid['r']**2 - 2.*grid['r'] + grid['a']**2) + + ucon_ks = np.zeros((grid['n1'], grid['n2'], 4), dtype=float) + for mu in range(4): + for nu in range(4): + ucon_ks[Ellipsis,mu] += dxdX[Ellipsis,mu,nu] * ucon_bl[Ellipsis,nu] + + # Convert ucon(KS) to ucon(MKS/FMKS) + ucon_mks = np.zeros((grid['n1'], grid['n2'], 4), dtype=float) + dxdX = dxdX_FMKS_to_KS() + for mu in range(4): + for nu in range(4): + ucon_mks[Ellipsis,mu] += dxdX[Ellipsis,mu,nu] * ucon_ks[Ellipsis,nu] + + ucov_mks = np.einsum('ijmn,ijn->ijm', grid['gcov'], ucon_mks) + + # Compute velocity primitives + utilde = np.zeros((grid['n1'], grid['n2'], 3), dtype=float) + + alpha = 1./np.sqrt(-grid['gcon'][Ellipsis,0,0]) + beta = np.zeros((grid['n1'], grid['n2'], 3), dtype=float) + beta[Ellipsis,0] = alpha * alpha * grid['gcon'][Ellipsis,0,1] + beta[Ellipsis,1] = alpha * alpha * grid['gcon'][Ellipsis,0,2] + beta[Ellipsis,2] = alpha * alpha * grid['gcon'][Ellipsis,0,3] + gamma = ucon_mks[Ellipsis,0] * alpha + + utilde[Ellipsis,0] = ucon_mks[Ellipsis,1] + beta[Ellipsis,0]*gamma/alpha + utilde[Ellipsis,1] = ucon_mks[Ellipsis,2] + beta[Ellipsis,1]*gamma/alpha + utilde[Ellipsis,2] = ucon_mks[Ellipsis,3] + beta[Ellipsis,2]*gamma/alpha + + # compute magnetic 4-vector + B = np.zeros((grid['n1'], grid['n2'], 3), dtype=float) + # radial magnetic field (B1 = 1/r^3) + B[Ellipsis,0] = 1. / grid['r']**3 + + gti = grid['gcon'][Ellipsis,0,1:4] + gij = grid['gcov'][Ellipsis,1:4,1:4] + beta_i = np.einsum('ijs,ij->ijs', gti, grid['lapse']**2) + qsq = np.einsum('ijy,ijy->ij', np.einsum('ijxy,ijx->ijy', gij, utilde), utilde) + gamma = np.sqrt(1 + qsq) + ui = utilde - np.einsum('ijs,ij->ijs', beta_i, gamma/grid['lapse']) + ut = gamma/grid['lapse'] + + bt = np.einsum('ijm,ijm->ij', np.einsum('ijsm,ijs->ijm', grid['gcov'][Ellipsis,1:4,:], B), ucon_mks) + bi = (B + np.einsum('ijs,ij->ijs', ucon_mks[Ellipsis,1:4], bt)) / ucon_mks[Ellipsis,0,None] + bcon_mks = np.append(bt[Ellipsis,None], bi, axis=2) + bcov_mks = np.einsum('ijmn,ijn->ijm', grid['gcov'], bcon_mks) + + soln['ucon'] = ucon_mks[:,0,:] + soln['ucov'] = ucov_mks[:,0,:] + soln['bcon'] = bcon_mks[:,0,:] + soln['bcov'] = bcov_mks[:,0,:] + soln['bsq'] = np.einsum('im,im->i', soln['bcon'], soln['bcov']) + + + +############### ADDITIONAL FUNCTIONS FOR VISCOUS BONDI FLOW ############### +# Compute Braginskii pressure anisotropy value +def compute_dP0(): + grid['dx'] = [grid['dx1'],grid['dx2']] + + soln['tau'] = 30. + soln['eta'] = 0.01 + nu_emhd = soln['eta'] / soln['rho'] + dP0 = np.zeros(grid['n1'], dtype=float) + + # Compute derivatives of 4-velocity + ducovDx1 = np.zeros((grid['n1'], 4), dtype=float) # Represents d_x1(u_\mu) + delta = 1.e-5 + x1 = grid['x1'][:,0] + x1h = x1 + delta + x1l = x1 - delta + + ucovt_splrep = splrep(x1, soln['ucov'][:,0]) + ucovr_splrep = splrep(x1, soln['ucov'][:,1]) + ucovt_h = splev(x1h, ucovt_splrep) + ucovt_l = splev(x1l, ucovt_splrep) + ucovr_h = splev(x1h, ucovr_splrep) + ucovr_l = splev(x1l, ucovr_splrep) + + ducovDx1[:,0] = (ucovt_h - ucovt_l) / (x1h - x1l) + ducovDx1[:,1] = (ucovr_h - ucovr_l) / (x1h - x1l) + + for mu in range(4): + for nu in range(4): + if mu == 1: + dP0 += 3*soln['rho']*nu_emhd * (soln['bcon'][:,mu]*soln['bcon'][:,nu] / soln['bsq']) \ + * ducovDx1[:,nu] + + gamma_term_1 = np.zeros((grid['n1'], grid['n2']), dtype=float) + for sigma in range(4): + gamma_term_1 += (3*soln['rho']*nu_emhd * (soln['bcon'][:,mu]*soln['bcon'][:,nu] / soln['bsq']))[:,None] \ + * (-conn_func(sigma, mu, nu) * soln['ucov'][:,None,sigma]) + + dP0 += np.mean(gamma_term_1, axis=1) + + derv_term_2 = np.zeros((grid['n1'], grid['n2']), dtype=float) + if mu == 1: + for sigma in range(4): + derv_term_2 += (-soln['rho']*nu_emhd * ducovDx1[:,sigma])[:,None] \ + * grid['gcon'][Ellipsis,mu,sigma] + + dP0 += np.mean(derv_term_2, axis=1) + + gamma_term_2 = np.zeros((grid['n1'], grid['n2']), dtype=float) + for sigma in range(4): + for delta in range(4): + gamma_term_2 += (soln['rho']*nu_emhd)[:,None] * (conn_func(sigma, mu, delta) * grid['gcon'][Ellipsis,mu,delta] * soln['ucov'][:,None,sigma]) + + dP0 += np.mean(gamma_term_2, axis=1) + + # r_start = 3.0 + # r_start_ind = np.argmin(np.fabs(grid['r'][:,0] - r_start)) + # plt.semilogx(grid['r'][r_start_ind:,0], dP0[r_start_ind:]) + # plt.savefig('dP0_analytic.png') + # plt.close() + + return dP0 + +# Compute the coefficient of the second term on the RHS of the evolution equation of dP +def compute_rhs_second_term(): + nu_emhd = soln['eta'] / soln['rho'] + P = soln['u'] * (dump['gam'] - 1.) + + # compute derivative + delta = 1.e-5 + x1 = grid['x1'][:,0] + x1h = x1 + delta + x1l = x1 - delta + expr = np.log(soln['tau'] / (soln['rho'] * nu_emhd * P)) + expr_splrep = splrep(x1, expr) + expr_h = splev(x1h, expr_splrep) + expr_l = splev(x1l, expr_splrep) + + coeff = 0.5 * (expr_h - expr_l) / (x1h - x1l) + + return coeff + +# Return derivative d(dP)/dx1. Refer Equation (36) in grim paper +def ddP_dX1(dP, x1, ur_splrep, dP0_splrep, coeff_splrep): + tau = soln['tau'] + ur = splev(x1, ur_splrep) + dP0 = splev(x1, dP0_splrep) + coeff = splev(x1, coeff_splrep) + + derivative = -((dP - dP0) / (tau * ur)) - (dP * coeff) + return derivative + + +############### MAIN IS MAIN ############### +if __name__=='__main__': + dumpsdir = '.' + + + load_data(dumpsdir, 0, True) + get_prim() + gcov_bl() + gcov_ks() + gcon_ks() + compute_ub() + + dP0 = compute_dP0() + coeff = compute_rhs_second_term() + + x1 = grid['x1'][:,0] + ur_splrep = splrep(x1, soln['ucon'][:,1]) + dP0_splrep = splrep(x1, dP0) + coeff_splrep = splrep(x1, coeff) + + solution = odeint(ddP_dX1, 0., x1[::-1], args=(ur_splrep, dP0_splrep, coeff_splrep)) + np.savetxt('bondi_analytic_{}.txt'.format(grid['n1']), np.asarray([soln['rho'], soln['u'], soln['v'], solution[::-1,0]]).T) + + r_start = 3.0 + r_start_ind = np.argmin(np.fabs(grid['r'][:,0] - r_start)) + plt.plot(grid['r'][r_start_ind:,0], solution[::-1,0][r_start_ind:], label='dP ODE check') + plt.plot(grid['r'][r_start_ind:,0], dP0[r_start_ind:], label='dP0 ODE check') + plt.plot(grid['r'][r_start_ind:,0], soln['ucon'][:,1][r_start_ind:], label='ur') + #plt.plot(grid['r'][r_start_ind:,0], coeff[r_start_ind:], label='coeff') + plt.legend() + plt.savefig('dP_soln.png') + plt.close() + \ No newline at end of file diff --git a/tests/bondi_viscous/run.sh b/tests/bondi_viscous/run.sh index 8c0f882a..a50a00bc 100755 --- a/tests/bondi_viscous/run.sh +++ b/tests/bondi_viscous/run.sh @@ -13,7 +13,7 @@ conv_2d() { do # Four blocks half=$(( $res / 2 )) - $BASE/run.sh -i $BASE/pars/emhd/bondi_viscous.par debug/verbose=1 parthenon/time/tlim=400 \ + $BASE/run.sh -i $BASE/pars/emhd/bondi_viscous.par debug/verbose=1 \ parthenon/mesh/nx1=$res parthenon/mesh/nx2=$res parthenon/mesh/nx3=1 \ parthenon/meshblock/nx1=$half parthenon/meshblock/nx2=$half parthenon/meshblock/nx3=1 \ b_field/implicit=false $2 >log_${1}_${res}.txt 2>&1 diff --git a/tests/conducting_atmosphere/check.py b/tests/conducting_atmosphere/check.py index dd0e3f42..7352b830 100644 --- a/tests/conducting_atmosphere/check.py +++ b/tests/conducting_atmosphere/check.py @@ -39,10 +39,9 @@ t = dfile['t'][()] gam = dfile['header/gam'][()] - higher_order_terms = dfile['header/higher_order_terms'] # compute q - if higher_order_terms=="TRUE": + if dfile['header/higher_order_terms']: print("Res: "+str(res)+"; higher order terms enabled") tau = 10. kappa = 0.1 diff --git a/tests/conducting_atmosphere/conducting_atmosphere.par b/tests/conducting_atmosphere/conducting_atmosphere.par index 523b5fc5..ef23da7b 100644 --- a/tests/conducting_atmosphere/conducting_atmosphere.par +++ b/tests/conducting_atmosphere/conducting_atmosphere.par @@ -34,6 +34,8 @@ inner_x1 = dirichlet outer_x1 = dirichlet check_inflow_inner_x1 = false check_inflow_outer_x1 = false +outflow_EMHD_inner_x1 = true +outflow_EMHD_outer_x1 = true tlim = 400. diff --git a/tests/conducting_atmosphere/run.sh b/tests/conducting_atmosphere/run.sh index 1f8b82f8..f1e801d9 100755 --- a/tests/conducting_atmosphere/run.sh +++ b/tests/conducting_atmosphere/run.sh @@ -36,3 +36,5 @@ conv_2d() { ALL_RES="64,128,256,512" conv_2d emhd2d_weno driver/reconstruction=weno5 "in 2D, WENO5" + +exit $exit_code From 9750787c3ff9adff53d735c9d6c42287aa95ace6 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 12 Oct 2023 12:05:58 -0500 Subject: [PATCH 182/219] Fix viscous Bondi test script for hypothetical case without higher order terms --- tests/bondi_viscous/check.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/bondi_viscous/check.py b/tests/bondi_viscous/check.py index 0a905ff7..50419312 100644 --- a/tests/bondi_viscous/check.py +++ b/tests/bondi_viscous/check.py @@ -51,11 +51,11 @@ Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] # we're directly modifying the cache here. Inadvisable dump.cache['dP'] = dump['dP'] * np.sqrt(eta * Theta / tau) + state.cache['dP'] = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau, start=np.mean(dump['dP'][-1])) else: Theta = (dump['gam'] - 1.) * dump['u'] / dump['rho'] - state.cache['dP'] /= np.sqrt(eta * Theta / tau) - - state.cache['dP'] = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau, start=np.mean(dump['dP'][-1])) + state.cache['dP'] = bondi.compute_dP(mdot, rc, gam, dump.grid, eta, tau, start=np.mean(dump['dP'][-1])) / \ + np.sqrt(eta * Theta / tau) # Plot for var in ['rho', 'u', 'B1', 'dP']: From 0d141db13288d3d5bc2c59d51b3baa757b45f040 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 19 Oct 2023 12:20:34 -0600 Subject: [PATCH 183/219] Pull new Parthenon (solvers, fixes, outputs). Fork old BiCGStab --- external/parthenon | 2 +- kharma/b_cleanup/b_cleanup.cpp | 3 - kharma/b_cleanup/bicgstab_solver.hpp | 3 +- kharma/b_cleanup/solver_utils.hpp | 176 +++++++++++++++++++++++++++ kharma/b_cleanup/solvers.cpp | 22 ++++ kharma/decs.hpp | 14 ++- kharma/main.cpp | 7 +- 7 files changed, 216 insertions(+), 11 deletions(-) create mode 100644 kharma/b_cleanup/solver_utils.hpp create mode 100644 kharma/b_cleanup/solvers.cpp diff --git a/external/parthenon b/external/parthenon index 72a97564..67cbb148 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 72a975647e5548fee643952a52f12a249fc2b325 +Subproject commit 67cbb1485400051ad94d2f96735e03de76308f07 diff --git a/kharma/b_cleanup/b_cleanup.cpp b/kharma/b_cleanup/b_cleanup.cpp index 9cca1a7b..51276f26 100644 --- a/kharma/b_cleanup/b_cleanup.cpp +++ b/kharma/b_cleanup/b_cleanup.cpp @@ -62,9 +62,6 @@ void B_Cleanup::CleanupDivergence(std::shared_ptr>& md) {} using namespace parthenon; using namespace parthenon::solvers; -// TODO get the transport manager working later -// Needs a call every X steps option, probably return a TaskList or TaskRegion - std::shared_ptr B_Cleanup::Initialize(ParameterInput *pin, std::shared_ptr& packages) { auto pkg = std::make_shared("B_Cleanup"); diff --git a/kharma/b_cleanup/bicgstab_solver.hpp b/kharma/b_cleanup/bicgstab_solver.hpp index dc4fe559..8721c8ff 100644 --- a/kharma/b_cleanup/bicgstab_solver.hpp +++ b/kharma/b_cleanup/bicgstab_solver.hpp @@ -22,10 +22,11 @@ #include "interface/meshblock_data.hpp" #include "interface/state_descriptor.hpp" #include "kokkos_abstraction.hpp" -#include "solvers/solver_utils.hpp" #include "tasks/task_id.hpp" #include "tasks/task_list.hpp" +#include "solver_utils.hpp" + namespace parthenon { namespace solvers { diff --git a/kharma/b_cleanup/solver_utils.hpp b/kharma/b_cleanup/solver_utils.hpp new file mode 100644 index 00000000..b7185e60 --- /dev/null +++ b/kharma/b_cleanup/solver_utils.hpp @@ -0,0 +1,176 @@ +//======================================================================================== +// (C) (or copyright) 2021. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== +#ifndef SOLVERS_SOLVER_UTILS_HPP_ +#define SOLVERS_SOLVER_UTILS_HPP_ + +#include +#include + +#include "basic_types.hpp" +#include "kokkos_abstraction.hpp" + +namespace parthenon { + +namespace solvers { + +struct SparseMatrixAccessor { + ParArray1D ioff, joff, koff; + ParArray1D ioff_inv, joff_inv, koff_inv; + ParArray1D inv_entries; + + const int nstencil; + int ndiag; + SparseMatrixAccessor() : nstencil(0), ndiag(0) {} + SparseMatrixAccessor(const SparseMatrixAccessor &sp) + : ioff(sp.ioff), joff(sp.joff), koff(sp.koff), nstencil(sp.nstencil), + ndiag(sp.ndiag), inv_entries(sp.inv_entries) {} + SparseMatrixAccessor(const std::string &label, const int n, + std::vector> off) + : ioff(label + "_ioff", n), joff(label + "_joff", n), koff(label + "_koff", n), + ioff_inv(label + "_ioff_inv", n), joff_inv(label + "_joff_inv", n), + koff_inv(label + "_koff_inv", n), inv_entries(label + "_inv_ent", n), + nstencil(n) { + PARTHENON_REQUIRE_THROWS(off.size() == 3, + "Offset array must have dimensions off[3][*]"); + PARTHENON_REQUIRE_THROWS(off[0].size() >= n, "Offset array off[0][*] too small"); + PARTHENON_REQUIRE_THROWS(off[1].size() >= n, "Offset array off[1][*] too small"); + PARTHENON_REQUIRE_THROWS(off[2].size() >= n, "Offset array off[2][*] too small"); + auto ioff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), ioff); + auto joff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), joff); + auto koff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), koff); + + auto ioff_inv_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), ioff_inv); + auto joff_inv_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), joff_inv); + auto koff_inv_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), koff_inv); + + auto inv_ent_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), inv_entries); + + for (int i = 0; i < n; i++) { + ioff_h(i) = off[0][i]; + joff_h(i) = off[1][i]; + koff_h(i) = off[2][i]; + // this is inverse. + ioff_inv_h(i) = -off[0][i]; + joff_inv_h(i) = -off[1][i]; + koff_inv_h(i) = -off[2][i]; + + if (off[0][i] == 0 && off[1][i] == 0 && off[2][i] == 0) { + ndiag = i; + } + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (ioff_h(i) == ioff_inv_h(j) && joff_h(i) == joff_inv_h(j) && + koff_h(i) == koff_inv_h(j)) { + inv_entries(i) = j; + std::cout << "inv_entries:" << i << " " << j << std::endl; + } + } // j + } // i + + Kokkos::deep_copy(ioff, ioff_h); + Kokkos::deep_copy(joff, joff_h); + Kokkos::deep_copy(koff, koff_h); + + Kokkos::deep_copy(inv_entries, inv_ent_h); + } + + template + KOKKOS_INLINE_FUNCTION Real MatVec(const PackType &spmat, const int imat_lo, + const int imat_hi, const PackType &v, const int iv, + const int b, const int k, const int j, + const int i) const { + Real matvec = 0.0; + for (int n = imat_lo; n <= imat_hi; n++) { + const int m = n - imat_lo; + matvec += spmat(b, n, k, j, i) * v(b, iv, k + koff(m), j + joff(m), i + ioff(m)); + } + return matvec; + } + + template + KOKKOS_INLINE_FUNCTION Real Jacobi(const PackType &spmat, const int imat_lo, + const int imat_hi, const PackType &v, const int iv, + const int b, const int k, const int j, const int i, + const Real rhs) const { + const Real matvec = MatVec(spmat, imat_lo, imat_hi, v, iv, b, k, j, i); + return (rhs - matvec + spmat(b, imat_lo + ndiag, k, j, i) * v(b, iv, k, j, i)) / + spmat(b, imat_lo + ndiag, k, j, i); + } +}; + +template +struct Stencil { + ParArray1D w; + ParArray1D ioff, joff, koff; + const int nstencil; + int ndiag; + Stencil() : nstencil(0), ndiag(0) {} + Stencil(const Stencil &st) + : w(st.w), ioff(st.ioff), joff(st.joff), koff(st.koff), nstencil(st.nstencil), + ndiag(st.ndiag) {} + Stencil(const std::string &label, const int n, std::vector wgt, + std::vector> off) + : w(label + "_w", n), ioff(label + "_ioff", n), joff(label + "_joff", n), + koff(label + "_koff", n), nstencil(n) { + PARTHENON_REQUIRE_THROWS(off.size() == 3, + "Offset array must have dimensions off[3][*]"); + PARTHENON_REQUIRE_THROWS(wgt.size() >= n, "Weight array too small"); + PARTHENON_REQUIRE_THROWS(off[0].size() >= n, "Offset array off[0][*] too small"); + PARTHENON_REQUIRE_THROWS(off[1].size() >= n, "Offset array off[1][*] too small"); + PARTHENON_REQUIRE_THROWS(off[2].size() >= n, "Offset array off[2][*] too small"); + auto w_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), w); + auto ioff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), ioff); + auto joff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), joff); + auto koff_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), koff); + + for (int i = 0; i < n; i++) { + w_h(i) = wgt[i]; + ioff_h(i) = off[0][i]; + joff_h(i) = off[1][i]; + koff_h(i) = off[2][i]; + if (off[0][i] == 0 && off[1][i] == 0 && off[2][i] == 0) { + ndiag = i; + } + } + + Kokkos::deep_copy(w, w_h); + Kokkos::deep_copy(ioff, ioff_h); + Kokkos::deep_copy(joff, joff_h); + Kokkos::deep_copy(koff, koff_h); + } + + template + KOKKOS_INLINE_FUNCTION Real MatVec(const PackType &v, const int iv, const int b, + const int k, const int j, const int i) const { + Real matvec = 0.0; + for (int n = 0; n < nstencil; n++) { + matvec += w(n) * v(b, iv, k + koff(n), j + joff(n), i + ioff(n)); + } + return matvec; + } + + template + KOKKOS_INLINE_FUNCTION Real Jacobi(const PackType &v, const int iv, const int b, + const int k, const int j, const int i, + const Real rhs) const { + const Real matvec = MatVec(v, iv, b, k, j, i); + return (rhs - matvec + w(ndiag) * v(b, iv, k, j, i)) / w(ndiag); + } +}; + +} // namespace solvers + +} // namespace parthenon + +#endif // SOLVERS_SOLVER_UTILS_HPP_ diff --git a/kharma/b_cleanup/solvers.cpp b/kharma/b_cleanup/solvers.cpp new file mode 100644 index 00000000..f61284c5 --- /dev/null +++ b/kharma/b_cleanup/solvers.cpp @@ -0,0 +1,22 @@ +//======================================================================================== +// (C) (or copyright) 2021. Triad National Security, LLC. All rights reserved. +// +// This program was produced under U.S. Government contract 89233218CNA000001 for Los +// Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC +// for the U.S. Department of Energy/National Nuclear Security Administration. All rights +// in the program are reserved by Triad National Security, LLC, and the U.S. Department +// of Energy/National Nuclear Security Administration. The Government is granted for +// itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide +// license in this material to reproduce, prepare derivative works, distribute copies to +// the public, perform publicly and display publicly, and to permit others to do so. +//======================================================================================== + +#include "bicgstab_solver.hpp" + +namespace parthenon { +namespace solvers { + +int BiCGStabCounter::global_num_bicgstab_solvers = 0; + +} // namespace solvers +} // namespace parthenon diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 8048b2f2..005d4b4e 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -135,7 +135,6 @@ KOKKOS_INLINE_FUNCTION int dir_of(const Loci loc) } } -#ifdef MPI_PARALLEL /** * Am I rank 0? Saves typing vs comparing the global every time */ @@ -143,12 +142,17 @@ inline bool MPIRank0() { return (parthenon::Globals::my_rank == 0 ? true : false); } -#else /** - * DUMMY version for no-MPI case: constexpr return for slight optimizations. + * Numbers I could just get as globals, but renamed for consistency */ -inline bool MPIRank0() { return true; } -#endif // MPI_PARALLEL +inline int MPINumRanks() +{ + return parthenon::Globals::nranks; +} +inline int MPIMyRank() +{ + return parthenon::Globals::my_rank; +} // A few generic "NDArray" overloads for readability. // TODO torn on futures of these: they're explicitly per-block diff --git a/kharma/main.cpp b/kharma/main.cpp index 4d319fdc..1c862262 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -174,7 +174,7 @@ int main(int argc, char *argv[]) const int &verbose = pmesh->packages.Get("Globals")->Param("verbose"); if(MPIRank0() && verbose > 0) { // Print a list of variables as Parthenon used to (still does by default) - std::cout << "#Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; + std::cout << "Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; // Print a list of all loaded packages. Surprisingly useful for debugging init logic std::cout << "Packages in use: " << std::endl; @@ -183,6 +183,11 @@ int main(int argc, char *argv[]) } std::cout << std::endl; + // Print the number of meshblocks and ranks in use + std::cout << "Running with " << pmesh->block_list.size() << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; + // TODO could print entire distribution if it gets interesting + std::cout << "Blocks on rank " << MPIMyRank() << ": " << pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; + // Write all parameters etc. to console if we should be especially wordy if ((verbose > 1) && MPIRank0()) { // This dumps the full Kokkos config, useful for double-checking From f077fac9aeb3deeb4e5b76484042f8b5a4aa3428 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 19 Oct 2023 16:45:08 -0600 Subject: [PATCH 184/219] Fix a nasty SMR/AMR artifact Parthenon's conventions for prolongation operators call over a different domain based on the values which determine the prolonged value. In our case despite working from faces, we are preserving divB in cells, so should be called over the same domain as the default prolongation operator, not over a domain tied to the particular face we're working on This was truly awful to chase down, but at least it's preventable going forward -- documentation of the operator interface will start to solve these sorts of issues. --- kharma/b_ct/b_ct.cpp | 35 ++++++++++++-------------------- kharma/b_ct/b_ct.hpp | 48 ++++++++++++++++++-------------------------- 2 files changed, 32 insertions(+), 51 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 3751bd20..cb6966d6 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -82,16 +82,12 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // FIELDS // Flags for B fields on faces. - // We don't mark these as "Primitive" and "Conserved" else they'd be bundled + // We don't mark these as "Conserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want // Also note we *always* sync B field conserved var - std::vector flags_prim_f = {Metadata::Real, Metadata::Face, Metadata::Derived, - Metadata::GetUserFlag("Explicit")}; std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; // TODO TODO Restart - auto m = Metadata(flags_prim_f); - pkg->AddField("prims.fB", m); - m = Metadata(flags_cons_f); + auto m = Metadata(flags_cons_f); if (!lazy_prolongation) m.RegisterRefinementOps(); else @@ -172,7 +168,6 @@ TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coa auto pmb = rc->GetBlockPointer(); const int ndim = pmb->pmy_mesh->ndim; auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); - auto B_Pf = rc->PackVariables(std::vector{"prims.fB"}); auto B_U = rc->PackVariables(std::vector{"cons.B"}); auto B_P = rc->PackVariables(std::vector{"prims.B"}); const auto& G = pmb->coords; @@ -182,26 +177,22 @@ TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coa // TODO get rid of prims on faces probably // Update the primitive B-fields on faces - const IndexRange3 bf = KDomain::GetRange(rc, domain, 0, 1, coarse); - pmb->par_for("UtoP_B", bf.ks, bf.ke, bf.js, bf.je, bf.is, bf.ie, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - // TODO will we need face area here? - B_Pf(F1, 0, k, j, i) = B_Uf(F1, 0, k, j, i) / G.gdet(Loci::face1, j, i); - B_Pf(F2, 0, k, j, i) = B_Uf(F2, 0, k, j, i) / G.gdet(Loci::face2, j, i); - B_Pf(F3, 0, k, j, i) = B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i); - } - ); - // Average the primitive vals for zone centers const IndexRange3 bc = KDomain::GetRange(rc, domain, coarse); + + // Average the primitive vals to zone centers pmb->par_for("UtoP_B_center", bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - B_P(V1, k, j, i) = (B_Pf(F1, 0, k, j, i) + B_Pf(F1, 0, k, j, i + 1)) / 2; - B_P(V2, k, j, i) = (ndim > 1) ? (B_Pf(F2, 0, k, j, i) + B_Pf(F2, 0, k, j + 1, i)) / 2 - : B_Pf(F2, 0, k, j, i); - B_P(V3, k, j, i) = (ndim > 2) ? (B_Pf(F3, 0, k, j, i) + B_Pf(F3, 0, k + 1, j, i)) / 2 - : B_Pf(F3, 0, k, j, i); + B_P(V1, k, j, i) = (B_Uf(F1, 0, k, j, i) / G.gdet(Loci::face1, j, i) + + B_Uf(F1, 0, k, j, i + 1) / G.gdet(Loci::face1, j, i + 1)) / 2; + B_P(V2, k, j, i) = (ndim > 1) ? (B_Uf(F2, 0, k, j, i) / G.gdet(Loci::face2, j, i) + + B_Uf(F2, 0, k, j + 1, i) / G.gdet(Loci::face2, j + 1, i)) / 2 + : B_Uf(F2, 0, k, j, i) / G.gdet(Loci::face2, j, i); + B_P(V3, k, j, i) = (ndim > 2) ? (B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i) + + B_Uf(F3, 0, k + 1, j, i) / G.gdet(Loci::face3, j, i)) / 2 + : B_Uf(F3, 0, k, j, i) / G.gdet(Loci::face3, j, i); } ); + // Recover conserved B at centers pmb->par_for("UtoP_B_centerPtoU", 0, NVEC-1, bc.ks, bc.ke, bc.js, bc.je, bc.is, bc.ie, KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { B_U(v, k, j, i) = B_P(v, k, j, i) * G.gdet(Loci::center, j, i); diff --git a/kharma/b_ct/b_ct.hpp b/kharma/b_ct/b_ct.hpp index b9db5649..420ca471 100644 --- a/kharma/b_ct/b_ct.hpp +++ b/kharma/b_ct/b_ct.hpp @@ -230,29 +230,25 @@ KOKKOS_FORCEINLINE_FUNCTION Real F(const ParArrayND &fine, constexpr int of_is_k = (offset == V3 && DIM > 2); constexpr int of_is_j = (offset == V2 && DIM > 1); constexpr int of_is_i = (offset == V1 && DIM > 0); - // if (fi == 56 && fj == 70) - // printf("I used dir %d offset %d %d %d, %d %d %d, %d %d %d, %d %d %d\n", diff_face+1, - // df_is_k+ds_is_k+of_is_k, df_is_j+ds_is_j+of_is_j, df_is_i+ds_is_i+of_is_i, - // ds_is_k+of_is_k , ds_is_j+of_is_j , ds_is_i+of_is_i, - // df_is_k+of_is_k , df_is_j+of_is_j , df_is_i+of_is_i, - // of_is_k , of_is_j , of_is_i); return fine(diff_face, l, m, n, fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) - * coords.FaceArea(fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) + * coords.FaceArea(fk+df_is_k+ds_is_k+of_is_k, fj+df_is_j+ds_is_j+of_is_j, fi+df_is_i+ds_is_i+of_is_i) - fine(diff_face, l, m, n, fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) - * coords.FaceArea(fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) + * coords.FaceArea(fk+ds_is_k+of_is_k , fj+ds_is_j+of_is_j , fi+ds_is_i+of_is_i) - fine(diff_face, l, m, n, fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) - * coords.FaceArea(fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) + * coords.FaceArea(fk+df_is_k+of_is_k , fj+df_is_j+of_is_j , fi+df_is_i+of_is_i) + fine(diff_face, l, m, n, fk+of_is_k , fj+of_is_j , fi+of_is_i) - * coords.FaceArea(fk+of_is_k , fj+of_is_j , fi+of_is_i); + * coords.FaceArea(fk+of_is_k , fj+of_is_j , fi+of_is_i); } struct ProlongateInternalOlivares { static constexpr bool OperationRequired(TopologicalElement fel, TopologicalElement cel) { - return fel == cel && (fel == F1 || fel == F2 || fel == F3); + // We will always be filling some locations of fine element fel with others of the same element. + // However, the chosen coarse element cel defines our *domain* + return IsSubmanifold(fel, cel); } - template KOKKOS_FORCEINLINE_FUNCTION static void Do(const int l, const int m, const int n, const int k, const int j, const int i, @@ -263,20 +259,19 @@ struct ProlongateInternalOlivares { const ParArrayND *pfine) { // Definitely exit on what we can't handle - if constexpr (el != TE::F1 && el != TE::F2 && el != TE::F3) - return; - // Exit if we're computing a trivial direction - if constexpr ((el == TE::F3 && (DIM < 3)) || (el == TE::F2 && (DIM < 2))) + // This is never hit as currently compiled in KHARMA + if constexpr (fel != TE::F1 && fel != TE::F2 && fel != TE::F3) return; // Handle permutations "naturally." // Olivares et al. is fond of listing x1 versions which permute, // this makes translating/checking those easier - constexpr int me = static_cast(el) % 3; + constexpr int me = static_cast(fel) % 3; constexpr int next = (me+1) % 3; constexpr int third = (me+2) % 3; // Fine array, indices + // Note the boundaries are *always the interior* auto &fine = *pfine; const int fi = (DIM > 0) ? (i - cib.s) * 2 + ib.s : ib.s; const int fj = (DIM > 1) ? (j - cjb.s) * 2 + jb.s : jb.s; @@ -298,31 +293,26 @@ struct ProlongateInternalOlivares { {1 - a[next], 3 + a[next], 3 - a[third], 1 + a[third]}, {1 - a[next], 3 + a[next], 1 + a[third], 3 - a[third]}}; - constexpr int diff_k = (me == V3), diff_j = (me == V2), diff_i = (me == V1); + constexpr int diff_k = (me == V3 && DIM > 2), diff_j = (me == V2 && DIM > 1), diff_i = (me == V1 && DIM > 0); // Iterate through the 4 sub-faces for (int elem=0; elem < 4; elem++) { // Make sure we can offset in other directions before doing so, though - // TODO eliminate redundant work or template these so the compiler can? const int off_i = (DIM > 0) ? (elem%2)*(me == V2) + (elem/2)*(me == V3) + (me == V1) : 0; const int off_j = (DIM > 1) ? (elem%2)*(me == V3) + (elem/2)*(me == V1) + (me == V2) : 0; const int off_k = (DIM > 2) ? (elem%2)*(me == V1) + (elem/2)*(me == V2) + (me == V3) : 0; - if (((el == TE::F1) && (fi + off_i > ib.e)) || - ((el == TE::F2) && (fj + off_j > jb.e)) || - ((el == TE::F3) && (fk + off_k > kb.e))) - return; fine(me, l, m, n, fk+off_k, fj+off_j, fi+off_i) = ( // Average faces on either side of us in selected direction (diff), on each of the 4 sub-faces (off) 0.5*(fine(me, l, m, n, fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) - * coords.Volume(fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + * coords.Volume(fk+off_k-diff_k, fj+off_j-diff_j, fi+off_i-diff_i) + fine(me, l, m, n, fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i) - * coords.Volume(fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + - 1./16*(coeff[elem][0]*F(fine, coords, l, m, n, fk, fj, fi) - + coeff[elem][1]*F(fine, coords, l, m, n, fk, fj, fi) - + coeff[elem][2]*F(fine, coords, l, m, n, fk, fj, fi) + * coords.Volume(fk+off_k+diff_k, fj+off_j+diff_j, fi+off_i+diff_i)) + + 1./16*(coeff[elem][0]*F(fine, coords, l, m, n, fk, fj, fi) + + coeff[elem][1]*F(fine, coords, l, m, n, fk, fj, fi) + + coeff[elem][2]*F(fine, coords, l, m, n, fk, fj, fi) + coeff[elem][3]*F(fine, coords, l, m, n, fk, fj, fi)) - ) / coords.Volume(fk+off_k, fj+off_j, fi+off_i); + ) / coords.Volume(fk+off_k, fj+off_j, fi+off_i); } } }; From 137014377b91fd4a9553fddad974240f19dbfc86 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 19 Oct 2023 16:50:13 -0600 Subject: [PATCH 185/219] Little initialization things B field initialization comments & one fix --- kharma/domain.hpp | 1 - kharma/prob/post_initialize.cpp | 5 +- kharma/prob/seed_B.cpp | 7 +-- pars/amr/kelvin_helmholtz_adaptive.par | 79 ++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 pars/amr/kelvin_helmholtz_adaptive.par diff --git a/kharma/domain.hpp b/kharma/domain.hpp index a15f2d6a..c100b3a2 100644 --- a/kharma/domain.hpp +++ b/kharma/domain.hpp @@ -112,7 +112,6 @@ inline IndexRange3 GetRange(T data, IndexDomain domain, int left_halo=0, int rig // Compute sizes with specified halo zones included in non-trivial dimensions const int& ndim = GetNDim(data); // If ghost & not x1 direction - // if const IndexRange il = IndexRange{ib.s + left_halo, ib.e + right_halo}; const IndexRange jl = (ndim > 1) ? IndexRange{jb.s + left_halo, jb.e + right_halo} : jb; const IndexRange kl = (ndim > 2) ? IndexRange{kb.s + left_halo, kb.e + right_halo} : kb; diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 16a007da..402ae0c8 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -53,15 +53,14 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) { // This call: - // 1. Initializes any magnetic fields which are "seeded," i.e., defined with a magnetic field implementation - // rather than assuming an implementation and setting the field with problem initialization. + // 1. Initializes any magnetic fields, according to parameters set by the problem or user. // 2. Renormalizes magnetic fields based on a desired ratio of maximum magnetic/gas pressures // 3. Adds any extra material which might be superimposed when restarting, e.g. "hotspot" regions a.k.a. "blobs" // 4. Resets a couple of incidental flags, if Parthenon read them from a restart file // 5. If necessary, cleans up any magnetic field divergence present on the grid // Coming into this function, at least the *interior* regions should be initialized with a problem: - // that is, at least rho, u, uvec on each physical zone. + // that is, rho, u, uvec, and any nonzero auxiliary variables, on each physical zone. // If you need Dirichlet boundary conditions, the domain-edge *ghost* zones should also be initialized, // as they will be "frozen in" during this function and applied thereafter. diff --git a/kharma/prob/seed_B.cpp b/kharma/prob/seed_B.cpp index a335dc26..fc1c33f0 100644 --- a/kharma/prob/seed_B.cpp +++ b/kharma/prob/seed_B.cpp @@ -148,7 +148,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom amp_B1, amp_B2, amp_B3, amp2_B1, amp2_B2, amp2_B3, null1, B_Pf2, null2); - B_Uf(F2, 0, k, j, i) = B_Pf2; + B_Uf(F2, 0, k, j, i) = B_Pf2 * gdet; G.coord_embed(k, j, i, Loci::face3, Xembed); gdet = G.gdet(Loci::face3, j, i); @@ -297,8 +297,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom } }); - if (pkgs.count("B_CT")) - { + if (pkgs.count("B_CT")) { auto B_Uf = rc->PackVariables(std::vector{"cons.fB"}); // This fills a couple zones outside the exact interior with bad data // Careful of that w/e.g. Dirichlet bounds. @@ -340,7 +339,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom } // Finally, make sure we initialize the primitive field too B_FluxCT::BlockUtoP(rc, domain); - } // TODO B_CD!! + } return TaskStatus::complete; } diff --git a/pars/amr/kelvin_helmholtz_adaptive.par b/pars/amr/kelvin_helmholtz_adaptive.par new file mode 100644 index 00000000..c09087df --- /dev/null +++ b/pars/amr/kelvin_helmholtz_adaptive.par @@ -0,0 +1,79 @@ +# Kelvin-Helmholtz instability +# Basic K-H problem, usually used to test AMR +# since it gets refined in predictable places + + +problem_id = kelvin_helmholtz + + +refinement = adaptive +numlevel = 3 + +nx1 = 256 +x1min = 0.0 +x1max = 1.0 +ix1_bc = periodic +ox1_bc = periodic + +nx2 = 256 +x2min = 0.0 +x2max = 2.0 +ix2_bc = periodic +ox2_bc = periodic + +nx3 = 1 +x3min = -0.01 +x3max = 0.01 +ix3_bc = periodic +ox3_bc = periodic + + +nx1 = 64 +nx2 = 64 +nx3 = 1 + + +method = derivative_order_1 +field = prims.rho +refine_tol = 0.01 +derefine_tol = 0.001 + + +base = cartesian_minkowski +transform = null + + +tlim = 200.0 +integrator = rk2 + + +tscale = 0.01 + + +u_jitter = 0.01 + + +type = kharma + + +cfl = 0.9 +gamma = 1.666667 +reconstruction = linear_mc + + +type = constant +B10 = 1 +B20 = 1 +solver = face_ct +ct_scheme = bs99 + + +verbose = 1 +flag_verbose = 0 +extra_checks = 0 + + +file_type = hdf5 +dt = 5.0 +variables = prims.rho, prims.u, prims.uvec, prims.B, divB + From 83494d1eb0673fa0574f564684dda144d17a764d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 20 Oct 2023 15:22:27 -0600 Subject: [PATCH 186/219] Avoid overstepping arrays, copying non-cell vars --- kharma/b_ct/b_ct.cpp | 8 ++++---- kharma/driver/kharma_driver.hpp | 6 +++--- kharma/driver/kharma_step.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index cb6966d6..e361379d 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -211,8 +211,8 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::entire, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::entire, 1, 1); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 1, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); @@ -339,8 +339,8 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) auto& emf_pack = md->PackVariables(std::vector{"B_CT.emf"}); // Figure out indices - const IndexRange3 b = KDomain::GetRange(md, IndexDomain::entire, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::entire, 0, 1); + const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); diff --git a/kharma/driver/kharma_driver.hpp b/kharma/driver/kharma_driver.hpp index 5fe825c4..7e6f0a47 100644 --- a/kharma/driver/kharma_driver.hpp +++ b/kharma/driver/kharma_driver.hpp @@ -165,12 +165,12 @@ class KHARMADriver : public MultiStageDriver { static TaskStatus WeightedSumDataFace(const std::vector &flags, MeshData *in1, MeshData *in2, const Real w1, const Real w2, MeshData *out) { - Kokkos::Profiling::pushRegion("Task_WeightedSumData"); + Kokkos::Profiling::pushRegion("Task_WeightedSumDataFace"); const auto &x = in1->PackVariables(flags); const auto &y = in2->PackVariables(flags); const auto &z = out->PackVariables(flags); parthenon::par_for( - DEFAULT_LOOP_PATTERN, "WeightedSumData", DevExecSpace(), 0, x.GetDim(5) - 1, 0, + DEFAULT_LOOP_PATTERN, "WeightedSumDataFace", DevExecSpace(), 0, x.GetDim(5) - 1, 0, x.GetDim(4) - 1, 0, x.GetDim(3) - 1, 0, x.GetDim(2) - 1, 0, x.GetDim(1) - 1, KOKKOS_LAMBDA(const int b, const int l, const int k, const int j, const int i) { // TOOD(someone) This is potentially dangerous and/or not intended behavior @@ -182,7 +182,7 @@ class KHARMADriver : public MultiStageDriver { z(b, F3, l, k, j, i) = w1 * x(b, F3, l, k, j, i) + w2 * y(b, F3, l, k, j, i); } }); - Kokkos::Profiling::popRegion(); // Task_WeightedSumData + Kokkos::Profiling::popRegion(); // Task_WeightedSumDataFace return TaskStatus::complete; } diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 9adb64ec..5645958a 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -105,7 +105,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int // At the end of the step, updating "mbd_sub_step_final" updates the base // So we have to keep a copy at the beginning to calculate jcon // We have to explicitly copy, since after the first step `Add`==`Get` - Copy>({}, base.get(), pmb->meshblock_data.Add("preserve").get()); + Copy>({Metadata::Cell}, base.get(), pmb->meshblock_data.Add("preserve").get()); } } } From 5879ca06ee9cad161166bd69496dfa2906aeeff6 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 20 Oct 2023 16:37:07 -0600 Subject: [PATCH 187/219] Fix B_CT indices from last commit --- kharma/b_ct/b_ct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index e361379d..90f70291 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -212,7 +212,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) // Figure out indices const IndexRange3 b = KDomain::GetRange(md, IndexDomain::interior, 0, 0); - const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 1, 1); + const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); From b98056163d9c727945fc96161d0fe084b662dfbd Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 27 Oct 2023 12:32:07 -0600 Subject: [PATCH 188/219] Fix an ordering issue in the KHARMA driver --- kharma/driver/kharma_step.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 5645958a..58c1dea2 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -153,29 +153,29 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int const KReconstruction::Type& recon = driver_pkg.Get("recon"); auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_flux, tl, recon, md_sub_step_init.get()); + // Any package modifications to the fluxes. e.g.: + // 1. Flux-CT calculations for B field transport + // 2. Zero fluxes through poles + // etc + auto t_fix_flux = tl.AddTask(t_fluxes, Packages::FixFlux, md_sub_step_init.get()); + // If we're in AMR, correct fluxes from neighbors - auto t_emf = t_fluxes; + auto t_flux_bounds = t_fix_flux; if (pmesh->multilevel || use_b_ct) { - tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); - auto t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_b_ct = pmesh->mesh_data.AddShallow("B_CT", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition - auto t_emf_local = tl.AddTask(t_fluxes, B_CT::CalculateEMF, md_sub_step_init.get()); - auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_b_ct); + auto t_emf_local = tl.AddTask(t_flux_bounds, B_CT::CalculateEMF, md_sub_step_init.get()); + t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_b_ct); } + auto t_load_send_flux = tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_load_send_flux, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } - // Any package modifications to the fluxes. e.g.: - // 1. Flux-CT calculations for B field transport - // 2. Zero fluxes through poles - // etc - auto t_fix_flux = tl.AddTask(t_emf, Packages::FixFlux, md_sub_step_init.get()); - // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" - auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); + auto t_flux_div = tl.AddTask(t_flux_bounds, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); // Add any source terms: geometric \Gamma * T, wind, damping, etc etc // Also where CT sets the change in face fields @@ -221,7 +221,7 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int md_sub_step_init.get(), md_sub_step_final.get()); } - KHARMADriver::AddBoundarySync(t_copy_prims, tl, md_sync); + KHARMADriver::AddBoundarySync(t_copy_prims | t_update, tl, md_sync); } EndFlag(); From 2a39af0c3ac2e40aa48534bd3129ee218998122d Mon Sep 17 00:00:00 2001 From: Benjamin Prather Date: Fri, 3 Nov 2023 15:04:45 -0400 Subject: [PATCH 189/219] Fix an issue where mass at outer boundaries was not allowed to *outflow* rather than inflow. --- kharma/boundaries/boundaries.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 87399e60..a7afe62e 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -466,11 +466,21 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) const int m_rho = cons_map["cons.rho"].first; // ...and if this face of the block corresponds to a global boundary... if (pmb->boundary_flag[bface] == BoundaryFlag::user) { - pmb->par_for( - "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { - F.flux(bdir, m_rho, k, j, i) = m::min(F.flux(bdir, m_rho, k, j, i), 0.); - }); + if (binner) { + pmb->par_for( + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + F.flux(bdir, m_rho, k, j, i) = m::min(F.flux(bdir, m_rho, k, j, i), 0.); + } + ); + } else { + pmb->par_for( + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + F.flux(bdir, m_rho, k, j, i) = m::max(F.flux(bdir, m_rho, k, j, i), 0.); + } + ); + } } } From 1692a9dbe070d3ee5daa09288118fe447fedf0b2 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 15:14:12 -0500 Subject: [PATCH 190/219] Incidentals Mark some device functions as forceinline Remove old comments Add new personal machine compile parameters --- kharma/b_ct/b_ct.cpp | 3 --- kharma/b_flux_ct/b_flux_ct.hpp | 10 +++++----- kharma/decs.hpp | 4 ++-- kharma/driver/kharma_driver.cpp | 2 -- kharma/flux/flux_functions.hpp | 18 +++++++++--------- kharma/prob/seed_B.cpp | 6 ++++-- machines/bp.sh | 8 ++++++++ 7 files changed, 28 insertions(+), 23 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 90f70291..741d2fab 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -174,9 +174,6 @@ TaskStatus B_CT::BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coa // Return if we're not syncing U & P at all (e.g. edges) if (B_Uf.GetDim(4) == 0) return TaskStatus::complete; - // TODO get rid of prims on faces probably - - // Update the primitive B-fields on faces const IndexRange3 bc = KDomain::GetRange(rc, domain, coarse); // Average the primitive vals to zone centers diff --git a/kharma/b_flux_ct/b_flux_ct.hpp b/kharma/b_flux_ct/b_flux_ct.hpp index bb266fbc..398ab55b 100644 --- a/kharma/b_flux_ct/b_flux_ct.hpp +++ b/kharma/b_flux_ct/b_flux_ct.hpp @@ -146,7 +146,7 @@ inline Real ReducePhi5(MeshData *md) * TODO likely better templated, as with all ND stuff */ template -KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& B_U, const int& b, +KOKKOS_FORCEINLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& B_U, const int& b, const int& k, const int& j, const int& i, const bool& do_3D) { const double norm = (do_3D) ? 0.25 : 0.5; @@ -170,7 +170,7 @@ KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& B return norm*term1/G.Dxc<1>(i) + norm*term2/G.Dxc<2>(j) + norm*term3/G.Dxc<3>(k); } template -KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& P, const VarMap& m_p, +KOKKOS_FORCEINLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& P, const VarMap& m_p, const int& b, const int& k, const int& j, const int& i, const bool& do_3D) { @@ -200,7 +200,7 @@ KOKKOS_INLINE_FUNCTION double corner_div(const GRCoordinates& G, const Global& P * Note this is forward-difference, while previous def is backward */ template -KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, const int& b, +KOKKOS_FORCEINLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, const int& b, const int& k, const int& j, const int& i, const bool& do_3D, double& B1, double& B2, double& B3) { @@ -227,7 +227,7 @@ KOKKOS_INLINE_FUNCTION void center_grad(const GRCoordinates& G, const Global& P, B3 = norm*term3/G.Dxc<3>(k); } -KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, +KOKKOS_FORCEINLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) { // Take a flux-ct step from the corner potentials. @@ -270,7 +270,7 @@ KOKKOS_INLINE_FUNCTION void averaged_curl_3D(const GRCoordinates& G, const GridV B_U(V3, k, j, i) = (A2c1f - A2c1b) / G.Dxc<1>(i) - (A1c2f - A1c2b) / G.Dxc<2>(j); } -KOKKOS_INLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, +KOKKOS_FORCEINLINE_FUNCTION void averaged_curl_2D(const GRCoordinates& G, const GridVector& A, const GridVector& B_U, const int& k, const int& j, const int& i) { // A3,2 derivative diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 005d4b4e..27bd5641 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -104,7 +104,7 @@ using GReal = double; enum class Loci{face1=0, face2, face3, center, corner}; // Return the face location corresponding to the direction 'dir' -KOKKOS_INLINE_FUNCTION Loci loc_of(const int& dir) +KOKKOS_FORCEINLINE_FUNCTION Loci loc_of(const int& dir) { switch (dir) { case 0: @@ -119,7 +119,7 @@ KOKKOS_INLINE_FUNCTION Loci loc_of(const int& dir) return Loci::corner; } } -KOKKOS_INLINE_FUNCTION int dir_of(const Loci loc) +KOKKOS_FORCEINLINE_FUNCTION int dir_of(const Loci loc) { switch (loc) { case Loci::center: diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index b1c6e0f9..9f6497e6 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -173,8 +173,6 @@ TaskID KHARMADriver::AddBoundarySync(const TaskID t_start, TaskList &tl, std::sh auto ¶ms = pmesh->packages.Get("Driver")->AllParams(); bool multilevel = pmesh->multilevel; - // TODO PtoU for B field when sync_prims? - // The Parthenon exchange tasks include applying physical boundary conditions now. // We generally do not take advantage of this yet, but good to know when reasoning about initialization. Flag("ParthenonAddSync"); diff --git a/kharma/flux/flux_functions.hpp b/kharma/flux/flux_functions.hpp index c128960c..6436fa07 100644 --- a/kharma/flux/flux_functions.hpp +++ b/kharma/flux/flux_functions.hpp @@ -51,7 +51,7 @@ namespace Flux // TODO Q > 0 != emhd_enabled. Store enablement in emhd_params since we need it anyway template -KOKKOS_INLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const FourVectors D, +KOKKOS_FORCEINLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& dir, Real T[GR_DIM]) { @@ -79,7 +79,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Local& P, const VarMap& m_p, const } template -KOKKOS_INLINE_FUNCTION void calc_tensor(const Global& P, const VarMap& m_p, const FourVectors D, +KOKKOS_FORCEINLINE_FUNCTION void calc_tensor(const Global& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const int& dir, Real T[GR_DIM]) @@ -114,7 +114,7 @@ KOKKOS_INLINE_FUNCTION void calc_tensor(const Global& P, const VarMap& m_p, cons * Keep in mind loc should usually correspond to dir for perpendicuar fluxes */ template -KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, +KOKKOS_FORCEINLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& j, const int& i, const int& dir, const Local& flux, const VarMap& m_u, const Loci loc=Loci::center) { @@ -178,7 +178,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Local& P, } template -KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, +KOKKOS_FORCEINLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const int dir, const Global& flux, const VarMap& m_u, const Loci loc=Loci::center) @@ -245,7 +245,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux(const GRCoordinates& G, const Global& P * P->U for just the GRMHD variables, but using the full tensor. Needed with floors and in a few places */ template -KOKKOS_INLINE_FUNCTION void prim_to_flux_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, +KOKKOS_FORCEINLINE_FUNCTION void prim_to_flux_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, const FourVectors D, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const int dir, const Global& flux, const VarMap& m_u, const Loci loc=Loci::center) @@ -266,7 +266,7 @@ KOKKOS_INLINE_FUNCTION void prim_to_flux_mhd(const GRCoordinates& G, const Globa * Get the conserved (E)GRMHD variables corresponding to primitives in a zone. Equivalent to prim_to_flux with dir==0 */ template -KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Local& P, const VarMap& m_p, +KOKKOS_FORCEINLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Local& P, const VarMap& m_p, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& j, const int& i, const Local& U, const VarMap& m_u, const Loci& loc=Loci::center) { @@ -276,7 +276,7 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Local& P, const } template -KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Global& P, const VarMap& m_p, +KOKKOS_FORCEINLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Global& P, const VarMap& m_p, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const Global& U, const VarMap& m_u, const Loci& loc=Loci::center) @@ -287,7 +287,7 @@ KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const Global& P, cons } template -KOKKOS_INLINE_FUNCTION void p_to_u_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, +KOKKOS_FORCEINLINE_FUNCTION void p_to_u_mhd(const GRCoordinates& G, const Global& P, const VarMap& m_p, const EMHD::EMHD_parameters& emhd_params, const Real& gam, const int& k, const int& j, const int& i, const Global& U, const VarMap& m_u, const Loci& loc=Loci::center) @@ -302,7 +302,7 @@ KOKKOS_INLINE_FUNCTION void p_to_u_mhd(const GRCoordinates& G, const Global& P, * This is only called in GetFlux, so we only provide a ScratchPad form */ template -KOKKOS_INLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const VarMap& m, const FourVectors& D, +KOKKOS_FORCEINLINE_FUNCTION void vchar(const GRCoordinates& G, const Local& P, const VarMap& m, const FourVectors& D, const Real& gam, const EMHD::EMHD_parameters& emhd_params, const int& k, const int& j, const int& i, const Loci& loc, const int& dir, Real& cmax, Real& cmin) diff --git a/kharma/prob/seed_B.cpp b/kharma/prob/seed_B.cpp index fc1c33f0..843d2678 100644 --- a/kharma/prob/seed_B.cpp +++ b/kharma/prob/seed_B.cpp @@ -287,8 +287,9 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom G.coords.con_vec_to_native(Xnative, A_tilt_embed, A_tilt); // Lower the result as we need curl(A_mu). Done at local zone. - double A_tilt_lower[GR_DIM] = {0}; - G.lower(A_tilt, A_tilt_lower, k, j, i, Loci::corner); + double A_tilt_lower[GR_DIM] = {0}, gcov[GR_DIM][GR_DIM] = {0}; + G.coords.gcov_native(Xnative, gcov); + DLOOP2 A_tilt_lower[mu] += gcov[mu][nu] * A_tilt[nu]; VLOOP A(v, k, j, i) = A_tilt_lower[1 + v]; } else { // Some problems rely on a very accurate A->B, which the rotation lacks. @@ -318,6 +319,7 @@ TaskStatus SeedBFieldType(MeshBlockData *rc, ParameterInput *pin, IndexDom throw std::runtime_error("Must initialize 1D field directly!"); } B_CT::BlockUtoP(rc, domain); + //std::cout << "Block divB: " << B_CT::BlockMaxDivB(rc) << std::endl; } else if (pkgs.count("B_FluxCT")) { // Calculate B-field GridVector B_U = rc->Get("cons.B").data; diff --git a/machines/bp.sh b/machines/bp.sh index 47b52cd1..8cca870e 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -1,6 +1,14 @@ # BP's machines +if [[ $HOST == "pn2400633"* ]]; then + export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH" + PREFIX_PATH=/opt/homebrew/ + C_NATIVE=/opt/homebrew/bin/gcc-13 + CXX_NATIVE=/opt/homebrew/bin/g++-13 + CXXFLAGS="-Wl,-ld_classic" +fi + if [[ $HOST == "cheshire"* ]]; then HOST_ARCH="HSW" DEVICE_ARCH="PASCAL61" From 6b5529bca611b8f8f767bc0f1ef00f58f61f73a0 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 15:18:02 -0500 Subject: [PATCH 191/219] Initial meshification Try to avoid per-block segments in the ImEx driver. This should make KHARMA faster & more flexible in future Currently still slow since several "mesh" things are loops, but soon^TM --- kharma/driver/imex_step.cpp | 82 +++++++++++++++++++++------------- kharma/electrons/electrons.hpp | 10 ++++- kharma/grmhd/grmhd.cpp | 1 + kharma/grmhd/grmhd.hpp | 11 +++++ kharma/implicit/implicit.hpp | 7 +++ kharma/inverter/inverter.hpp | 7 +++ kharma/kharma_package.cpp | 8 ++++ kharma/kharma_package.hpp | 1 + kharma/main.cpp | 6 +-- 9 files changed, 97 insertions(+), 36 deletions(-) diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 7fb9db74..0b179327 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -46,6 +46,7 @@ // Other headers #include "boundaries.hpp" #include "flux.hpp" +#include "kharma.hpp" #include "resize_restart.hpp" #include "implicit.hpp" @@ -96,7 +97,18 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta } } } - + + static std::vector sync_vars; + if (sync_vars.size() == 0) { + // Build the universe of variables to let Parthenon see when exchanging boundaries. + // This is built to exclude incidental variables like B field initialization stuff, EMFs, etc. + using FC = Metadata::FlagCollection; + auto sync_flags = FC({Metadata::GetUserFlag("Primitive"), Metadata::Conserved, Metadata::Face}, true); + sync_vars = KHARMA::GetVariableNames(&(pmesh->packages), sync_flags); + } + // We'll only ever sync the current stage "final" + //pmesh->mesh_data.AddShallow("sync", integrator->stage_name[stage], sync_vars); + // Big synchronous region: get & apply fluxes to advance the fluid state // num_partitions is nearly always 1 const int num_partitions = pmesh->DefaultNumPartitions(); @@ -118,6 +130,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Normally we put explicit update in md_solver, then add implicitly-evolved variables and copy back. // If we're not doing an implicit solve at all, just write straight to sub_step_final std::shared_ptr> &md_solver = (use_implicit) ? pmesh->mesh_data.GetOrAdd("solver", i) : md_sub_step_final; + auto &md_sync = pmesh->mesh_data.AddShallow("sync"+integrator->stage_name[stage]+std::to_string(i), md_sub_step_final, sync_vars); // Start receiving flux corrections and ghost cells auto t_start_recv_bound = tl.AddTask(t_none, parthenon::StartReceiveBoundBufs, md_sub_step_final); @@ -131,29 +144,28 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta const KReconstruction::Type& recon = driver_pkg.Get("recon"); auto t_fluxes = KHARMADriver::AddFluxCalculations(t_start_recv_bound, tl, recon, md_sub_step_init.get()); + // Any package modifications to the fluxes. e.g.: + // 1. CT calculations for B field transport + // 2. Zero fluxes through poles + // etc + auto t_fix_flux = tl.AddTask(t_fluxes, Packages::FixFlux, md_sub_step_init.get()); + // If we're in AMR, correct fluxes from neighbors - auto t_emf = t_fluxes; + auto t_flux_bounds = t_fix_flux; if (pmesh->multilevel || use_b_ct) { - tl.AddTask(t_fluxes, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_fluxes, parthenon::ReceiveFluxCorrections, md_sub_step_init); - auto t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); - auto t_emf = t_flux_bounds; + auto t_load_send_flux = tl.AddTask(t_fix_flux, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_load_send_flux, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_flux_bounds, B_CT::CalculateEMF, md_sub_step_init.get()); - auto t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); + t_flux_bounds = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_sub_step_init); } } - // Any package modifications to the fluxes. e.g.: - // 1. CT calculations for B field transport - // 2. Zero fluxes through poles - // etc - auto t_fix_flux = tl.AddTask(t_emf, Packages::FixFlux, md_sub_step_init.get()); - // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" - auto t_flux_div = tl.AddTask(t_fix_flux, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); + auto t_flux_div = tl.AddTask(t_flux_bounds, Update::FluxDivergence>, md_sub_step_init.get(), md_flux_src.get()); // Add any source terms: geometric \Gamma * T, wind, damping, etc etc auto t_sources = tl.AddTask(t_flux_div, Packages::AddSource, md_sub_step_init.get(), md_flux_src.get()); @@ -203,7 +215,7 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // Make sure the primitive values of *explicitly-evolved* variables are updated. // Packages with implicitly-evolved vars should only register BoundaryUtoP or BoundaryPtoU - auto t_explicit_UtoP = tl.AddTask(t_copy_prims, Packages::MeshUtoP, md_solver.get(), IndexDomain::interior, false); + auto t_explicit_UtoP = tl.AddTask(t_copy_prims | t_update, Packages::MeshUtoP, md_solver.get(), IndexDomain::entire, false); // Done with explicit update auto t_explicit = t_explicit_UtoP; @@ -250,16 +262,17 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // but hasn't been tested to do so yet. auto t_floors = tl.AddTask(t_implicit, Packages::MeshApplyFloors, md_sub_step_final.get(), IndexDomain::interior); - KHARMADriver::AddBoundarySync(t_floors, tl, md_sub_step_final); + KHARMADriver::AddBoundarySync(t_floors, tl, md_sync); } // Async Region: Any post-sync tasks. Fixups, timestep & AMR tagging. - TaskRegion &async_region2 = tc.AddRegion(blocks.size()); - for (int i = 0; i < blocks.size(); i++) { - auto &pmb = blocks[i]; + //TaskRegion &async_region2 = tc.AddRegion(blocks.size()); + TaskRegion &async_region2 = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { auto &tl = async_region2[i]; - auto &mbd_sub_step_init = pmb->meshblock_data.Get(integrator->stage_name[stage-1]); - auto &mbd_sub_step_final = pmb->meshblock_data.Get(integrator->stage_name[stage]); + auto &md_sub_step_init = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage-1], i); + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + auto &md_sync = pmesh->mesh_data.AddShallow("sync"+integrator->stage_name[stage]+std::to_string(i), md_sub_step_final, sync_vars); // If we're evolving the GRMHD variables explicitly, we need to fix UtoP variable inversion failures. // If implicitly, we run a (very similar) fix for solver failures. @@ -269,42 +282,43 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // TODO fixups as a callback? auto t_fix_utop = t_none; if (!pkgs.at("GRMHD")->Param("implicit")) { - t_fix_utop = tl.AddTask(t_none, Inverter::FixUtoP, mbd_sub_step_final.get()); + t_fix_utop = tl.AddTask(t_none, Inverter::MeshFixUtoP, md_sub_step_final.get()); } auto t_fix_solve = t_fix_utop; if (use_implicit) { - t_fix_solve = tl.AddTask(t_fix_utop, Implicit::FixSolve, mbd_sub_step_final.get()); + t_fix_solve = tl.AddTask(t_fix_utop, Implicit::MeshFixSolve, md_sub_step_final.get()); } - auto t_set_bc = tl.AddTask(t_fix_solve, parthenon::ApplyBoundaryConditions, mbd_sub_step_final); + // Re-apply boundary conditions to reflect fixes + auto t_set_bc = tl.AddTask(t_fix_solve, parthenon::ApplyBoundaryConditionsOnCoarseOrFineMD, md_sync, false); // Any package- (likely, problem-) specific source terms which must be applied to primitive variables // Apply these only after the final step so they're operator-split auto t_prim_source = t_set_bc; if (stage == integrator->nstages) { - t_prim_source = tl.AddTask(t_set_bc, Packages::BlockApplyPrimSource, mbd_sub_step_final.get()); + t_prim_source = tl.AddTask(t_set_bc, Packages::MeshApplyPrimSource, md_sub_step_final.get()); } // Electron heating goes where it does in the KHARMA Driver, for the same reasons auto t_heat_electrons = t_prim_source; if (use_electrons) { - t_heat_electrons = tl.AddTask(t_prim_source, Electrons::ApplyElectronHeating, - mbd_sub_step_init.get(), mbd_sub_step_final.get()); + t_heat_electrons = tl.AddTask(t_prim_source, Electrons::MeshApplyElectronHeating, + md_sub_step_init.get(), md_sub_step_final.get()); } // Make sure *all* conserved vars are synchronized at step end - auto t_ptou = tl.AddTask(t_heat_electrons, Flux::BlockPtoU, mbd_sub_step_final.get(), IndexDomain::entire, false); + auto t_ptou = tl.AddTask(t_heat_electrons, Flux::MeshPtoU, md_sub_step_final.get(), IndexDomain::entire, false); auto t_step_done = t_ptou; // Estimate next time step based on ctop if (stage == integrator->nstages) { auto t_new_dt = - tl.AddTask(t_step_done, Update::EstimateTimestep>, mbd_sub_step_final.get()); + tl.AddTask(t_step_done, Update::EstimateTimestep>, md_sub_step_final.get()); // Update refinement if (pmesh->adaptive) { auto tag_refine = tl.AddTask( - t_step_done, parthenon::Refinement::Tag>, mbd_sub_step_final.get()); + t_step_done, parthenon::Refinement::Tag>, md_sub_step_final.get()); } } } @@ -326,8 +340,12 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // modified on each rank. const auto &two_sync = pkgs.at("Driver")->Param("two_sync"); if (two_sync) { - auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], 0); - KHARMADriver::AddFullSyncRegion(tc, md_sub_step_final); + TaskRegion &async_region3 = tc.AddRegion(num_partitions); + for (int i = 0; i < num_partitions; i++) { + auto &md_sub_step_final = pmesh->mesh_data.GetOrAdd(integrator->stage_name[stage], i); + auto &md_sync = pmesh->mesh_data.AddShallow("sync"+integrator->stage_name[stage]+std::to_string(i), md_sub_step_final, sync_vars); + KHARMADriver::AddFullSyncRegion(tc, md_sync); + } } return tc; diff --git a/kharma/electrons/electrons.hpp b/kharma/electrons/electrons.hpp index 5d7cb64f..da6bfdb5 100644 --- a/kharma/electrons/electrons.hpp +++ b/kharma/electrons/electrons.hpp @@ -103,6 +103,14 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse=false); * TODO this function should update fflag to reflect temperature ratio floor hits */ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData *rc); +inline TaskStatus MeshApplyElectronHeating(MeshData *md_old, MeshData *md) +{ + Flag("MeshApplyElectronHeating"); + for (int i=0; i < md->NumBlocks(); ++i) + ApplyElectronHeating(md_old->GetBlockData(i).get(), md->GetBlockData(i).get()); + EndFlag(); + return TaskStatus::complete; +} /** * KHARMA requires some method for getting conserved variables from primitives, as well. @@ -111,7 +119,7 @@ TaskStatus ApplyElectronHeating(MeshBlockData *rc_old, MeshBlockData * package defining new primitive/conserved vars must not only provide a prim_to_flux here, * but add it to the list in Flux::prim_to_flux. */ -KOKKOS_INLINE_FUNCTION void p_to_u(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, +KOKKOS_FORCEINLINE_FUNCTION void p_to_u(const GRCoordinates& G, const VariablePack& P, const VarMap& m_p, const int& k, const int& j, const int& i, const VariablePack& flux, const VarMap m_u, const Loci loc=Loci::center) { diff --git a/kharma/grmhd/grmhd.cpp b/kharma/grmhd/grmhd.cpp index e1d7746a..b1ef47ad 100644 --- a/kharma/grmhd/grmhd.cpp +++ b/kharma/grmhd/grmhd.cpp @@ -187,6 +187,7 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

CheckRefinementBlock = GRMHD::CheckRefinement; pkg->EstimateTimestepBlock = GRMHD::EstimateTimestep; + pkg->EstimateTimestepMesh = GRMHD::MeshEstimateTimestep; pkg->PostStepDiagnosticsMesh = GRMHD::PostStepDiagnostics; // TODO TODO Reductions diff --git a/kharma/grmhd/grmhd.hpp b/kharma/grmhd/grmhd.hpp index 53878118..d9be5345 100644 --- a/kharma/grmhd/grmhd.hpp +++ b/kharma/grmhd/grmhd.hpp @@ -55,6 +55,17 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

dt */ Real EstimateTimestep(MeshBlockData *rc); +inline Real MeshEstimateTimestep(MeshData *md) +{ + Flag("MeshEstimateTimestep"); + Real ndt = std::numeric_limits::max(); + for (int i=0; i < md->NumBlocks(); ++i) { + double dtb = EstimateTimestep(md->GetBlockData(i).get()); + if (dtb < ndt) ndt = dtb; + } + EndFlag(); + return ndt; +} // Internal version for the light phase speed crossing time of smallest zone Real EstimateRadiativeTimestep(MeshBlockData *rc); diff --git a/kharma/implicit/implicit.hpp b/kharma/implicit/implicit.hpp index 91b4413b..e299f234 100644 --- a/kharma/implicit/implicit.hpp +++ b/kharma/implicit/implicit.hpp @@ -111,6 +111,13 @@ std::vector GetOrderedNames(MeshBlockData *rc, const Metadata * @return TaskStatus */ TaskStatus FixSolve(MeshBlockData *mbd); +inline TaskStatus MeshFixSolve(MeshData *md) { + Flag("MeshFixSolve"); + for (int i=0; i < md->NumBlocks(); ++i) + FixSolve(md->GetBlockData(i).get()); + EndFlag(); + return TaskStatus::complete; +} /** * Print diagnostics about number of failed solves diff --git a/kharma/inverter/inverter.hpp b/kharma/inverter/inverter.hpp index 78c1503d..fdf3096d 100644 --- a/kharma/inverter/inverter.hpp +++ b/kharma/inverter/inverter.hpp @@ -72,6 +72,13 @@ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse); * LOCKSTEP: this function expects and should preserve P<->U */ TaskStatus FixUtoP(MeshBlockData *rc); +inline TaskStatus MeshFixUtoP(MeshData *md) { + Flag("MeshFixUtoP"); + for (int i=0; i < md->NumBlocks(); ++i) + FixUtoP(md->GetBlockData(i).get()); + EndFlag(); + return TaskStatus::complete; +} /** * Print details of any inversion failures or fixed zones diff --git a/kharma/kharma_package.cpp b/kharma/kharma_package.cpp index 96099ef3..3f229a6a 100644 --- a/kharma/kharma_package.cpp +++ b/kharma/kharma_package.cpp @@ -183,6 +183,14 @@ TaskStatus Packages::BlockApplyPrimSource(MeshBlockData *rc) EndFlag(); return TaskStatus::complete; } +TaskStatus Packages::MeshApplyPrimSource(MeshData *md) +{ + Flag("MeshApplyPrimSource"); + for (int i=0; i < md->NumBlocks(); ++i) + BlockApplyPrimSource(md->GetBlockData(i).get()); + EndFlag(); + return TaskStatus::complete; +} TaskStatus Packages::BlockApplyFloors(MeshBlockData *mbd, IndexDomain domain) { diff --git a/kharma/kharma_package.hpp b/kharma/kharma_package.hpp index 6075ea4a..2604ed38 100644 --- a/kharma/kharma_package.hpp +++ b/kharma/kharma_package.hpp @@ -157,6 +157,7 @@ TaskStatus AddSource(MeshData *md, MeshData *mdudt); * Add any source terms to the primitive variables. Applied directly rather than adding to a derivative. */ TaskStatus BlockApplyPrimSource(MeshBlockData *rc); +TaskStatus MeshApplyPrimSource(MeshData *md); /** * Apply all floors, including any package-specific limiters. diff --git a/kharma/main.cpp b/kharma/main.cpp index 1c862262..a9174192 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -184,9 +184,9 @@ int main(int argc, char *argv[]) std::cout << std::endl; // Print the number of meshblocks and ranks in use - std::cout << "Running with " << pmesh->block_list.size() << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; - // TODO could print entire distribution if it gets interesting - std::cout << "Blocks on rank " << MPIMyRank() << ": " << pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; + // TODO get this right + // std::cout << "Running with " << pmesh->block_list.size() << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; + // std::cout << "Blocks on rank " << MPIMyRank() << ": " << pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; // Write all parameters etc. to console if we should be especially wordy if ((verbose > 1) && MPIRank0()) { From f26e3c4c5b808735d61ceb0de09008eaf8d1021e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 16:35:52 -0500 Subject: [PATCH 192/219] Choose a challenging SMR problem --- pars/smr/sane3d_refined.par | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pars/smr/sane3d_refined.par b/pars/smr/sane3d_refined.par index cf0ce2ec..b6ea072d 100644 --- a/pars/smr/sane3d_refined.par +++ b/pars/smr/sane3d_refined.par @@ -7,27 +7,27 @@ problem_id = torus refinement = static numlevel = 2 -nx1 = 64 -nx2 = 64 -nx3 = 64 +nx1 = 256 +nx2 = 160 +nx3 = 128 -nx1 = 16 -nx2 = 16 -nx3 = 16 +nx1 = 128 +nx2 = 32 +nx3 = 128 x1min = 1.0 x1max = 2.0 -x2min = 1.50 -x2max = 1.60 +x2min = 0.49 +x2max = 0.51 x3min = 0.0 x3max = 6.28 level = 1 base = spherical_ks -transform = eks +transform = mks r_out = 20 a = 0.9375 @@ -41,7 +41,7 @@ extra_checks = 1 flag_verbose = 0 -cfl = 0.9 +cfl = 0.8 gamma = 1.666667 reconstruction = weno5 @@ -71,8 +71,8 @@ bsq_over_rho_max = 100 file_type = hdf5 dt = 0.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, divB -ghost_zones = true +variables = prims, divB +#ghost_zones = true # Can't until face field output is enabled # From b6a216bb457348ac73db17655473b42a6ef4596a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 16:36:53 -0500 Subject: [PATCH 193/219] Quiet some warnings on recent Clang/hipcc --- kharma/prob/resize_restart.cpp | 2 +- kharma/prob/utils/hdf5_utils.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kharma/prob/resize_restart.cpp b/kharma/prob/resize_restart.cpp index e4f4f636..6b3747f5 100644 --- a/kharma/prob/resize_restart.cpp +++ b/kharma/prob/resize_restart.cpp @@ -262,7 +262,7 @@ TaskStatus ReadIharmRestart(std::shared_ptr>& rc, ParameterI pin->GetInteger("parthenon/mesh", "nx2") != n2tot || pin->GetInteger("parthenon/mesh", "nx3") != n3tot) { printf("Mesh size does not match!\n"); - printf("[%d %d %d] vs [%lu %lu %lu]", + printf("[%d %d %d] vs [%llu %llu %llu]", pin->GetInteger("parthenon/mesh", "nx1"), pin->GetInteger("parthenon/mesh", "nx2"), pin->GetInteger("parthenon/mesh", "nx3"), diff --git a/kharma/prob/utils/hdf5_utils.cpp b/kharma/prob/utils/hdf5_utils.cpp index 6caaed7b..a183beb2 100644 --- a/kharma/prob/utils/hdf5_utils.cpp +++ b/kharma/prob/utils/hdf5_utils.cpp @@ -397,11 +397,11 @@ int hdf5_read_array(void *data, const char *name, size_t rank, if(DEBUG) { fprintf(stderr,"Reading arr %s:\n", path); - fprintf(stderr,"Total file size: %lu %lu %lu %lu\n", fdims[0], fdims[1], fdims[2], fdims[3]); - fprintf(stderr,"File start: %lu %lu %lu %lu\n", fstart[0], fstart[1], fstart[2], fstart[3]); - fprintf(stderr,"File read size: %lu %lu %lu %lu\n", fcount[0], fcount[1], fcount[2], fcount[3]); - fprintf(stderr,"Total memory size: %lu %lu %lu %lu\n", mdims[0], mdims[1], mdims[2], mdims[3]); - fprintf(stderr,"Memory start: %lu %lu %lu %lu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); + fprintf(stderr,"Total file size: %llu %llu %llu %llu\n", fdims[0], fdims[1], fdims[2], fdims[3]); + fprintf(stderr,"File start: %llu %llu %llu %llu\n", fstart[0], fstart[1], fstart[2], fstart[3]); + fprintf(stderr,"File read size: %llu %llu %llu %llu\n", fcount[0], fcount[1], fcount[2], fcount[3]); + fprintf(stderr,"Total memory size: %llu %llu %llu %llu\n", mdims[0], mdims[1], mdims[2], mdims[3]); + fprintf(stderr,"Memory start: %llu %llu %llu %llu\n\n", mstart[0], mstart[1], mstart[2], mstart[3]); } hid_t dset_id = H5Dopen(file_id, path, H5P_DEFAULT); From 1931c94161ce423b934540266bc3f47e7b24bda6 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 17:05:34 -0500 Subject: [PATCH 194/219] Bump parthenon again for MeshBlockData fix --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index 67cbb148..4fc504fc 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 67cbb1485400051ad94d2f96735e03de76308f07 +Subproject commit 4fc504fc4f43c57e1c35292cf852821b29679816 From e6f6a55da84b2d5b73c8084d937c250940480ce7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 7 Nov 2023 17:08:33 -0500 Subject: [PATCH 195/219] Pull parthenon speed improvement --- external/parthenon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/parthenon b/external/parthenon index 4fc504fc..8cf9c652 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 4fc504fc4f43c57e1c35292cf852821b29679816 +Subproject commit 8cf9c65211df2c904aa097811852951915bca630 From 33b6f3cd471703ac5f5a8fe9f8e2cf0ed1712169 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 9 Nov 2023 12:06:52 -0700 Subject: [PATCH 196/219] Port forward a fix for divB on reflecting boundaries --- kharma/flux/flux.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kharma/flux/flux.cpp b/kharma/flux/flux.cpp index 79c482d7..37d4f771 100644 --- a/kharma/flux/flux.cpp +++ b/kharma/flux/flux.cpp @@ -35,6 +35,7 @@ #include "flux.hpp" // Most includes are in the header TODO fix? +#include "b_ct.hpp" #include "grmhd.hpp" #include "kharma.hpp" @@ -134,6 +135,14 @@ TaskStatus Flux::BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coa const VarMap m_u(cons_map, true), m_p(prims_map, false); const int nvar = U.GetDim(4); + // Return if we're not syncing U & P at all (e.g. edges) + if (P.GetDim(4) == 0) return TaskStatus::complete; + + // Make sure we always update center conserved B from the faces, not the prims + if (pmb->packages.AllPackages().count("B_CT")) + B_CT::BlockUtoP(rc, domain, coarse); + + // Indices auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; const IndexRange ib = bounds.GetBoundsI(domain); const IndexRange jb = bounds.GetBoundsJ(domain); @@ -181,6 +190,11 @@ TaskStatus Flux::BlockPtoU_Send(MeshBlockData *rc, IndexDomain domain, boo // Return if we're not syncing U & P at all (e.g. edges) if (P.GetDim(4) == 0) return TaskStatus::complete; + // Make sure we always update center conserved B from the faces, not the prims + if (pmb->packages.AllPackages().count("B_CT")) + B_CT::BlockUtoP(rc, IndexDomain::interior, coarse); + + // Indices auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; IndexRange ib = bounds.GetBoundsI(domain); IndexRange jb = bounds.GetBoundsJ(domain); From 546594e59523bc8556c1b92fab62503257209827 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 9 Nov 2023 13:41:39 -0700 Subject: [PATCH 197/219] Be a little more careful with polar face EMFs --- kharma/b_ct/b_ct.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 741d2fab..b28bbb2c 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -324,6 +324,38 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) } else { throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, gs05_0, gs05_c!"); } + + // Explicitly zero polar faces + // In spherical, zero B2 on X2 face regardless of boundary condition + // This shouldn't interfere with divB since the face size is zero anyway + if (md->GetBlockData(0)->GetBlockPointer()->coords.coords.is_spherical()) { + const IndexRange ib = md->GetBoundsI(IndexDomain::entire); + const IndexRange kb = md->GetBoundsK(IndexDomain::entire); + const int js = md->GetBoundsJ(IndexDomain::interior).s; + const int je = md->GetBoundsJ(IndexDomain::interior).e + 1; // Face + for (int i_block = 0; i_block < md->NumBlocks(); i_block++) { + auto &rc = md->GetBlockData(i_block); + auto pmb = rc->GetBlockPointer(); + auto& emf_block = rc->PackVariables(std::vector{"B_CT.emf"}); + if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::inner_x2)) { + pmb->par_for("B_CT_zero_B2_in", kb.s, kb.e, js, js, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + emf_block(E1, 0, k, j, i) = 0; + emf_block(E3, 0, k, j, i) = 0; + } + ); + } + if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::outer_x2)) { + pmb->par_for("B_CT_zero_B2_out", kb.s, kb.e, je, je, ib.s, ib.e, + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { + emf_block(E1, 0, k, j, i) = 0; + emf_block(E3, 0, k, j, i) = 0; + } + ); + } + } + } + return TaskStatus::complete; } From ece0e538788ed7c9355bbd716c8ce5806a310604 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 13 Nov 2023 15:07:06 -0500 Subject: [PATCH 198/219] Expand some indices when checking for inflows, make GetRange usable for boundary ranges --- .gitignore | 8 ++-- kharma/boundaries/boundaries.cpp | 27 ++++++++++--- kharma/domain.hpp | 12 ++++-- kharma/floors/floors.cpp | 4 +- pars/benchmark/sane_perf_emhd.par | 67 +++++++++++++++++++++++++++++++ pars/tori_3d/sane.par | 4 +- 6 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 pars/benchmark/sane_perf_emhd.par diff --git a/.gitignore b/.gitignore index 2e87022a..0cf1683b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Memory dumps +core* +*.swp + # Various script results/logs out-*.txt *.json @@ -6,7 +10,6 @@ convergence.txt *.png *.mp4 *.webm -core.* frames_*/ logs/ *.log @@ -77,6 +80,3 @@ make_args # Python files __pycache__/ *.pyc - -# added by Hyerin -*.swp diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index a7afe62e..8d08c468 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -378,9 +378,20 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom // Inflow check // Iterate over zones w/p=0 - pmb->par_for_bndry( - "check_inflow", IndexRange{0, 0}, domain, CC, coarse, - KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + // pmb->par_for_bndry( + // "check_inflow", IndexRange{0, 0}, domain, CC, coarse, + // KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + // KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); + // } + // ); + const auto bface = BoundaryFace(domain); + const auto bname = BoundaryName(bface); + const bool binner = BoundaryIsInner(bface); + // One domain interior to boundary + auto b = KDomain::GetRange(rc, domain, -((int) !binner), binner, coarse); + pmb->par_for( + "zero_inflow_" + bname, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); @@ -427,6 +438,10 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) // These functions do *not* need an extra row outside the domain, // like B_FluxCT::FixBoundaryFlux does. const int ndim = pmesh->ndim; + // Entire range + const IndexRange ibe = pmb0->cellbounds.GetBoundsI(IndexDomain::entire); + const IndexRange jbe = pmb0->cellbounds.GetBoundsJ(IndexDomain::entire); + const IndexRange kbe = pmb0->cellbounds.GetBoundsK(IndexDomain::entire); // Ranges for sides const IndexRange ibs = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); const IndexRange jbs = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); @@ -448,7 +463,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (bdir > ndim) continue; // Set ranges based - IndexRange ib = ibs, jb = jbs, kb = kbs; + IndexRange ib = ibe, jb = jbe, kb = kbe; // Range for inner_x1 bounds is first face only, etc. if (bdir == 1) { ib.s = ib.e = (binner) ? ibf.s : ibf.e; @@ -468,14 +483,14 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (pmb->boundary_flag[bface] == BoundaryFlag::user) { if (binner) { pmb->par_for( - "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { F.flux(bdir, m_rho, k, j, i) = m::min(F.flux(bdir, m_rho, k, j, i), 0.); } ); } else { pmb->par_for( - "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { F.flux(bdir, m_rho, k, j, i) = m::max(F.flux(bdir, m_rho, k, j, i), 0.); } diff --git a/kharma/domain.hpp b/kharma/domain.hpp index c100b3a2..b3cc6ee3 100644 --- a/kharma/domain.hpp +++ b/kharma/domain.hpp @@ -110,14 +110,18 @@ inline IndexRange3 GetRange(T data, IndexDomain domain, int left_halo=0, int rig const IndexRange jb = cellbounds.GetBoundsJ(domain); const IndexRange kb = cellbounds.GetBoundsK(domain); // Compute sizes with specified halo zones included in non-trivial dimensions + // TODO notion of activated x1+x3 with nx2==0? const int& ndim = GetNDim(data); - // If ghost & not x1 direction const IndexRange il = IndexRange{ib.s + left_halo, ib.e + right_halo}; const IndexRange jl = (ndim > 1) ? IndexRange{jb.s + left_halo, jb.e + right_halo} : jb; const IndexRange kl = (ndim > 2) ? IndexRange{kb.s + left_halo, kb.e + right_halo} : kb; - return IndexRange3{(uint) il.s, (uint) il.e, - (uint) jl.s, (uint) jl.e, - (uint) kl.s, (uint) kl.e}; + // Bounds of entire domain, we never mean to go beyond these + const IndexRange ibe = cellbounds.GetBoundsI(IndexDomain::entire); + const IndexRange jbe = cellbounds.GetBoundsJ(IndexDomain::entire); + const IndexRange kbe = cellbounds.GetBoundsK(IndexDomain::entire); + return IndexRange3{(uint) m::max(il.s, ibe.s), (uint) m::min(il.e, ibe.e), + (uint) m::max(jl.s, jbe.s), (uint) m::min(jl.e, jbe.e), + (uint) m::max(kl.s, kbe.s), (uint) m::min(kl.e, kbe.e)}; } template inline IndexRange3 GetRange(T data, IndexDomain domain, bool coarse) diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index e1a7ee76..e92aff7d 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -166,8 +166,8 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::Cell}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved, Metadata::Cell}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/pars/benchmark/sane_perf_emhd.par b/pars/benchmark/sane_perf_emhd.par new file mode 100644 index 00000000..94f06a86 --- /dev/null +++ b/pars/benchmark/sane_perf_emhd.par @@ -0,0 +1,67 @@ +# SANE model emulating a real run for performance testing +# Takes only 1k steps, dramatically reduced dump files +# Uses HARM driver harm_driver.cpp + +# (Also no archival parfile, B cleanup, or two-sync) + + +problem_id = torus + +# 8 meshblocks -> up to 2 nodes. +# Pretty representative size for a long simulation +# Larger simulations have smaller in-simulation timesteps + +refinement = none +numlevel = 1 +nx1 = 256 +nx2 = 128 +nx3 = 256 + + +nx1 = 128 +nx2 = 32 +nx3 = 128 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 + + +tlim = 10000.0 +# Limit to 1k steps +nlim = 1000 + + +cfl = 0.8 +gamma = 1.666667 +reconstruction = weno5 + + + + +type = kharma +two_sync = true + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +type = sane +beta_min = 100. + + +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 diff --git a/pars/tori_3d/sane.par b/pars/tori_3d/sane.par index 08d94b4e..2d2656ab 100644 --- a/pars/tori_3d/sane.par +++ b/pars/tori_3d/sane.par @@ -20,7 +20,7 @@ nx3 = 32 base = spherical_ks transform = fmks -r_out = 1000 +r_out = 100 a = 0.9375 hslope = 0.3 mks_smooth = 0.5 @@ -71,7 +71,7 @@ Tp = 10 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, divB +variables = prims, jcon, divB file_type = rst From cd3d6c3e1802a73960f3cee44ddb5fa899722d66 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 13 Nov 2023 15:07:06 -0500 Subject: [PATCH 199/219] Expand some indices when checking for inflows, make GetRange usable for boundary ranges --- .gitignore | 8 ++-- kharma/boundaries/boundaries.cpp | 27 ++++++++++--- kharma/domain.hpp | 12 ++++-- kharma/floors/floors.cpp | 4 +- pars/benchmark/sane_perf_emhd.par | 67 +++++++++++++++++++++++++++++++ pars/tori_3d/sane.par | 4 +- 6 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 pars/benchmark/sane_perf_emhd.par diff --git a/.gitignore b/.gitignore index 2e87022a..0cf1683b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Memory dumps +core* +*.swp + # Various script results/logs out-*.txt *.json @@ -6,7 +10,6 @@ convergence.txt *.png *.mp4 *.webm -core.* frames_*/ logs/ *.log @@ -77,6 +80,3 @@ make_args # Python files __pycache__/ *.pyc - -# added by Hyerin -*.swp diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index a7afe62e..8d08c468 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -378,9 +378,20 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom // Inflow check // Iterate over zones w/p=0 - pmb->par_for_bndry( - "check_inflow", IndexRange{0, 0}, domain, CC, coarse, - KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + // pmb->par_for_bndry( + // "check_inflow", IndexRange{0, 0}, domain, CC, coarse, + // KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { + // KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); + // } + // ); + const auto bface = BoundaryFace(domain); + const auto bname = BoundaryName(bface); + const bool binner = BoundaryIsInner(bface); + // One domain interior to boundary + auto b = KDomain::GetRange(rc, domain, -((int) !binner), binner, coarse); + pmb->par_for( + "zero_inflow_" + bname, b.ks, b.ke, b.js, b.je, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); @@ -427,6 +438,10 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) // These functions do *not* need an extra row outside the domain, // like B_FluxCT::FixBoundaryFlux does. const int ndim = pmesh->ndim; + // Entire range + const IndexRange ibe = pmb0->cellbounds.GetBoundsI(IndexDomain::entire); + const IndexRange jbe = pmb0->cellbounds.GetBoundsJ(IndexDomain::entire); + const IndexRange kbe = pmb0->cellbounds.GetBoundsK(IndexDomain::entire); // Ranges for sides const IndexRange ibs = pmb0->cellbounds.GetBoundsI(IndexDomain::interior); const IndexRange jbs = pmb0->cellbounds.GetBoundsJ(IndexDomain::interior); @@ -448,7 +463,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (bdir > ndim) continue; // Set ranges based - IndexRange ib = ibs, jb = jbs, kb = kbs; + IndexRange ib = ibe, jb = jbe, kb = kbe; // Range for inner_x1 bounds is first face only, etc. if (bdir == 1) { ib.s = ib.e = (binner) ? ibf.s : ibf.e; @@ -468,14 +483,14 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) if (pmb->boundary_flag[bface] == BoundaryFlag::user) { if (binner) { pmb->par_for( - "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { F.flux(bdir, m_rho, k, j, i) = m::min(F.flux(bdir, m_rho, k, j, i), 0.); } ); } else { pmb->par_for( - "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.s, + "zero_inflow_flux_" + bname, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { F.flux(bdir, m_rho, k, j, i) = m::max(F.flux(bdir, m_rho, k, j, i), 0.); } diff --git a/kharma/domain.hpp b/kharma/domain.hpp index c100b3a2..b3cc6ee3 100644 --- a/kharma/domain.hpp +++ b/kharma/domain.hpp @@ -110,14 +110,18 @@ inline IndexRange3 GetRange(T data, IndexDomain domain, int left_halo=0, int rig const IndexRange jb = cellbounds.GetBoundsJ(domain); const IndexRange kb = cellbounds.GetBoundsK(domain); // Compute sizes with specified halo zones included in non-trivial dimensions + // TODO notion of activated x1+x3 with nx2==0? const int& ndim = GetNDim(data); - // If ghost & not x1 direction const IndexRange il = IndexRange{ib.s + left_halo, ib.e + right_halo}; const IndexRange jl = (ndim > 1) ? IndexRange{jb.s + left_halo, jb.e + right_halo} : jb; const IndexRange kl = (ndim > 2) ? IndexRange{kb.s + left_halo, kb.e + right_halo} : kb; - return IndexRange3{(uint) il.s, (uint) il.e, - (uint) jl.s, (uint) jl.e, - (uint) kl.s, (uint) kl.e}; + // Bounds of entire domain, we never mean to go beyond these + const IndexRange ibe = cellbounds.GetBoundsI(IndexDomain::entire); + const IndexRange jbe = cellbounds.GetBoundsJ(IndexDomain::entire); + const IndexRange kbe = cellbounds.GetBoundsK(IndexDomain::entire); + return IndexRange3{(uint) m::max(il.s, ibe.s), (uint) m::min(il.e, ibe.e), + (uint) m::max(jl.s, jbe.s), (uint) m::min(jl.e, jbe.e), + (uint) m::max(kl.s, kbe.s), (uint) m::min(kl.e, kbe.e)}; } template inline IndexRange3 GetRange(T data, IndexDomain domain, bool coarse) diff --git a/kharma/floors/floors.cpp b/kharma/floors/floors.cpp index e1a7ee76..e92aff7d 100644 --- a/kharma/floors/floors.cpp +++ b/kharma/floors/floors.cpp @@ -166,8 +166,8 @@ TaskStatus Floors::ApplyInitialFloors(ParameterInput *pin, MeshBlockData * auto pmb = mbd->GetBlockPointer(); PackIndexMap prims_map, cons_map; - auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive")}, prims_map); - auto U = mbd->PackVariables(std::vector{Metadata::Conserved}, cons_map); + auto P = mbd->PackVariables({Metadata::GetUserFlag("Primitive"), Metadata::Cell}, prims_map); + auto U = mbd->PackVariables(std::vector{Metadata::Conserved, Metadata::Cell}, cons_map); const VarMap m_u(cons_map, true), m_p(prims_map, false); const auto& G = pmb->coords; diff --git a/pars/benchmark/sane_perf_emhd.par b/pars/benchmark/sane_perf_emhd.par new file mode 100644 index 00000000..94f06a86 --- /dev/null +++ b/pars/benchmark/sane_perf_emhd.par @@ -0,0 +1,67 @@ +# SANE model emulating a real run for performance testing +# Takes only 1k steps, dramatically reduced dump files +# Uses HARM driver harm_driver.cpp + +# (Also no archival parfile, B cleanup, or two-sync) + + +problem_id = torus + +# 8 meshblocks -> up to 2 nodes. +# Pretty representative size for a long simulation +# Larger simulations have smaller in-simulation timesteps + +refinement = none +numlevel = 1 +nx1 = 256 +nx2 = 128 +nx3 = 256 + + +nx1 = 128 +nx2 = 32 +nx3 = 128 + + +base = spherical_ks +transform = fmks +r_out = 1000 +a = 0.9375 + + +tlim = 10000.0 +# Limit to 1k steps +nlim = 1000 + + +cfl = 0.8 +gamma = 1.666667 +reconstruction = weno5 + + + + +type = kharma +two_sync = true + + +rin = 6.0 +rmax = 12.0 + + +u_jitter = 0.04 + + +type = sane +beta_min = 100. + + +rho_min_geom = 1e-6 +u_min_geom = 1e-8 +bsq_over_rho_max = 100 +u_over_rho_max = 2 + + +verbose = 1 +extra_checks = 1 +flag_verbose = 0 diff --git a/pars/tori_3d/sane.par b/pars/tori_3d/sane.par index 08d94b4e..2d2656ab 100644 --- a/pars/tori_3d/sane.par +++ b/pars/tori_3d/sane.par @@ -20,7 +20,7 @@ nx3 = 32 base = spherical_ks transform = fmks -r_out = 1000 +r_out = 100 a = 0.9375 hslope = 0.3 mks_smooth = 0.5 @@ -71,7 +71,7 @@ Tp = 10 file_type = hdf5 dt = 5.0 single_precision_output = true -variables = prims.rho, prims.u, prims.uvec, prims.B, jcon, divB +variables = prims, jcon, divB file_type = rst From b07d8ff36123b71eebdfac1ba8fb318b94d527b9 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 15 Nov 2023 15:19:08 -0700 Subject: [PATCH 200/219] Bump Parthenon version to add face-CT restarts --- external/parthenon | 2 +- external/patches/parthenon-use-gr-coordinates.patch | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/external/parthenon b/external/parthenon index 8cf9c652..3b719187 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 8cf9c65211df2c904aa097811852951915bca630 +Subproject commit 3b71918710c6d8210e1a46a899b73e0602bfde61 diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index fbb4bb3b..efcd3879 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 45566b0b..a9abdc1c 100644 +index aaeabd8a..e354ef3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,13 +11,13 @@ index 45566b0b..a9abdc1c 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -309,6 +309,8 @@ lint_target(parthenon) +@@ -331,6 +331,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ + $ + $ - $ + $ ) diff --git a/src/coordinates/coordinates.hpp b/src/coordinates/coordinates.hpp @@ -32,4 +32,3 @@ index d1290dee..50bfc840 100644 namespace parthenon { - From 1f65b7e56ec33d202012bc987f7d6a75aac0b295 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 15 Nov 2023 15:19:08 -0700 Subject: [PATCH 201/219] Bump Parthenon version to add face-CT restarts --- external/parthenon | 2 +- external/patches/parthenon-use-gr-coordinates.patch | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/external/parthenon b/external/parthenon index 67cbb148..3b719187 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 67cbb1485400051ad94d2f96735e03de76308f07 +Subproject commit 3b71918710c6d8210e1a46a899b73e0602bfde61 diff --git a/external/patches/parthenon-use-gr-coordinates.patch b/external/patches/parthenon-use-gr-coordinates.patch index fbb4bb3b..efcd3879 100644 --- a/external/patches/parthenon-use-gr-coordinates.patch +++ b/external/patches/parthenon-use-gr-coordinates.patch @@ -1,5 +1,5 @@ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 45566b0b..a9abdc1c 100644 +index aaeabd8a..e354ef3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -90,7 +90,7 @@ set(COMPILED_WITH ${CMAKE_CXX_COMPILER}) @@ -11,13 +11,13 @@ index 45566b0b..a9abdc1c 100644 configure_file(config.hpp.in generated/config.hpp @ONLY) -@@ -309,6 +309,8 @@ lint_target(parthenon) +@@ -331,6 +331,8 @@ lint_target(parthenon) target_include_directories(parthenon PUBLIC $ $ + $ + $ - $ + $ ) diff --git a/src/coordinates/coordinates.hpp b/src/coordinates/coordinates.hpp @@ -32,4 +32,3 @@ index d1290dee..50bfc840 100644 namespace parthenon { - From 3c537a2bfd1a2bddf10122d353696b32827075f5 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 16 Nov 2023 14:43:37 -0700 Subject: [PATCH 202/219] Boundary updates 1. Zero EMFs on boundaries in boundaries.cpp. Supercedes B and EMF fixes in b_ct.cpp 2. Remove check_inflow_flux_X because they were confusing, and should always match check_inflow_X --- kharma/b_ct/b_ct.cpp | 60 -------------------------------- kharma/boundaries/boundaries.cpp | 56 +++++++++-------------------- kharma/boundaries/boundaries.hpp | 5 --- 3 files changed, 17 insertions(+), 104 deletions(-) diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index b28bbb2c..6bcf003c 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -325,37 +325,6 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) throw std::invalid_argument("Invalid CT scheme specified! Must be one of bs99, gs05_0, gs05_c!"); } - // Explicitly zero polar faces - // In spherical, zero B2 on X2 face regardless of boundary condition - // This shouldn't interfere with divB since the face size is zero anyway - if (md->GetBlockData(0)->GetBlockPointer()->coords.coords.is_spherical()) { - const IndexRange ib = md->GetBoundsI(IndexDomain::entire); - const IndexRange kb = md->GetBoundsK(IndexDomain::entire); - const int js = md->GetBoundsJ(IndexDomain::interior).s; - const int je = md->GetBoundsJ(IndexDomain::interior).e + 1; // Face - for (int i_block = 0; i_block < md->NumBlocks(); i_block++) { - auto &rc = md->GetBlockData(i_block); - auto pmb = rc->GetBlockPointer(); - auto& emf_block = rc->PackVariables(std::vector{"B_CT.emf"}); - if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::inner_x2)) { - pmb->par_for("B_CT_zero_B2_in", kb.s, kb.e, js, js, ib.s, ib.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - emf_block(E1, 0, k, j, i) = 0; - emf_block(E3, 0, k, j, i) = 0; - } - ); - } - if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::outer_x2)) { - pmb->par_for("B_CT_zero_B2_out", kb.s, kb.e, je, je, ib.s, ib.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - emf_block(E1, 0, k, j, i) = 0; - emf_block(E3, 0, k, j, i) = 0; - } - ); - } - } - } - return TaskStatus::complete; } @@ -409,35 +378,6 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) } ); - // Explicitly zero polar faces - // In spherical, zero B2 on X2 face regardless of boundary condition - // This shouldn't interfere with divB since the face size is zero anyway - if (mdudt->GetBlockData(0)->GetBlockPointer()->coords.coords.is_spherical()) { - const IndexRange ib = mdudt->GetBoundsI(IndexDomain::entire); - const IndexRange kb = mdudt->GetBoundsK(IndexDomain::entire); - const int js = mdudt->GetBoundsJ(IndexDomain::interior).s; - const int je = mdudt->GetBoundsJ(IndexDomain::interior).e + 1; // Face - for (int i_block = 0; i_block < mdudt->NumBlocks(); i_block++) { - auto &rc = mdudt->GetBlockData(i_block); - auto pmb = rc->GetBlockPointer(); - auto& dB_Uf_dt_block = rc->PackVariables(std::vector{"cons.fB"}); - if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::inner_x2)) { - pmb->par_for("B_CT_zero_B2_in", kb.s, kb.e, js, js, ib.s, ib.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - dB_Uf_dt_block(F2, 0, k, j, i) = 0; - } - ); - } - if (KBoundaries::IsPhysicalBoundary(pmb, BoundaryFace::outer_x2)) { - pmb->par_for("B_CT_zero_B2_out", kb.s, kb.e, je, je, ib.s, ib.e, - KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { - dB_Uf_dt_block(F2, 0, k, j, i) = 0; - } - ); - } - } - } - return TaskStatus::complete; } diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 8d08c468..2a736d7d 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -129,8 +129,6 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: // This is two separate checks, but default to enabling/disabling together for X1 and not elsewhere bool check_inflow = pin->GetOrAddBoolean("boundaries", "check_inflow_" + bname, check_inflow_global && bdir == X1DIR); params.Add("check_inflow_" + bname, check_inflow); - bool check_inflow_flux = pin->GetOrAddBoolean("boundaries", "check_inflow_flux_" + bname, check_inflow); - params.Add("check_inflow_flux_" + bname, check_inflow_flux); // Ensure fluxes through the zero-size face at the pole are zero bool zero_flux = pin->GetOrAddBoolean("boundaries", "zero_flux_" + bname, zero_polar_flux && bdir == X2DIR); @@ -260,6 +258,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD const auto bname = BoundaryName(bface); const auto btype_name = params.Get(bname); const auto bdir = BoundaryDirection(bface); + const bool binner = BoundaryIsInner(bface); Flag("Apply "+bname+" boundary: "+btype_name); pkg->KBoundaries[bface](rc, coarse); @@ -272,6 +271,21 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // this generally guards against anytime we can't do the below PackIndexMap prims_map; if (GRMHD::PackMHDPrims(rc.get(), prims_map).GetDim(4) == 0) { + // If we're syncing EMFs and in spherical, explicitly zero polar faces + // TODO allow any other face? + auto& emf_block = rc->PackVariables(std::vector{"B_CT.emf"}); + if (bdir == X2DIR && pmb->coords.coords.is_spherical() && emf_block.GetDim(4) > 0) { + for (TE el : {TE::E1, TE::E3}) { //TE::E2, + int off = (binner) ? 1 : -1; + pmb->par_for_bndry( + "zero_EMF", IndexRange{0,0}, domain, el, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + emf_block(el, v, k, j + off, i) = 0; + } + ); + } + } + EndFlag(); return; } @@ -376,14 +390,6 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); const VarMap m_p(prims_map, false); - // Inflow check - // Iterate over zones w/p=0 - // pmb->par_for_bndry( - // "check_inflow", IndexRange{0, 0}, domain, CC, coarse, - // KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { - // KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); - // } - // ); const auto bface = BoundaryFace(domain); const auto bname = BoundaryName(bface); const bool binner = BoundaryIsInner(bface); @@ -397,34 +403,6 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom ); } -void KBoundaries::CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse) -{ - Flag("CorrectBPrimitive"); - std::shared_ptr pmb = rc->GetBlockPointer(); - auto B_P = rc->PackVariables(std::vector{"prims.B"}); - // Return if no field to correct - if (B_P.GetDim(4) == 0) return; - - const auto& G = pmb->coords; - - const auto &bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; - const int dir = BoundaryDirection(domain); - const auto &range = (dir == 1) ? bounds.GetBoundsI(IndexDomain::interior) - : (dir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) - : bounds.GetBoundsK(IndexDomain::interior)); - const int ref = BoundaryIsInner(domain) ? range.s : range.e; - - pmb->par_for_bndry( - "Correct_B_P", IndexRange{0,NVEC-1}, domain, CC, coarse, - KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { - B_P(v, k, j, i) *= G.gdet(Loci::center, (dir == 2) ? ref : j, (dir == 1) ? ref : i) - / G.gdet(Loci::center, j, i); - } - ); - - EndFlag(); -} - TaskStatus KBoundaries::FixFlux(MeshData *md) { auto pmesh = md->GetMeshPointer(); @@ -477,7 +455,7 @@ TaskStatus KBoundaries::FixFlux(MeshData *md) auto &F = rc->PackVariablesAndFluxes({Metadata::WithFluxes}, cons_map); // If we should check inflow on this face... - if (params.Get("check_inflow_flux_" + bname)) { + if (params.Get("check_inflow_" + bname)) { const int m_rho = cons_map["cons.rho"].first; // ...and if this face of the block corresponds to a global boundary... if (pmb->boundary_flag[bface] == BoundaryFlag::user) { diff --git a/kharma/boundaries/boundaries.hpp b/kharma/boundaries/boundaries.hpp index dde70a60..fb910995 100644 --- a/kharma/boundaries/boundaries.hpp +++ b/kharma/boundaries/boundaries.hpp @@ -84,11 +84,6 @@ TaskStatus FixFlux(MeshData *rc); */ void CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse); -/** - * Correct for geometry when applying primitive B field boundaries - */ -void CorrectBPrimitive(std::shared_ptr>& rc, IndexDomain domain, bool coarse); - /** * Check for velocity toward the simulation domain in a zone, and eliminate it. */ From abb6d907eb200304ce5da621927eb0af4ed43906 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 17 Nov 2023 14:51:26 -0700 Subject: [PATCH 203/219] Fix an issue stemming from zeroing EMFs without faces as well --- kharma/boundaries/boundaries.cpp | 55 +++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 2a736d7d..5923c91a 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -264,6 +264,46 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD pkg->KBoundaries[bface](rc, coarse); EndFlag(); + // If we're syncing EMFs and in spherical, explicitly zero polar faces + // TODO allow any other face? + auto& emfpack = rc->PackVariables(std::vector{"B_CT.emf"}); + if (bdir == X2DIR && pmb->coords.coords.is_spherical() && emfpack.GetDim(4) > 0) { + Flag("BoundaryEdge_"+bname); + for (TE el : {TE::E1, TE::E3}) { + int off = (binner) ? 1 : -1; + pmb->par_for_bndry( + "zero_EMF", IndexRange{0,0}, domain, el, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + emfpack(el, v, k, j + off, i) = 0; + } + ); + } + EndFlag(); + } + + // Zero/invert X2 faces at polar X2 boundary + auto fpack = rc->PackVariables({Metadata::Face, Metadata::FillGhost}); + if (bdir == X2DIR && pmb->coords.coords.is_spherical() && fpack.GetDim(4) > 0) { + Flag("BoundaryFace_"+bname); + // Zero face fluxes + auto b = KDomain::GetRange(rc, domain, coarse); + // "domain" is the boundary here + auto jf = (binner) ? b.je + 1 : b.js; + pmb->par_for( + "zero_polar_" + bname, b.ks, b.ke, jf, jf, b.is, b.ie, + KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + fpack(F2, 0, k, j, i) = 0.; + } + ); + pmb->par_for_bndry( + "invert_F2_" + bname, IndexRange{0, fpack.GetDim(4)-1}, domain, F2, coarse, + KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { + fpack(F2, 0, k, j, i) *= -1; + } + ); + EndFlag(); + } + // This will now be called in 2 places we might not expect, // where we still may want to control the physical bounds: // 1. Syncing only the EMF during runs with CT @@ -271,21 +311,6 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // this generally guards against anytime we can't do the below PackIndexMap prims_map; if (GRMHD::PackMHDPrims(rc.get(), prims_map).GetDim(4) == 0) { - // If we're syncing EMFs and in spherical, explicitly zero polar faces - // TODO allow any other face? - auto& emf_block = rc->PackVariables(std::vector{"B_CT.emf"}); - if (bdir == X2DIR && pmb->coords.coords.is_spherical() && emf_block.GetDim(4) > 0) { - for (TE el : {TE::E1, TE::E3}) { //TE::E2, - int off = (binner) ? 1 : -1; - pmb->par_for_bndry( - "zero_EMF", IndexRange{0,0}, domain, el, coarse, - KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { - emf_block(el, v, k, j + off, i) = 0; - } - ); - } - } - EndFlag(); return; } From 30c53a2bccb3854e1d5deecb83e33b3571fcca0c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 20 Nov 2023 18:35:48 -0700 Subject: [PATCH 204/219] Fix an embarrassing bug for non-multizone production/speherical problems with Flux-CT --- kharma/b_flux_ct/b_flux_ct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 11f14e3a..5a8a12d2 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -426,7 +426,7 @@ void FixBoundaryFlux(MeshData *md, IndexDomain domain, bool coarse) ); } - if (domain == IndexDomain::outer_x2 && + if (domain == IndexDomain::outer_x1 && pmb->boundary_flag[BoundaryFace::outer_x1] == BoundaryFlag::user) { pmb->par_for("fix_flux_b_out", kbs.s, kbs.e, jbs.s, jbs.e, ibf.e, ibf.e, // Hyerin (12/28/22) for 1st & 2nd prescription From 506ba036ba50f6ed8dc9202ebced8991d0da985b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 20 Nov 2023 18:54:30 -0700 Subject: [PATCH 205/219] Revert a change which corrected outward velocities on the domain, as it's unnecessary and not precedented --- kharma/boundaries/boundaries.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 5923c91a..cd75ee9f 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -415,14 +415,11 @@ void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDom auto P = GRMHD::PackMHDPrims(rc.get(), prims_map, coarse); const VarMap m_p(prims_map, false); - const auto bface = BoundaryFace(domain); - const auto bname = BoundaryName(bface); - const bool binner = BoundaryIsInner(bface); - // One domain interior to boundary - auto b = KDomain::GetRange(rc, domain, -((int) !binner), binner, coarse); - pmb->par_for( - "zero_inflow_" + bname, b.ks, b.ke, b.js, b.je, b.is, b.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + // Inflow check + // Iterate over all boundary domain zones w/p=0 + pmb->par_for_bndry( + "check_inflow", IndexRange{0, 0}, domain, CC, coarse, + KOKKOS_LAMBDA(const int &p, const int &k, const int &j, const int &i) { KBoundaries::check_inflow(G, P, domain, m_p.U1, k, j, i); } ); From 2a3827f2826f5e1f31c47b68638caa4106467ace Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 22 Nov 2023 16:27:17 -0700 Subject: [PATCH 206/219] Update imex driver and boundaries to keep divB under SMR --- kharma/boundaries/boundaries.cpp | 19 ++++++++++++------- kharma/driver/imex_step.cpp | 9 +++++---- kharma/driver/kharma_step.cpp | 4 ++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index cd75ee9f..0edd8fbb 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -265,9 +265,12 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD EndFlag(); // If we're syncing EMFs and in spherical, explicitly zero polar faces - // TODO allow any other face? + // Since we manipulate the j coord, we'd overstep coarse bufs auto& emfpack = rc->PackVariables(std::vector{"B_CT.emf"}); - if (bdir == X2DIR && pmb->coords.coords.is_spherical() && emfpack.GetDim(4) > 0) { + if (bdir == X2DIR && + pmb->coords.coords.is_spherical() && + emfpack.GetDim(4) > 0 && + !coarse) { Flag("BoundaryEdge_"+bname); for (TE el : {TE::E1, TE::E3}) { int off = (binner) ? 1 : -1; @@ -283,7 +286,9 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // Zero/invert X2 faces at polar X2 boundary auto fpack = rc->PackVariables({Metadata::Face, Metadata::FillGhost}); - if (bdir == X2DIR && pmb->coords.coords.is_spherical() && fpack.GetDim(4) > 0) { + if (bdir == X2DIR && + pmb->coords.coords.is_spherical() && + fpack.GetDim(4) > 0) { Flag("BoundaryFace_"+bname); // Zero face fluxes auto b = KDomain::GetRange(rc, domain, coarse); @@ -298,7 +303,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD pmb->par_for_bndry( "invert_F2_" + bname, IndexRange{0, fpack.GetDim(4)-1}, domain, F2, coarse, KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { - fpack(F2, 0, k, j, i) *= -1; + fpack(F2, v, k, j, i) *= -1; } ); EndFlag(); @@ -333,7 +338,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD const auto &range = (bdir == 1) ? bounds.GetBoundsI(IndexDomain::interior) : (bdir == 2 ? bounds.GetBoundsJ(IndexDomain::interior) : bounds.GetBoundsK(IndexDomain::interior)); - const int ref = BoundaryIsInner(domain) ? range.s : range.e; + const int ref = binner ? range.s : range.e; pmb->par_for_bndry( "outflow_EMHD", IndexRange{0,EMHDg.GetDim(4)-1}, domain, CC, coarse, KOKKOS_LAMBDA (const int &v, const int &k, const int &j, const int &i) { @@ -384,9 +389,9 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD // TODO there should be a set of B field wrappers that dispatch this auto pkgs = pmb->packages.AllPackages(); if (pkgs.count("B_FluxCT")) { - B_FluxCT::BlockUtoP(rc.get(), IndexDomain::entire); + B_FluxCT::BlockUtoP(rc.get(), domain, coarse); } else if (pkgs.count("B_CT")) { - B_CT::BlockUtoP(rc.get(), IndexDomain::entire); + B_CT::BlockUtoP(rc.get(), domain, coarse); } Flux::BlockPtoU(rc.get(), domain, coarse); } else { diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 0b179327..0ec5815c 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -153,15 +153,16 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta // If we're in AMR, correct fluxes from neighbors auto t_flux_bounds = t_fix_flux; if (pmesh->multilevel || use_b_ct) { - auto t_load_send_flux = tl.AddTask(t_fix_flux, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); - auto t_recv_flux = tl.AddTask(t_load_send_flux, parthenon::ReceiveFluxCorrections, md_sub_step_init); - t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); + auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_flux_bounds, B_CT::CalculateEMF, md_sub_step_init.get()); - t_flux_bounds = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_sub_step_init); + t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } + auto t_load_send_flux = tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); + auto t_recv_flux = tl.AddTask(t_load_send_flux, parthenon::ReceiveFluxCorrections, md_sub_step_init); + t_flux_bounds = tl.AddTask(t_recv_flux, parthenon::SetFluxCorrections, md_sub_step_init); } // Apply the fluxes to calculate a change in cell-centered values "md_flux_src" diff --git a/kharma/driver/kharma_step.cpp b/kharma/driver/kharma_step.cpp index 58c1dea2..f59e7976 100644 --- a/kharma/driver/kharma_step.cpp +++ b/kharma/driver/kharma_step.cpp @@ -165,9 +165,9 @@ TaskCollection KHARMADriver::MakeDefaultTaskCollection(BlockList_t &blocks, int auto t_emf = t_flux_bounds; if (use_b_ct) { // Pull out a container of only EMF to synchronize - auto &md_b_ct = pmesh->mesh_data.AddShallow("B_CT", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition + auto &md_emf_only = pmesh->mesh_data.AddShallow("EMF", std::vector{"B_CT.emf"}); // TODO this gets weird if we partition auto t_emf_local = tl.AddTask(t_flux_bounds, B_CT::CalculateEMF, md_sub_step_init.get()); - t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_b_ct); + t_emf = KHARMADriver::AddBoundarySync(t_emf_local, tl, md_emf_only); } auto t_load_send_flux = tl.AddTask(t_emf, parthenon::LoadAndSendFluxCorrections, md_sub_step_init); auto t_recv_flux = tl.AddTask(t_load_send_flux, parthenon::ReceiveFluxCorrections, md_sub_step_init); From 2c412995a12f1574e07438b1b59024c06854e583 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 22 Nov 2023 18:38:41 -0500 Subject: [PATCH 207/219] Fix printing total/local meshblocks on start --- kharma/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kharma/main.cpp b/kharma/main.cpp index a9174192..f825dcfe 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -185,8 +185,8 @@ int main(int argc, char *argv[]) // Print the number of meshblocks and ranks in use // TODO get this right - // std::cout << "Running with " << pmesh->block_list.size() << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; - // std::cout << "Blocks on rank " << MPIMyRank() << ": " << pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; + std::cout << "Running with " << pmesh->nbtotal << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; + std::cout << "Blocks on rank " << MPIRank() << ": " << pmesh->block_list.size() << "\n" << std::endl; //pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; // Write all parameters etc. to console if we should be especially wordy if ((verbose > 1) && MPIRank0()) { From 7111a79c6a5b1a058207fbb46ef2a5ac077fe4a9 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 22 Nov 2023 19:15:21 -0500 Subject: [PATCH 208/219] Supporting fn for previous fix --- kharma/decs.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kharma/decs.hpp b/kharma/decs.hpp index 27bd5641..edaa296f 100644 --- a/kharma/decs.hpp +++ b/kharma/decs.hpp @@ -149,10 +149,14 @@ inline int MPINumRanks() { return parthenon::Globals::nranks; } -inline int MPIMyRank() +inline int MPIRank() { return parthenon::Globals::my_rank; } +inline int MPIBarrier() +{ + return MPI_Barrier(MPI_COMM_WORLD); +} // A few generic "NDArray" overloads for readability. // TODO torn on futures of these: they're explicitly per-block From 10ae54dc688cb42990c7d987c55d1c050c68dc1c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 28 Nov 2023 16:55:00 -0500 Subject: [PATCH 209/219] Update to Parthenon that supports 2D face-field restarts, compat changes, Frontier fixes --- external/parthenon | 2 +- kharma/b_ct/b_ct.cpp | 14 +++++----- kharma/b_flux_ct/b_flux_ct.cpp | 4 +-- kharma/boundaries/boundaries.cpp | 2 +- kharma/boundaries/boundary_types.hpp | 3 ++- kharma/boundaries/dirichlet.cpp | 6 ++--- kharma/main.cpp | 39 ++++++++++++++++++---------- machines/frontier.sh | 1 + scripts/batch/frontier.sb | 23 ++++++++++++++++ 9 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 scripts/batch/frontier.sb diff --git a/external/parthenon b/external/parthenon index 3b719187..14d41123 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 3b71918710c6d8210e1a46a899b73e0602bfde61 +Subproject commit 14d411239eea0476e0ea1b8f099752a8eab1758a diff --git a/kharma/b_ct/b_ct.cpp b/kharma/b_ct/b_ct.cpp index 6bcf003c..87135db9 100644 --- a/kharma/b_ct/b_ct.cpp +++ b/kharma/b_ct/b_ct.cpp @@ -85,8 +85,8 @@ std::shared_ptr B_CT::Initialize(ParameterInput *pin, std::shared // We don't mark these as "Conserved" else they'd be bundled // with all the cell vars in a bunch of places we don't want // Also note we *always* sync B field conserved var - std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, - Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; // TODO TODO Restart + std::vector flags_cons_f = {Metadata::Real, Metadata::Face, Metadata::Independent, Metadata::Restart, + Metadata::GetUserFlag("Explicit"), Metadata::FillGhost}; auto m = Metadata(flags_cons_f); if (!lazy_prolongation) m.RegisterRefinementOps(); @@ -212,7 +212,7 @@ TaskStatus B_CT::CalculateEMF(MeshData *md) const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // Calculate circulation by averaging fluxes // This is the base of most other schemes, which make corrections @@ -341,7 +341,7 @@ TaskStatus B_CT::AddSource(MeshData *md, MeshData *mdudt) const IndexRange3 b1 = KDomain::GetRange(md, IndexDomain::interior, 0, 1); const IndexRange block = IndexRange{0, emf_pack.GetDim(5)-1}; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // This is what we're replacing auto& dB_Uf_dt = mdudt->PackVariables(std::vector{"cons.fB"}); @@ -394,7 +394,7 @@ double B_CT::MaxDivB(MeshData *md) const IndexRange kb = md->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); double max_divb; Kokkos::Max max_reducer(max_divb); @@ -482,7 +482,7 @@ void B_CT::CalcDivB(MeshData *md, std::string divb_field_name) const IndexRange kb = md->GetBoundsK(IndexDomain::interior); const IndexRange block = IndexRange{0, B_U.GetDim(5)-1}; - auto pmb0 = md->GetBlockData(0)->GetBlockPointer().get(); + auto pmb0 = md->GetBlockData(0)->GetBlockPointer(); // See MaxDivB for details pmb0->par_for("calc_divB", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, @@ -495,7 +495,7 @@ void B_CT::CalcDivB(MeshData *md, std::string divb_field_name) void B_CT::FillOutput(MeshBlock *pmb, ParameterInput *pin) { - auto rc = pmb->meshblock_data.Get().get(); + auto rc = pmb->meshblock_data.Get(); const int ndim = pmb->pmy_mesh->ndim; if (ndim < 2) return; diff --git a/kharma/b_flux_ct/b_flux_ct.cpp b/kharma/b_flux_ct/b_flux_ct.cpp index 5a8a12d2..deb17f85 100644 --- a/kharma/b_flux_ct/b_flux_ct.cpp +++ b/kharma/b_flux_ct/b_flux_ct.cpp @@ -508,7 +508,7 @@ double MaxDivB(MeshData *md) // Could consolidate at the cost of lots of bounds checking. double max_divb = 0.0; for (int b = block.s; b <= block.e; ++b) { - auto pmb = md->GetBlockData(b)->GetBlockPointer().get(); + auto pmb = md->GetBlockData(b)->GetBlockPointer(); const IndexRange ib = ValidDivBX1(pmb); @@ -585,7 +585,7 @@ void CalcDivB(MeshData *md, std::string divb_field_name) // See MaxDivB for details for (int b = block.s; b <= block.e; ++b) { - auto pmb = md->GetBlockData(b)->GetBlockPointer().get(); + auto pmb = md->GetBlockData(b)->GetBlockPointer(); const IndexRange ib = ValidDivBX1(pmb); diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 0edd8fbb..9224c187 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -412,7 +412,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD void KBoundaries::CheckInflow(std::shared_ptr> &rc, IndexDomain domain, bool coarse) { - std::shared_ptr pmb = rc->GetBlockPointer(); + auto pmb = rc->GetBlockPointer(); const auto &G = pmb->coords; const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); diff --git a/kharma/boundaries/boundary_types.hpp b/kharma/boundaries/boundary_types.hpp index e9ef80ec..6358e1f9 100644 --- a/kharma/boundaries/boundary_types.hpp +++ b/kharma/boundaries/boundary_types.hpp @@ -179,7 +179,8 @@ inline BoundaryFace BoundaryFaceOf(const IndexDomain domain) /** * Function for checking boundary flags: is this a domain or internal bound? */ -inline bool IsPhysicalBoundary(std::shared_ptr pmb, const BoundaryFace face) +template +inline bool IsPhysicalBoundary(T pmb, const BoundaryFace face) { return !(pmb->boundary_flag[face] == BoundaryFlag::block || pmb->boundary_flag[face] == BoundaryFlag::periodic); diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index 809ae84e..ee27d8b6 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -43,7 +43,7 @@ using namespace parthenon; // TODO can SetDirichlet be folded into this? void KBoundaries::DirichletImpl(std::shared_ptr> &rc, BoundaryFace bface, bool coarse) { - std::shared_ptr pmb = rc->GetBlockPointer(); + auto pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); // Get all ghosts, minus those in the B_Cleanup package if it is present @@ -93,7 +93,7 @@ void KBoundaries::DirichletImpl(std::shared_ptr> &rc, Bounda void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain, bool coarse) { - std::shared_ptr pmb = rc->GetBlockPointer(); + auto pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); const BoundaryFace bface = BoundaryFaceOf(domain); @@ -151,7 +151,7 @@ void KBoundaries::FreezeDirichlet(std::shared_ptr> &md) // ...on all blocks... for (int i=0; i < md->NumBlocks(); i++) { auto rc = md->GetBlockData(i).get(); - std::shared_ptr pmb = rc->GetBlockPointer(); + auto pmb = rc->GetBlockPointer(); auto domain = BoundaryDomain(bface); // Set whatever is in that domain as the Dirichlet bound SetDomainDirichlet(rc, domain, false); diff --git a/kharma/main.cpp b/kharma/main.cpp index f825dcfe..1eadf4ea 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -173,6 +173,15 @@ int main(int argc, char *argv[]) // Note reading "verbose" parameter from "Globals" instead of pin: it may change during simulation const int &verbose = pmesh->packages.Get("Globals")->Param("verbose"); if(MPIRank0() && verbose > 0) { + // Write all parameters etc. to console if we should be especially wordy + // Printed above the rest to stay out of the way + if (verbose > 1) { + // This dumps the full Kokkos config, useful for double-checking + // that the compile did what we wanted + parthenon::ShowConfig(); + pin->ParameterDump(std::cout); + } + // Print a list of variables as Parthenon used to (still does by default) std::cout << "Variables in use:\n" << *(pmesh->resolved_packages) << std::endl; @@ -184,21 +193,16 @@ int main(int argc, char *argv[]) std::cout << std::endl; // Print the number of meshblocks and ranks in use - // TODO get this right std::cout << "Running with " << pmesh->nbtotal << " total meshblocks, " << MPINumRanks() << " MPI ranks." << std::endl; - std::cout << "Blocks on rank " << MPIRank() << ": " << pmesh->block_list.size() << "\n" << std::endl; //pmesh->GetNumMeshBlocksThisRank() << "\n" << std::endl; - - // Write all parameters etc. to console if we should be especially wordy - if ((verbose > 1) && MPIRank0()) { - // This dumps the full Kokkos config, useful for double-checking - // that the compile did what we wanted - parthenon::ShowConfig(); - pin->ParameterDump(std::cout); - } - - // This is for the next bit - std::cout << "Running post-initialization tasks..." << std::endl; + std::cout << "Blocks on rank " << MPIRank() << ": " << pmesh->block_list.size() << "\n" << std::endl; } + // If very verbose, print # meshblocks on every rank + if (verbose > 1) { + MPIBarrier(); + if (MPIRank() > 0) + std::cout << "Blocks on rank " << MPIRank() << ": " << pmesh->block_list.size() << "\n" << std::endl; + } + // PostInitialize: Add magnetic field to the problem, initialize ghost zones. // Any init which may be run even when restarting, or requires all @@ -206,6 +210,13 @@ int main(int argc, char *argv[]) // TODO(BSP) split to package hooks auto prob = pin->GetString("parthenon/job", "problem_id"); bool is_restart = (prob == "resize_restart") || (prob == "resize_restart_kharma") || pman.IsRestart(); + if(MPIRank0() && verbose > 0) { + if (is_restart) { + std::cout << "Running post-restart tasks..." << std::endl; + } else { + std::cout << "Running post-initialization tasks..." << std::endl; + } + } Flag("PostInitialize"); KHARMA::PostInitialize(pin, pmesh, is_restart); EndFlag(); @@ -223,12 +234,14 @@ int main(int argc, char *argv[]) auto pin = pman.pinput.get(); // All parameters in the input file or command line // We now have just one driver package, with different TaskLists for different modes + MPIBarrier(); KHARMADriver driver(pin, papp, pmesh); // Then execute the driver. This is a Parthenon function inherited by our KHARMADriver object, // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion // of each step until a stop criterion is reached. Flag("driver.Execute"); + MPIBarrier(); auto driver_status = driver.Execute(); EndFlag(); } diff --git a/machines/frontier.sh b/machines/frontier.sh index 5f0df32b..d6a6b9d5 100644 --- a/machines/frontier.sh +++ b/machines/frontier.sh @@ -39,6 +39,7 @@ then MPI_NUM_PROCS=8 MPI_EXTRA_ARGS="-c1 --gpus-per-node=8 --gpu-bind=closest" export MPICH_GPU_SUPPORT_ENABLED=1 + export FI_CXI_RX_MATCH_MODE=software # Old workaround, for non-GPU MPI only! #export MPICH_SMP_SINGLE_COPY_MODE=NONE diff --git a/scripts/batch/frontier.sb b/scripts/batch/frontier.sb new file mode 100644 index 00000000..2e6d4558 --- /dev/null +++ b/scripts/batch/frontier.sb @@ -0,0 +1,23 @@ +#!/bin/bash +#SBATCH -A AST185 +#SBATCH -p batch +#SBATCH -J KHARMA +#SBATCH -t 2:00:00 +#SBATCH -N 1 +#SBATCH -o out-%j.txt + +# debug QOS +##SBATCH -q debug + +KHARMA_DIR=~/Code/kharma + +module load PrgEnv-amd +module load craype-accel-amd-gfx90a +module load cray-hdf5-parallel + +export OMP_PROC_BIND=spread +export OMP_PLACES=threads +export KOKKOS_MAP_DEVICE_ID_BY=mpi_rank +export MPICH_GPU_SUPPORT_ENABLED=1 + +srun -n $((8 * $SLURM_NNODES )) -c 1 --gpus-per-node=8 --gpu-bind=closest $KHARMA_DIR/kharma.hip "$@" From 712de122499a144626b90eb91acb7467c2a6bc73 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 30 Nov 2023 16:53:49 -0600 Subject: [PATCH 210/219] Fix GCC GPU running on Delta by reverting to host-side buffers --- CMakeLists.txt | 3 +++ machines/delta.sh | 15 ++++++++++----- make.sh | 5 ++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73010d2c..c3b61010 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,9 @@ set(PARTHENON_LINT_DEFAULT OFF CACHE BOOL "KHARMA Override") set(PARTHENON_DISABLE_HDF5_COMPRESSION OFF CACHE BOOL "KHARMA Override") # Don't build sparse (selectively-allocated) variable support set(PARTHENON_DISABLE_SPARSE ON CACHE BOOL "KHARMA Override") +# Set to move MPI buffers to host; slower but less crashy +# Favor setting this per-machine in machines/ +set(PARTHENON_ENABLE_HOST_COMM_BUFFERS OFF CACHE BOOL "KHARMA Override") # Parthenon internal build options set(BUILD_TESTING OFF CACHE BOOL "KHARMA Override") diff --git a/machines/delta.sh b/machines/delta.sh index 292388d6..cff5f220 100644 --- a/machines/delta.sh +++ b/machines/delta.sh @@ -13,8 +13,10 @@ then HOST_ARCH=ZEN3 DEVICE_ARCH=AMPERE80 MPI_EXE=mpirun + NPROC=64 module purge + module load cmake if [[ $ARGS == *"cuda"* ]] then @@ -23,8 +25,11 @@ then MPI_EXTRA_ARGS="--map-by ppr:4:node:pe=16" MPI_NUM_PROCS=4 + # Device-side buffers are broken on some Nvidia machines + EXTRA_FLAGS="-DPARTHENON_ENABLE_HOST_COMM_BUFFERS=ON $EXTRA_FLAGS" + # Load common GPU modules - module load modtree/gpu hdf5 cmake + module load modtree/gpu cmake if [[ $ARGS == *"latest"* ]]; then # nvhpc only on request, MPI crashes @@ -35,13 +40,13 @@ then C_NATIVE=gcc CXX_NATIVE=g++ else - module load nvhpc - #C_NATIVE=nvc - #CXX_NATIVE=nvc++ + module load nvhpc_latest/22.11 openmpi + C_NATIVE=nvc + CXX_NATIVE=nvc++ fi else # CPU Compile - module load modtree/cpu gcc hdf5 cmake + module load modtree/cpu gcc MPI_NUM_PROCS=1 fi fi diff --git a/make.sh b/make.sh index 6dc9e585..7931fa68 100755 --- a/make.sh +++ b/make.sh @@ -76,8 +76,7 @@ if [[ "$(which python3 2>/dev/null)" == *"conda"* ]]; then echo echo "make.sh note:" echo "It looks like you have Anaconda loaded." - echo "Anaconda loads a serial version of HDF5 which may make this compile impossible." - echo "If you run into trouble, deactivate your environment with 'conda deactivate'" + echo "This is usually okay, but double-check the line 'Found MPI_CXX:' below!" fi # Save arguments if we've changed them # Used in run.sh for loading the same modules/etc. @@ -314,7 +313,7 @@ if [[ "$ARGS" == *"clean"* ]]; then -DKokkos_ENABLE_CUDA=$ENABLE_CUDA \ -DKokkos_ENABLE_SYCL=$ENABLE_SYCL \ -DKokkos_ENABLE_HIP=$ENABLE_HIP \ - "$EXTRA_FLAGS" + $EXTRA_FLAGS if [[ "$ARGS" == *"dryrun"* ]]; then set +x From 556eab5cf67642a726192f6b07fe01af77c0ac0e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 30 Nov 2023 16:02:47 -0700 Subject: [PATCH 211/219] Fix Chicoma build & bump Parthenon to Kokkos 4.2 --- external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp | 2 ++ external/parthenon | 2 +- machines/chicoma.sh | 10 ++++++++-- make.sh | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp b/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp index e7374341..9552fa06 100644 --- a/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp +++ b/external/kokkos-kernels/KokkosBatched_Dot_Internal.hpp @@ -5,6 +5,8 @@ #include "KokkosBatched_Util.hpp" +#define KOKKOS_IMPL_DO_NOT_USE_PRINTF(...) ::printf(__VA_ARGS__) + namespace KokkosBatched { /// diff --git a/external/parthenon b/external/parthenon index 14d41123..994df614 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 14d411239eea0476e0ea1b8f099752a8eab1758a +Subproject commit 994df614305325331cdef57f8fea57e9ac6b8a0a diff --git a/machines/chicoma.sh b/machines/chicoma.sh index 3efe4e61..7e01fd52 100644 --- a/machines/chicoma.sh +++ b/machines/chicoma.sh @@ -32,10 +32,16 @@ if [[ "$HOST" == "ch-fe"* || "$HOST" == "nid00"* ]]; then else module load PrgEnv-aocc fi - module load cray-hdf5-parallel cmake + # Use your own HDF5, Chicoma's is old + #module load cray-hdf5-parallel + module load cmake # System HDF5 can't use compression EXTRA_FLAGS="-DPARTHENON_DISABLE_HDF5_COMPRESSION=ON $EXTRA_FLAGS" - export MPICH_GPU_SUPPORT_ENABLED=1 + # Parthenon crashes with device buffers on some Nvidia machines... + # if [conditions] + EXTRA_FLAGS="-DPARTHENON_ENABLE_HOST_COMM_BUFFERS=ON $EXTRA_FLAGS" + #else + #export MPICH_GPU_SUPPORT_ENABLED=1 # Runtime opts MPI_EXE=srun diff --git a/make.sh b/make.sh index 7931fa68..71b241fa 100755 --- a/make.sh +++ b/make.sh @@ -217,7 +217,7 @@ fi ### Build HDF5 ### # If we're building HDF5, do it after we set *all flags* -if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* ]]; then +if [[ "$ARGS" == *"hdf5"* && "$ARGS" == *"clean"* && "$ARGS" != *"dryrun"* ]]; then H5VER=1.14.2 H5VERU=1_14_2 cd external From 5f45aef2cf36b28129374c682648722bc47b5629 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 1 Dec 2023 16:33:30 -0600 Subject: [PATCH 212/219] Bump Parthenon to include a hanging fix, saner SMR SANE defaults --- external/parthenon | 2 +- pars/smr/sane2d_refined.par | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/external/parthenon b/external/parthenon index 994df614..665aedf0 160000 --- a/external/parthenon +++ b/external/parthenon @@ -1 +1 @@ -Subproject commit 994df614305325331cdef57f8fea57e9ac6b8a0a +Subproject commit 665aedf0bf816d6894d474a2e742fd7b84d4fd6f diff --git a/pars/smr/sane2d_refined.par b/pars/smr/sane2d_refined.par index ec7003d9..fb410403 100644 --- a/pars/smr/sane2d_refined.par +++ b/pars/smr/sane2d_refined.par @@ -67,7 +67,7 @@ bsq_over_rho_max = 100 file_type = hdf5 -dt = 0.0 +dt = 10.0 single_precision_output = true variables = prims.rho, prims.u, prims.uvec, prims.B, divB From abd00a483291b8aca507928b48385dc97fb3014a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 1 Dec 2023 15:39:35 -0700 Subject: [PATCH 213/219] Make Darwin machine config less error-prone --- machines/darwin.sh | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/machines/darwin.sh b/machines/darwin.sh index df526e35..dc6b605c 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -3,8 +3,8 @@ # Must list which node you're compiling for, # from the options below -if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then - module purge +if [[ ($HOSTNAME == "cn"* || $HOSTNAME == "darwin"*) && "$PWD" == "/vast"* ]]; then + #module purge # This messes things up on ARM nodes module load cmake # Where we're going, we don't need system libraries @@ -17,16 +17,16 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then # Load compiler... if [[ "$ARGS" == *"gcc12"* ]]; then - module load openmpi gcc/12.2.0 + module load gcc/12.2.0 openmpi C_NATIVE=gcc CXX_NATIVE=g++ elif [[ "$ARGS" == *"gcc10"* ]]; then - module load openmpi gcc/10.4.0 + module load gcc/10.4.0 openmpi C_NATIVE=gcc CXX_NATIVE=g++ elif [[ "$ARGS" == *"gcc"* ]]; then # Default GCC - module load openmpi gcc/13.1.0 + #module load gcc/13.1.0 openmpi C_NATIVE=gcc CXX_NATIVE=g++ elif [[ "$ARGS" == *"aocc"* ]]; then @@ -79,57 +79,58 @@ if [[ $HOSTNAME == "cn"* || $HOSTNAME == "darwin"* ]]; then if [[ "$ARGS" == *"arm-ampere"* ]]; then HOST_ARCH="ARMV81" DEVICE_ARCH="AMPERE80" - MPI_NUM_PROCS=2 - NODE_SLICE=2 + MPI_NUM_PROCS_D=2 + NODE_SLICE=1 elif [[ "$ARGS" == *"arm-hopper"* ]]; then HOST_ARCH="ARMV81" DEVICE_ARCH="HOPPER90" - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 NODE_SLICE=1 elif [[ "$ARGS" == *"ampere"* ]]; then HOST_ARCH="ZEN3" DEVICE_ARCH="AMPERE80" - MPI_NUM_PROCS=2 - NODE_SLICE=2 + MPI_NUM_PROCS_D=2 + NODE_SLICE=1 elif [[ "$ARGS" == *"volta"* ]]; then HOST_ARCH="HSW" DEVICE_ARCH="VOLTA70" - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 # Some nodes have 2 GPUs, be conservative NODE_SLICE=2 elif [[ "$ARGS" == *"knl"* ]]; then HOST_ARCH="KNL" - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 # 4-way SMT, not 2 NODE_SLICE=2 elif [[ "$ARGS" == *"hsw"* ]]; then HOST_ARCH="HSW" - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 NODE_SLICE=1 elif [[ "$ARGS" == *"skx"* ]]; then HOST_ARCH="SKX" - MPI_NUM_PROCS=${MPI_NUM_PROCS:-$NPROC} - NODE_SLICE=${MPI_NUM_PROCS:-$NPROC} + MPI_NUM_PROCS_D=1 + NODE_SLICE=1 elif [[ "$ARGS" == *"zen2"* ]]; then HOST_ARCH=ZEN2 - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 NODE_SLICE=1 elif [[ "$ARGS" == *"zen3"* ]]; then HOST_ARCH=ZEN3 - MPI_NUM_PROCS=1 + MPI_NUM_PROCS_D=1 NODE_SLICE=1 elif [[ "$ARGS" == *"mi250"* ]]; then HOST_ARCH=ZEN3 DEVICE_ARCH=VEGA90A - MPI_NUM_PROCS=8 - NODE_SLICE=16 + MPI_NUM_PROCS_D=8 + NODE_SLICE=2 else echo "Must specify an architecture on Darwin!" exit fi + MPI_NUM_PROCS=${MPI_NUM_PROCS:-$MPI_NUM_PROCS_D} # Runtime MPI_EXE="mpirun" # Lead MPI to water - MPI_EXTRA_ARGS="--map-by ppr:${MPI_NUM_PROCS}:node:pe=$(($NPROC / $NODE_SLICE))" + MPI_EXTRA_ARGS="--map-by ppr:${MPI_NUM_PROCS}:node:pe=$(($NPROC / $MPI_NUM_PROCS / $NODE_SLICE))" fi From f15c1aee672b2d0032b325fb45a854c6a768b959 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 4 Dec 2023 10:27:01 -0600 Subject: [PATCH 214/219] Fix a face-CT divB problem by avoiding applying any custom conditions to coarse buffers --- kharma/boundaries/boundaries.cpp | 9 +++++++-- kharma/driver/kharma_driver.cpp | 4 ++++ kharma/main.cpp | 6 +++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 9224c187..6cac8543 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -264,13 +264,18 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD pkg->KBoundaries[bface](rc, coarse); EndFlag(); + // Nothing below is designed, nor necessary, for coarse buffers + if (coarse) { + EndFlag(); + return; + } + // If we're syncing EMFs and in spherical, explicitly zero polar faces // Since we manipulate the j coord, we'd overstep coarse bufs auto& emfpack = rc->PackVariables(std::vector{"B_CT.emf"}); if (bdir == X2DIR && pmb->coords.coords.is_spherical() && - emfpack.GetDim(4) > 0 && - !coarse) { + emfpack.GetDim(4) > 0) { Flag("BoundaryEdge_"+bname); for (TE el : {TE::E1, TE::E3}) { int off = (binner) ? 1 : -1; diff --git a/kharma/driver/kharma_driver.cpp b/kharma/driver/kharma_driver.cpp index 9f6497e6..7eaed423 100644 --- a/kharma/driver/kharma_driver.cpp +++ b/kharma/driver/kharma_driver.cpp @@ -215,11 +215,15 @@ TaskStatus KHARMADriver::SyncAllBounds(std::shared_ptr> &md) Flag("SyncAllBounds"); TaskID t_none(0); + //MPIBarrier(); + TaskCollection tc; auto tr = tc.AddRegion(1); AddBoundarySync(t_none, tr[0], md); while (!tr.Execute()); + //MPIBarrier(); + EndFlag(); return TaskStatus::complete; } diff --git a/kharma/main.cpp b/kharma/main.cpp index 1eadf4ea..40c082d7 100644 --- a/kharma/main.cpp +++ b/kharma/main.cpp @@ -198,7 +198,7 @@ int main(int argc, char *argv[]) } // If very verbose, print # meshblocks on every rank if (verbose > 1) { - MPIBarrier(); + //MPIBarrier(); if (MPIRank() > 0) std::cout << "Blocks on rank " << MPIRank() << ": " << pmesh->block_list.size() << "\n" << std::endl; } @@ -234,14 +234,14 @@ int main(int argc, char *argv[]) auto pin = pman.pinput.get(); // All parameters in the input file or command line // We now have just one driver package, with different TaskLists for different modes - MPIBarrier(); + //MPIBarrier(); KHARMADriver driver(pin, papp, pmesh); // Then execute the driver. This is a Parthenon function inherited by our KHARMADriver object, // which will call MakeTaskCollection, then execute the tasks on the mesh for each portion // of each step until a stop criterion is reached. Flag("driver.Execute"); - MPIBarrier(); + //MPIBarrier(); auto driver_status = driver.Execute(); EndFlag(); } From 84061662c6d90d1d320f5665086c41236aa84588 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 4 Dec 2023 10:21:59 -0700 Subject: [PATCH 215/219] Fix Dirichlet conditions under ImEx by including boundary buffers in sync variable packs --- kharma/boundaries/boundaries.cpp | 149 ++++++++++++++++--------------- kharma/boundaries/dirichlet.cpp | 11 +-- kharma/driver/imex_step.cpp | 4 +- 3 files changed, 85 insertions(+), 79 deletions(-) diff --git a/kharma/boundaries/boundaries.cpp b/kharma/boundaries/boundaries.cpp index 6cac8543..8068fc46 100644 --- a/kharma/boundaries/boundaries.cpp +++ b/kharma/boundaries/boundaries.cpp @@ -152,79 +152,82 @@ std::shared_ptr KBoundaries::Initialize(ParameterInput *pin, std: pin->SetString("parthenon/mesh", bname_parthenon, "periodic"); } else { pin->SetString("parthenon/mesh", bname_parthenon, "user"); - } - // TODO TODO any way to save this verbosity with constexpr/macros/something? - if (btype == "dirichlet") { - // Dirichlet boundaries: allocate - pkg->AddField("bounds." + bname, (bdir == X1DIR) ? m_x1 : ((bdir == X2DIR) ? m_x2 : m_x3)); - switch (bface) { - case BoundaryFace::inner_x1: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - case BoundaryFace::outer_x1: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - case BoundaryFace::inner_x2: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - case BoundaryFace::outer_x2: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - case BoundaryFace::inner_x3: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - case BoundaryFace::outer_x3: - pkg->KBoundaries[bface] = KBoundaries::Dirichlet; - break; - default: - break; - } - } else if (btype == "reflecting") { - switch (bface) { - case BoundaryFace::inner_x1: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX1; - break; - case BoundaryFace::outer_x1: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX1; - break; - case BoundaryFace::inner_x2: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX2; - break; - case BoundaryFace::outer_x2: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX2; - break; - case BoundaryFace::inner_x3: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX3; - break; - case BoundaryFace::outer_x3: - pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX3; - break; - default: - break; - } - } else if (btype == "outflow") { - switch (bface) { - case BoundaryFace::inner_x1: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX1; - break; - case BoundaryFace::outer_x1: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX1; - break; - case BoundaryFace::inner_x2: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX2; - break; - case BoundaryFace::outer_x2: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX2; - break; - case BoundaryFace::inner_x3: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX3; - break; - case BoundaryFace::outer_x3: - pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX3; - break; - default: - break; + // Register the actual boundaries with the package, which our wrapper will use + // when called via Parthenon's "user" conditions + if (btype == "dirichlet") { + // Dirichlet boundaries: allocate + pkg->AddField("Boundaries." + bname, (bdir == X1DIR) ? m_x1 : ((bdir == X2DIR) ? m_x2 : m_x3)); + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = KBoundaries::Dirichlet; + break; + default: + break; + } + } else if (btype == "reflecting") { + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX1; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX1; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX2; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX2; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectInnerX3; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = BoundaryFunction::ReflectOuterX3; + break; + default: + break; + } + } else if (btype == "outflow") { + switch (bface) { + case BoundaryFace::inner_x1: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX1; + break; + case BoundaryFace::outer_x1: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX1; + break; + case BoundaryFace::inner_x2: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX2; + break; + case BoundaryFace::outer_x2: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX2; + break; + case BoundaryFace::inner_x3: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowInnerX3; + break; + case BoundaryFace::outer_x3: + pkg->KBoundaries[bface] = BoundaryFunction::OutflowOuterX3; + break; + default: + break; + } + } else { + throw std::runtime_error("Unknown boundary type: "+btype); } } } @@ -301,7 +304,7 @@ void KBoundaries::ApplyBoundary(std::shared_ptr> &rc, IndexD auto jf = (binner) ? b.je + 1 : b.js; pmb->par_for( "zero_polar_" + bname, b.ks, b.ke, jf, jf, b.is, b.ie, - KOKKOS_LAMBDA(const int &k, const int &j, const int &i) { + KOKKOS_LAMBDA (const int &k, const int &j, const int &i) { fpack(F2, 0, k, j, i) = 0.; } ); diff --git a/kharma/boundaries/dirichlet.cpp b/kharma/boundaries/dirichlet.cpp index ee27d8b6..5131eacc 100644 --- a/kharma/boundaries/dirichlet.cpp +++ b/kharma/boundaries/dirichlet.cpp @@ -40,33 +40,34 @@ using namespace parthenon; -// TODO can SetDirichlet be folded into this? +// TODO TODO unify getter/setter when we add face support void KBoundaries::DirichletImpl(std::shared_ptr> &rc, BoundaryFace bface, bool coarse) { auto pmb = rc->GetBlockPointer(); const Real gam = pmb->packages.Get("GRMHD")->Param("gamma"); // Get all ghosts, minus those in the B_Cleanup package if it is present + // TODO TODO this won't do face fields, need a separate loop over (present) faces + // and more logic for bounds buffer size using FC = Metadata::FlagCollection; FC ghost_vars = FC({Metadata::FillGhost, Metadata::Conserved}) + FC({Metadata::FillGhost, Metadata::GetUserFlag("Primitive")}) - FC({Metadata::GetUserFlag("StartupOnly")}); PackIndexMap ghostmap; auto q = rc->PackVariables(ghost_vars, ghostmap, coarse); - auto bound = rc->Get("bounds." + BoundaryName(bface)).data; - // We're sometimes called without any variables to sync (e.g. syncing flags, EMFs), just return if (q.GetDim(4) == 0) return; + auto bound = rc->Get("Boundaries." + BoundaryName(bface)).data; if (q.GetDim(4) != bound.GetDim(4)) { std::cerr << "Dirichlet boundary mismatch! Boundary cache: " << bound.GetDim(4) << " for pack: " << q.GetDim(4) << std::endl; std::cerr << "Variables with ghost zones:" << std::endl; ghostmap.print(); } + // Indices const IndexRange vars = IndexRange{0, q.GetDim(4) - 1}; const bool right = !BoundaryIsInner(bface); - // Subtract off the starting index if we're on the right const auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; const int dir = BoundaryDirection(bface); @@ -103,7 +104,7 @@ void KBoundaries::SetDomainDirichlet(MeshBlockData *rc, IndexDomain domain PackIndexMap ghostmap; auto q = rc->PackVariables(main_ghosts, ghostmap, coarse); const int q_index = ghostmap["prims.q"].first; - auto bound = rc->Get("bounds." + BoundaryName(bface)).data; + auto bound = rc->Get("Boundaries." + BoundaryName(bface)).data; // We're sometimes called without any variables to sync (e.g. syncing flags, EMFs), just return if (q.GetDim(4) == 0) return; diff --git a/kharma/driver/imex_step.cpp b/kharma/driver/imex_step.cpp index 0ec5815c..991d1981 100644 --- a/kharma/driver/imex_step.cpp +++ b/kharma/driver/imex_step.cpp @@ -102,8 +102,10 @@ TaskCollection KHARMADriver::MakeImExTaskCollection(BlockList_t &blocks, int sta if (sync_vars.size() == 0) { // Build the universe of variables to let Parthenon see when exchanging boundaries. // This is built to exclude incidental variables like B field initialization stuff, EMFs, etc. + // "Boundaries" packs in buffers e.g. Dirichlet boundaries using FC = Metadata::FlagCollection; - auto sync_flags = FC({Metadata::GetUserFlag("Primitive"), Metadata::Conserved, Metadata::Face}, true); + auto sync_flags = FC({Metadata::GetUserFlag("Primitive"), Metadata::Conserved, + Metadata::Face, Metadata::GetUserFlag("Boundaries")}, true); sync_vars = KHARMA::GetVariableNames(&(pmesh->packages), sync_flags); } // We'll only ever sync the current stage "final" From 234009e7055cd0e21fe33c6028dd968969c1ed0f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 4 Dec 2023 10:22:29 -0700 Subject: [PATCH 216/219] Add gcc build to personal config --- machines/bp.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/machines/bp.sh b/machines/bp.sh index 8cca870e..df4e4ca5 100644 --- a/machines/bp.sh +++ b/machines/bp.sh @@ -19,8 +19,15 @@ if [[ $HOST == "cheshire"* ]]; then module load nvhpc NPROC=8 # so much memory else - # Intel oneAPI - module load compiler mpi/2021 + if [[ "$ARGS" == *"gcc"* ]]; then + # GCC + module load mpi/mpich-x86_64 + C_NATIVE=gcc + CXX_NATIVE=g++ + else + # Intel oneAPI + module load compiler mpi/2021 + fi NPROC=24 fi # Even CPU kharma is unkillable without this From 3f5887ad8498a8c6bb81b8cccec690df90997e55 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 4 Dec 2023 10:30:04 -0700 Subject: [PATCH 217/219] make.sh: Include CI when testing whether we're on Darwin --- machines/darwin.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/machines/darwin.sh b/machines/darwin.sh index dc6b605c..a3c9ceb9 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -3,7 +3,8 @@ # Must list which node you're compiling for, # from the options below -if [[ ($HOSTNAME == "cn"* || $HOSTNAME == "darwin"*) && "$PWD" == "/vast"* ]]; then +if [[ ($HOSTNAME == "cn"* || $HOSTNAME == "darwin"*) && + ("$PWD" == "/projects/jacamar-ci"* || "$PWD" == "/vast"*) ]]; then #module purge # This messes things up on ARM nodes module load cmake From ce23cbbc6ee8a14b56f5c417b88a2bf7aa207926 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 4 Dec 2023 10:49:54 -0700 Subject: [PATCH 218/219] make.sh: Load MPI w/NVHPC on Darwin now it isn't default --- machines/darwin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machines/darwin.sh b/machines/darwin.sh index a3c9ceb9..8559346e 100644 --- a/machines/darwin.sh +++ b/machines/darwin.sh @@ -48,7 +48,7 @@ if [[ ($HOSTNAME == "cn"* || $HOSTNAME == "darwin"*) && else # Default: NVHPC if cuda else IntelLLVM if [[ "$ARGS" == *"cuda"* ]]; then - module load nvhpc + module load nvhpc openmpi C_NATIVE="nvc" CXX_NATIVE="nvc++" # New NVHPC doesn't like CUDA_HOME From 9d4e8729b40f109acf3c2e6c6f5087c4d7cad61d Mon Sep 17 00:00:00 2001 From: Vedant Dhruv Date: Fri, 8 Dec 2023 09:25:42 -0600 Subject: [PATCH 219/219] restart EMHD runs --- kharma/emhd/emhd.cpp | 32 ++++++++++++++++++++++++++++++++ kharma/emhd/emhd.hpp | 1 + kharma/prob/post_initialize.cpp | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/kharma/emhd/emhd.cpp b/kharma/emhd/emhd.cpp index c76272e5..1046c148 100644 --- a/kharma/emhd/emhd.cpp +++ b/kharma/emhd/emhd.cpp @@ -181,6 +181,38 @@ std::shared_ptr Initialize(ParameterInput *pin, std::shared_ptr

*md, IndexDomain domain, bool coarse) +{ + auto pmb = md->GetBlockData(0)->GetBlockPointer(); + + // Get only relevant cons, but all prims as we need the Lorentz factor + PackIndexMap prims_map, cons_map; + auto U_E = md->PackVariables(std::vector{Metadata::GetUserFlag("EMHDVar"), Metadata::Conserved}, cons_map); + auto P = md->PackVariables(std::vector{Metadata::GetUserFlag("Primitive")}, prims_map); + const VarMap m_p(prims_map, false), m_u(cons_map, true); + + const auto& G = pmb->coords; + + auto bounds = coarse ? pmb->c_cellbounds : pmb->cellbounds; + IndexRange ib = bounds.GetBoundsI(domain); + IndexRange jb = bounds.GetBoundsJ(domain); + IndexRange kb = bounds.GetBoundsK(domain); + IndexRange block = IndexRange{0, U_E.GetDim(5)-1}; + + pmb->par_for("UtoP_EMHD", block.s, block.e, kb.s, kb.e, jb.s, jb.e, ib.s, ib.e, + KOKKOS_LAMBDA (const int& b, const int &k, const int &j, const int &i) { + const Real gamma = GRMHD::lorentz_calc(G, P(b), m_p, k, j, i, Loci::center); + const Real inv_alpha = m::sqrt(-G.gcon(Loci::center, j, i, 0, 0)); + const Real ucon0 = gamma * inv_alpha; + + // Update the primitive EMHD fields + if (m_p.Q >= 0) + P(b, m_p.Q, k, j, i) = U_E(b, m_u.Q, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + if (m_p.DP >= 0) + P(b, m_p.DP, k, j, i) = U_E(b, m_u.DP, k, j, i) / (ucon0 * G.gdet(Loci::center, j, i)); + } + ); +} void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse) { auto pmb = rc->GetBlockPointer(); diff --git a/kharma/emhd/emhd.hpp b/kharma/emhd/emhd.hpp index 90b0505d..9b58b5ad 100644 --- a/kharma/emhd/emhd.hpp +++ b/kharma/emhd/emhd.hpp @@ -110,6 +110,7 @@ void InitEMHDVariables(std::shared_ptr>& rc, ParameterInput * only on boundaries in order to sync the primitive/conserved variables specifically. */ void BlockUtoP(MeshBlockData *rc, IndexDomain domain, bool coarse); +void MeshUtoP(MeshData *md, IndexDomain domain, bool coarse=false); void BlockPtoU(MeshBlockData *rc, IndexDomain domain, bool coarse); /** diff --git a/kharma/prob/post_initialize.cpp b/kharma/prob/post_initialize.cpp index 402ae0c8..ee0e29cf 100644 --- a/kharma/prob/post_initialize.cpp +++ b/kharma/prob/post_initialize.cpp @@ -40,6 +40,7 @@ #include "b_flux_ct.hpp" #include "blob.hpp" #include "boundaries.hpp" +#include "emhd.hpp" #include "floors.hpp" #include "flux.hpp" #include "gr_coordinates.hpp" @@ -116,6 +117,9 @@ void KHARMA::PostInitialize(ParameterInput *pin, Mesh *pmesh, bool is_restart) } else if (pkgs.count("B_CT")) { B_CT::MeshUtoP(md.get(), IndexDomain::entire); } + if (pkgs.count("EMHD")) { + EMHD::MeshUtoP(md.get(), IndexDomain::entire); + } } else { if (pkgs.count("B_FluxCT")) { B_FluxCT::MeshPtoU(md.get(), IndexDomain::entire);