Cantera 2.6.0
OneDim.cpp
Go to the documentation of this file.
1//! @file OneDim.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8#include "cantera/base/ctml.h"
10#include "cantera/base/AnyMap.h"
11
12#include <fstream>
13#include <ctime>
14
15using namespace std;
16
17namespace Cantera
18{
19
20OneDim::OneDim()
21 : m_tmin(1.0e-16), m_tmax(1e8), m_tfactor(0.5),
22 m_rdt(0.0), m_jac_ok(false),
23 m_bw(0), m_size(0),
24 m_init(false), m_pts(0),
25 m_ss_jac_age(20), m_ts_jac_age(20),
26 m_interrupt(0), m_time_step_callback(0),
27 m_nsteps(0), m_nsteps_max(500),
28 m_nevals(0), m_evaltime(0.0)
29{
30 m_newt.reset(new MultiNewton(1));
31}
32
33OneDim::OneDim(vector<Domain1D*> domains) :
34 m_tmin(1.0e-16), m_tmax(1e8), m_tfactor(0.5),
35 m_rdt(0.0), m_jac_ok(false),
36 m_bw(0), m_size(0),
37 m_init(false),
38 m_ss_jac_age(20), m_ts_jac_age(20),
39 m_interrupt(0), m_time_step_callback(0),
40 m_nsteps(0), m_nsteps_max(500),
41 m_nevals(0), m_evaltime(0.0)
42{
43 // create a Newton iterator, and add each domain.
44 m_newt.reset(new MultiNewton(1));
45 for (size_t i = 0; i < domains.size(); i++) {
46 addDomain(domains[i]);
47 }
48 init();
49 resize();
50}
51
52OneDim::~OneDim()
53{
54}
55
56size_t OneDim::domainIndex(const std::string& name)
57{
58 for (size_t n = 0; n < m_dom.size(); n++) {
59 if (domain(n).id() == name) {
60 return n;
61 }
62 }
63 throw CanteraError("OneDim::domainIndex","no domain named >>"+name+"<<");
64}
65
66std::tuple<std::string, size_t, std::string> OneDim::component(size_t i) {
67 size_t n;
68 for (n = nDomains()-1; n != npos; n--) {
69 if (i >= start(n)) {
70 break;
71 }
72 }
73 Domain1D& dom = domain(n);
74 size_t offset = i - start(n);
75 size_t pt = offset / dom.nComponents();
76 size_t comp = offset - pt*dom.nComponents();
77 return make_tuple(dom.id(), pt, dom.componentName(comp));
78}
79
81{
82 // if 'd' is not the first domain, link it to the last domain
83 // added (the rightmost one)
84 size_t n = m_dom.size();
85 if (n > 0) {
86 m_dom.back()->append(d);
87 }
88
89 // every other domain is a connector
90 if (n % 2 == 0) {
91 m_connect.push_back(d);
92 } else {
93 m_bulk.push_back(d);
94 }
95
96 // add it also to the global domain list, and set its container and position
97 m_dom.push_back(d);
98 d->setContainer(this, m_dom.size()-1);
99 resize();
100}
101
103{
104 return *m_jac;
105}
107{
108 return *m_newt;
109}
110
111void OneDim::setJacAge(int ss_age, int ts_age)
112{
113 m_ss_jac_age = ss_age;
114 if (ts_age > 0) {
115 m_ts_jac_age = ts_age;
116 } else {
117 m_ts_jac_age = m_ss_jac_age;
118 }
119}
120
121void OneDim::writeStats(int printTime)
122{
123 saveStats();
124 writelog("\nStatistics:\n\n Grid Timesteps Functions Time Jacobians Time\n");
125 size_t n = m_gridpts.size();
126 for (size_t i = 0; i < n; i++) {
127 if (printTime) {
128 writelog("{:5d} {:5d} {:6d} {:9.4f} {:5d} {:9.4f}\n",
129 m_gridpts[i], m_timeSteps[i], m_funcEvals[i], m_funcElapsed[i],
130 m_jacEvals[i], m_jacElapsed[i]);
131 } else {
132 writelog("{:5d} {:5d} {:6d} NA {:5d} NA\n",
133 m_gridpts[i], m_timeSteps[i], m_funcEvals[i], m_jacEvals[i]);
134 }
135 }
136}
137
139{
140 if (m_jac) {
141 int nev = m_jac->nEvals();
142 if (nev > 0 && m_nevals > 0) {
143 m_gridpts.push_back(m_pts);
144 m_jacEvals.push_back(m_jac->nEvals());
145 m_jacElapsed.push_back(m_jac->elapsedTime());
146 m_funcEvals.push_back(m_nevals);
147 m_nevals = 0;
148 m_funcElapsed.push_back(m_evaltime);
149 m_evaltime = 0.0;
150 m_timeSteps.push_back(m_nsteps);
151 m_nsteps = 0;
152 }
153 }
154}
155
157{
158 m_gridpts.clear();
159 m_jacEvals.clear();
160 m_jacElapsed.clear();
161 m_funcEvals.clear();
162 m_funcElapsed.clear();
163 m_timeSteps.clear();
164 m_nevals = 0;
165 m_evaltime = 0.0;
166 m_nsteps = 0;
167}
168
170{
171 m_bw = 0;
172 m_nvars.clear();
173 m_loc.clear();
174 size_t lc = 0;
175
176 // save the statistics for the last grid
177 saveStats();
178 m_pts = 0;
179 for (size_t i = 0; i < nDomains(); i++) {
180 Domain1D* d = m_dom[i];
181
182 size_t np = d->nPoints();
183 size_t nv = d->nComponents();
184 for (size_t n = 0; n < np; n++) {
185 m_nvars.push_back(nv);
186 m_loc.push_back(lc);
187 lc += nv;
188 m_pts++;
189 }
190
191 // update the Jacobian bandwidth
192
193 // bandwidth of the local block
194 size_t bw1 = d->bandwidth();
195 if (bw1 == npos) {
196 bw1 = std::max<size_t>(2*d->nComponents(), 1) - 1;
197 }
198 m_bw = std::max(m_bw, bw1);
199
200 // bandwidth of the block coupling the first point of this
201 // domain to the last point of the previous domain
202 if (i > 0) {
203 size_t bw2 = m_dom[i-1]->bandwidth();
204 if (bw2 == npos) {
205 bw2 = m_dom[i-1]->nComponents();
206 }
207 bw2 += d->nComponents() - 1;
208 m_bw = std::max(m_bw, bw2);
209 }
210 m_size = d->loc() + d->size();
211 }
212
213 m_newt->resize(size());
214 m_mask.resize(size());
215
216 // delete the current Jacobian evaluator and create a new one
217 m_jac.reset(new MultiJac(*this));
218 m_jac_ok = false;
219
220 for (size_t i = 0; i < nDomains(); i++) {
221 m_dom[i]->setJac(m_jac.get());
222 }
223}
224
225int OneDim::solve(doublereal* x, doublereal* xnew, int loglevel)
226{
227 if (!m_jac_ok) {
228 eval(npos, x, xnew, 0.0, 0);
229 m_jac->eval(x, xnew, 0.0);
230 m_jac->updateTransient(m_rdt, m_mask.data());
231 m_jac_ok = true;
232 }
233
234 return m_newt->solve(x, xnew, *this, *m_jac, loglevel);
235}
236
237void OneDim::evalSSJacobian(doublereal* x, doublereal* xnew)
238{
239 doublereal rdt_save = m_rdt;
240 m_jac_ok = false;
242 eval(npos, x, xnew, 0.0, 0);
243 m_jac->eval(x, xnew, 0.0);
244 m_rdt = rdt_save;
245}
246
248{
249 Domain1D* d = right();
250 while (d) {
251 if (d->loc() <= i) {
252 return d;
253 }
254 d = d->left();
255 }
256 return 0;
257}
258
259void OneDim::eval(size_t j, double* x, double* r, doublereal rdt, int count)
260{
261 clock_t t0 = clock();
262 if (m_interrupt) {
263 m_interrupt->eval(m_nevals);
264 }
265 fill(r, r + m_size, 0.0);
266 if (j == npos) {
267 fill(m_mask.begin(), m_mask.end(), 0);
268 }
269 if (rdt < 0.0) {
270 rdt = m_rdt;
271 }
272
273 // iterate over the bulk domains first
274 for (const auto& d : m_bulk) {
275 d->eval(j, x, r, m_mask.data(), rdt);
276 }
277
278 // then over the connector domains
279 for (const auto& d : m_connect) {
280 d->eval(j, x, r, m_mask.data(), rdt);
281 }
282
283 // increment counter and time
284 if (count) {
285 clock_t t1 = clock();
286 m_evaltime += double(t1 - t0)/CLOCKS_PER_SEC;
287 m_nevals++;
288 }
289}
290
291doublereal OneDim::ssnorm(doublereal* x, doublereal* r)
292{
293 eval(npos, x, r, 0.0, 0);
294 doublereal ss = 0.0;
295 for (size_t i = 0; i < m_size; i++) {
296 ss = std::max(fabs(r[i]),ss);
297 }
298 return ss;
299}
300
301void OneDim::initTimeInteg(doublereal dt, doublereal* x)
302{
303 doublereal rdt_old = m_rdt;
304 m_rdt = 1.0/dt;
305
306 // if the stepsize has changed, then update the transient part of the
307 // Jacobian
308 if (fabs(rdt_old - m_rdt) > Tiny) {
309 m_jac->updateTransient(m_rdt, m_mask.data());
310 }
311
312 // iterate over all domains, preparing each one to begin time stepping
313 Domain1D* d = left();
314 while (d) {
315 d->initTimeInteg(dt, x);
316 d = d->right();
317 }
318}
319
321{
322 if (m_rdt == 0) {
323 return;
324 }
325
326 m_rdt = 0.0;
327 m_jac->updateTransient(m_rdt, m_mask.data());
328
329 // iterate over all domains, preparing them for steady-state solution
330 Domain1D* d = left();
331 while (d) {
332 d->setSteadyMode();
333 d = d->right();
334 }
335}
336
338{
339 if (!m_init) {
340 Domain1D* d = left();
341 while (d) {
342 d->init();
343 d = d->right();
344 }
345 }
346 m_init = true;
347}
348
349doublereal OneDim::timeStep(int nsteps, doublereal dt, doublereal* x,
350 doublereal* r, int loglevel)
351{
352 // set the Jacobian age parameter to the transient value
353 newton().setOptions(m_ts_jac_age);
354
355 debuglog("\n\n step size (s) log10(ss) \n", loglevel);
356 debuglog("===============================\n", loglevel);
357
358 int n = 0;
359 int successiveFailures = 0;
360
361 while (n < nsteps) {
362 if (loglevel > 0) {
363 doublereal ss = ssnorm(x, r);
364 writelog(" {:>4d} {:10.4g} {:10.4g}", n, dt, log10(ss));
365 }
366
367 // set up for time stepping with stepsize dt
368 initTimeInteg(dt,x);
369
370 // solve the transient problem
371 int m = solve(x, r, loglevel-1);
372
373 // successful time step. Copy the new solution in r to
374 // the current solution in x.
375 if (m >= 0) {
376 successiveFailures = 0;
377 m_nsteps++;
378 n += 1;
379 debuglog("\n", loglevel);
380 copy(r, r + m_size, x);
381 if (m == 100) {
382 dt *= 1.5;
383 }
386 }
387 dt = std::min(dt, m_tmax);
388 if (m_nsteps >= m_nsteps_max) {
389 throw CanteraError("OneDim::timeStep",
390 "Took maximum number of timesteps allowed ({}) without "
391 "reaching steady-state solution.", m_nsteps_max);
392 }
393 } else {
394 successiveFailures++;
395 // No solution could be found with this time step.
396 // Decrease the stepsize and try again.
397 debuglog("...failure.\n", loglevel);
398 if (successiveFailures > 2) {
399 //debuglog("Resetting negative species concentrations.\n", loglevel);
400 resetBadValues(x);
401 successiveFailures = 0;
402 } else {
403 dt *= m_tfactor;
404 if (dt < m_tmin) {
405 throw CanteraError("OneDim::timeStep",
406 "Time integration failed.");
407 }
408 }
409 }
410 }
411
412 // return the value of the last stepsize, which may be smaller
413 // than the initial stepsize
414 return dt;
415}
416
417void OneDim::resetBadValues(double* x)
418{
419 for (auto dom : m_dom) {
420 dom->resetBadValues(x);
421 }
422}
423
424void OneDim::save(const std::string& fname, std::string id,
425 const std::string& desc, doublereal* sol,
426 int loglevel)
427{
428 time_t aclock;
429 ::time(&aclock); // Get time in seconds
430 struct tm* newtime = localtime(&aclock); // Convert time to struct tm form
431
432 XML_Node root("ctml");
433 ifstream fin(fname);
434 if (fin) {
435 root.build(fin, fname);
436 // Remove existing solution with the same id
437 XML_Node* same_ID = root.findID(id);
438 if (same_ID) {
439 same_ID->parent()->removeChild(same_ID);
440 }
441 fin.close();
442 }
443 XML_Node& sim = root.addChild("simulation");
444 sim.addAttribute("id",id);
445 addString(sim,"timestamp",asctime(newtime));
446 if (desc != "") {
447 addString(sim,"description",desc);
448 }
449
450 Domain1D* d = left();
451 while (d) {
452 d->save(sim, sol);
453 d = d->right();
454 }
455 ofstream s(fname);
456 if (!s) {
457 throw CanteraError("OneDim::save","could not open file "+fname);
458 }
459 root.write(s);
460 s.close();
461 debuglog("Solution saved to file "+fname+" as solution "+id+".\n", loglevel);
462}
463
464AnyMap OneDim::serialize(const double* soln) const
465{
466 AnyMap state;
467 for (size_t i = 0; i < m_dom.size(); i++) {
468 state[m_dom[i]->id()] = m_dom[i]->serialize(soln + start(i));
469 }
470 return state;
471}
472
473}
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Base class for one-dimensional domains.
Definition: Domain1D.h:38
size_t nComponents() const
Number of components at each grid point.
Definition: Domain1D.h:133
size_t bandwidth()
Set the Jacobian bandwidth for this domain.
Definition: Domain1D.h:99
Domain1D * left() const
Return a pointer to the left neighbor.
Definition: Domain1D.h:414
void setContainer(OneDim *c, size_t index)
Specify the container object for this domain, and the position of this domain in the list.
Definition: Domain1D.h:74
size_t nPoints() const
Number of grid points in this domain.
Definition: Domain1D.h:155
virtual std::string componentName(size_t n) const
Name of the nth component. May be overloaded.
Definition: Domain1D.cpp:64
void initTimeInteg(doublereal dt, const doublereal *x0)
Prepare to do time stepping with time step dt.
Definition: Domain1D.h:255
Domain1D * right() const
Return a pointer to the right neighbor.
Definition: Domain1D.h:419
virtual void init()
Definition: Domain1D.h:108
void setSteadyMode()
Prepare to solve the steady-state problem.
Definition: Domain1D.h:264
virtual size_t loc(size_t j=0) const
Location of the start of the local solution vector in the global solution vector,.
Definition: Domain1D.h:373
virtual doublereal eval(doublereal t) const
Evaluate the function.
Definition: Func1.cpp:60
Class MultiJac evaluates the Jacobian of a system of equations defined by a residual function supplie...
Definition: MultiJac.h:23
Newton iterator for multi-domain, one-dimensional problems.
Definition: MultiNewton.h:20
void setOptions(int maxJacAge=5)
Set options.
Definition: MultiNewton.h:68
size_t start(size_t i) const
The index of the start of domain i in the solution vector.
Definition: OneDim.h:86
int m_nsteps
Number of time steps taken in the current call to solve()
Definition: OneDim.h:363
vector_int m_timeSteps
Number of time steps taken in each call to solve() (for example, for each successive grid refinement)
Definition: OneDim.h:380
doublereal m_rdt
reciprocal of time step
Definition: OneDim.h:339
size_t m_size
solution vector size
Definition: OneDim.h:343
int m_nsteps_max
Maximum number of timesteps allowed per call to solve()
Definition: OneDim.h:366
virtual void resize()
Call after one or more grids has changed size, for example after being refined.
Definition: OneDim.cpp:169
std::unique_ptr< MultiJac > m_jac
Jacobian evaluator.
Definition: OneDim.h:337
void saveStats()
Save statistics on function and Jacobian evaluation, and reset the counters.
Definition: OneDim.cpp:138
size_t size() const
Total solution vector length;.
Definition: OneDim.h:97
doublereal ssnorm(doublereal *x, doublereal *r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x.
Definition: OneDim.cpp:291
size_t nDomains() const
Number of domains.
Definition: OneDim.h:55
Domain1D * right()
Pointer to right-most domain (last added).
Definition: OneDim.h:107
std::tuple< std::string, size_t, std::string > component(size_t i)
Return the domain, local point index, and component name for the i-th component of the global solutio...
Definition: OneDim.cpp:66
doublereal m_tmin
minimum timestep size
Definition: OneDim.h:331
double timeStep(int nsteps, double dt, double *x, double *r, int loglevel)
Definition: OneDim.cpp:349
void eval(size_t j, double *x, double *r, doublereal rdt=-1.0, int count=1)
Evaluate the multi-domain residual function.
Definition: OneDim.cpp:259
Func1 * m_interrupt
Function called at the start of every call to eval.
Definition: OneDim.h:357
void initTimeInteg(doublereal dt, doublereal *x)
Prepare for time stepping beginning with solution x and timestep dt.
Definition: OneDim.cpp:301
std::unique_ptr< MultiNewton > m_newt
Newton iterator.
Definition: OneDim.h:338
size_t m_bw
Jacobian bandwidth.
Definition: OneDim.h:342
bool m_jac_ok
if true, Jacobian is current
Definition: OneDim.h:340
Domain1D * pointDomain(size_t i)
Return a pointer to the domain global point i belongs to.
Definition: OneDim.cpp:247
doublereal m_tmax
maximum timestep size
Definition: OneDim.h:332
void addDomain(Domain1D *d)
Add a domain. Domains are added left-to-right.
Definition: OneDim.cpp:80
MultiJac & jacobian()
Return a reference to the Jacobian evaluator.
Definition: OneDim.cpp:102
void writeStats(int printTime=1)
Write statistics about the number of iterations and Jacobians at each grid level.
Definition: OneDim.cpp:121
void clearStats()
Clear saved statistics.
Definition: OneDim.cpp:156
void setSteadyMode()
Definition: OneDim.cpp:320
Func1 * m_time_step_callback
User-supplied function called after each successful timestep.
Definition: OneDim.h:360
doublereal m_tfactor
factor time step is multiplied by if time stepping fails ( < 1 )
Definition: OneDim.h:335
Domain1D & domain(size_t i) const
Return a reference to domain i.
Definition: OneDim.h:60
int solve(doublereal *x0, doublereal *x1, int loglevel)
Solve F(x) = 0, where F(x) is the multi-domain residual function.
Definition: OneDim.cpp:225
Domain1D * left()
Pointer to left-most domain (first added).
Definition: OneDim.h:102
MultiNewton & newton()
Return a reference to the Newton iterator.
Definition: OneDim.cpp:106
doublereal rdt() const
Reciprocal of the time step.
Definition: OneDim.h:151
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:164
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
const double Tiny
Small number to compare differences of mole fractions against.
Definition: ct_defs.h:170
void debuglog(const std::string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition: global.h:146
void addString(XML_Node &node, const std::string &titleString, const std::string &valueString, const std::string &typeString="")
This function adds a child node with the name string with a string value to the current node.
Definition: ctml.cpp:113