hips
Loading...
Searching...
No Matches
hips.cc
Go to the documentation of this file.
1
18
19#include "hips.h"
20
21#ifdef REACTIONS_ENABLED
22#include "batchReactor_cvode.h"
24#endif
25
26#include "randomGenerator.h"
27
28#include <iostream>
29#include <iomanip>
30#include <fstream>
31#include <sstream>
32#include <cmath>
33#include <vector>
34
35using namespace std;
36
38hips::hips(int nLevels_,
39 double domainLength_,
40 double tau0_,
41 double C_param_,
42 int forceTurb_,
43 int nVar_,
44 vector<double> &ScHips_,
45 bool performReaction_,
46 shared_ptr<void> vcantSol,
47 int seed,
48 int realization_):
49 nLevels(nLevels_),
50 domainLength(domainLength_),
51 tau0(tau0_),
52 C_param(C_param_),
53 forceTurb(forceTurb_),
54 ScHips(ScHips_),
55 nVar(nVar_),
56 LrandSet(true),
57 rand(seed),
58 performReaction(performReaction_),
59 realization(realization_){
60
61 #ifdef REACTIONS_ENABLED
62 if(performReaction) {
63 shared_ptr<Cantera::Solution> cantSol = static_pointer_cast<Cantera::Solution>(vcantSol);
64 gas = cantSol->thermo();
65 nsp = gas->nSpecies();
66
67 bRxr = make_shared<batchReactor_cvode>(cantSol); // By default, use batchReactor_cvode
68
69 // Uncomment the following line to switch to batchReactor_cantera
70 // bRxr = make_unique<batchReactor_cantera>(cantSol);
71 }
72 #endif
73
74 // Resize vectors to the number of variables
75 varData.resize(nVar);
76 varName.resize(nVar);
77
78 set_tree(nLevels, domainLength, tau0, ScHips);
79}
80
82
83hips::hips(double C_param_,
84 int forceTurb_,
85 int nVar_,
86 bool performReaction_,
87 shared_ptr<void> vcantSol,
88 int seed,
89 int realization_):
90 C_param(C_param_),
91 forceTurb(forceTurb_),
92 nVar(nVar_),
93 LrandSet(true),
94 rand(seed),
95 performReaction(performReaction_),
96 realization(realization_){
97
98 #ifdef REACTIONS_ENABLED
99 // Initialize Cantera thermo phase and species count.
100 if(performReaction) {
101 shared_ptr<Cantera::Solution> cantSol = static_pointer_cast<Cantera::Solution>(vcantSol);
102 gas = cantSol->thermo();
103 nsp = gas->nSpecies();
104
105 // Set up the default batch reactor (cvode).
106 // bRxr = make_shared<batchReactor_cvode>(cantSol);
107
108 // Uncomment the following line to switch to batchReactor_cantera.
109 bRxr = make_shared<batchReactor_cantera>(cantSol);
110 }
111 #endif
112
113 // Resize vectors to accommodate the number of variables.
114 varData.resize(nVar);
115 varName.resize(nVar);
116}
117
119
120void hips::set_tree(int nLevels_, double domainLength_, double tau0_, vector<double> &ScHips_){
121
123 domainLength = domainLength_;
124 tau0 = tau0_;
125 ScHips = ScHips_;
126
127 if (nLevels == -1)
128 nLevels = nL;
129
130 iEta = nLevels - 3; // Kolmogorov level; if nLevels = 7, then 0, 1, 2, 3, (4), 5, 6; iEta=4 is the lowest swap level: swap grandchildren of iEta=4 at level 6.
131
132 int maxSc = 1.0;
133
134 for (int i=0; i<ScHips.size(); i++)
135 maxSc = ScHips[i]>maxSc ? ScHips[i] : maxSc;
136
137 if (maxSc > 1.0)
138 nLevels += ceil(log(maxSc)/log(4)); // Changing number of levels!
139
140 Nm1 = nLevels - 1;
141 Nm2 = nLevels - 2;
142 Nm3 = nLevels - 3;
143
144 // --------------------------
145
146 nparcels = static_cast<int>(pow(2, Nm1));
147 parcelTimes.resize(nparcels,0);
148 i_batchelor.resize(nVar,0);
149
150 vector<double> levelLengths(nLevels); // Including all levels, but last 2 don't count:
151 vector<double> levelTaus(nLevels); // Smallest scale is 2 levels up from bottom
152 levelRates = vector<double>(nLevels);
153
154 for (int i=0; i<nLevels; i++) {
155 levelLengths[i] = domainLength * pow(Afac,i);
156 //levelLengths[i] = domainLength * pow(Anew,i);
157
158 levelTaus[i] = tau0 * pow(levelLengths[i]/domainLength, 2.0/3.0) / C_param;
159 levelRates[i] = 1.0/levelTaus[i] * pow(2.0,i);
160 }
161
162 LScHips = ScHips.size() > 0 ? true : false;
163 if (LScHips) { // Ccorrect levels for high Sc (levels > Kolmogorov)
164 for (int i=iEta+1; i<nLevels; i++) {
165 levelTaus[i] = tau0 *
166 pow(levelLengths[iEta]/domainLength, 2.0/3.0) /
167 C_param;
168 levelRates[i] = 1.0/levelTaus[i] * pow(2.0,i);
169 }
170 }
171
172 //-------------------------------------------------
173
174 eddyRate_total = 0.0;
175 for (int i=0; i<=Nm3; i++)
177
178 eddyRate_inertial = 0.0;
179 for (int i=0; i<=iEta; i++)
181
182 //-------------------
183
184 i_plus.resize(nVar);
185
186 for (int k=0; k<nVar; k++) {
187 if (ScHips[k] < 1.0)
188 i_batchelor[k] = iEta + 1.5*log(ScHips[k])/log(4);
189 else if (ScHips[k] > 1.0)
190 i_batchelor[k] = iEta + log(ScHips[k])/log(4);
191 else
192 i_batchelor[k] = iEta;
193
194 i_plus[k] = ceil(i_batchelor[k]);
195 }
196
197 //------------------- Set the parcel addresses (index array)
198
199 varRho.resize(nparcels);
200 Temp.resize(nparcels);
201
202 pLoc.resize(nparcels);
203 for (int i=0; i<nparcels; i++)
204 pLoc[i] = i;
205}
206
208
209void hips::set_tree(double Re_, double domainLength_, double tau0_, std::vector<double> &ScHips_, std::string approach_) {
210 Re = Re_;
211 domainLength = domainLength_;
212 tau0 = tau0_;
213 ScHips = ScHips_;
214 approach = approach_;
215
216 double baseLevelEstimate = (3.0 / 4) * log(1 / Re) / log(Afac); // Calculate the base tree level estimate (non-integer)
217 int baseLevel;
218
219 if (approach == "rounding") {
220 baseLevel = round(baseLevelEstimate); // Round the base level to the nearest integer
221 }
222 else if (approach == "probability") {
223 baseLevel = ceil(baseLevelEstimate); // Ceil the base level to the nearest integer
224 int previousLevel = baseLevel - 1;
225 Prob = baseLevelEstimate - previousLevel; // Calculate the probability
226 }
227 else if (approach == "micromixing") {
228 baseLevel = ceil(baseLevelEstimate); // Ceil the base level to the nearest integer
229 lStar = std::pow(Re, -3.0 / 4); // Calculate lStar based on Re
230 }
231 else if (approach == "dynamic_A") {
232 baseLevel = round(baseLevelEstimate); // Round the base level to the nearest integer
233 Anew = exp(-log(Re) / ((4.0 / 3.0) * baseLevel)); // Calculate the new value of parameter A
234 }
235 else {
236 throw std::invalid_argument("Invalid approach specified"); // Handle invalid approach case if needed
237 }
238
239 nL = baseLevel + 3; // Set the number of levels for the binary tree structure
240
241 //----------------------------------------------------------------------
242 nLevels = nL;
243 iEta = nLevels - 3; // Kolmogorov level
244
245 int maxSc = 1;
246 for (const auto &sc : ScHips)
247 maxSc = std::max(maxSc, static_cast<int>(sc));
248 if (maxSc > 1.0)
249 nLevels += ceil(log(maxSc) / log(4));
250
251 Nm1 = nLevels - 1;
252 Nm2 = nLevels - 2;
253 Nm3 = nLevels - 3;
254
255 nparcels = static_cast<int>(pow(2, Nm1));
256 parcelTimes.resize(nparcels, 0);
257 i_batchelor.resize(nVar, 0);
258
259 std::vector<double> levelLengths(nLevels); // Including all levels, but last 2 don't count
260 std::vector<double> levelTaus(nLevels); // Smallest scale is 2 levels up from bottom
261 levelRates.resize(nLevels);
262
263 for (int i = 0; i < nLevels; ++i) {
264 levelLengths[i] = domainLength * pow((approach == "dynamic_A" ? Anew : Afac), i);
265
266 levelTaus[i] = tau0 * pow(levelLengths[i] / domainLength, 2.0 / 3.0) / C_param;
267 levelRates[i] = 1.0 / levelTaus[i] * pow(2.0, i);
268 }
269
270 if (approach == "micromixing") { // Adjust rates for micromixing model
271 levelTaus[Nm3] = tau0 * pow(lStar / domainLength, 2.0 / 3.0) / C_param;
272 levelRates[Nm3] = 1.0 / levelTaus[Nm3] * pow(2.0, Nm3);
273 }
274
275 if (approach == "probability") { // Adjust final mixing rate based on probability
276 levelRates[Nm3] = levelRates[nL - 3] * Prob;
277 }
278
279 LScHips = !ScHips.empty(); // Correct levels for high Sc (levels > Kolmogorov)
280 if (LScHips) {
281 for (int i = iEta + 1; i < nLevels; ++i) {
282 levelTaus[i] = tau0 * pow(levelLengths[iEta] / domainLength, 2.0 / 3.0) / C_param;
283 levelRates[i] = 1.0 / levelTaus[i] * pow(2.0, i);
284 }
285 }
286
287 //-----------------------------------------------------
288
289 eddyRate_total = 0.0;
290 for (int i = 0; i <= Nm3; ++i)
292
293 eddyRate_inertial = 0.0;
294 for (int i = 0; i <= iEta; ++i)
296
297 //-----------------------------------------------------
298
299 i_plus.resize(nVar);
300 for (int k = 0; k < nVar; ++k) {
301 if (ScHips[k] < 1.0)
302 i_batchelor[k] = iEta + 1.5 * log(ScHips[k]) / log(4);
303 else if (ScHips[k] > 1.0)
304 i_batchelor[k] = iEta + log(ScHips[k]) / log(4);
305 else
306 i_batchelor[k] = iEta;
307 i_plus[k] = ceil(i_batchelor[k]);
308 }
309
310 //-----------------------------------------------------
311
312 varRho.resize(nparcels);
313 Temp.resize(nparcels);
314 pLoc.resize(nparcels);
315 for (int i = 0; i < nparcels; ++i)
316 pLoc[i] = i;
317}
318
336
337void hips::set_varData(std::vector<double> &v, std::vector<double> &w, const std::string &varN) {
338
339 varData[currentIndex] = std::make_shared<std::vector<double>>(projection(v, w));
340 varName[currentIndex] = varN;
341
342 currentIndex++;
343}
344
364
365void hips::set_varData(std::vector<double> &v, std::vector<double> &w, const std::string &varN, const std::vector<double> &rho) {
366
367 std::pair<std::vector<double>, std::vector<double>> results = projection(v, w, rho);
368 std::vector<double> vh = results.first;
369 std::vector<double> rho_h = results.second;
370
371 varData[currentIndex] = std::make_shared<std::vector<double>>(projection(v, w));
372 varRho = std::vector<double>(rho_h);
373 varName[currentIndex] = varN;
374
375 currentIndex++;
376}
377
399
400std::vector<double> hips::projection(std::vector<double> &vcfd, std::vector<double> &weight) {
401
402 xc = setGridCfd(weight); // Populate the physical domain for flow particles
403 xh = setGridHips(nparcels); // Populate the physical domain for hips parcels
404
405 int nc = xc.size() - 1;
406 int nh = xh.size() - 1;
407
408 std::vector<double> vh(nh, 0.0);
409 int jprev = 0;
410
411 for(int i = 0; i < nh; i++) {
412 for(int j = jprev + 1; j <= nc; ++j) {
413 if(xc[j] <= xh[i + 1]) {
414 double d1 = xc[j] - xc[j - 1];
415 double d2 = xc[j] - xh[i];
416 // calculation of shortest distance
417 // handling if distance is zero but substarction gives comutational error
418 double d = std::min(d1, d2) < 1E-15 ? 0 : std::min(d1, d2);
419
420 vh[i] += vcfd[j - 1] * d;
421 }
422 else {
423 double d1 = xh[i + 1] - xc[j - 1];
424 double d2 = xh[i + 1] - xh[i];
425 // calculation of shortest distance
426 // handling if distance is zero but substarction gives comutational error
427 double d = std::min(d1, d2) < 1E-15 ? 0 : std::min(d1, d2);
428
429 vh[i] += vcfd[j - 1] * d;
430 jprev = j - 1;
431 break;
432 }
433 }
434 vh[i] /= (xh[i + 1] - xh[i]);
435 }
436 return vh;
437}
438
463
464std::pair<std::vector<double>, std::vector<double>> hips::projection(std::vector<double> &vcfd,
465 std::vector<double> &weight,
466 const std::vector<double> &density) {
467
468 xc = setGridCfd(weight);
470
471 // Initialize variables
472 int nc = xc.size() - 1;
473 int nh = xh.size() - 1;
474
475 std::vector<double> vh(nh, 0.0);
476 std::vector<double> rho_h(nh, 0.0);
477 int jprev = 0;
478
479 for (int i = 0; i < nh; i++) {
480 double total_dx = 0.0;
481
482 for (int j = jprev + 1; j <= nc; ++j) {
483 if (xc[j] <= xh[i + 1]) {
484 double d = std::min(xc[j] - xc[j - 1], xc[j] - xh[i]);
485 total_dx += d;
486 rho_h[i] += density[j - 1] * d;
487 vh[i] += density[j - 1] * vcfd[j - 1] * d;
488 }
489 else {
490 double d = std::min(xh[i + 1] - xc[j - 1], xh[i + 1] - xh[i]);
491 total_dx += d;
492 rho_h[i] += density[j - 1] * d;
493 vh[i] += density[j - 1] * vcfd[j - 1] * d;
494 jprev = j - 1;
495 break;
496 }
497 }
498
499 // ------------------------ Normalize results
500
501 rho_h[i] /= (xh[i + 1] - xh[i]);
502 vh[i] /= rho_h[i] * (xh[i + 1] - xh[i]);
503 }
504 return {vh, rho_h};
505}
506
525
526std::vector<double> hips::setGridCfd(std::vector<double> &w) {
527
528 std::vector<double> pos; // Initializing a vector to hold the grid positions
529 double posL = 0.0; // Initializing the starting position
530
531 int i = 0;
532
533 while (i <= w.size()) { // Generate the grid positions based on the weights
534 pos.push_back(posL); // Add the current position to the grid
535 posL += w[i]; // Move to the next position by adding the corresponding weight
536 i++;
537 }
538 return pos; // Return the generated grid positions
539}
540
559
560std::vector<double> hips::setGridHips(int N){
561
562 std::vector<double> xh(N + 1); // Initialize a vector to hold the grid points
563 double step = 1.0 / N; // Calculate the step size
564
565 for(int i = 0; i <= N; i++) // Populate the grid with evenly spaced points
566 xh[i] = i * step;
567
568 return xh; // Return the generated grid
569}
570
607
608void hips::calculateSolution(const double tRun, bool shouldWriteData) {
609
610 unsigned long long nEddies = 0; // Number of eddy events
611 int fileCounter = 0; // Number of data files written
612 int iLevel; // Tree level of EE with top at iLevel=0
613 int iTree; // One of two subtrees involved in swap at iLevel
614 time = 0.0; // Initialize simulation time
615 int lastEddyOutput = 0; // Track last eddy-based output event
616
617 // Apply default values if user hasn't set them
620 useEddyBasedWriting = true; // Default to eddy-based writing
621 }
622
623 sample_hips_eddy(dtEE, iLevel); // Get first EE at time 0+dtEE
624 nEddies++;
625 eddyCounter = 0; // Reset eddy counter at start
626 lastOutputTime = 0.0; // Reset last output time
627
628 while (time + dtEE <= tRun) {
629 time += dtEE;
630 selectAndSwapTwoSubtrees(iLevel, iTree);
631 advanceHips(iLevel, iTree); // Reaction and micromixing (if needed) to t=time
632
633 sample_hips_eddy(dtEE, iLevel);
634 nEddies++;
635 eddyCounter++;
636
637 // Only check the selected mode (set in example code or by default)
638 bool writeByEddy = (useEddyBasedWriting && eddyCounter >= lastEddyOutput + outputIntervalEddy);
639 bool writeByTime = (useTimeBasedWriting && time - lastOutputTime >= outputIntervalTime);
640
641 if (shouldWriteData) {
642 if (writeByEddy) { // Only write if using eddy-based writing
643 writeData(realization, ++fileCounter, time);
644 lastEddyOutput = eddyCounter; // Update last output event
645 }
646 else if (writeByTime) { // Only write if using time-based writing
647 writeData(realization, ++fileCounter, time);
649 }
650 }
651 }
652
653 // Ensure the final time step completes
654 time = tRun;
655 iLevel = 0;
656 iTree = 0;
657
658 if (performReaction)
659 reactParcels_LevelTree(iLevel, iTree); // React all parcels up to end time
661}
662
679
680void hips::sample_hips_eddy(double &dtEE, int &iLevel) {
681
682 static double c1 = 1.0 - pow(2.0, 5.0/3.0*(iEta+1));
683 static double c2 = pow(2.0, Nm2) - pow(2.0, iEta+1);
684 static double c3 = pow(2.0, iEta+1);
685
686 //--------------- time to next eddy
687
688 double r = rand.getRand();
689 dtEE = -log(r)/eddyRate_total;
690
691 //----------------- get eddy level
692
693 r = rand.getRand();
694
695 if ( r <= eddyRate_inertial/eddyRate_total) { // Inertial region
696 r = rand.getRand();
697 iLevel = ceil(3.0/5.0*log2(1.0-r*c1) - 1.0);
698 if (iLevel < 0) iLevel = 0;
699 if (iLevel > iEta) iLevel = iEta;
700 }
701
702 else { // "Batchelor" region
703 r = rand.getRand();
704 iLevel = ceil(log2(r*c2 + c3) - 1.0);
705 if (iLevel < iEta+1) iLevel = iEta+1;
706 if (iLevel > Nm3) iLevel = Nm3;
707 }
708 return;
709}
710
761
762void hips::selectAndSwapTwoSubtrees(const int iLevel, int &iTree) {
763
764 iTree = rand.getRandInt((1 << iLevel)-1);
765 int zero_q = rand.getRandInt(1); // 0q where q is 0 or 1
766 int one_r = 2 + rand.getRandInt(1); // 1r where r is 0 or 1
767
768 int Qstart = (zero_q << (Nm3-iLevel)) + (iTree << (Nm1-iLevel)); // starting index of Q parcels
769 int Rstart = (one_r << (Nm3-iLevel)) + (iTree << (Nm1-iLevel)); // starting index of R parcels
770 int nPswap = 1 << (Nm3-iLevel); // number of parcels that will be swapped
771
772 int Qend = Qstart + nPswap; // inclusive indices are Qstart to Qend-1
773 int Rend = Rstart + nPswap; // inclusive indices are Rstart to Rend-1
774 vector<int> aa(pLoc.begin()+Qstart, pLoc.begin()+Qend);
775 copy(pLoc.begin()+Rstart, pLoc.begin()+Rend, pLoc.begin()+Qstart); // python: pLoc[Qstart:Qend]=pLoc[Rstart:Rend]
776 copy(aa.begin(), aa.end(), pLoc.begin()+Rstart); // python: pLoc[Rstart:Rend]=aa
777}
778
803
804void hips::advanceHips(const int iLevel, const int iTree) {
805
806 if (forceTurb == 2 && iLevel == 0) {
807 forceProfile(); // Forcing for statistically stationary
808 }
809
810 bool rxnDone = false; // React all variables once
811 for (int k = 0; k < nVar; k++) { // Upon finding first variable needing micromixing
812 // Combined condition check with approach condition
813 if ((iLevel >= i_plus[k]) ||
814 (iLevel == i_plus[k] - 1 && rand.getRand() <= i_plus[k] - i_batchelor[k])) {
815 if (!rxnDone && performReaction) {
816 reactParcels_LevelTree(iLevel, iTree);
817 rxnDone = true;
818 }
819 mixAcrossLevelTree(k, iLevel, iTree);
820 }
821 }
822}
823
851
852int hips::getVariableIndex(const std::string &varName) const {
853
854 auto it = std::find(this->varName.begin(), this->varName.end(), varName);
855 if (it == this->varName.end()) {
856 throw std::runtime_error("Error: Variable name '" + varName + "' not found.");
857 }
858 return std::distance(this->varName.begin(), it);
859}
860
888
889void hips::reactParcels_LevelTree(const int iLevel, const int iTree) {
890
891 #ifdef REACTIONS_ENABLED
892
893 int enthalpyIdx = getVariableIndex("enthalpy"); // Dynamically find enthalpy index
894 int nP = 1 << (Nm1 - iLevel);
895 int istart = iTree * nP;
896 int iend = istart + nP;
897 int ime;
898 double dt;
899 double h;
900 std::vector<double> y(nsp);
901
902 for (int i = istart; i < iend; i++) {
903 ime = pLoc[i];
904 dt = time - parcelTimes[ime];
905
906 // Access enthalpy using its index
907 h = (*varData[enthalpyIdx])[ime];
908
909 // Access species using their indices
910 for (int k = 0; k < nsp; k++) {
911 int speciesIdx = getVariableIndex(gas->speciesName(k));
912 y[k] = (*varData[speciesIdx])[ime];
913 }
914 if (performReaction) {
915 bRxr->react(h, y, dt);
916 varRho[ime] = bRxr->getDensity();
917 Temp[ime] = bRxr->temperature;
918 }
919 // Update enthalpy and species
920 (*varData[enthalpyIdx])[ime] = h;
921 for (int k = 0; k < nsp; k++) {
922 int speciesIdx = getVariableIndex(gas->speciesName(k));
923 (*varData[speciesIdx])[ime] = y[k];
924 }
925
926 parcelTimes[ime] = time;
927 }
928 #endif
929}
930
966
967void hips::mixAcrossLevelTree(int kVar, const int iLevel, const int iTree) {
968
969 int istart;
970 int iend;
971
972 int nPmix = 1 << (nLevels - iLevel - 2); // Number of parcels mixed together
973
974 int ime;
975
976 //---------- Mix left branch of iTree
977
978 istart = iTree << (Nm1-iLevel);
979 iend = istart + nPmix;
980
981 double s = 0; // Initialize sum to 0
982 for (int i=istart; i<iend; i++) {
983 ime = pLoc[i];
984 s += (*varData[kVar])[ime];
985 }
986 for (int i=istart; i<iend; i++) {
987 ime = pLoc[i];
988 (*varData[kVar])[ime] = s / nPmix;
989 }
990
991 //--------- Mix right branch of iTree
992
993 istart = iend;
994 iend = istart + nPmix;
995
996 s = 0; // initialize sum to 0
997 for (int i=istart; i<iend; i++) {
998 ime = pLoc[i];
999 s += (*varData[kVar])[ime];
1000 }
1001 for (int i=istart; i<iend; i++) {
1002 ime = pLoc[i];
1003 (*varData[kVar])[ime] = s / nPmix;
1004 }
1005}
1006
1029
1031 // Loop through each variable in the HiPS profile
1032 for (int k = 0; k < varData.size(); k++) {
1033 double s; // Temporary variable for summation
1034
1035 //---------- Force the left half of parcels to average 0 ----------
1036
1037 for (int i = 0; i < nparcels >> 1; i++)
1038 s += (*varData[k])[pLoc[i]]; // Calculate the sum of values in the left half of parcels
1039
1040 s /= (nparcels >> 1); // Calculate the average of values in the left half of parcels
1041
1042 for (int i = 0; i < nparcels >> 1; i++)
1043 (*varData[k])[pLoc[i]] += (-s - 0.0); // Adjust values in the left half of parcels to achieve an average of 0
1044
1045 //---------- Force the right half of parcels to average 1 ----------
1046 s = 0.0;
1047
1048 for (int i = nparcels >> 1; i < nparcels; i++)
1049 s += (*varData[k])[pLoc[i]]; // Calculate the sum of values in the right half of parcels
1050
1051 s /= (nparcels >> 1); // Calculate the average of values in the right half of parcels
1052
1053 for (int i = nparcels >> 1; i < nparcels; i++)
1054 (*varData[k])[pLoc[i]] += (-s + 1.0); // Adjust values in the right half of parcels to achieve an average of 1
1055 }
1056}
1057
1085
1086void hips::writeData(int real, const int ifile, const double outputTime) {
1087
1088 stringstream ss1, ss2;
1089 string s1, s2;
1090
1091 // Prepare directory path
1092 ss1 << "../data/rlz_" << setfill('0') << setw(5) << real;
1093 ss1 >> s1;
1094
1095 // Create directories if they don't exist
1096 #ifdef _WIN32
1097 system(("mkdir " + s1).c_str());
1098 #else
1099 system(("mkdir -p " + s1).c_str());
1100 #endif
1101
1102 ss2 << "Data_" << setfill('0') << setw(5) << ifile << ".dat";
1103 ss2 >> s2;
1104
1105 string fname = s1 + "/" + s2;
1106 ofstream ofile(fname.c_str());
1107
1108 // Check if file opened successfully
1109 if (!ofile) {
1110 cerr << "Error: Unable to open file " << fname << " for writing!" << endl;
1111 return;
1112 }
1113
1114 // Write metadata (header information)
1115 ofile << "# time = " << outputTime << "\n";
1116 ofile << "# Grid Points = " << nparcels << "\n";
1117
1118 // Write column names (include temperature if reactions are enabled)
1119 if(performReaction)
1120 ofile << setw(19) << "# Temp"; // Include temperature column if reactions are enabled
1121
1122 for (const auto& varN : varName) {
1123 ofile << setw(19) << "# " << varN;
1124 }
1125 ofile << endl; // End of the header line
1126
1127 // Set scientific notation and precision
1128 ofile << scientific;
1129 ofile << setprecision(10);
1130
1131 // Write data
1132 for (int i = 0; i < nparcels; i++) {
1133 // Write temperature first if reactions are enabled
1134 if(performReaction)
1135 ofile << setw(19) << Temp[pLoc[i]];
1136
1137 // Write variables
1138 for (int k = 0; k < nVar; k++) {
1139 ofile << setw(19) << (*varData[k])[pLoc[i]];
1140 }
1141 ofile << endl;
1142 }
1143
1144 ofile.close();
1145 // cout << "Data successfully written to: " << fname << endl;
1146}
1147
1179
1180std::vector<double> hips::projection_back(std::vector<double> &vh) {
1181
1182 int nh = xh.size() - 1;
1183 int nc = xc.size() - 1;
1184
1185 std::vector<double> vc(nc, 0.0);
1186 int jprev = 0;
1187
1188 for (double val : vc)
1189 std::cout << val << " ";
1190 std::cout << std::endl;
1191
1192 for (int i = 0; i < nc; ++i) {
1193 for (int j = jprev + 1; j <= nh; ++j) {
1194 if (xh[j] <= xc[i + 1]) {
1195 double d1 = xh[j] - xh[j - 1];
1196 double d2 = xh[j] - xc[i];
1197 // calculation of shortest distance
1198 // handling if distance is zero but substarction gives comutational error
1199 double d = std::min(d1, d2) < 1E-15 ? 0 : std::min(d1, d2);
1200
1201 vc[i] += vh[j - 1] * d;
1202 } else {
1203 double d1 = xc[i + 1] - xh[j - 1];
1204 double d2 = xc[i + 1] - xc[i];
1205 // calculation of shortest distance
1206 // handling if distance is zero but substarction gives comutational error
1207 double d = std::min(d1, d2) < 1E-15 ? 0 : std::min(d1, d2);
1208
1209 vc[i] += vh[j - 1] * d;
1210
1211 jprev = j - 1;
1212 break;
1213 }
1214 }
1215 vc[i] /= (xc[i + 1] - xc[i]);
1216 }
1217 return vc;
1218}
1219
1255
1256std::pair<std::vector<double>, std::vector<double>> hips::projection_back_with_density(std::vector<double> &vh,
1257 std::vector<double> &rho_h) {
1258 int nh = xh.size() - 1;
1259 int nc = xc.size() - 1;
1260
1261 std::vector<double> vc(nc, 0.0);
1262 std::vector<double> rho_c(nc, 0.0);
1263 int jprev = 0;
1264
1265 for (int i = 0; i < nc; ++i) {
1266 double total_dx = 0.0;
1267
1268 for (int j = jprev + 1; j <= nh; ++j) {
1269 if (xh[j] <= xc[i + 1]) {
1270 double d = std::min(xh[j] - xh[j - 1], xh[j] - xc[i]);
1271 total_dx += d;
1272 rho_c[i] += rho_h[j - 1] * d;
1273 vc[i] += vh[j - 1] * rho_h[j - 1] * d;
1274 } else {
1275 double d = std::min(xc[i + 1] - xh[j - 1], xc[i + 1] - xc[i]);
1276 total_dx += d;
1277 rho_c[i] += rho_h[j - 1] * d;
1278 vc[i] += vh[j - 1] * rho_h[j - 1] * d;
1279
1280 jprev = j - 1;
1281 break;
1282 }
1283 }
1284
1285 // Normalize the results
1286 vc[i] /= rho_c[i];
1287 }
1288 return {vc, rho_c};
1289}
1290
1310
1311std::vector<std::vector<double>> hips::get_varData(){
1312
1313 std::vector<std::vector<double>> varDataProjections; // Vector to store modified data projections
1314
1315 for (int i = 0; i < varData.size(); i++) // Loop through each element of varData and project the data back
1316 varDataProjections.push_back(projection_back((*varData[i]))); // Project the data back and store it in vh
1317
1318 return varDataProjections; // Return the vector containing modified data projections
1319}
1340
1341std::vector<std::pair<std::vector<double>, std::vector<double>>> hips::get_varData_with_density() {
1342
1343 std::vector<std::pair<std::vector<double>, std::vector<double>>> varDataProjections; // Vector to store data projections with densities
1344
1345 for (int i = 0; i < varData.size(); i++) {
1346 // Extract the value and density data
1347 std::vector<double> vh = (*varData[i]); // Assuming varData[i][0] holds the values
1348 std::vector<double> rho_h = varRho; // Assuming varData[i][1] holds the densities
1349
1350 // Project the data back with density and store the result
1351 varDataProjections.push_back(projection_back_with_density(vh, rho_h));
1352 }
1353
1354 return varDataProjections; // Return the vector containing modified data projections with densities
1355}
1356
1369
1371
1372 outputIntervalEddy = interval;
1373 useEddyBasedWriting = true;
1374 useTimeBasedWriting = false;
1375}
1376
1389
1390void hips::setOutputIntervalTime(double interval) {
1391
1392 outputIntervalTime = interval;
1393 useTimeBasedWriting = true;
1394 useEddyBasedWriting = false;
1395}
1396
1410
1412
1413 std::string filepath = "../post/parameters.dat";
1414 std::ofstream file(filepath);
1415
1416 if (!file) {
1417 std::cerr << "Error: Could not open " << filepath << " for writing!\n";
1418 return;
1419 }
1420
1421 // Write user-defined input parameters
1422 file << "nLevels " << nLevels << "\n";
1423 file << "domainLength " << domainLength << "\n";
1424 file << "tau0 " << tau0 << "\n";
1425 file << "C_param " << C_param << "\n";
1426 file << "forceTurb " << forceTurb << "\n";
1427 file << "nVar " << nVar << "\n";
1428 file << "performReaction " << performReaction << "\n";
1429 file << "realization " << realization << "\n";
1430
1431 // Write variable names
1432 if (!varName.empty()) {
1433 file << "varName ";
1434 for (const std::string &name : varName) {
1435 file << name << " ";
1436 }
1437 file << "\n";
1438 } else {
1439 file << "varName (undefined)\n";
1440 }
1441
1442 // Write i_batchelor vector if it exists and has the correct size
1443 if (!i_batchelor.empty() && i_batchelor.size() == nVar) {
1444 file << "i_batchelor ";
1445 for (const auto &val : i_batchelor) {
1446 file << val << " ";
1447 }
1448 file << "\n";
1449 } else {
1450 file << "i_batchelor (undefined or size mismatch)\n";
1451 }
1452
1453 file.close();
1454 std::cout << "All parameters saved in: " << filepath << std::endl;
1455}
int Nm3
nLevels - 3
Definition hips.h:46
std::vector< double > setGridHips(int N)
Generates a physical domain for HiPS parcels.
Definition hips.cc:560
std::vector< double > parcelTimes
current times corresponding to the parcel states
Definition hips.h:71
double lastOutputTime
Last time data was written.
Definition hips.h:81
int Nm2
nLevels - 2
Definition hips.h:45
int nVar
number of parcel variables (e.g., h, ysp)
Definition hips.h:42
int nLevels_
number of tree levels?
Definition hips.h:40
std::pair< std::vector< double >, std::vector< double > > projection_back_with_density(std::vector< double > &vh, std::vector< double > &rho_h)
Projects HiPS parcel values and densities back onto the flow particles.
Definition hips.cc:1256
int getVariableIndex(const std::string &varName) const
Retrieves the index of a variable by its name in the varName list.
Definition hips.cc:852
hips(double C_param_, int forceTurb_, int nVar_, bool performReaction, std::shared_ptr< void > vcantSol=nullptr, int seed=10, int realization_=1)
Constructor for initializing a HiPS object without building the tree immediately.
std::vector< double > xc
vector containing physical domain of flow particles
Definition hips.h:74
std::vector< int > pLoc
parcel index array for fast implementation of swaps
Definition hips.h:67
bool performReaction
flag indicating whether chemical reactions are performed in the simulation
Definition hips.h:51
std::vector< std::pair< std::vector< double >, std::vector< double > > > get_varData_with_density()
Retrieves final simulation data, including both values and densities.
Definition hips.cc:1341
void forceProfile()
Adjusts the HiPS profile to enforce statistical stationarity.
Definition hips.cc:1030
std::vector< double > xh
vector containing physical domain of HiPS parcels
Definition hips.h:75
int nL
adjusted number of levels based on the Reynolds number
Definition hips.h:48
std::vector< double > projection_back(std::vector< double > &vb)
Projects HiPS parcel values back onto the flow particles.
Definition hips.cc:1180
void setOutputIntervalEddy(int interval)
Sets the interval (in number of eddy events) for writing simulation data.
Definition hips.cc:1370
std::vector< std::vector< double > > get_varData()
Retrieves the final data from the simulation.
Definition hips.cc:1311
std::vector< int > i_plus
ceil(i_batchelor)
Definition hips.h:66
double outputIntervalTime
Default: write data every 0.1s.
Definition hips.h:79
double Afac
level lengthscale reduction factor (0.5)
Definition hips.h:57
double C_param
Eddy frequency parameter.
Definition hips.h:33
int forceTurb
forcing function for statistically stationary: -1 = none, 1 = source term, 2 = dir
Definition hips.h:41
int nsp
number of species
Definition hips.h:43
int realization
number of realizations
Definition hips.h:23
std::string approach
Definition hips.h:77
const int DEFAULT_EDDY_INTERVAL
Default: Write every 1000 eddies.
Definition hips.h:84
void selectAndSwapTwoSubtrees(const int iLevel, int &iTree)
Performs eddy events by swapping parcels within the HiPS tree.
Definition hips.cc:762
void writeData(int real, const int ifile, const double outputTime)
Writes simulation data to a file for a specific realization, time, and file index.
Definition hips.cc:1086
double eddyRate_inertial
total rate of all eddies 0 through iEta (= eddyRate_total if Sc=1)
Definition hips.h:56
double Anew
adjusted level lengthscale reduction factor for dynamic adjustment of reduction factor
Definition hips.h:62
int currentIndex
member variable to keep track of current index of variables
Definition hips.h:38
std::vector< double > i_batchelor
Batchelor level for variable Sc scalars; NOTE: double, as in, between levels.
Definition hips.h:73
void calculateSolution(const double tRun, bool shouldWriteData=false)
Runs the HiPS simulation, advancing the solution using eddy events.
Definition hips.cc:608
randomGenerator rand
Definition hips.h:64
double time
current simulation time
Definition hips.h:54
void sample_hips_eddy(double &dt, int &iLevel)
Samples stochastic eddy events on the HiPS tree, determining the time increment and tree level.
Definition hips.cc:680
void setOutputIntervalTime(double interval)
Sets the interval (in simulation time) for writing simulation data.
Definition hips.cc:1390
std::vector< double > varRho
Definition hips.h:68
void reactParcels_LevelTree(const int iLevel, const int iTree)
Simulates chemical reactions for parcels affected by a micromixing event.
Definition hips.cc:889
bool useTimeBasedWriting
Tracks if time writing is set.
Definition hips.h:83
std::vector< double > levelRates
list of eddy event rates at each level
Definition hips.h:72
std::vector< std::string > varName
vector containing the names of parcel variables
Definition hips.h:70
double Re
Reynolds number.
Definition hips.h:58
bool useEddyBasedWriting
Tracks if eddy writing is set.
Definition hips.h:82
void set_varData(std::vector< double > &v, std::vector< double > &w, const std::string &varN)
Assigns variables, their corresponding weights, and names to the parcels in the HiPS tree.
Definition hips.cc:337
int nparcels
number of parcels
Definition hips.h:22
double Prob
probability value for probability-based solution
Definition hips.h:60
std::vector< double > Temp
Vector containg temperature in each parcel;.
Definition hips.h:34
void mixAcrossLevelTree(int kVar, const int iMixLevel, const int iTree)
Uniformly mixes parcels at a specified level and subtree within the HiPS model.
Definition hips.cc:967
double dtEE
time increment to next eddy event
Definition hips.h:59
int iEta
Kolmogorov level (needed for variable Sc scalars)
Definition hips.h:47
double eddyRate_total
total rate of all eddies 0 through nLevels-3
Definition hips.h:55
double domainLength
length of domain (m)
Definition hips.h:31
int eddyCounter
Counter for eddy events.
Definition hips.h:80
std::vector< std::shared_ptr< std::vector< double > > > varData
vector of pointers to vector
Definition hips.h:24
std::vector< double > projection(std::vector< double > &vcfd, std::vector< double > &weight)
Projects values from flow particles onto HiPS parcels assuming constant density.
Definition hips.cc:400
std::vector< double > setGridCfd(std::vector< double > &w)
Generates a physical domain for flow particles based on their weights.
Definition hips.cc:526
void advanceHips(const int iLevel, const int iTree)
Advances the HiPS model by simulating micromixing and reactions at a specific tree level.
Definition hips.cc:804
double lStar
length of the level associated with the Reynolds number
Definition hips.h:61
int outputIntervalEddy
Default: write data every 10 eddy events.
Definition hips.h:78
double tau0
integral timescale
Definition hips.h:32
void saveAllParameters()
Saves all user-defined to a file.
Definition hips.cc:1411
int nLevels
number of tree levels
Definition hips.h:39
bool LScHips
hips schmidt number
Definition hips.h:50
int Nm1
nLevels - 1
Definition hips.h:44
void set_tree(int nLevels_, double domainLength_, double tau0_, std::vector< double > &ScHips_)
Sets up the HiPS tree using explicitly specified tree parameters.
std::vector< double > ScHips
vector containing Schmidt numbers related to each variable
Definition hips.h:69
int getRandInt(unsigned n)