Loading [MathJax]/extensions/tex2jax.js
Finite Volume Solver  prototype
A framework to build finite volume solvers for the AG Klein at the Freie Universität Berlin.
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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