Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
IncNavierStokes.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File IncNavierStokes.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 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Incompressible Navier Stokes class definition built on
33 // ADRBase class
34 //
35 ///////////////////////////////////////////////////////////////////////////////
36 
37 #include <iomanip>
38 #include <boost/algorithm/string.hpp>
39 
46 
47 namespace Nektar
48 {
49 
50  /**
51  * Constructor. Creates ...
52  *
53  * \param
54  * \param
55  */
57  UnsteadySystem(pSession),
58  AdvectionSystem(pSession),
59  m_subSteppingScheme(false),
60  m_SmoothAdvection(false),
61  m_steadyStateSteps(0)
62  {
63  }
64 
66  {
68 
69  int i,j;
70  int numfields = m_fields.num_elements();
71  std::string velids[] = {"u","v","w"};
72 
73  // Set up Velocity field to point to the first m_expdim of m_fields;
74  m_velocity = Array<OneD,int>(m_spacedim);
75 
76  for(i = 0; i < m_spacedim; ++i)
77  {
78  for(j = 0; j < numfields; ++j)
79  {
80  std::string var = m_boundaryConditions->GetVariable(j);
81  if(boost::iequals(velids[i], var))
82  {
83  m_velocity[i] = j;
84  break;
85  }
86 
87  ASSERTL0(j != numfields, "Failed to find field: " + var);
88  }
89  }
90 
91  // Set up equation type enum using kEquationTypeStr
92  for(i = 0; i < (int) eEquationTypeSize; ++i)
93  {
94  bool match;
95  m_session->MatchSolverInfo("EQTYPE",kEquationTypeStr[i],match,false);
96  if(match)
97  {
99  break;
100  }
101  }
102  ASSERTL0(i != eEquationTypeSize,"EQTYPE not found in SOLVERINFO section");
103 
104  // This probably should to into specific implementations
105  // Equation specific Setups
106  switch(m_equationType)
107  {
108  case eSteadyStokes:
109  case eSteadyOseen:
110  case eSteadyNavierStokes:
111  case eSteadyLinearisedNS:
112  break;
114  case eUnsteadyStokes:
115  {
116  m_session->LoadParameter("IO_InfoSteps", m_infosteps, 0);
117  m_session->LoadParameter("IO_CFLSteps", m_cflsteps, 0);
118  m_session->LoadParameter("SteadyStateSteps", m_steadyStateSteps, 0);
119  m_session->LoadParameter("SteadyStateTol", m_steadyStateTol, 1e-6);
120 
121 
122  // check to see if any user defined boundary condition is
123  // indeed implemented
124 
125  for(int n = 0; n < m_fields[0]->GetBndConditions().num_elements(); ++n)
126  {
127  // Time Dependent Boundary Condition (if no user
128  // defined then this is empty)
129  ASSERTL0 (
130  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
132  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
134  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
136  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
138  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
140  m_fields[0]->GetBndConditions()[n]->GetUserDefined() ==
142  "Unknown USERDEFINEDTYPE boundary condition");
143  }
144  }
145  break;
146  case eNoEquationType:
147  default:
148  ASSERTL0(false,"Unknown or undefined equation type");
149  }
150 
151  m_session->LoadParameter("Kinvis", m_kinvis);
152 
153  // Default advection type per solver
154  std::string vConvectiveType;
155  switch(m_equationType)
156  {
157  case eUnsteadyStokes:
158  vConvectiveType = "NoAdvection";
159  break;
161  case eSteadyNavierStokes:
162  vConvectiveType = "Convective";
163  break;
165  vConvectiveType = "Linearised";
166  break;
167  default:
168  break;
169  }
170 
171  // Check if advection type overridden
172  if (m_session->DefinesTag("AdvectiveType") && m_equationType != eUnsteadyStokes)
173  {
174  vConvectiveType = m_session->GetTag("AdvectiveType");
175  }
176 
177  // Initialise advection
178  m_advObject = SolverUtils::GetAdvectionFactory().CreateInstance(vConvectiveType, vConvectiveType);
179  m_advObject->InitObject( m_session, m_fields);
180 
181  // Forcing terms
184 
185  // check to see if any Robin boundary conditions and if so set
186  // up m_field to boundary condition maps;
187  m_fieldsBCToElmtID = Array<OneD, Array<OneD, int> >(numfields);
188  m_fieldsBCToTraceID = Array<OneD, Array<OneD, int> >(numfields);
189  m_fieldsRadiationFactor = Array<OneD, Array<OneD, NekDouble> > (numfields);
190 
191  for (i = 0; i < m_fields.num_elements(); ++i)
192  {
193  bool Set = false;
194 
195  Array<OneD, const SpatialDomains::BoundaryConditionShPtr > BndConds;
196  Array<OneD, MultiRegions::ExpListSharedPtr> BndExp;
197  int radpts = 0;
198 
199  BndConds = m_fields[i]->GetBndConditions();
200  BndExp = m_fields[i]->GetBndCondExpansions();
201  for(int n = 0; n < BndConds.num_elements(); ++n)
202  {
203  if(BndConds[n]->GetUserDefined() == SpatialDomains::eRadiation)
204  {
205  ASSERTL0(BndConds[n]->GetBoundaryConditionType() == SpatialDomains::eRobin,
206  "Radiation boundary condition must be of type Robin <R>");
207 
208  if(Set == false)
209  {
210  m_fields[i]->GetBoundaryToElmtMap(m_fieldsBCToElmtID[i],m_fieldsBCToTraceID[i]);
211  Set = true;
212  }
213  radpts += BndExp[n]->GetTotPoints();
214  }
215  }
216 
217  m_fieldsRadiationFactor[i] = Array<OneD, NekDouble>(radpts);
218 
219  radpts = 0; // reset to use as a counter
220 
221  for(int n = 0; n < BndConds.num_elements(); ++n)
222  {
223  if(BndConds[n]->GetUserDefined() == SpatialDomains::eRadiation)
224  {
225 
226  int npoints = BndExp[n]->GetNpoints();
227  Array<OneD, NekDouble> x0(npoints,0.0);
228  Array<OneD, NekDouble> x1(npoints,0.0);
229  Array<OneD, NekDouble> x2(npoints,0.0);
230  Array<OneD, NekDouble> tmpArray;
231 
232  BndExp[n]->GetCoords(x0,x1,x2);
233 
234  LibUtilities::Equation coeff =
235  boost::static_pointer_cast<
237  >(BndConds[n])->m_robinPrimitiveCoeff;
238 
239  coeff.Evaluate(x0,x1,x2,m_time,
240  tmpArray = m_fieldsRadiationFactor[i]+ radpts);
241  //Vmath::Neg(npoints,tmpArray = m_fieldsRadiationFactor[i]+ radpts,1);
242  radpts += npoints;
243  }
244  }
245  }
246 
247  // Set up Field Meta Data for output files
248  m_fieldMetaDataMap["Kinvis"] = boost::lexical_cast<std::string>(m_kinvis);
249  m_fieldMetaDataMap["TimeStep"] = boost::lexical_cast<std::string>(m_timestep);
250 
251  }
252 
253  /**
254  * Destructor
255  */
257  {
258  }
259 
260 
261  /**
262  *
263  */
265  Array<OneD, Array<OneD, NekDouble> > &physfield,
266  Array<OneD, Array<OneD, NekDouble> > &flux)
267  {
268  ASSERTL1(flux.num_elements() == m_velocity.num_elements(),"Dimension of flux array and velocity array do not match");
269 
270  for(int j = 0; j < flux.num_elements(); ++j)
271  {
272  Vmath::Vmul(GetNpoints(), physfield[i], 1, m_fields[m_velocity[j]]->GetPhys(), 1, flux[j], 1);
273  }
274  }
275 
276  /**
277  * Calcualate numerical fluxes
278  */
279  void IncNavierStokes::v_NumericalFlux(Array<OneD, Array<OneD, NekDouble> > &physfield,
280  Array<OneD, Array<OneD, NekDouble> > &numflux)
281  {
282  /// Counter variable
283  int i;
284 
285  /// Number of trace points
286  int nTracePts = GetTraceNpoints();
287 
288  /// Number of spatial dimensions
289  int nDimensions = m_spacedim;
290 
291  /// Forward state array
292  Array<OneD, NekDouble> Fwd(2*nTracePts);
293 
294  /// Backward state array
295  Array<OneD, NekDouble> Bwd = Fwd + nTracePts;
296 
297  /// Normal velocity array
298  Array<OneD, NekDouble> Vn (nTracePts, 0.0);
299 
300  // Extract velocity field along the trace space and multiply by trace normals
301  for(i = 0; i < nDimensions; ++i)
302  {
303  m_fields[0]->ExtractTracePhys(m_fields[m_velocity[i]]->GetPhys(), Fwd);
304  Vmath::Vvtvp(nTracePts, m_traceNormals[i], 1, Fwd, 1, Vn, 1, Vn, 1);
305  }
306 
307  /// Compute the numerical fluxes at the trace points
308  for(i = 0; i < numflux.num_elements(); ++i)
309  {
310  /// Extract forwards/backwards trace spaces
311  m_fields[i]->GetFwdBwdTracePhys(physfield[i], Fwd, Bwd);
312 
313  /// Upwind between elements
314  m_fields[i]->GetTrace()->Upwind(Vn, Fwd, Bwd, numflux[i]);
315 
316  /// Calculate the numerical fluxes multipling Fwd or Bwd
317  /// by the normal advection velocity
318  Vmath::Vmul(nTracePts, numflux[i], 1, Vn, 1, numflux[i], 1);
319  }
320  }
321 
322  /**
323  * Evaluation -N(V) for all fields except pressure using m_velocity
324  */
325  void IncNavierStokes::EvaluateAdvectionTerms(const Array<OneD, const Array<OneD, NekDouble> > &inarray,
326  Array<OneD, Array<OneD, NekDouble> > &outarray,
327  Array<OneD, NekDouble> &wk)
328  {
329  int i;
330  int nqtot = m_fields[0]->GetTotPoints();
331  int VelDim = m_velocity.num_elements();
332  Array<OneD, Array<OneD, NekDouble> > velocity(VelDim);
333  Array<OneD, NekDouble > Deriv;
334 
335  for(i = 0; i < VelDim; ++i)
336  {
337  if(m_fields[i]->GetWaveSpace() && !m_SingleMode && !m_HalfMode)
338  {
339  velocity[i] = Array<OneD, NekDouble>(nqtot,0.0);
340  m_fields[i]->HomogeneousBwdTrans(inarray[m_velocity[i]],velocity[i]);
341  }
342  else
343  {
344  velocity[i] = inarray[m_velocity[i]];
345  }
346  }
347 
348  // Set up Derivative work space;
349  if(wk.num_elements())
350  {
351  ASSERTL0(wk.num_elements() >= nqtot*VelDim,
352  "Workspace is not sufficient");
353  Deriv = wk;
354  }
355  else
356  {
357  Deriv = Array<OneD, NekDouble> (nqtot*VelDim);
358  }
359 
361  velocity, inarray, outarray, m_time);
362  }
363 
364  /**
365  * Time dependent boundary conditions updating
366  */
368  {
369  int i, n;
370  std::string varName;
371  int nvariables = m_fields.num_elements();
372 
373  for (i = 0; i < nvariables; ++i)
374  {
375  for(n = 0; n < m_fields[i]->GetBndConditions().num_elements(); ++n)
376  {
377  if(m_fields[i]->GetBndConditions()[n]->GetUserDefined() ==
379  {
380  varName = m_session->GetVariable(i);
381  m_fields[i]->EvaluateBoundaryConditions(time, varName);
382  }
383 
384  }
385 
386  // Set Radiation conditions if required
388  }
389  }
390 
391  /**
392  * Probably should be pushed back into ContField?
393  */
395  {
396  int i,n;
397 
398  Array<OneD, const SpatialDomains::BoundaryConditionShPtr > BndConds;
399  Array<OneD, MultiRegions::ExpListSharedPtr> BndExp;
400 
401 
402  BndConds = m_fields[fieldid]->GetBndConditions();
403  BndExp = m_fields[fieldid]->GetBndCondExpansions();
404 
407 
408  int cnt;
409  int elmtid,nq,offset, boundary;
410  Array<OneD, NekDouble> Bvals, U;
411  int cnt1 = 0;
412 
413 
414  for(cnt = n = 0; n < BndConds.num_elements(); ++n)
415  {
416  SpatialDomains::BndUserDefinedType type = BndConds[n]->GetUserDefined();
417 
418  if((BndConds[n]->GetBoundaryConditionType() == SpatialDomains::eRobin)&&(type == SpatialDomains::eRadiation))
419  {
420  for(i = 0; i < BndExp[n]->GetExpSize(); ++i,cnt++)
421  {
422  elmtid = m_fieldsBCToElmtID[fieldid][cnt];
423  elmt = m_fields[fieldid]->GetExp(elmtid);
424  offset = m_fields[fieldid]->GetPhys_Offset(elmtid);
425 
426  U = m_fields[fieldid]->UpdatePhys() + offset;
427  Bc = BndExp[n]->GetExp(i);
428 
429  boundary = m_fieldsBCToTraceID[fieldid][cnt];
430 
431  // Get edge values and put into ubc
432  nq = Bc->GetTotPoints();
433  Array<OneD, NekDouble> ubc(nq);
434  elmt->GetTracePhysVals(boundary,Bc,U,ubc);
435 
436  Vmath::Vmul(nq,&m_fieldsRadiationFactor[fieldid][cnt1 +
437  BndExp[n]->GetPhys_Offset(i)],1,&ubc[0],1,&ubc[0],1);
438 
439  Bvals = BndExp[n]->UpdateCoeffs()+BndExp[n]->GetCoeff_Offset(i);
440 
441  Bc->IProductWRTBase(ubc,Bvals);
442  }
443  cnt1 += BndExp[n]->GetTotPoints();
444  }
445  else if(type == SpatialDomains::eNoUserDefined ||
448  type == SpatialDomains::eHigh ||
450  {
451  cnt += BndExp[n]->GetExpSize();
452  }
453  else
454  {
455  ASSERTL0(false,"Unknown USERDEFINEDTYPE in pressure boundary condition");
456  }
457  }
458  }
459 
460 
461  /**
462  * Add an additional forcing term programmatically.
463  */
465  {
466  m_forcing.push_back(pForce);
467  }
468 
469 
470  /**
471  * Decide if at a steady state if the discrerte L2 sum of the
472  * coefficients is the same as the previous step to within the
473  * tolerance m_steadyStateTol;
474  */
476  {
477  static NekDouble previousL2 = 0.0;
478  bool returnval = false;
479 
480  NekDouble L2 = 0.0;
481 
482  // calculate L2 discrete summation
483  int ncoeffs = m_fields[0]->GetNcoeffs();
484 
485  for(int i = 0; i < m_fields.num_elements(); ++i)
486  {
487  L2 += Vmath::Dot(ncoeffs,m_fields[i]->GetCoeffs(),1,m_fields[i]->GetCoeffs(),1);
488  }
489 
490  if(fabs(L2-previousL2) < ncoeffs*m_steadyStateTol)
491  {
492  returnval = true;
493  }
494 
495  previousL2 = L2;
496 
497  return returnval;
498  }
499 
500  /**
501  *
502  */
503  Array<OneD, NekDouble> IncNavierStokes::GetElmtCFLVals(void)
504  {
505  int n_vel = m_velocity.num_elements();
506  int n_element = m_fields[0]->GetExpSize();
507 
508  const Array<OneD, int> ExpOrder = GetNumExpModesPerExp();
509  Array<OneD, int> ExpOrderList (n_element, ExpOrder);
510 
511  const NekDouble cLambda = 0.2; // Spencer book pag. 317
512 
513  Array<OneD, NekDouble> cfl (n_element, 0.0);
514  Array<OneD, NekDouble> stdVelocity(n_element, 0.0);
515  Array<OneD, Array<OneD, NekDouble> > velfields;
516 
517  if(m_HomogeneousType == eHomogeneous1D) // just do check on 2D info
518  {
519  velfields = Array<OneD, Array<OneD, NekDouble> >(2);
520 
521  for(int i = 0; i < 2; ++i)
522  {
523  velfields[i] = m_fields[m_velocity[i]]->UpdatePhys();
524  }
525  }
526  else
527  {
528  velfields = Array<OneD, Array<OneD, NekDouble> >(n_vel);
529 
530  for(int i = 0; i < n_vel; ++i)
531  {
532  velfields[i] = m_fields[m_velocity[i]]->UpdatePhys();
533  }
534  }
535 
536  stdVelocity = m_extrapolation->GetMaxStdVelocity(velfields);
537 
538  for(int el = 0; el < n_element; ++el)
539  {
540  cfl[el] = m_timestep*(stdVelocity[el] * cLambda *
541  (ExpOrder[el]-1) * (ExpOrder[el]-1));
542  }
543 
544  return cfl;
545  }
546 
547  /**
548  *
549  */
551  {
552  int n_element = m_fields[0]->GetExpSize();
553 
554  Array<OneD, NekDouble> cfl = GetElmtCFLVals();
555 
556  elmtid = Vmath::Imax(n_element,cfl,1);
557  NekDouble CFL,CFL_loc;
558 
559  CFL = CFL_loc = cfl[elmtid];
560  m_comm->AllReduce(CFL,LibUtilities::ReduceMax);
561 
562  // unshuffle elmt id if data is not stored in consecutive order.
563  elmtid = m_fields[0]->GetExp(elmtid)->GetGeom()->GetGlobalID();
564  if(CFL != CFL_loc)
565  {
566  elmtid = -1;
567  }
568 
569  m_comm->AllReduce(elmtid,LibUtilities::ReduceMax);
570 
571  // express element id with respect to plane
573  {
574  elmtid = elmtid%m_fields[0]->GetPlane(0)->GetExpSize();
575  }
576  return CFL;
577  }
578 
579 
580  /**
581  * Perform the extrapolation.
582  */
584  {
585  m_extrapolation->SubStepSaveFields(step);
586  m_extrapolation->SubStepAdvance(m_intSoln,step,m_time);
587  return false;
588  }
589 
590 
591  /**
592  * Estimate CFL and perform steady-state check
593  */
595  {
596  if(m_cflsteps && !((step+1)%m_cflsteps))
597  {
598  int elmtid;
599  NekDouble cfl = GetCFLEstimate(elmtid);
600 
601  if(m_comm->GetRank() == 0)
602  {
603  cout << "CFL (zero plane): "<< cfl << " (in elmt "
604  << elmtid << ")" << endl;
605  }
606  }
607 
608  if(m_steadyStateSteps && step && (!((step+1)%m_steadyStateSteps)))
609  {
610  if(CalcSteadyState() == true)
611  {
612  cout << "Reached Steady State to tolerance "
613  << m_steadyStateTol << endl;
614  return true;
615  }
616  }
617 
618  return false;
619  }
620 } //end of namespace
621