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
42using namespace std;
43
44namespace Nektar
45{
46
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 */
68 : PulseWaveSystem(pSession, pGraph)
69{
70}
71
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 }
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 {
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 if (inarray != outarray)
211 {
212 for (size_t i = 0; i < m_nVariables; ++i)
213 {
214 Vmath::Vcopy(inarray[i].size(), inarray[i], 1, outarray[i], 1);
215 }
216 }
217}
218
219/**
220 * Does the projection between ... space and the ... space. Also checks for
221 *Q-inflow boundary conditions at the inflow of the current arterial segment and
222 *applies the Q-inflow if specified
223 */
225 const Array<OneD, const Array<OneD, NekDouble>> &inarray,
226 Array<OneD, Array<OneD, NekDouble>> &outarray, const NekDouble time)
227
228{
229 boost::ignore_unused(outarray);
230
231 size_t omega;
232
234
235 size_t offset = 0;
236
237 // This will be moved to the RCR boundary condition once factory is setup
238 if (time == 0)
239 {
241
242 for (omega = 0; omega < m_nDomains; ++omega)
243 {
244 vessel[0] = m_vessels[2 * omega];
245 vessel[1] = m_vessels[2 * omega + 1];
246
247 for (size_t j = 0; j < 2; ++j)
248 {
249 std::string BCType;
250
251 if (j < vessel[0]->GetBndConditions().size())
252 {
253 BCType = vessel[0]->GetBndConditions()[j]->GetUserDefined();
254 }
255
256 // if no condition given define it to be NoUserDefined
257 if (BCType.empty() || BCType == "Interface")
258 {
259 BCType = "NoUserDefined";
260 }
261
264
265 // turn on time dependent BCs
266 if (BCType == "Q-inflow")
267 {
268 vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
269 }
270 else if (BCType == "A-inflow")
271 {
272 vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
273 }
274 else if (BCType == "U-inflow")
275 {
276 vessel[1]->GetBndConditions()[j]->SetIsTimeDependent(true);
277 }
278 else if (BCType == "RCR-terminal")
279 {
280 vessel[0]->GetBndConditions()[j]->SetIsTimeDependent(true);
281 }
282 }
283 }
284 }
285
287
288 // Loop over all vessels and set boundary conditions
290 for (omega = 0; omega < m_nDomains; ++omega)
291 {
292 timer.Start();
293 for (size_t n = 0; n < 2; ++n)
294 {
295 m_Boundary[2 * omega + n]->DoBoundary(
296 inarray, m_A_0, m_beta, m_alpha, time, omega, offset, n);
297 }
298
299 offset += m_vessels[2 * omega]->GetTotPoints();
300 timer.Stop();
301 timer.AccumulateRegion("PulseWavePropagation:_SetBCs", 1);
302 }
303}
304
305/**
306 * Calculates the second term of the weak form (1): \f$
307 * \left( \frac{\partial \mathbf{F(\mathbf{U})}^{\delta}
308 * }{\partial x}, \mathbf{\psi}^{\delta} \right)_{\Omega_e}
309 * \f$
310 * The variables of the system are $\mathbf{U} = [A,u]^T$
311 * physfield[0] = A physfield[1] = u
312 * flux[0] = F[0] = A*u flux[1] = F[1] = u^2/2 + p/rho
313 */
315 const Array<OneD, Array<OneD, NekDouble>> &physfield,
317{
318 size_t nq = m_vessels[m_currentDomain * m_nVariables]->GetTotPoints();
319 NekDouble domain = m_currentDomain;
321 Array<OneD, NekDouble> dAUdx(nq);
322 NekDouble viscoelasticGradient = 0.0;
323
325
326 for (size_t j = 0; j < nq; ++j)
327 {
328 timer.Start();
329 flux[0][0][j] = physfield[0][j] * physfield[1][j];
330 timer.Stop();
331 timer.AccumulateRegion("PulseWavePropagation:GetFluxVector-flux", 3);
332 }
333
334 // d/dx of AU, for the viscoelastic tube law and extra fields
335 m_fields[0]->PhysDeriv(flux[0][0], dAUdx);
336
337 for (size_t j = 0; j < nq; ++j)
338 {
339 if ((j == 0) || (j == nq - 1))
340 {
341 viscoelasticGradient = dAUdx[j];
342 }
343 else
344 {
345 viscoelasticGradient = (dAUdx[j] + dAUdx[j + 1]) / 2;
346 }
347
348 m_pressureArea->GetPressure(m_pressure[domain][j], m_beta[domain][j],
349 physfield[0][j], m_A_0[domain][j],
350 viscoelasticGradient, m_gamma[domain][j],
351 m_alpha[domain][j]);
352
353 flux[1][0][j] = physfield[1][j] * physfield[1][j] / 2 +
354 m_pressure[domain][j] / m_rho;
355 }
356
357 m_session->MatchSolverInfo("OutputExtraFields", "True", extraFields, true);
358
359 if (extraFields)
360 {
361 /*
362 Calculates wave speed and characteristic variables.
363
364 Ideally this should be moved to PulseWaveSystem, but it's easiest to
365 implement here.
366 */
367 size_t counter = 0;
368
369 m_PWV[domain] = Array<OneD, NekDouble>(nq);
370 m_W1[domain] = Array<OneD, NekDouble>(nq);
371 m_W2[domain] = Array<OneD, NekDouble>(nq);
372
373 for (size_t j = 0; j < nq; ++j)
374 {
375 m_pressureArea->GetC(m_PWV[domain][j], m_beta[domain][j],
376 physfield[0][counter + j], m_A_0[domain][j],
377 m_alpha[domain][j]);
378 m_pressureArea->GetW1(m_W1[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 m_pressureArea->GetW2(m_W2[domain][j], physfield[1][counter + j],
382 m_beta[domain][j], physfield[0][counter + j],
383 m_A_0[domain][j], m_alpha[domain][j]);
384 }
385
386 counter += nq;
387 }
388}
389
391{
393}
394
396{
398}
399
401{
403}
404
406{
408}
409
411{
412 return m_rho;
413}
414
416{
417 return m_nDomains;
418}
419
420/**
421 * Print summary routine, calls virtual routine reimplemented in
422 * UnsteadySystem
423 */
425{
427}
428
429} // 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:72
Array< OneD, NekDouble > & GetAlpha()
virtual void v_InitObject(bool DeclareField=false) override
SolverUtils::RiemannSolverSharedPtr m_riemannSolver
PulseWavePropagation(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
SolverUtils::AdvectionSharedPtr m_advObject
void SetPulseWaveBoundaryConditions(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
void DoOdeProjection(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
Array< OneD, PulseWaveBoundarySharedPtr > m_Boundary
Array< OneD, NekDouble > & GetN()
Array< OneD, NekDouble > & GetA0()
void GetFluxVector(const Array< OneD, Array< OneD, NekDouble > > &physfield, Array< OneD, Array< OneD, Array< OneD, NekDouble > > > &flux)
DG Pulse Wave Propagation routines:
virtual void v_GenerateSummary(SolverUtils::SummaryList &s) override
void DoOdeRhs(const Array< OneD, const Array< OneD, NekDouble > > &inarray, Array< OneD, Array< OneD, NekDouble > > &outarray, const NekDouble time)
Array< OneD, NekDouble > & GetBeta()
static std::string className
Name of class.
static EquationSystemSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const SpatialDomains::MeshGraphSharedPtr &pGraph)
Creates an instance of this class.
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
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
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) 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:176
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:513
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1191