Nektar++
Loading...
Searching...
No Matches
FilterReynoldsStresses.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: FilterReynoldsStresses.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: Append Reynolds stresses to the average fields
32//
33///////////////////////////////////////////////////////////////////////////////
34
36
37namespace Nektar::SolverUtils
38{
39
41 GetFilterFactory().RegisterCreatorFunction("ReynoldsStresses",
43
44/**
45 * @class FilterReynoldsStresses
46 *
47 * @brief Append Reynolds stresses to the average fields
48 *
49 * This class appends the average fields with the Reynolds stresses of the form
50 * \f$ \overline{u' v'} \f$.
51 *
52 * For the default case, this is achieved by calculating
53 * \f$ C_{n} = \Sigma_{i=1}^{n} (u_i - \bar{u}_n)(v_i - \bar{v}_n)\f$
54 * using the recursive relation:
55 *
56 * \f[ C_{n} = C_{n-1} + \frac{n}{n-1} (u_n - \bar{u}_n)(v_n - \bar{v}_n) \f]
57 *
58 * The FilterSampler base class then divides the result by n, leading
59 * to the Reynolds stress.
60 *
61 * It is also possible to perform the averages using an exponential moving
62 * average, in which case either the moving average parameter \f$ \alpha \f$
63 * or the time constant \f$ \tau \f$ must be prescribed.
64 */
67 const std::shared_ptr<SolverUtils::EquationSystem> &pEquation,
68 const std::map<std::string, std::string> &pParams)
69 : FilterFieldConvert(pSession, pEquation, pParams)
70{
71 // Load sampling frequency
72 auto it = pParams.find("SampleFrequency");
73 if (it == pParams.end())
74 {
76 }
77 else
78 {
79 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
80 m_sampleFrequency = round(equ.Evaluate());
81 }
82
83 // Check if we should use moving average
84 it = pParams.find("MovingAverage");
85 if (it == pParams.end())
86 {
87 m_movAvg = false;
88 }
89 else
90 {
91 std::string sOption = it->second.c_str();
92 m_movAvg = (boost::iequals(sOption, "true")) ||
93 (boost::iequals(sOption, "yes"));
94 }
95
96 // Check if highOrder is switched on
97 it = pParams.find("ScaleNumModes");
98 if (it == pParams.end())
99 {
100 m_Scale = false;
101 }
102 else
103 {
104 m_Scale = true;
105 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
107 }
108
109 if (!m_movAvg)
110 {
111 return;
112 }
113
114 // Load alpha parameter for moving average
115 it = pParams.find("alpha");
116 if (it == pParams.end())
117 {
118 it = pParams.find("tau");
119 if (it == pParams.end())
120 {
121 ASSERTL0(false, "MovingAverage needs either alpha or tau.");
122 }
123 else
124 {
125 // Load time constant
126 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
127 NekDouble tau = equ.Evaluate();
128 // Load delta T between samples
129 NekDouble dT;
130 m_session->LoadParameter("TimeStep", dT);
131 dT = dT * m_sampleFrequency;
132 // Calculate alpha
133 m_alpha = dT / (tau + dT);
134 }
135 }
136 else
137 {
138 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
139 m_alpha = equ.Evaluate();
140 // Check if tau was also defined
141 it = pParams.find("tau");
142 if (it != pParams.end())
143 {
145 "Cannot define both alpha and tau in MovingAverage");
146 }
147 }
148 // Check bounds of m_alpha
149 ASSERTL0(m_alpha > 0 && m_alpha < 1, "Alpha out of bounds.");
150}
151
154 const NekDouble &time)
155{
156 size_t dim = pFields.size() - 1;
157 size_t nExtraFields = (dim + 1) * dim / 2;
158 size_t origFields = pFields.size();
159 size_t nqtot = pFields[0]->GetTotPoints();
160 bool waveSpace = pFields[0]->GetWaveSpace();
161
162 if (m_Scale)
163 {
164 // Getting mesh
165 SpatialDomains::MeshGraphSharedPtr graph = pFields[0]->GetGraph();
166
167 // Initialising high order m_pFieldsInterp
168 int nvariables = m_session->GetVariables().size();
171
173 graph->GetExpansionInfo(m_session->GetVariable(0));
174
176 SpatialDomains::ExpansionInfoMap>::AllocateSharedPtr();
177
178 for (auto expIt = expInfo.begin(); expIt != expInfo.end(); ++expIt)
179 {
180 int expSpDim = expIt->second->m_basisKeyVector.size();
181
182 std::vector<LibUtilities::BasisKey> BKeyVector;
183 std::vector<int> oldPts(expSpDim);
184
185 for (int i = 0; i < expSpDim; ++i)
186 {
188 expIt->second->m_basisKeyVector[i];
189 oldPts[i] = bkey.GetNumPoints();
190 }
191
192 for (int i = 0; i < expSpDim; ++i)
193 {
194 LibUtilities::BasisKey bkeyold =
195 expIt->second->m_basisKeyVector[i];
196 int newNumModes =
197 static_cast<int>(m_ScaleNumModes * bkeyold.GetNumModes());
198
199 int npts;
200 // 1D
201 if (i == 0)
202 {
203 npts = static_cast<int>(oldPts[0] * m_ScaleNumModes);
204 }
205 else // 2D and 3D
206 {
207 npts =
208 (oldPts[0] - oldPts[i] == 1)
209 ? static_cast<int>(oldPts[0] * m_ScaleNumModes - 1)
210 : static_cast<int>(oldPts[i] * m_ScaleNumModes);
211 }
212
213 const LibUtilities::PointsKey pkey(npts,
214 bkeyold.GetPointsType());
215 LibUtilities::BasisKey bkeynew(bkeyold.GetBasisType(),
216 newNumModes, pkey);
217 BKeyVector.push_back(bkeynew);
218 }
219
220 (*expInfoScaled)[expIt->first] =
222 expIt->second->m_geomPtr, BKeyVector);
223 }
224
225 graph->SetExpansionInfo("Highorder", expInfoScaled);
226
227 for (int i = 0; i < pFields.size(); ++i)
228 {
229 if (waveSpace)
230 {
231 ASSERTL0(pFields[0]->GetExpType() == MultiRegions::e3DH1D,
232 "Nummodes scaling for Reynolds stresses is "
233 "implemented for 3DH1D expansions only.");
234
235 int npointsZ;
236 NekDouble LhomZ;
237 bool useFFT, homogen_dealiasing;
238
239 m_session->LoadParameter("HomModesZ", npointsZ);
240 m_session->LoadParameter("LZ", LhomZ);
241 m_session->MatchSolverInfo("USEFFT", "FFTW", useFFT, false);
242 m_session->MatchSolverInfo("DEALIASING", "True",
243 homogen_dealiasing, false);
244
245 const LibUtilities::PointsKey PkeyZ(
248 npointsZ, PkeyZ);
249
250 m_pFieldsScaled[i] =
252 AllocateSharedPtr(m_session, BkeyZ, LhomZ, useFFT,
253 homogen_dealiasing, graph,
254 "Highorder", Collections::eNoImpType);
255 }
256 else
257 {
258 m_pFieldsScaled[i] =
260 m_session, graph, false, "Highorder",
262 }
263 }
264
265 nqtot = m_pFieldsScaled[0]->GetTotPoints();
266 WARNINGL0(nqtot != pFields[0]->GetTotPoints(),
267 "The scaled number of modes did not increase in the Reynolds "
268 "Stress filter, "
269 "please increase scaled factor further.")
270 }
271
272 // Allocate storage
273 m_fields.resize(origFields + nExtraFields);
274 m_delta.resize(dim);
275 // Initialising average fields
276 for (size_t n = 0; n < m_fields.size(); ++n)
277 {
278 m_fields[n] = Array<OneD, NekDouble>(nqtot, 0.0);
279 }
280 // Initialising fluctuating fields
281 for (size_t n = 0; n < m_delta.size(); ++n)
282 {
283 m_delta[n] = Array<OneD, NekDouble>(nqtot, 0.0);
284 }
285
286 // Initialise output arrays
287 if (m_Scale)
288 {
290 }
291 else
292 {
294 }
295
296 // Update m_fields if using restart file
297 if (m_numSamples)
298 {
299 for (size_t j = 0; j < m_fields.size(); ++j)
300 {
301 pFields[0]->BwdTrans(m_outFields[j], m_fields[j]);
302 if (pFields[0]->GetWaveSpace())
303 {
304 pFields[0]->HomogeneousBwdTrans(nqtot, m_fields[j],
305 m_fields[j]);
306 }
307 }
308 }
309}
310
313{
314 size_t dim = pFields.size() - 1;
315 size_t origFields = pFields.size();
316
317 // Fill name of variables
318 for (size_t n = 0; n < origFields; ++n)
319 {
320 m_variables.push_back(pFields[n]->GetSession()->GetVariable(n));
321 }
322 for (int i = 0; i < dim; ++i)
323 {
324 for (int j = i; j < dim; ++j)
325 {
326 std::string var = pFields[i]->GetSession()->GetVariable(i) +
327 pFields[j]->GetSession()->GetVariable(j);
328 m_variables.push_back(var);
329 }
330 }
331}
332
335 [[maybe_unused]] std::vector<Array<OneD, NekDouble>> &fieldcoeffs,
336 [[maybe_unused]] const NekDouble &time)
337{
338 size_t i, j, n;
339 size_t dim = pFields.size() - 1;
340 bool waveSpace = pFields[0]->GetWaveSpace();
341 NekDouble nSamples = (NekDouble)m_numSamples;
342 size_t nq = pFields[0]->GetTotPoints();
343 size_t ncoeffs = pFields[0]->GetNcoeffs();
344
345 if (m_Scale)
346 {
347 nq = m_pFieldsScaled[0]->GetTotPoints();
348 ncoeffs = m_pFieldsScaled[0]->GetNcoeffs();
349 }
350
351 // For moving average, take first sample as initial vector
352 NekDouble alpha = m_alpha;
353 if (m_numSamples == 1)
354 {
355 alpha = 1.0;
356 }
357
358 // Define auxiliary constants for averages
359 NekDouble facOld, facAvg, facStress, facDelta;
360 if (m_movAvg)
361 {
362 facOld = 1.0 - alpha;
363 facAvg = alpha;
364 facStress = alpha;
365 facDelta = 1.0;
366 }
367 else
368 {
369 facOld = 1.0;
370 facAvg = 1.0;
371 facStress = nSamples / (nSamples - 1);
372 facDelta = 1.0 / nSamples;
373 }
374
377
378 // Update original velocities in phys space and calculate (\bar{u} - u_n)
379 for (n = 0; n < dim; ++n)
380 {
381 if (waveSpace)
382 {
383 if (m_Scale)
384 {
385 // BwdTrans into phys space before interpolating onto high-order
386 // grid
387 Array<OneD, NekDouble> phys(pFields[n]->GetTotPoints());
388 pFields[n]->HomogeneousBwdTrans(pFields[n]->GetTotPoints(),
389 pFields[n]->GetPhys(), phys);
390 pFields[0]->PhysInterp1DScaled(m_ScaleNumModes, phys, vel);
391 }
392 else
393 {
394 pFields[n]->HomogeneousBwdTrans(nq, pFields[n]->GetPhys(), vel);
395 }
396 }
397 else
398 {
399 if (m_Scale)
400 {
401 // Interpolate phys-field by ScaleNumModes
402 pFields[0]->PhysInterp1DScaled(m_ScaleNumModes,
403 pFields[n]->GetPhys(), vel);
404 }
405 else
406 {
407 vel = pFields[n]->GetPhys();
408 }
409 }
410
411 Vmath::Svtsvtp(nq, facAvg, vel, 1, facOld, m_fields[n], 1, m_fields[n],
412 1);
413 Vmath::Svtvm(nq, facDelta, m_fields[n], 1, vel, 1, m_delta[n], 1);
414 }
415
416 // Update pressure (directly to outFields)
417 if (m_Scale)
418 {
419 Array<OneD, NekDouble> wsp1(nq);
420 Array<OneD, NekDouble> wsp2(ncoeffs);
421
422 if (waveSpace)
423 {
424 Array<OneD, NekDouble> phys(pFields[dim]->GetTotPoints());
425 pFields[dim]->HomogeneousBwdTrans(pFields[dim]->GetTotPoints(),
426 pFields[dim]->GetPhys(), phys);
427
428 // Interpolate phys-field by ScaleNumModes
429 pFields[dim]->PhysInterp1DScaled(m_ScaleNumModes, phys, wsp1);
430
431 m_pFieldsScaled[dim]->FwdTransLocalElmt(wsp1, wsp2);
432 Vmath::Svtsvtp(m_outFields[dim].size(), facAvg, wsp2, 1, facOld,
433 m_outFields[dim], 1, m_outFields[dim], 1);
434 }
435 else
436 {
437 // Interpolate phys-field by ScaleNumModes
438 pFields[dim]->PhysInterp1DScaled(m_ScaleNumModes,
439 pFields[dim]->GetPhys(), wsp1);
440 m_pFieldsScaled[dim]->FwdTransLocalElmt(wsp1, wsp2);
441 Vmath::Svtsvtp(m_outFields[dim].size(), facAvg, wsp2, 1, facOld,
442 m_outFields[dim], 1, m_outFields[dim], 1);
443 }
444 }
445 else
446 {
447 Vmath::Svtsvtp(m_outFields[dim].size(), facAvg,
448 pFields[dim]->GetCoeffs(), 1, facOld, m_outFields[dim],
449 1, m_outFields[dim], 1);
450 }
451
452 // Ignore Reynolds stress for first sample (its contribution is zero)
453 if (m_numSamples == 1)
454 {
455 return;
456 }
457
458 // Calculate C_{n} = facOld * C_{n-1} + facStress * deltaI * deltaJ
459 for (i = 0, n = dim + 1; i < dim; ++i)
460 {
461 for (j = i; j < dim; ++j, ++n)
462 {
463 Vmath::Vmul(nq, m_delta[i], 1, m_delta[j], 1, tmp, 1);
464 Vmath::Svtsvtp(nq, facStress, tmp, 1, facOld, m_fields[n], 1,
465 m_fields[n], 1);
466 }
467 }
468}
469
472 [[maybe_unused]] const NekDouble &time)
473{
474 size_t dim = pFields.size() - 1;
475
476 m_fieldMetaData["NumberOfFieldDumps"] = std::to_string(m_numSamples);
477
478 // Set wavespace to false, as calculations were performed in physical space
479 bool waveSpace = pFields[0]->GetWaveSpace();
480 pFields[0]->SetWaveSpace(false);
481
482 // Forward transform and put into m_outFields (except pressure)
483 for (size_t i = 0; i < m_fields.size(); ++i)
484 {
485 if (i != dim)
486 {
487 if (m_Scale)
488 {
489 m_pFieldsScaled[0]->FwdTransLocalElmt(m_fields[i],
490 m_outFields[i]);
491 }
492 else
493 {
494 pFields[0]->FwdTransLocalElmt(m_fields[i], m_outFields[i]);
495 }
496 }
497 }
498
499 // Restore waveSpace
500 pFields[0]->SetWaveSpace(waveSpace);
501}
502
504{
505 if (m_movAvg)
506 {
507 return 1.0;
508 }
509 else
510 {
511 return 1.0 / m_numSamples;
512 }
513}
514
517{
518 NekDouble scale = v_GetScale();
519 for (int n = 0; n < m_outFields.size(); ++n)
520 {
521 Vmath::Smul(m_outFields[n].size(), scale, m_outFields[n], 1,
522 m_outFields[n], 1);
523 }
524
525 // Generating high order field
526 if (m_Scale)
527 {
529 }
530 else
531 {
532 CreateFields(pFields);
533 }
534
535 // Determine new file name
536 std::stringstream tmpOutname;
537 std::string outname;
538 int dot = m_outputFile.find_last_of('.');
539 std::string name = m_outputFile.substr(0, dot);
540 std::string ext = m_outputFile.substr(dot, m_outputFile.length() - dot);
541 std::string suffix = v_GetFileSuffix();
542
543 if (dump == -1) // final dump
544 {
545 tmpOutname << name << suffix << ext;
546 }
547 else
548 {
549 tmpOutname << name << "_" << dump << suffix << ext;
550 }
551 outname = Filter::SetupOutput(ext, tmpOutname.str());
552 m_modules[m_modules.size() - 1]->RegisterConfig("outfile", outname);
553
554 // Run field process.
555 for (int n = 0; n < SIZE_ModulePriority; ++n)
556 {
557 ModulePriority priority = static_cast<ModulePriority>(n);
558 for (int i = 0; i < m_modules.size(); ++i)
559 {
560 if (m_modules[i]->GetModulePriority() == priority)
561 {
562 m_modules[i]->Process(m_vm);
563 }
564 }
565 }
566
567 // Empty m_f to save memory
568 m_f->ClearField();
569
570 if (dump != -1) // not final dump so rescale
571 {
572 for (int n = 0; n < m_outFields.size(); ++n)
573 {
574 Vmath::Smul(m_outFields[n].size(), 1.0 / scale, m_outFields[n], 1,
575 m_outFields[n], 1);
576 }
577 }
578}
579
580} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
#define WARNINGL0(condition, msg)
Describes the specification for a Basis.
Definition Basis.h:45
int GetNumPoints() const
Return points order at which basis is defined.
Definition Basis.h:120
BasisType GetBasisType() const
Return type of expansion basis.
Definition Basis.h:131
int GetNumModes() const
Returns the order of the basis.
Definition Basis.h:74
PointsType GetPointsType() const
Return type of quadrature.
Definition Basis.h:143
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Defines a specification for a set of points.
Definition Points.h:50
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
SOLVER_UTILS_EXPORT void CreateFields(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields)
LibUtilities::FieldMetaDataMap m_fieldMetaData
std::vector< Array< OneD, NekDouble > > m_outFields
std::vector< ModuleSharedPtr > m_modules
SOLVER_UTILS_EXPORT void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
SOLVER_UTILS_EXPORT std::string SetupOutput(const std::string ext, const ParamMap &pParams)
Definition Filter.h:139
LibUtilities::SessionReaderSharedPtr m_session
Definition Filter.h:93
void v_OutputField(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, int dump) override
FilterReynoldsStresses(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< SolverUtils::EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
static std::string className
Name of the class.
std::vector< Array< OneD, NekDouble > > m_delta
void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
void v_FillVariablesName(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields) override
static FilterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< SolverUtils::EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
Creates an instance of this class.
std::vector< Array< OneD, NekDouble > > m_fields
Array< OneD, MultiRegions::ExpListSharedPtr > m_pFieldsScaled
void v_PrepareOutput(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
void v_ProcessSample(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, std::vector< Array< OneD, NekDouble > > &fieldcoeffs, const NekDouble &time) override
std::shared_ptr< SessionReader > SessionReaderSharedPtr
@ eFourierEvenlySpaced
1D Evenly-spaced points using Fourier Fit
Definition PointsType.h:74
@ eFourier
Fourier Expansion .
Definition BasisType.h:55
FilterFactory & GetFilterFactory()
std::shared_ptr< ExpansionInfoMap > ExpansionInfoMapShPtr
Definition MeshGraph.h:193
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition MeshGraph.h:224
std::map< int, ExpansionInfoShPtr > ExpansionInfoMap
Definition MeshGraph.h:191
void Svtsvtp(int n, const T alpha, const T *x, int incx, const T beta, const T *y, int incy, T *z, int incz)
Svtsvtp (scalar times vector plus scalar times vector):
Definition Vmath.hpp:473
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition Vmath.hpp:72
void Svtvm(int n, const T alpha, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Svtvm (scalar times vector minus vector): z = alpha*x - y.
Definition Vmath.hpp:424
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition Vmath.hpp:100