Nektar++
ForcingSyntheticEddy.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: ForcingSyntheticEddy.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Derived base class - Synthetic turbulence generation.
32// This code implements the Synthetic Eddy Method (SEM).
33//
34///////////////////////////////////////////////////////////////////////////////
35
37#include <ctime>
38#include <fstream>
39#include <iomanip>
40
41namespace Nektar::SolverUtils
42{
43
46 "SyntheticTurbulence", ForcingSyntheticEddy::create,
47 "Synthetic Eddy Turbulence Method");
48
51 const std::weak_ptr<EquationSystem> &pEquation)
52 : Forcing(pSession, pEquation)
53{
54}
55
56/**
57 * @brief Read input from xml file and initialise the class members.
58 * The main parameters are the characteristic lengths, Reynolds
59 * stresses and the synthetic eddy region (box of eddies).
60 *
61 * @param pFields Pointer to fields.
62 * @param pNumForcingField Number of forcing fields.
63 * @param pForce Xml element describing the mapping.
64 */
67 [[maybe_unused]] const unsigned int &pNumForcingFields,
68 [[maybe_unused]] const TiXmlElement *pForce)
69{
70 // Space dimension
71 m_spacedim = pFields[0]->GetGraph()->GetMeshDimension();
72
73 if (m_spacedim != 3)
74 {
76 "Sythetic eddy method "
77 "only supports fully three-dimensional simulations");
78 }
79
80 // Get gamma parameter
81 m_session->LoadParameter("Gamma", m_gamma, 1.4);
82
83 const TiXmlElement *elmtInfTurb;
84
85 // Reynolds Stresses
86 elmtInfTurb = pForce->FirstChildElement("ReynoldsStresses");
87 ASSERTL0(elmtInfTurb, "Requires ReynoldsStresses tag specifying function "
88 "name which prescribes the reynodls stresses.");
89
90 std::string reyStressesFuncName = elmtInfTurb->GetText();
91 ASSERTL0(m_session->DefinesFunction(reyStressesFuncName),
92 "Function '" + reyStressesFuncName +
93 "' is not defined in the session.");
94
95 // Reynolds stresses variables. Do not change the order of the variables.
96 std::vector<std::string> reyStressesVars = {"r00", "r10", "r20",
97 "r11", "r21", "r22"};
98
99 for (size_t i = 0; i < reyStressesVars.size(); ++i)
100 {
101 std::string varStr = reyStressesVars[i];
102 if (m_session->DefinesFunction(reyStressesFuncName, varStr))
103 {
104 m_R[i] = m_session->GetFunction(reyStressesFuncName, varStr);
105 }
106 else
107 {
109 "Missing parameter '" + varStr +
110 "' in the FUNCTION NAME = " + reyStressesFuncName +
111 ".");
112 }
113 }
114
115 // Characteristic length scales
116 elmtInfTurb = pForce->FirstChildElement("CharLengthScales");
117 ASSERTL0(elmtInfTurb, "Requires CharLengthScales tag specifying function "
118 "name which prescribes the characteristic "
119 "length scales.");
120
121 std::string charLenScalesFuncName = elmtInfTurb->GetText();
122 ASSERTL0(m_session->DefinesFunction(charLenScalesFuncName),
123 "Function '" + charLenScalesFuncName +
124 "' is not defined in the session.");
125
126 // Characteristic length scale variables
127 // Do not change the order of the variables
128 std::vector<std::string> lenScalesVars = {"l00", "l10", "l20", "l01", "l11",
129 "l21", "l02", "l12", "l22"};
132
133 for (size_t i = 0; i < lenScalesVars.size(); ++i)
134 {
135 std::string varStr = lenScalesVars[i];
136 if (m_session->DefinesFunction(charLenScalesFuncName, varStr))
137 {
138 clsAux = m_session->GetFunction(charLenScalesFuncName, varStr);
139 m_l[i] = (NekDouble)clsAux->Evaluate();
140 }
141 else
142 {
144 "Missing parameter '" + varStr +
145 "' in the FUNCTION NAME = " + charLenScalesFuncName +
146 ".");
147 }
148 }
149
150 // Read box of eddies parameters
152 // Array<OneD, NekDouble> m_lyz(m_spacedim - 1, 0.0);
154 elmtInfTurb = pForce->FirstChildElement("BoxOfEddies");
155 ASSERTL0(elmtInfTurb,
156 "Unable to find BoxOfEddies tag. in SyntheticTurbulence forcing");
157
158 if (elmtInfTurb)
159 {
160 std::stringstream boxStream;
161 std::string boxStr = elmtInfTurb->GetText();
162 boxStream.str(boxStr);
163 size_t countVar = 0;
164 for (size_t i = 0; i < (2 * m_spacedim - 1); ++i)
165 {
166 boxStream >> boxStr;
167 if (i < m_spacedim)
168 {
169 m_rc[i] = boost::lexical_cast<NekDouble>(boxStr);
170 }
171 else
172 {
173 m_lyz[i - m_spacedim] = boost::lexical_cast<NekDouble>(boxStr);
174 }
175 countVar += 1;
176 }
177 if (countVar != (2 * m_spacedim - 1))
178 {
180 "Missing parameter in the BoxOfEddies tag");
181 }
182 }
183
184 // Read sigma
185 elmtInfTurb = pForce->FirstChildElement("Sigma");
186 ASSERTL0(elmtInfTurb,
187 "Unable to find Sigma tag. in SyntheticTurbulence forcing");
188 std::string sigmaStr = elmtInfTurb->GetText();
189 m_sigma = boost::lexical_cast<NekDouble>(sigmaStr);
190
191 // Read bulk velocity
192 elmtInfTurb = pForce->FirstChildElement("BulkVelocity");
193 ASSERTL0(elmtInfTurb,
194 "Unable to find BulkVelocity tag. in SyntheticTurbulence forcing");
195 std::string bVelStr = elmtInfTurb->GetText();
196 m_Ub = boost::lexical_cast<NekDouble>(bVelStr);
197
198 // Read flag to check if the run is a test case
199 elmtInfTurb = pForce->FirstChildElement("TestCase");
200 const char *tcaseStr = (elmtInfTurb) ? elmtInfTurb->GetText() : "NoName";
201 m_tCase = (strcmp(tcaseStr, "ChanFlow3D") == 0) ? true : false;
202
203 // Set Cholesky decomposition of the Reynolds Stresses in the domain
204 SetCholeskyReyStresses(pFields);
205 // Compute reference lengths
207 // Compute Xi maximum
208 ComputeXiMax();
209 // Set Number of Eddies
211 // Set mask
212 SetBoxOfEddiesMask(pFields);
213 // Set Forcing for each eddy
214 InitialiseForcingEddy(pFields);
215 // Check for test case
216 if (!m_tCase)
217 {
218 // Compute initial location of the eddies in the box
220 }
221 else
222 {
223 // Compute initial location of the eddies for the test case
225 }
226
227 // Seed to generate random positions for the eddies
228 srand(time(nullptr));
229
230 // Initialise member from the base class
232 for (int i = 0; i < pFields.size(); ++i)
233 {
234 m_Forcing[i] = Array<OneD, NekDouble>(pFields[0]->GetTotPoints(), 0.0);
235 }
236
237 // Initialise eddies ID vector
238 for (size_t n = 0; n < m_N; ++n)
239 {
240 m_eddiesIDForcing.push_back(n);
241 }
242}
243
244/**
245 * @brief Apply forcing term if an eddy left the box of eddies and
246 * update the eddies positions.
247 *
248 * @param pFields Pointer to fields.
249 * @param inarray Given fields. The fields are in in physical space.
250 * @param outarray Calculated solution after forcing term being applied
251 * in physical space.
252 * @param time time.
253 */
255 [[maybe_unused]] const Array<OneD, MultiRegions::ExpListSharedPtr> &pFields,
256 [[maybe_unused]] const Array<OneD, Array<OneD, NekDouble>> &inarray,
257 [[maybe_unused]] Array<OneD, Array<OneD, NekDouble>> &outarray,
258 [[maybe_unused]] const NekDouble &time)
259{
260}
261
262/**
263 * @brief Apply forcing term if an eddy left the box of eddies and
264 * update the eddies positions.
265 *
266 * @param pFields Pointer to fields.
267 * @param inarray Given fields. The fields are in in physical space.
268 * @param outarray Calculated solution after forcing term being applied
269 * in coefficient space.
270 * @param time time.
271 */
273 [[maybe_unused]] const Array<OneD, MultiRegions::ExpListSharedPtr> &pFields,
274 [[maybe_unused]] const Array<OneD, Array<OneD, NekDouble>> &inarray,
275 [[maybe_unused]] Array<OneD, Array<OneD, NekDouble>> &outarray,
276 [[maybe_unused]] const NekDouble &time)
277{
278}
279
280/**
281 * @brief Compute characteristic convective turbulent time.
282 *
283 * @param pFields Pointer to fields.
284 */
288{
289 // Total number of quadrature points
290 int nqTot = pFields[0]->GetTotPoints();
291 // Characteristic convective turbulent time
293
294 for (size_t k = 0; k < m_spacedim; ++k)
295 {
296 convTurbTime[k] = Array<OneD, NekDouble>(nqTot, 0.0);
297 for (size_t i = 0; i < nqTot; ++i)
298 {
299 NekDouble convTurbLength = m_xiMaxMin * m_lref[0];
300 // 3*k because of the structure of the m_l parameter
301 // to obtain lxk.
302 if ((m_l[3 * k] > m_xiMaxMin * m_lref[0]) && (m_mask[i]))
303 {
304 convTurbLength = m_l[3 * k];
305 }
306 convTurbTime[k][i] = convTurbLength / m_Ub;
307 }
308 }
309
310 return convTurbTime;
311}
312
313/**
314 * @brief Compute smoothing factor to avoid strong variations
315 * of the source term across the domain.
316 *
317 * @param pFields Pointer to fields.
318 * @param convTurbTime Convective turbulent time.
319 */
323 Array<OneD, Array<OneD, NekDouble>> convTurbTime)
324{
325 // Total number of quadrature points
326 int nqTot = pFields[0]->GetTotPoints();
327 // Number of elements
328 int nElmts = pFields[0]->GetNumElmts();
329 // Total number of quadrature points of each element
330 int nqe;
331 // Characteristic convective turbulent time
333 // Counter
334 int count = 0;
335 // module
336 NekDouble mod;
337 // Create Array with zeros
338 for (size_t i = 0; i < m_spacedim; ++i)
339 {
340 smoothFac[i] = Array<OneD, NekDouble>(nqTot, 0.0);
341 }
342
343 for (size_t e = 0; e < nElmts; ++e)
344 {
345 nqe = pFields[0]->GetExp(e)->GetTotPoints();
346
347 Array<OneD, NekDouble> coords0(nqe, 0.0);
348 Array<OneD, NekDouble> coords1(nqe, 0.0);
349 Array<OneD, NekDouble> coords2(nqe, 0.0);
350
351 pFields[0]->GetExp(e)->GetCoords(coords0, coords1, coords2);
352
353 for (size_t i = 0; i < nqe; ++i)
354 {
355 if (m_mask[count + i])
356 {
357 mod = (coords0[i] - m_rc[0] + m_lref[0]) *
358 (coords0[i] - m_rc[0] + m_lref[0]);
359
360 smoothFac[0][count + i] =
361 exp((-0.5 * M_PI * mod) /
362 (convTurbTime[0][count + i] *
363 convTurbTime[0][count + i] * m_Ub * m_Ub));
364 smoothFac[1][count + i] =
365 exp((-0.5 * M_PI * mod) /
366 (convTurbTime[1][count + i] *
367 convTurbTime[1][count + i] * m_Ub * m_Ub));
368 smoothFac[2][count + i] =
369 exp((-0.5 * M_PI * mod) /
370 (convTurbTime[2][count + i] *
371 convTurbTime[2][count + i] * m_Ub * m_Ub));
372 }
373 }
374 count += nqe;
375 }
376
377 return smoothFac;
378}
379
380/**
381 * @brief Calculate velocity fluctuation for the source term
382 *
383 * @param pFields Pointer to fields.
384 * @param stochasticSignal Stochastic signal.
385 * @return Velocity fluctuation.
386 */
390 Array<OneD, Array<OneD, NekDouble>> stochasticSignal)
391{
392 // Total number of quadrature points
393 int nqTot = pFields[0]->GetTotPoints();
394 // Velocity fluctuation vector
396 // Control loop for the m_Cholesky (Cholesky decomposition matrix)
397 int l;
398
399 for (auto &n : m_eddiesIDForcing)
400 {
401 velFluc[n] = Array<OneD, NekDouble>(nqTot * m_spacedim, 0.0);
402 for (size_t k = 0; k < m_spacedim; ++k)
403 {
404 for (size_t j = 0; j < k + 1; ++j)
405 {
406 for (size_t i = 0; i < nqTot; ++i)
407 {
408 if (m_mask[i])
409 {
410 l = k + j * (2 * m_spacedim - j - 1) * 0.5;
411 velFluc[n][k * nqTot + i] +=
412 m_Cholesky[i][l] *
413 stochasticSignal[n][j * nqTot + i];
414 }
415 }
416 }
417 }
418 }
419
420 return velFluc;
421}
422
423/**
424 * @brief Compute stochastic signal.
425 *
426 * @param pFields Pointer to fields.
427 * @return Stochastic signal.
428 */
432{
433 // Total number of quadrature points
434 int nqTot = pFields[0]->GetTotPoints();
435 // Number of elements
436 int nElmts = pFields[0]->GetNumElmts();
437 // Total number of quadrature points of each element
438 int nqe;
439 // Stochastic Signal vector
440 Array<OneD, Array<OneD, NekDouble>> stochasticSignal(m_N);
441 // Random numbers: -1 and 1
442 Array<OneD, Array<OneD, int>> epsilonSign;
443
444 // Generate only for the new eddies after the first time step
445 epsilonSign = GenerateRandomOneOrMinusOne();
446
447 // Calculate the stochastic signal for the eddies
448 for (auto &n : m_eddiesIDForcing)
449 {
450 stochasticSignal[n] = Array<OneD, NekDouble>(nqTot * m_spacedim, 0.0);
451
452 // Evaluate the function at interpolation points for each component
453 for (size_t j = 0; j < m_spacedim; ++j)
454 {
455 // Count the number of quadrature points
456 int nqeCount = 0;
457
458 for (size_t e = 0; e < nElmts; ++e)
459 {
460 nqe = pFields[0]->GetExp(e)->GetTotPoints();
461
462 Array<OneD, NekDouble> coords0(nqe, 0.0);
463 Array<OneD, NekDouble> coords1(nqe, 0.0);
464 Array<OneD, NekDouble> coords2(nqe, 0.0);
465
466 pFields[0]->GetExp(e)->GetCoords(coords0, coords1, coords2);
467
468 // i: degrees of freedom, j: direction, n: eddies
469 for (size_t i = 0; i < nqe; ++i)
470 {
471 if (m_mask[nqeCount + i])
472 {
473 stochasticSignal[n][j * nqTot + nqeCount + i] =
474 epsilonSign[j][n] *
475 ComputeGaussian((coords0[i] - m_eddyPos[n][0]) /
476 m_lref[0],
477 m_xiMax[j * m_spacedim + 0],
478 ComputeConstantC(0, j)) *
479 ComputeGaussian((coords1[i] - m_eddyPos[n][1]) /
480 m_lref[1],
481 m_xiMax[j * m_spacedim + 1],
482 ComputeConstantC(1, j)) *
483 ComputeGaussian((coords2[i] - m_eddyPos[n][2]) /
484 m_lref[2],
485 m_xiMax[j * m_spacedim + 2],
486 ComputeConstantC(2, j));
487 }
488 }
489 nqeCount += nqe;
490 }
491 }
492 }
493
494 return stochasticSignal;
495}
496
497/**
498 * @brief Update the position of the eddies for every time step.
499 */
501{
502 NekDouble dt = m_session->GetParameter("TimeStep");
503
504 for (size_t n = 0; n < m_N; ++n)
505 {
506 // Check if any eddy went through the outlet plane (box)
507 if ((m_eddyPos[n][0] + m_Ub * dt) < (m_rc[0] + m_lref[0]))
508 {
509 m_eddyPos[n][0] = m_eddyPos[n][0] + m_Ub * dt;
510 }
511 else
512 {
513 // Generate a new one in the inlet plane
514 m_eddyPos[n][0] = m_rc[0] - m_lref[0];
515 // Check if test case
516 if (!m_tCase)
517 {
518 m_eddyPos[n][1] =
519 (m_rc[1] - 0.5 * m_lyz[0]) +
520 (NekSingle(std::rand()) / NekSingle(RAND_MAX)) * (m_lyz[0]);
521 m_eddyPos[n][2] =
522 (m_rc[2] - 0.5 * m_lyz[1] + 0.5 * m_lref[2]) +
523 (NekSingle(std::rand()) / NekSingle(RAND_MAX)) * (m_lyz[1]);
524 }
525 else
526 {
527 // same place (center) for the test case
528 m_eddyPos[n][1] = m_rc[1];
529 m_eddyPos[n][2] = m_rc[2];
530 }
531 m_eddiesIDForcing.push_back(n);
532 m_calcForcing = true;
533 }
534 }
535}
536
537/**
538 * @brief Remove eddy from forcing term
539 *
540 * @param pFields Pointer to fields.
541 */
544{
545 // Total number of quadrature points
546 int nqTot = pFields[0]->GetTotPoints();
547 // Number of Variables
548 int nVars = pFields.size();
549
550 for (auto &n : m_eddiesIDForcing)
551 {
552 for (size_t j = 0; j < nVars; ++j)
553 {
554 for (size_t i = 0; i < nqTot; ++i)
555 {
556 m_Forcing[j][i] -= m_ForcingEddy[n][j][i];
557 }
558 }
559 }
560}
561
562/**
563 * @brief Calculate distribution of eddies in the box.
564 */
566{
568
569 for (size_t n = 0; n < m_N; ++n)
570 {
572 // Generate randomly eddies inside the box
573 m_eddyPos[n][0] =
574 (m_rc[0] - m_lref[0]) +
575 (NekSingle(std::rand()) / NekSingle(RAND_MAX)) * 2 * m_lref[0];
576 m_eddyPos[n][1] =
577 (m_rc[1] - 0.5 * m_lyz[0]) +
578 (NekSingle(std::rand()) / NekSingle(RAND_MAX)) * m_lyz[0];
579 m_eddyPos[n][2] =
580 (m_rc[2] - 0.5 * m_lyz[1]) +
581 (NekSingle(std::rand()) / NekSingle(RAND_MAX)) * m_lyz[1];
582 }
583}
584
585/**
586 * @brief Compute standard Gaussian with zero mean.
587 *
588 * @param coord Coordianate.
589 * @return Gaussian value for the coordinate.
590 */
592 NekDouble xiMaxVal,
593 NekDouble constC)
594{
595 NekDouble epsilon = 1e-6;
596 if (abs(coord) <= (xiMaxVal + epsilon))
597 {
598 return ((1.0 / (m_sigma * sqrt(2.0 * M_PI * constC))) *
599 exp(-0.5 * (coord / m_sigma) * (coord / m_sigma)));
600 }
601 else
602 {
603 return 0.0;
604 }
605}
606
607/**
608 * @brief Compute constant C for the gaussian funcion.
609 *
610 * @param row index for the rows of the matrix.
611 * @param col index for the columns of the matrix.
612 * @return Value of C.
613 */
615{
616 NekDouble sizeLenScale = m_xiMax[col * m_spacedim + row];
617
618 // Integration
619 NekDouble sum = 0.0;
620 NekDouble step = 0.025;
621 NekDouble xi0 = -1;
622 NekDouble xif = 1;
623 while (xi0 < xif)
624 {
625 sum += (ComputeGaussian(xi0 + step, sizeLenScale) *
626 ComputeGaussian(xi0 + step, sizeLenScale) +
627 ComputeGaussian(xi0, sizeLenScale) *
628 ComputeGaussian(xi0, sizeLenScale));
629 xi0 += step;
630 }
631
632 return (0.5 * 0.5 * step * sum);
633}
634
635/**
636 * @brief Generate random 1 or -1 values to be use to compute
637 * the stochastic signal.
638 * @return ramdom 1 or -1 values.
639 */
642{
644
645 // j: component of the stochastic signal
646 // n: eddy
647 for (size_t j = 0; j < m_spacedim; ++j)
648 {
649 epsilonSign[j] = Array<OneD, int>(m_N, 0.0);
650 for (auto &n : m_eddiesIDForcing)
651 {
652 // Convert to -1 or to 1
653 // Check if test case
654 if (!m_tCase)
655 {
656 epsilonSign[j][n] =
657 ((NekSingle(std::rand()) / NekSingle(RAND_MAX)) <= 0.5) ? -1
658 : 1;
659 }
660 else
661 {
662 // Positive values only for the test case
663 epsilonSign[j][n] = 1;
664 }
665 }
666 }
667
668 return epsilonSign;
669}
670
671/**
672 * @brief Set box of eddies mask to be used to seprate the
673 * degrees of freedom (coordinates) inside and outside
674 * the box of eddies.
675 *
676 * @param pFields Pointer to fields.
677 */
680{
681 // Total number of quadrature points
682 int nqTot = pFields[0]->GetTotPoints();
683 // Number of elements
684 int nElmts = pFields[0]->GetNumElmts();
685 // Total number of quadrature points of each element
686 int nqe;
687 // Mask
688 m_mask = Array<OneD, int>(nqTot, 0); // 0 for ouside, 1 for inside
689 // Counter
690 int count = 0;
691
692 for (size_t e = 0; e < nElmts; ++e)
693 {
694 nqe = pFields[0]->GetExp(e)->GetTotPoints();
695
696 Array<OneD, NekDouble> coords0(nqe, 0.0);
697 Array<OneD, NekDouble> coords1(nqe, 0.0);
698 Array<OneD, NekDouble> coords2(nqe, 0.0);
699
700 pFields[0]->GetExp(e)->GetCoords(coords0, coords1, coords2);
701
702 for (size_t i = 0; i < nqe; ++i)
703 {
704 if (InsideBoxOfEddies(coords0[i], coords1[i], coords2[i]))
705 {
706 // 0 for outside, 1 for inside
707 m_mask[count + i] = 1;
708 }
709 }
710 count += nqe;
711 }
712}
713
714/**
715 * @brief Initialise Forcing term for each eddy.
716 */
719{
720 // Total number of quadrature points
721 int nqTot = pFields[0]->GetTotPoints();
722 // Number of Variables
723 int nVars = pFields.size();
725
726 for (size_t i = 0; i < m_N; ++i)
727 {
729 for (size_t j = 0; j < nVars; ++j)
730 {
732 for (size_t k = 0; k < nqTot; ++k)
733 {
734 m_ForcingEddy[i][j][k] = 0.0;
735 }
736 }
737 }
738}
739
740/**
741 * @brief Check it point is inside the box of eddies.
742 *
743 * @param coord0 coordinate in the x-direction.
744 * @param coord1 coordinate in the y-direction.
745 * @param coord2 coordinate in the z-direction.
746 * @return true or false
747 */
749 NekDouble coord2)
750{
751 NekDouble tol = 1e-6;
752 if ((coord0 >= (m_rc[0] - m_lref[0] - m_lref[0])) &&
753 (coord0 <= (m_rc[0] + m_lref[0] + tol)) &&
754 (coord1 >= (m_rc[1] - 0.5 * m_lyz[0] - tol)) &&
755 (coord1 <= (m_rc[1] + 0.5 * m_lyz[0] + tol)) &&
756 (coord2 >= (m_rc[2] - 0.5 * m_lyz[1] - tol)) &&
757 (coord2 <= (m_rc[2] + 0.5 * m_lyz[1] + tol)))
758 {
759 return true;
760 }
761
762 return false;
763}
764
765/**
766 * @brief Calculates the reference lenghts
767 */
769{
771 m_lref[0] = m_l[0];
772 m_lref[1] = m_l[1];
773 m_lref[2] = m_l[2];
774
775 // The l_{x}^{ref}, l_{y}^{ref} and l_{z}^{ref}
776 // are the maximum among the velocity components
777 // over the domain.
778 for (size_t i = 0; i < m_spacedim; ++i)
779 {
780 for (size_t j = 1; j < m_spacedim; ++j)
781 {
782 if (m_l[m_spacedim * j + i] > m_lref[i])
783 {
784 m_lref[i] = m_l[m_spacedim * j + i];
785 }
786 }
787 }
788}
789
790/**
791 * @brief Calculates the \f$\xi_{max}\f$.
792 */
794{
795 NekDouble value;
797
798 for (size_t i = 0; i < m_spacedim; i++)
799 {
800 for (size_t j = 0; j < m_spacedim; j++)
801 {
802 value = (m_l[m_spacedim * j + i] / m_lref[i]);
803 if (value > m_xiMaxMin)
804 {
805 m_xiMax[m_spacedim * j + i] = value;
806 }
807 else
808 {
809 m_xiMax[m_spacedim * j + i] = m_xiMaxMin;
810 }
811 }
812 }
813}
814
815/**
816 * @brief Calculates the Cholesky decomposition of the Reynolds Stresses
817 * in each degree of freedom of the mesh.
818 *
819 * @param pFields Pointer to fields.
820 */
823{
824 // Total number of quadrature points
825 int nqTot = pFields[0]->GetTotPoints();
826 // Total number of quadrature points of each element
827 int nqe;
828 // Number of elements
829 int nElmts = pFields[0]->GetNumElmts();
830 // Count the degrees of freedom
831 int nqeCount = 0;
832 // Block diagonal size
833 int diagSize = m_spacedim * (m_spacedim + 1) * 0.5;
834 // Cholesky decomposition matrix for the synthetic eddy region (box)
836
837 for (size_t e = 0; e < nElmts; ++e)
838 {
839 nqe = pFields[0]->GetExp(e)->GetTotPoints();
840
841 Array<OneD, NekDouble> coords0(nqe, 0.0);
842 Array<OneD, NekDouble> coords1(nqe, 0.0);
843 Array<OneD, NekDouble> coords2(nqe, 0.0);
844
845 // Coordinates (for each degree of freedom) for the element k.
846 pFields[0]->GetExp(e)->GetCoords(coords0, coords1, coords2);
847
848 // Evaluate Cholesky decomposition
849 for (size_t i = 0; i < nqe; ++i)
850 {
851 int l = 0;
852 // Size of Cholesky decomposition matrix - aux vector
853 Array<OneD, NekDouble> A(diagSize, 0.0);
854
855 while (l < diagSize)
856 {
857 // Variable to compute the Cholesky decomposition for each
858 // degree of freedom
859 A[l] = m_R[l]->Evaluate(coords0[i], coords1[i], coords2[i]);
860 l++;
861 }
862 int info = 0;
863 Lapack::Dpptrf('L', m_spacedim, A.get(), info);
864 if (info < 0)
865 {
866 std::string message =
867 "ERROR: The " + std::to_string(-info) +
868 "th parameter had an illegal parameter for dpptrf";
869 NEKERROR(ErrorUtil::efatal, message.c_str());
870 }
871 m_Cholesky[nqeCount + i] = Array<OneD, NekDouble>(diagSize);
872 for (size_t l = 0; l < diagSize; ++l)
873 {
874 m_Cholesky[nqeCount + i][l] = A[l];
875 }
876 }
877 nqeCount += nqe;
878 }
879}
880
881/**
882 * @brief Calculate the number of eddies that are going to be
883 * injected in the synthetic eddy region (box).
884 */
886{
887 m_N = int((m_lyz[0] * m_lyz[1]) /
888 (4 * m_lref[m_spacedim - 2] * m_lref[m_spacedim - 1])) +
889 1;
890}
891
892/**
893 * @brief Place eddies in specific locations in the test case
894 * geometry for consistency and comparison.
895 *
896 * This function was design for a three-dimensional
897 * channel flow test case (ChanFlow3d_infTurb).
898 * It is only called for testing purposes (unit test).
899 */
901{
902 m_N = 3; // Redefine number of eddies
904
905 // First eddy (center)
907 m_eddyPos[0][0] = (m_rc[0] - m_lref[0]) + 0.6 * 2 * m_lref[0];
908 m_eddyPos[0][1] = m_rc[1];
909 m_eddyPos[0][2] = m_rc[2];
910
911 // Second eddy (top)
913 m_eddyPos[1][0] = (m_rc[0] - m_lref[0]) + 0.7 * 2 * m_lref[0];
914 m_eddyPos[1][1] = (m_rc[1] - 0.5 * m_lyz[0]) + 0.9 * m_lyz[0];
915 m_eddyPos[1][2] = m_rc[2];
916
917 // Third eddy (bottom)
919 m_eddyPos[2][0] = (m_rc[0] - m_lref[0]) + 0.8 * 2 * m_lref[0];
920 m_eddyPos[2][1] = (m_rc[1] - 0.5 * m_lyz[0]) + 0.1 * m_lyz[0];
921 m_eddyPos[2][2] = m_rc[2];
922}
923
924} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:202
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Defines a forcing term to be explicitly applied.
Definition: Forcing.h:71
Array< OneD, Array< OneD, NekDouble > > m_Forcing
Evaluated forcing function.
Definition: Forcing.h:119
LibUtilities::SessionReaderSharedPtr m_session
Session reader.
Definition: Forcing.h:115
SOLVER_UTILS_EXPORT void SetBoxOfEddiesMask(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Set box of eddies mask.
std::map< int, LibUtilities::EquationSharedPtr > m_R
Array< OneD, NekDouble > m_lyz
Inlet length in the y-direction and z-direction.
Array< OneD, NekDouble > m_lref
Reference lenghts.
SOLVER_UTILS_EXPORT Array< OneD, Array< OneD, NekDouble > > ComputeSmoothingFactor(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, Array< OneD, Array< OneD, NekDouble > > convTurb)
Compute Smoothing Factor.
SOLVER_UTILS_EXPORT void v_ApplyCoeff(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const Array< OneD, Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble &time) override
Apply forcing term if an eddy left the box of eddies and update the eddies positions.
Array< OneD, Array< OneD, Array< OneD, NekDouble > > > m_ForcingEddy
Forcing for each eddy.
SOLVER_UTILS_EXPORT ForcingSyntheticEddy(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< EquationSystem > &pEquation)
SOLVER_UTILS_EXPORT NekDouble ComputeConstantC(int row, int col)
Compute Constant C.
Array< OneD, NekDouble > m_l
Characteristic lenght scales.
static SOLVER_UTILS_EXPORT std::string className
Name of the class.
SOLVER_UTILS_EXPORT Array< OneD, Array< OneD, NekDouble > > ComputeStochasticSignal(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Compute Stochastic Signal.
SOLVER_UTILS_EXPORT Array< OneD, Array< OneD, int > > GenerateRandomOneOrMinusOne()
Compute Random 1 or -1 value.
SOLVER_UTILS_EXPORT Array< OneD, Array< OneD, NekDouble > > ComputeVelocityFluctuation(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, Array< OneD, Array< OneD, NekDouble > > stochasticSignal)
Compute Velocity Fluctuation.
SOLVER_UTILS_EXPORT void UpdateEddiesPositions()
Update positions of the eddies inside the box.
SOLVER_UTILS_EXPORT NekDouble ComputeGaussian(NekDouble coord, NekDouble xiMaxVal, NekDouble constC=1.0)
Compute Gaussian.
std::vector< unsigned int > m_eddiesIDForcing
Eddies that add to the domain.
SOLVER_UTILS_EXPORT void SetNumberOfEddies()
Set the Number of the Eddies in the box.
SOLVER_UTILS_EXPORT void InitialiseForcingEddy(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Initialise forcing term for each eddy.
Array< OneD, NekDouble > m_rc
Center of the inlet plane.
SOLVER_UTILS_EXPORT void v_Apply(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const Array< OneD, Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble &time) override
Apply forcing term if an eddy left the box of eddies and update the eddies positions.
SOLVER_UTILS_EXPORT void ComputeInitialLocationTestCase()
Compute the initial location of the eddies for the test case.
bool m_calcForcing
Check when the forcing should be applied.
SOLVER_UTILS_EXPORT void v_InitObject(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const unsigned int &pNumForcingFields, const TiXmlElement *pForce) override
Read input from xml file and initialise the class members. The main parameters are the characteristic...
static SOLVER_UTILS_EXPORT ForcingSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< EquationSystem > &pEquation, const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const unsigned int &pNumForcingFields, const TiXmlElement *pForce)
Creates an instance of this class.
SOLVER_UTILS_EXPORT void ComputeInitialRandomLocationOfEddies()
Compute the initial position of the eddies inside the box.
SOLVER_UTILS_EXPORT void ComputeXiMax()
Set Xi max.
Array< OneD, NekDouble > m_xiMax
Xi max.
SOLVER_UTILS_EXPORT void RemoveEddiesFromForcing(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Remove eddy from forcing term.
Array< OneD, Array< OneD, NekDouble > > m_Cholesky
Cholesky decomposition of the Reynolds Stresses for all points in the mesh.
SOLVER_UTILS_EXPORT Array< OneD, Array< OneD, NekDouble > > ComputeCharConvTurbTime(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Compute Characteristic Convective Turbulent Time.
SOLVER_UTILS_EXPORT void ComputeRefLenghts()
Set reference lengths.
Array< OneD, int > m_mask
Box of eddies mask.
SOLVER_UTILS_EXPORT bool InsideBoxOfEddies(NekDouble coord0, NekDouble coord1, NekDouble coord2)
Check if point is inside the box of eddies.
SOLVER_UTILS_EXPORT void SetCholeskyReyStresses(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Calculates the Cholesky decomposition of the Reynolds Stresses in each degree of freedom of the mesh.
Array< OneD, Array< OneD, NekDouble > > m_eddyPos
Eddy position.
NekDouble m_gamma
Ration of specific heats.
static void Dpptrf(const char &uplo, const int &n, double *ap, int &info)
Cholesky factor a real positive definite packed-symmetric matrix.
Definition: Lapack.hpp:199
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Equation > EquationSharedPtr
Definition: Equation.h:125
ForcingFactory & GetForcingFactory()
Declaration of the forcing factory singleton.
Definition: Forcing.cpp:42
double NekDouble
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294