Nektar++
ForcingMovingReferenceFrame.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: ForcingMovingReferenceFrame.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: Solving the absolute flow in a moving body frame,
32// by adding (U0 + Omega X (x - x0)) . grad u - Omega X u
33// as the body force.
34// U0 is the translational velocity of the body frame.
35// Omega is the angular velocity.
36// x0 is the rotation pivot in the body frame.
37// All vectors use the basis of the body frame.
38// Translational motion is allowed for all dimensions.
39// Rotation is not allowed for 1D, 2DH1D, 3DH2D.
40// Rotation in z direction is allowed for 2D and 3DH1D.
41// Rotation in 3 directions are allowed for 3D.
42// TODO: add suport for 3D rotation using Quaternion
43///////////////////////////////////////////////////////////////////////////////
44
50#include <boost/format.hpp>
51using namespace std;
52
53namespace Nektar::SolverUtils
54{
55
58 "MovingReferenceFrame", ForcingMovingReferenceFrame::create,
59 "Moving Frame");
60
61/**
62 * @brief
63 * @param pSession
64 * @param pEquation
65 */
68 const std::weak_ptr<EquationSystem> &pEquation)
69 : Forcing(pSession, pEquation)
70{
71}
72
74{
75 if (m_isRoot)
76 {
77 m_outputStream.close();
78 }
79}
80
81/**
82 * @brief Initialise the forcing module
83 * @param pFields
84 * @param pNumForcingFields
85 * @param pForce
86 */
89 [[maybe_unused]] const unsigned int &pNumForcingFields,
90 const TiXmlElement *pForce)
91{
92 m_session->MatchSolverInfo("Homogeneous", "1D", m_isH1d, false);
93 m_session->MatchSolverInfo("Homogeneous", "2D", m_isH2d, false);
94 bool singleMode, halfMode;
95 m_session->MatchSolverInfo("ModeType", "SingleMode", singleMode, false);
96 m_session->MatchSolverInfo("ModeType", "HalfMode", halfMode, false);
97 if (singleMode || halfMode)
98 {
99 m_isH1d = false;
100 }
101 m_expdim = m_isH2d ? 1 : pFields[0]->GetGraph()->GetMeshDimension();
102 m_spacedim = m_expdim + (m_isH1d ? 1 : 0) + (m_isH2d ? 2 : 0);
103 m_isRoot = pFields[0]->GetComm()->TreatAsRankZero();
104 m_hasPlane0 = true;
105 if (m_isH1d)
106 {
107 m_hasPlane0 = pFields[0]->GetZIDs()[0] == 0;
108 }
109 // initialize variables
112 m_body.extForceXYZ = Array<OneD, NekDouble>(6, 0.0);
113 m_hasVel = Array<OneD, bool>(3, false);
114 m_hasOmega = Array<OneD, bool>(3, false);
117 m_index = 0;
118 m_currentTime = -1.;
119 LoadParameters(pForce);
120 InitBodySolver(pForce);
121 if (m_body.isCircular || m_expdim == 1)
122 {
123 for (int i = 0; i < 3; ++i)
124 {
125 m_hasOmega[i] = false;
126 }
127 m_hasRotation = false;
128 }
129 // account for the effect of rotation
130 // Omega_X results in having v and w even if not defined by user
131 // Omega_Y results in having u and w even if not defined by user
132 // Omega_Z results in having u and v even if not defined by user
133 for (int i = 0; i < 3; ++i)
134 {
135 int j = (i + 1) % 3;
136 int k = (i + 2) % 3;
137 if (m_hasOmega[i])
138 {
139 m_hasVel[j] = true;
140 m_hasVel[k] = true;
141 }
142 }
143 if (m_hasRotation)
144 {
145 int npoints = pFields[0]->GetNpoints();
147 for (int j = 0; j < m_spacedim; ++j)
148 {
149 m_coords[j] = Array<OneD, NekDouble>(npoints);
150 }
151 pFields[0]->GetCoords(m_coords[0], m_coords[1], m_coords[2]);
152 // move the origin to the pivot point
153 for (int i = 0; i < m_spacedim; ++i)
154 {
155 Vmath::Sadd(npoints, -m_pivotPoint[i], m_coords[i], 1, m_coords[i],
156 1);
157 }
158 }
159 // initialise pivot point for fluid interface
160 auto equ = m_equ.lock();
161 ASSERTL0(equ, "Weak pointer to the equation system is expired");
162 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
163 FluidEq->SetMovingFramePivot(m_pivotPoint);
164 // initialize aeroforce filter
165 if (m_body.hasFreeMotion)
166 {
167 InitialiseFilter(m_session, pFields, pForce);
168 }
169}
170
172 std::string expression)
173{
174 NekDouble value = 0.;
175 try
176 {
177 LibUtilities::Equation expession(m_session->GetInterpreter(),
178 expression);
179 value = expession.Evaluate();
180 }
181 catch (const std::runtime_error &)
182 {
183 NEKERROR(ErrorUtil::efatal, "Error evaluating expression" + expression);
184 }
185 return value;
186}
187
188void ForcingMovingReferenceFrame::LoadParameters(const TiXmlElement *pForce)
189{
190 const TiXmlElement *funcNameElmt;
191 // body is a circular cylinder
192 const TiXmlElement *mssgTagSpecial =
193 pForce->FirstChildElement("CIRCULARCYLINDER");
194 if (mssgTagSpecial)
195 {
196 m_body.isCircular = true;
197 }
198 else
199 {
200 m_body.isCircular = false;
201 }
202 // load frame velocity
203 funcNameElmt = pForce->FirstChildElement("FRAMEVELOCITY");
204 if (funcNameElmt)
205 {
206 std::string FuncName = funcNameElmt->GetText();
207 ASSERTL0(m_session->DefinesFunction(FuncName),
208 "Function '" + FuncName + "' is not defined in the session.");
209 // linear velocity
210 for (int i = 0; i < m_spacedim; ++i)
211 {
212 std::string var = m_session->GetVariable(i);
213 if (m_session->DefinesFunction(FuncName, var))
214 {
215 m_frameVelFunction[i] = m_session->GetFunction(FuncName, var);
216 m_hasVel[i] = true;
217 }
218 }
219 // linear displacement and acceleration
220 std::vector<std::string> linearDispVar = {"X", "Y", "Z"};
221 std::vector<std::string> linearAcceVar = {"A_x", "A_y", "A_z"};
222 for (int i = 0; i < m_spacedim; ++i)
223 {
224 if (m_session->DefinesFunction(FuncName, linearDispVar[i]))
225 {
226 m_frameVelFunction[i + 6] =
227 m_session->GetFunction(FuncName, linearDispVar[i]);
228 }
229 if (m_session->DefinesFunction(FuncName, linearAcceVar[i]))
230 {
231 m_frameVelFunction[i + 12] =
232 m_session->GetFunction(FuncName, linearAcceVar[i]);
233 }
234 }
235 // angular velocities
236 m_hasRotation = false;
237 std::vector<std::string> angularVar = {"Omega_x", "Omega_y", "Omega_z"};
238 for (int i = 0; i < 3; ++i)
239 {
240 std::string var = angularVar[i];
241 if (m_session->DefinesFunction(FuncName, var))
242 {
243 m_frameVelFunction[i + 3] =
244 m_session->GetFunction(FuncName, var);
245 m_hasOmega[i] = true;
246 m_hasRotation = true;
247 }
248 }
249 // angular displacement and acceleration
250 std::vector<std::string> angularDispVar = {"Theta_x", "Theta_y",
251 "Theta_z"};
252 std::vector<std::string> angularAccepVar = {"DOmega_x", "DOmega_y",
253 "DOmega_z"};
254 for (int i = 0; i < 3; ++i)
255 {
256 if (m_session->DefinesFunction(FuncName, angularDispVar[i]))
257 {
258 m_frameVelFunction[i + 3 + 6] =
259 m_session->GetFunction(FuncName, angularDispVar[i]);
260 }
261 if (m_session->DefinesFunction(FuncName, angularAccepVar[i]))
262 {
263 m_frameVelFunction[i + 3 + 12] =
264 m_session->GetFunction(FuncName, angularAccepVar[i]);
265 }
266 }
267 // TODO: add the support for general rotation
268 for (int i = 0; i < 2; ++i)
269 {
270 ASSERTL0(!m_hasOmega[i], "Currently only Omega_z is supported");
271 }
272 }
273
274 // load external force
275 funcNameElmt = pForce->FirstChildElement("EXTERNALFORCE");
276 if (funcNameElmt)
277 {
278 std::string FuncName = funcNameElmt->GetText();
279 if (m_session->DefinesFunction(FuncName))
280 {
281 std::vector<std::string> forceVar = {"Fx", "Fy", "Fz"};
282 for (int i = 0; i < m_spacedim; ++i)
283 {
284 std::string var = forceVar[i];
285 if (m_session->DefinesFunction(FuncName, var))
286 {
287 m_body.extForceFunction[i] =
288 m_session->GetFunction(FuncName, var);
289 }
290 }
291 std::vector<std::string> momentVar = {"Mx", "My", "Mz"};
292 for (int i = 0; i < 3; ++i)
293 {
294 std::string var = momentVar[i];
295 if (m_session->DefinesFunction(FuncName, var))
296 {
297 m_body.extForceFunction[i + 3] =
298 m_session->GetFunction(FuncName, var);
299 }
300 }
301 }
302 }
303
304 // load pitching pivot
305 const TiXmlElement *pivotElmt = pForce->FirstChildElement("PIVOTPOINT");
306 if (pivotElmt)
307 {
308 std::vector<std::string> values;
309 std::string mssgStr = pivotElmt->GetText();
310 ParseUtils::GenerateVector(mssgStr, values);
311 for (int j = 0; j < m_spacedim; ++j)
312 {
313 m_pivotPoint[j] = EvaluateExpression(values[j]);
314 }
315 }
316
317 // load travelling wave speed
318 const TiXmlElement *TWSElmt =
319 pForce->FirstChildElement("TRAVELINGWAVESPEED");
320 if (TWSElmt)
321 {
322 std::vector<std::string> values;
323 std::string mssgStr = TWSElmt->GetText();
324 ParseUtils::GenerateVector(mssgStr, values);
325 for (int j = 0; j < m_spacedim; ++j)
326 {
327 NekDouble tmp = EvaluateExpression(values[j]);
328 if (fabs(tmp) > NekConstants::kNekMachineEpsilon)
329 {
330 m_travelWave[j] = tmp;
331 m_hasVel[j] = true;
332 }
333 }
334 }
335
336 // OutputFile
337 const TiXmlElement *mssgTag = pForce->FirstChildElement("OutputFile");
338 string filename;
339 if (mssgTag)
340 {
341 filename = mssgTag->GetText();
342 }
343 else
344 {
345 filename = m_session->GetSessionName();
346 }
347 if (!(filename.length() >= 4 &&
348 filename.substr(filename.length() - 4) == ".mrf"))
349 {
350 filename += ".mrf";
351 }
352 if (m_isRoot)
353 {
354 m_outputStream.open(filename.c_str());
355 if (m_spacedim == 2)
356 {
358 << "Variables = t, x, ux, ax, y, uy, ay, theta, omega, domega"
359 << endl;
360 }
361 else if (m_spacedim == 3)
362 {
363 m_outputStream << "Variables = t, x, ux, ax, y, uy, ay, z, uz, az, "
364 "theta, omega, domega"
365 << endl;
366 }
367 }
368
369 // output frequency
371 mssgTag = pForce->FirstChildElement("OutputFrequency");
372 if (mssgTag)
373 {
374 std::vector<std::string> values;
375 std::string mssgStr = mssgTag->GetText();
376 m_outputFrequency = round(EvaluateExpression(mssgStr));
377 }
379 "OutputFrequency should be greater than zero.");
380}
381
382void ForcingMovingReferenceFrame::InitBodySolver(const TiXmlElement *pForce)
383{
384 int NumDof = m_spacedim + 1;
385 const TiXmlElement *mssgTag;
386 std::string mssgStr;
387 // allocate memory and initialise
389 for (size_t i = 0; i < 3; ++i)
390 {
391 m_body.vel[i] = Array<OneD, NekDouble>(NumDof, 0.);
392 }
395 // read free motion DoFs
396 m_body.dirDoFs.clear();
397 for (int i = 0; i < NumDof; ++i)
398 {
399 m_body.dirDoFs.insert(i);
400 }
401 mssgTag = pForce->FirstChildElement("MOTIONPRESCRIBED");
402 if (mssgTag)
403 {
404 std::vector<std::string> values;
405 mssgStr = mssgTag->GetText();
406 ParseUtils::GenerateVector(mssgStr, values);
407 ASSERTL0(values.size() == NumDof,
408 "MOTIONPRESCRIBED vector should be of size " +
409 std::to_string(NumDof));
410 for (int i = 0; i < NumDof; ++i)
411 {
412 if (EvaluateExpression(values[i]) == 0)
413 {
414 m_body.dirDoFs.erase(i);
415 if (i < m_spacedim)
416 {
417 m_hasVel[i] = true;
418 }
419 else if (i == m_spacedim)
420 {
421 m_hasOmega[2] = true;
422 m_hasRotation = true;
423 }
424 }
425 }
426 }
427 m_body.hasFreeMotion = m_body.dirDoFs.size() < NumDof;
428 // read mass matrix
429 m_body.M = Array<OneD, NekDouble>(NumDof * NumDof, 0.);
430 mssgTag = pForce->FirstChildElement("MASS");
431 ASSERTL0(m_body.dirDoFs.size() == NumDof || mssgTag,
432 "Mass matrix is required.");
433 if (mssgTag)
434 {
435 std::vector<std::string> values;
436 mssgStr = mssgTag->GetText();
437 ParseUtils::GenerateVector(mssgStr, values);
438 ASSERTL0(values.size() == NumDof * NumDof,
439 "Mass matrix should be of size " + std::to_string(NumDof) +
440 "X" + std::to_string(NumDof));
441 int count = 0;
442 for (int i = 0; i < NumDof; ++i)
443 {
444 for (int j = 0; j < NumDof; ++j)
445 {
446 m_body.M[count] = EvaluateExpression(values[count]);
447 ++count;
448 }
449 }
450 }
451 // read damping matrix
452 m_body.C = Array<OneD, NekDouble>(NumDof * NumDof, 0.);
453 mssgTag = pForce->FirstChildElement("DAMPING");
454 if (mssgTag)
455 {
456 std::vector<std::string> values;
457 mssgStr = mssgTag->GetText();
458 ParseUtils::GenerateVector(mssgStr, values);
459 ASSERTL0(values.size() == NumDof * NumDof,
460 "Damping matrix should be of size " + std::to_string(NumDof) +
461 "X" + std::to_string(NumDof));
462 int count = 0;
463 for (int i = 0; i < NumDof; ++i)
464 {
465 for (int j = 0; j < NumDof; ++j)
466 {
467 m_body.C[count] = EvaluateExpression(values[count]);
468 ++count;
469 }
470 }
471 }
472 // read rigidity matrix
473 m_body.K = Array<OneD, NekDouble>(NumDof * NumDof, 0.);
474 mssgTag = pForce->FirstChildElement("RIGIDITY");
475 if (mssgTag)
476 {
477 std::vector<std::string> values;
478 mssgStr = mssgTag->GetText();
479 ParseUtils::GenerateVector(mssgStr, values);
480 ASSERTL0(values.size() == NumDof * NumDof,
481 "Rigidity matrix should be of size " + std::to_string(NumDof) +
482 "X" + std::to_string(NumDof));
483 int count = 0;
484 for (int i = 0; i < NumDof; ++i)
485 {
486 for (int j = 0; j < NumDof; ++j)
487 {
488 m_body.K[count] = EvaluateExpression(values[count]);
489 ++count;
490 }
491 }
492 }
493 // read Newmark Beta paramters
494 m_timestep = m_session->GetParameter("TimeStep");
495 NekDouble beta = 0.25;
496 NekDouble gamma = 0.51;
497 if (m_session->DefinesParameter("NewmarkBeta"))
498 {
499 beta = m_session->GetParameter("NewmarkBeta");
500 }
501 if (m_session->DefinesParameter("NewmarkGamma"))
502 {
503 gamma = m_session->GetParameter("NewmarkGamma");
504 }
506 m_body.K, m_body.dirDoFs);
507}
508
510 const NekDouble &time, std::map<int, NekDouble> &Dirs)
511{
512 int NumDof = m_spacedim + 1;
513 for (auto it : m_frameVelFunction)
514 {
515 if (it.first < 3)
516 {
517 Dirs[it.first] = it.second->Evaluate(0., 0., 0., time);
518 }
519 else if (it.first == 5)
520 {
521 Dirs[m_spacedim] = it.second->Evaluate(0., 0., 0., time);
522 }
523 else if (it.first < 9)
524 {
525 Dirs[NumDof + it.first - 6] = it.second->Evaluate(0., 0., 0., time);
526 }
527 else if (it.first == 11)
528 {
529 Dirs[NumDof + m_spacedim] = it.second->Evaluate(0., 0., 0., time);
530 }
531 else if (it.first < 15)
532 {
533 Dirs[(NumDof << 1) + it.first - 12] =
534 it.second->Evaluate(0., 0., 0., time);
535 }
536 else if (it.first == 17)
537 {
538 Dirs[(NumDof << 1) + m_spacedim] =
539 it.second->Evaluate(0., 0., 0., time);
540 }
541 }
542 for (auto i : m_body.dirDoFs)
543 {
544 if (Dirs.find(i) == Dirs.end())
545 {
546 Dirs[i] = 0.;
547 Dirs[i + NumDof] = 0.;
548 Dirs[i + (NumDof << 1)] = 0.;
549 }
550 }
551}
552/**
553 * @brief Updates the forcing array with the current required forcing.
554 * @param pFields
555 * @param time
556 */
559 const NekDouble &time)
560{
561 if (m_currentTime >= time)
562 {
563 return;
564 }
565 m_currentTime = time;
566 std::map<int, NekDouble> Dirs;
567 UpdatePrescribed(time, Dirs);
568 if (m_index == 0)
569 {
571 }
572 else
573 {
574 // compute the velocites whoes functions are provided in inertial frame
575 Array<OneD, NekDouble> forcebody(6, 0.); // fluid force
576 if (m_body.hasFreeMotion)
577 {
578 for (auto it : m_body.extForceFunction)
579 {
580 m_body.extForceXYZ[it.first] =
581 it.second->Evaluate(0., 0., 0., time);
582 }
583 m_body.aeroforceFilter->GetForces(pFields, NullNekDouble1DArray,
584 time);
585 auto equ = m_equ.lock();
586 ASSERTL0(equ, "Weak pointer to the equation system is expired");
587 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
588 FluidEq->GetAeroForce(forcebody);
589 }
590 SolveBodyMotion(m_body.vel, forcebody, Dirs);
591 }
592 if (m_isRoot && m_index % m_outputFrequency == 0)
593 {
594 m_outputStream << boost::format("%25.19e") % time << " ";
595 for (size_t i = 0; i < m_body.vel[0].size(); ++i)
596 {
597 m_outputStream << boost::format("%25.19e") % m_body.vel[0][i] << " "
598 << boost::format("%25.19e") % m_body.vel[1][i] << " "
599 << boost::format("%25.19e") % m_body.vel[2][i]
600 << " ";
601 }
602 m_outputStream << endl;
603 }
604 // extract values and transform to body frame
605 for (int i = 0; i < m_spacedim; ++i)
606 {
607 m_velxyz[i] = m_body.vel[1][i];
608 }
609 if (m_hasRotation)
610 {
611 m_omegaxyz[2] = m_body.vel[1][m_spacedim];
612 Array<OneD, NekDouble> angle(3, 0.);
613 angle[2] = m_body.vel[0][m_spacedim];
614 m_frame.SetAngle(angle);
616 }
617 // set displacements at the current time step
619 ++m_index;
620}
621
623 Array<OneD, Array<OneD, NekDouble>> &bodyVel, const int step)
624{
625 // set displacements at the current or next time step
626 Array<OneD, NekDouble> disp(6, 0.);
628 for (int i = 0; i < m_spacedim; ++i)
629 {
630 disp[i] = bodyVel[0][i];
631 }
632 if (!m_body.isCircular)
633 {
634 disp[5] = bodyVel[0][m_spacedim];
635 }
636 // to set the boundary condition of the next time step
637 // update the frame velocities and accelerations
638 for (int i = 0; i < m_spacedim; ++i)
639 {
640 vel[i] = bodyVel[1][i];
641 vel[i + 6] = bodyVel[2][i];
642 }
643 vel[5] = bodyVel[1][m_spacedim];
644 vel[11] = bodyVel[2][m_spacedim];
646 if (step && m_hasRotation)
647 {
648 m_frame.SetAngle(disp + 3);
650 m_frame.IneritalToBody(3, vel + 6, tmp = vel + 6);
651 }
652 auto equ = m_equ.lock();
653 ASSERTL0(equ, "Weak pointer to the equation system is expired");
654 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
655 FluidEq->SetMovingFrameVelocities(vel, step);
656 FluidEq->SetMovingFrameDisp(disp, step);
657}
658
661 const Array<OneD, NekDouble> &forcebody, std::map<int, NekDouble> &Dirs)
662{
663 if (!m_body.hasFreeMotion)
664 {
665 m_bodySolver.SolvePrescribed(bodyVel, Dirs);
666 }
667 else if (!m_hasRotation || Dirs.find(m_spacedim) != Dirs.end())
668 {
669 m_bodySolver.SolvePrescribed(bodyVel, Dirs);
670 Array<OneD, NekDouble> force(6, 0.), tmp;
671 if (m_hasRotation && Dirs.find(m_spacedim) != Dirs.end())
672 {
673 Array<OneD, NekDouble> angle(3, 0.);
674 angle[2] = bodyVel[0][m_spacedim];
675 m_frame.SetAngle(angle);
676 m_frame.BodyToInerital(3, forcebody, force);
677 m_frame.BodyToInerital(3, forcebody + 3, tmp = force + 3);
678 }
679 else
680 {
681 Vmath::Vcopy(6, forcebody, 1, force, 1);
682 }
683 for (int i = 0; i < m_spacedim; ++i)
684 {
685 force[i] += m_body.extForceXYZ[i];
686 }
687 force[m_spacedim] = force[5] + m_body.extForceXYZ[5];
688 m_bodySolver.SolveFree(bodyVel, force);
689 }
690 else
691 {
692 Array<OneD, Array<OneD, NekDouble>> tmpbodyVel(bodyVel.size());
693 for (size_t i = 0; i < bodyVel.size(); ++i)
694 {
695 tmpbodyVel[i] = Array<OneD, NekDouble>(bodyVel[i].size());
696 }
697 Array<OneD, NekDouble> angle(3, 0.);
698 angle[2] = bodyVel[0][m_spacedim];
699 for (int iter = 0; iter < 2; ++iter)
700 {
701 // copy initial condition
702 for (size_t i = 0; i < bodyVel.size(); ++i)
703 {
704 Vmath::Vcopy(bodyVel[i].size(), bodyVel[i], 1, tmpbodyVel[i],
705 1);
706 }
707 Array<OneD, NekDouble> force(6, 0.), tmp;
708 m_frame.SetAngle(angle);
709 m_frame.BodyToInerital(3, forcebody, force);
710 m_frame.BodyToInerital(3, forcebody + 3, tmp = force + 3);
711 for (int i = 0; i < m_spacedim; ++i)
712 {
713 force[i] += m_body.extForceXYZ[i];
714 }
715 force[m_spacedim] = force[5] + m_body.extForceXYZ[5];
716 m_bodySolver.Solve(tmpbodyVel, force, Dirs);
717 angle[2] = tmpbodyVel[0][m_spacedim];
718 }
719 // copy final results
720 for (size_t i = 0; i < bodyVel.size(); ++i)
721 {
722 Vmath::Vcopy(bodyVel[i].size(), tmpbodyVel[i], 1, bodyVel[i], 1);
723 }
724 }
725}
726
727/**
728 * @brief Adds the body force, -Omega X u.
729 * @param fields
730 * @param inarray
731 * @param outarray
732 * @param time
733 */
736 const Array<OneD, Array<OneD, NekDouble>> &inarray,
737 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble &time)
738{
739 // If there is no rotation, body force is zero,
740 // nothing needs to be done here.
741 if (m_hasRotation)
742 {
743 int npoints = fields[0]->GetNpoints();
744 addRotation(npoints, outarray, -1., inarray, outarray);
745 }
747}
748
749/**
750 * @brief Set velocity boundary condition at the next time step
751 */
753{
754 time += m_timestep;
755 // compute the velocities whose functions are provided in inertial frame
756 std::map<int, NekDouble> Dirs;
757 UpdatePrescribed(time, Dirs);
758 Array<OneD, Array<OneD, NekDouble>> bodyVel(m_body.vel.size());
759 for (size_t i = 0; i < m_body.vel.size(); ++i)
760 {
761 bodyVel[i] = Array<OneD, NekDouble>(m_body.vel[i].size());
762 Vmath::Vcopy(m_body.vel[i].size(), m_body.vel[i], 1, bodyVel[i], 1);
763 }
764 m_bodySolver.SolvePrescribed(bodyVel, Dirs);
765 // set displacements at the next time step
766 UpdateFluidInterface(bodyVel, 1);
767}
768
769/**
770 * @brief outarray = inarray0 + angVelScale Omega x inarray1
771 */
773 int nPnts, // number of points
774 const Array<OneD, Array<OneD, NekDouble>> &inarray0, NekDouble angVelScale,
775 const Array<OneD, Array<OneD, NekDouble>> &inarray1,
777{
778 ASSERTL0(&inarray1 != &outarray, "inarray1 and outarray "
779 "should not be the same.");
780
781 // TODO: In case of having support for all three components of Omega,
782 // they should be transformed into the rotating frame first!
783
784 // In case that the inarray0 and outarry are different, to avoid using
785 // un-initialized array, copy the array first
786 if (&inarray0 != &outarray)
787 {
788 ASSERTL0(inarray0.size() == outarray.size(),
789 "inarray0 and outarray must have same dimentions");
790 for (int i = 0; i < inarray0.size(); ++i)
791 {
792 Vmath::Vcopy(nPnts, inarray0[i], 1, outarray[i], 1);
793 }
794 }
795
796 if (m_spacedim >= 2 && m_hasOmega[2])
797 {
798 NekDouble cp = m_omegaxyz[2] * angVelScale;
799 NekDouble cm = -1. * cp;
800
801 Vmath::Svtvp(nPnts, cm, inarray1[1], 1, outarray[0], 1, outarray[0], 1);
802 Vmath::Svtvp(nPnts, cp, inarray1[0], 1, outarray[1], 1, outarray[1], 1);
803 }
804
805 if (m_spacedim == 3 && m_hasOmega[0])
806 {
807 NekDouble cp = m_omegaxyz[0] * angVelScale;
808 NekDouble cm = -1. * cp;
809
810 Vmath::Svtvp(nPnts, cp, inarray1[1], 1, outarray[2], 1, outarray[2], 1);
811 Vmath::Svtvp(nPnts, cm, inarray1[2], 1, outarray[1], 1, outarray[1], 1);
812 }
813
814 if (m_spacedim == 3 && m_hasOmega[1])
815 {
816 NekDouble cp = m_omegaxyz[1] * angVelScale;
817 NekDouble cm = -1. * cp;
818
819 Vmath::Svtvp(nPnts, cp, inarray1[2], 1, outarray[0], 1, outarray[0], 1);
820 Vmath::Svtvp(nPnts, cm, inarray1[0], 1, outarray[2], 1, outarray[2], 1);
821 }
822}
823
824/**
825 * @brief Compute the moving frame velocity at given time
826 */
829 const Array<OneD, Array<OneD, NekDouble>> &inarray,
830 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble &time)
831{
832 UpdateFrameVelocity(fields, time);
833 int npoints = fields[0]->GetNpoints();
834 if (m_isH2d && fields[0]->GetWaveSpace())
835 {
836 for (int i = 0; i < m_spacedim; ++i)
837 {
838 if (m_hasVel[i])
839 {
840 Array<OneD, NekDouble> tmpphys(npoints,
841 -m_velxyz[i] - m_travelWave[i]);
842 Array<OneD, NekDouble> tmpcoef(npoints);
843 fields[0]->HomogeneousFwdTrans(npoints, tmpphys, tmpcoef);
844 Vmath::Vadd(npoints, tmpcoef, 1, inarray[i], 1, outarray[i], 1);
845 }
846 else if (&inarray != &outarray)
847 {
848 Vmath::Vcopy(npoints, inarray[i], 1, outarray[i], 1);
849 }
850 }
851 }
852 else
853 {
854 int npoints0 = npoints;
855 if (m_isH1d && fields[0]->GetWaveSpace())
856 {
857 npoints0 = m_hasPlane0 ? fields[0]->GetPlane(0)->GetNpoints() : 0;
858 }
859 for (int i = 0; i < m_spacedim; ++i)
860 {
861 if (m_hasVel[i])
862 {
863 Vmath::Sadd(npoints0, -m_velxyz[i] - m_travelWave[i],
864 inarray[i], 1, outarray[i], 1);
865 if (&inarray != &outarray && npoints != npoints0)
866 {
867 Array<OneD, NekDouble> tmp = outarray[i] + npoints0;
868 Vmath::Vcopy(npoints - npoints0, inarray[i] + npoints0, 1,
869 tmp, 1);
870 }
871 }
872 else if (&inarray != &outarray)
873 {
874 Vmath::Vcopy(npoints, inarray[i], 1, outarray[i], 1);
875 }
876 }
877 if (m_hasRotation)
878 {
879 addRotation(npoints0, outarray, -1., m_coords, outarray);
880 }
881 }
882}
883
885{
886 NekDouble time = 0.;
887 std::map<std::string, std::string> fieldMetaDataMap;
888 std::vector<std::string> strFrameData = {
889 "X", "Y", "Z", "Theta_x", "Theta_y", "Theta_z",
890 "U", "V", "W", "Omega_x", "Omega_y", "Omega_z",
891 "A_x", "A_y", "A_z", "DOmega_x", "DOmega_y", "DOmega_z"};
892 std::map<std::string, NekDouble> fileData;
893 if (m_session->DefinesFunction("InitialConditions"))
894 {
895 for (int i = 0; i < m_session->GetVariables().size(); ++i)
896 {
897 if (m_session->GetFunctionType("InitialConditions",
898 m_session->GetVariable(i)) ==
900 {
901 std::string filename = m_session->GetFunctionFilename(
902 "InitialConditions", m_session->GetVariable(i));
903 fs::path pfilename(filename);
904 // redefine path for parallel file which is in directory
905 if (fs::is_directory(pfilename))
906 {
907 fs::path metafile("Info.xml");
908 fs::path fullpath = pfilename / metafile;
909 filename = LibUtilities::PortablePath(fullpath);
910 }
913 fld->ImportFieldMetaData(filename, fieldMetaDataMap);
914
915 // check to see if time is defined
916 if (fieldMetaDataMap != LibUtilities::NullFieldMetaDataMap)
917 {
918 if (fieldMetaDataMap.find("Time") != fieldMetaDataMap.end())
919 {
920 time = std::stod(fieldMetaDataMap["Time"]);
921 }
922 fileData.clear();
923 for (auto &var : strFrameData)
924 {
925 if (fieldMetaDataMap.find(var) !=
926 fieldMetaDataMap.end())
927 {
928 fileData[var] = std::stod(fieldMetaDataMap[var]);
929 }
930 }
931 if (fileData.size() == strFrameData.size())
932 {
933 break;
934 }
935 }
936 }
937 }
938 }
939 if (m_session->DefinesCmdLineArgument("set-start-time"))
940 {
941 time = std::stod(
942 m_session->GetCmdLineArgument<std::string>("set-start-time"));
943 }
944 if (fileData.size() == strFrameData.size())
945 {
946 int NumDofm1 = m_body.vel[0].size() - 1;
947 for (int i = 0; i < m_spacedim; ++i)
948 {
949 m_body.vel[0][i] = fileData[strFrameData[i]];
950 m_body.vel[1][i] = fileData[strFrameData[i + 6]];
951 m_body.vel[2][i] = fileData[strFrameData[i + 12]];
952 }
953 m_body.vel[0][NumDofm1] = fileData[strFrameData[5]];
954 m_body.vel[1][NumDofm1] = fileData[strFrameData[11]];
955 m_body.vel[2][NumDofm1] = fileData[strFrameData[17]];
956 }
957 std::map<int, NekDouble> Dirs;
958 UpdatePrescribed(time, Dirs);
960}
961
963 std::map<int, NekDouble> &Dirs)
964{
965 for (auto it : Dirs)
966 {
967 int NumDof = m_body.vel[0].size();
968 int NumDof2 = NumDof << 1;
969 if (it.first < m_body.vel[0].size())
970 {
971 m_body.vel[1][it.first] = it.second;
972 }
973 else if (it.first < NumDof2)
974 {
975 m_body.vel[0][it.first - NumDof] = it.second;
976 }
977 else
978 {
979 m_body.vel[2][it.first - NumDof2] = it.second;
980 }
981 }
982}
983
987 const TiXmlElement *pForce)
988{
989 std::map<std::string, std::string> vParams;
990 vParams["OutputFile"] = ".dummyMRFForceFile";
991 const TiXmlElement *param = pForce->FirstChildElement("BOUNDARY");
992 ASSERTL0(param, "Body surface should be assigned");
993
994 vParams["Boundary"] = param->GetText();
995 const TiXmlElement *pivotElmt = pForce->FirstChildElement("PIVOTPOINT");
996 if (pivotElmt)
997 {
998 std::string pstr = pivotElmt->GetText();
999 std::replace(pstr.begin(), pstr.end(), ',', ' ');
1000 vParams["MomentPoint"] = pstr;
1001 }
1003 pSession, m_equ.lock(), vParams);
1004
1005 m_body.aeroforceFilter->Initialise(pFields, 0.0);
1006}
1007
1012 std::set<int> DirDoFs)
1013{
1015 m_coeffs[0] = 1. / (gamma * dt);
1016 m_coeffs[1] = 1. / gamma - 1.;
1017 m_coeffs[2] = beta * dt / gamma;
1018 m_coeffs[3] = dt * (1. - beta / gamma);
1019 m_coeffs[4] = (0.5 - beta / gamma) * dt * dt;
1020
1021 m_rows = sqrt(M.size());
1022 m_index.resize(m_rows, -1);
1023 m_motionDofs = 0;
1024 for (int i = 0; i < m_rows; ++i)
1025 {
1026 if (DirDoFs.find(i) == DirDoFs.end())
1027 {
1028 m_index[m_motionDofs++] = i;
1029 }
1030 }
1031 for (int i = 0, count = m_motionDofs; i < m_rows; ++i)
1032 {
1033 if (DirDoFs.find(i) != DirDoFs.end())
1034 {
1035 m_index[count++] = i;
1036 }
1037 }
1038 if (m_motionDofs)
1039 {
1045 DNekMatSharedPtr inverseMatrix =
1047 m_motionDofs, 0.0, eFULL);
1048 for (int i = 0; i < m_motionDofs; ++i)
1049 {
1054 int offset = m_index[i] * m_rows;
1055 for (int j = 0; j < m_rows; ++j)
1056 {
1057 int ind = offset + m_index[j];
1058 m_M[i][j] = M[ind];
1059 m_C[i][j] = C[ind];
1060 m_K[i][j] = K[ind];
1061 NekDouble value =
1062 m_coeffs[0] * M[ind] + C[ind] + m_coeffs[2] * K[ind];
1063 if (j < m_motionDofs)
1064 {
1065 inverseMatrix->SetValue(i, j, value);
1066 }
1067 }
1068 }
1069 inverseMatrix->Invert();
1070 for (int i = 0; i < m_motionDofs; ++i)
1071 {
1072 for (int j = 0; j < m_rows; ++j)
1073 {
1074 if (j < m_motionDofs)
1075 {
1076 m_Matrix[i][j] = inverseMatrix->GetValue(i, j);
1077 }
1078 }
1079 }
1080 }
1081}
1082
1085 std::map<int, NekDouble> motionPrescribed)
1086{
1087 for (int i = 0; i < m_rows; ++i)
1088 {
1089 if (motionPrescribed.find(i) != motionPrescribed.end())
1090 {
1091 int i0 = i + m_rows;
1092 int i2 = i0 + m_rows;
1093 NekDouble bm = 0., bk = 0.;
1094 if (motionPrescribed.find(i2) == motionPrescribed.end())
1095 {
1096 bm = m_coeffs[0] * u[1][i] + m_coeffs[1] * u[2][i];
1097 }
1098 if (motionPrescribed.find(i0) == motionPrescribed.end())
1099 {
1100 bk = u[0][i] + m_coeffs[3] * u[1][i] + m_coeffs[4] * u[2][i];
1101 }
1102
1103 u[1][i] = motionPrescribed[i];
1104 if (motionPrescribed.find(i2) == motionPrescribed.end())
1105 {
1106 u[2][i] = m_coeffs[0] * u[1][i] - bm;
1107 }
1108 else
1109 {
1110 u[2][i] = motionPrescribed[i2];
1111 }
1112 if (motionPrescribed.find(i0) == motionPrescribed.end())
1113 {
1114 u[0][i] = m_coeffs[2] * u[1][i] + bk;
1115 }
1116 else
1117 {
1118 u[0][i] = motionPrescribed[i0];
1119 }
1120 }
1121 }
1122}
1123
1126 std::map<int, NekDouble> motionPrescribed)
1127{
1128 SolvePrescribed(u, motionPrescribed);
1129 SolveFree(u, force);
1130}
1131
1134{
1135 if (m_motionDofs)
1136 {
1139 for (int j = 0; j < m_motionDofs; ++j)
1140 {
1141 int j1 = m_index[j];
1142 bm[j] = m_coeffs[0] * u[1][j1] + m_coeffs[1] * u[2][j1];
1143 bk[j] = u[0][j1] + m_coeffs[3] * u[1][j1] + m_coeffs[4] * u[2][j1];
1144 }
1146 for (int i = 0; i < m_motionDofs; ++i)
1147 {
1148 rhs[i] = force[m_index[i]];
1149 for (int j = 0; j < m_motionDofs; ++j)
1150 {
1151 rhs[i] += m_M[i][j] * bm[j] - m_K[i][j] * bk[j];
1152 }
1153 for (int j = m_motionDofs; j < m_rows; ++j)
1154 {
1155 int j1 = m_index[j];
1156 rhs[i] -= m_M[i][j] * u[2][j1] + m_C[i][j] * u[1][j1] +
1157 m_K[i][j] * u[0][j1];
1158 }
1159 }
1160 for (int j = 0; j < m_motionDofs; ++j)
1161 {
1162 int j1 = m_index[j];
1163 u[1][j1] = Vmath::Dot(m_motionDofs, m_Matrix[j], 1, rhs, 1);
1164 u[0][j1] = m_coeffs[2] * u[1][j1] + bk[j];
1165 u[2][j1] = m_coeffs[0] * u[1][j1] - bm[j];
1166 }
1167 }
1168}
1169
1171{
1173}
1174
1176{
1177 m_matrix[0] = cos(theta[2]);
1178 m_matrix[1] = sin(theta[2]);
1179}
1180
1182 const Array<OneD, NekDouble> &body,
1183 Array<OneD, NekDouble> &inertial)
1184{
1185 NekDouble xi = body[0], eta = body[1];
1186 inertial[0] = xi * m_matrix[0] - eta * m_matrix[1];
1187 inertial[1] = eta * m_matrix[0] + xi * m_matrix[1];
1188 if (body != inertial && dim > 2)
1189 {
1190 for (int i = 2; i < dim; ++i)
1191 {
1192 inertial[i] = body[i];
1193 }
1194 }
1195}
1196
1198 const Array<OneD, NekDouble> &inertial,
1200{
1201 NekDouble x = inertial[0], y = inertial[1];
1202 body[0] = x * m_matrix[0] + y * m_matrix[1];
1203 body[1] = y * m_matrix[0] - x * m_matrix[1];
1204 if (body != inertial && dim > 2)
1205 {
1206 for (int i = 2; i < dim; ++i)
1207 {
1208 body[i] = inertial[i];
1209 }
1210 }
1211}
1212
1213} // 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
static std::shared_ptr< FieldIO > CreateForFile(const LibUtilities::SessionReaderSharedPtr session, const std::string &filename)
Construct a FieldIO object for a given input filename.
Definition: FieldIO.cpp:224
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.
static bool GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
Definition: ParseUtils.cpp:130
Defines a forcing term to be explicitly applied.
Definition: Forcing.h:71
const std::weak_ptr< EquationSystem > m_equ
Weak pointer to equation system using this forcing.
Definition: Forcing.h:117
LibUtilities::SessionReaderSharedPtr m_session
Session reader.
Definition: Forcing.h:115
SOLVER_UTILS_EXPORT void v_PreApply(const Array< OneD, MultiRegions::ExpListSharedPtr > &fields, const Array< OneD, Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble &time) override
Compute the moving frame velocity at given time.
void addRotation(int npoints, const Array< OneD, Array< OneD, NekDouble > > &inarray0, NekDouble angVelScale, const Array< OneD, Array< OneD, NekDouble > > &inarray1, Array< OneD, Array< OneD, NekDouble > > &outarray)
outarray = inarray0 + angVelScale Omega x inarray1
std::map< int, LibUtilities::EquationSharedPtr > m_frameVelFunction
void SolveBodyMotion(Array< OneD, Array< OneD, NekDouble > > &bodyVel, const Array< OneD, NekDouble > &forcebody, std::map< int, NekDouble > &Dirs)
void UpdateFluidInterface(Array< OneD, Array< OneD, NekDouble > > &bodyVel, const int step)
void UpdatePrescribed(const NekDouble &time, std::map< int, NekDouble > &Dirs)
ForcingMovingReferenceFrame(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< EquationSystem > &pEquation)
SOLVER_UTILS_EXPORT void v_Apply(const Array< OneD, MultiRegions::ExpListSharedPtr > &fields, const Array< OneD, Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble &time) override
Adds the body force, -Omega X u.
SOLVER_UTILS_EXPORT void v_InitObject(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const unsigned int &pNumForcingFields, const TiXmlElement *pForce) override
Initialise the forcing module.
void UpdateFrameVelocity(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time)
Updates the forcing array with the current required forcing.
void UpdateBoundaryConditions(NekDouble time)
Set velocity boundary condition at the next time step.
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.
struct Nektar::SolverUtils::ForcingMovingReferenceFrame::@2 m_body
void InitialiseFilter(const LibUtilities::SessionReaderSharedPtr &pSession, const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, const TiXmlElement *pForce)
void SetAngle(const Array< OneD, NekDouble > theta)
void BodyToInerital(const int dim, const Array< OneD, NekDouble > &body, Array< OneD, NekDouble > &inertial)
void IneritalToBody(const int dim, const Array< OneD, NekDouble > &inertial, Array< OneD, NekDouble > &body)
void SetNewmarkBeta(NekDouble beta, NekDouble gamma, NekDouble dt, Array< OneD, NekDouble > M, Array< OneD, NekDouble > C, Array< OneD, NekDouble > K, std::set< int > DirDoFs)
Array< OneD, Array< OneD, NekDouble > > m_K
void SolveFree(Array< OneD, Array< OneD, NekDouble > > u, Array< OneD, NekDouble > force)
void SolvePrescribed(Array< OneD, Array< OneD, NekDouble > > u, std::map< int, NekDouble > motionPrescribed)
Array< OneD, Array< OneD, NekDouble > > m_Matrix
Array< OneD, Array< OneD, NekDouble > > m_C
void Solve(Array< OneD, Array< OneD, NekDouble > > u, Array< OneD, NekDouble > force, std::map< int, NekDouble > motionPrescribed)
Array< OneD, Array< OneD, NekDouble > > m_M
std::shared_ptr< FieldIO > FieldIOSharedPtr
Definition: FieldIO.h:322
static std::string PortablePath(const fs::path &path)
create portable path on different platforms for std::filesystem path.
Definition: Filesystem.hpp:56
std::shared_ptr< SessionReader > SessionReaderSharedPtr
static FieldMetaDataMap NullFieldMetaDataMap
Definition: FieldIO.h:51
@ beta
Gauss Radau pinned at x=-1,.
Definition: PointsType.h:59
static const NekDouble kNekMachineEpsilon
ForcingFactory & GetForcingFactory()
Declaration of the forcing factory singleton.
Definition: Forcing.cpp:42
static Array< OneD, NekDouble > NullNekDouble1DArray
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:75
double NekDouble
void Svtvp(int n, const T alpha, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Svtvp (scalar times vector plus vector): z = alpha*x + y.
Definition: Vmath.hpp:396
T Dot(int n, const T *w, const T *x)
dot product
Definition: Vmath.hpp:761
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 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 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:294