CppNoddy  0.92
Loading...
Searching...
No Matches
PDE_IBVP.cpp
Go to the documentation of this file.
1/// \file PDE_IBVP.cpp
2/// Implementation of a class for an \f$ n^{th} \f$-order IBVP of the form
3/// \f[ M_1( {\underline f}(y,t), y, t )\cdot {\underline f}_t (y,t)+ M_0( {\underline f}(y,t), y, t ) \cdot {\underline f}_y (y,t) = {\underline R}( {\underline f}(y,t), y, t )\,, \f]
4/// subject to \f$ n \f$ conditions defined at \f$ y = y_{left} \f$ and
5/// \f$ y_{right} \f$ for some components of \f$ {\underline f}(y) \f$.
6/// Here \f$ M_{0,1} \f$ are matrices.
7/// The solution at the new time step \f$ t+\Delta t \f$ is
8/// \f[ {\underline f}^{new} = {\underline F} + {\underline g} \f]
9/// where \f$ {\underline F} \f$ is the current guess at the solution
10/// and \f$ {\underline g} \f$ is the linearised correction.
11/// The solution at the previous time \f$ t \f$ is
12/// \f[ {\underline f}^{old} = {\underline O} \f]
13/// A Crank-Nicolson method is employed with the linearised problem at the mid-time point
14/// \f$ t + \Delta t /2 \f$ being:
15/// \f[ \frac{2}{\Delta t} M_1 \cdot {\underline g } + 2 M_0 \cdot {\underline g}_y - J \cdot {\underline g} + J_2 \cdot \frac{\underline F - \underline O}{\Delta t} \cdot {\underline g} + J_1 \cdot \frac{\underline F_y + \underline O_y}{2} \cdot {\underline g} = 2 {\underline R} - \frac{2}{\Delta t} M_2 \cdot ( {\underline F} - {\underline O} ) - M_1 \cdot ( {\underline F}_y + {\underline O}_y )\f]
16/// Where \f$ M_{0,1}, J, J_{1,2}, R \f$ are evaluated at the mid-time step with arguments \f$ \left ( \frac{\underline F + \underline O}{2}, y, t + \frac{\Delta t}{2} \right ) \f$,
17/// with \f$ J_{1,2} \f$ denoting the Jacobian of the matrices \f$ \partial {(M_{0,1})}_{ij} / \partial f_k \f$.
18/// This problem is solved by second-order central differencing at the spatial (\f$ y \f$) inter-node mid points.
19
20#include <string>
21#include <cassert>
22
23#include <PDE_IBVP.h>
24#include <Equation_2matrix.h>
26#include <Exceptions.h>
27#include <BandedLinearSystem.h>
28#include <Timer.h>
29#include <Utility.h>
30
31namespace CppNoddy {
32
33 template <typename _Type>
35 const DenseVector<double> &nodes,
36 Residual_with_coords<_Type>* ptr_to_left_residual,
37 Residual_with_coords<_Type>* ptr_to_right_residual) :
38 TOL(1.e-8),
39 T(0.0),
40 MAX_ITERATIONS(12),
41 p_EQUATION(ptr_to_equation),
42 p_LEFT_RESIDUAL(ptr_to_left_residual),
43 p_RIGHT_RESIDUAL(ptr_to_right_residual) {
44 SOLN = OneD_Node_Mesh<_Type>(nodes, p_EQUATION -> get_order());
45 PREV_SOLN = SOLN;
46 //
47 p_EQUATION -> coord(1) = T;
48 if(p_EQUATION -> get_order() - p_LEFT_RESIDUAL -> get_order() - p_RIGHT_RESIDUAL -> get_order() != 0) {
49 std::string problem("\n The PDE_IBVP class has been constructed, but the order of the \n");
50 problem += "system does not match the number of boundary conditions.\n";
51 throw ExceptionRuntime(problem);
52 }
53#ifdef TIME
54 // timers
55 T_ASSEMBLE = Timer("Assembling of the matrix (incl. equation updates):");
56 T_SOLVE = Timer("Solving of the matrix:");
57#endif
58 }
59
60 template <typename _Type>
62#ifdef TIME
63 std::cout << "\n";
64 T_ASSEMBLE.stop();
65 T_ASSEMBLE.print();
66 T_SOLVE.stop();
67 T_SOLVE.print();
68#endif
69 }
70
71 template <typename _Type>
72 void PDE_IBVP<_Type>::step2(const double& dt) {
73 // the order of the problem
74 unsigned order(p_EQUATION -> get_order());
75 // get the number of nodes in the mesh
76 // -- this may have been refined by the user since the last call.
77 unsigned ny(SOLN.get_nnodes());
78 // measure of maximum residual
79 double max_residual(1.0);
80 // iteration counter
81 int counter = 0;
82 // store this soln as the 'previous SOLUTION'
83 PREV_SOLN = SOLN;
84 // ANY LARGE STORAGE USED IN THE MAIN LOOP IS
85 // DEFINED HERE TO AVOID REPEATED CONSTRUCTION.
86 // Note we blank the A matrix after every iteration.
87 //
88 // Banded LHS matrix - max obove diagonal band width is
89 // from first variable at node i to last variable at node i+1
90 BandedMatrix<_Type> a(ny * order, 2 * order - 1, 0.0);
91 // RHS
92 DenseVector<_Type> b(ny * order, 0.0);
93 // linear solver definition
94#ifdef LAPACK
95 BandedLinearSystem<_Type> system(&a, &b, "lapack");
96#else
97 BandedLinearSystem<_Type> system(&a, &b, "native");
98#endif
99 // loop until converged or too many iterations
100 do {
101 // iteration counter
102 ++counter;
103#ifdef TIME
104 T_ASSEMBLE.start();
105#endif
106 assemble_matrix_problem(a, b, dt);
107 max_residual = b.inf_norm();
108#ifdef DEBUG
109 std::cout << " PDE_IBVP.solve : Residual_max = " << max_residual << " tol = " << TOL << "\n";
110#endif
111#ifdef TIME
112 T_ASSEMBLE.stop();
113 T_SOLVE.start();
114#endif
115 // linear solver
116 system.solve();
117 // keep the solution in a OneD_GenMesh object
118 for(std::size_t var = 0; var < order; ++var) {
119 for(std::size_t i = 0; i < ny; ++i) {
120 SOLN(i, var) += b[ i * order + var ];
121 }
122 }
123#ifdef TIME
124 T_SOLVE.stop();
125#endif
126 } while((max_residual > TOL) && (counter < MAX_ITERATIONS));
127 if(max_residual > TOL) {
128 // restore to previous state because this step failed
129 SOLN = PREV_SOLN;
130 std::string problem("\n The PDE_IBVP.step2 method took too many iterations.\n");
131 problem += "Solution has been restored to the previous accurate state.\n";
132 throw ExceptionItn(problem, counter, max_residual);
133 }
134#ifdef DEBUG
135 std::cout << "[DEBUG] time-like variable = " << T << "\n";
136#endif
137 // set the time to the updated level
138 T += dt;
139 }
140
141
142 template <typename _Type>
144 // clear the Jacobian matrix
145 a.assign(0.0);
146 // inverse of the time step
147 const double inv_dt(1. / dt);
148 // the order of the problem
149 const unsigned order(p_EQUATION -> get_order());
150 // number of spatial nodes
151 const unsigned ny(SOLN.get_nnodes());
152 // row counter
153 std::size_t row(0);
154 // a matrix that is used in the Jacobian of the mass matrix terms
155 DenseMatrix<_Type> h0(order, order, 0.0);
156 DenseMatrix<_Type> h1(order, order, 0.0);
157 // local state variable and functions
158 DenseVector<_Type> F_midpt(order, 0.0);
159 DenseVector<_Type> O_midpt(order, 0.0);
160 DenseVector<_Type> state(order, 0.0);
161 DenseVector<_Type> state_dt(order, 0.0);
162 DenseVector<_Type> state_dy(order, 0.0);
163 // BCn equation is evaluated at the next time step
164 p_LEFT_RESIDUAL -> coord(0) = T + dt;
165 // update the BC residuals for the current iteration
166 p_LEFT_RESIDUAL -> update(SOLN.get_nodes_vars(0));
167 // add the (linearised) LHS BCs to the matrix problem
168 for(unsigned i = 0; i < p_LEFT_RESIDUAL -> get_order(); ++i) {
169 // loop thru variables at LHS of the domain
170 for(unsigned var = 0; var < order; ++var) {
171 a(row, var) = p_LEFT_RESIDUAL -> jacobian()(i, var);
172 }
173 b[ row ] = - p_LEFT_RESIDUAL -> residual()[ i ];
174 ++row;
175 }
176 // inner nodes of the mesh, node = 0,1,2,...,N-2
177 for(std::size_t node = 0; node <= ny - 2; ++node) {
178 const std::size_t l_node = node;
179 const std::size_t r_node = node + 1;
180 // inverse of step size
181 const double inv_dy = 1. / (SOLN.coord(r_node) - SOLN.coord(l_node));
182 // set the current solution at this node by 2nd order evaluation at mid point
183 for(unsigned var = 0; var < order; ++var) {
184 const _Type F_midpt = (SOLN(l_node, var) + SOLN(r_node, var)) / 2.;
185 const _Type O_midpt = (PREV_SOLN(l_node, var) + PREV_SOLN(r_node, var)) / 2.;
186 state_dy[ var ] = (SOLN(r_node, var) - SOLN(l_node, var)
187 + PREV_SOLN(r_node, var) - PREV_SOLN(l_node, var)) * inv_dy / 2.;
188 state[ var ] = (F_midpt + O_midpt) / 2.;
189 state_dt[ var ] = (F_midpt - O_midpt) * inv_dt;
190 }
191 // set the equation's y & t values to be mid points
192 // y
193 p_EQUATION -> coord(0) = 0.5 * (SOLN.coord(l_node) + SOLN.coord(r_node));
194 // t
195 p_EQUATION -> coord(1) = T + dt / 2;
196 // Update the equation to the mid point position
197 p_EQUATION -> update(state);
198 // evaluate the Jacobian of mass contribution multiplied by state_dy
199 p_EQUATION -> get_jacobian_of_matrix0_mult_vector(state, state_dy, h0);
200 // evaluate the Jacobian of mass contribution multiplied by state_dt
201 p_EQUATION -> get_jacobian_of_matrix1_mult_vector(state, state_dt, h1);
202 // loop over all the variables
203 //
204 // to avoid repeated mapping arithmetic within operator() of the
205 // BandedMatrix class we'll access the matrix with the iterator.
206 //
207 // il = position for (0, lnode*order)
208 typename BandedMatrix<_Type>::elt_iter l_iter(a.get_elt_iter(0, l_node * order));
209 // ir = position for (0, rnode*order)
210 typename BandedMatrix<_Type>::elt_iter r_iter(a.get_elt_iter(0, r_node * order));
211 //
212 for(unsigned var = 0; var < order; ++var) {
213 // add the matrix mult terms to the linearised problem
214 for(unsigned i = 0; i < order; ++i) { // dummy index
215 // add the Jacobian terms
216 // indirect access: a( row, order * l_node + i ) -= jac_midpt( var, i ) * 0.5;
217 const std::size_t offset(i * a.noffdiag() * 3 + row);
218 const typename BandedMatrix<_Type>::elt_iter left(l_iter + offset);
219 const typename BandedMatrix<_Type>::elt_iter right(r_iter + offset);
220 //
221 *left -= p_EQUATION -> jacobian()(var, i) * 0.5;
222 // indirect access: a( row, order * r_node + i ) -= jac_midpt( var, i ) * 0.5;
223 *right -= p_EQUATION -> jacobian()(var, i) * 0.5;
224 // add the Jacobian of mass terms for dt terms
225 // indirect access: a( row, order * l_node + i ) += h1( var, i ) * 0.5;
226 *left += h0(var, i) * 0.5;
227 // indirect access: a( row, order * r_node + i ) += h1( var, i ) * 0.5;
228 *right += h0(var, i) * 0.5;
229 // add the Jacobian of mass terms for dt terms
230 // indirect access: a( row, order * l_node + i ) += h2( var, i ) * 0.5;
231 *left += h1(var, i) * 0.5;
232 // indirect access: a( row, order * r_node + i ) += h2( var, i ) * 0.5;
233 *right += h1(var, i) * 0.5;
234 // add the mass matrix terms
235 // indirect access: a( row, order * l_node + i ) -= mass_midpt( var, i ) * inv_dy;
236 *left -= p_EQUATION -> matrix0()(var, i) * inv_dy;
237 // indirect access: a( row, order * r_node + i ) += mass_midpt( var, i ) * inv_dy;
238 *right += p_EQUATION -> matrix0()(var, i) * inv_dy;
239 // indirect access: a( row, order * l_node + i ) += mass_midpt( var, i ) * inv_dt;
240 *left += p_EQUATION -> matrix1()(var, i) * inv_dt;
241 // indirect access: a( row, order * r_node + i ) += mass_midpt( var, i ) * inv_dt;
242 *right += p_EQUATION -> matrix1()(var, i) * inv_dt;
243 }
244 // RHS
245 b[ row ] = p_EQUATION -> residual()[ var ];
246 b[ row ] -= Utility::dot(p_EQUATION -> matrix0()[ var ], state_dy);
247 b[ row ] -= Utility::dot(p_EQUATION -> matrix1()[ var ], state_dt);
248 b[ row ] *= 2;
249 // increment the row
250 row += 1;
251 }
252 }
253 // BCn equation is evaluated at the next step time point as
254 // they cannot depend on d/dt terms
255 p_RIGHT_RESIDUAL -> coord(0) = T + dt;
256 // update the BC residuals for the current iteration
257 p_RIGHT_RESIDUAL -> update(SOLN.get_nodes_vars(ny - 1));
258 // add the (linearised) RHS BCs to the matrix problem
259 for(unsigned i = 0; i < p_RIGHT_RESIDUAL -> get_order(); ++i) {
260 // loop thru variables at RHS of the domain
261 for(unsigned var = 0; var < order; ++var) {
262 a(row, order * (ny - 1) + var) = p_RIGHT_RESIDUAL -> jacobian()(i, var);
263 }
264 b[ row ] = - p_RIGHT_RESIDUAL -> residual()[ i ];
265 ++row;
266 }
267#ifdef PARANOID
268 if(row != ny * order) {
269 std::string problem("\n The ODE_BVP has an incorrect number of boundary conditions. \n");
270 throw ExceptionRuntime(problem);
271 }
272#endif
273 }
274
275 // the templated versions that we require are:
276 template class PDE_IBVP<double>
277 ;
278 template class PDE_IBVP< D_complex >
279 ;
280
281} // end namespace
282
Specification of the linear system class.
A templated class for equations that can be inherited from to allow instantiation of PDE_double_IBVP ...
The collection of CppNoddy exceptions.
A specification of a class for an -order IBVP of the form.
A specification of a (double/complex) residual class that not only defines a vector residual of a vec...
A spec for the CppNoddy Timer object.
A spec for a collection of utility functions.
A linear system class for vector right-hand sides.
void solve()
Solve the banded system.
A matrix class that constructs a BANDED matrix.
Definition: BandedMatrix.h:16
std::size_t noffdiag() const
Get the number of off-diagonal elements where the total INPUT band width is 2*noffdiag+1 since the ba...
Definition: BandedMatrix.h:227
void assign(_Type elt)
Assign a value the matrix so that it has the same geometry, but zero entries in all locations includi...
Definition: BandedMatrix.h:106
DenseVector< _Type >::elt_iter elt_iter
Definition: BandedMatrix.h:20
elt_iter get_elt_iter(std::size_t row, std::size_t col)
Definition: BandedMatrix.h:127
A matrix class that constructs a DENSE matrix as a row major std::vector of DenseVectors.
Definition: DenseMatrix.h:25
An DenseVector class – a dense vector object.
Definition: DenseVector.h:34
double inf_norm() const
Infinity norm.
Definition: DenseVector.cpp:59
An equation object base class used in the PDE_double_IBVP class.
An exception class that is thrown if too many Newton steps are taken in either the scalar or vector N...
Definition: Exceptions.h:90
A generic runtime exception.
Definition: Exceptions.h:158
A one dimensional mesh utility object.
A templated object for real/complex vector system of unsteady equations.
Definition: PDE_IBVP.h:37
void step2(const double &dt)
A Crank-Nicolson 'time' stepper.
Definition: PDE_IBVP.cpp:72
double & coord()
Return a reference to the current value of the 'timelike/parabolic' coordinate.
Definition: PDE_IBVP.h:65
void assemble_matrix_problem(BandedMatrix< _Type > &a, DenseVector< _Type > &b, const double &dt)
Assembles the matrix problem for a BVP solve at the current time level.
Definition: PDE_IBVP.cpp:143
~PDE_IBVP()
Destructor.
Definition: PDE_IBVP.cpp:61
PDE_IBVP(Equation_2matrix< _Type > *equation_ptr, const DenseVector< double > &nodes, Residual_with_coords< _Type > *ptr_to_left_residual, Residual_with_coords< _Type > *ptr_to_right_residual)
The class is defined by a vector function for the system.
Definition: PDE_IBVP.cpp:34
A base class to be inherited by objects that define residuals.
A simple CPU-clock-tick timer for timing metods.
Definition: Timer.h:19
_Type dot(const DenseVector< _Type > &X, const DenseVector< _Type > &Y)
Templated dot product.
Definition: Utility.h:314
A collection of OO numerical routines aimed at simple (typical) applied problems in continuum mechani...

© 2012

R.E. Hewitt