Nektar++
FilterAeroForces.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: FilterAeroForces.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: Output values of aerodynamic forces during time-stepping.
32//
33///////////////////////////////////////////////////////////////////////////////
34
44#include <boost/algorithm/string.hpp>
45#include <iomanip>
46
47using namespace std;
48
49namespace Nektar::SolverUtils
50{
54
55/**
56 *
57 */
60 const std::shared_ptr<EquationSystem> &pEquation, const ParamMap &pParams)
61 : Filter(pSession, pEquation)
62{
63 // OutputFile
64 std::string ext = ".fce";
65 m_outputFile = Filter::SetupOutput(ext, pParams);
66
67 // OutputFrequency
68 auto it = pParams.find("OutputFrequency");
69 if (it == pParams.end())
70 {
72 }
73 else
74 {
75 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
76 m_outputFrequency = round(equ.Evaluate());
77 }
78
79 // Time after which we need to calculate the forces
80 it = pParams.find("StartTime");
81 if (it == pParams.end())
82 {
83 m_startTime = 0;
84 }
85 else
86 {
87 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
88 m_startTime = equ.Evaluate();
89 }
90
91 // For 3DH1D, OutputAllPlanes or just average forces?
92 m_session->MatchSolverInfo("Homogeneous", "1D", m_isHomogeneous1D, false);
94 {
95 it = pParams.find("OutputAllPlanes");
96 if (it == pParams.end())
97 {
98 m_outputAllPlanes = false;
99 }
100 else
101 {
102 std::string sOption = it->second.c_str();
103 m_outputAllPlanes = (boost::iequals(sOption, "true")) ||
104 (boost::iequals(sOption, "yes"));
105 }
106 }
107 else
108 {
109 m_outputAllPlanes = false;
110 }
111
112 // Boundary (to calculate forces on)
113 it = pParams.find("Boundary");
114 ASSERTL0(it != pParams.end(), "Missing parameter 'Boundary");
115 ASSERTL0(it->second.length() > 0, "Empty parameter 'Boundary'.");
116 m_BoundaryString = it->second;
117
118 //
119 // Directions (to project forces)
120 //
121
122 // Allocate m_directions and m_directions0
123 // m_directions0 is to save the original direction defined by the user
124 // and projected every time to the new orientation which is then be saved
125 // in the m_direction, note if moving reference frame is not being used,
126 // m_directions and m_directions0 will be the same
129 // Initialise directions to default values (ex, ey, ez)
130 for (int i = 0; i < 3; ++i)
131 {
133 m_directions[i][i] = 1.0;
134
136 m_directions0[i][i] = 1.0;
137 }
138 std::stringstream directionStream;
139 std::string directionString;
140 // Override with input from xml file (if defined)
141 for (int i = 0; i < 3; ++i)
142 {
143 std::stringstream tmp;
144 tmp << i + 1;
145 std::string dir = "Direction" + tmp.str();
146 it = pParams.find(dir);
147 if (it != pParams.end())
148 {
149 ASSERTL0(!(it->second.empty()), "Missing parameter '" + dir + "'.");
150 directionStream.str(it->second);
151 // Guarantee the stream is in its start position
152 // before extracting
153 directionStream.clear();
154 // normalisation factor
155 NekDouble norm = 0.0;
156 for (int j = 0; j < 3; j++)
157 {
158 directionStream >> directionString;
159 if (!directionString.empty())
160 {
161 LibUtilities::Equation equ(m_session->GetInterpreter(),
162 directionString);
163 m_directions[i][j] = equ.Evaluate();
164 norm += m_directions[i][j] * m_directions[i][j];
165 }
166 }
167 // Normalise direction
168 for (int j = 0; j < 3; j++)
169 {
170 m_directions[i][j] /= sqrt(norm);
171 }
172 }
173 }
174
175 for (int i = 0; i < 3; ++i)
176 {
177 for (int j = 0; j < 3; ++j)
178 {
179 m_directions0[i][j] = m_directions[i][j];
180 }
181 }
182
183 // Point around which we compute the moments (default to the origin)
185 it = pParams.find("PivotPoint");
186 if (it == pParams.end())
187 {
188 it = pParams.find("MomentPoint");
189 }
190
191 if (it != pParams.end())
192 {
193 ASSERTL0(!(it->second.empty()), "Missing parameter 'PivotPoint'.");
194 std::stringstream pivotPointStream;
195 std::string pivotPointString;
196 pivotPointStream.str(it->second);
197
198 for (int j = 0; j < 3; ++j)
199 {
200 pivotPointStream >> pivotPointString;
201 if (!pivotPointString.empty())
202 {
203 LibUtilities::Equation equ(m_session->GetInterpreter(),
204 pivotPointString);
205 m_pivotPoint[j] = equ.Evaluate();
206 }
207 }
208 }
209}
210
211/**
212 *
213 */
215{
216}
217
218/**
219 *
220 */
223 [[maybe_unused]] const NekDouble &time)
224{
225 // Load mapping
227
228 // Parse the boundary regions into a list.
229 std::string::size_type FirstInd = m_BoundaryString.find_first_of('[') + 1;
230 std::string::size_type LastInd = m_BoundaryString.find_last_of(']') - 1;
231
232 ASSERTL0(FirstInd <= LastInd,
233 (std::string("Error reading boundary region definition:") +
235 .c_str());
236
237 std::string IndString =
238 m_BoundaryString.substr(FirstInd, LastInd - FirstInd + 1);
239 bool parseGood =
241 ASSERTL0(parseGood && !m_boundaryRegionsIdList.empty(),
242 (std::string("Unable to read boundary regions index "
243 "range for FilterAeroForces: ") +
244 IndString)
245 .c_str());
246
247 // determine what boundary regions need to be considered
248 int cnt;
249 unsigned int numBoundaryRegions = pFields[0]->GetBndConditions().size();
251 numBoundaryRegions, 0);
252
253 SpatialDomains::BoundaryConditions bcs(m_session, pFields[0]->GetGraph());
255 bcs.GetBoundaryRegions();
256
257 cnt = 0;
258 for (auto &it : bregions)
259 {
262 it.first) != m_boundaryRegionsIdList.end())
263 {
265 }
266 cnt++;
267 }
268
269 // Create map for element and edge/face of each boundary expansion
271 {
272 pFields[0]->GetPlane(0)->GetBoundaryToElmtMap(m_BCtoElmtID,
274 }
275 else
276 {
277 pFields[0]->GetBoundaryToElmtMap(m_BCtoElmtID, m_BCtoTraceID);
278 }
279
280 // Define number of planes to calculate the forces
281 // in the Homogeneous direction ( if m_outputAllPlanes is false,
282 // consider only first plane in wave space)
283 // If flow has no Homogeneous direction, use 1 to make code general
284 if (m_isHomogeneous1D && (m_outputAllPlanes || m_mapping->IsDefined()))
285 {
286 m_nPlanes = pFields[0]->GetHomogeneousBasis()->GetZ().size();
287 }
288 else
289 {
290 m_nPlanes = 1;
291 }
292
293 // Create map for Planes ID for Homogeneous direction
294 // If flow has no Homogeneous direction, create trivial map
295 int j;
298 {
299 Array<OneD, const unsigned int> IDs = pFields[0]->GetZIDs();
300 // Loop through all planes
301 for (cnt = 0; cnt < m_nPlanes; cnt++)
302 {
303 for (j = 0; j < IDs.size(); ++j)
304 {
305 // Look for current plane ID in this process
306 if (IDs[j] == cnt)
307 {
308 break;
309 }
310 }
311 // Assign ID to planesID
312 // If plane is not found in this process, leave it with -1
313 if (j != IDs.size())
314 {
315 m_planesID[cnt] = j;
316 }
317 }
318 }
319 else
320 {
321 m_planesID[0] = 0;
322 }
323
324 LibUtilities::CommSharedPtr vComm = pFields[0]->GetComm();
325
326 // Write header
327 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
328 int momdim = (expdim == 2) ? 1 : 3;
329 if (vComm->GetRank() == 0)
330 {
331 // Open output stream
332 bool adaptive;
333 m_session->MatchSolverInfo("Driver", "Adaptive", adaptive, false);
334 if (adaptive)
335 {
336 m_outputStream.open(m_outputFile.c_str(), ofstream::app);
337 }
338 else
339 {
340 m_outputStream.open(m_outputFile.c_str());
341 }
342 m_outputStream << "# Forces and moments acting on bodies" << endl;
343 for (int i = 0; i < expdim; i++)
344 {
345 m_outputStream << "#"
346 << " Direction" << i + 1 << " = (";
347 m_outputStream.width(8);
348 m_outputStream << setprecision(4) << m_directions[i][0];
349 m_outputStream.width(8);
350 m_outputStream << setprecision(4) << m_directions[i][1];
351 m_outputStream.width(8);
352 m_outputStream << setprecision(4) << m_directions[i][2];
353 m_outputStream << ")" << endl;
354 }
355
356 m_outputStream << "#"
357 << " Moments around"
358 << " (";
359 m_outputStream.width(8);
360 m_outputStream << setprecision(4) << m_pivotPoint[0];
361 m_outputStream.width(8);
362 m_outputStream << setprecision(4) << m_pivotPoint[1];
363 m_outputStream.width(8);
364 m_outputStream << setprecision(4) << m_pivotPoint[2];
365 m_outputStream << ")" << endl;
366
367 m_outputStream << "# Boundary regions: " << IndString.c_str() << endl;
368 m_outputStream << "#";
369 m_outputStream.width(7);
370 m_outputStream << "Time";
371 for (int i = 1; i <= expdim; i++)
372 {
373 m_outputStream.width(8);
374 m_outputStream << "F" << i << "-press";
375 m_outputStream.width(9);
376 m_outputStream << "F" << i << "-visc";
377 m_outputStream.width(8);
378 m_outputStream << "F" << i << "-total";
379 }
380 if (momdim == 1)
381 {
382 // 2D case: we only have M3 (z-moment)
383 m_outputStream.width(8);
384 m_outputStream << "M" << 3 << "-press";
385 m_outputStream.width(9);
386 m_outputStream << "M" << 3 << "-visc";
387 m_outputStream.width(8);
388 m_outputStream << "M" << 3 << "-total";
389 }
390 else
391 {
392 for (int i = 1; i <= momdim; i++)
393 {
394 m_outputStream.width(8);
395 m_outputStream << "M" << i << "-press";
396 m_outputStream.width(9);
397 m_outputStream << "M" << i << "-visc";
398 m_outputStream.width(8);
399 m_outputStream << "M" << i << "-total";
400 }
401 }
403 {
404 m_outputStream.width(10);
405 m_outputStream << "Plane";
406 }
407 if (m_session->DefinesSolverInfo("HomoStrip"))
408 {
409 ASSERTL0(
410 m_outputAllPlanes == false,
411 "Output forces on all planes not compatible with HomoStrips");
412 m_outputStream.width(10);
413 m_outputStream << "Strip";
414 }
415
416 m_outputStream << endl;
417 }
418
419 m_lastTime = -1;
420 m_index = 0;
421
423 {
424 v_Update(pFields, time);
425 }
426}
427
428/**
429 *
430 */
433 const NekDouble &time)
434{
435 // Only output every m_outputFrequency.
436 if (m_outputFrequency == 0)
437 {
438 return;
439 }
440 if ((m_index++) % m_outputFrequency || (time - m_startTime) < -1.0e-07)
441 {
442 return;
443 }
444
445 // Calculate the forces
446 CalculateForces(pFields, time);
447
448 // Calculate forces including all planes
449 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
450 int momdim = (expdim == 2) ? 1 : 3;
451 Array<OneD, NekDouble> Fp(expdim, 0.0);
452 Array<OneD, NekDouble> Fv(expdim, 0.0);
453 Array<OneD, NekDouble> Ft(expdim, 0.0);
454 for (int i = 0; i < expdim; i++)
455 {
456 Fp[i] = Vmath::Vsum(m_nPlanes, m_Fpplane[i], 1) / m_nPlanes;
457 Fv[i] = Vmath::Vsum(m_nPlanes, m_Fvplane[i], 1) / m_nPlanes;
458 Ft[i] = Fp[i] + Fv[i];
459 }
460
461 Array<OneD, NekDouble> Mp(momdim, 0.0);
462 Array<OneD, NekDouble> Mv(momdim, 0.0);
463 Array<OneD, NekDouble> Mt(momdim, 0.0);
464 for (int i = 0; i < momdim; i++)
465 {
466 Mp[i] = Vmath::Vsum(m_nPlanes, m_Mpplane[i], 1) / m_nPlanes;
467 Mv[i] = Vmath::Vsum(m_nPlanes, m_Mvplane[i], 1) / m_nPlanes;
468 Mt[i] = Mp[i] + Mv[i];
469 }
470
471 // Communicators to exchange results
472 LibUtilities::CommSharedPtr vComm = pFields[0]->GetComm();
473
474 // get the total number of planes per strip for the case of homoStrip method
475 int nZ{1};
476 int nstrips{1};
477 int colSize;
478 int idOffset{0};
479
480 if (m_session->DefinesSolverInfo("HomoStrip"))
481 {
482 m_session->LoadParameter("Strip_Z", nstrips);
483 colSize = vComm->GetColumnComm()->GetSize();
484 idOffset = colSize / nstrips;
485 }
486
487 if (m_isHomogeneous1D && m_session->DefinesSolverInfo("HomoStrip"))
488 {
489 m_session->LoadParameter("HomModesZ", nZ);
490 }
491
492 // Write Results
493 if (vComm->GetRank() == 0)
494 {
495 // Write result in each plane
497 {
498 for (int plane = 0; plane < m_nPlanes; plane++)
499 {
500 // Write time
501 m_outputStream.width(8);
502 m_outputStream << setprecision(6) << time;
503 // Write forces
504 for (int i = 0; i < expdim; i++)
505 {
506 m_outputStream.width(15);
507 m_outputStream << setprecision(8) << m_Fpplane[i][plane];
508 m_outputStream.width(15);
509 m_outputStream << setprecision(8) << m_Fvplane[i][plane];
510 m_outputStream.width(15);
511 m_outputStream << setprecision(8) << m_Ftplane[i][plane];
512 }
513 // Write moments
514 for (int i = 0; i < momdim; i++)
515 {
516 m_outputStream.width(15);
517 m_outputStream << setprecision(8) << m_Mpplane[i][plane];
518 m_outputStream.width(15);
519 m_outputStream << setprecision(8) << m_Mvplane[i][plane];
520 m_outputStream.width(15);
521 m_outputStream << setprecision(8) << m_Mtplane[i][plane];
522 }
523 m_outputStream.width(10);
524 m_outputStream << plane;
525 m_outputStream << endl;
526 }
527 }
528 // Output average (or total) force
529 m_outputStream.width(8);
530 m_outputStream << setprecision(6) << time;
531 for (int i = 0; i < expdim; i++)
532 {
533 m_outputStream.width(15);
534 m_outputStream << setprecision(8) << Fp[i];
535 m_outputStream.width(15);
536 m_outputStream << setprecision(8) << Fv[i];
537 m_outputStream.width(15);
538 m_outputStream << setprecision(8) << Ft[i];
539 }
540 for (int i = 0; i < momdim; i++)
541 {
542 m_outputStream.width(15);
543 m_outputStream << setprecision(8) << Mp[i];
544 m_outputStream.width(15);
545 m_outputStream << setprecision(8) << Mv[i];
546 m_outputStream.width(15);
547 m_outputStream << setprecision(8) << Mt[i];
548 }
550 {
551 m_outputStream.width(10);
552 m_outputStream << "average";
553 }
554
555 if (m_session->DefinesSolverInfo("HomoStrip"))
556 {
557 // The result we already wrote is for strip 0
558 m_outputStream.width(10);
559 m_outputStream << 0;
560 m_outputStream << endl;
561
562 // Now get result from other strips
563
564 for (int i = 1; i < nstrips; i++)
565 {
566 int id = i * idOffset;
567 vComm->GetColumnComm()->Recv(id, Fp);
568 vComm->GetColumnComm()->Recv(id, Fv);
569 vComm->GetColumnComm()->Recv(id, Ft);
570 vComm->GetColumnComm()->Recv(id, Mp);
571 vComm->GetColumnComm()->Recv(id, Mv);
572 vComm->GetColumnComm()->Recv(id, Mt);
573
574 m_outputStream.width(8);
575 m_outputStream << setprecision(6) << time;
576 for (int j = 0; j < expdim; j++)
577 {
578 m_outputStream.width(15);
579 m_outputStream << setprecision(8) << Fp[j];
580 m_outputStream.width(15);
581 m_outputStream << setprecision(8) << Fv[j];
582 m_outputStream.width(15);
583 m_outputStream << setprecision(8) << Ft[j];
584 }
585 for (int j = 0; j < momdim; j++)
586 {
587 m_outputStream.width(15);
588 m_outputStream << setprecision(8) << Mp[j];
589 m_outputStream.width(15);
590 m_outputStream << setprecision(8) << Mv[j];
591 m_outputStream.width(15);
592 m_outputStream << setprecision(8) << Mt[j];
593 }
594 m_outputStream.width(10);
595 m_outputStream << i;
596 m_outputStream << endl;
597 }
598 }
599 else
600 {
601 m_outputStream << endl;
602 }
603 }
604 else
605 {
606 // In the homostrips case, we need to send result to root
607 if (m_session->DefinesSolverInfo("HomoStrip") &&
608 (vComm->GetRowComm()->GetRank() == 0))
609 {
610 // we start from strip 1 not 0, since we already have the data of
611 // strip 0
612 for (int i = 1; i < nstrips; ++i)
613 {
614 int rid = i * idOffset;
615 int sid = vComm->GetColumnComm()->GetRank();
616 if (rid == sid)
617 {
618 vComm->GetColumnComm()->Send(0, Fp);
619 vComm->GetColumnComm()->Send(0, Fv);
620 vComm->GetColumnComm()->Send(0, Ft);
621 vComm->GetColumnComm()->Send(0, Mp);
622 vComm->GetColumnComm()->Send(0, Mv);
623 vComm->GetColumnComm()->Send(0, Mt);
624 }
625 }
626 }
627 }
628}
629
630/**
631 *
632 */
635 [[maybe_unused]] const NekDouble &time)
636{
637 if (pFields[0]->GetComm()->GetRank() == 0)
638 {
639 m_outputStream.close();
640 }
641}
642
643/**
644 *
645 */
647{
648 return true;
649}
650
651/**
652 * This function outputs the force on all planes of the current
653 * process, in the format required by ForcingMovingBody
654 */
657 Array<OneD, NekDouble> &Aeroforces, const NekDouble &time)
658{
659 // Calculate forces if the result we have is not up-to-date
660 if (time > m_lastTime)
661 {
662 CalculateForces(pFields, time);
663 }
664 if (Aeroforces == NullNekDouble1DArray || Aeroforces.size() == 0)
665 {
666 return;
667 }
668 // Get information to write result
669 Array<OneD, unsigned int> ZIDs = pFields[0]->GetZIDs();
670 int local_planes = ZIDs.size();
671 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
672
673 // Copy results to Aeroforces
675 {
676 for (int plane = 0; plane < local_planes; plane++)
677 {
678 for (int dir = 0; dir < expdim; dir++)
679 {
680 Aeroforces[plane + dir * local_planes] =
681 m_Ftplane[dir][ZIDs[plane]];
682 }
683 }
684 }
685 else
686 {
687 for (int plane = 0; plane < local_planes; plane++)
688 {
689 for (int dir = 0; dir < expdim; dir++)
690 {
691 Aeroforces[plane + dir * local_planes] = m_Ft[dir];
692 // m_Ftplane[dir][0];
693 }
694 }
695 }
696}
697
698/**
699 * This function outputs the moments of force on all planes of the current
700 * process
701 */
704 Array<OneD, NekDouble> &moments, const NekDouble &time)
705{
706 // Calculate forces if the result we have is not up-to-date
707 if (time > m_lastTime)
708 {
709 CalculateForces(pFields, time);
710 }
711
712 // Get information to write result
713 Array<OneD, unsigned int> ZIDs = pFields[0]->GetZIDs();
714 int local_planes = ZIDs.size();
715 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
716 int momdim = (expdim == 2) ? 1 : 3;
717
718 // Copy results to moments
720 {
721 for (int plane = 0; plane < local_planes; plane++)
722 {
723 for (int dir = 0; dir < momdim; ++dir)
724 {
725 moments[plane + dir * local_planes] =
726 m_Mtplane[dir][ZIDs[plane]];
727 }
728 }
729 }
730 else
731 {
732 for (int plane = 0; plane < local_planes; ++plane)
733 {
734 for (int dir = 0; dir < momdim; dir++)
735 {
736 moments[plane + dir * local_planes] = m_Mt[dir];
737 }
738 }
739 }
740}
741
742/**
743 * This function calculates the forces
744 */
747 const NekDouble &time)
748{
749 // Store time so we can check if result is up-to-date
750 m_lastTime = time;
751
752 // If a mapping is defined, call specific function
753 // Note: CalculateForcesMapping should work without a mapping,
754 // but since it is not very efficient the way it is now,
755 // it is only used when actually required
756 if (m_mapping->IsDefined())
757 {
758 CalculateForcesMapping(pFields, time);
759 return;
760 }
761
762 // Lock equation system weak pointer
763 auto equ = m_equ.lock();
764 ASSERTL0(equ, "Weak pointer expired");
765
766 auto fluidEqu = std::dynamic_pointer_cast<FluidInterface>(equ);
767 ASSERTL0(fluidEqu, "Aero forces filter is incompatible with this solver.");
768
769 // update the direction vectors
770 // only effective if we use moving reference frame
771 Array<OneD, NekDouble> vFrameDisp(6, 0.);
772 if (fluidEqu->GetMovingFrameDisp(vFrameDisp, 0))
773 {
774 if (vFrameDisp[5] != 0.)
775 {
776 NekDouble c = cos(vFrameDisp[5]);
777 NekDouble s = sin(vFrameDisp[5]);
778 // note: rotation around z-axis is considered only
779 // loop over the directions (ex, ey)
780 for (int idir = 0; idir < 2; ++idir)
781 {
782 NekDouble x = m_directions0[idir][0];
783 NekDouble y = m_directions0[idir][1];
784 m_directions[idir][0] = x * c + y * s;
785 m_directions[idir][1] = -x * s + y * c;
786 }
787 }
788 }
789
790 int cnt, elmtid, nq, offset, boundary;
791 // Get number of quadrature points and dimensions
792 int physTot = pFields[0]->GetNpoints();
793 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
794 int momdim = (expdim == 2) ? 1 : 3;
795 int nVel = expdim;
797 {
798 nVel = nVel + 1;
799 }
800
802
803 // Fields used to calculate forces (a single plane for 3DH1D)
804 Array<OneD, MultiRegions::ExpListSharedPtr> fields(pFields.size());
805
806 // Arrays of variables in field
807 Array<OneD, Array<OneD, NekDouble>> physfields(pFields.size());
810
811 // Arrays of variables in the element
812 Array<OneD, Array<OneD, NekDouble>> velElmt(expdim);
813 Array<OneD, NekDouble> pElmt(physTot);
814
815 // Velocity gradient
816 Array<OneD, Array<OneD, NekDouble>> grad(expdim * expdim);
818
820
821 // Values at the boundary
823 Array<OneD, Array<OneD, NekDouble>> gradb(expdim * expdim);
825
826 // Communicators to exchange results
827 LibUtilities::CommSharedPtr vComm = pFields[0]->GetComm();
828 LibUtilities::CommSharedPtr rowComm = vComm->GetRowComm();
830 m_session->DefinesSolverInfo("HomoStrip")
831 ? vComm->GetColumnComm()->GetColumnComm()
832 : vComm->GetColumnComm();
833
834 // Arrays with forces in each plane
835 m_Fp = Array<OneD, NekDouble>(expdim, 0.0);
836 m_Fv = Array<OneD, NekDouble>(expdim, 0.0);
837 m_Ft = Array<OneD, NekDouble>(expdim, 0.0);
838 m_Mp = Array<OneD, NekDouble>(momdim, 0.0);
839 m_Mv = Array<OneD, NekDouble>(momdim, 0.0);
840 m_Mt = Array<OneD, NekDouble>(momdim, 0.0);
841
845 for (int i = 0; i < expdim; i++)
846 {
850 }
851
852 // Arrays with moments in each plane
856 for (int i = 0; i < momdim; ++i)
857 {
861 }
862
863 // Forces per element length in a boundary
866
867 // Moments per element length in a boundary
870
871 // Get viscosity
872 NekDouble mu{1}, rho{1.};
873 if (m_session->DefinesParameter("Kinvis"))
874 {
875 rho = (m_session->DefinesParameter("rho"))
876 ? (m_session->GetParameter("rho"))
877 : 1;
878 mu = rho * m_session->GetParameter("Kinvis");
879 }
880 else
881 {
882 mu = m_session->GetParameter("mu");
883 }
884 NekDouble lambda = -2.0 / 3.0;
885
886 // Perform BwdTrans: when we only want the mean force in a 3DH1D
887 // we work in wavespace, otherwise we use physical space
888 for (int i = 0; i < pFields.size(); ++i)
889 {
891 {
892 pFields[i]->SetWaveSpace(false);
893 }
894 pFields[i]->BwdTrans(pFields[i]->GetCoeffs(), pFields[i]->UpdatePhys());
895 pFields[i]->SetPhysState(true);
896 }
897
898 // Define boundary expansions
902 {
903 BndConds = pFields[0]->GetPlane(0)->GetBndConditions();
904 BndExp = pFields[0]->GetPlane(0)->GetBndCondExpansions();
905 }
906 else
907 {
908 BndConds = pFields[0]->GetBndConditions();
909 BndExp = pFields[0]->GetBndCondExpansions();
910 }
911
912 // For Homogeneous, calculate force on each 2D plane
913 // Otherwise, m_nPlanes = 1, and loop only runs once
914 for (int plane = 0; plane < m_nPlanes; plane++)
915 {
916 // Check if plane is in this proc
917 if (m_planesID[plane] != -1)
918 {
919 // For Homogeneous, consider the 2D expansion
920 // on the current plane
922 {
923 for (int n = 0; n < pFields.size(); n++)
924 {
925 fields[n] = pFields[n]->GetPlane(m_planesID[plane]);
926 }
927 }
928 else
929 {
930 for (int n = 0; n < pFields.size(); n++)
931 {
932 fields[n] = pFields[n];
933 }
934 }
935
936 // Get velocity and pressure values
937 for (int n = 0; n < physfields.size(); ++n)
938 {
939 physfields[n] = fields[n]->GetPhys();
940 }
941 for (int n = 0; n < nVel; ++n)
942 {
943 velocity[n] = Array<OneD, NekDouble>(fields[n]->GetTotPoints());
944 }
945 pressure = Array<OneD, NekDouble>(fields[0]->GetTotPoints());
946 fluidEqu->GetVelocity(physfields, velocity);
947 fluidEqu->GetPressure(physfields, pressure);
948
949 // Loop all the Boundary Regions
950 for (int n = cnt = 0; n < BndConds.size(); n++)
951 {
952 if (m_boundaryRegionIsInList[n] == 1)
953 {
954 for (int i = 0; i < BndExp[n]->GetExpSize(); ++i, cnt++)
955 {
956 elmtid = m_BCtoElmtID[cnt];
957 elmt = fields[0]->GetExp(elmtid);
958 nq = elmt->GetTotPoints();
959 offset = fields[0]->GetPhys_Offset(elmtid);
960
961 // Extract fields on this element
962 for (int j = 0; j < expdim; j++)
963 {
964 velElmt[j] = velocity[j] + offset;
965 }
966 pElmt = pressure + offset;
967
968 // Compute the velocity gradients
969 div = Array<OneD, NekDouble>(nq, 0.0);
970 for (int j = 0; j < expdim; j++)
971 {
972 for (int k = 0; k < expdim; k++)
973 {
974 grad[j * expdim + k] =
975 Array<OneD, NekDouble>(nq, 0.0);
976 elmt->PhysDeriv(k, velElmt[j],
977 grad[j * expdim + k]);
978
979 if (j == k)
980 {
981 Vmath::Vadd(nq, grad[j * expdim + k], 1,
982 div, 1, div, 1);
983 }
984 }
985 }
986 // Scale div by lambda (for compressible flows)
987 Vmath::Smul(nq, lambda, div, 1, div, 1);
988
989 // Get Coordinates
990 for (int j = 0; j < 3; ++j)
991 {
992 coords[j] = Array<OneD, NekDouble>(nq, 0.0);
993 }
994 elmt->GetCoords(coords[0], coords[1], coords[2]);
995
996 // identify boundary of element
997 boundary = m_BCtoTraceID[cnt];
998
999 // Dimension specific part for obtaining values
1000 // at boundary and normal vector
1002 elmt->GetTraceNormal(boundary);
1003
1004 // Get expansion on boundary
1006 BndExp[n]->GetExp(i);
1007
1008 // Get number of points on the boundary
1009 int nbc = bc->GetTotPoints();
1010
1011 // Extract values at boundary
1012 Pb = Array<OneD, NekDouble>(nbc, 0.0);
1013 elmt->GetTracePhysVals(boundary, bc, pElmt, Pb);
1014
1015 for (int j = 0; j < expdim * expdim; ++j)
1016 {
1017 gradb[j] = Array<OneD, NekDouble>(nbc, 0.0);
1018 elmt->GetTracePhysVals(boundary, bc, grad[j],
1019 gradb[j]);
1020 }
1021
1022 for (int j = 0; j < 3; ++j)
1023 {
1024 coordsb[j] = Array<OneD, NekDouble>(nbc, 0.0);
1025 elmt->GetTracePhysVals(boundary, bc, coords[j],
1026 coordsb[j]);
1027
1028 // subtract m_pivotPoint
1029 Vmath::Sadd(nbc, -1.0 * m_pivotPoint[j], coordsb[j],
1030 1, coordsb[j], 1);
1031 }
1032
1033 // Calculate forces per unit length
1034
1035 // Pressure component: fp[j] = rho* p*n[j]
1036 for (int j = 0; j < expdim; j++)
1037 {
1038 fp[j] = Array<OneD, NekDouble>(nbc, 0.0);
1039 Vmath::Vmul(nbc, Pb, 1, normals[j], 1, fp[j], 1);
1040 Vmath::Smul(nbc, rho, fp[j], 1, fp[j], 1);
1041 }
1042
1043 // Viscous component:
1044 // fv[j] = -mu*{(grad[k,j]+grad[j,k]) *n[k]}
1045 for (int j = 0; j < expdim; j++)
1046 {
1047 fv[j] = Array<OneD, NekDouble>(nbc, 0.0);
1048 for (int k = 0; k < expdim; k++)
1049 {
1050 Vmath::Vvtvp(nbc, gradb[k * expdim + j], 1,
1051 normals[k], 1, fv[j], 1, fv[j], 1);
1052 Vmath::Vvtvp(nbc, gradb[j * expdim + k], 1,
1053 normals[k], 1, fv[j], 1, fv[j], 1);
1054 }
1055 if (!fluidEqu->HasConstantDensity())
1056 {
1057 // Add gradient term
1058 Vmath::Vvtvp(nbc, div, 1, normals[j], 1, fv[j],
1059 1, fv[j], 1);
1060 }
1061 Vmath::Smul(nbc, -mu, fv[j], 1, fv[j], 1);
1062 }
1063
1064 // calculate moments per unit length
1065 if (momdim == 1)
1066 {
1067
1068 mp[0] = Array<OneD, NekDouble>(nbc, 0.0);
1069 mv[0] = Array<OneD, NekDouble>(nbc, 0.0);
1070
1071 Array<OneD, NekDouble> fptmp(nbc, 0.0);
1072 Array<OneD, NekDouble> fvtmp(nbc, 0.0);
1073 Vmath::Smul(nbc, -1.0, fp[0], 1, fptmp, 1);
1074 Vmath::Smul(nbc, -1.0, fv[0], 1, fvtmp, 1);
1075
1076 // Mz = Fy * x - Fx * y
1077 Vmath::Vvtvvtp(nbc, fp[1], 1, coordsb[0], 1, fptmp,
1078 1, coordsb[1], 1, mp[0], 1);
1079
1080 Vmath::Vvtvvtp(nbc, fv[1], 1, coordsb[0], 1, fvtmp,
1081 1, coordsb[1], 1, mv[0], 1);
1082 }
1083 else
1084 {
1085 Array<OneD, NekDouble> fptmp(nbc, 0.0);
1086 Array<OneD, NekDouble> fvtmp(nbc, 0.0);
1087
1088 // Mx = Fz * y - Fy * z
1089 mp[0] = Array<OneD, NekDouble>(nbc, 0.0);
1090 mv[0] = Array<OneD, NekDouble>(nbc, 0.0);
1091
1092 Vmath::Smul(nbc, -1.0, fp[1], 1, fptmp, 1);
1093 Vmath::Smul(nbc, -1.0, fv[1], 1, fvtmp, 1);
1094 Vmath::Vvtvvtp(nbc, fp[2], 1, coordsb[1], 1, fptmp,
1095 1, coordsb[2], 1, mp[0], 1);
1096 Vmath::Vvtvvtp(nbc, fv[2], 1, coordsb[1], 1, fvtmp,
1097 1, coordsb[2], 1, mv[0], 1);
1098 // My = Fx * z - Fz * x
1099 mp[1] = Array<OneD, NekDouble>(nbc, 0.0);
1100 mv[1] = Array<OneD, NekDouble>(nbc, 0.0);
1101
1102 Vmath::Smul(nbc, -1.0, fp[2], 1, fptmp, 1);
1103 Vmath::Smul(nbc, -1.0, fv[2], 1, fvtmp, 1);
1104 Vmath::Vvtvvtp(nbc, fp[0], 1, coordsb[2], 1, fptmp,
1105 1, coordsb[0], 1, mp[1], 1);
1106 Vmath::Vvtvvtp(nbc, fv[0], 1, coordsb[2], 1, fvtmp,
1107 1, coordsb[0], 1, mv[1], 1);
1108 // Mz = Fy * x - Fx * y
1109 mp[2] = Array<OneD, NekDouble>(nbc, 0.0);
1110 mv[2] = Array<OneD, NekDouble>(nbc, 0.0);
1111
1112 Vmath::Smul(nbc, -1.0, fp[0], 1, fptmp, 1);
1113 Vmath::Smul(nbc, -1.0, fv[0], 1, fvtmp, 1);
1114 Vmath::Vvtvvtp(nbc, fp[1], 1, coordsb[0], 1, fptmp,
1115 1, coordsb[1], 1, mp[2], 1);
1116 Vmath::Vvtvvtp(nbc, fv[1], 1, coordsb[0], 1, fvtmp,
1117 1, coordsb[1], 1, mv[2], 1);
1118 }
1119
1120 // Integrate to obtain force
1121 for (int j = 0; j < expdim; j++)
1122 {
1123 m_Fpplane[j][plane] +=
1124 BndExp[n]->GetExp(i)->Integral(fp[j]);
1125 m_Fvplane[j][plane] +=
1126 BndExp[n]->GetExp(i)->Integral(fv[j]);
1127 }
1128 for (int j = 0; j < momdim; ++j)
1129 {
1130 m_Mpplane[j][plane] +=
1131 BndExp[n]->GetExp(i)->Integral(mp[j]);
1132 m_Mvplane[j][plane] +=
1133 BndExp[n]->GetExp(i)->Integral(mv[j]);
1134 }
1135 }
1136 }
1137 else
1138 {
1139 cnt += BndExp[n]->GetExpSize();
1140 }
1141 }
1142 }
1143 }
1144
1145 // Combine contributions from different processes
1146 // this is split between row and col comm because of
1147 // homostrips case, which only keeps its own strip
1148 for (int i = 0; i < expdim; i++)
1149 {
1150 rowComm->AllReduce(m_Fpplane[i], LibUtilities::ReduceSum);
1151 colComm->AllReduce(m_Fpplane[i], LibUtilities::ReduceSum);
1152
1153 rowComm->AllReduce(m_Fvplane[i], LibUtilities::ReduceSum);
1154 colComm->AllReduce(m_Fvplane[i], LibUtilities::ReduceSum);
1155 }
1156 for (int i = 0; i < momdim; ++i)
1157 {
1158 rowComm->AllReduce(m_Mpplane[i], LibUtilities::ReduceSum);
1159 colComm->AllReduce(m_Mpplane[i], LibUtilities::ReduceSum);
1160
1161 rowComm->AllReduce(m_Mvplane[i], LibUtilities::ReduceSum);
1162 colComm->AllReduce(m_Mvplane[i], LibUtilities::ReduceSum);
1163 }
1164
1165 // Pass force (computatonal frame) to FluidInterface (required for
1166 // MovingReferenceFrame)
1167 Array<OneD, NekDouble> aeroforces(6, 0.);
1168 for (size_t i = 0; i < m_Ft.size(); ++i)
1169 {
1170 aeroforces[i] = (Vmath::Vsum(m_nPlanes, m_Fpplane[i], 1) +
1172 m_nPlanes;
1173 }
1174 for (size_t i = 0; i < m_Mt.size(); ++i)
1175 {
1176 int j = m_Mt.size() - 1 - i;
1177 aeroforces[5 - i] = (Vmath::Vsum(m_nPlanes, m_Mpplane[j], 1) +
1179 m_nPlanes;
1180 }
1181 fluidEqu->SetAeroForce(aeroforces);
1182
1183 // Project results to new directions
1184 for (int plane = 0; plane < m_nPlanes; plane++)
1185 {
1186 Array<OneD, NekDouble> tmpP(expdim, 0.0);
1187 Array<OneD, NekDouble> tmpV(expdim, 0.0);
1188 for (int i = 0; i < expdim; i++)
1189 {
1190 for (int j = 0; j < expdim; j++)
1191 {
1192 tmpP[i] += m_Fpplane[j][plane] * m_directions[i][j];
1193 tmpV[i] += m_Fvplane[j][plane] * m_directions[i][j];
1194 }
1195 }
1196 // Copy result
1197 for (int i = 0; i < expdim; i++)
1198 {
1199 m_Fpplane[i][plane] = tmpP[i];
1200 m_Fvplane[i][plane] = tmpV[i];
1201 }
1202
1203 // Project moments only in 3D, since 2D moment is always in z direction
1204 if (momdim == 3)
1205 {
1206 for (int i = 0; i < 3; ++i)
1207 {
1208 tmpP[i] = 0.0;
1209 tmpV[i] = 0.0;
1210 for (int j = 0; j < 3; ++j)
1211 {
1212 tmpP[i] += m_Mpplane[j][plane] * m_directions[i][j];
1213 tmpV[i] += m_Mvplane[j][plane] * m_directions[i][j];
1214 }
1215 }
1216 // Copy result
1217 for (int i = 0; i < 3; ++i)
1218 {
1219 m_Mpplane[i][plane] = tmpP[i];
1220 m_Mvplane[i][plane] = tmpV[i];
1221 }
1222 }
1223 }
1224
1225 // Sum viscous and pressure components
1226 for (int plane = 0; plane < m_nPlanes; plane++)
1227 {
1228 for (int i = 0; i < expdim; i++)
1229 {
1230 m_Ftplane[i][plane] = m_Fpplane[i][plane] + m_Fvplane[i][plane];
1231 }
1232
1233 if (momdim == 3)
1234 {
1235 for (int i = 0; i < 3; ++i)
1236 {
1237 m_Mtplane[i][plane] = m_Mpplane[i][plane] + m_Mvplane[i][plane];
1238 }
1239 }
1240 else
1241 {
1242 m_Mtplane[0][plane] = m_Mpplane[0][plane] + m_Mvplane[0][plane];
1243 }
1244 }
1245
1246 // combine planes
1247 for (int i = 0; i < expdim; i++)
1248 {
1251 m_Ft[i] = m_Fp[i] + m_Fv[i];
1252 }
1253 for (int i = 0; i < momdim; i++)
1254 {
1257 m_Mt[i] = m_Mp[i] + m_Mv[i];
1258 }
1259
1260 // Put results back to wavespace, if necessary
1262 {
1263 for (int i = 0; i < pFields.size(); ++i)
1264 {
1265 pFields[i]->SetWaveSpace(true);
1266 pFields[i]->HomogeneousFwdTrans(pFields[i]->GetTotPoints(),
1267 pFields[i]->GetPhys(),
1268 pFields[i]->UpdatePhys());
1269 }
1270 }
1271}
1272
1273/**
1274 * This function calculates the forces when we have a mapping
1275 * defining a coordinate system transformation
1276 */
1279 [[maybe_unused]] const NekDouble &time)
1280{
1281 int cnt, elmtid, offset, boundary;
1282 // Get number of quadrature points and dimensions
1283 int physTot = pFields[0]->GetNpoints();
1284 int expdim = pFields[0]->GetGraph()->GetMeshDimension();
1285 int momdim = (expdim == 2) ? 1 : 3;
1286 int nVel = expdim;
1288 {
1289 nVel = nVel + 1;
1290 }
1291
1293
1294 // Pressure stress tensor
1295 // (global, in a plane, in element and boundary)
1298 Array<OneD, Array<OneD, NekDouble>> PElmt(nVel * nVel);
1299 Array<OneD, Array<OneD, NekDouble>> PBnd(nVel * nVel);
1300 // Velocity gradient
1302 Array<OneD, MultiRegions::ExpListSharedPtr> gradPlane(nVel * nVel);
1303 Array<OneD, Array<OneD, NekDouble>> gradElmt(nVel * nVel);
1304 Array<OneD, Array<OneD, NekDouble>> gradBnd(nVel * nVel);
1305
1306 // Coordinates
1311
1312 // Transformation to cartesian system
1315 Array<OneD, Array<OneD, NekDouble>> CElmt(nVel * nVel);
1316 Array<OneD, Array<OneD, NekDouble>> CBnd(nVel * nVel);
1317
1318 // Jacobian
1321 Array<OneD, NekDouble> JacElmt;
1323
1324 // Communicators to exchange results
1325 LibUtilities::CommSharedPtr vComm = pFields[0]->GetComm();
1326 LibUtilities::CommSharedPtr rowComm = vComm->GetRowComm();
1328 m_session->DefinesSolverInfo("HomoStrip")
1329 ? vComm->GetColumnComm()->GetColumnComm()
1330 : vComm->GetColumnComm();
1331
1332 // Arrays to store the plane average forces in each direction
1333 m_Fp = Array<OneD, NekDouble>(expdim, 0.0);
1334 m_Fv = Array<OneD, NekDouble>(expdim, 0.0);
1335 m_Ft = Array<OneD, NekDouble>(expdim, 0.0);
1336 m_Mp = Array<OneD, NekDouble>(momdim, 0.0);
1337 m_Mv = Array<OneD, NekDouble>(momdim, 0.0);
1338 m_Mt = Array<OneD, NekDouble>(momdim, 0.0);
1339
1340 // Arrays with forces in each plane
1344 for (int i = 0; i < expdim; i++)
1345 {
1349 }
1350
1351 // Arrays with moments in each plane
1355 for (int i = 0; i < momdim; ++i)
1356 {
1360 }
1361
1362 // Forces per element length in a boundary
1365
1366 // moments per element length in a boundary
1369
1370 // Get viscosity
1371 NekDouble mu{1}, rho{1.};
1372 if (m_session->DefinesParameter("Kinvis"))
1373 {
1374 rho = (m_session->DefinesParameter("rho"))
1375 ? (m_session->GetParameter("rho"))
1376 : 1;
1377 mu = rho * m_session->GetParameter("Kinvis");
1378 }
1379 else
1380 {
1381 mu = m_session->GetParameter("mu");
1382 }
1383 // NekDouble lambda = -2.0/3.0;
1384
1385 // Perform BwdTrans: for case with mapping, we can never work
1386 // in wavespace
1387 for (int i = 0; i < pFields.size(); ++i)
1388 {
1390 {
1391 pFields[i]->SetWaveSpace(false);
1392 }
1393 pFields[i]->BwdTrans(pFields[i]->GetCoeffs(), pFields[i]->UpdatePhys());
1394 pFields[i]->SetPhysState(true);
1395 }
1396
1397 // Define boundary expansions
1401 {
1402 BndConds = pFields[0]->GetPlane(0)->GetBndConditions();
1403 BndExp = pFields[0]->GetPlane(0)->GetBndCondExpansions();
1404 }
1405 else
1406 {
1407 BndConds = pFields[0]->GetBndConditions();
1408 BndExp = pFields[0]->GetBndCondExpansions();
1409 }
1410
1411 //
1412 // Calculate pressure stress tensor, velocity gradient
1413 // and get informations about the mapping
1414
1415 // Initialise variables
1416 switch (expdim)
1417 {
1418 case 2:
1419 {
1421 {
1423 Exp3DH1 = std::dynamic_pointer_cast<
1425 for (int i = 0; i < nVel * nVel; i++)
1426 {
1427 grad[i] =
1429 AllocateSharedPtr(*Exp3DH1);
1430
1432 AllocateSharedPtr(*Exp3DH1);
1433
1435 AllocateSharedPtr(*Exp3DH1);
1436 }
1437
1438 for (int i = 0; i < 3; ++i)
1439 {
1440 coords[i] =
1442 AllocateSharedPtr(*Exp3DH1);
1443 }
1445 AllocateSharedPtr(*Exp3DH1);
1446 }
1447 else
1448 {
1450 Exp2D = std::dynamic_pointer_cast<MultiRegions::ExpList>(
1451 pFields[0]);
1452 for (int i = 0; i < nVel * nVel; i++)
1453 {
1454 grad[i] =
1456 *Exp2D);
1457
1458 P[i] =
1460 *Exp2D);
1461
1462 C[i] =
1464 *Exp2D);
1465 }
1466 for (int i = 0; i < 3; ++i)
1467 {
1468 coords[i] =
1470 *Exp2D);
1471 }
1473 *Exp2D);
1474 }
1475 break;
1476 }
1477 case 3:
1478 {
1480 Exp3D =
1481 std::dynamic_pointer_cast<MultiRegions::ExpList>(pFields[0]);
1482 for (int i = 0; i < nVel * nVel; i++)
1483 {
1484 grad[i] =
1486 *Exp3D);
1487
1489 *Exp3D);
1490
1492 *Exp3D);
1493 }
1494 for (int i = 0; i < 3; ++i)
1495 {
1496 coords[i] =
1498 *Exp3D);
1499 }
1500 Jac =
1502
1503 break;
1504 }
1505 default:
1506 ASSERTL0(false,
1507 "Expansion dimension not supported by FilterAeroForces");
1508 break;
1509 }
1510
1511 // Get g^ij
1512 Array<OneD, Array<OneD, NekDouble>> tmp(nVel * nVel);
1513 m_mapping->GetInvMetricTensor(tmp);
1514
1515 // Get Cartesian coordinates
1516 m_mapping->GetCartesianCoordinates(coords[0]->UpdatePhys(),
1517 coords[1]->UpdatePhys(),
1518 coords[2]->UpdatePhys());
1519
1520 // Calculate P^ij = g^ij*p
1521 for (int i = 0; i < nVel * nVel; ++i)
1522 {
1523 Vmath::Vmul(physTot, tmp[i], 1, pFields[nVel]->GetPhys(), 1,
1524 P[i]->UpdatePhys(), 1);
1525 }
1526
1527 // Calculate covariant derivatives of U = u^i_,k
1529 for (int i = 0; i < nVel; ++i)
1530 {
1531 wk[i] = Array<OneD, NekDouble>(physTot, 0.0);
1532 Vmath::Vcopy(physTot, pFields[i]->GetPhys(), 1, wk[i], 1);
1533 }
1534 m_mapping->ApplyChristoffelContravar(wk, tmp);
1535 for (int i = 0; i < nVel; ++i)
1536 {
1537 for (int k = 0; k < nVel; ++k)
1538 {
1539 pFields[0]->PhysDeriv(MultiRegions::DirCartesianMap[k], wk[i],
1540 grad[i * nVel + k]->UpdatePhys());
1541
1542 Vmath::Vadd(physTot, tmp[i * nVel + k], 1,
1543 grad[i * nVel + k]->UpdatePhys(), 1,
1544 grad[i * nVel + k]->UpdatePhys(), 1);
1545 }
1546 }
1547 // Raise index to obtain Grad^ij = g^jk u^i_,k
1548 for (int i = 0; i < nVel; ++i)
1549 {
1550 for (int k = 0; k < nVel; ++k)
1551 {
1552 Vmath::Vcopy(physTot, grad[i * nVel + k]->GetPhys(), 1, wk[k], 1);
1553 }
1554 m_mapping->RaiseIndex(wk, wk);
1555 for (int j = 0; j < nVel; ++j)
1556 {
1557 Vmath::Vcopy(physTot, wk[j], 1, grad[i * nVel + j]->UpdatePhys(),
1558 1);
1559 }
1560 }
1561
1562 // Get Jacobian
1563 m_mapping->GetJacobian(Jac->UpdatePhys());
1564
1565 // Get transformation to Cartesian system
1566 for (int i = 0; i < nVel; ++i)
1567 {
1568 // Zero wk storage
1569 for (int k = 0; k < nVel; ++k)
1570 {
1571 wk[k] = Array<OneD, NekDouble>(physTot, 0.0);
1572 }
1573 // Make wk[i] = 1
1574 wk[i] = Array<OneD, NekDouble>(physTot, 1.0);
1575 // Transform wk to Cartesian
1576 m_mapping->ContravarToCartesian(wk, wk);
1577 // Copy result to a column in C
1578 for (int k = 0; k < nVel; k++)
1579 {
1580 Vmath::Vcopy(physTot, wk[k], 1, C[k * nVel + i]->UpdatePhys(), 1);
1581 }
1582 }
1583
1584 //
1585 // Calculate forces
1586 //
1587
1588 // For Homogeneous, calculate force on each 2D plane
1589 // Otherwise, m_nPlanes = 1, and loop only runs once
1590 for (int plane = 0; plane < m_nPlanes; ++plane)
1591 {
1592 // Check if plane is in this proc
1593 if (m_planesID[plane] != -1)
1594 {
1595 // For Homogeneous, consider the 2D expansion
1596 // on the current plane
1598 {
1599 for (int n = 0; n < nVel * nVel; ++n)
1600 {
1601 PPlane[n] = P[n]->GetPlane(m_planesID[plane]);
1602 gradPlane[n] = grad[n]->GetPlane(m_planesID[plane]);
1603 CPlane[n] = C[n]->GetPlane(m_planesID[plane]);
1604 }
1605 for (int n = 0; n < 3; ++n)
1606 {
1607 coordsPlane[n] = coords[n]->GetPlane(m_planesID[plane]);
1608 }
1609 JacPlane = Jac->GetPlane(m_planesID[plane]);
1610 }
1611 else
1612 {
1613 for (int n = 0; n < nVel * nVel; ++n)
1614 {
1615 PPlane[n] = P[n];
1616 gradPlane[n] = grad[n];
1617 CPlane[n] = C[n];
1618 }
1619 for (int n = 0; n < 3; ++n)
1620 {
1621 coordsPlane[n] = coords[n];
1622 }
1623 JacPlane = Jac;
1624 }
1625
1626 // Loop all the Boundary Regions
1627 for (int n = cnt = 0; n < BndConds.size(); n++)
1628 {
1629 if (m_boundaryRegionIsInList[n] == 1)
1630 {
1631 for (int i = 0; i < BndExp[n]->GetExpSize(); ++i, cnt++)
1632 {
1633 elmtid = m_BCtoElmtID[cnt];
1634 elmt = PPlane[0]->GetExp(elmtid);
1635 offset = PPlane[0]->GetPhys_Offset(elmtid);
1636
1637 // Extract fields on this element
1638 for (int j = 0; j < nVel * nVel; j++)
1639 {
1640 PElmt[j] = PPlane[j]->GetPhys() + offset;
1641 gradElmt[j] = gradPlane[j]->GetPhys() + offset;
1642 CElmt[j] = CPlane[j]->GetPhys() + offset;
1643 }
1644 for (int j = 0; j < 3; ++j)
1645 {
1646 coordsElmt[j] = coordsPlane[j]->GetPhys() + offset;
1647 }
1648 JacElmt = JacPlane->GetPhys() + offset;
1649
1650 // identify boundary of element
1651 boundary = m_BCtoTraceID[cnt];
1652
1653 // Dimension specific part for obtaining values
1654 // at boundary and normal vector
1656 // Get normals
1657 normals = elmt->GetTraceNormal(boundary);
1658
1659 // Get expansion on boundary
1661 BndExp[n]->GetExp(i);
1662
1663 // Get number of points on the boundary
1664 int nbc = bc->GetTotPoints();
1665
1666 // Extract values at boundary
1667 for (int j = 0; j < nVel * nVel; ++j)
1668 {
1669 gradBnd[j] = Array<OneD, NekDouble>(nbc, 0.0);
1670 elmt->GetTracePhysVals(boundary, bc, gradElmt[j],
1671 gradBnd[j]);
1672
1673 PBnd[j] = Array<OneD, NekDouble>(nbc, 0.0);
1674 elmt->GetTracePhysVals(boundary, bc, PElmt[j],
1675 PBnd[j]);
1676
1677 CBnd[j] = Array<OneD, NekDouble>(nbc, 0.0);
1678 elmt->GetTracePhysVals(boundary, bc, CElmt[j],
1679 CBnd[j]);
1680 }
1681 for (int j = 0; j < 3; ++j)
1682 {
1683 coordsBnd[j] = Array<OneD, NekDouble>(nbc, 0.0);
1684 elmt->GetTracePhysVals(boundary, bc, coordsElmt[j],
1685 coordsBnd[j]);
1686
1687 // substract the m_pivotPoint
1688 Vmath::Sadd(nbc, -1.0 * m_pivotPoint[j],
1689 coordsBnd[j], 1, coordsBnd[j], 1);
1690 }
1691 JacBnd = Array<OneD, NekDouble>(nbc, 0.0);
1692 elmt->GetTracePhysVals(boundary, bc, JacElmt, JacBnd);
1693
1694 // Calculate forces per unit length
1695
1696 // Pressure component: fp[j] = rho*P[j,k]*n[k]
1697 for (int j = 0; j < nVel; j++)
1698 {
1699 fp[j] = Array<OneD, NekDouble>(nbc, 0.0);
1700 // Normals only has expdim elements
1701 for (int k = 0; k < expdim; k++)
1702 {
1703 Vmath::Vvtvp(nbc, PBnd[j * nVel + k], 1,
1704 normals[k], 1, fp[j], 1, fp[j], 1);
1705 }
1706 Vmath::Smul(nbc, rho, fp[j], 1, fp[j], 1);
1707 }
1708
1709 // Viscous component:
1710 // fv[j] = -mu*{(grad[k,j]+grad[j,k]) *n[k]}
1711 for (int j = 0; j < nVel; j++)
1712 {
1713 fv[j] = Array<OneD, NekDouble>(nbc, 0.0);
1714 for (int k = 0; k < expdim; k++)
1715 {
1716 Vmath::Vvtvp(nbc, gradBnd[k * nVel + j], 1,
1717 normals[k], 1, fv[j], 1, fv[j], 1);
1718 Vmath::Vvtvp(nbc, gradBnd[j * nVel + k], 1,
1719 normals[k], 1, fv[j], 1, fv[j], 1);
1720 }
1721 Vmath::Smul(nbc, -mu, fv[j], 1, fv[j], 1);
1722 }
1723
1724 // Multiply by Jacobian
1725 for (int k = 0; k < nVel; k++)
1726 {
1727 Vmath::Vmul(nbc, JacBnd, 1, fp[k], 1, fp[k], 1);
1728 Vmath::Vmul(nbc, JacBnd, 1, fv[k], 1, fv[k], 1);
1729 }
1730
1731 // Convert to cartesian system
1732 for (int k = 0; k < nVel; k++)
1733 {
1734 wk[k] = Array<OneD, NekDouble>(physTot, 0.0);
1735 for (int j = 0; j < nVel; j++)
1736 {
1737 Vmath::Vvtvp(nbc, CBnd[k * nVel + j], 1, fp[j],
1738 1, wk[k], 1, wk[k], 1);
1739 }
1740 }
1741 for (int k = 0; k < nVel; k++)
1742 {
1743 Vmath::Vcopy(nbc, wk[k], 1, fp[k], 1);
1744 }
1745
1746 for (int k = 0; k < nVel; k++)
1747 {
1748 wk[k] = Array<OneD, NekDouble>(physTot, 0.0);
1749 for (int j = 0; j < nVel; j++)
1750 {
1751 Vmath::Vvtvp(nbc, CBnd[k * nVel + j], 1, fv[j],
1752 1, wk[k], 1, wk[k], 1);
1753 }
1754 }
1755 for (int k = 0; k < nVel; k++)
1756 {
1757 Vmath::Vcopy(nbc, wk[k], 1, fv[k], 1);
1758 }
1759
1760 // Calculate moments per unit length
1761 if (momdim == 1)
1762 {
1763 Array<OneD, NekDouble> fptmp(nbc, 0.0);
1764 Array<OneD, NekDouble> fvtmp(nbc, 0.0);
1765
1766 mp[0] = Array<OneD, NekDouble>(nbc, 0.0);
1767 mv[0] = Array<OneD, NekDouble>(nbc, 0.0);
1768
1769 Vmath::Smul(nbc, -1.0, fp[0], 1, fptmp, 1);
1770 Vmath::Smul(nbc, -1.0, fv[0], 1, fvtmp, 1);
1771 // Mz = Fy * x - Fx * y
1772 Vmath::Vvtvvtp(nbc, fp[1], 1, coordsBnd[0], 1,
1773 fptmp, 1, coordsBnd[1], 1, mp[0], 1);
1774 Vmath::Vvtvvtp(nbc, fv[1], 1, coordsBnd[0], 1,
1775 fvtmp, 1, coordsBnd[1], 1, mv[0], 1);
1776 }
1777 else
1778 {
1779 Array<OneD, NekDouble> fptmp(nbc, 0.0);
1780 Array<OneD, NekDouble> fvtmp(nbc, 0.0);
1781
1782 // Mx = Fz * y - Fy * z
1783 mp[0] = Array<OneD, NekDouble>(nbc, 0.0);
1784 mv[0] = Array<OneD, NekDouble>(nbc, 0.0);
1785
1786 Vmath::Smul(nbc, -1.0, fp[1], 1, fptmp, 1);
1787 Vmath::Smul(nbc, -1.0, fv[1], 1, fvtmp, 1);
1788 Vmath::Vvtvvtp(nbc, fp[2], 1, coordsBnd[1], 1,
1789 fptmp, 1, coordsBnd[2], 1, mp[0], 1);
1790 Vmath::Vvtvvtp(nbc, fv[2], 1, coordsBnd[1], 1,
1791 fvtmp, 1, coordsBnd[2], 1, mv[0], 1);
1792 // My = Fx * z - Fz * x
1793 mp[1] = Array<OneD, NekDouble>(nbc, 0.0);
1794 mv[1] = Array<OneD, NekDouble>(nbc, 0.0);
1795
1796 Vmath::Smul(nbc, -1.0, fp[2], 1, fptmp, 1);
1797 Vmath::Smul(nbc, -1.0, fv[2], 1, fvtmp, 1);
1798 Vmath::Vvtvvtp(nbc, fp[0], 1, coordsBnd[2], 1,
1799 fptmp, 1, coordsBnd[0], 1, mp[1], 1);
1800 Vmath::Vvtvvtp(nbc, fv[0], 1, coordsBnd[2], 1,
1801 fvtmp, 1, coordsBnd[0], 1, mv[1], 1);
1802 // Mz = Fy * x - Fx * y
1803 mp[2] = Array<OneD, NekDouble>(nbc, 0.0);
1804 mv[2] = Array<OneD, NekDouble>(nbc, 0.0);
1805
1806 Vmath::Smul(nbc, -1.0, fp[0], 1, fptmp, 1);
1807 Vmath::Smul(nbc, -1.0, fv[0], 1, fvtmp, 1);
1808 Vmath::Vvtvvtp(nbc, fp[1], 1, coordsBnd[0], 1,
1809 fptmp, 1, coordsBnd[1], 1, mp[2], 1);
1810 Vmath::Vvtvvtp(nbc, fv[1], 1, coordsBnd[0], 1,
1811 fvtmp, 1, coordsBnd[1], 1, mv[2], 1);
1812 }
1813
1814 // Integrate to obtain force
1815 for (int j = 0; j < expdim; j++)
1816 {
1817 m_Fpplane[j][plane] +=
1818 BndExp[n]->GetExp(i)->Integral(fp[j]);
1819 m_Fvplane[j][plane] +=
1820 BndExp[n]->GetExp(i)->Integral(fv[j]);
1821 }
1822
1823 // Integrate to obtain moments
1824 for (int j = 0; j < momdim; ++j)
1825 {
1826 m_Mpplane[j][plane] +=
1827 BndExp[n]->GetExp(i)->Integral(mp[j]);
1828 m_Mvplane[j][plane] +=
1829 BndExp[n]->GetExp(i)->Integral(mv[j]);
1830 }
1831 }
1832 }
1833 else
1834 {
1835 cnt += BndExp[n]->GetExpSize();
1836 }
1837 }
1838 }
1839 }
1840
1841 // Combine contributions from different processes
1842 // this is split between row and col comm because of
1843 // homostrips case, which only keeps its own strip
1844 for (int i = 0; i < expdim; ++i)
1845 {
1846 rowComm->AllReduce(m_Fpplane[i], LibUtilities::ReduceSum);
1847 colComm->AllReduce(m_Fpplane[i], LibUtilities::ReduceSum);
1848
1849 rowComm->AllReduce(m_Fvplane[i], LibUtilities::ReduceSum);
1850 colComm->AllReduce(m_Fvplane[i], LibUtilities::ReduceSum);
1851 }
1852 for (int i = 0; i < momdim; ++i)
1853 {
1854 rowComm->AllReduce(m_Mpplane[i], LibUtilities::ReduceSum);
1855 colComm->AllReduce(m_Mpplane[i], LibUtilities::ReduceSum);
1856
1857 rowComm->AllReduce(m_Mvplane[i], LibUtilities::ReduceSum);
1858 colComm->AllReduce(m_Mvplane[i], LibUtilities::ReduceSum);
1859 }
1860
1861 // Project results to new directions
1862 for (int plane = 0; plane < m_nPlanes; plane++)
1863 {
1864 Array<OneD, NekDouble> tmpP(expdim, 0.0);
1865 Array<OneD, NekDouble> tmpV(expdim, 0.0);
1866 for (int i = 0; i < expdim; i++)
1867 {
1868 for (int j = 0; j < expdim; j++)
1869 {
1870 tmpP[i] += m_Fpplane[j][plane] * m_directions[i][j];
1871 tmpV[i] += m_Fvplane[j][plane] * m_directions[i][j];
1872 }
1873 }
1874 // Copy result
1875 for (int i = 0; i < expdim; i++)
1876 {
1877 m_Fpplane[i][plane] = tmpP[i];
1878 m_Fvplane[i][plane] = tmpV[i];
1879 }
1880 //
1881 // For 3D moment, project the results, note in 2D case the moment is
1882 // always in z direction
1883 if (momdim == 3)
1884 {
1885 for (int i = 0; i < 3; ++i)
1886 {
1887 tmpP[i] = 0.0;
1888 tmpV[i] = 0.0;
1889 for (int j = 0; j < 3; ++j)
1890 {
1891 tmpP[i] += m_Mpplane[j][plane] * m_directions[i][j];
1892 tmpV[i] += m_Mvplane[j][plane] * m_directions[i][j];
1893 }
1894 }
1895
1896 // copy the result
1897 for (int i = 0; i < 3; ++i)
1898 {
1899 m_Mpplane[i][plane] = tmpP[i];
1900 m_Mvplane[i][plane] = tmpV[i];
1901 }
1902 }
1903 }
1904
1905 // Sum viscous and pressure components
1906 for (int plane = 0; plane < m_nPlanes; plane++)
1907 {
1908 for (int i = 0; i < expdim; i++)
1909 {
1910 m_Ftplane[i][plane] = m_Fpplane[i][plane] + m_Fvplane[i][plane];
1911 }
1912
1913 if (momdim == 3)
1914 {
1915 for (int i = 0; i < 3; ++i)
1916 {
1917 m_Mtplane[i][plane] = m_Mpplane[i][plane] + m_Mvplane[i][plane];
1918 }
1919 }
1920 else
1921 {
1922 m_Mtplane[0][plane] = m_Mpplane[0][plane] + m_Mvplane[0][plane];
1923 }
1924 }
1925
1926 // combine planes
1927 for (int i = 0; i < expdim; i++)
1928 {
1931 m_Ft[i] = m_Fp[i] + m_Fv[i];
1932 }
1933 for (int i = 0; i < momdim; i++)
1934 {
1937 m_Mt[i] = m_Mp[i] + m_Mv[i];
1938 }
1939
1940 // Put results back to wavespace, if necessary
1942 {
1943 for (int i = 0; i < pFields.size(); ++i)
1944 {
1945 pFields[i]->SetWaveSpace(true);
1946 pFields[i]->HomogeneousFwdTrans(pFields[i]->GetTotPoints(),
1947 pFields[i]->GetPhys(),
1948 pFields[i]->UpdatePhys());
1949 }
1950 }
1951}
1952
1953} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
static GLOBAL_MAPPING_EXPORT MappingSharedPtr Load(const LibUtilities::SessionReaderSharedPtr &pSession, const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields)
Return a pointer to the mapping, creating it on first call.
Definition: Mapping.cpp:264
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
Abstraction of a two-dimensional multi-elemental expansion which is merely a collection of local expa...
static bool GenerateSeqVector(const std::string &str, std::vector< unsigned int > &out)
Takes a comma-separated compressed string and converts it to entries in a vector.
Definition: ParseUtils.cpp:104
void v_Finalise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
Array< OneD, Array< OneD, NekDouble > > m_Fvplane
void v_Update(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
void CalculateForces(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time)
SOLVER_UTILS_EXPORT FilterAeroForces(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
int m_nPlanes
number of planes for homogeneous1D expansion
SOLVER_UTILS_EXPORT ~FilterAeroForces() override
Array< OneD, NekDouble > m_pivotPoint
bool m_outputAllPlanes
if using a homogeneous1D expansion, determine if should output all planes or just the average
void CalculateForcesMapping(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time)
std::vector< unsigned int > m_boundaryRegionsIdList
ID's of boundary regions where we want the forces.
Array< OneD, Array< OneD, NekDouble > > m_directions
std::vector< bool > m_boundaryRegionIsInList
Determines if a given Boundary Region is in m_boundaryRegionsIdList.
static std::string className
Name of the class.
Array< OneD, Array< OneD, NekDouble > > m_Mpplane
SOLVER_UTILS_EXPORT void GetMoments(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, Array< OneD, NekDouble > &moments, const NekDouble &time)
Array< OneD, Array< OneD, NekDouble > > m_directions0
GlobalMapping::MappingSharedPtr m_mapping
Array< OneD, Array< OneD, NekDouble > > m_Ftplane
void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
SOLVER_UTILS_EXPORT void GetForces(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, Array< OneD, NekDouble > &Aeroforces, const NekDouble &time)
static FilterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
Creates an instance of this class.
Array< OneD, Array< OneD, NekDouble > > m_Mtplane
Array< OneD, Array< OneD, NekDouble > > m_Fpplane
Array< OneD, Array< OneD, NekDouble > > m_Mvplane
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
const std::weak_ptr< EquationSystem > m_equ
Definition: Filter.h:94
std::map< std::string, std::string > ParamMap
Definition: Filter.h:66
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:235
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:55
@ P
Monomial polynomials .
Definition: BasisType.h:62
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:66
MultiRegions::Direction const DirCartesianMap[]
Definition: ExpList.h:87
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
std::shared_ptr< ExpList3DHomogeneous1D > ExpList3DHomogeneous1DSharedPtr
Shared pointer to an ExpList3DHomogeneous1D object.
FilterFactory & GetFilterFactory()
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:211
InputIterator find(InputIterator first, InputIterator last, InputIterator startingpoint, const EqualityComparable &value)
Definition: StdRegions.hpp:475
static Array< OneD, NekDouble > NullNekDouble1DArray
double NekDouble
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 Vvtvp(int n, const T *w, const int incw, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
vvtvp (vector times vector plus vector): z = w*x + y
Definition: Vmath.hpp:366
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.hpp:608
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.hpp:180
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
void Sadd(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Add vector y = alpha + x.
Definition: Vmath.hpp:194
void Vvtvvtp(int n, const T *v, int incv, const T *w, int incw, const T *x, int incx, const T *y, int incy, T *z, int incz)
vvtvvtp (vector times vector plus vector times vector):
Definition: Vmath.hpp:439
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.hpp:825
STL namespace.
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:285