/
TestWritingPdeSolversTutorial.hpp
557 lines (507 loc) · 25 KB
/
TestWritingPdeSolversTutorial.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
/*
Copyright (c) 2005-2024, University of Oxford.
All rights reserved.
University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.
This file is part of Chaste.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Oxford nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
*
*
*
* Chaste tutorial - this page gets automatically changed to a wiki page
* DO NOT remove the comments below, and if the code has to be changed in
* order to run, please check the comments are still accurate
*
*
*
*
*
*/
#ifndef TESTWRITINGPDESOLVERSTUTORIAL_HPP_
#define TESTWRITINGPDESOLVERSTUTORIAL_HPP_
/* HOW_TO_TAG PDE
* Write new PDE solvers (especially for linear coupled elliptic/parabolic systems)
*/
/*
* ### Introduction
*
* Chaste can be used to set up solvers for more general (coupled) PDEs. To do this the
* user just needs to code up the integrands of any finite element (FE) matrices or vectors,
* without having to deal with set-up, looping over elements, numerical quadrature, assembly
* or solving the linear system. If you have a set of coupled linear PDEs for which it is
* appropriate to use linear basis functions for each unknown (for example, a reaction-diffusion
* system), then it is relatively straightforward to set up a solver that will be parallel and
* reliable, since all the base components are heavily tested.
*
* Some solvers for general simple (uncoupled) linear PDEs are already provided in Chaste, such
* as the `SimpleLinearEllipticSolver`. These are for PDEs that can be written in a generic
* form (`SimpleLinearEllipticPde`, for example). However ''coupled'' PDEs can't
* be easily written in generic form, so the user has to write their own solver. In this tutorial
* we explain how to do this.
*
* For this tutorial the user needs to have read the solving-PDEs tutorials. It may also be
* helpful to read the associated [lectures notes](https://chaste.cs.ox.ac.uk/trac/wiki/ChasteGuides/NmoodLectureNotes),
* in particular the slides on solving equations using finite elements if you are not familiar
* with this (lecture 2), the slides on the general design of the Chaste finite element solvers
* (lecture 3), and the first part of lecture 4.
*
* Let us use the terminology "assembled in an FE manner" for any matrix or vector that is
* defined via a volume/surface/line integral, and which is constructed by: looping over
* elements (or surface elements, etc); computing the elemental contribution (i.e. a small
* matrix/vector) using numerical quadrature; and adding to the full matrix/vector.
*
* We only consider linear problems here. In these problems the discretised FE problem leads
* to a linear system, Ax=b, to be solved once in static problems and at each time step in
* time-dependent problems. There are two cases to be distinguished. The first case is where
* BOTH A and b are assembled in an FE manner, b possibly being composed of a volume integral
* plus a surface integral. The other case is where this is not true, for example where b = Mc+d,
* where the vector d and matrix M are assembled in an FE manner, but not the vector c.
*
* The Chaste PDE classes include ASSEMBLER classes, for setting up anything assembled in an
* FE manner, and SOLVER classes, for setting up linear systems. In the general case, solvers
* need to own assemblers for setting up each part of the linear system. However for the first
* case described above (in which both A and b in Ax=b are assembled), we can use the design
* where the solver IS AN assembler. We illustrate how to do this in the first tutorial.
*
* ### Writing solvers
*
* Let us write a solver for the coupled 2-unknown problem
*
* $$
* \begin{align*}
* \nabla^2 u + v &= f(x,y),\\\\\\
* \nabla^2 u + u &= g(x,y),
* \end{align*}
* $$
*
* where $f$ and $g$ are chosen so that,
* with zero-dirichlet boundary conditions, the solution is given by $u = \sin(\pi x)\sin(\pi x)$,
* $v = \sin(2\pi x)\sin(2\pi x)$.
*
* (As a brief aside, note that the solver we write will in fact work with general Dirichlet-Neumann
* boundary conditions, though the test will only provide all-Dirichlet boundary conditions. We
* save a discussion on general Dirichlet-Neumann boundary conditions for the second example.)
*
* Using linear basis functions, and a mesh with N nodes, the linear system that needs to be set up is
* of size 2N by 2N, and in block form is:
*
* ```
* [ K -M ] [U] = [b1]
* [ -M K ] [V] [b2]
* ```
*
* where `K` is the stiffness matrix, `M` the mass matrix, `U` the vector of nodal values
* of u, `V` the vector of nodal values of v, `b1` the vector with entries $\int f\phi_i dV$ ($i=1,\dots,N$)
* and `b2` has entries $\int g\phi_i dV$ (here $\phi_i$ are the linear basis functions).
*
* This is the linear system which we now write a solver to set up.
* {{< callout context="note" title="Note" icon="info-circle" >}}
* The main Chaste solvers assume a **STRIPED** data format, i.e. that the unknown vector
* is:
*
* `[U_1 V_1 U_2 V_2 .. U_n V_n]`, not `[U_1 U_2 .. U_n V_1 V_2 .. V_n]`.
*
* *We write down equations in block form as it makes things clearer, but have to remember that the code
* deals with STRIPED data structures.* Striping is used in the code for parallelisation reasons.
* {{< /callout >}}
*
* These are some basic includes as in the solving-PDEs tutorials
*/
#include <cxxtest/TestSuite.h>
#include "TetrahedralMesh.hpp"
#include "TrianglesMeshReader.hpp"
#include "BoundaryConditionsContainer.hpp"
#include "ConstBoundaryCondition.hpp"
#include "PetscSetupAndFinalize.hpp"
#include "TrianglesMeshWriter.hpp"
/* We need to include the following two classes if we are going to use a combination of
* (element_dim, space_dim, problem_dim) that isn't explicitly instantiated in
* `BoundaryConditionsContainer.cpp` (see the bottom of that file) (without these includes this test will
* fail to link).
*/
#include "BoundaryConditionsContainerImplementation.hpp"
#include "AbstractBoundaryConditionsContainerImplementation.hpp"
/* These two classes will be used in writing the solver */
#include "AbstractAssemblerSolverHybrid.hpp"
#include "AbstractStaticLinearPdeSolver.hpp"
/* We will solve a second problem, below, which will be time-dependent and will
* require the following class */
#include "AbstractDynamicLinearPdeSolver.hpp"
/* The linear system is Ax=b where A and b are FE assembled, so we can use the solver-is-an-assembler
* design. To construct our solver, we inherit from `AbstractAssemblerSolverHybrid` which links
* the solver clases to the assembler classes, and `AbstractStaticLinearPdeSolver`. (For time-dependent
* problems, the second parent would be `AbstractDynamicLinearPdeSolver`). Note the template
* parameter `PROBLEM_DIM` below, in this case it is 2 as there are two unknowns.
*/
class MyTwoVariablePdeSolver
: public AbstractAssemblerSolverHybrid<2/*elem_dim*/,2/*space_dim*/,2/*problem_dim*/,NORMAL/*amount of interpolation*/>,
public AbstractStaticLinearPdeSolver<2,2,2>
{
private:
/* The function f */
double f(double x,double y)
{
return -2*M_PI*M_PI*sin(M_PI*x)*sin(M_PI*y) + sin(2*M_PI*x)*sin(2*M_PI*y);
}
/* The function g */
double g(double x,double y)
{
return -8*M_PI*M_PI*sin(2*M_PI*x)*sin(2*M_PI*y) + sin(M_PI*x)*sin(M_PI*y);
}
/*
* The abstract assembler parent classes know how to assemble matrices and vectors, but the concrete
* class needs to provide the integrand of the elemental contribution to A and b. This first
* method returns the elemental contribution of the matrix A, given the provided bases
* (`rPhi`, `rGradPhi`). The '3's here represent the number of bases per element (ie the number
* of nodes as linear bases are being used). The returned matrix is 6 by 6 (`problem_dim *
* num_bases_per_element = 2*3 = 6`).
*/
c_matrix<double,2*3,2*3> ComputeMatrixTerm(c_vector<double,3>& rPhi /* the three bases for the current element, evaluated at the current quad pt*/,
c_matrix<double,2,3>& rGradPhi /* gradients of the three bases */,
ChastePoint<2>& rX /* physical coordinate of quad point */,
c_vector<double,2>& rU /* current solution (unused here as a linear static problem */,
c_matrix<double,2,2>& rGradU /* current solution gradient (unused here as a linear static problem */,
Element<2,2>* pElement)
{
/*
* Set up the matrix, which corresponds to the elemental contribution for the matrix
* written above, taking into account the striped nature of the matrices and vectors.
* (Note: the following can be done more efficiently using matrix slices and products,
* see `BidomainAssembler` for example).
*/
c_matrix<double,2*3,2*3> ret = zero_matrix<double>(2*3, 2*3);
for (unsigned i=0; i<3; i++)
{
for (unsigned j=0; j<3; j++)
{
for (unsigned k=0; k<2; k++)
{
// stiffness matrix on diagonal 'blocks'
ret(2*i, 2*j) += rGradPhi(k,i)*rGradPhi(k,j);
ret(2*i+1,2*j+1) += rGradPhi(k,i)*rGradPhi(k,j);
}
// (negative) mass matrix on off-diagonal 'blocks'
ret(2*i+1, 2*j) = -rPhi(i)*rPhi(j);
ret(2*i, 2*j+1) = -rPhi(i)*rPhi(j);
}
}
return ret;
}
/* Similarly compute the elemental contribution to the RHS vector */
c_vector<double,2*3> ComputeVectorTerm(c_vector<double, 3>& rPhi,
c_matrix<double, 2, 2+1>& rGradPhi,
ChastePoint<2>& rX,
c_vector<double,2>& rU,
c_matrix<double,2,2>& rGradU,
Element<2,2>* pElement)
{
c_vector<double,2*3> ret;
for (unsigned i=0; i<3; i++)
{
ret(2*i) = -f(rX[0],rX[1]) * rPhi(i);
ret(2*i+1) = -g(rX[0],rX[1]) * rPhi(i);
}
return ret;
}
/* These classes which inherit from both assemblers and solvers must
* provide the following method, which links the two. Just copy and paste
* the following.
*/
void SetupLinearSystem(Vec currentSolution, bool computeMatrix)
{
SetupGivenLinearSystem(currentSolution, computeMatrix, this->mpLinearSystem);
}
public:
/* The constructor takes in a mesh and boundary conditions container, and passes
* them to the parent classes.
*/
MyTwoVariablePdeSolver(TetrahedralMesh<2,2>* pMesh,
BoundaryConditionsContainer<2,2,2>* pBoundaryConditions)
: AbstractAssemblerSolverHybrid<2,2,2,NORMAL>(pMesh,pBoundaryConditions),
AbstractStaticLinearPdeSolver<2,2,2>(pMesh)
{
}
};
/*
* That is the solver written. The usage is the same as see the PDE solvers described in the
* previous tutorials - have a look at the first test below.
*
* ### A solver of 3 parabolic equations
*
* Let us also write a solver for the following problem, which is composed of 3 parabolic PDEs
*
* $$
* \begin{align*}
* u_t &= \nabla^2 u + v,\\\\\\
* v_t &= \nabla^2 v + u + 2w,\\\\\\
* w_t &= \nabla^2 w + g(t,x,y),
* \end{align*}
* $$
*
* where $g(t,x,y) = t$ if $x>0.5$ and $0$ otherwise. This time we assume general
* Dirichlet-Neumann boundary conditions will be specified.
*
* The `AbstractAssemblerSolverHybrid` deals with the Dirichlet and Neumann boundary parts of the implementation,
* so, we, the writer of the solver, don't have to worry about this. The user has to realise though that they are
* specifying NATURAL Neumann BCs, which are whatever appears naturally in the weak form of the problem. In this case, natural
* Neumann BCs are specification of: $\partial u/\partial n = s_1, \partial v/\partial n = s_2, \partial w/ \partial n = s_3$,
* which coincide with usual Neumann BCs. However, suppose the last equation was $w_t = \nabla^2 w + \nabla . (D \nabla u)$,
* then the natural BCs would be:
* $\partial u/\partial n = s_1, \partial v/\partial n = s_2, \partial w/\partial n + (D\nabla u).n = s_3$.
*
* We need to choose a time-discretisation. Let us choose an implicit discretisation, ie
*
* ```
* (u^{n+1} - u^{n})/dt = Laplacian(u^{n+1}) + v^{n+1}
* (v^{n+1} - v^{n})/dt = Laplacian(v^{n+1}) + u^{n+1} + 2w^{n+1}
* (w^{n+1} - w^{n})/dt = Laplacian(w^{n+1}) + g(t^{n+1},x)
* ```
*
* Using linear basis functions, and a mesh with N nodes, the linear system that needs to be set up is
* of size 3N by 3N, and in block form is:
*
* ```
* [ M/dt+K -M 0 ] [U^{n+1}] = [b1] + [c1]
* [ -M M/dt+K -2M ] [V^{n+1}] [b2] + [c2]
* [ 0 0 M/dt+K ] [W^{n+1}] [b3] + [c3]
* ```
*
* where `K` is the stiffness matrix, `M` the mass matrix, `U^n` the vector of nodal values
* of u at time t_n, etc, `b1` has entries `integral( (u^n/dt)phi_i dV )`, and similarly for
* `b2` and `b3`. Writing the Neumann boundary conditions for
* u as `du/dn = s1(x,y)` on Gamma, a subset of the boundary, then `c1` has entries
* `integral_over_Gamma (s1*phi_i dS)`, and similarly for `c2` and `c3`.
*
* Let us create a solver for this linear system, which will be written in a way in which the RHS
* vector is assembled in an FE manner, so that the solver-is-an-assembler design can be used.
* Note that this solver inherits from `AbstractDynamicLinearPdeSolver` and PROBLEM_DIM is now equal
* to 3. We don't have to worry about setting up [c1 c2 c3] (we just need to take in a `BoundaryConditionsContainer`
* and the parent will use it in assembling this vector). We do however have to tell it
* how to assemble the volume integral part of the RHS vector, and the LHS matrix.
*/
class ThreeParabolicPdesSolver
: public AbstractAssemblerSolverHybrid<2,2,3,NORMAL>,
public AbstractDynamicLinearPdeSolver<2,2,3>
{
private:
/* Define the function g(t,x,y) */
double g(double t, ChastePoint<2>& rX)
{
return t*(rX[0]>0.5);
}
/* Provide the (elemental contribution to the) LHS matrix. The matrix is 9 by 9, where
* 9 = 3*3 = PROBLEM_DIM * NUM_NODES_PER_ELEMENT */
c_matrix<double,3*3,3*3> ComputeMatrixTerm(c_vector<double,3>& rPhi,
c_matrix<double,2,3>& rGradPhi,
ChastePoint<2>& rX,
c_vector<double,3>& rU,
c_matrix<double,3,2>& rGradU,
Element<2,2>* pElement)
{
c_matrix<double,9,9> ret = zero_matrix<double>(9,9);
// this is how to get the current timestep
double dt = PdeSimulationTime::GetPdeTimeStep();
for (unsigned i=0; i<3; i++)
{
for (unsigned j=0; j<3; j++)
{
// mass matrix on the diagonal blocks
ret(3*i, 3*j) = rPhi(i)*rPhi(j)/dt;
ret(3*i+1,3*j+1) = rPhi(i)*rPhi(j)/dt;
ret(3*i+2,3*j+2) = rPhi(i)*rPhi(j)/dt;
// mass matrix on some off-diagonal blocks
ret(3*i, 3*j+1) = -rPhi(i)*rPhi(j);
ret(3*i+1,3*j) = -rPhi(i)*rPhi(j);
ret(3*i+1,3*j+2) = -2*rPhi(i)*rPhi(j);
// stiffness matrix on the diagonal blocks
for (unsigned dim=0; dim<2; dim++)
{
ret(3*i, 3*j) += rGradPhi(dim,i)*rGradPhi(dim,j);
ret(3*i+1,3*j+1) += rGradPhi(dim,i)*rGradPhi(dim,j);
ret(3*i+2,3*j+2) += rGradPhi(dim,i)*rGradPhi(dim,j);
}
}
}
return ret;
}
/* Provide the volume elemental contribution to the RHS vector, ie the vector `[b1 b2 b3]` above */
c_vector<double,3*3> ComputeVectorTerm(c_vector<double, 3>& rPhi,
c_matrix<double, 2, 3>& rGradPhi,
ChastePoint<2>& rX,
c_vector<double,3>& rU,
c_matrix<double,3,2>& rGradU,
Element<2,2>* pElement)
{
c_vector<double,3*3> ret;
// get u,v,w out of the provided parameters
double u = rU(0);
double v = rU(1);
double w = rU(2);
// this is how to get the current time, timestep and inverse of the timestep
double t = PdeSimulationTime::GetTime();
double dt = PdeSimulationTime::GetPdeTimeStep();
double inverse_dt = PdeSimulationTime::GetPdeTimeStepInverse();
for (unsigned i=0; i<3; i++)
{
ret(3*i) = u* inverse_dt * rPhi(i);
ret(3*i+1) = v* inverse_dt * rPhi(i);
ret(3*i+2) = (w * inverse_dt + g(t+dt,rX)) * rPhi(i);
}
return ret;
}
/* Define this method as before */
void SetupLinearSystem(Vec currentSolution, bool computeMatrix)
{
SetupGivenLinearSystem(currentSolution, computeMatrix, this->mpLinearSystem);
}
public:
/* The constructor is similar to before. However: **important** - by default the dynamic solvers
* will reassemble the matrix each timestep. In this (and most other) problems the matrix is constant
* and only needs to be assembled once. Make sure we tell the solver this, otherwise performance
* will be destroyed.
*/
ThreeParabolicPdesSolver(TetrahedralMesh<2,2>* pMesh,
BoundaryConditionsContainer<2,2,3>* pBoundaryConditions)
: AbstractAssemblerSolverHybrid<2,2,3,NORMAL>(pMesh,pBoundaryConditions),
AbstractDynamicLinearPdeSolver<2,2,3>(pMesh)
{
this->mMatrixIsConstant = true;
}
};
/* Now the tests using the two solvers */
class TestWritingPdeSolversTutorial : public CxxTest::TestSuite
{
public:
/* Use the first solver to solve the static PDE. We apply zero Dirichlet boundary conditions
* on the whole of the boundary for both variables.
*/
void TestMyTwoVariablePdeSolver()
{
TetrahedralMesh<2,2> mesh;
mesh.ConstructRegularSlabMesh(0.01 /*h*/, 1.0 /*width*/, 1.0 /*height*/);
// Boundary conditions for 2-unknown problem
BoundaryConditionsContainer<2,2,2> bcc;
bcc.DefineZeroDirichletOnMeshBoundary(&mesh,0); // zero dirichlet for u
bcc.DefineZeroDirichletOnMeshBoundary(&mesh,1); // zero dirichlet for v
// Use our purpose-made solver for this problem:
MyTwoVariablePdeSolver solver(&mesh,&bcc);
/* The `AbstractStaticLinearPdeSolver` class from which our solver
* inherits, provides a `Solve` method.
*/
Vec result = solver.Solve();
ReplicatableVector result_repl(result);
/* Compare against the exact solution. */
for (unsigned i=0; i<mesh.GetNumNodes(); i++)
{
double x = mesh.GetNode(i)->GetPoint()[0];
double y = mesh.GetNode(i)->GetPoint()[1];
double u = result_repl[2*i];
double v = result_repl[2*i+1];
double u_exact = sin(M_PI*x)*sin(M_PI*y);
double v_exact = sin(2*M_PI*x)*sin(2*M_PI*y);
TS_ASSERT_DELTA(u, u_exact, 0.002);
TS_ASSERT_DELTA(v, v_exact, 0.007);
}
PetscTools::Destroy(result);
}
/* Now run a test solving the parabolic-parabolic-parabolic PDE system. */
void TestMyParaEllipticSetOfPdesSolver()
{
TetrahedralMesh<2,2> mesh;
mesh.ConstructRegularSlabMesh(0.05 /*h*/, 1.0 /*width*/, 1.0 /*height*/);
/* Set up the boundary conditions. v and w are zero on the entire boundary,
* and $\partial u/\partial n=1$ on the LHS and 0 otherwise.
*/
BoundaryConditionsContainer<2,2,3> bcc;
bcc.DefineZeroDirichletOnMeshBoundary(&mesh,1 /*index of unknown, ie v*/);
bcc.DefineZeroDirichletOnMeshBoundary(&mesh,2 /*index of unknown, ie w*/);
ConstBoundaryCondition<2>* p_neumann_bc = new ConstBoundaryCondition<2>(1.0);
TetrahedralMesh<2,2>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin();
while (iter < mesh.GetBoundaryElementIteratorEnd())
{
if (fabs((*iter)->CalculateCentroid()[0])<1e-6)
{
bcc.AddNeumannBoundaryCondition(*iter, p_neumann_bc, 0 /*index of unknown, ie u*/);
}
iter++;
}
/* Use our solver */
ThreeParabolicPdesSolver solver(&mesh,&bcc);
/* The interface is exactly the same as the `SimpleLinearParabolicSolver`. */
solver.SetTimeStep(0.01);
solver.SetTimes(0.0, 2.0);
Vec initial_condition = PetscTools::CreateAndSetVec(3*mesh.GetNumNodes(), 0.0);
solver.SetInitialCondition(initial_condition);
/* For this test we show how to output results to file for multiple sampling times. We start by
* specifying an output directory and filename prefix for our results file:
*/
solver.SetOutputDirectoryAndPrefix("ThreeVarCoupledProblem","results");
/* When an output directory has been specified, the solver writes output in HDF5 format. To
* convert this to another output format, we call the relevant method. Here, we convert
* the output to plain txt files. We also say how often to write the data, telling the
* solver to output results to file every tenth timestep. The solver will create
* one file for each variable and for each time, so for example, the file
* `results_Variable_0_10` is the results for $u$, over all nodes, at the 11th printed time.
* Have a look in the output directory after running the test. (Note: the HDF5 data can also be
* converted to VTK or cmgui formats).
*/
solver.SetOutputToTxt(true);
solver.SetPrintingTimestepMultiple(10);
/* We are now ready to solve the system. */
Vec result = solver.Solve();
ReplicatableVector result_repl(result);
/* The plain txt output can be loaded into matlab for easy visualisation. For this we
* also need the mesh data - at the very least the nodal locations - so we also write out
* the mesh
*/
TrianglesMeshWriter<2,2> mesh_writer("ThreeVarCoupledProblem", "mesh", false /*don't clean (ie delete everything in) directory!*/);
mesh_writer.WriteFilesUsingMesh(mesh);
/* Note that we need to destroy the initial condition vector as well as the solution. */
PetscTools::Destroy(initial_condition);
PetscTools::Destroy(result);
}
};
/*
*
* **Visualisation:** To visualise in matlab/octave, you can load the node file,
* and then the data files. However, the node file needs to be edited to remove any
* comment lines (lines starting with `#`) and the header line (which says how
* many nodes there are). (The little matlab script [`anim/matlab/read_chaste_node_file.m`](https://github.com/Chaste/Chaste/blob/develop/anim/matlab/read_chaste_node_file.m)
* can be used to do this, though it is not claimed to be very robust). As an
* example matlab visualisation script, the following, if run from the output
* directory, plots w:
*
* ```matlab
* pos = read_chaste_node_file('mesh.node');
* for i=0:200
* file = ['txt_output/results_Variable_2_',num2str(i),'.txt'];
* w = load(file);
* plot3(pos(:,1),pos(:,2),w,'*');
* pause;
* end;
* ```
*
*/
#endif // TESTWRITINGPDESOLVERSTUTORIAL_HPP_