Nektar++
PulseWavePropagation.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: PulseWavePropagation.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: Pulse Wave Propagation solve routines based on the weak
33 // formulation (1):
34 //
35 ///////////////////////////////////////////////////////////////////////////////
36 
37 #include <iostream>
38 
41 
42 using namespace std;
43 
44 namespace Nektar
45 {
46 
47 string PulseWavePropagation::className =
49  "PulseWavePropagation", PulseWavePropagation::create,
50  "Pulse Wave Propagation equation.");
51 /**
52  * @class PulseWavePropagation
53  *
54  * Set up the routines based on the weak formulation from
55  * "Computational Modelling of 1D blood flow with variable
56  * mechanical properties" by S. J. Sherwin et al. The weak
57  * formulation (1) reads:
58  * \f$ \sum_{e=1}^{N_{el}} \left[ \left( \frac{\partial \mathbf{U}^{\delta}
59  * }{\partial t} , \mathbf{\psi}^{\delta} \right)_{\Omega_e} - \left(
60  * \frac{\partial \mathbf{F(\mathbf{U})}^{\delta} }
61  * {\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e} + \left[
62  * \mathbf{\psi}^{\delta} \cdot \{ \mathbf{F}^u -
63  * \mathbf{F}(\mathbf{U}^{\delta}) \} \right]_{x_e^l}^{x_eû} \right] = 0 \f$
64  */
65 PulseWavePropagation::PulseWavePropagation(
68  : PulseWaveSystem(pSession, pGraph)
69 {
70 }
71 
72 void PulseWavePropagation::v_InitObject(bool DeclareField)
73 {
74  boost::ignore_unused(DeclareField);
75 
76  // Will set up an array of vessels/fields in PulseWaveSystem::v_InitObject
77  // so set DeclareField to false so that the fields are not set up in
78  // EquationSystem unnecessarily. Note the number of fields in Equation
79  // system is related to the number of variables. The number of vessels is
80  // therefore held in PulwWaveSystem.
82 
83  if (m_session->DefinesSolverInfo("PressureArea"))
84  {
86  m_session->GetSolverInfo("PressureArea"), m_vessels, m_session);
87  }
88  else
89  {
91  "Beta", m_vessels, m_session);
92  }
93 
95  {
98  }
99  else
100  {
101  ASSERTL0(false, "Implicit Pulse Wave Propagation not set up.");
102  }
103 
104  // Create advection object
105  string advName;
106  string riemName;
107  switch (m_upwindTypePulse)
108  {
109  case eUpwindPulse:
110  {
111  advName = "WeakDG";
112  riemName = "UpwindPulse";
113  }
114  break;
115  default:
116  {
117  ASSERTL0(false, "populate switch statement for upwind flux");
118  }
119  break;
120  }
121  m_advObject =
123  m_advObject->SetFluxVector(&PulseWavePropagation::GetFluxVector, this);
125  riemName, m_session);
126  m_riemannSolver->SetScalar("A0", &PulseWavePropagation::GetA0, this);
127  m_riemannSolver->SetScalar("beta", &PulseWavePropagation::GetBeta, this);
128  m_riemannSolver->SetScalar("alpha", &PulseWavePropagation::GetAlpha, this);
129  m_riemannSolver->SetScalar("N", &PulseWavePropagation::GetN, this);
130  m_riemannSolver->SetParam("rho", &PulseWavePropagation::GetRho, this);
132  this);
133 
134  m_advObject->SetRiemannSolver(m_riemannSolver);
135  m_advObject->InitObject(m_session, m_fields);
136 }
137 
139 {
140 }
141 
142 /**
143  * Computes the right hand side of (1). The RHS is everything
144  * except the term that contains the time derivative
145  * \f$\frac{\partial \mathbf{U}}{\partial t}\f$. In case of a
146  * Discontinuous Galerkin projection, m_advObject->Advect
147  * will be called
148  *
149  */
151  const Array<OneD, const Array<OneD, NekDouble>> &inarray,
152  Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
153 {
154  size_t i;
155 
157 
158  // Dummy array for WeakDG advection
160 
161  // Output array for advection
163 
164  size_t cnt = 0;
165 
166  // Set up Inflow and Outflow boundary conditions.
167  SetPulseWaveBoundaryConditions(inarray, outarray, time);
168 
169  // Set up any interface conditions and write into boundary condition
171 
172  // do advection evaluation in all domains
173  for (size_t omega = 0; omega < m_nDomains; ++omega)
174  {
175  LibUtilities::Timer timer;
176  m_currentDomain = omega;
177  size_t nq = m_vessels[omega * m_nVariables]->GetTotPoints();
178 
179  timer.Start();
180  for (i = 0; i < m_nVariables; ++i)
181  {
182  physarray[i] = inarray[i] + cnt;
183  out[i] = outarray[i] + cnt;
184  }
185 
186  for (i = 0; i < m_nVariables; ++i)
187  {
188  m_fields[i] = m_vessels[omega * m_nVariables + i];
189  }
190 
191  m_advObject->Advect(m_nVariables, m_fields, advVel, physarray, out,
192  time);
193  for (i = 0; i < m_nVariables; ++i)
194  {
195  Vmath::Neg(nq, out[i], 1);
196  }
197  timer.Stop();
198  timer.AccumulateRegion("PulseWavePropagation:_DoOdeRHS", 1);
199  cnt += nq;
200  }
201 }
202 
204  const Array<OneD, const Array<OneD, NekDouble>> &inarray,
205  Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
206 {
207  boost::ignore_unused(time);
208 
209  // Just copy over array
210  for (size_t i = 0; i < m_nVariables; ++i)
211  {
212  Vmath::Vcopy(inarray[i].size(), inarray[i], 1, outarray[i], 1);
213  }
214 }
215 
216 /**
217  * Does the projection between ... space and the ... space. Also checks for
218  *Q-inflow boundary conditions at the inflow of the current arterial segment and
219  *applies the Q-inflow if specified
220  */
222  const Array<OneD, const Array<OneD, NekDouble>> &inarray,
223  Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
224 
225 {
226  boost::ignore_unused(outarray);
227 
228  size_t omega;
229 
231 
232  size_t offset = 0;
233 
234  // This will be moved to the RCR boundary condition once factory is setup
235  if (time == 0)
236  {
238 
239  for (omega = 0; omega < m_nDomains; ++omega)
240  {
241  vessel[0] = m_vessels[2 * omega];
242  vessel[1] = m_vessels[2 * omega + 1];
243 
244  for (size_t j = 0; j < 2; ++j)
245  {
246  std::string BCType;
247 
248  if (j < vessel[0]->GetBndConditions().size())
249  {
250  BCType = vessel[0]->GetBndConditions()[j]->GetUserDefined();
251  }
252 
253  // if no condition given define it to be NoUserDefined
254  if (BCType.empty() || BCType == "Interface")
255  {
256  BCType = "NoUserDefined";
257  }
258 
259  m_Boundary[2 * omega + j] = GetBoundaryFactory().CreateInstance(
261 
262  // turn on time dependent BCs
263  if (BCType == "Q-inflow")
264  {
265  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
266  }
267  else if (BCType == "A-inflow")
268  {
269  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
270  }
271  else if (BCType == "U-inflow")
272  {
273  vessel[1]->GetBndConditions()[j]->SetIsTimeDependent(true);
274  }
275  else if (BCType == "RCR-terminal")
276  {
277  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
278  }
279  }
280  }
281  }
282 
283  SetBoundaryConditions(time);
284 
285  // Loop over all vessels and set boundary conditions
286  LibUtilities::Timer timer;
287  for (omega = 0; omega < m_nDomains; ++omega)
288  {
289  timer.Start();
290  for (size_t n = 0; n < 2; ++n)
291  {
292  m_Boundary[2 * omega + n]->DoBoundary(
293  inarray, m_A_0, m_beta, m_alpha, time, omega, offset, n);
294  }
295 
296  offset += m_vessels[2 * omega]->GetTotPoints();
297  timer.Stop();
298  timer.AccumulateRegion("PulseWavePropagation:_SetBCs", 1);
299  }
300 }
301 
302 /**
303  * Calculates the second term of the weak form (1): \f$
304  * \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta}
305  * }{\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e}
306  * \f$
307  * The variables of the system are $\mathbf{U} = [A,u]^T$
308  * physfield[0] = A physfield[1] = u
309  * flux[0] = F[0] = A*u flux[1] = F[1] = u^2/2 + p/rho
310  */
312  const Array<OneD, Array<OneD, NekDouble>> &physfield,
314 {
315  size_t nq = m_vessels[m_currentDomain * m_nVariables]->GetTotPoints();
316  NekDouble domain = m_currentDomain;
317  m_pressure[domain] = Array<OneD, NekDouble>(nq);
318  Array<OneD, NekDouble> dAUdx(nq);
319  NekDouble viscoelasticGradient = 0.0;
320 
321  LibUtilities::Timer timer;
322 
323  for (size_t j = 0; j < nq; ++j)
324  {
325  timer.Start();
326  flux[0][0][j] = physfield[0][j] * physfield[1][j];
327  timer.Stop();
328  timer.AccumulateRegion("PulseWavePropagation:GetFluxVector-flux", 3);
329  }
330 
331  // d/dx of AU, for the viscoelastic tube law and extra fields
332  m_fields[0]->PhysDeriv(flux[0][0], dAUdx);
333 
334  for (size_t j = 0; j < nq; ++j)
335  {
336  if ((j == 0) || (j == nq - 1))
337  {
338  viscoelasticGradient = dAUdx[j];
339  }
340  else
341  {
342  viscoelasticGradient = (dAUdx[j] + dAUdx[j + 1]) / 2;
343  }
344 
345  m_pressureArea->GetPressure(m_pressure[domain][j], m_beta[domain][j],
346  physfield[0][j], m_A_0[domain][j],
347  viscoelasticGradient, m_gamma[domain][j],
348  m_alpha[domain][j]);
349 
350  flux[1][0][j] = physfield[1][j] * physfield[1][j] / 2 +
351  m_pressure[domain][j] / m_rho;
352  }
353 
354  m_session->MatchSolverInfo("OutputExtraFields", "True", extraFields, true);
355 
356  if (extraFields)
357  {
358  /*
359  Calculates wave speed and characteristic variables.
360 
361  Ideally this should be moved to PulseWaveSystem, but it's easiest to
362  implement here.
363  */
364  size_t counter = 0;
365 
366  m_PWV[domain] = Array<OneD, NekDouble>(nq);
367  m_W1[domain] = Array<OneD, NekDouble>(nq);
368  m_W2[domain] = Array<OneD, NekDouble>(nq);
369 
370  for (size_t j = 0; j < nq; ++j)
371  {
372  m_pressureArea->GetC(m_PWV[domain][j], m_beta[domain][j],
373  physfield[0][counter + j], m_A_0[domain][j],
374  m_alpha[domain][j]);
375  m_pressureArea->GetW1(m_W1[domain][j], physfield[1][counter + j],
376  m_beta[domain][j], physfield[0][counter + j],
377  m_A_0[domain][j], m_alpha[domain][j]);
378  m_pressureArea->GetW2(m_W2[domain][j], physfield[1][counter + j],
379  m_beta[domain][j], physfield[0][counter + j],
380  m_A_0[domain][j], m_alpha[domain][j]);
381  }
382 
383  counter += nq;
384  }
385 }
386 
388 {
390 }
391 
393 {
395 }
396 
398 {
400 }
401 
403 {
405 }
406 
408 {
409  return m_rho;
410 }
411 
413 {
414  return m_nDomains;
415 }
416 
417 /**
418  * Print summary routine, calls virtual routine reimplemented in
419  * UnsteadySystem
420  */
422 {
424 }
425 
426 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:198
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
void DefineProjection(FuncPointerT func, ObjectPointerT obj)
void DefineOdeRhs(FuncPointerT func, ObjectPointerT obj)
void AccumulateRegion(std::string, int iolevel=0)
Accumulate elapsed time for a region.
Definition: Timer.cpp:73
Array< OneD, NekDouble > & GetAlpha()
virtual void v_InitObject(bool DeclareField=false) override
SolverUtils::RiemannSolverSharedPtr m_riemannSolver
SolverUtils::AdvectionSharedPtr m_advObject
void GetFluxVector(const Array< OneD, Array< OneD, NekDouble >> &physfield, Array< OneD, Array< OneD, Array< OneD, NekDouble >>> &flux)
DG Pulse Wave Propagation routines:
Array< OneD, PulseWaveBoundarySharedPtr > m_Boundary
void SetPulseWaveBoundaryConditions(const Array< OneD, const Array< OneD, NekDouble >> &inarray, Array< OneD, Array< OneD, NekDouble >> &outarray, const NekDouble time)
Array< OneD, NekDouble > & GetN()
Array< OneD, NekDouble > & GetA0()
virtual void v_GenerateSummary(SolverUtils::SummaryList &s) override
void DoOdeProjection(const Array< OneD, const Array< OneD, NekDouble >> &inarray, Array< OneD, Array< OneD, NekDouble >> &outarray, const NekDouble time)
Array< OneD, NekDouble > & GetBeta()
void DoOdeRhs(const Array< OneD, const Array< OneD, NekDouble >> &inarray, Array< OneD, Array< OneD, NekDouble >> &outarray, const NekDouble time)
Base class for unsteady solvers.
Array< OneD, Array< OneD, NekDouble > > m_A_0
Array< OneD, Array< OneD, NekDouble > > m_beta_trace
void EnforceInterfaceConditions(const Array< OneD, const Array< OneD, NekDouble >> &fields)
Array< OneD, Array< OneD, NekDouble > > m_W2
Array< OneD, Array< OneD, NekDouble > > m_trace_fwd_normal
PulseWavePressureAreaSharedPtr m_pressureArea
Array< OneD, Array< OneD, NekDouble > > m_W1
virtual void v_InitObject(bool DeclareField=false) override
Array< OneD, Array< OneD, NekDouble > > m_alpha_trace
UpwindTypePulse m_upwindTypePulse
Array< OneD, Array< OneD, NekDouble > > m_A_0_trace
Array< OneD, Array< OneD, NekDouble > > m_pressure
Array< OneD, Array< OneD, NekDouble > > m_gamma
Array< OneD, Array< OneD, NekDouble > > m_alpha
Array< OneD, Array< OneD, NekDouble > > m_PWV
Array< OneD, MultiRegions::ExpListSharedPtr > m_vessels
Array< OneD, Array< OneD, NekDouble > > m_beta
int m_spacedim
Spatial dimension (>= expansion dim).
Array< OneD, MultiRegions::ExpListSharedPtr > m_fields
Array holding all dependent variables.
LibUtilities::SessionReaderSharedPtr m_session
The session reader.
SOLVER_UTILS_EXPORT void SetBoundaryConditions(NekDouble time)
Evaluates the boundary conditions at the given time.
LibUtilities::TimeIntegrationSchemeOperators m_ode
The time integration scheme operators to use.
bool m_explicitAdvection
Indicates if explicit or implicit treatment of advection is used.
virtual SOLVER_UTILS_EXPORT void v_GenerateSummary(SummaryList &s) override
Print a summary of time stepping parameters.
std::shared_ptr< SessionReader > SessionReaderSharedPtr
AdvectionFactory & GetAdvectionFactory()
Gets the factory for initialising advection objects.
Definition: Advection.cpp:47
std::vector< std::pair< std::string, std::string > > SummaryList
Definition: Misc.h:48
EquationSystemFactory & GetEquationSystemFactory()
RiemannSolverFactory & GetRiemannSolverFactory()
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:172
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
PressureAreaFactory & GetPressureAreaFactory()
@ eUpwindPulse
simple upwinding scheme
BoundaryFactory & GetBoundaryFactory()
double NekDouble
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.cpp:518
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255