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 formulation (1):
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 #include <iostream>
37 
39 
40 using namespace std;
41 
42 namespace Nektar
43 {
44 
45 string PulseWavePropagation::className =
46  GetEquationSystemFactory().RegisterCreatorFunction("PulseWavePropagation",
47  PulseWavePropagation::create, "Pulse Wave Propagation equation.");
48 /**
49  * @class PulseWavePropagation
50  *
51  * Set up the routines based on the weak formulation from
52  * "Computational Modelling of 1D blood flow with variable
53  * mechanical properties" by S. J. Sherwin et al. The weak
54  * formulation (1) reads:
55  * \f$ \sum_{e=1}^{N_{el}} \left[ \left( \frac{\partial \mathbf{U}^{\delta} }{\partial t} ,
56  * \mathbf{\psi}^{\delta} \right)_{\Omega_e} - \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta} }
57  * {\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e} + \left[ \mathbf{\psi}^{\delta}
58  * \cdot \{ \mathbf{F}^u - \mathbf{F}(\mathbf{U}^{\delta}) \} \right]_{x_e^l}^{x_eû} \right] = 0 \f$
59  */
60 PulseWavePropagation::PulseWavePropagation(
63  : PulseWaveSystem(pSession, pGraph)
64 {
65 }
66 
68 {
70 
71  if (m_session->DefinesSolverInfo("PressureArea"))
72  {
74  m_session->GetSolverInfo("PressureArea"), m_vessels, m_session);
75  }
76  else
77  {
80  }
81 
83  {
86  }
87  else
88  {
89  ASSERTL0(false, "Implicit Pulse Wave Propagation not set up.");
90  }
91 
92  // Create advection object
93  string advName;
94  string riemName;
95  switch(m_upwindTypePulse)
96  {
97  case eUpwindPulse:
98  {
99  advName = "WeakDG";
100  riemName = "UpwindPulse";
101  }
102  break;
103  default:
104  {
105  ASSERTL0(false, "populate switch statement for upwind flux");
106  }
107  break;
108  }
110  GetAdvectionFactory().CreateInstance(advName, advName);
111  m_advObject->SetFluxVector(
115  m_riemannSolver->SetScalar(
116  "A0", &PulseWavePropagation::GetA0, this);
117  m_riemannSolver->SetScalar(
118  "beta", &PulseWavePropagation::GetBeta, this);
119  m_riemannSolver->SetScalar(
120  "alpha", &PulseWavePropagation::GetAlpha, this);
121  m_riemannSolver->SetScalar(
122  "N", &PulseWavePropagation::GetN, this);
123  m_riemannSolver->SetParam(
124  "rho", &PulseWavePropagation::GetRho, this);
125  m_riemannSolver->SetParam(
126  "domains", &PulseWavePropagation::GetDomains, this);
127 
128  m_advObject->SetRiemannSolver(m_riemannSolver);
129  m_advObject->InitObject(m_session, m_fields);
130 }
131 
133 {
134 }
135 
136 /**
137  * Computes the right hand side of (1). The RHS is everything
138  * except the term that contains the time derivative
139  * \f$\frac{\partial \mathbf{U}}{\partial t}\f$. In case of a
140  * Discontinuous Galerkin projection, m_advObject->Advect
141  * will be called
142  *
143  */
145  const Array<OneD, NekDouble> >&inarray,
146  Array<OneD, Array<OneD, NekDouble> >&outarray,
147  const NekDouble time)
148 {
149  int i;
150 
152 
153  // Dummy array for WeakDG advection
155 
156  // Output array for advection
158 
159  int cnt = 0;
160 
161  // Set up Inflow and Outflow boundary conditions.
162  SetPulseWaveBoundaryConditions(inarray, outarray, time);
163 
164  // Set up any interface conditions and write into boundary condition
166 
167  // do advection evaluation in all domains
168  for (int omega = 0; omega < m_nDomains; ++omega)
169  {
170  m_currentDomain = omega;
171  int nq = m_vessels[omega * m_nVariables]->GetTotPoints();
172 
173  for (i = 0; i < m_nVariables; ++i)
174  {
175  physarray[i] = inarray[i] + cnt;
176  out[i] = outarray[i] + cnt;
177  }
178 
179  for (i = 0; i < m_nVariables; ++i)
180  {
181  m_fields[i] = m_vessels[omega * m_nVariables + i];
182  }
183 
184  m_advObject->Advect(m_nVariables, m_fields, advVel, physarray,
185  out, time);
186  for (i = 0; i < m_nVariables; ++i)
187  {
188  Vmath::Neg(nq, out[i], 1);
189  }
190 
191  cnt += nq;
192  }
193 }
194 
196  const Array<OneD, NekDouble> >&inarray,
197  Array<OneD, Array<OneD, NekDouble> >&outarray,
198  const NekDouble time)
199 {
200  // Just copy over array
201  for (int i = 0; i < m_nVariables; ++i)
202  {
203  Vmath::Vcopy(inarray[i].size(), inarray[i], 1, outarray[i], 1);
204  }
205 }
206 
207 
208 
209 /**
210  * Does the projection between ... space and the ... space. Also checks for Q-inflow boundary
211  * conditions at the inflow of the current arterial segment and applies the Q-inflow if specified
212  */
214  const Array<OneD,const Array<OneD, NekDouble> >&inarray,
215  Array<OneD, Array<OneD, NekDouble> >&outarray,
216  const NekDouble time)
217 
218 {
219  int omega;
220 
222 
223  int offset = 0;
224 
225 // This will be moved to the RCR boundary condition once factory is setup
226  if (time == 0)
227  {
229 
230  for (omega = 0; omega < m_nDomains; ++omega)
231  {
232  vessel[0] = m_vessels[2 * omega];
233  vessel[1] = m_vessels[2 * omega + 1];
234 
235  for (int j = 0; j < 2; ++j)
236  {
237  std::string BCType = vessel[0]->GetBndConditions()[j]->GetUserDefined();
238  if (BCType.empty()) // if not condition given define it to be NoUserDefined
239  {
240  BCType = "NoUserDefined";
241  }
242 
243  m_Boundary[2 * omega + j] =
246 
247  // turn on time dependent BCs
248  if (BCType == "Q-inflow")
249  {
250  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
251  }
252  else if (BCType == "A-inflow")
253  {
254  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
255  }
256  else if (BCType == "U-inflow")
257  {
258  vessel[1]->GetBndConditions()[j]->SetIsTimeDependent(true);
259  }
260  else if (BCType == "RCR-terminal")
261  {
262  vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
263  }
264  }
265  }
266 
267  }
268 
269  SetBoundaryConditions(time);
270 
271  // Loop over all vessels and set boundary conditions
272  for (omega = 0; omega < m_nDomains; ++omega)
273  {
274  for (int n = 0; n < 2; ++n)
275  {
276  m_Boundary[2 * omega + n]->DoBoundary(inarray, m_A_0, m_beta, m_alpha,
277  time, omega, offset, n);
278  }
279 
280  offset += m_vessels[2 * omega]->GetTotPoints();
281  }
282 }
283 
284 /**
285 * Calculates the second term of the weak form (1): \f$
286 * \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta}
287 * }{\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e}
288 * \f$
289 * The variables of the system are $\mathbf{U} = [A,u]^T$
290 * physfield[0] = A physfield[1] = u
291 * flux[0] = F[0] = A*u flux[1] = F[1] = u^2/2 + p/rho
292 */
294  const Array<OneD, Array<OneD, NekDouble> > &physfield,
296 {
297  int nq = m_vessels[m_currentDomain * m_nVariables]->GetTotPoints();
298  NekDouble domain = m_currentDomain;
299  m_pressure[domain] = Array<OneD, NekDouble>(nq);
300  Array<OneD, NekDouble> dAUdx(nq);
301  NekDouble viscoelasticGradient = 0.0;
302 
303  for (int j = 0; j < nq; ++j)
304  {
305  flux[0][0][j] = physfield[0][j] * physfield[1][j];
306  }
307 
308  // d/dx of AU, for the viscoelastic tube law and extra fields
309  m_fields[0]->PhysDeriv(flux[0][0], dAUdx);
310 
311  for (int j = 0; j < nq; ++j)
312  {
313  if ((j == 0) || (j == nq - 1))
314  {
315  viscoelasticGradient = dAUdx[j];
316  }
317  else
318  {
319  viscoelasticGradient = (dAUdx[j] + dAUdx[j + 1]) / 2;
320  }
321 
322  m_pressureArea->GetPressure(m_pressure[domain][j],
323  m_beta[domain][j], physfield[0][j], m_A_0[domain][j],
324  viscoelasticGradient, m_gamma[domain][j], m_alpha[domain][j]);
325 
326  flux[1][0][j] = physfield[1][j] * physfield[1][j] / 2 +
327  m_pressure[domain][j] / m_rho;
328  }
329 
330  m_session->MatchSolverInfo("OutputExtraFields", "True", extraFields, true);
331 
332  if (extraFields)
333  {
334  /*
335  Calculates wave speed and characteristic variables.
336 
337  Ideally this should be moved to PulseWaveSystem, but it's easiest to
338  implement here.
339  */
340  int counter = 0;
341 
342  m_PWV[domain] = Array<OneD, NekDouble>(nq);
343  m_W1[domain] = Array<OneD, NekDouble>(nq);
344  m_W2[domain] = Array<OneD, NekDouble>(nq);
345 
346  for (int j = 0; j < nq; ++j)
347  {
348  m_pressureArea->GetC(m_PWV[domain][j], m_beta[domain][j],
349  physfield[0][counter + j], m_A_0[domain][j], m_alpha[domain][j]);
350  m_pressureArea->GetW1(m_W1[domain][j], physfield[1][counter + j],
351  m_beta[domain][j], physfield[0][counter + j], m_A_0[domain][j],
352  m_alpha[domain][j]);
353  m_pressureArea->GetW2(m_W2[domain][j], physfield[1][counter + j],
354  m_beta[domain][j], physfield[0][counter + j], m_A_0[domain][j],
355  m_alpha[domain][j]);
356  }
357 
358  counter += nq;
359  }
360 }
361 
363 {
365 }
366 
368 {
370 }
371 
373 {
375 }
376 
378 {
380 }
381 
383 {
384  return m_rho;
385 }
386 
388 {
389  return m_nDomains;
390 }
391 
392 /**
393  * Print summary routine, calls virtual routine reimplemented in
394  * UnsteadySystem
395  */
397 {
399 }
400 
401 } // Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:200
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:145
void DefineProjection(FuncPointerT func, ObjectPointerT obj)
void DefineOdeRhs(FuncPointerT func, ObjectPointerT obj)
Array< OneD, NekDouble > & GetAlpha()
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()
virtual void v_GenerateSummary(SolverUtils::SummaryList &s)
Array< OneD, NekDouble > & GetA0()
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
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
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
void EnforceInterfaceConditions(const Array< OneD, const Array< OneD, NekDouble > > &fields)
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)
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:46
EquationSystemFactory & GetEquationSystemFactory()
RiemannSolverFactory & GetRiemannSolverFactory()
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:174
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
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:461
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1199