1 Motivating Questions

advertisement
Qinghai Zhang
1
An Introduction to Software Engineering for Mathematicians
Motivating Questions
2014-SEP-30
explicit (IMEX) scheme has the following steps:
for s = 1, 2, · · · , ns ,
ns
ns
X
X
[I]
[E]
as,j L φ(j) , t(j) ,
φ(s) = φn + k
as,j f t(j) + k
Q1. What is math?
Q2. Why do we care about math?
j=1
j=1
Q3. What is software and software engineering?
φn+1 = φn + k
Q4. Why do we care about software engineering?
ns
X
s=1
(s)
+k
b[E]
s f t
ns
X
(6a)
(s) (s)
,
b[I]
,t
s L φ
s=1
(6b)
2
An Illustrating Example
where the superscript (s) denotes an intermediate stage,
t(s) = tn + cs k the time of that stage, ns the number
of stages, and A, b, c the standard coefficients of the
Butcher tableau.
Consider numerically solving the diffusion equation
∂φ
= f (x, t) + ν∇2 φ,
∂t
(1)
Software Quality
on a rectangular domain Ω ⊂ RD , where u is a given 3
velocity field and f a given forcing term. The initial condition is φ(x, t) = φ0 (x) and the boundary condition is S1. Correctness: perform the tasks as defined by their
specifications;
Dirichlet, Neumann, Robin, or a mixed one of the above.
The problem domain Ω is discretized into square control S2. Ease of use
volumes, each of which denoted by a multi-index i ∈ ZD ;
the region of cell i is represented by
S3. Reusability: serve for the construction of many different applications;
Ci := xO + ih, xO + (i + 1) h ,
(2)
S4. Extendibility: easily adapt to changes of specifications;
where xO is some fixed origin of the coordinates, h the
uniform grid size, and 1 ∈ ZD the multi-index with all
its components equal to one. The averaged φ over cell i S5. Robustness: react appropriately to abnormal conditions;
is denoted by
Z
S6. Efficiency: place as few demands as possible on hard1
ware resources to achieve a certain metric;
φ (x) dx.
(3)
hφii := D
h Ci
S7. Compatibility: easily to be combined with others;
A fourth-order finite-volume
discretization of the
2 S8. Portability: easily to be transferred to various hardLaplacian is L hφii = ∇ φ C + O(h4 ) with
i
ware and software environments;
1 X
L hφii :=
− hφii+2ed + 16 hφii+ed
(4)
12h2
d
4 An Example of OOP
− 30 hφii + 16 hφii−ed − hφii−2ed , Below is some pseudo-code illustrating the paradigm of
object-oriented programming (OOP).
where ed ∈ ZD is a multi-index with 1 as its d-th component and 0 otherwise. Integrating (1) and applying (3) LevelData data;
and (4) yield a system of ODEs that approximates (1) data.define(xo, nCells, h);
ScalarFunction initFunc;
within O(h4 ):
initFunc.setData(data);
ScalarFunction bcFunc;
d hφi
= L hφi + hf (t)i ,
(5) bcFunc.define(...);
dt
BoundaryCondition bc;
for which we can use the “method of lines” to advance bc.define("Dirichlet", bcFunc);
the solution for each time step. For example, an implicit- DiscreteLaplacian<4> lapl;
1
Qinghai Zhang
An Introduction to Software Engineering for Mathematicians
5
lapl.define(xo, nCells, h);
ScalarFunction forcingFunc;
forcingFunc.define(...);
TimeIntegrator ti;
ti.define("ERK-ESDIRK", lapl, forcingFunc);
for (int i=0; i<nTimeSteps; i++)
{
Real t = t0 + i*dt;
ti.timeStep(data, t);
}
ti.report(data);
2014-SEP-30
Some OOP principles
5.1
Abstraction
5.2
Encapsulation
5.3
Inheritance: reusing the interface
5.4
Polymorphism:
jects
6
interchanging ob-
Testing
Some real C++ code are also included at the end of T1. unit tests;
this document.
T2. acceptance test.
• TimeIntegrator.H: interface class for time integrators.
7
Debugging
• ExplicitRungeKutta.H: an concrete class capturD1. a NP-complete problem;
ing explicit Runge-Kutta methods.
D2. use patterns to avoid cross-level bugs;
• ExplicitRungeKutta.cpp: the definition of various explicit Runge-Kutta methods.
D3. search by bisections.
2
File: /home/tsinghai/Dao/src/TimeIntegrators/TimeIntegrator.H
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
#ifndef _TIMEINTEGRATOR_H_
#define _TIMEINTEGRATOR_H_
/**
* Abstract base interface for time integrators.
* F is either FArrayBox or FluxBox
*/
template<class F>
class TimeIntegrator
{
public:
virtual ~TimeIntegrator(){}
virtual void updateTimeStepSize(const Real& dt) = 0;
///
/// Advance one time step for the unknown.
///
virtual void timeStep(Vector<LevelData<F>*>& U, Real time) = 0;
};
#endif
Page 1 of 1
File: /home/tsinghai/Dao/src/TimeIntegrators/ExplicitRungeKutta.H
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
#ifndef _EXPLICITRUNGEKUTTA_H_
#define _EXPLICITRUNGEKUTTA_H_
#include "BoundaryConditionFactory.H"
#include "HierarchyDataOps.H"
/// The list of implemented ERK methods.
enum ERK_Type
{
ForwardEuler=1,
ClassicRK4,
nERK_Type
};
/// The compile time constants of orders-of-accuracy
template<ERK_Type Type>
struct ERK_Order;
template<>
struct ERK_Order<ForwardEuler>
{
enum {val=1};
};
template<>
struct ERK_Order<ClassicRK4>
{
enum {val=4};
};
/// The compile time constants of numbers of stages
template<ERK_Type Type>
struct ERK_NumStages;
template<>
struct ERK_NumStages<ForwardEuler>
{
enum {val=1};
};
template<>
struct ERK_NumStages<ClassicRK4>
{
enum {val=4};
};
/// The Butcher Tableau of ERK methods
template <ERK_Type Type>
struct ERK_ButcherTableau
{
static const int nS = ERK_NumStages<Type>::val;
// The first nStages numbers are the nominators
// while the last one their common denominator.
static const int a[nS][nS+1];
static const int b[nS+1];
static const int c[nS+1];
};
/// default ERK methods for different orders-of-accuracy.
template<unsigned int Order>
struct ERK_DefaultMethods;
template<>
struct ERK_DefaultMethods<1>
{
enum {val=ForwardEuler};
};
template<>
struct ERK_DefaultMethods<4>
{
enum {val=ClassicRK4};
};
Page 1 of 3
File: /home/tsinghai/Dao/src/TimeIntegrators/ExplicitRungeKutta.H
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
/**
* This class encapsulates timeStepping algorithms
* of an ERK (explicit Runge-Kutta) method.
* To implement a new ERK method,
* all one needs to do is to
* (1) add a new key to the enumerations in ERK_Type;
* (2) set its order with template specialization of ERK_Order;
* (3) set its # of stages with template specialization of ERK_NumStages;
* (4) specify its Butcher tableau in ExplicitRungeKutta.cpp.
* In other words, the following class stays the same
* if the ERK method can be expressed with a single Butcher tableau.
*/
template<ERK_Type Type, /// The type of the ERK method
class F,
/// FArrayBox or FluxBox?
class EOS>
/// Equation Op strategies
class ExplicitRungeKutta
{
public:
/// type acronyms
typedef BoundaryConditionBase
typedef RefCountedPtr<BCB>
typedef ERK_ButcherTableau<Type>
typedef HierarchyDataOps
typedef Vector<LevelData<F>*>
typedef Vector<Vector<LevelData<F>*> >
BCB;
BCP;
ERC;
HOP;
VLF;
VVL;
static const int nStages = ERC::nS;
/// gamma is never used, just for interface uniformality
static const int gamma = 1;
void define(const Real& dx, const Vector<int>& refV, const BCP phiBC)
{
m_dx
= dx;
m_refV = refV;
m_phiBC = phiBC;
// initialize the Butcher arrays from ERC
a.resize(nStages);
for (int i=0; i<a.size(); i++)
a[i].resize(nStages);
b.resize(nStages);
c.resize(nStages);
for (int i=0; i<a.size(); i++)
{
for (int j=0; j<a[i].size(); j++)
a[i][j] = static_cast<Real>(ERC::a[i][j])/ERC::a[i][ERC::nS];
b[i] = static_cast<Real>(ERC::b[i])/ERC::b[ERC::nS];
c[i] = static_cast<Real>(ERC::c[i])/ERC::c[ERC::nS];
}
}
///
/// Advance one time step for the unknown.
/// The container imp here is intended for interface uniformity
/// and never used.
///
void timeStep(const Real& time, const Real& dt,
VLF& u, VVL& phi, VVL& exp, VVL& imp, EOS& eos)
{
for (int s=0; s<nStages; s++)
HOP::assign(phi[s], u);
// Loop over all the stages
for (int ns=0; ns<nStages; ns++)
{
pout() << " ERK stage: " << ns+1 << endl;
for (int j=0; j<ns; j++)
{
HOP::incr(phi[ns], exp[j], a[ns][j]*dt);
}
Page 2 of 3
File: /home/tsinghai/Dao/src/TimeIntegrators/ExplicitRungeKutta.H
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
// The current intermediate time
const Real ts = time + c[ns]*dt;
m_phiBC->setTime(ts);
eos.computeOperators(phi[ns], exp[ns], imp[ns], ts);
// add results of implicit operators to explicit results
// since we are using explicit Runge-Kutta methods.
HOP::incr(exp[ns], imp[ns], 1.0);
eos.stageStepPostProcessing(phi[ns], ts);
}
// calculate the results of the final stage
HOP::assign(u, phi[0]);
for (int s=0; s<nStages; s++)
HOP::incr(u, exp[s], b[s]*dt);
HOP::checkData(*u[0]);
// set BC of u for other operators like vorticity.
m_phiBC->fillGhostCells(u, m_dx, m_refV, false); // false: homogeneous
}
private:
// The Butcher arrays from ERC
Vector<Vector<Real> > a;
Vector<Real> b;
Vector<Real> c;
BCP
m_phiBC;
Real
m_dx;
Vector<int> m_refV;
};
#endif
Page 3 of 3
File: /home/tsinghai/Dao/src/TimeIn…grators/ExplicitRungeKutta.cpp
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
#include "ExplicitRungeKutta.H"
///-----------------------------------------------/// Butcher tableau of the forward Euler method
///-----------------------------------------------template<>
const int ERK_ButcherTableau<ForwardEuler>::a[][2] =
{
{0, 1}
};
template<>
const int ERK_ButcherTableau<ForwardEuler>::b[] =
{
1, 1
};
template<>
const int ERK_ButcherTableau<ForwardEuler>::c[] =
{
0, 1
};
///-------------------------------------------------------/// Butcher tableau of the fourth-order Runge-Kutta method
///-------------------------------------------------------template<>
const int ERK_ButcherTableau<ClassicRK4>::a[][5] =
{
{0, 0, 0, 0, 1},
{1, 0, 0, 0, 2},
{0, 1, 0, 0, 2},
{0, 0, 1, 0, 1}
};
template<>
const int ERK_ButcherTableau<ClassicRK4>::b[] =
{
1, 2, 2, 1, 6
};
template<>
const int ERK_ButcherTableau<ClassicRK4>::c[] =
{
0, 1, 1, 2, 2
};
Page 1 of 1
Download