26 #ifndef FUB_AMREX_CUTCELL_TURBINE_MASS_FLOW_BOUNDARY_HPP 
   27 #define FUB_AMREX_CUTCELL_TURBINE_MASS_FLOW_BOUNDARY_HPP 
   39 #include <boost/log/utility/manipulators/add_value.hpp> 
   40 #include <boost/math/tools/roots.hpp> 
   47   template <
typename EulerEquation>
 
   50                   double required_massflow, 
double relative_surface_area,
 
   58   template <
typename EulerEquation>
 
   61                   double required_massflow, 
double relative_surface_area,
 
   83       "TurbineMassflowBoundary"}; 
 
  113 template <
typename EulerEquation,
 
  126   void FillBoundary(::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
 
  127                     const ::amrex::Geometry& geom);
 
  129   void FillBoundary(::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
 
  130                     const ::amrex::Geometry& geom, 
double required_massflow);
 
  150                       double required_massflow);
 
  161   template <
typename I>
 
  162   static I 
MapToSrc(I& dest, const ::amrex::Geometry& geom, 
int side,
 
  164     const int boundary = (side == 0) * geom.Domain().smallEnd(
int(dir)) +
 
  165                          (side == 1) * geom.Domain().bigEnd(
int(dir));
 
  167     src[int(dir)] = boundary;
 
  176 template <
typename EulerEquation, 
typename Transform>
 
  179     : equation_{eq}, options_{options} {}
 
  181 template <
typename EulerEquation, 
typename Transform>
 
  186   double local_average_massflow = 0.0;
 
  188       static_cast<double>(options_.coarse_average_mirror_box.numPts());
 
  190       mf, options_.coarse_average_mirror_box, local_average_massflow,
 
  192         CopyFromBuffer(local_state, buffer);
 
  193         const double local_massflow = ComputeRequiredMassflow(local_state);
 
  194         average += local_massflow / n;
 
  196   double required_massflow = 0.0;
 
  197   ::MPI_Allreduce(&local_average_massflow, &required_massflow, 1, MPI_DOUBLE,
 
  198                   MPI_SUM, ::amrex::ParallelDescriptor::Communicator());
 
  199   return required_massflow;
 
  202 template <
typename EulerEquation, 
typename Transform>
 
  207   BOOST_LOG_SCOPED_LOGGER_TAG(debug, 
"Channel", options_.channel_name);
 
  208   BOOST_LOG_SCOPED_LOGGER_TAG(debug, 
"Time", grid.
GetTimePoint().count());
 
  209   const double required_massflow = AverageRequiredMassflow(mf, grid, level);
 
  210   BOOST_LOG(debug) << fmt::format(
"Required massflow: {}", required_massflow)
 
  211                    << boost::log::add_value(
"required_massflow",
 
  218     AverageState(cons, mf, geom, options_.coarse_average_mirror_box);
 
  220     BOOST_LOG(debug) << fmt::format(
 
  221         "Average inner state: {} kg/m3, {} m/s, {} Pa", state.density,
 
  222         state.momentum.matrix().norm() / state.density, state.pressure);
 
  223     TransformState(expanded, state);
 
  225         options_.
dir, options_.side, equation_, expanded};
 
  226     const double mdot = expanded.momentum[
static_cast<int>(options_.dir)] *
 
  227                         options_.relative_surface_area;
 
  228     BOOST_LOG(debug) << fmt::format(
 
  229         "Homogenous ghost state: {} kg/m3, {} m/s, {} Pa => massflow: {} kg/s, " 
  231         expanded.density, expanded.momentum.matrix().norm() / expanded.density,
 
  232         expanded.pressure, mdot,
 
  233         expanded.momentum[
static_cast<int>(options_.dir)]);
 
  234     constant_boundary.FillBoundary(mf, grid, level);
 
  236     const ::amrex::MultiFab& alphas = factory->getVolFrac();
 
  246         static_cast<double>(options_.coarse_average_mirror_box.numPts());
 
  249                       CopyFromBuffer(cons, buffer);
 
  250                       CompleteFromCons(equation_, state, cons);
 
  251                       TransformState(expanded, state);
 
  253                           [n](auto&& avg, auto&& q) { avg += q / n; }, average,
 
  258     std::vector<double> local_buffer(n_comps);
 
  260     std::vector<double> global_buffer(n_comps);
 
  261     MPI_Allreduce(local_buffer.data(), global_buffer.data(), n_comps,
 
  263                   ::amrex::ParallelDescriptor::Communicator());
 
  266     const double mdot = state.momentum[
static_cast<int>(options_.dir)] *
 
  267                         options_.relative_surface_area;
 
  268     BOOST_LOG(debug) << fmt::format(
 
  269         "Average ghost state: {} kg/m3, {} m/s, {} Pa => massflow: {} kg/s",
 
  270         state.density, state.momentum.matrix().norm() / state.density,
 
  271         state.pressure, mdot);
 
  273         options_.
dir, options_.side, equation_, state};
 
  274     constant_boundary.FillBoundary(mf, grid, level);
 
  276     const ::amrex::MultiFab& alphas = factory->getVolFrac();
 
  277     FillBoundary(mf, alphas, grid.GetPatchHierarchy().GetGeometry(level));
 
  281 template <
