Finite Volume Solver  prototype
A framework to build finite volume solvers for the AG Klein at the Freie Universität Berlin.
TurbineMassflowBoundary.hpp
Go to the documentation of this file.
1 // Copyright (c) 2020 Maikel Nadolski
2 // Copyright (c) 2020 Christian Zenker
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 
22 /// \file
23 /// This file defines a Boundary Condition which emulates a Plenum-Turbine
24 /// Interaction.
25 
26 #ifndef FUB_AMREX_CUTCELL_TURBINE_MASS_FLOW_BOUNDARY_HPP
27 #define FUB_AMREX_CUTCELL_TURBINE_MASS_FLOW_BOUNDARY_HPP
28 
33 
34 #include "fub/Direction.hpp"
35 #include "fub/NewtonIteration.hpp"
36 #include "fub/ext/Log.hpp"
37 
38 #include <AMReX.H>
39 #include <boost/log/utility/manipulators/add_value.hpp>
40 #include <boost/math/tools/roots.hpp>
41 
42 namespace fub {
43 /// \brief Compute the new complete state from the old complete state to
44 /// enforce the required massflow. This is done by following the method from
45 /// Jirasek. TODO: cite references
47  template <typename EulerEquation>
48  void operator()(EulerEquation& eq, Complete<EulerEquation>& expanded,
49  const Complete<EulerEquation>& source,
50  double required_massflow, double relative_surface_area,
51  Direction dir) const noexcept;
52 };
53 
54 /// \brief Compute the new complete state from the old complete state to
55 /// enforce the required massflow. This is done by solving the exact Riemann
56 /// Problem as described in Toro. TODO: cite references
58  template <typename EulerEquation>
59  void operator()(EulerEquation& eq, Complete<EulerEquation>& expanded,
60  const Complete<EulerEquation>& source,
61  double required_massflow, double relative_surface_area,
62  Direction dir) const noexcept;
63 };
64 } // namespace fub
65 
66 namespace fub::amrex::cutcell {
67 enum class TurbineMassflowMode {
68  cellwise,
72 };
73 
74 /// \ingroup BoundaryCondition
75 ///
79 
80  void Print(SeverityLogger& log) const;
81 
82  std::string channel_name{
83  "TurbineMassflowBoundary"}; ///< the channel name for logging
85  boundary_section{}; ///< the amrex box where the boundary is located
87  TurbineMassflowMode::cellwise; ///< the mode how the massflow is enforced
89  boost::uintmax_t max_iter{
90  100}; ///< allowed maximum iterations for the bisection root fnding
92  1.0; ///< the relative surface area for the turbine
93  /// the massflow correlation, this is given by the idealized compressor
94  /// characteristics
95  double massflow_correlation = 0.06;
97  Direction::X; ///< the dimensional split direction which will be used
98  int side = 0; ///< the side where the boundary condition is applied (0==left, 1==right)
99 };
100 
101 /// \ingroup BoundaryCondition
102 ///
103 /// This is an outflow boundary condition that models the massflow condition of
104 /// a turbine machine.
105 ///
106 /// The massflow is given by the relation
107 ///
108 /// \f$\dot{m} / A \cdot \frac{\sqrt{T_0}}{p_0} = \text{const}\f$
109 ///
110 /// Therefore, for given surface area \f$A\f$, total pressure \f$p_0\f$ and
111 /// total temperature \f$T_0\f$ one determines the required massflow \f$\dot
112 /// m\f$ and recomputes the static pressure and temperature values.
113 template <typename EulerEquation,
116 public:
119 
120  TurbineMassflowBoundary(const EulerEquation& eq,
121  const TurbineMassflowBoundaryOptions& options);
122 
123  void FillBoundary(::amrex::MultiFab& mf, const GriddingAlgorithm& gridding,
124  int level);
125 
126  void FillBoundary(::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
127  const ::amrex::Geometry& geom);
128 
129  void FillBoundary(::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
130  const ::amrex::Geometry& geom, double required_massflow);
131 
132  void FillBoundary(::amrex::MultiFab& mf, const GriddingAlgorithm& gridding,
133  int level, Direction dir) {
134  if (dir == options_.dir) {
135  FillBoundary(mf, gridding, level);
136  }
137  }
138 
139  /// @{
140  /// \brief Compute the new complete state from the old complete state to
141  /// enforce the required massflow. This is done by solving the exact Riemann
142  /// Problem as described in Toro or by following the method from Jirasek.
143  /// TODO: cite references
144  ///
145  /// \param source the old complete state
146  /// \param expanded the new complete state which enforces the required
147  /// massflow condition
148  void TransformState(Complete& expanded, const Complete& source);
149  void TransformState(Complete& expanded, const Complete& source,
150  double required_massflow);
151  /// @}
152 
153  /// \brief Returns the required mass flow for a given complete state.
154  double ComputeRequiredMassflow(const Complete& state) const;
155 
156  /// \brief Returns the averaged required mass flow over all boundary cells.
157  double AverageRequiredMassflow(::amrex::MultiFab& mf,
158  const GriddingAlgorithm& grid, int level);
159 
160 private:
161  template <typename I>
162  static I MapToSrc(I& dest, const ::amrex::Geometry& geom, int side,
163  Direction dir) noexcept {
164  const int boundary = (side == 0) * geom.Domain().smallEnd(int(dir)) +
165  (side == 1) * geom.Domain().bigEnd(int(dir));
166  I src{dest};
167  src[int(dir)] = boundary;
168  return src;
169  }
170 
171  EulerEquation equation_;
174 };
175 
176 template <typename EulerEquation, typename Transform>
178  const EulerEquation& eq, const TurbineMassflowBoundaryOptions& options)
179  : equation_{eq}, options_{options} {}
180 
181 template <typename EulerEquation, typename Transform>
182 double
184  ::amrex::MultiFab& mf, const GriddingAlgorithm& grid, int level) {
185  Complete local_state{equation_};
186  double local_average_massflow = 0.0;
187  const double n =
188  static_cast<double>(options_.coarse_average_mirror_box.numPts());
190  mf, options_.coarse_average_mirror_box, local_average_massflow,
191  [&](double& average, span<const double> buffer) {
192  CopyFromBuffer(local_state, buffer);
193  const double local_massflow = ComputeRequiredMassflow(local_state);
194  average += local_massflow / n;
195  });
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;
200 }
201 
202 template <typename EulerEquation, typename Transform>
204  ::amrex::MultiFab& mf, const GriddingAlgorithm& grid, int level) {
205  auto factory = grid.GetPatchHierarchy().GetEmbeddedBoundary(level);
206  SeverityLogger debug = GetLogger(boost::log::trivial::debug);
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",
212  required_massflow);
213  if (options_.mode == TurbineMassflowMode::average_inner_state) {
214  Conservative cons{equation_};
215  Complete state{equation_};
216  Complete expanded{equation_};
217  const ::amrex::Geometry& geom = grid.GetPatchHierarchy().GetGeometry(level);
218  AverageState(cons, mf, geom, options_.coarse_average_mirror_box);
219  CompleteFromCons(equation_, state, cons);
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);
224  ConstantBoundary<EulerEquation> constant_boundary{
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, "
230  "{:12g} kg/(m2 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);
235  } else if (options_.mode == TurbineMassflowMode::average_massflow) {
236  const ::amrex::MultiFab& alphas = factory->getVolFrac();
237  FillBoundary(mf, alphas, grid.GetPatchHierarchy().GetGeometry(level),
238  required_massflow);
239  } else if (options_.mode == TurbineMassflowMode::average_outer_state) {
240  Conservative avg{equation_};
241  Conservative cons{equation_};
242  Complete state{equation_};
243  Complete expanded{equation_};
244 
245  const double n =
246  static_cast<double>(options_.coarse_average_mirror_box.numPts());
247  AccumulateState(mf, options_.coarse_average_mirror_box, avg,
248  [&](Conservative& average, span<const double> buffer) {
249  CopyFromBuffer(cons, buffer);
250  CompleteFromCons(equation_, state, cons);
251  TransformState(expanded, state);
252  ForEachVariable(
253  [n](auto&& avg, auto&& q) { avg += q / n; }, average,
254  AsCons(expanded));
255  });
256  const int n_comps =
258  std::vector<double> local_buffer(n_comps);
259  CopyToBuffer(local_buffer, avg);
260  std::vector<double> global_buffer(n_comps);
261  MPI_Allreduce(local_buffer.data(), global_buffer.data(), n_comps,
262  MPI_DOUBLE, MPI_SUM,
263  ::amrex::ParallelDescriptor::Communicator());
264  CopyFromBuffer(avg, global_buffer);
265  CompleteFromCons(equation_, state, avg);
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);
272  ConstantBoundary<EulerEquation> constant_boundary{
273  options_.dir, options_.side, equation_, state};
274  constant_boundary.FillBoundary(mf, grid, level);
275  } else if (options_.mode == TurbineMassflowMode::cellwise) {
276  const ::amrex::MultiFab& alphas = factory->getVolFrac();
277  FillBoundary(mf, alphas, grid.GetPatchHierarchy().GetGeometry(level));
278  }
279 }
280 
281 template <typename EulerEquation, typename Transform>
283  ::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
284  const ::amrex::Geometry& geom) {
285  Complete state{equation_};
286  Complete expanded{equation_};
287  ForEachFab(execution::seq, mf, [&](const ::amrex::MFIter& mfi) {
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());
293  ForEachIndex(
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]))};
300  ::amrex::IntVect iv{
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);
306  }
307  });
308  }
309  });
310 }
311 
312 template <typename EulerEquation, typename Transform>
314  ::amrex::MultiFab& mf, const ::amrex::MultiFab& alphas,
315  const ::amrex::Geometry& geom, double required_massflow) {
316  Complete state{equation_};
317  Complete expanded{equation_};
318  ForEachFab(execution::seq, mf, [&](const ::amrex::MFIter& mfi) {
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());
324  ForEachIndex(
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]))};
331  ::amrex::IntVect iv{
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);
337  }
338  });
339  }
340  });
341 }
342 
343 template <typename EulerEquation, typename Transform>
345  Complete& dest, const Complete& source, double required_massflow) {
346  transform_(equation_, dest, source, required_massflow,
347  options_.relative_surface_area, options_.dir);
348 }
349 
350 template <typename EulerEquation, typename Transform>
351 double
353  const Complete& state) const {
354  const double p = euler::Pressure(equation_, state);
355  const double c = euler::SpeedOfSound(equation_, state);
356  const double T = euler::Temperature(equation_, state);
357  const double c2 = c * c;
358  const int dir_v = static_cast<int>(options_.dir);
359  const double u = euler::Velocity(equation_, state, dir_v);
360  const double u2 = u * u;
361  const double Ma2 = u2 / c2;
362 
363  const double gamma = euler::Gamma(equation_, state);
364  const double gm1 = gamma - 1.0;
365  const double oogm1 = 1.0 / gm1;
366 
367  // compute total quantities
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;
371 
372  // const int enable_massflow = (p_0 > options_.pressure_threshold);
373  const double required_massflow =
374  options_.massflow_correlation * p_0 / std::sqrt(T_0);
375 
376  return required_massflow;
377 }
378 
379 template <typename EulerEquation, typename Transform>
381  Complete& dest, const Complete& source) {
382  const double required_massflow = ComputeRequiredMassflow(source);
383  std::invoke(transform_, equation_, dest, source, required_massflow,
384  options_.relative_surface_area, options_.dir);
385 }
386 
387 } // namespace fub::amrex::cutcell
388 
389 namespace fub {
390 template <typename EulerEquation>
392 operator()(EulerEquation& eq, Complete<EulerEquation>& expanded,
393  const Complete<EulerEquation>& source, double required_massflow,
394  double relative_surface_area, Direction dir) const noexcept {
395  Primitive<EulerEquation> prim(eq);
396  PrimFromComplete(eq, prim, source);
397  const double rho = euler::Density(eq, source);
398  const double p = euler::Pressure(eq, source);
399  const double gamma = euler::Gamma(eq, source);
400  const double c = euler::SpeedOfSound(eq, source);
401  const double T = euler::Temperature(eq, source);
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;
407 
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;
413  // const double gammaPlus = gamma + 1.0;
414 
415  // compute total quantities
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;
421  // const double c_0 = std::sqrt(c_02);
422 
423  // const int enable_massflow = (p_0 > options_.pressure_threshold);
424  // const double required_massflow =
425  // options_.massflow_correlation * p_0 / std::sqrt(T_0);
426 
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;
431  };
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 *
435  gammaMinusInv *
436  std::pow(x, gammaMinusInv - 1.0);
437  };
438  const double required_u = NewtonIteration(f, Df, u);
439  // limit outflow velocity to a physically possible one
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;
444 
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;
449  CompleteFromPrim(eq, expanded, prim);
450 }
451 
452 template <typename EulerEquation>
454 operator()(EulerEquation& eq, Complete<EulerEquation>& expanded,
455  const Complete<EulerEquation>& source, double required_massflow,
456  double relative_surface_area, Direction dir) const noexcept {
457  Primitive<EulerEquation> prim(eq);
458  PrimFromComplete(eq, prim, source);
459  const double rho = euler::Density(eq, source);
460  const double p = euler::Pressure(eq, source);
461  const double c = euler::SpeedOfSound(eq, source);
462  const int dir_v = static_cast<int>(dir);
463  const double u = prim.velocity[dir_v];
464 
465  const double gamma = euler::Gamma(eq, source);
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;
470  // const double gammaPlus = gamma + 1.0;
471 
472  struct UserData {
473  double rhoL;
474  double uL;
475  double pL;
476  double aL;
477  double gamma;
478  double gm1;
479  double gp1;
480  double gm1_over_gp1;
481  double oogm1;
482  double rhou_star;
483  } data;
484 
485  data.rhoL = rho;
486  data.uL = u;
487  data.pL = p;
488  data.aL = c;
489  data.gamma = gamma;
490  data.gm1 = gm1;
491  data.gp1 = gp1;
492  data.gamma = gamma;
493  data.gm1_over_gp1 = gm1_over_gp1;
494  data.oogm1 = oogm1;
495  data.rhou_star = required_massflow / relative_surface_area;
496 
497  // Toro p.122 f_L for the case: left wave is shock wave
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;
503  };
504 
505  // Toro p.122 eq(4.21) u_L for the case: left wave is shock wave
506  static auto u_shock = [](double p, const UserData& d) {
507  return d.uL - f_shock(p, d);
508  };
509 
510  // Toro p.122 eq(4.19) rho_L for the case: left wave is shock wave
511  static auto rho_shock = [](double p, const UserData& d) {
512  FUB_ASSERT(p > 0.0);
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;
516  FUB_ASSERT(denom > 0.0);
517  const double coeff = nom / denom;
518  return d.rhoL * coeff;
519  };
520 
521  // Toro p.123 f_L for the case: left wave is rarefaction wave
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);
527  };
528 
529  // Toro p.123 eq(4.26) u_L for the case: left wave is rarefaction wave
530  static auto u_rarefaction = [](double p, const UserData& d) {
531  return d.uL - f_rarefaction(p, d);
532  };
533 
534  // Toro p.123 eq(4.23) rho_L for the case: left wave is rarefaction wave
535  static auto rho_rarefaction = [](double p, const UserData& d) {
536  FUB_ASSERT(p > 0.0);
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);
540  };
541 
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;
546  };
547 
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;
552  };
553 
554  auto delta_rhou = [&data](double p) {
555  if (p < data.pL) {
556  return delta_rhou_rarefaction(p, data);
557  } else {
558  return delta_rhou_shock(p, data);
559  }
560  };
561 
562  auto FindLowerAndUpperBounds = [&] {
563  using Result = std::optional<std::pair<double, double>>;
564  double p0 = data.pL;
565  double upper = p0;
566  double lower = p0;
567  double drhou = delta_rhou(p0);
568  int counter = 12;
569  if (drhou < 0.0) {
570  do {
571  lower *= 0.5;
572  if (counter-- == 0) {
573  return Result{};
574  }
575  drhou = delta_rhou(lower);
576  } while (drhou < 0.0);
577  } else {
578  do {
579  upper *= 2.0;
580  if (counter-- == 0) {
581  return Result{};
582  }
583  drhou = delta_rhou(upper);
584  } while (drhou > 0.0);
585  }
586  return Result{std::pair{lower, upper}};
587  };
588 
589  // Attempt to find lower and upper bounds for the bisection
590  std::optional bounds = FindLowerAndUpperBounds();
591  if (bounds) {
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; // TODO: options_.max_iter;
597  auto [lo, hi] =
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;
607  CompleteFromPrim(eq, expanded, prim);
608  } else {
609  throw std::runtime_error("No alternative is currently implemented.");
610  }
611 }
612 
613 } // namespace fub
614 
615 #endif // !FUB_AMREX_CUTCELL_TURBINE_MASS_FLOW_BOUNDARY_HPP
#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
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