CppNoddy  0.92
Loading...
Searching...
No Matches
PDE_double_IBVP.cpp
Go to the documentation of this file.
1/// \file PDE_double_IBVP.cpp
2
3
4#include <string>
5#include <cassert>
6
7#include <PDE_double_IBVP.h>
9#include <Equation_3matrix.h>
10#include <Exceptions.h>
11#include <Utility.h>
12#include <Timer.h>
13
14//#define DEBUG
15
16namespace CppNoddy {
17 template <typename _Type>
19 Equation_3matrix<_Type >* ptr_to_equation,
20 const DenseVector<double> &xnodes,
21 const DenseVector<double> &ynodes,
22 Residual_with_coords<_Type>* ptr_to_bottom_residual,
23 Residual_with_coords<_Type>* ptr_to_top_residual) :
24 TOL(1.e-8),
25 T(0.0),
26 MAX_ITERATIONS(20),
27 p_EQUATION(ptr_to_equation),
28 p_BOTTOM_RESIDUAL(ptr_to_bottom_residual),
29 p_TOP_RESIDUAL(ptr_to_top_residual),
30 UPDATED(false) {
31 // create the 2D mesh for the current time level and previous time level
32 SOLN = TwoD_Node_Mesh<_Type>(xnodes, ynodes, p_EQUATION -> get_order());
33 // copy construct the previous solution's storage
34 PREV_SOLN = SOLN;
35 // initialise the eqn object
36 // "x"
37 p_EQUATION -> coord(2) = xnodes[0];
38#ifdef TIME
39 // timers
40 T_ASSEMBLE = Timer("Assembling of the matrix (incl. equation updates):");
41 T_SOLVE = Timer("Solving of the matrix:");
42#endif
43 }
44
45 template <typename _Type>
47#ifdef TIME
48 std::cout << "\n";
49 T_ASSEMBLE.stop();
50 T_ASSEMBLE.print();
51 T_SOLVE.stop();
52 T_SOLVE.print();
53#endif
54 }
55
56 template <typename _Type>
57 void PDE_double_IBVP<_Type>::step2(const double& dt) {
58 DT = dt;
59 if(!UPDATED) {
60 std::string problem;
61 problem = " You have called the PDE_double_IBVP::step2 method without calling\n";
62 problem += " the PDE_double_IBVP::update_previous_solution method first. This\n";
63 problem += " method is required to copy/store the previous time level's solution\n";
64 problem += " and then allow you to specify/alter the x-initial condition by\n";
65 problem += " for the current time level.";
66 throw ExceptionRuntime(problem);
67 }
68 // the order of the problem
69 unsigned order(p_EQUATION -> get_order());
70 // get the number of nodes in the mesh
71 // -- this may have been refined by the user since the last call.
72 unsigned nx(SOLN.get_nnodes().first);
73 unsigned ny(SOLN.get_nnodes().second);
74 // measure of maximum residual
75 double max_residual(1.0);
76 // the current solution moves to the previous solution
77 // ANY LARGE STORAGE USED IN THE MAIN LOOP IS
78 // DEFINED HERE TO AVOID REPEATED CONSTRUCTION.
79 // Note we blank the A matrix after every iteration.
80 //
81 // Banded LHS matrix - max obove diagonal band wTidth is
82 // from first variable at node i to last variable at node i+1
83 BandedMatrix<_Type> a(ny * order, 2 * order - 1, 0.0);
84 // RHS
85 DenseVector<_Type> b(ny * order, 0.0);
86 // linear solver definition
87#ifdef LAPACK
88 BandedLinearSystem<_Type> system(&a, &b, "lapack");
89#else
90 BandedLinearSystem<_Type> system(&a, &b, "native");
91#endif
92 for(std::size_t j = 0; j < nx - 1; ++j) {
93 //// next x-soln guess is the previous x-soln
94 //for ( std::size_t var = 0; var < order; ++var )
95 //{
96 //for ( std::size_t i = 0; i < ny; ++i )
97 //{
98 //SOLN( j + 1, i, var ) = SOLN( j, i, var );
99 //}
100 //}
101 //
102 // iteration counter
103 int counter = 0;
104 // loop until converged or too many iterations
105 do {
106 // iteration counter
107 ++counter;
108 // time the assemble phase
109#ifdef TIME
110 T_ASSEMBLE.start();
111#endif
112 assemble_matrix_problem(a, b, j);
113 max_residual = b.inf_norm();
114#ifdef DEBUG
115 std::cout.precision(12);
116 std::cout << " PDE_double_IBVP.step2 : x_j = " << SOLN.coord(j,0).first << " Residual_max = " << max_residual << " tol = " << TOL << " counter = " << counter << "\n";
117#endif
118#ifdef TIME
119 T_ASSEMBLE.stop();
120 T_SOLVE.start();
121#endif
122 // linear solver
123 system.solve();
124 // keep the solution in a OneD_GenMesh object
125 for(std::size_t i = 0; i < ny; ++i) {
126 for(std::size_t var = 0; var < order; ++var) {
127 SOLN(j + 1, i, var) += b[ i * order + var ];
128 }
129 }
130#ifdef TIME
131 T_SOLVE.stop();
132#endif
133 } while((max_residual > TOL) && (counter < MAX_ITERATIONS));
134 if(counter >= MAX_ITERATIONS) {
135 std::string problem("\n The PDE_double_IBVP.step2 method took too many iterations. \n");
136 throw ExceptionItn(problem, counter, max_residual);
137 }
138 }
139 // set the time to the updated level
140 T += DT;
141#ifdef DEBUG
142 std::cout << "[DEBUG] solved at t = " << T << "\n";
143#endif
144 UPDATED = false;
145 }
146
147
148 template <typename _Type>
152 const std::size_t& j) {
153 // clear the Jacobian matrix
154 a.assign(0.0);
155 // inverse of the t-step
156 const double inv_dt(1. / DT);
157 // inverse of the x-step
158 const double inv_dx(1. / (SOLN.coord(j + 1, 0).first - SOLN.coord(j, 0).first));
159 // mid point of the x variable
160 const double x_midpt = 0.5 * (SOLN.coord(j + 1, 0).first + SOLN.coord(j, 0).first);
161 // the order of the problem
162 const unsigned order(p_EQUATION -> get_order());
163 // number of spatial nodes in the y-direction
164 const unsigned ny(SOLN.get_nnodes().second);
165 // row counter
166 std::size_t row(0);
167 // local state variables
168 DenseVector<_Type> state(order, 0.0);
169 DenseVector<_Type> state_dx(order, 0.0);
170 DenseVector<_Type> state_dt(order, 0.0);
171 DenseVector<_Type> state_dy(order, 0.0);
172 // Current and old state
173 DenseVector<_Type> F_midpt(order, 0.0);
174 DenseVector<_Type> O_midpt(order, 0.0);
175 // these store the Jacobian of the mass matrices times a state vector
176 DenseMatrix<_Type> h0(order, order, 0.0);
177 DenseMatrix<_Type> h1(order, order, 0.0);
178 DenseMatrix<_Type> h2(order, order, 0.0);
179 // set the x-t coordinates in the BC
180 p_BOTTOM_RESIDUAL -> coord(0) = SOLN.coord(j + 1, 0).first;
181 p_BOTTOM_RESIDUAL -> coord(1) = T + DT;
182 // update the BC residuals for the current iteration
183 p_BOTTOM_RESIDUAL -> update(SOLN.get_nodes_vars(j + 1, 0));
184 // add the (linearised) bottom BCs to the matrix problem
185 for(unsigned i = 0; i < p_BOTTOM_RESIDUAL-> get_order(); ++i) {
186 // loop thru variables at LHS of the domain
187 for(unsigned var = 0; var < order; ++var) {
188 a(row, var) = p_BOTTOM_RESIDUAL -> jacobian()(i, var);
189 }
190 b[ row ] = -p_BOTTOM_RESIDUAL -> residual()[ i ];
191 ++row;
192 }
193 // inner nodes of the mesh, node = 0,1,2,...,ny-2
194 for(std::size_t node = 0; node <= ny - 2; ++node) {
195 // bottom and top nodes of the mid-y-point we're considering
196 const std::size_t b_node = node;
197 const std::size_t t_node = node + 1;
198 //
199 const double inv_dy = 1. / (SOLN.coord(j, t_node).second - SOLN.coord(j, b_node).second);
200 // work out state, state_dx, state_dt, state_dy ... all at the mid-point in x,y,t.
201 for(unsigned var = 0; var < order; ++var) {
202 const double Fj_midpt = (SOLN(j, b_node, var) + SOLN(j, t_node, var)) / 2;
203 const double Oj_midpt = (PREV_SOLN(j, b_node, var) + PREV_SOLN(j, t_node, var)) / 2;
204 const double Fjp1_midpt = (SOLN(j + 1, b_node, var) + SOLN(j + 1, t_node, var)) / 2;
205 const double Ojp1_midpt = (PREV_SOLN(j + 1, b_node, var) + PREV_SOLN(j + 1, t_node, var)) / 2;
206 state[ var ] = (Fj_midpt + Fjp1_midpt + Oj_midpt + Ojp1_midpt) / 4;
207 state_dx[ var ] = (Fjp1_midpt - Fj_midpt + Ojp1_midpt - Oj_midpt) * inv_dx / 2;
208 state_dt[ var ] = (Fjp1_midpt + Fj_midpt - Ojp1_midpt - Oj_midpt) * inv_dt / 2;
209 state_dy[ var ] = (SOLN(j + 1, t_node, var) - SOLN(j + 1, b_node, var)
210 + SOLN(j, t_node, var) - SOLN(j, b_node, var)
211 + PREV_SOLN(j + 1, t_node, var) - PREV_SOLN(j + 1, b_node, var)
212 + PREV_SOLN(j, t_node, var) - PREV_SOLN(j, b_node, var)) * inv_dy / 4;
213 }
214 //
215 double y_midpt = 0.5 * (SOLN.coord(j, b_node).second + SOLN.coord(j, t_node).second);
216 // set the coord in the equation object
217 p_EQUATION -> coord(0) = y_midpt;
218 p_EQUATION -> coord(1) = T + DT / 2.;
219 p_EQUATION -> coord(2) = x_midpt;
220 // Update the equation to the mid point position
221 p_EQUATION -> update(state);
222 // evaluate the Jacobian of mass contribution multiplied by state_dy
223 p_EQUATION -> get_jacobian_of_matrix0_mult_vector(state, state_dy, h0);
224 // evaluate the Jacobian of mass contribution multiplied by state_dt
225 p_EQUATION -> get_jacobian_of_matrix1_mult_vector(state, state_dt, h1);
226 // evaluate the Jacobian of mass contribution multiplied by state_dx
227 p_EQUATION -> get_jacobian_of_matrix2_mult_vector(state, state_dx, h2);
228 // loop over all the variables
229 // il = position for (0, b_node*order)
230 typename BandedMatrix<_Type>::elt_iter b_iter(a.get_elt_iter(0, b_node * order));
231 // ir = position for (0, t_node*order)
232 typename BandedMatrix<_Type>::elt_iter t_iter(a.get_elt_iter(0, t_node * order));
233 for(unsigned var = 0; var < order; ++var) {
234 // add the matrix mult terms to the linearised problem
235 for(unsigned i = 0; i < order; ++i) { // dummy index
236 const std::size_t offset(i * a.noffdiag() * 3 + row);
237 typename BandedMatrix<_Type>::elt_iter bottom(b_iter + offset);
238 typename BandedMatrix<_Type>::elt_iter top(t_iter + offset);
239 // add the Jacobian terms
240 //a( row, order * b_node + i ) -= p_EQUATION -> jacobian()( var, i ) / 2;
241 *bottom -= p_EQUATION -> jacobian()(var, i) / 2;
242 //a( row, order * t_node + i ) -= p_EQUATION -> jacobian()( var, i ) / 2;
243 *top -= p_EQUATION -> jacobian()(var, i) / 2;
244 // add the Jacobian of matrix terms
245 // indirect access: a( row, order * l_node + i ) += h1( var, i ) * 0.5;
246 *bottom += h0(var, i) * 0.5;
247 // indirect access: a( row, order * r_node + i ) += h1( var, i ) * 0.5;
248 *top += h0(var, i) * 0.5;
249 //a( row, order * b_node + i ) += h1( var, i ) * .5;
250 *bottom += h1(var, i) * .5;
251 //a( row, order * t_node + i ) += h1( var, i ) * .5;
252 *top += h1(var, i) * .5;
253 // add the Jacobian of matrix terms
254 //a( row, order * b_node + i ) += h2( var, i ) * .5;
255 *bottom += h2(var, i) * .5;
256 //a( row, order * t_node + i ) += h2( var, i ) * .5;
257 *top += h2(var, i) * .5;
258 // add the matrix terms
259
260 // indirect access: a( row, order * l_node + i ) -= mass_midpt( var, i ) * inv_dy;
261 *bottom -= p_EQUATION -> matrix0()(var, i) * inv_dy;
262 // indirect access: a( row, order * r_node + i ) += mass_midpt( var, i ) * inv_dy;
263 *top += p_EQUATION -> matrix0()(var, i) * inv_dy;
264
265 //a( row, order * b_node + i ) += p_EQUATION -> mass2()( var, i ) * inv_dt;
266 *bottom += p_EQUATION -> matrix1()(var, i) * inv_dt;
267 //a( row, order * t_node + i ) += p_EQUATION -> mass2()( var, i ) * inv_dt;
268 *top += p_EQUATION -> matrix1()(var, i) * inv_dt;
269 //a( row, order * b_node + i ) += p_EQUATION -> mass1()( var, i ) * inv_dx;
270 *bottom += p_EQUATION -> matrix2()(var, i) * inv_dx;
271 //a( row, order * t_node + i ) += p_EQUATION -> mass1()( var, i ) * inv_dx;
272 *top += p_EQUATION -> matrix2()(var, i) * inv_dx;
273 }
274 // RHS
275 b[ row ] = p_EQUATION -> residual()[ var ];
276 b[ row ] -= Utility::dot(p_EQUATION -> matrix0()[ var ], state_dy);
277 b[ row ] -= Utility::dot(p_EQUATION -> matrix1()[ var ], state_dt);
278 b[ row ] -= Utility::dot(p_EQUATION -> matrix2()[ var ], state_dx);
279 b[ row ] *= 4;
280 // increment the row
281 row += 1;
282 }
283 }
284 // set the x-t coordinates in the BC
285 p_TOP_RESIDUAL -> coord(0) = SOLN.coord(j + 1, ny - 1).first;
286 p_TOP_RESIDUAL -> coord(1) = T + DT;
287 // update the BC residuals for the current iteration
288 p_TOP_RESIDUAL -> update(SOLN.get_nodes_vars(j + 1, ny - 1));
289 // add the (linearised) RHS BCs to the matrix problem
290 for(unsigned i = 0; i < p_TOP_RESIDUAL -> get_order(); ++i) {
291 // loop thru variables at RHS of the domain
292 for(unsigned var = 0; var < order; ++var) {
293 a(row, order * (ny - 1) + var) = p_TOP_RESIDUAL -> jacobian()(i, var);
294 }
295 b[ row ] = - p_TOP_RESIDUAL -> residual()[ i ];
296 ++row;
297 }
298#ifdef PARANOID
299 if(row != ny * order) {
300 std::string problem("\n The ODE_BVP has an incorrect number of boundary conditions. \n");
301 throw ExceptionRuntime(problem);
302 }
303#endif
304 }
305
306 // the templated versions that we require are:
307 template class PDE_double_IBVP<double>
308 ;
309
310} // end namespace
311
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 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 templated object for real/complex vector system of unsteady equations.
PDE_double_IBVP(Equation_3matrix< _Type > *equation_ptr, const DenseVector< double > &xnodes, const DenseVector< double > &ynodes, Residual_with_coords< _Type > *ptr_to_bottom_residual, Residual_with_coords< _Type > *ptr_to_top_residual)
The class is defined by a vector function for the system.
void step2(const double &dt)
A Crank-Nicolson 'time' stepper.
A base class to be inherited by objects that define residuals.
A simple CPU-clock-tick timer for timing metods.
Definition: Timer.h:19
A two dimensional mesh utility object.
_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