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_rank == 0)
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
102 int npoints = pFields[0]->GetNpoints();
103 m_expdim = m_isH2d ? 1 : pFields[0]->GetGraph()->GetMeshDimension();
104 m_spacedim = m_expdim + (m_isH1d ? 1 : 0) + (m_isH2d ? 2 : 0);
106
107 m_hasPlane0 = true;
108 if (m_isH1d)
109 {
110 m_hasPlane0 = pFields[0]->GetZIDs()[0] == 0;
111 }
112
113 // initialize variables
119 m_hasVel = Array<OneD, bool>(3, false);
120 m_hasOmega = Array<OneD, bool>(3, false);
124
125 // initialize time
126 NekDouble time = 0.;
127 CheckForRestartTime(pFields, time);
128 m_startTime = time;
129 // load moving frame infomation
130 LoadParameters(pForce, time);
131 if (m_hasRotation)
132 {
134 for (int j = 0; j < m_spacedim; ++j)
135 {
136 m_coords[j] = Array<OneD, NekDouble>(npoints);
137 }
138 pFields[0]->GetCoords(m_coords[0], m_coords[1], m_coords[2]);
139 // move the origin to the pivot point
140 for (int i = 0; i < m_spacedim; ++i)
141 {
142 Vmath::Sadd(npoints, -m_pivotPoint[i], m_coords[i], 1, m_coords[i],
143 1);
144 }
145 }
146
147 // init body solver
148 m_rank = pFields[0]->GetComm()->GetRank();
149 InitBodySolver(pForce, m_spacedim, m_rank, time);
150
151 // Initialize theta with the data from NS class
152 // This ensure correct moving coordinate orientation with respect to the
153 // stationary inertial frame when we restart the simulation
154 auto equ = m_equ.lock();
155 ASSERTL0(equ, "Weak pointer to the equation system is expired");
156 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
157 FluidEq->SetMovingFrameDisp(m_disp);
158
159 // initiate the rotation matrix to zero,
160 // Note that the rotation matrix is assumed for the rotation around z axis
161 // TODO: Generalize this for the 3D case with possiblity of rotation around
162 // each of axis. Probabley we only can support rotation around one axis. In
163 // that case the generalization means the user can provide Omega for one of
164 // x, y or z axis. Not sure how complicated to consider a general axis of
165 // rotation
166 //
167 // Note that these rotation matrices should be extrinsic rotations
168 m_ProjMatZ = bn::ublas::zero_matrix<NekDouble>(3, 3);
169 // populate the rotation matrix R(z)
170 {
171 NekDouble sn, cs;
172 sn = sin(m_disp[5]);
173 cs = cos(m_disp[5]);
174
175 m_ProjMatZ(0, 0) = cs;
176 m_ProjMatZ(0, 1) = sn;
177 m_ProjMatZ(1, 0) = -1. * sn;
178 m_ProjMatZ(1, 1) = cs;
179 m_ProjMatZ(2, 2) = 1.0;
180 }
181 // account for the effect of rotation
182 // Omega_X results in having v and w even if not defined by user
183 // Omega_Y results in having u and w even if not defined by user
184 // Omega_Z results in having u and v even if not defined by user
185 //
186 for (int i = 0; i < 3; ++i)
187 {
188 int j = (i + 1) % 3;
189 int k = (i + 2) % 3;
190
191 if (m_hasOmega[i])
192 {
193 m_hasVel[j] = true;
194 m_hasVel[k] = true;
195 }
196 }
197 // initialize aeroforce filter
198 if (m_hasFreeMotion)
199 {
200 InitialiseFilter(m_session, pFields, pForce);
201 }
202}
203
205 std::string expression)
206{
207 NekDouble value = 0.;
208 try
209 {
210 LibUtilities::Equation expession(m_session->GetInterpreter(),
211 expression);
212 value = expession.Evaluate();
213 }
214 catch (const std::runtime_error &)
215 {
216 NEKERROR(ErrorUtil::efatal, "Error evaluating expression" + expression);
217 }
218 return value;
219}
220
221void ForcingMovingReferenceFrame::LoadParameters(const TiXmlElement *pForce,
222 const NekDouble time)
223{
224 const TiXmlElement *funcNameElmt;
225 // load frame velocity
226 funcNameElmt = pForce->FirstChildElement("FRAMEVELOCITY");
227 if (funcNameElmt)
228 {
229 std::string FuncName = funcNameElmt->GetText();
230 ASSERTL0(m_session->DefinesFunction(FuncName),
231 "Function '" + FuncName + "' is not defined in the session.");
232 // linear velocity
233 for (int i = 0; i < m_spacedim; ++i)
234 {
235 std::string var = m_session->GetVariable(i);
236 if (m_session->DefinesFunction(FuncName, var))
237 {
238 m_frameVelFunction[i] = m_session->GetFunction(FuncName, var);
239 m_hasVel[i] = true;
240 }
241 }
242 // angular velocities
243 m_hasRotation = false;
244 std::vector<std::string> angularVar = {"Omega_x", "Omega_y", "Omega_z"};
245 for (int i = 0; i < 3; ++i)
246 {
247 std::string var = angularVar[i];
248 if (m_session->DefinesFunction(FuncName, var))
249 {
250 m_frameVelFunction[i + 3] =
251 m_session->GetFunction(FuncName, var);
252 m_hasOmega[i] = true;
253 m_hasRotation = true;
254 }
255 }
256 // TODO: add the support for general rotation
257 for (int i = 0; i < 2; ++i)
258 {
259 ASSERTL0(!m_hasOmega[i], "Currently only Omega_z is supported");
260 }
261 }
262 if (m_expdim == 1)
263 {
264 m_hasRotation = false;
265 }
266
267 // load external force
268 funcNameElmt = pForce->FirstChildElement("EXTERNALFORCE");
269 if (funcNameElmt)
270 {
271 std::string FuncName = funcNameElmt->GetText();
272 if (m_session->DefinesFunction(FuncName))
273 {
274 std::vector<std::string> forceVar = {"fx", "fy", "fz"};
275 for (int i = 0; i < m_spacedim; ++i)
276 {
277 std::string var = forceVar[i];
278 if (m_session->DefinesFunction(FuncName, var))
279 {
281 m_session->GetFunction(FuncName, var);
282 }
283 }
284 std::vector<std::string> momentVar = {"Mx", "My", "Mz"};
285 for (int i = 0; i < 3; ++i)
286 {
287 std::string var = momentVar[i];
288 if (m_session->DefinesFunction(FuncName, var))
289 {
290 m_extForceFunction[i + 3] =
291 m_session->GetFunction(FuncName, var);
292 }
293 }
294 }
295 }
296
297 // load pitching pivot
298 const TiXmlElement *pivotElmt = pForce->FirstChildElement("PIVOTPOINT");
299 if (pivotElmt)
300 {
301 std::vector<std::string> values;
302 std::string mssgStr = pivotElmt->GetText();
303 ParseUtils::GenerateVector(mssgStr, values);
304 for (int j = 0; j < m_spacedim; ++j)
305 {
306 m_pivotPoint[j] = EvaluateExpression(values[j]);
307 m_disp[6 + j] = m_pivotPoint[j];
308 }
309 }
310
311 // load travelling wave speed
312 const TiXmlElement *TWSElmt =
313 pForce->FirstChildElement("TRAVELINGWAVESPEED");
314 if (TWSElmt)
315 {
316 std::vector<std::string> values;
317 std::string mssgStr = TWSElmt->GetText();
318 ParseUtils::GenerateVector(mssgStr, values);
319 for (int j = 0; j < m_spacedim; ++j)
320 {
321 NekDouble tmp = EvaluateExpression(values[j]);
322 if (fabs(tmp) > NekConstants::kNekMachineEpsilon)
323 {
324 m_travelWave[j] = tmp;
325 m_hasVel[j] = true;
326 }
327 }
328 }
329
330 // load initial displacement
331 std::map<int, LibUtilities::EquationSharedPtr> initDisplacement;
332 funcNameElmt = pForce->FirstChildElement("INITIALDISPLACEMENT");
333 if (funcNameElmt)
334 {
335 std::string FuncName = funcNameElmt->GetText();
336 ASSERTL0(m_session->DefinesFunction(FuncName),
337 "Function '" + FuncName + "' is not defined in the session.");
338 // linear displacement
339 std::vector<std::string> dispVar = {"x", "y", "z"};
340 for (int i = 0; i < m_spacedim; ++i)
341 {
342 std::string var = dispVar[i];
343 if (m_session->DefinesFunction(FuncName, var))
344 {
345 m_disp[i] = m_session->GetFunction(FuncName, var)
346 ->Evaluate(0., 0., 0., time);
347 }
348 }
349 // angular displacement
350 std::vector<std::string> angleVar = {"theta_x", "theta_y", "theta_z"};
351 for (int i = 0; i < 3; ++i)
352 {
353 std::string var = angleVar[i];
354 if (m_session->DefinesFunction(FuncName, var))
355 {
356 m_disp[i + 3] =
357 m_session->GetFunction(FuncName, var)->Evaluate();
358 }
359 }
360 }
361}
362
363void ForcingMovingReferenceFrame::InitBodySolver(const TiXmlElement *pForce,
364 const int dim, const int rank,
365 const NekDouble time)
366{
367 int NumDof = dim + 1;
368 std::set<int> DirBCs;
369 const TiXmlElement *mssgTag;
370 std::string mssgStr;
371 // read free motion DoFs
372 m_DirDoFs.clear();
373 for (int i = 0; i < NumDof; ++i)
374 {
375 m_DirDoFs.insert(i);
376 }
377 mssgTag = pForce->FirstChildElement("MOTIONPRESCRIBED");
378 if (mssgTag)
379 {
380 std::vector<std::string> values;
381 mssgStr = mssgTag->GetText();
382 ParseUtils::GenerateVector(mssgStr, values);
383 ASSERTL0(values.size() >= NumDof,
384 "MOTIONPRESCRIBED vector should be of size " +
385 std::to_string(NumDof));
386 for (int i = 0; i < NumDof; ++i)
387 {
388 if (EvaluateExpression(values[i]) == 0)
389 {
390 m_DirDoFs.erase(i);
391 if (i < dim)
392 {
393 m_hasVel[i] = true;
394 }
395 else if (i == dim)
396 {
397 m_hasOmega[2] = true;
398 }
399 }
400 }
401 }
402 m_hasFreeMotion = m_DirDoFs.size() < NumDof;
403 // read mass matrix
406 mssgTag = pForce->FirstChildElement("MASS");
407 ASSERTL0(m_DirDoFs.size() == NumDof || mssgTag, "Mass matrix is required.");
408 if (mssgTag)
409 {
410 std::vector<std::string> values;
411 mssgStr = mssgTag->GetText();
412 ParseUtils::GenerateVector(mssgStr, values);
413 ASSERTL0(values.size() >= NumDof * NumDof,
414 "Mass matrix should be of size " + std::to_string(NumDof) +
415 "X" + std::to_string(NumDof));
416 for (int i = 0; i < NumDof; ++i)
417 {
418 for (int j = 0; j < NumDof; ++j)
419 {
420 M->SetValue(i, j, EvaluateExpression(values[i * NumDof + j]));
421 }
422 }
423 }
424 // read damping matrix
427 mssgTag = pForce->FirstChildElement("DAMPING");
428 if (mssgTag)
429 {
430 std::vector<std::string> values;
431 mssgStr = mssgTag->GetText();
432 ParseUtils::GenerateVector(mssgStr, values);
433 ASSERTL0(values.size() >= NumDof * NumDof,
434 "Damping matrix should be of size " + std::to_string(NumDof) +
435 "X" + std::to_string(NumDof));
436 for (int i = 0; i < NumDof; ++i)
437 {
438 for (int j = 0; j < NumDof; ++j)
439 {
440 C->SetValue(i, j, EvaluateExpression(values[i * NumDof + j]));
441 }
442 }
443 }
444 // read rigidity matrix
447 mssgTag = pForce->FirstChildElement("RIGIDITY");
448 if (mssgTag)
449 {
450 std::vector<std::string> values;
451 mssgStr = mssgTag->GetText();
452 ParseUtils::GenerateVector(mssgStr, values);
453 ASSERTL0(values.size() >= NumDof * NumDof,
454 "Rigidity matrix should be of size " + std::to_string(NumDof) +
455 "X" + std::to_string(NumDof));
456 for (int i = 0; i < NumDof; ++i)
457 {
458 for (int j = 0; j < NumDof; ++j)
459 {
460 K->SetValue(i, j, EvaluateExpression(values[i * NumDof + j]));
461 }
462 }
463 }
464 // read Newmark Beta paramters
465 m_timestep = m_session->GetParameter("TimeStep");
466 NekDouble beta = 0.25;
467 NekDouble gamma = 0.75;
468 if (m_session->DefinesParameter("NewmarkBeta"))
469 {
470 beta = m_session->GetParameter("NewmarkBeta");
471 }
472 if (m_session->DefinesParameter("NewmarkGamma"))
473 {
474 gamma = m_session->GetParameter("NewmarkGamma");
475 }
477 // OutputFile
478 string filename;
479 mssgTag = pForce->FirstChildElement("OutputFile");
480 if (mssgTag)
481 {
482 filename = mssgTag->GetText();
483 }
484 else
485 {
486 filename = m_session->GetSessionName();
487 }
488 if (!(filename.length() >= 4 &&
489 filename.substr(filename.length() - 4) == ".mrf"))
490 {
491 filename += ".mrf";
492 }
493 if (rank == 0)
494 {
495 m_outputStream.open(filename.c_str());
496 if (dim == 2)
497 {
499 << "Variables = t, x, ux, ax, y, uy, ay, theta, omega, domega"
500 << endl;
501 }
502 else if (dim == 3)
503 {
504 m_outputStream << "Variables = t, x, ux, ax, y, uy, ay, z, uz, az, "
505 "theta, omega, domega"
506 << endl;
507 }
508 }
509 // output frequency
510 m_index = 0;
512 mssgTag = pForce->FirstChildElement("OutputFrequency");
513 if (mssgTag)
514 {
515 std::vector<std::string> values;
516 mssgStr = mssgTag->GetText();
517 m_outputFrequency = round(EvaluateExpression(mssgStr));
518 }
520 "OutputFrequency should be greater than zero.");
521 // initialize displacement velocity
523 for (size_t i = 0; i < 3; ++i)
524 {
525 m_bodyVel[i] = Array<OneD, NekDouble>(NumDof, 0.);
526 }
527 for (int i = 0; i < m_spacedim; ++i)
528 {
529 m_bodyVel[0][i] = m_disp[i];
530 }
531 m_bodyVel[0][NumDof - 1] = m_disp[5];
532 for (auto it : m_frameVelFunction)
533 {
534 if (it.first < 3)
535 {
536 m_bodyVel[1][it.first] = it.second->Evaluate(0., 0., 0., time);
537 }
538 else if (it.first == 5)
539 {
540 m_bodyVel[1][NumDof - 1] = it.second->Evaluate(0., 0., 0., time);
541 }
542 }
543}
544
545/**
546 * @brief Updates the forcing array with the current required forcing.
547 * @param pFields
548 * @param time
549 */
552 const NekDouble &time)
553{
554 // compute the velociites whos functions are provided in inertial frame
555 for (auto it : m_frameVelFunction)
556 {
557 if (it.first < 3)
558 {
559 m_velXYZ[it.first] = it.second->Evaluate(0., 0., 0., time);
560 }
561 else
562 {
563 m_omegaXYZ[it.first - 3] = it.second->Evaluate(0., 0., 0., time);
564 }
565 }
566 for (auto it : m_extForceFunction)
567 {
568 m_extForceXYZ[it.first] = it.second->Evaluate(0., 0., 0., time);
569 }
570 auto equ = m_equ.lock();
571 ASSERTL0(equ, "Weak pointer to the equation system is expired");
572 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
573 if (time > m_startTime)
574 {
575 Array<OneD, NekDouble> forcebody(6, 0.); // fluid force
576 std::map<int, NekDouble> Dirs; // prescribed Motion
577 for (auto it : m_DirDoFs)
578 {
579 if (it < m_spacedim)
580 {
581 Dirs[it] = m_velXYZ[it];
582 }
583 else if (it == m_spacedim)
584 {
585 Dirs[it] = m_omegaXYZ[2];
586 }
587 }
588 if (m_hasFreeMotion)
589 {
590 m_aeroforceFilter->GetForces(pFields, NullNekDouble1DArray, time);
591 }
592 FluidEq->GetAeroForce(forcebody);
593 SolveBodyMotion(m_bodyVel, forcebody, Dirs);
594 }
595 if (m_rank == 0 && m_index % m_outputFrequency == 0)
596 {
597 m_outputStream << boost::format("%25.19e") % time << " ";
598 for (size_t i = 0; i < m_bodyVel[0].size(); ++i)
599 {
600 m_outputStream << boost::format("%25.19e") % m_bodyVel[0][i] << " "
601 << boost::format("%25.19e") % m_bodyVel[1][i] << " "
602 << boost::format("%25.19e") % m_bodyVel[2][i] << " ";
603 }
604 m_outputStream << endl;
605 }
606 // overwirte m_omegaXYZ with u, also update theta
607 for (int i = 0; i < m_spacedim; ++i)
608 {
609 m_velXYZ[i] = m_bodyVel[1][i];
610 }
612 for (int i = 0; i < m_spacedim; ++i)
613 {
614 m_disp[i] = m_bodyVel[0][i] + m_travelWave[i] * time;
615 }
616 m_disp[5] = m_bodyVel[0][m_spacedim];
617
618 // include the effect of rotation
619 if (m_hasRotation)
620 {
621 UpdateRotMat();
625 }
626 else
627 {
628 // for translation only,
629 for (int i = 0; i < m_spacedim; ++i)
630 {
631 m_velxyz[i] = m_velXYZ[i];
632 }
633 }
634 ++m_index;
635}
636
639 const Array<OneD, NekDouble> &forcebody, std::map<int, NekDouble> &Dirs)
640{
641 if (!m_hasRotation || Dirs.find(m_spacedim) != Dirs.end())
642 {
643 Array<OneD, NekDouble> force(6, 0.), tmp;
644 if (Dirs.find(m_spacedim) != Dirs.end())
645 {
646 Array<OneD, NekDouble> angle(3, 0.);
647 angle[2] =
648 bodyVel[0][m_spacedim] +
649 0.5 * m_timestep * (bodyVel[1][m_spacedim] + Dirs[m_spacedim]);
650 m_frame.SetAngle(angle);
651 m_frame.BodyToInerital(3, forcebody, force);
652 m_frame.BodyToInerital(3, forcebody + 3, tmp = force + 3);
653 }
654 else
655 {
656 Vmath::Vcopy(6, forcebody, 1, force, 1);
657 }
658 for (int i = 0; i < m_spacedim; ++i)
659 {
660 force[i] += m_extForceXYZ[i];
661 }
662 force[m_spacedim] = force[5] + m_extForceXYZ[5];
663 m_bodySolver.Solve(bodyVel, force, Dirs);
664 }
665 else
666 {
667 Array<OneD, Array<OneD, NekDouble>> tmpbodyVel(bodyVel.size());
668 for (size_t i = 0; i < bodyVel.size(); ++i)
669 {
670 tmpbodyVel[i] = Array<OneD, NekDouble>(bodyVel[i].size());
671 }
672 Array<OneD, NekDouble> angle(3, 0.);
673 angle[2] = bodyVel[0][m_spacedim];
674 for (int iter = 0; iter < 2; ++iter)
675 {
676 // copy initial condition
677 for (size_t i = 0; i < bodyVel.size(); ++i)
678 {
679 Vmath::Vcopy(bodyVel[i].size(), bodyVel[i], 1, tmpbodyVel[i],
680 1);
681 }
682 Array<OneD, NekDouble> force(6, 0.), tmp;
683 m_frame.SetAngle(angle);
684 m_frame.BodyToInerital(3, forcebody, force);
685 m_frame.BodyToInerital(3, forcebody + 3, tmp = force + 3);
686 for (int i = 0; i < m_spacedim; ++i)
687 {
688 force[i] += m_extForceXYZ[i];
689 }
690 force[m_spacedim] = force[5] + m_extForceXYZ[5];
691 m_bodySolver.Solve(tmpbodyVel, force, Dirs);
692 angle[2] = tmpbodyVel[0][m_spacedim];
693 }
694 // copy final results
695 for (size_t i = 0; i < bodyVel.size(); ++i)
696 {
697 Vmath::Vcopy(bodyVel[i].size(), tmpbodyVel[i], 1, bodyVel[i], 1);
698 }
699 }
700}
701
703{
704 // update the rotation matrix
705 NekDouble sn, cs;
706 sn = sin(m_disp[5]);
707 cs = cos(m_disp[5]);
708
709 m_ProjMatZ(0, 0) = cs;
710 m_ProjMatZ(0, 1) = sn;
711 m_ProjMatZ(1, 0) = -sn;
712 m_ProjMatZ(1, 1) = cs;
713 m_ProjMatZ(2, 2) = 1.0;
714}
715
716/**
717 * @brief Adds the body force, -Omega X u.
718 * @param fields
719 * @param inarray
720 * @param outarray
721 * @param time
722 */
725 const Array<OneD, Array<OneD, NekDouble>> &inarray,
726 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble &time)
727{
728 // If there is no rotation, body force is zero,
729 // nothing needs to be done here.
730 if (m_hasRotation)
731 {
732 int npoints = fields[0]->GetNpoints();
733 addRotation(npoints, outarray, -1., inarray, outarray);
734 }
736}
737
738/**
739 * @brief Set velocity boundary condition at the next time step
740 */
742{
743 time += m_timestep;
744 // compute the velocities whose functions are provided in inertial frame
745 for (auto it : m_frameVelFunction)
746 {
747 if (it.first < 3)
748 {
749 m_velXYZ[it.first] = it.second->Evaluate(0., 0., 0., time);
750 }
751 else
752 {
753 m_omegaXYZ[it.first - 3] = it.second->Evaluate(0., 0., 0., time);
754 }
755 }
756 for (auto it : m_extForceFunction)
757 {
758 m_extForceXYZ[it.first] = it.second->Evaluate(0., 0., 0., time);
759 }
760 std::map<int, NekDouble> Dirs; // prescribed Motion
761 for (auto it : m_DirDoFs)
762 {
763 if (it < m_spacedim)
764 {
765 Dirs[it] = m_velXYZ[it];
766 }
767 else if (it == m_spacedim)
768 {
769 Dirs[it] = m_omegaXYZ[2];
770 }
771 }
772
773 auto equ = m_equ.lock();
774 ASSERTL0(equ, "Weak pointer to the equation system is expired");
775 auto FluidEq = std::dynamic_pointer_cast<FluidInterface>(equ);
776 Array<OneD, NekDouble> forcebody(6, 0.); // fluid force
777 FluidEq->GetAeroForce(forcebody);
778
780 for (size_t i = 0; i < m_bodyVel.size(); ++i)
781 {
782 bodyVel[i] = Array<OneD, NekDouble>(m_bodyVel[i].size());
783 }
784 // copy initial condition
785 for (size_t i = 0; i < m_bodyVel.size(); ++i)
786 {
787 Vmath::Vcopy(m_bodyVel[i].size(), m_bodyVel[i], 1, bodyVel[i], 1);
788 }
789 SolveBodyMotion(bodyVel, forcebody, Dirs);
790 // to stablize the velocity bcs, use the old values if extrapolation is
791 // involved
792 for (size_t i = 0; i < bodyVel[0].size(); ++i)
793 {
794 if (Dirs.find(i) == Dirs.end())
795 {
796 for (size_t k = 0; k < bodyVel.size(); ++k)
797 {
798 bodyVel[k][i] = m_bodyVel[k][i];
799 }
800 }
801 }
802 // set displacements at the next time step
803 for (int i = 0; i < m_spacedim; ++i)
804 {
805 m_disp[i] = bodyVel[0][i] + m_travelWave[i] * time;
806 }
807 m_disp[5] = bodyVel[0][m_spacedim];
808 // copy the velocities and rotation matrix for enforcing bc
809 // copy linear and angular velocities and accelerations
810 Array<OneD, NekDouble> vel(12, 0.0), tmp;
811 for (int i = 0; i < m_spacedim; ++i)
812 {
813 vel[i] = bodyVel[1][i];
814 vel[i + 6] = bodyVel[2][i];
815 }
816 vel[5] = bodyVel[1][m_spacedim];
817 vel[11] = bodyVel[2][m_spacedim];
818 if (m_hasRotation)
819 {
820 UpdateRotMat();
822 m_frame.IneritalToBody(3, vel, vel);
823 m_frame.IneritalToBody(3, tmp = vel + 6, vel + 6);
824 }
825
826 // to set the boundary condition of the next time step
827 // update the frame velocities
828 FluidEq->SetMovingFrameVelocities(vel);
829 // update the projection matrix
830 FluidEq->SetMovingFrameProjectionMat(m_ProjMatZ);
831 // update the frame angle (with respect to the inertial frame)
832 // this angle is used to update the meta data,
833 // on the other hand, for boundary conditions the projection matrix is used
834 FluidEq->SetMovingFrameDisp(m_disp);
835}
836
837/**
838 * @brief outarray = inarray0 + angVelScale Omega x inarray1
839 */
841 int nPnts, // number of points
842 const Array<OneD, Array<OneD, NekDouble>> &inarray0, NekDouble angVelScale,
843 const Array<OneD, Array<OneD, NekDouble>> &inarray1,
845{
846 ASSERTL0(&inarray1 != &outarray, "inarray1 and outarray "
847 "should not be the same.");
848
849 // TODO: In case of having support for all three components of Omega,
850 // they should be transformed into the rotating frame first!
851
852 // In case that the inarray0 and outarry are different, to avoid using
853 // un-initialized array, copy the array first
854 if (&inarray0 != &outarray)
855 {
856 ASSERTL0(inarray0.size() == outarray.size(),
857 "inarray0 and outarray must have same dimentions");
858 for (int i = 0; i < inarray0.size(); ++i)
859 {
860 Vmath::Vcopy(nPnts, inarray0[i], 1, outarray[i], 1);
861 }
862 }
863
864 if (m_spacedim >= 2 && m_hasOmega[2])
865 {
866 NekDouble cp = m_omegaxyz[2] * angVelScale;
867 NekDouble cm = -1. * cp;
868
869 Vmath::Svtvp(nPnts, cm, inarray1[1], 1, outarray[0], 1, outarray[0], 1);
870 Vmath::Svtvp(nPnts, cp, inarray1[0], 1, outarray[1], 1, outarray[1], 1);
871 }
872
873 if (m_spacedim == 3 && m_hasOmega[0])
874 {
875 NekDouble cp = m_omegaxyz[0] * angVelScale;
876 NekDouble cm = -1. * cp;
877
878 Vmath::Svtvp(nPnts, cp, inarray1[1], 1, outarray[2], 1, outarray[2], 1);
879 Vmath::Svtvp(nPnts, cm, inarray1[2], 1, outarray[1], 1, outarray[1], 1);
880 }
881
882 if (m_spacedim == 3 && m_hasOmega[1])
883 {
884 NekDouble cp = m_omegaxyz[1] * angVelScale;
885 NekDouble cm = -1. * cp;
886
887 Vmath::Svtvp(nPnts, cp, inarray1[2], 1, outarray[0], 1, outarray[0], 1);
888 Vmath::Svtvp(nPnts, cm, inarray1[0], 1, outarray[2], 1, outarray[2], 1);
889 }
890}
891
894 const Array<OneD, Array<OneD, NekDouble>> &inarray,
895 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble &time)
896{
897 Update(fields, time);
898 int npoints = fields[0]->GetNpoints();
899 if (m_isH2d && fields[0]->GetWaveSpace())
900 {
901 for (int i = 0; i < m_spacedim; ++i)
902 {
903 if (m_hasVel[i])
904 {
905 Array<OneD, NekDouble> tmpphys(npoints,
906 -m_velxyz[i] - m_travelWave[i]);
907 Array<OneD, NekDouble> tmpcoef(npoints);
908 fields[0]->HomogeneousFwdTrans(npoints, tmpphys, tmpcoef);
909 Vmath::Vadd(npoints, tmpcoef, 1, inarray[i], 1, outarray[i], 1);
910 }
911 else if (&inarray != &outarray)
912 {
913 Vmath::Vcopy(npoints, inarray[i], 1, outarray[i], 1);
914 }
915 }
916 }
917 else
918 {
919 int npoints0 = npoints;
920 if (m_isH1d && fields[0]->GetWaveSpace())
921 {
922 npoints0 = m_hasPlane0 ? fields[0]->GetPlane(0)->GetNpoints() : 0;
923 }
924 for (int i = 0; i < m_spacedim; ++i)
925 {
926 if (m_hasVel[i])
927 {
928 Vmath::Sadd(npoints0, -m_velxyz[i] - m_travelWave[i],
929 inarray[i], 1, outarray[i], 1);
930 if (&inarray != &outarray && npoints != npoints0)
931 {
932 Array<OneD, NekDouble> tmp = outarray[i] + npoints0;
933 Vmath::Vcopy(npoints - npoints0, inarray[i] + npoints0, 1,
934 tmp, 1);
935 }
936 }
937 else if (&inarray != &outarray)
938 {
939 Vmath::Vcopy(npoints, inarray[i], 1, outarray[i], 1);
940 }
941 }
942 if (m_hasRotation)
943 {
944 addRotation(npoints0, outarray, -1., m_coords, outarray);
945 }
946 }
947}
948
951{
952 std::map<std::string, std::string> fieldMetaDataMap;
953 if (m_session->DefinesFunction("InitialConditions"))
954 {
955 for (int i = 0; i < pFields.size(); ++i)
956 {
958
959 vType = m_session->GetFunctionType("InitialConditions",
960 m_session->GetVariable(i));
961
963 {
964 std::string filename = m_session->GetFunctionFilename(
965 "InitialConditions", m_session->GetVariable(i));
966
967 fs::path pfilename(filename);
968
969 // redefine path for parallel file which is in directory
970 if (fs::is_directory(pfilename))
971 {
972 fs::path metafile("Info.xml");
973 fs::path fullpath = pfilename / metafile;
974 filename = LibUtilities::PortablePath(fullpath);
975 }
978 fld->ImportFieldMetaData(filename, fieldMetaDataMap);
979
980 // check to see if time is defined
981 if (fieldMetaDataMap != LibUtilities::NullFieldMetaDataMap)
982 {
983 std::string strt = "Time";
984 if (fieldMetaDataMap.find(strt) != fieldMetaDataMap.end())
985 {
986 time = boost::lexical_cast<NekDouble>(
987 fieldMetaDataMap[strt]);
988 break;
989 }
990 }
991 }
992 }
993 }
994}
995
999 const TiXmlElement *pForce)
1000{
1001 std::map<std::string, std::string> vParams;
1002 vParams["OutputFile"] = ".dummyfilename";
1003 const TiXmlElement *param = pForce->FirstChildElement("BOUNDARY");
1004 ASSERTL0(param, "Body surface should be assigned");
1005 vParams["Boundary"] = param->GetText();
1007 pSession, m_equ, vParams);
1008 m_aeroforceFilter->Initialise(pFields, 0.0);
1009}
1010
1014 std::set<int> DirDoFs)
1015{
1017 m_coeffs[0] = 1. / (gamma * dt);
1018 m_coeffs[1] = 1. / gamma - 1.;
1019 m_coeffs[2] = beta * dt / gamma;
1020 m_coeffs[3] = dt * (1. - beta / gamma);
1021 m_coeffs[4] = (0.5 - beta / gamma) * dt * dt;
1022
1023 m_rows = M->GetRows();
1028 for (int i = 0; i < m_rows; ++i)
1029 {
1030 for (int j = 0; j < m_rows; ++j)
1031 {
1032 NekDouble value = m_coeffs[0] * M->GetValue(i, j) +
1033 C->GetValue(i, j) +
1034 m_coeffs[2] * K->GetValue(i, j);
1035 m_coeffMatrix->SetValue(i, j, value);
1036 if (DirDoFs.find(i) != DirDoFs.end() ||
1037 DirDoFs.find(j) != DirDoFs.end())
1038 {
1039 value = (i == j) ? 1. : 0.;
1040 }
1041 m_inverseMatrix->SetValue(i, j, value);
1042 }
1043 }
1044 m_inverseMatrix->Invert();
1045 m_M = M;
1046 m_C = C;
1047 m_K = K;
1048}
1049
1052 std::map<int, NekDouble> motionPrescribed)
1053{
1055 for (int i = 0; i < m_rows; ++i)
1056 {
1057 bm[i] = m_coeffs[0] * u[1][i] + m_coeffs[1] * u[2][i];
1058 }
1060 Multiply(fbm, *m_M, bm);
1062 for (int i = 0; i < m_rows; ++i)
1063 {
1064 bk[i] = u[0][i] + m_coeffs[3] * u[1][i] + m_coeffs[4] * u[2][i];
1065 }
1067 Multiply(fbk, *m_K, bk);
1069 for (int i = 0; i < m_rows; ++i)
1070 {
1071 rhs[i] = force[i] - fbk[i] + fbm[i];
1072 }
1073 // apply Dirichelet DoFs
1074 for (int i = 0; i < 3; ++i)
1075 {
1076 if (motionPrescribed.find(i) != motionPrescribed.end())
1077 {
1078 rhs[i] = motionPrescribed[i];
1079 }
1080 else
1081 {
1082 for (auto it : motionPrescribed)
1083 {
1084 rhs[i] -= it.second * m_coeffMatrix->GetValue(it.first, i);
1085 }
1086 }
1087 }
1088 // solve
1090 Multiply(b, *m_inverseMatrix, rhs);
1091 for (int i = 0; i < m_rows; ++i)
1092 {
1093 u[1][i] = b[i];
1094 u[0][i] = m_coeffs[2] * u[1][i] + bk[i];
1095 u[2][i] = m_coeffs[0] * u[1][i] - bm[i];
1096 }
1097}
1098
1100{
1102}
1103
1105{
1106 m_matrix[0] = cos(theta[2]);
1107 m_matrix[1] = sin(theta[2]);
1108}
1110 const Array<OneD, NekDouble> &body,
1111 Array<OneD, NekDouble> &inertial)
1112{
1113 NekDouble xi = body[0], eta = body[1];
1114 inertial[0] = xi * m_matrix[0] - eta * m_matrix[1];
1115 inertial[1] = eta * m_matrix[0] + xi * m_matrix[1];
1116 if (body != inertial && dim > 2)
1117 {
1118 for (int i = 2; i < dim; ++i)
1119 {
1120 inertial[i] = body[i];
1121 }
1122 }
1123}
1125 const Array<OneD, NekDouble> &inertial)
1126{
1127 NekDouble x = inertial[0], y = inertial[1];
1128 body[0] = x * m_matrix[0] + y * m_matrix[1];
1129 body[1] = y * m_matrix[0] - x * m_matrix[1];
1130 if (body != inertial && dim > 2)
1131 {
1132 for (int i = 2; i < dim; ++i)
1133 {
1134 body[i] = inertial[i];
1135 }
1136 }
1137}
1138
1139} // 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.
Definition: NekFactory.hpp:197
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
int m_NumVariable
Number of variables.
Definition: Forcing.h:121
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
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 CheckForRestartTime(const Array< OneD, MultiRegions::ExpListSharedPtr > &pFields, NekDouble &time)
void InitBodySolver(const TiXmlElement *pForce, const int dim, const int rank, const NekDouble time)
ForcingMovingReferenceFrame(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< EquationSystem > &pEquation)
std::map< int, LibUtilities::EquationSharedPtr > m_extForceFunction
void LoadParameters(const TiXmlElement *pForce, const NekDouble time)
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 Update(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.
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, Array< OneD, NekDouble > &body, const Array< OneD, NekDouble > &inertial)
void SetNewmarkBeta(NekDouble beta, NekDouble gamma, NekDouble dt, DNekMatSharedPtr M, DNekMatSharedPtr C, DNekMatSharedPtr K, std::set< int > DirDoFs)
void Solve(Array< OneD, Array< OneD, NekDouble > > u, Array< OneD, NekDouble > force, std::map< int, NekDouble > motionPrescribed)
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
void Multiply(NekMatrix< ResultDataType, StandardMatrixTag > &result, const NekMatrix< LhsDataType, LhsMatrixType > &lhs, const ResultDataType &rhs)
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
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