DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
EulerSolver_PrintData.hxx
Go to the documentation of this file.
1/** @file EulerSolver_PrintData.hxx
2 * @brief Template implementations of EulerSolver output methods: PrintData for VTK/HDF5
3 * volume and boundary surface output, PrintRestart/ReadRestart for checkpoint I/O,
4 * and ReadRestartOtherSolver for cross-solver restart loading.
5 *
6 * PrintData outputs primitive variables (rho, p, T, Mach, velocity), RANS quantities
7 * (mut, nuTilde, k, omega), reconstruction quality (beta), cell residuals, and
8 * boundary surface quantities (wall shear stress, Cp, Cf, y+, heat flux).
9 * Supports time-averaged field output and configurable precision/encoding.
10 */
11#pragma once
12
13#include <future>
14#include <hdf5.h>
15#include "EulerSolver.hpp"
16
17namespace DNDS::Euler
18{
19 static const auto model = NS;
20 DNDS_SWITCH_INTELLISENSE(template <EulerModel model>, )
21 /** @brief Write volume and boundary surface data to VTK-HDF5 or legacy VTK files.
22 *
23 * Outputs the following per-cell fields for volume data:
24 * - Primitive variables: density, velocity components, pressure, temperature, Mach number.
25 * - Reconstruction quality indicator (beta / smooth threshold).
26 * - Cell residual from ODE integrator.
27 * - RANS quantities (nuTilde for SA; k, omega/epsilon for 2-equation models).
28 * - Additional user-registered cell scalar fields.
29 *
30 * For boundary surface data (wall faces):
31 * - Wall shear stress vector, pressure coefficient, skin friction coefficient.
32 * - y+ (wall unit distance), heat flux.
33 *
34 * Supports time-averaged mode, point-interpolated output, async I/O,
35 * and configurable ASCII precision / VTK float encoding.
36 *
37 * @param fname Base filename for volume output.
38 * @param fnameSeries Filename for VTK time series metadata.
39 * @param odeResidualF Functor returning per-cell ODE residual scalar.
40 * @param additionalCellScalars List of additional named cell scalar fields to output.
41 * @param eval Reference to the EulerEvaluator.
42 * @param tSimu Current simulation time for time-series annotation.
43 * @param mode Output mode (normal or time-averaged).
44 */
45 void EulerSolver<model>::PrintData(
46 const std::string &fname,
47 const std::string &fnameSeries,
48 const tCellScalarFGet &odeResidualF,
49 tAdditionalCellScalarList &additionalCellScalars,
50 TEval &eval, real tSimu, PrintDataMode mode)
51 {
53 reader->SetASCIIPrecision(config.dataIOControl.nASCIIPrecision);
54 reader->SetVTKFloatEncodeMode(config.dataIOControl.vtuFloatEncodeMode);
55 mesh->SetHDF5OutSetting(config.dataIOControl.hdfChunkSize, config.dataIOControl.hdfDeflateLevel,
56 config.dataIOControl.hdfCollOnData, config.dataIOControl.hdfCollOnMeta);
57 const int cDim = dim;
58
59 ArrayDOFV<nVarsFixed> &uOut = mode == PrintDataTimeAverage ? uAveraged : u;
60 // int nBad;
61 // do
62 // {
63 // nBad = 0;
64 // for (auto &f : outFuture)
65 // if (f.valid() && f.wait_for(std::chrono::microseconds(10)) != std::future_status::ready)
66 // nBad++;
67 // for (auto &f : outBndFuture)
68 // if (f.valid() && f.wait_for(std::chrono::microseconds(10)) != std::future_status::ready)
69 // nBad++;
70 // } while (nBad);
71
72 std::vector<std::function<void()>> fOuts;
73 // std::cout << "usize " << u.father->Size() << std::endl;
74
75 if (config.dataIOControl.outVolumeData || mode == PrintDataTimeAverage)
76 {
77 {
78 std::lock_guard<std::mutex> outLock(outArraysMutex);
79 if (config.dataIOControl.outAtCellData || mode == PrintDataTimeAverage)
80 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
81 {
82 // TU recu =
83 // vfv->GetIntPointDiffBaseValue(iCell, -1, -1, -1, std::array<int, 1>{0}, 1) *
84 // uRec[iCell];
85 // recu += uOut[iCell];
86 // recu = EulerEvaluator::CompressRecPart(uOut[iCell], recu);
87 TU recu = uOut[iCell];
88 if (eval.settings.frameConstRotation.enabled)
89 eval.TransformURotatingFrame_ABS_VELO(recu, vfv->GetCellQuadraturePPhys(iCell, -1), -1);
90 TVec velo = (recu(Seq123).array() / recu(0)).matrix();
91 real vsqr = velo.squaredNorm();
92 real asqr, p, H;
93 Gas::IdealGasThermal(recu(I4), recu(0), vsqr, eval.settings.idealGasProperty.gamma, p, asqr, H);
94 // DNDS_assert(asqr > 0);
95 real M = std::sqrt(std::abs(vsqr / asqr));
96 real T = p / recu(0) / eval.settings.idealGasProperty.Rgas;
97
98 (*outDist)[iCell][0] = recu(0);
99 for (int i = 0; i < dim; i++)
100 (*outDist)[iCell][i + 1] = velo(i);
101 (*outDist)[iCell][I4 + 0] = p;
102 (*outDist)[iCell][I4 + 1] = T;
103 (*outDist)[iCell][I4 + 2] = M;
104 // (*outDist)[iCell][7] = (bool)(ifUseLimiter[iCell] & 0x0000000FU);
105 (*outDist)[iCell][I4 + 3] = ifUseLimiter[iCell][0] / (vfv->getSettings().smoothThreshold + verySmallReal);
106 // std::cout << iCell << ode.rhsbuf[0][iCell] << std::endl;
107 (*outDist)[iCell][I4 + 4] = odeResidualF(iCell);
108 // { // see the cond
109 // auto A = vfv->GetCellRecMatA(iCell);
110 // Eigen::MatrixXd AInv = A;
111 // real aCond = HardEigen::EigenLeastSquareInverse(A, AInv);
112 // (*outDist)[iCell][I4 + 4] = aCond;
113 // }
114 // (*outDist)[iCell][8] = (*vfv->SOR_iCell2iScan)[iCell];//!using SOR rb seq instead
115
116 for (int i = I4 + 1; i < nVars; i++)
117 {
118 (*outDist)[iCell][4 + i] = recu(i) / recu(0); // 4 is additional amount offset, not Index of last flow variable (I4)
119 }
120 int iCur = 4 + nVars;
121 for (auto &out : additionalCellScalars)
122 {
123 (*outDist)[iCell][iCur] = std::get<1>(out)(iCell);
124 iCur++;
125 }
126 }
127
128 if (config.dataIOControl.outAtPointData)
129 {
130 if (config.limiterControl.useLimiter)
131 {
132 uRecNew.trans.startPersistentPull();
133 uRecNew.trans.waitPersistentPull();
134 }
135 else
136 {
137 uRec.trans.startPersistentPull();
138 uRec.trans.waitPersistentPull();
139 }
140
141 uOut.trans.startPersistentPull();
142 uOut.trans.waitPersistentPull();
143
144 for (index iN = 0; iN < mesh->NumNodeProc(); iN++)
145 outDistPointPair[iN].setZero();
146 std::vector<int> nN2C(mesh->NumNodeProc(), 0);
147 DNDS_assert(outDistPointPair.father->Size() == mesh->NumNode());
148 DNDS_assert(outDistPointPair.son->Size() == mesh->NumNodeGhost());
149 for (index iCell = 0; iCell < mesh->NumCellProc(); iCell++) //! all cells
150 {
151 for (int ic2n = 0; ic2n < mesh->cell2node.RowSize(iCell); ic2n++)
152 {
153 auto iNode = mesh->cell2node(iCell, ic2n);
154 nN2C.at(iNode)++;
155 auto pPhy = mesh->GetCoordNodeOnCell(iCell, ic2n);
156
157 RowVectorXR DiBj;
158 DiBj.resize(1, uRecNew[iCell].rows() + 1);
159 // std::cout << uRecNew[iCell].rows() << std::endl;
160 vfv->FDiffBaseValue(DiBj, pPhy, iCell, -2, -2);
161
162 TU vRec = (DiBj(EigenAll, Eigen::seq(1, EigenLast)) * (config.limiterControl.useLimiter ? uRecNew[iCell] : uRec[iCell])).transpose() +
163 uOut[iCell];
164 if (mesh->isPeriodic) // transform velocity to node reference frame
165 vRec(Seq123) = mesh->periodicInfo.GetVectorBackByBits<dim, 1>(vRec(Seq123), mesh->cell2nodePbi(iCell, ic2n));
166 if (mode == PrintDataTimeAverage)
167 vRec = uOut[iCell];
168 if (eval.settings.frameConstRotation.enabled)
169 eval.TransformURotatingFrame_ABS_VELO(vRec, pPhy, -1);
170 if (iNode < mesh->NumNode())
171 outDistPointPair[iNode](Eigen::seq(0, nVars - 1)) += vRec;
172 }
173 }
174
175 for (index iN = 0; iN < mesh->NumNode(); iN++)
176 {
177 TU recu = outDistPointPair[iN](Eigen::seq(0, nVars - 1)) / (nN2C.at(iN) + verySmallReal);
178 DNDS_assert(nN2C.at(iN) > 0);
179
180 TVec velo = (recu(Seq123).array() / recu(0)).matrix();
181 real vsqr = velo.squaredNorm();
182 real asqr, p, H;
183 Gas::IdealGasThermal(recu(I4), recu(0), vsqr, eval.settings.idealGasProperty.gamma, p, asqr, H);
184 // DNDS_assert(asqr > 0);
185 real M = std::sqrt(std::abs(vsqr / asqr));
186 real T = p / recu(0) / eval.settings.idealGasProperty.Rgas;
187
188 outDistPointPair[iN][0] = recu(0);
189 for (int i = 0; i < dim; i++)
190 outDistPointPair[iN][i + 1] = velo(i);
191 outDistPointPair[iN][I4 + 0] = p;
192 outDistPointPair[iN][I4 + 1] = T;
193 outDistPointPair[iN][I4 + 2] = M;
194
195 for (int i = I4 + 1; i < nVars; i++)
196 {
197 outDistPointPair[iN][2 + i] = recu(i) / recu(0); // 2 is additional amount offset
198 }
199 }
200 outDistPointPair.trans.startPersistentPull();
201 outDistPointPair.trans.waitPersistentPull();
202 }
203
204 if (config.dataIOControl.outPltMode == 0)
205 {
206 if (config.dataIOControl.outAtCellData || mode == PrintDataTimeAverage)
207 {
208 outDist2SerialTrans.startPersistentPull();
209 outDist2SerialTrans.waitPersistentPull();
210 }
211 if (config.dataIOControl.outAtPointData)
212 {
213 outDist2SerialTransPoint.startPersistentPull();
214 outDist2SerialTransPoint.waitPersistentPull();
215 }
216 }
217 }
218 int NOUTS_C{0}, NOUTSPoint_C{0};
219 if (config.dataIOControl.outAtCellData || mode == PrintDataTimeAverage)
220 NOUTS_C = nOUTS;
221 if (config.dataIOControl.outAtPointData)
222 NOUTSPoint_C = nOUTSPoint;
223
224 std::vector<std::string> names, namesPoint;
225 if constexpr (dim == 2)
226 names = {
227 "R", "U", "V", "P", "T", "M", "ifUseLimiter", "RHSr"};
228 else
229 names = {
230 "R", "U", "V", "W", "P", "T", "M", "ifUseLimiter", "RHSr"};
231 if constexpr (dim == 2)
232 namesPoint = {
233 "R", "U", "V", "P", "T", "M"};
234 else
235 namesPoint = {
236 "R", "U", "V", "W", "P", "T", "M"};
237 for (int i = I4 + 1; i < nVars; i++)
238 {
239 names.push_back("V" + std::to_string(i - I4));
240 namesPoint.push_back("V" + std::to_string(i - I4));
241 }
242 for (auto &out : additionalCellScalars)
243 {
244 names.push_back(std::get<0>(out));
245 }
246 if (config.dataIOControl.outAtCellData)
247 DNDS_assert(names.size() == NOUTS_C);
248 if (config.dataIOControl.outAtPointData)
249 DNDS_assert(namesPoint.size() == NOUTSPoint_C);
250
251 if (config.dataIOControl.outPltTecplotFormat)
252 {
253 if (config.dataIOControl.outPltMode == 0)
254 {
255 auto outRun = [mesh = mesh, reader = reader,
256 outDist = outDist, outSerial = outSerial, &outDistPointPair = outDistPointPair,
257 outSerialPoint = outSerialPoint,
258 fname, fnameSeries, NOUTS_C, NOUTSPoint_C, cDim,
259 names, namesPoint, tSimu,
260 &outArraysMutex = outArraysMutex]()
261 {
262 std::lock_guard<std::mutex> outArraysLock(outArraysMutex);
263 reader->PrintSerialPartPltBinaryDataArray(
264 fname,
265 NOUTS_C, NOUTSPoint_C,
266 [&](int idata)
267 { return names[idata]; }, // cellNames
268 [&](int idata, index iv)
269 {
270 return (*outSerial)[iv][idata]; // cellData
271 },
272 [&](int idata)
273 { return namesPoint[idata] + "_p"; }, // pointNames
274 [&](int idata, index in)
275 { return (*outSerialPoint)[in][idata]; }, // pointData
276 tSimu,
277 0);
278 };
279 // if (outFuture.at(0).valid())
280 // outFuture.at(0).wait();
281 // outFuture.at(0) = std::async(std::launch::async, outRun);
282 outRun();
283 }
284 else if (config.dataIOControl.outPltMode == 1)
285 {
286
287 auto outRun = [mesh = mesh, reader = reader,
288 outDist = outDist, outSerial = outSerial, &outDistPointPair = outDistPointPair,
289 outSerialPoint = outSerialPoint,
290 fname, fnameSeries, NOUTS_C, NOUTSPoint_C, cDim,
291 names, namesPoint, tSimu,
292 &outArraysMutex = outArraysMutex]()
293 {
294 std::lock_guard<std::mutex> outArraysLock(outArraysMutex);
295 reader->PrintSerialPartPltBinaryDataArray(
296 fname,
297 NOUTS_C, NOUTSPoint_C,
298 [&](int idata)
299 { return names[idata]; }, // cellNames
300 [&](int idata, index iv)
301 {
302 return (*outDist)[iv][idata]; // cellData
303 },
304 [&](int idata)
305 { return namesPoint[idata] + "_p"; }, // pointNames
306 [&](int idata, index in)
307 { return outDistPointPair[in][idata]; }, // pointData
308 tSimu,
309 1);
310 };
311 // if (outFuture.at(0).valid())
312 // outFuture.at(0).wait();
313 // outFuture.at(0) = std::async(std::launch::async, outRun);
314 outRun();
315 }
316 }
317
318 if (config.dataIOControl.outPltVTKFormat)
319 {
320 if (config.dataIOControl.outPltMode == 0)
321 {
322 auto outRun = [mesh = mesh, reader = reader,
323 outDist = outDist, outSerial = outSerial, &outDistPointPair = outDistPointPair,
324 outSerialPoint = outSerialPoint,
325 fname, fnameSeries, NOUTS_C, NOUTSPoint_C, cDim,
326 names, namesPoint, tSimu,
327 &outArraysMutex = outArraysMutex]()
328 {
329 std::lock_guard<std::mutex> outArraysLock(outArraysMutex);
330 reader->PrintSerialPartVTKDataArray(
331 fname, fnameSeries,
332 std::max(NOUTS_C - cDim, 0), std::min(NOUTS_C, 1),
333 std::max(NOUTSPoint_C - cDim, 0), std::min(NOUTSPoint_C, 1), //! vectors number is not cDim but 1
334 [&](int idata)
335 {
336 idata = idata > 0 ? idata + cDim : 0;
337 return names[idata]; // cellNames
338 },
339 [&](int idata, index iv)
340 {
341 idata = idata > 0 ? idata + cDim : 0;
342 return (*outSerial)[iv][idata]; // cellData
343 },
344 [&](int idata)
345 {
346 return "Velo"; // cellVecNames
347 },
348 [&](int idata, index iv, int idim)
349 {
350 return (*outSerial)[iv][1 + idim]; // cellVecData
351 },
352 [&](int idata)
353 {
354 idata = idata > 0 ? idata + cDim : 0;
355 return namesPoint[idata]; // pointNames
356 },
357 [&](int idata, index iv)
358 {
359 idata = idata > 0 ? idata + cDim : 0;
360 return (*outSerialPoint)[iv][idata]; // pointData
361 },
362 [&](int idata)
363 {
364 return "Velo"; // pointVecNames
365 },
366 [&](int idata, index iv, int idim)
367 {
368 idata += 1;
369 return (*outSerialPoint)[iv][1 + idim]; // pointVecData
370 },
371 tSimu,
372 0);
373 };
374 // if (outFuture.at(1).valid())
375 // outFuture.at(1).wait();
376 // outFuture.at(1) = std::async(std::launch::async, outRun);
377 fOuts.push_back(outRun);
378 }
379 else if (config.dataIOControl.outPltMode == 1)
380 {
381 auto outRun = [mesh = mesh, reader = reader,
382 outDist = outDist, outSerial = outSerial, &outDistPointPair = outDistPointPair,
383 outSerialPoint = outSerialPoint,
384 fname, fnameSeries, NOUTS_C, NOUTSPoint_C, cDim,
385 names, namesPoint, tSimu,
386 &outArraysMutex = outArraysMutex]()
387 {
388 std::lock_guard<std::mutex> outArraysLock(outArraysMutex);
389 reader->PrintSerialPartVTKDataArray(
390 fname, fnameSeries,
391 std::max(NOUTS_C - cDim, 0), std::min(NOUTS_C, 1),
392 std::max(NOUTSPoint_C - cDim, 0), std::min(NOUTSPoint_C, 1), //! vectors number is not cDim but 1
393 [&](int idata)
394 {
395 idata = idata > 0 ? idata + cDim : 0;
396 return names[idata]; // cellNames
397 },
398 [&](int idata, index iv)
399 {
400 idata = idata > 0 ? idata + cDim : 0;
401 return (*outDist)[iv][idata]; // cellData
402 },
403 [&](int idata)
404 {
405 return "Velo"; // cellVecNames
406 },
407 [&](int idata, index iv, int idim)
408 {
409 return idim < cDim ? (*outDist)[iv][1 + idim] : 0.0; // cellVecData
410 },
411 [&](int idata)
412 {
413 idata = idata > 0 ? idata + cDim : 0;
414 return namesPoint[idata]; // pointNames
415 },
416 [&](int idata, index iv)
417 {
418 idata = idata > 0 ? idata + cDim : 0;
419 return outDistPointPair[iv][idata]; // pointData
420 },
421 [&](int idata)
422 {
423 return "Velo"; // pointVecNames
424 },
425 [&](int idata, index iv, int idim)
426 {
427 return idim < cDim ? outDistPointPair[iv][1 + idim] : 0.0; // pointVecData
428 },
429 tSimu,
430 1);
431 };
432 // if (outFuture.at(1).valid())
433 // outFuture.at(1).wait();
434 // outFuture.at(1) = std::async(std::launch::async, outRun);
435 fOuts.push_back(outRun);
436 }
437 }
438
439 if (config.dataIOControl.outPltVTKHDFFormat)
440 {
441 MPI_Comm commDup = MPI_COMM_NULL;
442 MPI_Comm_dup(mpi.comm, &commDup);
443 auto outRun = [mesh = mesh, reader = reader, outDist = outDist, &outDistPointPair = outDistPointPair,
444 fname, fnameSeries, NOUTS_C, NOUTSPoint_C, cDim,
445 names, namesPoint, tSimu,
446 &outArraysMutex = outArraysMutex, commDup]()
447 {
448 // std::lock_guard<std::mutex> outHdfLock(HDF_mutex);
449 // std::lock_guard<std::mutex> outArraysLock(outArraysMutex);
450 std::scoped_lock lock(outArraysMutex, HDF_mutex);
451 MPI_Comm commDup1 = commDup;
452 mesh->PrintParallelVTKHDFDataArray(
453 fname, fnameSeries,
454 std::max(NOUTS_C - cDim, 0), std::min(NOUTS_C, 1),
455 std::max(NOUTSPoint_C - cDim, 0), std::min(NOUTSPoint_C, 1), //! vectors number is not cDim but 1
456 [&](int idata)
457 {
458 idata = idata > 0 ? idata + cDim : 0;
459 return names[idata]; // cellNames
460 },
461 [&](int idata, index iv)
462 {
463 idata = idata > 0 ? idata + cDim : 0;
464 return (*outDist)[iv][idata]; // cellData
465 },
466 [&](int idata)
467 {
468 return "Velo"; // cellVecNames
469 },
470 [&](int idata, index iv, int idim)
471 {
472 return idim < cDim ? (*outDist)[iv][1 + idim] : 0.0; // cellVecData
473 },
474 [&](int idata)
475 {
476 idata = idata > 0 ? idata + cDim : 0;
477 return namesPoint[idata]; // pointNames
478 },
479 [&](int idata, index iv)
480 {
481 idata = idata > 0 ? idata + cDim : 0;
482 return outDistPointPair[iv][idata]; // pointData
483 },
484 [&](int idata)
485 {
486 return "Velo"; // pointVecNames
487 },
488 [&](int idata, index iv, int idim)
489 {
490 return idim < cDim ? outDistPointPair[iv][1 + idim] : 0.0; // pointVecData
491 },
492 tSimu, commDup);
493 MPI_Comm_free(&commDup1);
494 };
495
496 // outRun();
497 // if (outFuture.at(2).valid())
498 // outFuture.at(2).wait();
499 // MPI::Barrier(mpi.comm);
500 // outFuture.at(2) = std::async(std::launch::async, outRun);
501 fOuts.push_back(outRun);
502 }
503 }
504
505 if (config.dataIOControl.outBndData)
506 {
507 {
508 std::lock_guard<std::mutex> outBndLock(outBndArraysMutex);
509 DNDS_MPI_InsertCheck(mpi, "EulerSolver<model>::PrintData === bnd enter");
510 for (index iB = 0; iB < meshBnd->NumCell(); iB++)
511 {
512 // TU recu =
513 // vfv->GetIntPointDiffBaseValue(iCell, -1, -1, -1, std::array<int, 1>{0}, 1) *
514 // uRec[iCell];
515 // recu += uOut[iCell];
516 // recu = EulerEvaluator::CompressRecPart(uOut[iCell], recu);
517 index iBnd = meshBnd->cell2parentCell.at(iB);
518 index iCell = mesh->bnd2cell[iBnd][0];
519 index iFace = mesh->bnd2faceV.at(iBnd);
520 if (iFace == -1)
521 {
522 DNDS_assert(mesh->isPeriodic); // only internal bnd is valid, periodic bnd should be omitted
523 (*outDistBnd)[iB](nOUTSBnd - 4) = meshBnd->GetCellZone(iB); // add this to enable blanking
524 continue;
525 }
526 TU recu = uOut[iCell];
527 if (eval.settings.frameConstRotation.enabled)
528 eval.TransformURotatingFrame_ABS_VELO(recu, vfv->GetCellQuadraturePPhys(iCell, -1), -1);
529 TVec velo = (recu(Seq123).array() / recu(0)).matrix();
530 real vsqr = velo.squaredNorm();
531 real asqr, p, H;
532 Gas::IdealGasThermal(recu(I4), recu(0), vsqr, eval.settings.idealGasProperty.gamma, p, asqr, H);
533 // DNDS_assert(asqr > 0);
534 real M = std::sqrt(std::abs(vsqr / asqr));
535 real T = p / recu(0) / eval.settings.idealGasProperty.Rgas;
536
537 (*outDistBnd)[iB][0] = recu(0);
538 for (int i = 0; i < dim; i++)
539 (*outDistBnd)[iB][i + 1] = velo(i);
540 (*outDistBnd)[iB][I4 + 0] = p;
541 (*outDistBnd)[iB][I4 + 1] = T;
542 (*outDistBnd)[iB][I4 + 2] = M;
543 for (int i = I4 + 1; i < nVars; i++)
544 {
545 (*outDistBnd)[iB][2 + i] = recu(i) / recu(0); // 4 is additional amount offset, not Index of last flow variable (I4)
546 }
547 // if(iFace < 0)
548 // {
549 // std::cout << iFace << std::endl;
550 // std::abort();
551 // }
552
553 (*outDistBnd)[iB](Eigen::seq(nVars + 2, nVars + 2 + nVars - 1)) = eval.fluxBnd.at(iBnd);
554 Geom::tPoint fluxT;
555 fluxT.setZero();
556 fluxT(Seq012) = eval.fluxBndForceT.at(iBnd);
557 (*outDistBnd)[iB](Eigen::seq(nVars + 2 + nVars, nVars + 2 + nVars + 3 - 1)) = fluxT;
558 // (*outDistBnd)[iB](nOUTSBnd - 4) = mesh->GetFaceZone(iFace);
559 (*outDistBnd)[iB](nOUTSBnd - 4) = meshBnd->GetCellZone(iB);
560 (*outDistBnd)[iB](Eigen::seq(nOUTSBnd - 3, nOUTSBnd - 1)) = vfv->GetFaceNorm(iFace, 0) * vfv->GetFaceArea(iFace);
561
562 // (*outDist)[iCell][8] = (*vfv->SOR_iCell2iScan)[iCell];//!using SOR rb seq instead
563 }
564
565 if (config.dataIOControl.outPltMode == 0)
566 {
567 outDist2SerialTransBnd.startPersistentPull();
568 outDist2SerialTransBnd.waitPersistentPull();
569 }
570 }
571 int NOUTS_C{0}, NOUTSPoint_C{0};
572 NOUTS_C = nOUTSBnd;
573 DNDS_MPI_InsertCheck(mpi, "EulerSolver<model>::PrintData === bnd transfer done");
574
575 std::vector<std::string> names;
576 std::vector<std::string> namesScalar;
577 std::vector<std::string> namesVector;
578 std::vector<int> offsetsScalar;
579 std::vector<int> offsetsVector;
580 if constexpr (dim == 2)
581 names = {
582 "R", "U", "V", "P", "T", "M"};
583 else
584 names = {
585 "R", "U", "V", "W", "P", "T", "M"};
586 namesScalar = {"R", "P", "T", "M"};
587 offsetsScalar = {0, dim + 1, dim + 2, dim + 3};
588 namesVector = {"Velo"};
589 offsetsVector = {1};
590 int currentTop = dim + 4;
591 for (int i = I4 + 1; i < nVars; i++)
592 {
593 names.push_back("V" + std::to_string(i - I4));
594 namesScalar.push_back("V" + std::to_string(i - I4));
595 offsetsScalar.push_back(currentTop++);
596 }
597 for (int i = 0; i < nVars; i++)
598 {
599 names.push_back("F" + std::to_string(i));
600 namesScalar.push_back("F" + std::to_string(i));
601 offsetsScalar.push_back(currentTop++);
602 }
603 names.push_back("FT1");
604 names.push_back("FT2");
605 names.push_back("FT3");
606 namesVector.push_back("FT");
607 offsetsVector.push_back(currentTop), currentTop += 3;
608 names.push_back("FaceZone");
609 namesScalar.push_back("FaceZone");
610 offsetsScalar.push_back(currentTop++);
611 names.push_back("N0");
612 names.push_back("N1");
613 names.push_back("N2");
614 namesVector.push_back("Norm");
615 offsetsVector.push_back(currentTop), currentTop += 3;
616
617 if (config.dataIOControl.outPltTecplotFormat)
618 {
619 DNDS_MPI_InsertCheck(mpi, "EulerSolver<model>::PrintData === bnd tecplot start");
620 if (config.dataIOControl.outPltMode == 0)
621 {
622 auto outBndRun = [meshBnd = meshBnd, readerBnd = readerBnd, outDistBnd = outDistBnd, outSerialBnd = outSerialBnd,
623 fname, fnameSeries, NOUTS_C, nOUTSBnd = nOUTSBnd, cDim, names, tSimu,
624 &outBndArraysMutex = outBndArraysMutex]()
625 {
626 std::lock_guard<std::mutex> outBndArraysLock(outBndArraysMutex);
627 readerBnd->PrintSerialPartPltBinaryDataArray(
628 fname + "_bnd",
629 NOUTS_C, 0,
630 [&](int idata)
631 { return names.at(idata); }, // cellNames
632 [&](int idata, index iv)
633 {
634 return (*outSerialBnd)[iv][idata]; // cellData
635 },
636 [&](int idata)
637 { return "ERROR"; }, // pointNames
638 [&](int idata, index in)
639 { return std::nan("0"); }, // pointData
640 tSimu,
641 0);
642 };
643 // if (outBndFuture.at(0).valid())
644 // outBndFuture.at(0).wait();
645 // outBndFuture.at(0) = std::async(std::launch::async, outBndRun);
646 outBndRun();
647 }
648 else if (config.dataIOControl.outPltMode == 1)
649 {
650 auto outBndRun = [meshBnd = meshBnd, readerBnd = readerBnd, outDistBnd = outDistBnd, outSerialBnd = outSerialBnd,
651 fname, fnameSeries, NOUTS_C, nOUTSBnd = nOUTSBnd, cDim, names, tSimu,
652 &outBndArraysMutex = outBndArraysMutex]()
653 {
654 std::lock_guard<std::mutex> outBndArraysLock(outBndArraysMutex);
655 readerBnd->PrintSerialPartPltBinaryDataArray(
656 fname + "_bnd",
657 NOUTS_C, 0,
658 [&](int idata)
659 { return names.at(idata); }, // cellNames
660 [&](int idata, index iv)
661 {
662 return (*outDistBnd)[iv][idata]; // cellData
663 },
664 [&](int idata)
665 { return "ERROR"; }, // pointNames
666 [&](int idata, index in)
667 { return std::nan("0"); }, // pointData
668 tSimu,
669 1);
670 };
671 // if (outBndFuture.at(0).valid())
672 // outBndFuture.at(0).wait();
673 // outBndFuture.at(0) = std::async(std::launch::async, outBndRun);
674 outBndRun();
675 }
676 }
677
678 const int cDim = dim;
679 if (config.dataIOControl.outPltVTKFormat)
680 {
681 DNDS_MPI_InsertCheck(mpi, "EulerSolver<model>::PrintData === bnd vtk start");
682 if (config.dataIOControl.outPltMode == 0)
683 {
684 auto outBndRun = [meshBnd = meshBnd, readerBnd = readerBnd, outDistBnd = outDistBnd, outSerialBnd = outSerialBnd,
685 fname, fnameSeries, NOUTS_C, nOUTSBnd = nOUTSBnd, nVars = nVars, cDim,
686 namesScalar, namesVector, offsetsScalar, offsetsVector, tSimu,
687 &outBndArraysMutex = outBndArraysMutex]()
688 {
689 std::lock_guard<std::mutex> outBndArraysLock(outBndArraysMutex);
690 readerBnd->PrintSerialPartVTKDataArray(
691 fname + "_bnd",
692 fnameSeries.size() ? fnameSeries + "_bnd" : "",
693 namesScalar.size(), namesVector.size(),
694 0, 0, //! vectors number is not cDim but 3
695 [&](int idata)
696 {
697 return namesScalar.at(idata); // cellNames
698 },
699 [&](int idata, index iv)
700 {
701 return (*outSerialBnd)[iv][offsetsScalar.at(idata)]; // cellData
702 },
703 [&](int idata)
704 {
705 return namesVector.at(idata);
706 },
707 [&](int idata, index iv, int idim)
708 {
709 return (*outSerialBnd)[iv][offsetsVector.at(idata) + idim];
710 },
711 [&](int idata)
712 {
713 return "error"; // pointNames
714 },
715 [&](int idata, index iv)
716 {
717 return std::nan("0"); // pointData
718 },
719 [&](int idata)
720 {
721 return "error"; // pointNames
722 },
723 [&](int idata, index iv, int idim)
724 {
725 return std::nan("0"); // pointData
726 },
727 tSimu,
728 0);
729 };
730 // if (outBndFuture.at(1).valid())
731 // outBndFuture.at(1).wait();
732 // outBndFuture.at(1) = std::async(std::launch::async, outBndRun);
733 fOuts.push_back(outBndRun);
734 }
735 else if (config.dataIOControl.outPltMode == 1)
736 {
737 auto outBndRun = [meshBnd = meshBnd, readerBnd = readerBnd, outDistBnd = outDistBnd, outSerialBnd = outSerialBnd,
738 fname, fnameSeries, NOUTS_C, nOUTSBnd = nOUTSBnd, cDim,
739 namesScalar, namesVector, offsetsScalar, offsetsVector, tSimu,
740 &outBndArraysMutex = outBndArraysMutex]()
741 {
742 std::lock_guard<std::mutex> outBndArraysLock(outBndArraysMutex);
743 readerBnd->PrintSerialPartVTKDataArray(
744 fname + "_bnd",
745 fnameSeries.size() ? fnameSeries + "_bnd" : "",
746 namesScalar.size(), namesVector.size(),
747 0, 0, //! vectors number is not cDim but 2
748 [&](int idata)
749 {
750 return namesScalar.at(idata); // cellNames
751 },
752 [&](int idata, index iv)
753 {
754 return (*outDistBnd)[iv][offsetsScalar.at(idata)]; // cellData
755 },
756 [&](int idata)
757 {
758 return namesVector.at(idata);
759 },
760 [&](int idata, index iv, int idim)
761 {
762 return (*outDistBnd)[iv][offsetsVector.at(idata) + idim];
763 },
764 [&](int idata)
765 {
766 return "error"; // pointNames
767 },
768 [&](int idata, index iv)
769 {
770 return std::nan("0"); // pointData
771 },
772 [&](int idata)
773 {
774 return "error"; // pointNames
775 },
776 [&](int idata, index iv, int idim)
777 {
778 return std::nan("0"); // pointData
779 },
780 tSimu,
781 1);
782 };
783 // if (outBndFuture.at(1).valid())
784 // outBndFuture.at(1).wait();
785 // outBndFuture.at(1) = std::async(std::launch::async, outBndRun);
786 fOuts.push_back(outBndRun);
787 }
788 }
789
790 if (config.dataIOControl.outPltVTKHDFFormat)
791 {
792 MPI_Comm commDup = MPI_COMM_NULL;
793 MPI_Comm_dup(mpi.comm, &commDup);
794 auto outBndRun = [meshBnd = meshBnd, outDistBnd = outDistBnd,
795 fname, fnameSeries, NOUTS_C, nOUTSBnd = nOUTSBnd, cDim,
796 namesScalar, namesVector, offsetsScalar, offsetsVector, tSimu,
797 &outBndArraysMutex = outBndArraysMutex, commDup]()
798 {
799 // std::lock_guard<std::mutex> outHdfLock(HDF_mutex);
800 // std::lock_guard<std::mutex> outBndArraysLock(outBndArraysMutex);
801 // std::lock_guard<std::mutex> outBndArraysLock1(outArraysMutex);
802 std::scoped_lock lock(outBndArraysMutex, HDF_mutex);
803 MPI_Comm commDup1 = commDup;
804 meshBnd->PrintParallelVTKHDFDataArray(
805 fname + "_bnd",
806 fnameSeries.size() ? fnameSeries + "_bnd" : "",
807 namesScalar.size(), namesVector.size(),
808 0, 0, //! vectors number is not cDim but 2
809 [&](int idata)
810 {
811 return namesScalar.at(idata); // cellNames
812 },
813 [&](int idata, index iv)
814 {
815 return (*outDistBnd)[iv][offsetsScalar.at(idata)]; // cellData
816 },
817 [&](int idata)
818 {
819 return namesVector.at(idata);
820 },
821 [&](int idata, index iv, int idim)
822 {
823 return (*outDistBnd)[iv][offsetsVector.at(idata) + idim];
824 },
825 [](int idata)
826 {
827 return "error"; // pointNames
828 },
829 [](int idata, index iv)
830 {
831 return std::nan("0"); // pointData
832 },
833 [](int idata)
834 {
835 return "error"; // pointNames
836 },
837 [](int idata, index iv, int idim)
838 {
839 return std::nan("0"); // pointData
840 },
841 tSimu, commDup);
842 MPI_Comm_free(&commDup1);
843 };
844 // if (outBndFuture.at(2).valid())
845 // outBndFuture.at(2).wait();
846 // MPI::Barrier(mpi.comm);
847 // outBndFuture.at(2) = std::async(std::launch::async, outBndRun);
848 fOuts.push_back(outBndRun);
849 // outBndRun();
850 }
851 }
852 auto runFOuts = [fOuts]()
853 {
854 for (auto &f : fOuts)
855 f();
856 };
857 bool useAsyncOut = config.dataIOControl.allowAsyncPrintData;
858#ifndef H5_HAVE_THREADSAFE
859 if (config.dataIOControl.outPltVTKHDFFormat)
860 useAsyncOut = false;
861#endif
862 if (config.dataIOControl.outPltVTKHDFFormat)
863 if (MPI::GetMPIThreadLevel() < MPI_THREAD_MULTIPLE)
864 useAsyncOut = false;
865
866 // std::cout << fOuts.size() << std::endl;
867 if (outSeqFuture.valid())
868 outSeqFuture.wait();
869 if (useAsyncOut)
870 outSeqFuture = std::async(std::launch::async, runFOuts);
871 else
872 runFOuts();
873
874 DNDS_MPI_InsertCheck(mpi, "EulerSolver<model>::PrintData === bnd output done");
875 }
876
877 DNDS_SWITCH_INTELLISENSE(template <EulerModel model>, )
878 /** @brief Write a checkpoint/restart file containing the current solution.
879 *
880 * Serializes the conservative variable DOF array and cell ordering information
881 * to either JSON (per-rank directory) or HDF5 (single file with original indices
882 * for redistribution support). Also writes the current configuration.
883 *
884 * @param fname Base filename for the restart output.
885 */
886 void EulerSolver<model>::PrintRestart(std::string fname)
887 {
888 if (config.dataIOControl.restartWriter.type == "JSON")
889 {
890 std::filesystem::path outPath;
891 outPath = {fname + "_p" + std::to_string(mpi.size) + "_restart.dir"};
892 std::filesystem::create_directories(outPath);
893 char BUF[512];
894 std::sprintf(BUF, "%04d", mpi.rank);
895 fname = getStringForcePath(outPath / (std::string(BUF) + ".json"));
896 config.restartState.lastRestartFile = getStringForcePath(outPath);
897 }
898 else if (config.dataIOControl.restartWriter.type == "H5")
899 {
900 fname += "_p" + std::to_string(mpi.size) + ".restart.dnds.h5";
901 std::filesystem::path outPath = fname;
902 std::filesystem::create_directories(outPath.parent_path() / ".");
903 config.restartState.lastRestartFile = fname;
904 }
905 else
906 DNDS_assert_info(false, "restartWriter is invalid");
907
908 Serializer::SerializerBaseSSP serializerP = config.dataIOControl.restartWriter.BuildSerializer(mpi);
909
910 serializerP->OpenFile(fname, false);
911 if (!serializerP->IsPerRank())
912 {
913 // H5 path: write with origIndex for redistribution support
914 std::vector<index> origIdx(mesh->NumCell());
915 for (index i = 0; i < mesh->NumCell(); i++)
916 origIdx[i] = mesh->cell2cellOrig(i, 0);
917 u.WriteSerialize(serializerP, "u", origIdx, /*PIG*/ false, /*son*/ false);
918 }
919 else
920 {
921 // JSON path: no redistribution support
922 u.WriteSerialize(serializerP, "u", /*PIG*/ false, /*son*/ false);
923 }
924 mesh->cell2cellOrig.WriteSerialize(serializerP, "cell2cellOrig", /*PIG*/ false, /*son*/ false);
925 serializerP->CloseFile();
926
927 PrintConfig();
928 }
929
930 DNDS_SWITCH_INTELLISENSE(template <EulerModel model>, )
931 /** @brief Reorder restart data to match current cell ordering using cell2cellOrig mapping.
932 *
933 * When restart data was saved with a different partition layout (JSON path),
934 * reads the original cell ordering and permutes the read DOF to match the
935 * current mesh partition. Falls back to direct copy with a warning if
936 * cell2cellOrig is not available.
937 *
938 * @param self Reference to the EulerSolver instance.
939 * @param u Target DOF array (output, reordered).
940 * @param uRead DOF array as read from file (input).
941 * @param serializerP Serializer used to read cell ordering metadata.
942 */
944 EulerSolver<model> &self,
945 typename EulerSolver<model>::TDof &u,
946 typename EulerSolver<model>::TDof &uRead,
947 Serializer::SerializerBaseSSP serializerP)
948 {
949 auto mesh = self.getMesh();
950 auto mpi = self.getMPI();
951 auto vfv = self.getVFV();
952 auto list_current_path = serializerP->ListCurrentPath();
953 if (mpi.rank == 0)
954 {
955 log() << "Contains: [";
956 for (auto &v : list_current_path)
957 log() << v << ", ";
958 log() << "]";
959 log() << std::endl;
960 }
961 if (list_current_path.count("cell2cellOrig"))
962 {
963 // TODO: lazy-build this inverse map
964 std::unordered_map<index, index> cellOrig2localCell;
965 cellOrig2localCell.reserve(mesh->NumCell());
966 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
967 cellOrig2localCell[mesh->cell2cellOrig(iCell, 0)] = iCell;
968 bool isLocalReorder = true;
969
970 Geom::tAdj1Pair cell2cellOrigRead;
971 DNDS_MAKE_SSP(cell2cellOrigRead.father, mpi);
972 DNDS_MAKE_SSP(cell2cellOrigRead.son, mpi);
973 cell2cellOrigRead.ReadSerialize(serializerP, "cell2cellOrig", /*PIG*/ false, /*son*/ false);
974 DNDS_assert_info(cell2cellOrigRead.father->Size() == u.father->Size(),
975 fmt::format("read size of cell2cellOrig not consistent: needed {}, got {}",
976 u.father->Size(), cell2cellOrigRead.father->Size()));
977 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
978 if (cellOrig2localCell.count(cell2cellOrigRead(iCell, 0)) == 0)
979 isLocalReorder = false;
980 DNDS_assert_info(isLocalReorder, "must be local reorder now! global reorder not implemented for now");
981 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
982 {
983 index iCellOrigG = cell2cellOrigRead(iCell, 0);
984 u[cellOrig2localCell.at(iCellOrigG)] = uRead[iCell];
985 }
986 }
987 else
988 {
989 u.CopyFather(uRead);
990 log() << TermColor::Red << "!!! Warning !!!\n"
991 << "Reading restart without cell2cellOrig information, ordering could be bad!" << std::endl;
992 }
993 }
994
995 DNDS_SWITCH_INTELLISENSE(template <EulerModel model>, )
996 /** @brief Read a checkpoint/restart file and load the solution into the DOF array.
997 *
998 * Supports two file formats:
999 * - JSON directory (.dir): per-rank files with local cell ordering reorder.
1000 * - HDF5 (.dnds.h5): single file with redistributed read using original cell indices,
1001 * supporting different MPI rank counts and partition layouts from the checkpoint.
1002 *
1003 * @param fname Path to the restart file or directory.
1004 */
1005 void EulerSolver<model>::ReadRestart(std::string fname)
1006 {
1007 if (mpi.rank == 0)
1008 log() << fmt::format("=== Reading Restart From [{}]", fname) << std::endl;
1009 std::filesystem::path outPath;
1010 outPath = fname;
1011
1013
1014 if (sstringHasSuffix(fname, ".dir"))
1015 {
1016 char BUF[512];
1017 std::sprintf(BUF, "%04d", mpi.rank);
1018 fname = getStringForcePath(outPath / (std::string(BUF) + ".json"));
1019
1020 serializerP = std::make_shared<DNDS::Serializer::SerializerJSON>();
1021 std::dynamic_pointer_cast<DNDS::Serializer::SerializerJSON>(serializerP)->SetUseCodecOnUint8(true);
1022 }
1023 else if (sstringHasSuffix(fname, ".dnds.h5"))
1024 {
1025 serializerP = std::make_shared<DNDS::Serializer::SerializerH5>(mpi);
1026 }
1027 else
1028 DNDS_assert_info(false, "restart file suffix not supported");
1029
1030 serializerP->OpenFile(fname, true);
1031
1032 if (!serializerP->IsPerRank())
1033 {
1034 // H5 path: use redistributed read (supports different np and partition layout)
1035 std::vector<index> newOrigIdx(mesh->NumCell());
1036 for (index i = 0; i < mesh->NumCell(); i++)
1037 newOrigIdx[i] = mesh->cell2cellOrig(i, 0);
1038
1039 u.ReadSerializeRedistributed(serializerP, "u", newOrigIdx);
1040 }
1041 else
1042 {
1043 // JSON path: read with same-partition, reorder locally
1044 TDof uRead;
1045 vfv->BuildUDof(uRead, nVars, false, false);
1046 uRead.ReadSerialize(serializerP, "u", /*PIG*/ false, /*son*/ false);
1047 DNDS_assert_info(uRead.father->Size() == u.father->Size(),
1048 fmt::format("read size not consistent: needed {}, got {}",
1049 u.father->Size(), uRead.father->Size()));
1050 // Doing reorder
1051 paste_read_restart_with_cell_ordering(*this, u, uRead, serializerP);
1052 }
1053
1054 u.trans.startPersistentPull();
1055 u.trans.waitPersistentPull();
1056
1057 MPI::Barrier(mpi.comm);
1058 serializerP->CloseFile();
1059 if (mpi.rank == 0)
1060 log() << fmt::format("=== Read Restart") << std::endl;
1061 }
1062
1063 DNDS_SWITCH_INTELLISENSE(template <EulerModel model>, )
1064 /** @brief Load selected DOF components from a restart file written by a different solver configuration.
1065 *
1066 * Reads a restart file that may have a different number of variables (e.g.,
1067 * loading a laminar solution into a RANS solver) and copies only the specified
1068 * variable dimensions (dimStore) into the current DOF array. Supports both
1069 * JSON and HDF5 restart formats with redistribution.
1070 *
1071 * @param fname Path to the other solver's restart file.
1072 * @param dimStore Indices of DOF components to copy from the restart file.
1073 */
1074 void EulerSolver<model>::ReadRestartOtherSolver(std::string fname, const std::vector<int> &dimStore)
1075 {
1077 DNDS_MAKE_SSP(readBuf.father, mpi);
1078 DNDS_MAKE_SSP(readBuf.son, mpi);
1079
1080 if (mpi.rank == 0)
1081 log() << fmt::format("=== Reading Other Solver Restart From [{}]", fname) << std::endl;
1082 std::filesystem::path outPath;
1083 outPath = fname;
1084
1086 if (sstringHasSuffix(fname, ".dir"))
1087 {
1088 char BUF[512];
1089 std::sprintf(BUF, "%04d", mpi.rank);
1090 fname = getStringForcePath(outPath / (std::string(BUF) + ".json"));
1091
1092 serializerP = std::make_shared<DNDS::Serializer::SerializerJSON>();
1093 std::dynamic_pointer_cast<DNDS::Serializer::SerializerJSON>(serializerP)->SetUseCodecOnUint8(true);
1094 }
1095 else if (sstringHasSuffix(fname, ".dnds.h5"))
1096 {
1097 serializerP = std::make_shared<DNDS::Serializer::SerializerH5>(mpi);
1098 }
1099 else
1100 DNDS_assert_info(false, "restart file suffix not supported");
1101
1102 serializerP->OpenFile(fname, true);
1103
1104 if (!serializerP->IsPerRank())
1105 {
1106 // H5 path: use redistributed read (supports different np and partition layout)
1107 std::vector<index> newOrigIdx(mesh->NumCell());
1108 for (index i = 0; i < mesh->NumCell(); i++)
1109 newOrigIdx[i] = mesh->cell2cellOrig(i, 0);
1110
1111 // Peek at the file to determine the stored nVars (row dimension),
1112 // then pre-size readBuf so RedistributeArrayWithTransformer can copy into it.
1113 {
1114 auto meta = readBuf.father->ReadSerializerMeta(serializerP, "u/father");
1115 // ArrayDOFV<Eigen::Dynamic> stores nVars in row_size_dynamic
1116 readBuf.father->Resize(mesh->NumCell(), meta.row_size_dynamic, 1);
1117 readBuf.son->Resize(0, meta.row_size_dynamic, 1);
1118 }
1119
1120 readBuf.ReadSerializeRedistributed(serializerP, "u", newOrigIdx);
1121
1122 int iMax = std::min(u.RowSize(), readBuf.RowSize()) - 1;
1123 for (auto item : dimStore)
1124 DNDS_assert(item <= iMax);
1125
1126 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
1127 u[iCell](dimStore) = readBuf[iCell](dimStore);
1128 }
1129 else
1130 {
1131 // JSON path: read with same-partition, reorder locally
1132 readBuf.ReadSerialize(serializerP, "u");
1133
1134 DNDS_assert_info(readBuf.father->Size() == u.father->Size(), fmt::format("{}, {}", readBuf.father->Size(), u.father->Size()));
1135 DNDS_assert_info(readBuf.son->Size() == u.son->Size(), fmt::format("{}, {}", readBuf.son->Size(), u.son->Size()));
1136 int iMax = std::min(u.RowSize(), readBuf.RowSize()) - 1;
1137 for (auto item : dimStore)
1138 DNDS_assert(item <= iMax);
1139 TDof uRead;
1140 vfv->BuildUDof(uRead, nVars, false, false);
1141 for (index iCell = 0; iCell < mesh->NumCell(); iCell++)
1142 uRead[iCell](dimStore) = readBuf[iCell](dimStore);
1143
1144 paste_read_restart_with_cell_ordering(*this, u, uRead, serializerP);
1145 }
1146
1147 u.trans.startPersistentPull();
1148 u.trans.waitPersistentPull();
1149
1150 serializerP->CloseFile();
1151 if (mpi.rank == 0)
1152 log() << fmt::format("=== Read Restart") << std::endl;
1153 }
1154}
#define DNDS_MAKE_SSP(ssp,...)
Definition Defines.hpp:212
#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
Top-level solver orchestrator for compressible Navier-Stokes / Euler simulations.
#define DNDS_FV_EULEREVALUATOR_GET_FIXED_EIGEN_SEQS
Declares compile-time Eigen index sequences for sub-vector access into conservative state vectors.
Definition Euler.hpp:56
#define DNDS_MPI_InsertCheck(mpi, info)
Debug helper: barrier + print "CHECK <info> RANK r @ fn @ file:line".
Definition MPI.hpp:320
#define DNDS_SWITCH_INTELLISENSE(real, intellisense)
Definition Macros.hpp:86
MPI-parallel distributed array of per-cell Degrees-of-Freedom (conservative variable vectors).
Definition Euler.hpp:93
Core finite-volume evaluator for compressible Navier-Stokes / Euler equations.
Top-level solver orchestrator for compressible Navier-Stokes / Euler equations.
tCellScalarList tAdditionalCellScalarList
Type alias for additional output scalar list.
PrintDataMode
Output mode selector for PrintData.
void IdealGasThermal(real E, real rho, real vSqr, real gamma, real &p, real &asqr, real &H)
Thin wrapper delegating to IdealGas::IdealGasThermal.
Definition Gas.hpp:190
void paste_read_restart_with_cell_ordering(EulerSolver< model > &self, typename EulerSolver< model >::TDof &u, typename EulerSolver< model >::TDof &uRead, Serializer::SerializerBaseSSP serializerP)
Reorder restart data to match current cell ordering using cell2cellOrig mapping.
std::function< real(index)> tCellScalarFGet
Function type returning a scalar value for a given cell index [0, NumCell).
Definition EulerBC.hpp:704
@ NS
Navier-Stokes, 2D geometry, 3D physics (5 vars).
Definition Euler.hpp:876
Eigen::Vector3d tPoint
Definition Geometric.hpp:9
MPI_int Barrier(MPI_Comm comm)
Wrapper over MPI_Barrier.
Definition MPI.cpp:248
int GetMPIThreadLevel()
Return the MPI thread-support level the current process was initialised with.
Definition MPI.hpp:474
ssp< SerializerBase > SerializerBaseSSP
constexpr std::string_view Red
ANSI escape: bright red foreground.
Definition Defines.hpp:869
bool sstringHasSuffix(const std::string &str, const std::string &suffix)
Definition Defines.hpp:800
DNDS_CONSTANT const real verySmallReal
Catch-all lower bound ("effectively zero").
Definition Defines.hpp:194
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:107
std::string getStringForcePath(const std::filesystem::path::string_type &v)
Portable conversion of a platform-native path string to std::string.
Definition Defines.hpp:630
Eigen::RowVector< real, Eigen::Dynamic > RowVectorXR
Dynamic row-vector of reals.
Definition Defines.hpp:209
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
Definition Defines.hpp:105
std::ostream & log()
Return the current DNDSR log stream (either std::cout or the installed file).
Definition Defines.cpp:49
std::mutex HDF_mutex
Global mutex serialising host-side HDF5 calls.
Definition MPI.cpp:449
Convenience bundle of a father, son, and attached ArrayTransformer.
void ReadSerializeRedistributed(Serializer::SerializerBaseSSP serializerP, const std::string &name, const std::vector< index > &newOrigIndex)
Reads ArrayPair data from HDF5 with redistribution support.
ssp< TArray > father
Owned-side array (must be resized before ghost setup).
ssp< TArray > son
Ghost-side array (sized automatically by createMPITypes / BorrowAndPull).
auto RowSize() const
Uniform row width (delegates to father).
void ReadSerialize(Serializer::SerializerBaseSSP serializerP, const std::string &name, bool includePIG=true, bool includeSon=true)
Reads an ArrayPair written by WriteSerialize (same partition count).
TTrans trans
Ghost-communication engine bound to father and son.
Eigen::Matrix wrapper that hides begin/end from fmt.
Definition EigenUtil.hpp:62
Eigen::Matrix< real, 5, 1 > v
Eigen::Vector3d velo