DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
EulerBC.hpp
Go to the documentation of this file.
1/** @file EulerBC.hpp
2 * @brief Boundary condition types, handlers, integration recorders, and 1-D profile
3 * utilities for the compressible Euler/Navier-Stokes solver.
4 *
5 * Provides:
6 * - EulerBCType enumeration of supported boundary condition kinds.
7 * - BoundaryHandler: per-zone BC storage with JSON (de)serialization.
8 * - IntegrationRecorder: MPI-reduced boundary integral accumulator.
9 * - AnchorPointRecorder: MPI_MINLOC anchor-point finder.
10 * - OutputPicker: registry mapping field names to cell-scalar lambdas.
11 * - OneDimProfile: 1-D profile with uniform/tanh nodes, cell-interval
12 * accumulation, MPI reduction, interpolation, and CSV output.
13 */
14#pragma once
15#include "Euler.hpp"
17#include "Geom/Grid.hpp"
18
19#include <unordered_map>
20
21#include "DNDS/JsonUtil.hpp"
22#include "DNDS/ConfigEnum.hpp"
23
24namespace DNDS::Euler
25{
26 /**
27 * @brief Boundary condition type identifiers for compressible flow solvers.
28 *
29 * Each enumerator selects the physics applied at a boundary zone:
30 * - Far-field and inflow/outflow conditions use characteristic or prescribed states.
31 * - Wall conditions enforce no-penetration (inviscid) or no-slip (viscous) constraints.
32 * - Special conditions implement test-case-specific setups (Riemann problems, DMR,
33 * isentropic vortex, Rayleigh–Taylor).
34 */
36 {
37 BCUnknown = 0, ///< Uninitialized / invalid sentinel.
38 BCFar, ///< Far-field (characteristic-based) boundary.
39 BCWall, ///< No-slip viscous wall boundary.
40 BCWallInvis, ///< Inviscid slip wall boundary.
41 BCWallIsothermal, ///< Isothermal wall; requires wall temperature in the BC value vector.
42 BCOut, ///< Supersonic outflow (extrapolation) boundary.
43 BCOutP, ///< Back-pressure (subsonic) outflow boundary.
44 BCIn, ///< Supersonic inflow (fully prescribed state) boundary.
45 BCInPsTs, ///< Subsonic inflow with prescribed total pressure and temperature.
46 BCSym, ///< Symmetry plane boundary.
47 BCSpecial, ///< Test-case-specific boundary (Riemann, DMR, isentropic vortex, RT).
48 };
49
52 {
53 {BCUnknown, nullptr},
54 {BCFar, "BCFar"},
55 {BCWall, "BCWall"},
56 {BCWallIsothermal, "BCWallIsothermal"},
57 {BCWallInvis, "BCWallInvis"},
58 {BCOut, "BCOut"},
59 {BCOutP, "BCOutP"},
60 {BCIn, "BCIn"},
61 {BCInPsTs, "BCInPsTs"},
62 {BCSym, "BCSym"},
63 {BCSpecial, "BCSpecial"},
64 })
65
66 /**
67 * @brief Convert an EulerBCType enumerator to its JSON string representation.
68 * @param type The boundary condition type to convert.
69 * @return A human-readable string such as "BCFar", "BCWall", etc.
70 */
71 inline std::string to_string(EulerBCType type)
72 {
73 return nlohmann::json(type).get<std::string>();
74 }
75
76 /// @brief Build a JSON Schema (draft-07) for the bcSettings array.
77 ///
78 /// Each BC entry is a discriminated union keyed on `"type"`. The schema
79 /// uses `oneOf` with `"const"` on the discriminator so that IDEs can
80 /// provide type-specific autocomplete.
81 ///
82 /// There are three groups:
83 /// - Flow BCs (BCFar, BCOut, BCOutP, BCIn, BCInPsTs):
84 /// required: type, name, value
85 /// optional: frameOption, anchorOption, integrationOption, valueExtra
86 /// - Wall BCs (BCWall, BCWallInvis, BCWallIsothermal, BCSpecial):
87 /// required: type, name
88 /// optional: value, frameOption, integrationOption, specialOption, valueExtra
89 /// (value is required for BCWallIsothermal)
90 /// - Symmetry (BCSym):
91 /// required: type, name
92 /// optional: rectifyOption, integrationOption, valueExtra
93 ///
94 /// @return An `ordered_json` object representing the JSON Schema array definition
95 /// suitable for validating a user-supplied boundary condition configuration.
96 inline nlohmann::ordered_json bcSettingsSchema()
97 {
98 using json = nlohmann::ordered_json;
99
100 auto numArray = []() -> json
101 {
102 json s;
103 s["type"] = "array";
104 s["items"] = json{{"type", "number"}};
105 return s;
106 };
107 auto intProp = [](const char *desc) -> json
108 {
109 return json{{"type", "integer"}, {"description", desc}, {"default", 0}};
110 };
111
112 // -- Helper: build a single variant schema for one or more BC type strings.
113 auto makeVariant = [&](std::vector<std::string> types,
114 const char *groupDesc,
115 json extraProps,
116 std::vector<std::string> required) -> json
117 {
118 json s;
119 s["type"] = "object";
120 s["description"] = groupDesc;
121
122 json typeSchema;
123 if (types.size() == 1)
124 typeSchema = json{{"const", types[0]}};
125 else
126 typeSchema = json{{"enum", types}};
127 typeSchema["description"] = "Boundary condition type";
128
129 json props;
130 props["type"] = typeSchema;
131 props["name"] = json{{"type", "string"}, {"description", "Boundary zone name"}};
132 for (auto &[k, v] : extraProps.items())
133 props[k] = v;
134
135 s["properties"] = props;
136 s["required"] = required;
137 return s;
138 };
139
140 // --- Flow BCs: BCFar, BCOut, BCOutP, BCIn, BCInPsTs ---
141 auto flowTypes = std::vector<std::string>{"BCFar", "BCOut", "BCOutP", "BCIn", "BCInPsTs"};
142 json flowProps;
143 flowProps["value"] = numArray();
144 flowProps["value"]["description"] = "BC state vector (size = nVars)";
145 flowProps["frameOption"] = intProp("Reference frame option");
146 flowProps["anchorOption"] = intProp("Anchor point option");
147 flowProps["integrationOption"] = intProp("Integration option");
148 flowProps["valueExtra"] = numArray();
149 flowProps["valueExtra"]["description"] = "Extra BC values (e.g. anchor coordinates)";
150 // Emit one variant per type for best IDE discrimination.
151 json oneOf = json::array();
152 for (auto &t : flowTypes)
153 {
154 oneOf.push_back(makeVariant({t}, ("Flow BC: " + t).c_str(),
155 flowProps,
156 {"type", "name", "value"}));
157 }
158
159 // --- Wall BCs: BCWall, BCWallInvis, BCWallIsothermal, BCSpecial ---
160 json wallProps;
161 wallProps["value"] = numArray();
162 wallProps["value"]["description"] = "BC state vector (optional except for BCWallIsothermal)";
163 wallProps["frameOption"] = intProp("Reference frame option");
164 wallProps["integrationOption"] = intProp("Integration option");
165 wallProps["specialOption"] = intProp("Special BC sub-type option");
166 wallProps["valueExtra"] = numArray();
167 wallProps["valueExtra"]["description"] = "Extra BC values";
168 auto wallTypes = std::vector<std::string>{"BCWall", "BCWallInvis", "BCWallIsothermal", "BCSpecial"};
169 for (auto &t : wallTypes)
170 {
171 auto req = std::vector<std::string>{"type", "name"};
172 if (t == "BCWallIsothermal")
173 req.push_back("value"); // value is required for isothermal
174 oneOf.push_back(makeVariant({t}, ("Wall BC: " + t).c_str(),
175 wallProps, req));
176 }
177
178 // --- Symmetry: BCSym ---
179 json symProps;
180 symProps["rectifyOption"] = intProp("Symmetry plane rectification option");
181 symProps["integrationOption"] = intProp("Integration option");
182 symProps["valueExtra"] = numArray();
183 symProps["valueExtra"]["description"] = "Extra BC values";
184 oneOf.push_back(makeVariant({"BCSym"}, "Symmetry BC",
185 symProps, {"type", "name"}));
186
187 // --- Assemble array schema ---
188 json schema;
189 schema["type"] = "array";
190 schema["description"] = "Boundary condition settings (per-BC array)";
191 schema["items"] = json{{"oneOf", oneOf}};
192 return schema;
193 }
194
195 /**
196 * @brief Per-zone boundary condition handler for Euler/Navier-Stokes solvers.
197 *
198 * Stores the BC type, state vector (value), option flags, and extra values
199 * for every boundary zone in the mesh. Zones are identified by name; the
200 * internal @c name2ID map translates mesh zone names to sequential BC indices.
201 *
202 * Default zones (far-field, wall, special test-case walls, etc.) are
203 * pre-populated during construction from Geom::GetFaceName2IDDefault().
204 *
205 * Supports JSON round-trip serialization via ADL `from_json` / `to_json`.
206 *
207 * @tparam model The EulerModel tag that determines nVarsFixed and the
208 * conservative variable layout.
209 */
210 template <EulerModel model>
212 {
213 int nVars; ///< Number of conservative variables (runtime).
214
215 public:
216 static const int nVarsFixed = getnVarsFixed(model); ///< Compile-time variable count (or Eigen::Dynamic).
217 using TU_R = Eigen::Vector<real, nVarsFixed>; ///< Fixed-size (read-only) state vector type.
218 using TU = Eigen::VectorFMTSafe<real, nVarsFixed>; ///< fmt-printable state vector type.
219 using TFlags = std::map<std::string, uint32_t>; ///< Per-zone option flag map (key → integer flag).
220
221 private:
222 std::vector<TU> BCValues; ///< State vectors for each BC zone.
223 std::vector<EulerBCType> BCTypes; ///< BC type for each zone.
224 std::vector<TFlags> BCFlags; ///< Option flags for each zone.
225 std::vector<Eigen::Vector<real, Eigen::Dynamic>> BCValuesExtra; ///< Extra BC data (e.g. anchor coordinates).
226 std::unordered_map<std::string, Geom::t_index> name2ID; ///< Zone name → internal BC index.
227 std::unordered_map<Geom::t_index, std::string> ID2name; ///< Internal BC index → zone name (reverse map).
228
229 public:
230 /**
231 * @brief Construct a BoundaryHandler and pre-populate default BC zones.
232 *
233 * Allocates storage for Geom::BC_ID_DEFAULT_MAX zones with uninitialized
234 * values and populates the default zone names and types (far-field, wall,
235 * special test-case boundaries) from Geom::GetFaceName2IDDefault().
236 *
237 * @param _nVars Number of conservative variables (must match model).
238 */
239 BoundaryHandler(int _nVars) : nVars(_nVars)
240 {
241 BCValues.resize(Geom::BC_ID_DEFAULT_MAX);
242 for (auto &v : BCValues)
243 v.setConstant(UnInitReal);
244 BCValuesExtra.resize(Geom::BC_ID_DEFAULT_MAX);
245 for (auto &v : BCValuesExtra)
246 v.setConstant(UnInitReal);
247 BCFlags.resize(Geom::BC_ID_DEFAULT_MAX);
248 name2ID = Geom::GetFaceName2IDDefault();
249 BCTypes.resize(Geom::BC_ID_DEFAULT_MAX, BCUnknown);
250 BCTypes[Geom::BC_ID_DEFAULT_FAR] = BCFar;
251 BCTypes[Geom::BC_ID_DEFAULT_SPECIAL_2DRiemann_FAR] = BCSpecial;
252 BCTypes[Geom::BC_ID_DEFAULT_SPECIAL_DMR_FAR] = BCSpecial;
253 BCTypes[Geom::BC_ID_DEFAULT_SPECIAL_IV_FAR] = BCSpecial;
254 BCTypes[Geom::BC_ID_DEFAULT_SPECIAL_RT_FAR] = BCSpecial;
255 BCTypes[Geom::BC_ID_DEFAULT_WALL] = BCWall;
256 BCTypes[Geom::BC_ID_DEFAULT_WALL_INVIS] = BCWallInvis;
257 BCTypes[Geom::BC_ID_NULL] = BCUnknown;
258 RenewID2name();
259 }
260
261 /**
262 * @brief Return the names of all registered BC zones, ordered by internal ID.
263 * @return Vector of zone name strings.
264 */
265 std::vector<std::string> GetAllNames()
266 {
267 std::vector<std::string> ret;
268 for (Geom::t_index i = 0; i < BCTypes.size(); i++)
269 if (ID2name.count(i))
270 ret.push_back(ID2name.at(i));
271 return ret;
272 }
273
274 /// @brief Return the number of registered BC zones (including default slots).
276 {
277 return BCTypes.size();
278 }
279
280 /// @brief Rebuild the reverse map (ID2name) from the current name2ID map.
282 {
283 ID2name.clear();
284 for (auto &p : name2ID)
285 ID2name[p.second] = p.first;
286 }
287
288 using json = nlohmann::ordered_json;
289
290 /// @brief Push a new BC zone from a JSON sub-object (not yet implemented).
291 /// @param gS JSON object describing the boundary condition.
292 void PushBCWithJson(const json &gS)
293 {
294 // TODO
295 }
296
297 /**
298 * @brief Deserialize an array of BC entries from JSON into @p bc.
299 *
300 * Each JSON element must contain at least `"type"` and `"name"`. Additional
301 * keys depend on the BC group (flow, wall, symmetry). Validates that the
302 * value vector dimension matches @c nVars and that zone names are unique.
303 *
304 * @param j JSON array of BC objects.
305 * @param bc Target BoundaryHandler to populate.
306 */
307 friend void from_json(const json &j, BoundaryHandler<model> &bc)
308 {
309 DNDS_assert(j.is_array());
310 for (auto &item : j)
311 {
312 EulerBCType bcType = item["type"].get<EulerBCType>();
313 std::string bcName = item["name"];
314 bc.BCFlags.emplace_back();
315 switch (bcType)
316 {
322 {
323 uint32_t frameOption = 0;
324 uint32_t anchorOption = 0;
325 uint32_t integrationOption = 0;
326 Eigen::VectorXd bcValueExtra;
327 if (item.count("frameOption"))
328 frameOption = item["frameOption"];
329 if (item.count("anchorOption"))
330 anchorOption = item["anchorOption"];
331 if (item.count("integrationOption"))
332 integrationOption = item["integrationOption"];
333 if (item.count("valueExtra"))
334 bcValueExtra = item["valueExtra"];
335 Eigen::VectorXd bcValue = item["value"];
336 DNDS_assert_info(bcValue.size() == bc.nVars, fmt::format("[{}] bc value dim not right", bcName));
337 bc.BCValues.push_back(bcValue);
338 bc.BCFlags.back()["frameOpt"] = frameOption;
339 bc.BCFlags.back()["anchorOpt"] = anchorOption;
340 bc.BCFlags.back()["integrationOpt"] = integrationOption;
341 bc.BCValuesExtra.push_back(bcValueExtra);
342 }
343 break;
344
349 {
350 uint32_t frameOption = 0;
351 uint32_t integrationOption = 0;
352 uint32_t specialOption = 0;
353 Eigen::VectorXd bcValueExtra;
354 Eigen::VectorXd bcValue;
355 if (item.count("frameOption"))
356 frameOption = item["frameOption"];
357 if (item.count("integrationOption"))
358 integrationOption = item["integrationOption"];
359 if (item.count("valueExtra"))
360 bcValueExtra = item["valueExtra"];
361 if (item.count("value"))
362 {
363 bcValue = item["value"];
364 // std::cout << bcValue.transpose() << std::endl;
365 DNDS_assert_info(bcValue.size() == bc.nVars, fmt::format("[{}] bc value dim not right", bcName));
366 }
367 else
368 {
369 bcValue.setZero(bc.nVars);
370 if (bcType == EulerBCType::BCWallIsothermal)
371 DNDS_assert_info(false, "missing bc value for BCWallIsothermal");
372 }
373 if (item.count("specialOption"))
374 specialOption = item["specialOption"];
375 // DNDS_assert(false);
376 bc.BCValues.push_back(bcValue);
377 bc.BCFlags.back()["frameOpt"] = frameOption;
378 bc.BCFlags.back()["integrationOpt"] = integrationOption;
379 bc.BCFlags.back()["specialOpt"] = specialOption;
380 bc.BCValuesExtra.push_back(bcValueExtra);
381 }
382 break;
383
385 {
386 uint32_t rectifyOption = 0;
387 uint32_t integrationOption = 0;
388 Eigen::VectorXd bcValueExtra;
389 if (item.count("rectifyOption"))
390 rectifyOption = item["rectifyOption"];
391 if (item.count("integrationOption"))
392 integrationOption = item["integrationOption"];
393 if (item.count("valueExtra"))
394 bcValueExtra = item["valueExtra"];
395 bc.BCValues.push_back(TU::Zero(bc.nVars));
396 bc.BCFlags.back()["rectifyOpt"] = rectifyOption;
397 bc.BCFlags.back()["integrationOpt"] = integrationOption;
398 bc.BCValuesExtra.push_back(bcValueExtra);
399 }
400 break;
401
402 default:
403 DNDS_assert(false);
404 break;
405 }
406
407 bc.BCTypes.push_back(bcType);
408 DNDS_assert_info(bc.name2ID.count(bcName) == 0, "the bc names are duplicate");
409 bc.name2ID[bcName] = bc.BCTypes.size() - 1;
410
412 bc.BCFlags.size() == bc.BCTypes.size() &&
413 bc.BCValues.size() == bc.BCTypes.size() &&
414 bc.BCValuesExtra.size() == bc.BCTypes.size());
415 }
416 bc.RenewID2name();
417 }
418
419 /**
420 * @brief Serialize user-defined BC zones (those beyond the default slots) to JSON.
421 *
422 * Zones with index < Geom::BC_ID_DEFAULT_MAX are built-in defaults and are
423 * not emitted. The output is a JSON array suitable for round-trip with from_json().
424 *
425 * @param j Output JSON array.
426 * @param bc Source BoundaryHandler to serialize.
427 */
428 friend void to_json(json &j, const BoundaryHandler<model> &bc)
429 {
430 j = json::array();
431 for (Geom::t_index i = Geom::BC_ID_DEFAULT_MAX; i < bc.BCTypes.size(); i++)
432 {
433 json item;
434 EulerBCType bcType = bc.BCTypes[i];
435 item["type"] = bcType;
436 item["name"] = bc.ID2name.at(i);
437 item["__bcId"] = i; //! TODO: make bcId arbitrary not sequential?
438 switch (bcType)
439 {
445 {
446 item["value"] = static_cast<TU_R>(bc.BCValues.at(i)); // force begin() and end() to be exposed
447 item["frameOption"] = bc.BCFlags.at(i).at("frameOpt");
448 item["anchorOption"] = bc.BCFlags.at(i).at("anchorOpt");
449 item["valueExtra"] = bc.BCValuesExtra.at(i);
450 }
451 break;
452
457 {
458 item["frameOption"] = bc.BCFlags.at(i).at("frameOpt");
459 item["integrationOption"] = bc.BCFlags.at(i).at("integrationOpt");
460 item["specialOption"] = bc.BCFlags.at(i).at("specialOpt");
461 item["valueExtra"] = bc.BCValuesExtra.at(i);
462 item["value"] = static_cast<TU_R>(bc.BCValues.at(i));
463 }
464 break;
465
467 {
468 item["rectifyOption"] = bc.BCFlags.at(i).at("rectifyOpt");
469 item["valueExtra"] = bc.BCValuesExtra.at(i);
470 }
471 break;
472
473 default:
474 DNDS_assert(false);
475 break;
476 }
477 j.push_back(item);
478 }
479 }
480
481 /**
482 * @brief Look up the internal BC index for a given zone name.
483 *
484 * If periodic, the returned index is negative (by convention).
485 *
486 * @param name Mesh zone name string.
487 * @return The BC index, or Geom::BC_ID_NULL if the name is not registered.
488 */
489 Geom::t_index GetIDFromName(const std::string &name)
490 {
491 if (name2ID.count(name))
492 return name2ID[name];
493 else
494 return Geom::BC_ID_NULL;
495 }
496
497 /**
498 * @brief Look up the zone name for a given internal BC index.
499 * @param id Internal BC index.
500 * @return The zone name, or "UnNamedBC" if the index has no associated name.
501 */
503 {
504 if (!ID2name.count(id))
505 return std::string("UnNamedBC");
506 return ID2name.at(id);
507 }
508
509 /**
510 * @brief Retrieve the BC type for a given zone index.
511 * @param id Internal BC index.
512 * @return The EulerBCType, or BCUnknown if @p id is not an external BC face.
513 */
515 {
516 // std::cout << "id " << std::endl;
518 return BCUnknown;
519 return BCTypes.at(id);
520 }
521
522 /**
523 * @brief Retrieve the BC state vector for a given zone index.
524 * @param id Internal BC index.
525 * @return The state vector, or the default (index 0) vector if @p id is not an external BC.
526 */
528 {
530 return BCValues.at(0);
531 return BCValues.at(id);
532 }
533
534 /**
535 * @brief Retrieve the extra BC value vector for a given zone index.
536 * @param id Internal BC index.
537 * @return The extra value vector (e.g. anchor coordinates), or default if not an external BC.
538 */
539 Eigen::Vector<real, Eigen::Dynamic> GetValueExtraFromID(Geom::t_index id)
540 {
542 return BCValuesExtra.at(0);
543 return BCValuesExtra.at(id);
544 }
545
546 /**
547 * @brief Retrieve an option flag for a given zone index (strict).
548 *
549 * Aborts if @p key is not present in the zone's flag map.
550 *
551 * @param id Internal BC index.
552 * @param key Flag key string (e.g. "frameOpt", "anchorOpt").
553 * @return The flag value, or 0 if @p id is not an external BC face.
554 */
555 uint32_t GetFlagFromID(Geom::t_index id, const std::string &key)
556 {
558 return 0;
559 return BCFlags.at(id).at(key);
560 }
561
562 /**
563 * @brief Retrieve an option flag for a given zone index (lenient).
564 *
565 * Returns 0 instead of aborting when @p key is absent.
566 *
567 * @param id Internal BC index.
568 * @param key Flag key string.
569 * @return The flag value, or 0 if the key is missing or @p id is not external.
570 */
571 uint32_t GetFlagFromIDSoft(Geom::t_index id, const std::string &key)
572 {
573 if (!Geom::FaceIDIsExternalBC(id) || !BCFlags.at(id).count(key))
574 return 0;
575 return BCFlags.at(id).at(key);
576 }
577 };
578
579 /**
580 * @brief Accumulator for MPI-reduced boundary-face integrals.
581 *
582 * Collects integrated quantities (e.g. force components, mass flux)
583 * over boundary faces on each MPI rank, then performs an MPI_SUM
584 * Allreduce to obtain global totals. The divisor @c div is reduced
585 * in parallel and can be used for area-weighted averaging.
586 */
588 {
589 Eigen::Vector<real, Eigen::Dynamic> v; ///< Accumulated integral vector.
590 real div; ///< Accumulated divisor (area weight).
591 MPIInfo mpi; ///< MPI communicator info.
592
593 /**
594 * @brief Construct and zero-initialize the integration recorder.
595 * @param _nmpi MPI communicator wrapper.
596 * @param siz Length of the integral vector.
597 */
598 IntegrationRecorder(const MPIInfo &_nmpi, int siz) : mpi(_nmpi)
599 {
600 v.resize(siz);
601 v.setZero();
603 }
604
605 /// @brief Reset the integral vector and divisor to zero.
606 void Reset()
607 {
608 v.setZero();
610 }
611
612 /**
613 * @brief Accumulate a contribution from a single boundary face.
614 * @tparam TU Vector type compatible with Eigen addition.
615 * @param add Integral contribution vector to add.
616 * @param dAdd Corresponding divisor (area) contribution.
617 */
618 template <class TU>
619 void Add(TU &&add, real dAdd)
620 {
621 v += add;
622 div += dAdd;
623 }
624
625 /// @brief MPI Allreduce (SUM) both the integral vector and the divisor.
626 void Reduce()
627 {
628 // TODO: assure the consistency on different procs?
629 Eigen::Vector<real, Eigen::Dynamic> v0 = v;
630 MPI::Allreduce(v0.data(), v.data(), v.size(), DNDS_MPI_REAL, MPI_SUM, mpi.comm);
631 MPI::AllreduceOneReal(div, MPI_SUM, mpi);
632 }
633 };
634
635 /**
636 * @brief Finds the cell closest to a specified anchor point across all MPI ranks.
637 *
638 * Each rank calls AddAnchor() with candidate cells; ObtainAnchorMPI() then
639 * performs an MPI_MINLOC reduction to identify the globally closest cell and
640 * broadcasts its state vector to all ranks. Used for inlet/outlet reference
641 * state anchoring.
642 *
643 * @tparam nVarsFixed Compile-time number of conservative variables (or Eigen::Dynamic).
644 */
645 template <int nVarsFixed>
647 {
648 using TU = Eigen::Vector<real, nVarsFixed>; ///< State vector type.
649 MPIInfo mpi; ///< MPI communicator info.
650 TU val; ///< State vector of the closest cell found so far.
651 real dist{veryLargeReal}; ///< Distance to the closest cell found so far.
652
653 /// @brief Construct with MPI communicator.
654 /// @param _mpi MPI communicator wrapper.
655 AnchorPointRecorder(const MPIInfo &_mpi) : mpi(_mpi) {}
656
657 /// @brief Reset the recorded distance to veryLargeReal (invalidate current anchor).
659
660 /**
661 * @brief Submit a candidate anchor cell on this rank.
662 *
663 * Keeps the candidate only if @p nDist is less than the current best.
664 *
665 * @param vin State vector of the candidate cell.
666 * @param nDist Distance from the candidate cell center to the anchor point.
667 */
668 void AddAnchor(const TU &vin, real nDist)
669 {
670 if (nDist < dist)
671 dist = nDist, val = vin;
672 }
673
674 /**
675 * @brief Global MPI reduction to find the closest anchor across all ranks.
676 *
677 * Uses MPI_DOUBLE_INT + MPI_MINLOC to identify the rank holding the
678 * minimum distance, then broadcasts that rank's state vector to all ranks.
679 */
681 {
682 struct DI
683 {
684 double d;
685 MPI_int i;
686 };
687 union Doubleint
688 {
689 uint8_t pad[16];
690 DI dint;
691 };
692 Doubleint minDist, minDistall;
693 minDist.dint.d = dist;
694 minDist.dint.i = mpi.rank;
695 MPI::Allreduce(&minDist, &minDistall, 1, MPI_DOUBLE_INT, MPI_MINLOC, mpi.comm);
696 // std::cout << minDistall.dint.d << std::endl;
697 MPI::Bcast(val.data(), val.size(), DNDS_MPI_REAL, minDistall.dint.i, mpi.comm);
698 }
699 };
700
701 /// @brief Function type returning a scalar value for a given cell index [0, NumCell).
702 using tCellScalarFGet = std::function<real(
703 index // iCell which is [0, mesh->NumCell)
704 )>;
705 /// @brief List of (field_name, getter_function) pairs for cell-scalar output.
706 using tCellScalarList = std::vector<std::tuple<std::string, const tCellScalarFGet>>;
707
708 /**
709 * @brief Registry that maps named cell-scalar output fields to getter lambdas.
710 *
711 * Used to select which derived quantities (e.g. Mach number, pressure
712 * coefficient) are written to output files. Call setMap() to register all
713 * available fields, then getSubsetList() to extract a user-requested subset.
714 */
716 {
717 public:
718 using tMap = std::map<std::string, tCellScalarFGet>; ///< Name-to-getter mapping type.
719
720 private:
721 tMap cellOutRegMap; ///< Registered cell-scalar output fields.
722
723 public:
724 /// @brief Register (or replace) the full set of available output fields.
725 /// @param v Map of field names to their getter functions.
726 void setMap(const tMap &v)
727 {
728 cellOutRegMap = v;
729 }
730
731 /**
732 * @brief Extract a subset of registered output fields by name.
733 *
734 * Asserts that every requested name exists in the registry.
735 *
736 * @param names Ordered list of field names to extract.
737 * @return A tCellScalarList with the requested (name, getter) pairs.
738 */
739 tCellScalarList getSubsetList(const std::vector<std::string> &names)
740 {
741 tCellScalarList ret;
742 for (auto &name : names)
743 {
744 DNDS_assert(cellOutRegMap.count(name) == 1);
745 ret.push_back(std::make_tuple(
746 name,
747 cellOutRegMap[name]));
748 }
749 return ret;
750 }
751 };
752
753 /**
754 * @brief One-dimensional profile for inlet/outlet boundary data.
755 *
756 * Discretizes a 1-D coordinate range into cells delimited by @c nodes.
757 * Supports uniform and tanh (bilateral clustering) node distributions.
758 * Cell values are accumulated via AddSimpleInterval(), reduced across MPI
759 * ranks with Reduce(), then queried with Get() or interpolated with
760 * GetPlain(). The profile can be written to CSV with OutProfileCSV().
761 *
762 * The number of cells equals `nodes.size() - 1`.
763 *
764 * @tparam nVarsFixed Compile-time number of state variables (or Eigen::Dynamic).
765 */
766 template <int nVarsFixed>
768 {
769 MPIInfo mpi; ///< MPI communicator info.
770 Eigen::Vector<real, Eigen::Dynamic> nodes; ///< Node coordinates (size = nCells + 1).
771 Eigen::Matrix<real, nVarsFixed, Eigen::Dynamic> v; ///< Accumulated cell values (nVars × nCells).
772 Eigen::RowVector<real, Eigen::Dynamic> div; ///< Per-cell divisor (area weight).
773
774 /// @brief Construct with MPI communicator (no allocation yet).
775 /// @param _mpi MPI communicator wrapper.
776 OneDimProfile(const MPIInfo &_mpi) : mpi(_mpi) {}
777
778 /// @brief Sort node coordinates in ascending order.
780 {
781 std::sort(nodes.begin(), nodes.end());
782 }
783
784 /// @brief Return the number of cells (= nodes.size() - 1).
785 index Size() const
786 {
787 return nodes.size() - 1;
788 }
789
790 /// @brief Return the length of cell @p i (= nodes[i+1] - nodes[i]).
791 /// @param i Cell index in [0, Size()).
793 {
794 return nodes[i + 1] - nodes[i];
795 }
796
797 /**
798 * @brief Allocate a uniformly spaced profile.
799 * @param size Number of cells.
800 * @param nvars Number of state variables.
801 * @param minV Left coordinate bound.
802 * @param maxV Right coordinate bound.
803 */
804 void GenerateUniform(index size, int nvars, real minV, real maxV)
805 {
806 nodes.setLinSpaced(size + 1, minV, maxV);
807 v.resize(nvars, size);
808 div.resize(size);
809 }
810
811 /**
812 * @brief Allocate a tanh-clustered (bilateral) profile.
813 * @param size Number of cells.
814 * @param nvars Number of state variables.
815 * @param minV Left coordinate bound.
816 * @param maxV Right coordinate bound.
817 * @param d0 First cell width controlling clustering intensity.
818 */
819 void GenerateTanh(index size, int nvars, real minV, real maxV, real d0)
820 {
821 Geom::GetTanhDistributionBilateral(minV, maxV, size, d0, nodes);
822 // if (MPIWorldRank() == 0)
823 // std::cout << std::setprecision(12) << nodes.transpose() << std::endl;
824 v.resize(nvars, size);
825 div.resize(size);
826 }
827
828 /// @brief Zero both the value matrix and the divisor row vector.
829 void SetZero()
830 {
831 v.setZero();
832 div.setZero();
833 }
834
835 /**
836 * @brief Accumulate a cell-mean value over the interval [lV, rV].
837 *
838 * Distributes @p vmean weighted by @p divV across every profile cell that
839 * overlaps [lV, rV], proportional to the overlap fraction.
840 *
841 * @tparam TU Vector type compatible with scalar multiplication and Eigen addition.
842 * @param vmean Mean state vector for the contributing interval.
843 * @param divV Divisor (area / weight) for the contributing interval.
844 * @param lV Left coordinate of the contributing interval.
845 * @param rV Right coordinate of the contributing interval.
846 */
847 template <class TU>
848 void AddSimpleInterval(TU vmean, real divV, real lV, real rV)
849 {
850 index iCL = (std::lower_bound(nodes.begin(), nodes.end(), lV) - nodes.begin()) - index(1);
851 iCL = std::min(Size() - 1, std::max(index(0), iCL));
852 index iCR = std::upper_bound(nodes.begin(), nodes.end(), rV) - nodes.begin(); // max is Size() + 1
853 iCR = std::min(Size(), iCR);
854 // std::cout << iCL << " " << iCR << " " << lV << " " << rV << std::endl;
855 for (index i = iCL; i < iCR; i++)
856 {
857 real cL = nodes[i];
858 real cR = nodes[i + 1];
859 real cinIntervalL = std::max(lV, cL);
860 real cinInvervalR = std::min(rV, cR);
861 real cinInvervalLenRel = std::max(cinInvervalR - cinIntervalL, 0.0) / (rV - lV);
862 div[i] += divV * cinInvervalLenRel;
863 v(EigenAll, i) += vmean * divV * cinInvervalLenRel;
864 }
865 }
866
867 /// @brief MPI Allreduce (SUM) the value matrix and divisor across all ranks.
868 void Reduce()
869 {
870 // TODO: assure the consistency on different procs?
871 Eigen::RowVector<real, Eigen::Dynamic> div0 = div;
872 Eigen::Matrix<real, nVarsFixed, Eigen::Dynamic> v0 = v;
873 MPI::Allreduce(div0.data(), div.data(), div.size(), DNDS_MPI_REAL, MPI_SUM, mpi.comm);
874 MPI::Allreduce(v0.data(), v.data(), v.size(), DNDS_MPI_REAL, MPI_SUM, mpi.comm);
875 }
876
877 /**
878 * @brief Retrieve the averaged value for cell @p i.
879 *
880 * Returns `v(:, i) / div(i)`, with a tiny epsilon to avoid division by zero.
881 *
882 * @param i Cell index in [0, Size()).
883 * @return The area-weighted average state vector for cell @p i.
884 */
885 Eigen::Vector<real, nVarsFixed> Get(index i) const
886 {
887 DNDS_assert(i < Size());
888 return v(EigenAll, i) / (div(i) + verySmallReal);
889 }
890
891 /**
892 * @brief Linearly interpolate the profile at coordinate @p v.
893 *
894 * Locates the cell containing @p v, then blends the averaged values of that
895 * cell and its neighbors to produce a smooth interpolation.
896 *
897 * @param v Coordinate at which to evaluate the profile.
898 * @return Interpolated state vector.
899 */
900 Eigen::Vector<real, nVarsFixed> GetPlain(real v) const
901 {
902 index iCL = (std::lower_bound(nodes.begin(), nodes.end(), v) - nodes.begin()) - index(1);
903 iCL = std::min(Size() - 1, std::max(index(0), iCL));
904 real vL = nodes[iCL];
905 real vR = nodes[iCL + 1];
906 real vRel = (v - vL) / (vR - vL + verySmallReal);
907 vRel = std::min(vRel, 1.);
908 vRel = std::max(vRel, 0.);
909 Eigen::Vector<real, nVarsFixed> valL = Get(std::max(iCL - 1, index(0)));
910 Eigen::Vector<real, nVarsFixed> valR = Get(std::min(iCL + 1, Size() - 1));
911 valL = 0.5 * (valL + Get(iCL));
912 valR = 0.5 * (valR + Get(iCL));
913 return vRel * valR + (1 - vRel) * valL;
914 }
915
916 /**
917 * @brief Write the profile to a CSV stream after a valid MPI reduction.
918 *
919 * Should be called on a single MPI rank. Outputs one row per node
920 * coordinate with interpolated values.
921 *
922 * @param o Output stream to write CSV data to.
923 * @param title If true, write a CSV header row first.
924 * @param precision Floating-point precision for std::scientific output.
925 */
926 void OutProfileCSV(std::ostream &o, bool title = true, int precision = 16)
927 {
928 if (title)
929 {
930 o << "X";
931 for (index i = 0; i < v.rows(); i++)
932 o << ", U" << std::to_string(i);
933 o << "\n";
934 }
935 o << std::scientific << std::setprecision(precision);
936 for (index iN = 0; iN < v.cols(); iN++)
937 {
938 o << nodes[iN];
939 auto val = GetPlain(nodes[iN]);
940 for (index i = 0; i < val.size(); i++)
941 o << ", " << val[i];
942 o << "\n";
943 }
944 }
945 };
946
947}
Extended enum-to-JSON serialization macro that also exposes allowed string values for JSON Schema gen...
#define DNDS_DEFINE_ENUM_JSON(EnumType_,...)
Define JSON serialization for an enum AND expose its allowed string values.
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:113
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:108
Core type definitions, enumerations, and distributed array wrappers for compressible Navier-Stokes / ...
JSON-to-Eigen conversion utilities and nlohmann_json helper macros.
Per-zone boundary condition handler for Euler/Navier-Stokes solvers.
Definition EulerBC.hpp:212
nlohmann::ordered_json json
Definition EulerBC.hpp:288
Geom::t_index GetIDFromName(const std::string &name)
Look up the internal BC index for a given zone name.
Definition EulerBC.hpp:489
void RenewID2name()
Rebuild the reverse map (ID2name) from the current name2ID map.
Definition EulerBC.hpp:281
auto GetNameFormID(Geom::t_index id)
Look up the zone name for a given internal BC index.
Definition EulerBC.hpp:502
Eigen::Vector< real, Eigen::Dynamic > GetValueExtraFromID(Geom::t_index id)
Retrieve the extra BC value vector for a given zone index.
Definition EulerBC.hpp:539
TU GetValueFromID(Geom::t_index id)
Retrieve the BC state vector for a given zone index.
Definition EulerBC.hpp:527
std::vector< std::string > GetAllNames()
Return the names of all registered BC zones, ordered by internal ID.
Definition EulerBC.hpp:265
EulerBCType GetTypeFromID(Geom::t_index id)
Retrieve the BC type for a given zone index.
Definition EulerBC.hpp:514
Geom::t_index size()
Return the number of registered BC zones (including default slots).
Definition EulerBC.hpp:275
Eigen::Vector< real, nVarsFixed > TU_R
Fixed-size (read-only) state vector type.
Definition EulerBC.hpp:217
void PushBCWithJson(const json &gS)
Push a new BC zone from a JSON sub-object (not yet implemented).
Definition EulerBC.hpp:292
static const int nVarsFixed
Compile-time variable count (or Eigen::Dynamic).
Definition EulerBC.hpp:216
uint32_t GetFlagFromIDSoft(Geom::t_index id, const std::string &key)
Retrieve an option flag for a given zone index (lenient).
Definition EulerBC.hpp:571
friend void to_json(json &j, const BoundaryHandler< model > &bc)
Serialize user-defined BC zones (those beyond the default slots) to JSON.
Definition EulerBC.hpp:428
uint32_t GetFlagFromID(Geom::t_index id, const std::string &key)
Retrieve an option flag for a given zone index (strict).
Definition EulerBC.hpp:555
BoundaryHandler(int _nVars)
Construct a BoundaryHandler and pre-populate default BC zones.
Definition EulerBC.hpp:239
std::map< std::string, uint32_t > TFlags
Per-zone option flag map (key → integer flag).
Definition EulerBC.hpp:219
friend void from_json(const json &j, BoundaryHandler< model > &bc)
Deserialize an array of BC entries from JSON into bc.
Definition EulerBC.hpp:307
Registry that maps named cell-scalar output fields to getter lambdas.
Definition EulerBC.hpp:716
tCellScalarList getSubsetList(const std::vector< std::string > &names)
Extract a subset of registered output fields by name.
Definition EulerBC.hpp:739
void setMap(const tMap &v)
Register (or replace) the full set of available output fields.
Definition EulerBC.hpp:726
std::map< std::string, tCellScalarFGet > tMap
Name-to-getter mapping type.
Definition EulerBC.hpp:718
std::vector< std::tuple< std::string, const tCellScalarFGet > > tCellScalarList
List of (field_name, getter_function) pairs for cell-scalar output.
Definition EulerBC.hpp:706
std::function< real(index)> tCellScalarFGet
Function type returning a scalar value for a given cell index [0, NumCell).
Definition EulerBC.hpp:704
nlohmann::ordered_json bcSettingsSchema()
Build a JSON Schema (draft-07) for the bcSettings array.
Definition EulerBC.hpp:96
EulerBCType
Boundary condition type identifiers for compressible flow solvers.
Definition EulerBC.hpp:36
@ BCWallIsothermal
Isothermal wall; requires wall temperature in the BC value vector.
Definition EulerBC.hpp:41
@ BCWall
No-slip viscous wall boundary.
Definition EulerBC.hpp:39
@ BCWallInvis
Inviscid slip wall boundary.
Definition EulerBC.hpp:40
@ BCSpecial
Test-case-specific boundary (Riemann, DMR, isentropic vortex, RT).
Definition EulerBC.hpp:47
@ BCOut
Supersonic outflow (extrapolation) boundary.
Definition EulerBC.hpp:42
@ BCUnknown
Uninitialized / invalid sentinel.
Definition EulerBC.hpp:37
@ BCInPsTs
Subsonic inflow with prescribed total pressure and temperature.
Definition EulerBC.hpp:45
@ BCSym
Symmetry plane boundary.
Definition EulerBC.hpp:46
@ BCFar
Far-field (characteristic-based) boundary.
Definition EulerBC.hpp:38
@ BCOutP
Back-pressure (subsonic) outflow boundary.
Definition EulerBC.hpp:43
@ BCIn
Supersonic inflow (fully prescribed state) boundary.
Definition EulerBC.hpp:44
int32_t t_index
Definition Geometric.hpp:6
DNDS_DEVICE_CALLABLE bool FaceIDIsExternalBC(t_index id)
void GetTanhDistributionBilateral(real x0, real x1, index NInterval, real d0, Eigen::Vector< real, Eigen::Dynamic > &d)
Definition Grid.hpp:7
auto GetFaceName2IDDefault()
MPI_int Bcast(void *buf, MPI_int num, MPI_Datatype type, MPI_int source_rank, MPI_Comm comm)
dumb wrapper
Definition MPI.cpp:150
void AllreduceOneReal(real &v, MPI_Op op, const MPIInfo &mpi)
Single-scalar Allreduce helper for reals (in-place, count = 1).
Definition MPI.hpp:673
MPI_int Allreduce(const void *sendbuf, void *recvbuf, MPI_int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
Wrapper over MPI_Allreduce.
Definition MPI.cpp:203
DNDS_CONSTANT const real verySmallReal
Catch-all lower bound ("effectively zero").
Definition Defines.hpp:194
DNDS_CONSTANT const real UnInitReal
Sentinel "not initialised" real value (NaN). Cheap to detect with std::isnan or IsUnInitReal; survive...
Definition Defines.hpp:174
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:107
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Definition Defines.hpp:105
std::string to_string(const Eigen::DenseBase< dir > &v, int precision=5, bool scientific=false)
Render an Eigen::DenseBase to a string via operator<<.
Definition EigenUtil.hpp:29
DNDS_CONSTANT const real veryLargeReal
Catch-all upper bound ("practically infinity") for physical scalars.
Definition Defines.hpp:190
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:43
const MPI_Datatype DNDS_MPI_REAL
MPI datatype matching real (= MPI_REAL8).
Definition MPI.hpp:92
Finds the cell closest to a specified anchor point across all MPI ranks.
Definition EulerBC.hpp:647
void ObtainAnchorMPI()
Global MPI reduction to find the closest anchor across all ranks.
Definition EulerBC.hpp:680
MPIInfo mpi
MPI communicator info.
Definition EulerBC.hpp:649
TU val
State vector of the closest cell found so far.
Definition EulerBC.hpp:650
Eigen::Vector< real, nVarsFixed > TU
State vector type.
Definition EulerBC.hpp:648
AnchorPointRecorder(const MPIInfo &_mpi)
Construct with MPI communicator.
Definition EulerBC.hpp:655
void AddAnchor(const TU &vin, real nDist)
Submit a candidate anchor cell on this rank.
Definition EulerBC.hpp:668
void Reset()
Reset the recorded distance to veryLargeReal (invalidate current anchor).
Definition EulerBC.hpp:658
real dist
Distance to the closest cell found so far.
Definition EulerBC.hpp:651
Accumulator for MPI-reduced boundary-face integrals.
Definition EulerBC.hpp:588
void Add(TU &&add, real dAdd)
Accumulate a contribution from a single boundary face.
Definition EulerBC.hpp:619
void Reset()
Reset the integral vector and divisor to zero.
Definition EulerBC.hpp:606
void Reduce()
MPI Allreduce (SUM) both the integral vector and the divisor.
Definition EulerBC.hpp:626
real div
Accumulated divisor (area weight).
Definition EulerBC.hpp:590
MPIInfo mpi
MPI communicator info.
Definition EulerBC.hpp:591
Eigen::Vector< real, Eigen::Dynamic > v
Accumulated integral vector.
Definition EulerBC.hpp:589
IntegrationRecorder(const MPIInfo &_nmpi, int siz)
Construct and zero-initialize the integration recorder.
Definition EulerBC.hpp:598
One-dimensional profile for inlet/outlet boundary data.
Definition EulerBC.hpp:768
Eigen::Vector< real, nVarsFixed > GetPlain(real v) const
Linearly interpolate the profile at coordinate v.
Definition EulerBC.hpp:900
void GenerateTanh(index size, int nvars, real minV, real maxV, real d0)
Allocate a tanh-clustered (bilateral) profile.
Definition EulerBC.hpp:819
real Len(index i)
Return the length of cell i (= nodes[i+1] - nodes[i]).
Definition EulerBC.hpp:792
void SortNodes()
Sort node coordinates in ascending order.
Definition EulerBC.hpp:779
Eigen::Vector< real, nVarsFixed > Get(index i) const
Retrieve the averaged value for cell i.
Definition EulerBC.hpp:885
OneDimProfile(const MPIInfo &_mpi)
Construct with MPI communicator (no allocation yet).
Definition EulerBC.hpp:776
Eigen::Vector< real, Eigen::Dynamic > nodes
Node coordinates (size = nCells + 1).
Definition EulerBC.hpp:770
Eigen::RowVector< real, Eigen::Dynamic > div
Per-cell divisor (area weight).
Definition EulerBC.hpp:772
MPIInfo mpi
MPI communicator info.
Definition EulerBC.hpp:769
void GenerateUniform(index size, int nvars, real minV, real maxV)
Allocate a uniformly spaced profile.
Definition EulerBC.hpp:804
void OutProfileCSV(std::ostream &o, bool title=true, int precision=16)
Write the profile to a CSV stream after a valid MPI reduction.
Definition EulerBC.hpp:926
void Reduce()
MPI Allreduce (SUM) the value matrix and divisor across all ranks.
Definition EulerBC.hpp:868
Eigen::Matrix< real, nVarsFixed, Eigen::Dynamic > v
Accumulated cell values (nVars × nCells).
Definition EulerBC.hpp:771
void AddSimpleInterval(TU vmean, real divV, real lV, real rV)
Accumulate a cell-mean value over the interval [lV, rV].
Definition EulerBC.hpp:848
void SetZero()
Zero both the value matrix and the divisor row vector.
Definition EulerBC.hpp:829
index Size() const
Return the number of cells (= nodes.size() - 1).
Definition EulerBC.hpp:785
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:215
int rank
This rank's 0-based index within comm (-1 until initialised).
Definition MPI.hpp:219
MPI_Comm comm
The underlying MPI communicator handle.
Definition MPI.hpp:217
Eigen::Matrix wrapper that hides begin/end from fmt.
Definition EigenUtil.hpp:62
Eigen::Matrix< real, 5, 1 > v