typename EulerEquation, 
typename Transform>
 
  283     ::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
 
  284     const ::amrex::Geometry& geom) {
 
  288     ::amrex::FArrayBox& fab = mf[mfi];
 
  289     const ::amrex::FArrayBox& alpha = alphas[mfi];
 
  290     ::amrex::Box box_to_fill = mfi.growntilebox() & options_.boundary_section;
 
  291     if (!box_to_fill.isEmpty()) {
 
  292       auto states = MakeView<Complete>(fab, equation_, mfi.growntilebox());
 
  294           AsIndexBox<EulerEquation::Rank()>(box_to_fill), [&](auto... is) {
 
  295             Index<EulerEquation::Rank()> dest{is...};
 
  296             Index<EulerEquation::Rank()> src =
 
  297                 MapToSrc(dest, geom, options_.side, options_.dir);
 
  298             ::amrex::IntVect src_iv{
 
  299                 AMREX_D_DECL(int(src[0]), int(src[1]), int(src[2]))};
 
  301                 AMREX_D_DECL(int(dest[0]), int(dest[1]), int(dest[2]))};
 
  302             if (alpha(iv) > 0.0 && alpha(src_iv) > 0.0) {
 
  303               Load(state, states, src);
 
  304               TransformState(expanded, state);
 
  305               Store(states, expanded, dest);
 
  312 template <
typename EulerEquation, 
typename Transform>
 
  314     ::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
 
  315     const ::amrex::Geometry& geom, 
double required_massflow) {
 
  319     ::amrex::FArrayBox& fab = mf[mfi];
 
  320     const ::amrex::FArrayBox& alpha = alphas[mfi];
 
  321     ::amrex::Box box_to_fill = mfi.growntilebox() & options_.boundary_section;
 
  322     if (!box_to_fill.isEmpty()) {
 
  323       auto states = MakeView<Complete>(fab, equation_, mfi.growntilebox());
 
  325           AsIndexBox<EulerEquation::Rank()>(box_to_fill), [&](auto... is) {
 
  326             Index<EulerEquation::Rank()> dest{is...};
 
  327             Index<EulerEquation::Rank()> src =
 
  328                 MapToSrc(dest, geom, options_.side, options_.dir);
 
  329             ::amrex::IntVect src_iv{
 
  330                 AMREX_D_DECL(int(src[0]), int(src[1]), int(src[2]))};
 
  332                 AMREX_D_DECL(int(dest[0]), int(dest[1]), int(dest[2]))};
 
  333             if (alpha(iv) > 0.0 && alpha(src_iv) > 0.0) {
 
  334               Load(state, states, src);
 
  335               TransformState(expanded, state, required_massflow);
 
  336               Store(states, expanded, dest);
 
  343 template <
typename EulerEquation, 
typename Transform>
 
  346   transform_(equation_, dest, source, required_massflow,
 
  347              options_.relative_surface_area, options_.dir);
 
  350 template <
typename EulerEquation, 
typename Transform>
 
  357   const double c2 = c * c;
 
  358   const int dir_v = 
static_cast<int>(options_.dir);
 
  360   const double u2 = u * u;
 
  361   const double Ma2 = u2 / c2;
 
  364   const double gm1 = gamma - 1.0;
 
  365   const double oogm1 = 1.0 / gm1;
 
  368   const double alpha_0 = 1.0 + 0.5 * Ma2 * gm1;
 
  369   const double p_0 = p * std::pow(alpha_0, gamma * oogm1);
 
  370   const double T_0 = T * alpha_0;
 
  373   const double required_massflow =
 
  374       options_.massflow_correlation * p_0 / std::sqrt(T_0);
 
  376   return required_massflow;
 
  379 template <
typename EulerEquation, 
typename Transform>
 
  382   const double required_massflow = ComputeRequiredMassflow(source);
 
  383   std::invoke(transform_, equation_, dest, source, required_massflow,
 
  384               options_.relative_surface_area, options_.dir);
 
  390 template <
typename EulerEquation>
 
  394            double relative_surface_area, 
Direction dir) 
const noexcept {
 
  402   const double c2 = c * c;
 
  403   const int dir_v = 
static_cast<int>(dir);
 
  404   const double u = prim.velocity[dir_v];
 
  405   const double u2 = u * u;
 
  406   const double Ma2 = u2 / c2;
 
  408   const double gammaMinus = gamma - 1.0;
 
  409   const double gammaMinusHalf = 0.5 * gammaMinus;
 
  410   const double gammaMinusInv = 1.0 / gammaMinus;
 
  411   const double gammaOverGammaMinus = gamma * gammaMinusInv;
 
  412   const double gammaPlus = gamma + 1.0;
 
  416   const double alpha_0 = 1.0 + Ma2 * gammaMinusHalf;
 
  417   const double p_0 = p * std::pow(alpha_0, gammaOverGammaMinus);
 
  418   const double rho_0 = rho * std::pow(alpha_0, gammaMinusInv);
 
  419   const double T_0 = T * alpha_0;
 
  420   const double c_02 = c2 + gammaMinusHalf * u2;
 
  427   const double alpha = gammaMinusHalf / c_02;
 
  428   const double beta = required_massflow / (relative_surface_area * rho_0);
 
  429   auto f = [alpha, beta, gammaMinusInv](
double u_n) {
 
  430     return u_n * std::pow(1.0 - alpha * u_n * u_n, gammaMinusInv) - beta;
 
  432   auto Df = [alpha, gammaMinusInv](
double u_n) {
 
  433     const double x = 1.0 - alpha * u_n * u_n;
 
  434     return std::pow(x, gammaMinusInv) - 2.0 * alpha * u_n * u_n *
 
  436                                             std::pow(x, gammaMinusInv - 1.0);
 
  440   const double critical_speed_of_sound = std::sqrt(2.0 * c_02 / (gamma + 1.0));
 
  441   const double required_laval =
 
  442       std::min(required_u / critical_speed_of_sound, 1.0);
 
  443   const double required_laval2 = required_laval * required_laval;
 
  445   const double alpha_n = 1.0 - gammaMinus / gammaPlus * required_laval2;
 
  446   prim.density = rho_0 * std::pow(alpha_n, gammaMinusInv);
 
  447   prim.pressure = p_0 * std::pow(alpha_n, gammaOverGammaMinus);
 
  448   prim.velocity[dir_v] = required_u;
 
  452 template <
typename EulerEquation>
 
  456            double relative_surface_area, 
Direction dir) 
const noexcept {
 
  462   const int dir_v = 
static_cast<int>(dir);
 
  463   const double u = prim.velocity[dir_v];
 
  466   const double gp1 = gamma + 1.0;
 
  467   const double gm1 = gamma - 1.0;
 
  468   const double oogm1 = 1.0 / gm1;
 
  469   const double gm1_over_gp1 = gm1 / gp1;
 
  493   data.gm1_over_gp1 = gm1_over_gp1;
 
  495   data.rhou_star = required_massflow / relative_surface_area;
 
  498   static auto f_shock = [](
double p, 
const UserData& d) {
 
  499     const double A = 2.0 / (d.gp1 * d.rhoL);
 
  500     const double B = d.pL * d.gm1_over_gp1;
 
  501     const double ooQ = std::sqrt(A / (p + B));
 
  502     return (p - d.pL) * ooQ;
 
  506   static auto u_shock = [](
double p, 
const UserData& d) {
 
  507     return d.uL - f_shock(p, d);
 
  511   static auto rho_shock = [](
double p, 
const UserData& d) {
 
  513     const double p_rel = p / d.pL;
 
  514     const double nom = d.gm1_over_gp1 + p_rel;
 
  515     const double denom = d.gm1_over_gp1 * p_rel + 1.0;
 
  517     const double coeff = nom / denom;
 
  518     return d.rhoL * coeff;
 
  522   static auto f_rarefaction = [](
double p, 
const UserData& d) {
 
  523     const double pre_coeff = 2.0 * d.aL * d.oogm1;
 
  524     const double p_rel = p / d.pL;
 
  525     const double exponent = d.gm1 / 2.0 / d.gamma;
 
  526     return pre_coeff * (std::pow(p_rel, exponent) - 1.0);
 
  530   static auto u_rarefaction = [](
double p, 
const UserData& d) {
 
  531     return d.uL - f_rarefaction(p, d);
 
  535   static auto rho_rarefaction = [](
double p, 
const UserData& d) {
 
  537     const double p_rel = p / d.pL;
 
  538     const double exponent = 1.0 / d.gamma;
 
  539     return d.rhoL * std::pow(p_rel, exponent);
 
  542   static auto delta_rhou_shock = [](
double p, 
const UserData& d) {
 
  543     const double u_star = u_shock(p, d);
 
  544     const double rho_star = rho_shock(p, d);
 
  545     return rho_star * u_star - d.rhou_star;
 
  548   static auto delta_rhou_rarefaction = [](
double p, 
const UserData& d) {
 
  549     const double u_star = u_rarefaction(p, d);
 
  550     const double rho_star = rho_rarefaction(p, d);
 
  551     return rho_star * u_star - d.rhou_star;
 
  554   auto delta_rhou = [&data](
double p) {
 
  556       return delta_rhou_rarefaction(p, data);
 
  558       return delta_rhou_shock(p, data);
 
  562   auto FindLowerAndUpperBounds = [&] {
 
  563     using Result = std::optional<std::pair<double, double>>;
 
  567     double drhou = delta_rhou(p0);
 
  572         if (counter-- == 0) {
 
  575         drhou = delta_rhou(lower);
 
  576       } 
while (drhou < 0.0);
 
  580         if (counter-- == 0) {
 
  583         drhou = delta_rhou(upper);
 
  584       } 
while (drhou > 0.0);
 
  586     return Result{std::pair{lower, upper}};
 
  590   std::optional bounds = FindLowerAndUpperBounds();
 
  592     auto [lower, upper] = *bounds;
 
  593     const int digits = std::numeric_limits<double>::digits;
 
  594     const int get_digits = digits - 5;
 
  595     boost::math::tools::eps_tolerance<double> tol(get_digits);
 
  596     boost::uintmax_t it = 100; 
 
  598         boost::math::tools::bisect(delta_rhou, lower, upper, tol, it);
 
  599     const double p_star = lo + 0.5 * (hi - lo);
 
  600     const double u_star =
 
  601         p_star < data.pL ? u_rarefaction(p_star, data) : u_shock(p_star, data);
 
  602     const double rho_star = p_star < data.pL ? rho_rarefaction(p_star, data)
 
  603                                              : rho_shock(p_star, data);
 
  604     prim.pressure = p_star;
 
  605     prim.velocity[dir_v] = u_star;
 
  606     prim.density = rho_star;
 
  609     throw std::runtime_error(
"No alternative is currently implemented.");
 
#define FUB_ASSERT(x)
Definition: assert.hpp:39
 
This class modifies and initializes a cutcell::PatchLevel in a cutcell::PatchHierarchy.
Definition: AMReX/cutcell/GriddingAlgorithm.hpp:56
 
Duration GetTimePoint() const noexcept
Returns the current time point on the coarsest refinement level.
Definition: AMReX/cutcell/GriddingAlgorithm.hpp:126
 
const PatchHierarchy & GetPatchHierarchy() const noexcept
 
const std::shared_ptr<::amrex::EBFArrayBoxFactory > & GetEmbeddedBoundary(int level) const
Returns the number of cycles done for a specific level.
 
const DataDescription & GetDataDescription() const noexcept
Return some additional patch hierarchy options.
 
const ::amrex::Geometry & GetGeometry(int level) const
Returns the number of cycles done for a specific level.
 
This is an outflow boundary condition that models the massflow condition of a turbine machine.
Definition: TurbineMassflowBoundary.hpp:115
 
EulerEquation equation_
Definition: TurbineMassflowBoundary.hpp:171
 
double AverageRequiredMassflow(::amrex::MultiFab &mf, const GriddingAlgorithm &grid, int level)
Returns the averaged required mass flow over all boundary cells.
Definition: TurbineMassflowBoundary.hpp:183
 
void FillBoundary(::amrex::MultiFab &mf, const GriddingAlgorithm &gridding, int level)
Definition: TurbineMassflowBoundary.hpp:203
 
void TransformState(Complete &expanded, const Complete &source)
Compute the new complete state from the old complete state to enforce the required massflow....
Definition: TurbineMassflowBoundary.hpp:380
 
TurbineMassflowBoundaryOptions options_
Definition: TurbineMassflowBoundary.hpp:172
 
void FillBoundary(::amrex::MultiFab &mf, const GriddingAlgorithm &gridding, int level, Direction dir)
Definition: TurbineMassflowBoundary.hpp:132
 
static I MapToSrc(I &dest, const ::amrex::Geometry &geom, int side, Direction dir) noexcept
Definition: TurbineMassflowBoundary.hpp:162
 
Transform transform_
Definition: TurbineMassflowBoundary.hpp:173
 
TurbineMassflowBoundary(const EulerEquation &eq, const TurbineMassflowBoundaryOptions &options)
Definition: TurbineMassflowBoundary.hpp:177
 
double ComputeRequiredMassflow(const Complete &state) const
Returns the required mass flow for a given complete state.
Definition: TurbineMassflowBoundary.hpp:352
 
A span is a view over a contiguous sequence of objects, the storage of which is owned by some other o...
Definition: span.hpp:81
 
void CompleteFromCons(Equation &&equation, Complete< std::decay_t< Equation >> &complete, const Conservative< std::decay_t< Equation >> &cons)
Definition: CompleteFromCons.hpp:42
 
void ForEachFab(Tag, const ::amrex::FabArrayBase &fabarray, F function)
Iterate through all local FArrayBox objects in a MultiFab.
Definition: ForEachFab.hpp:34
 
Definition: FillCutCellData.hpp:30
 
TurbineMassflowMode
Definition: TurbineMassflowBoundary.hpp:67
 
void AccumulateState(const ::amrex::MultiFab &mf, const ::amrex::Box &box, InitialValue &&init, BinaryOp &&binary_op)
Definition: AverageState.hpp:36
 
void AverageState(Complete< Equation > &state, const ::amrex::MultiFab &mf, const ::amrex::Geometry &, const ::amrex::Box &box)
Definition: AverageState.hpp:57
 
constexpr struct fub::euler::SpeedOfSoundFn SpeedOfSound
 
constexpr struct fub::euler::GammaFn Gamma
 
constexpr struct fub::euler::PressureFn Pressure
 
constexpr struct fub::euler::DensityFn Density
 
constexpr struct fub::euler::VelocityFn Velocity
 
constexpr struct fub::euler::TemperatureFn Temperature
 
constexpr SequentialTag seq
Definition: Execution.hpp:31
 
The fub namespace.
Definition: AnyBoundaryCondition.hpp:31
 
void CopyToBuffer(span< double > buffer, const Conservative< Equation > &state)
Definition: State.hpp:906
 
constexpr auto Transform(Tuple &&tuple, Function f)
Definition: tuple.hpp:53
 
SeverityLogger GetLogger(boost::log::trivial::severity_level level)
Definition: Log.hpp:53
 
void PrimFromComplete(const PerfectGas< Rank > &, Primitive< PerfectGas< Rank >> &prim, const Complete< PerfectGas< Rank >> &complete)
Definition: PerfectGas.hpp:322
 
void CompleteFromPrim(const PerfectGas< Rank > &equation, Complete< PerfectGas< Rank >> &complete, const Primitive< PerfectGas< Rank >> &prim)
Definition: PerfectGas.hpp:314
 
void CopyFromBuffer(Complete< Equation > &state, span< const double > buffer)
Definition: State.hpp:884
 
boost::log::sources::severity_logger< boost::log::trivial::severity_level > SeverityLogger
Definition: Log.hpp:46
 
Direction
This is a type safe type to denote a dimensional split direction.
Definition: Direction.hpp:30
 
const Conservative< Eq > & AsCons(const Conservative< Eq > &x)
Definition: State.hpp:438
 
double NewtonIteration(Function &&f, Derivative &&Df, double x0, double tolerance=1e-7, int max_iterations=5000)
Definition: NewtonIteration.hpp:29
 
IndexBox< Rank > Box(const BasicView< State, Layout, Rank > &view)
Definition: State.hpp:486
 
boost::outcome_v2::result< T, E > Result
Definition: outcome.hpp:32
 
std::map< std::string, pybind11::object > ProgramOptions
Definition: ProgramOptions.hpp:40
 
Compute the new complete state from the old complete state to enforce the required massflow.
Definition: TurbineMassflowBoundary.hpp:46
 
void operator()(EulerEquation &eq, Complete< EulerEquation > &expanded, const Complete< EulerEquation > &source, double required_massflow, double relative_surface_area, Direction dir) const noexcept
Definition: TurbineMassflowBoundary.hpp:392
 
Compute the new complete state from the old complete state to enforce the required massflow.
Definition: TurbineMassflowBoundary.hpp:57
 
void operator()(EulerEquation &eq, Complete< EulerEquation > &expanded, const Complete< EulerEquation > &source, double required_massflow, double relative_surface_area, Direction dir) const noexcept
Definition: TurbineMassflowBoundary.hpp:454
 
Direction dir
Definition: boundary_condition/ConstantBoundary.hpp:38
 
int n_cons_components
Definition: AMReX/PatchHierarchy.hpp:76
 
Definition: TurbineMassflowBoundary.hpp:76
 
TurbineMassflowBoundaryOptions()=default
 
void Print(SeverityLogger &log) const
 
std::string channel_name
the channel name for logging
Definition: TurbineMassflowBoundary.hpp:82
 
boost::uintmax_t max_iter
allowed maximum iterations for the bisection root fnding
Definition: TurbineMassflowBoundary.hpp:89
 
TurbineMassflowBoundaryOptions(const ProgramOptions &options)
 
::amrex::Box boundary_section
the amrex box where the boundary is located
Definition: TurbineMassflowBoundary.hpp:85
 
Direction dir
the dimensional split direction which will be used
Definition: TurbineMassflowBoundary.hpp:96
 
double massflow_correlation
the massflow correlation, this is given by the idealized compressor characteristics
Definition: TurbineMassflowBoundary.hpp:95
 
TurbineMassflowMode mode
the mode how the massflow is enforced
Definition: TurbineMassflowBoundary.hpp:86
 
::amrex::Box coarse_average_mirror_box
Definition: TurbineMassflowBoundary.hpp:88
 
double relative_surface_area
the relative surface area for the turbine
Definition: TurbineMassflowBoundary.hpp:91
 
int side
the side where the boundary condition is applied (0==left, 1==right)
Definition: TurbineMassflowBoundary.hpp:98