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