Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 namespace Nektar
41 {
42  string PulseWavePropagation::className = GetEquationSystemFactory().RegisterCreatorFunction("PulseWavePropagation", PulseWavePropagation::create, "Pulse Wave Propagation equation.");
43  /**
44  * @class PulseWavePropagation
45  *
46  * Set up the routines based on the weak formulation from
47  * "Computational Modelling of 1D blood flow with variable
48  * mechanical properties" by S. J. Sherwin et al. The weak
49  * formulation (1) reads:
50  * \f$ \sum_{e=1}^{N_{el}} \left[ \left( \frac{\partial \mathbf{U}^{\delta} }{\partial t} ,
51  * \mathbf{\psi}^{\delta} \right)_{\Omega_e} - \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta} }
52  * {\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e} + \left[ \mathbf{\psi}^{\delta}
53  * \cdot \{ \mathbf{F}^u - \mathbf{F}(\mathbf{U}^{\delta}) \} \right]_{x_e^l}^{x_eû} \right] = 0 \f$
54  */
56  : PulseWaveSystem(pSession)
57  {
58  }
59 
61  {
63 
65  m_pressureArea->DoPressure();
66 
68  {
71  }
72  else
73  {
74  ASSERTL0(false, "Implicit Pulse Wave Propagation not set up.");
75  }
76  }
77 
79  {
80  }
81 
82 
83  /**
84  * Computes the right hand side of (1). The RHS is everything
85  * except the term that contains the time derivative
86  * \f$\frac{\partial \mathbf{U}}{\partial t}\f$. In case of a
87  * Discontinuous Galerkin projection, the routine WeakDGAdvection
88  * will be called which then calls v_GetFluxVector and
89  * v_NumericalFlux implemented in the PulseWavePropagation class.
90  *
91  */
92  void PulseWavePropagation::DoOdeRhs(const Array<OneD, const Array<OneD, NekDouble> >&inarray,
93  Array<OneD, Array<OneD, NekDouble> >&outarray,
94  const NekDouble time)
95  {
96  int i;
97 
98  Array<OneD, Array<OneD, NekDouble> > physarray(m_nVariables);
99  Array<OneD, Array<OneD, NekDouble> > modarray (m_nVariables);
100 
101  Array<OneD, NekDouble> tmpArray;
102 
103  int cnt = 0;
104 
105  // Set up Inflow and Outflow boundary conditions.
106  SetPulseWaveBoundaryConditions(inarray, outarray, time);
107 
108  // Set up any interface conditions and write into boundary condition
110 
111  // do advection evauation in all domains
112  for(int omega=0; omega < m_nDomains; ++omega)
113  {
114  m_currentDomain = omega;
115  int nq = m_vessels[omega*m_nVariables]->GetTotPoints();
116  int ncoeffs = m_vessels[omega*m_nVariables]->GetNcoeffs();
117 
118  for (i = 0; i < m_nVariables; ++i)
119  {
120  physarray[i] = inarray[i]+cnt;
121  modarray[i] = Array<OneD, NekDouble>(ncoeffs);
122  }
123 
124  for(i = 0; i < m_nVariables; ++i)
125  {
126  m_fields[i] = m_vessels[omega*m_nVariables+ i];
127  }
128 
129  WeakDGAdvection(physarray, modarray, true, true);
130 
131  for(i = 0; i < m_nVariables; ++i)
132  {
133  Vmath::Neg(ncoeffs,modarray[i],1);
134  }
135 
136  for(i = 0; i < m_nVariables; ++i)
137  {
138  m_vessels[omega*m_nVariables+i]->MultiplyByElmtInvMass(modarray[i],modarray[i]);
139  m_vessels[omega*m_nVariables+i]->BwdTrans(modarray[i],tmpArray = outarray[i]+cnt);
140  }
141  cnt += nq;
142  }
143  }
144 
145  void PulseWavePropagation::DoOdeProjection(const Array<OneD,const Array<OneD, NekDouble> >&inarray,
146  Array<OneD, Array<OneD, NekDouble> >&outarray,
147  const NekDouble time)
148  {
149  // Just copy over array
150  for(int i = 0; i < m_nVariables; ++i)
151  {
152  Vmath::Vcopy(inarray[i].num_elements(),inarray[i],1,outarray[i],1);
153  }
154  }
155 
156 
157 
158  /**
159  * Does the projection between ... space and the ... space. Also checks for Q-inflow boundary
160  * conditions at the inflow of the current arterial segment and applies the Q-inflow if specified
161  */
163  const Array<OneD,const Array<OneD, NekDouble> >&inarray,
164  Array<OneD, Array<OneD, NekDouble> >&outarray,
165  const NekDouble time)
166 
167  {
168  int omega;
169 
170  Array<OneD, MultiRegions::ExpListSharedPtr> vessel(2);
171 
172  int offset=0;
173 
174  //This will be moved to the RCR boundary condition once factory is setup
175  if (time == 0)
176  {
177  m_Boundary = Array<OneD,PulseWaveBoundarySharedPtr>(2*m_nDomains);
178 
179  for(omega = 0; omega < m_nDomains; ++omega)
180  {
181  vessel[0] = m_vessels[2*omega];
182 
183  for(int j = 0; j < 2; ++j)
184  {
185  std::string BCType = vessel[0]->GetBndConditions()[j]->
186  GetBndTypeAsString(vessel[0]->GetBndConditions()[j]->GetUserDefined());
188  }
189  }
190 
191  }
192 
193  SetBoundaryConditions(time);
194 
195  // Loop over all vessesls and set boundary conditions
196  for(omega = 0; omega < m_nDomains; ++omega)
197  {
198  for(int n = 0; n < 2; ++n)
199  {
200  m_Boundary[2*omega+n]->DoBoundary(inarray,m_A_0,m_beta,time,omega,offset,n);
201  }
202  offset += m_vessels[2*omega]->GetTotPoints();
203  }
204 
205  }
206 
207 
208  /**
209  * Calculates the second term of the weak form (1): \f$
210  * \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta}
211  * }{\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e}
212  * \f$
213  * The variables of the system are $\mathbf{U} = [A,u]^T$
214  * physfield[0] = A physfield[1] = u
215  * flux[0] = F[0] = A*u flux[1] = F[1] = u^2/2 + p/rho
216  * p-A-relationship: p = p_ext + beta*(sqrt(A)-sqrt(A_0))
217  */
218  void PulseWavePropagation::v_GetFluxVector(const int i, Array<OneD, Array<OneD, NekDouble> > &physfield,
219  Array<OneD, Array<OneD, NekDouble> > &flux)
220  {
221  int nq = m_vessels[m_currentDomain*m_nVariables]->GetTotPoints();
222  NekDouble p = 0.0;
223  NekDouble p_t = 0.0;
224 
225  switch (i)
226  {
227  case 0: // Flux for A equation
228  {
229  for (int j = 0; j < nq; j++)
230  {
231  flux[0][j] = physfield[0][j]*physfield[1][j];
232  }
233  }
234  break;
235  case 1: // Flux for u equation
236  {
237  for (int j = 0; j < nq; j++)
238  {
239  ASSERTL0(physfield[0][j]>=0,"Negative A not allowed.");
240 
241  p = m_pext + m_beta[m_currentDomain][j]*
242  (sqrt(physfield[0][j]) - sqrt(m_A_0[m_currentDomain][j]));
243 
244  p_t = (physfield[1][j]*physfield[1][j])/2 + p/m_rho;
245  flux[0][j] = p_t;
246  }
247  }
248  break;
249  default:
250  ASSERTL0(false,"GetFluxVector: illegal vector index");
251  break;
252  }
253  }
254 
255 
256  /**
257  * Calculates the third term of the weak form (1): numerical flux
258  * at boundary \f$ \left[ \mathbf{\psi}^{\delta} \cdot \{
259  * \mathbf{F}^u - \mathbf{F}(\mathbf{U}^{\delta}) \}
260  * \right]_{x_e^l}^{x_eû} \f$
261  */
262  void PulseWavePropagation::v_NumericalFlux(Array<OneD, Array<OneD, NekDouble> > &physfield,
263  Array<OneD, Array<OneD, NekDouble> > &numflux)
264  {
265  int i;
266  int nTracePts = GetTraceTotPoints();
267 
268  Array<OneD, Array<OneD, NekDouble> > Fwd(m_nVariables);
269  Array<OneD, Array<OneD, NekDouble> > Bwd(m_nVariables);
270 
271  for (i = 0; i < m_nVariables; ++i)
272  {
273  Fwd[i] = Array<OneD, NekDouble>(nTracePts);
274  Bwd[i] = Array<OneD, NekDouble>(nTracePts);
275  }
276 
277  // Get the physical values at the trace
278  for (i = 0; i < m_nVariables; ++i)
279  {
280  m_vessels[m_currentDomain*m_nVariables+ i]->
281  GetFwdBwdTracePhys(physfield[i],Fwd[i],Bwd[i]);
282  }
283 
284  // Solve the upwinding Riemann problem within one arterial
285  // segment by calling the upwinding Riemann solver implemented
286  // in this file
287  NekDouble Aflux, uflux;
288  for (i = 0; i < nTracePts; ++i)
289  {
290  switch(m_upwindTypePulse)
291  {
292  case eUpwindPulse:
293  {
294  RiemannSolverUpwind(Fwd[0][i],Fwd[1][i],Bwd[0][i],Bwd[1][i],
295  Aflux, uflux, m_A_0_trace[m_currentDomain][i],
298  }
299  break;
300  default:
301  {
302  ASSERTL0(false,"populate switch statement for upwind flux");
303  }
304  break;
305  }
306  numflux[0][i] = Aflux;
307  numflux[1][i] = uflux;
308  }
309  }
310 
311 
312  /**
313  * Riemann solver for upwinding at an interface between two
314  * elements. Uses the characteristic variables for calculating
315  * the upwinded state \f$(A_u,u_u)\f$ from the left
316  * \f$(A_L,u_L)\f$ and right state \f$(A_R,u_R)\f$. Returns the
317  * upwinded flux $\mathbf{F}^u$ needed for the weak formulation
318  * (1). Details can be found in "Pulse wave propagation in the
319  * human vascular system", section 3.3
320  *
321  */
323  NekDouble AR,NekDouble uR,
324  NekDouble &Aflux,
325  NekDouble &uflux,
326  NekDouble A_0,
327  NekDouble beta,
328  NekDouble n)
329  {
330  Array<OneD, NekDouble> W(2);
331  Array<OneD, NekDouble> upwindedphysfield(2);
332  NekDouble cL = 0.0;
333  NekDouble cR = 0.0;
334  NekDouble rho = m_rho;
335  NekDouble pext = m_pext;
336  NekDouble p = 0.0;
337  NekDouble p_t = 0.0;
338 
339  // Compute the wave speeds. The use of the normal here allows
340  // for the definition of the characteristics to be inverted
341  // (and hence the left and right state) if n is in the -ve
342  // x-direction. This means we end up with the positive
343  // defintion of the flux which has to therefore be multiplied
344  // by the normal at the end of the methods This is a bit of a
345  // mind twister but is efficient from a coding perspective.
346  cL = sqrt(beta*sqrt(AL)/(2*rho))*n;
347  cR = sqrt(beta*sqrt(AR)/(2*rho))*n;
348 
349  ASSERTL1(fabs(cL+cR) > fabs(uL+uR),"Conditions are not sub-sonic");
350 
351  // If upwinding from left and right for subsonic domain
352  // then know characteristics immediately
353  W[0] = uL + 4*cL;
354  W[1] = uR - 4*cR;
355 
356  // Calculate conservative variables from characteristics
357  NekDouble w0mw1 = 0.25*(W[0]-W[1]);
358  NekDouble fac = rho/(2*beta);
359  w0mw1 *= w0mw1; // squared
360  w0mw1 *= w0mw1; // fourth power
361  fac *= fac; // squared
362  upwindedphysfield[0]= w0mw1*fac;
363  upwindedphysfield[1]= 0.5*(W[0] + W[1]);
364 
365  // Compute the fluxes multipled by the normal.
366  Aflux = upwindedphysfield[0] * upwindedphysfield[1]*n;
367  p = pext + beta*(sqrt(upwindedphysfield[0]) - sqrt(A_0));
368  p_t = 0.5*(upwindedphysfield[1]*upwindedphysfield[1]) + p/rho;
369  uflux = p_t*n;
370  }
371 
372  /**
373  * Print summary routine, calls virtual routine reimplemented in
374  * UnsteadySystem
375  */
377  {
379  }
380 }