Cantera  3.2.0a1
Loading...
Searching...
No Matches
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
11
12#include <fstream>
13#include <ctime>
14
15using namespace std;
16
17namespace Cantera
18{
19
21{
22 m_newt = make_unique<MultiNewton>(1);
23}
24
25OneDim::OneDim(vector<shared_ptr<Domain1D>>& domains)
26{
27 // create a Newton iterator, and add each domain.
28 m_newt = make_unique<MultiNewton>(1);
29 m_state = make_shared<vector<double>>();
30 for (auto& dom : domains) {
31 addDomain(dom);
32 }
33 init();
34 resize();
35}
36
37OneDim::~OneDim()
38{
39}
40
41size_t OneDim::domainIndex(const string& name)
42{
43 for (size_t n = 0; n < m_dom.size(); n++) {
44 if (domain(n).id() == name) {
45 return n;
46 }
47 }
48 throw CanteraError("OneDim::domainIndex","no domain named >>"+name+"<<");
49}
50
51std::tuple<string, size_t, string> OneDim::component(size_t i) {
52 size_t n;
53 for (n = nDomains()-1; n != npos; n--) {
54 if (i >= start(n)) {
55 break;
56 }
57 }
58 Domain1D& dom = domain(n);
59 size_t offset = i - start(n);
60 size_t pt = offset / dom.nComponents();
61 size_t comp = offset - pt*dom.nComponents();
62 return make_tuple(dom.id(), pt, dom.componentName(comp));
63}
64
65void OneDim::addDomain(shared_ptr<Domain1D> d)
66{
67 // if 'd' is not the first domain, link it to the last domain
68 // added (the rightmost one)
69 size_t n = m_dom.size();
70 if (n > 0) {
71 m_dom.back()->append(d.get());
72 }
73
74 // every other domain is a connector
75 if (n % 2 == 0) {
76 m_connect.push_back(d);
77 } else {
78 m_bulk.push_back(d);
79 }
80
81 // add it also to the global domain list, and set its container and position
82 m_dom.push_back(d);
83 d->setData(m_state);
84 d->setContainer(this, m_dom.size()-1);
85 resize();
86}
87
89{
90 warn_deprecated("OneDim::jacobian",
91 "Replaced by getJacobian. To be removed after Cantera 3.2.");
92 auto multijac = dynamic_pointer_cast<MultiJac>(m_jac);
93 if (multijac) {
94 return *multijac;
95 } else {
96 throw CanteraError("OneDim::jacobian", "Active Jacobian is not a MultiJac");
97 }
98}
99
101{
102 return *m_newt;
103}
104
105void OneDim::setLinearSolver(shared_ptr<SystemJacobian> solver)
106{
107 m_jac = solver;
108 m_jac->initialize(size());
109 m_jac->setBandwidth(bandwidth());
110 m_jac->clearStats();
111 m_jac_ok = false;
112}
113
114void OneDim::setJacAge(int ss_age, int ts_age)
115{
116 m_ss_jac_age = ss_age;
117 if (ts_age > 0) {
118 m_ts_jac_age = ts_age;
119 } else {
121 }
122}
123
124void OneDim::writeStats(int printTime)
125{
126 saveStats();
127 writelog("\nStatistics:\n\n Grid Timesteps Functions Time Jacobians Time\n");
128 size_t n = m_gridpts.size();
129 for (size_t i = 0; i < n; i++) {
130 if (printTime) {
131 writelog("{:5d} {:5d} {:6d} {:9.4f} {:5d} {:9.4f}\n",
133 m_jacEvals[i], m_jacElapsed[i]);
134 } else {
135 writelog("{:5d} {:5d} {:6d} NA {:5d} NA\n",
137 }
138 }
139}
140
142{
143 if (m_jac) {
144 int nev = m_jac->nEvals();
145 if (nev > 0 && m_nevals > 0) {
146 m_gridpts.push_back(m_pts);
147 m_jacEvals.push_back(m_jac->nEvals());
148 m_jacElapsed.push_back(m_jac->elapsedTime());
149 m_funcEvals.push_back(m_nevals);
150 m_nevals = 0;
151 m_funcElapsed.push_back(m_evaltime);
152 m_evaltime = 0.0;
153 m_timeSteps.push_back(m_nsteps);
154 m_nsteps = 0;
155 }
156 }
157}
158
160{
161 m_gridpts.clear();
162 m_jacEvals.clear();
163 m_jacElapsed.clear();
164 m_funcEvals.clear();
165 m_funcElapsed.clear();
166 m_timeSteps.clear();
167 m_nevals = 0;
168 m_evaltime = 0.0;
169 m_nsteps = 0;
170}
171
173{
174 m_bw = 0;
175 m_nvars.clear();
176 m_loc.clear();
177 size_t lc = 0;
178
179 // save the statistics for the last grid
180 saveStats();
181 m_pts = 0;
182 for (size_t i = 0; i < nDomains(); i++) {
183 const auto& d = m_dom[i];
184
185 size_t np = d->nPoints();
186 size_t nv = d->nComponents();
187 for (size_t n = 0; n < np; n++) {
188 m_nvars.push_back(nv);
189 m_loc.push_back(lc);
190 lc += nv;
191 m_pts++;
192 }
193
194 // update the Jacobian bandwidth
195
196 // bandwidth of the local block
197 size_t bw1 = d->bandwidth();
198 if (bw1 == npos) {
199 bw1 = std::max<size_t>(2*d->nComponents(), 1) - 1;
200 }
201 m_bw = std::max(m_bw, bw1);
202
203 // bandwidth of the block coupling the first point of this
204 // domain to the last point of the previous domain
205 if (i > 0) {
206 size_t bw2 = m_dom[i-1]->bandwidth();
207 if (bw2 == npos) {
208 bw2 = m_dom[i-1]->nComponents();
209 }
210 bw2 += d->nComponents() - 1;
211 m_bw = std::max(m_bw, bw2);
212 }
213 m_size = d->loc() + d->size();
214 }
215
216 m_state->resize(size());
217
218 m_newt->resize(size());
219 m_mask.resize(size());
220
221 if (!m_jac) {
222 m_jac = newSystemJacobian("banded-direct");
223 }
224 m_jac->initialize(size());
225 m_jac->setBandwidth(bandwidth());
226 m_jac->clearStats();
227 m_jac_ok = false;
228}
229
230int OneDim::solve(double* x, double* xnew, int loglevel)
231{
232 if (!m_jac_ok) {
233 evalJacobian(x);
234 m_jac->updateTransient(m_rdt, m_mask.data());
235 m_jac_ok = true;
236 }
237 return m_newt->solve(x, xnew, *this, loglevel);
238}
239
240void OneDim::evalSSJacobian(double* x, double* rsd)
241{
242 double rdt_save = m_rdt;
243 m_jac_ok = false;
245 evalJacobian(x);
246 m_rdt = rdt_save;
247}
248
250{
251 Domain1D* d = right();
252 while (d) {
253 if (d->loc() <= i) {
254 return d;
255 }
256 d = d->left();
257 }
258 return 0;
259}
260
261void OneDim::eval(size_t j, double* x, double* r, double rdt, int count)
262{
263 clock_t t0 = clock();
264 if (m_interrupt) {
266 }
267 fill(r, r + m_size, 0.0);
268 if (j == npos) {
269 fill(m_mask.begin(), m_mask.end(), 0);
270 }
271 if (rdt < 0.0) {
272 rdt = m_rdt;
273 }
274
275 // iterate over the bulk domains first
276 for (const auto& d : m_bulk) {
277 d->eval(j, x, r, m_mask.data(), rdt);
278 }
279
280 // then over the connector domains
281 for (const auto& d : m_connect) {
282 d->eval(j, x, r, m_mask.data(), rdt);
283 }
284
285 // increment counter and time
286 if (count) {
287 clock_t t1 = clock();
288 m_evaltime += double(t1 - t0)/CLOCKS_PER_SEC;
289 m_nevals++;
290 }
291}
292
293void OneDim::evalJacobian(double* x0)
294{
295 m_jac->reset();
296 clock_t t0 = clock();
297 m_work1.resize(size());
298 m_work2.resize(size());
299 eval(npos, x0, m_work1.data(), 0.0, 0);
300 size_t ipt = 0;
301 for (size_t j = 0; j < points(); j++) {
302 size_t nv = nVars(j);
303 for (size_t n = 0; n < nv; n++) {
304 // perturb x(n); preserve sign(x(n))
305 double xsave = x0[ipt];
306 double dx = fabs(xsave) * m_jacobianRelPerturb + m_jacobianAbsPerturb;
307 if (xsave < 0) {
308 dx = -dx;
309 }
310 x0[ipt] = xsave + dx;
311 double rdx = 1.0 / (x0[ipt] - xsave);
312
313 // calculate perturbed residual
314 eval(j, x0, m_work2.data(), 0.0, 0);
315
316 // compute nth column of Jacobian
317 for (size_t i = j - 1; i != j+2; i++) {
318 if (i != npos && i < points()) {
319 size_t mv = nVars(i);
320 size_t iloc = loc(i);
321 for (size_t m = 0; m < mv; m++) {
322 double delta = m_work2[m+iloc] - m_work1[m+iloc];
323 if (std::abs(delta) > m_jacobianThreshold || m+iloc == ipt) {
324 m_jac->setValue(m + iloc, ipt, delta * rdx);
325 }
326 }
327 }
328 }
329 x0[ipt] = xsave;
330 ipt++;
331 }
332 }
333
334 m_jac->updateElapsed(double(clock() - t0) / CLOCKS_PER_SEC);
335 m_jac->incrementEvals();
336 m_jac->setAge(0);
337}
338
339double OneDim::ssnorm(double* x, double* r)
340{
341 eval(npos, x, r, 0.0, 0);
342 double ss = 0.0;
343 for (size_t i = 0; i < m_size; i++) {
344 ss = std::max(fabs(r[i]),ss);
345 }
346 return ss;
347}
348
349void OneDim::initTimeInteg(double dt, double* x)
350{
351 double rdt_old = m_rdt;
352 m_rdt = 1.0/dt;
353
354 // if the stepsize has changed, then update the transient part of the
355 // Jacobian
356 if (fabs(rdt_old - m_rdt) > Tiny) {
357 m_jac->updateTransient(m_rdt, m_mask.data());
358 }
359
360 // iterate over all domains, preparing each one to begin time stepping
361 Domain1D* d = left();
362 while (d) {
363 d->initTimeInteg(dt, x);
364 d = d->right();
365 }
366}
367
369{
370 if (m_rdt == 0) {
371 return;
372 }
373
374 m_rdt = 0.0;
375 m_jac->updateTransient(m_rdt, m_mask.data());
376
377 // iterate over all domains, preparing them for steady-state solution
378 Domain1D* d = left();
379 while (d) {
380 d->setSteadyMode();
381 d = d->right();
382 }
383}
384
386{
387 if (!m_init) {
388 Domain1D* d = left();
389 while (d) {
390 d->init();
391 d = d->right();
392 }
393 }
394 m_init = true;
395}
396
397double OneDim::timeStep(int nsteps, double dt, double* x, double* r, int loglevel)
398{
399 // set the Jacobian age parameter to the transient value
401
402 int n = 0;
403 int successiveFailures = 0;
404
405 // Only output this if nothing else under this function call will be output
406 if (loglevel == 1) {
407 writelog("\n============================");
408 writelog("\n{:<5s} {:<11s} {:<7s}\n", "step", "dt (s)", "log(ss)");
409 writelog("============================");
410 }
411 while (n < nsteps) {
412 if (loglevel == 1) { // At level 1, output concise information
413 double ss = ssnorm(x, r);
414 writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss));
415 } else if (loglevel > 1) {
416 double ss = ssnorm(x, r);
417 writelog("\nTimestep ({}) dt= {:<11.4e} log(ss)= {:<7.4f}", n, dt, log10(ss));
418 }
419
420 // set up for time stepping with stepsize dt
421 initTimeInteg(dt,x);
422
423 int j0 = m_jac->nEvals(); // Store the current number of Jacobian evaluations
424
425 // solve the transient problem
426 int status = solve(x, r, loglevel);
427
428 // successful time step. Copy the new solution in r to
429 // the current solution in x.
430 if (status >= 0) {
431 if (loglevel > 1) {
432 writelog("\nTimestep ({}) succeeded", n);
433 }
434 successiveFailures = 0;
435 m_nsteps++;
436 n += 1;
437 copy(r, r + m_size, x);
438 // No Jacobian evaluations were performed, so a larger timestep can be used
439 if (m_jac->nEvals() == j0) {
440 dt *= 1.5;
441 }
444 }
445 dt = std::min(dt, m_tmax);
446 if (m_nsteps >= m_nsteps_max) {
447 throw CanteraError("OneDim::timeStep",
448 "Took maximum number of timesteps allowed ({}) without "
449 "reaching steady-state solution.", m_nsteps_max);
450 }
451 } else {
452 // No solution could be found with this time step.
453 // Decrease the stepsize and try again.
454 successiveFailures++;
455 if (loglevel == 1) {
456 writelog("\nTimestep failed");
457 } else if (loglevel > 1) {
458 writelog("\nTimestep ({}) failed", n);
459 }
460 if (successiveFailures > 2) {
461 debuglog("--> Resetting negative species concentrations", loglevel);
463 successiveFailures = 0;
464 } else {
465 debuglog("--> Reducing timestep", loglevel);
466 dt *= m_tfactor;
467 if (dt < m_tmin) {
468 string err_msg = fmt::format(
469 "Time integration failed. Minimum timestep ({}) reached.", m_tmin);
470 throw CanteraError("OneDim::timeStep", err_msg);
471 }
472 }
473 }
474 }
475
476 // Write the final step to the log
477 if (loglevel == 1) {
478 double ss = ssnorm(x, r);
479 writelog("\n{:<5d} {:<6.4e} {:>7.4f}", n, dt, log10(ss));
480 writelog("\n============================");
481 } else if (loglevel > 1) {
482 double ss = ssnorm(x, r);
483 writelog("\nTimestep ({}) dt= {:<11.4e} log10(ss)= {:<7.4f}\n", n, dt, log10(ss));
484 }
485
486 // return the value of the last stepsize, which may be smaller
487 // than the initial stepsize
488 return dt;
489}
490
492{
493 for (auto dom : m_dom) {
494 dom->resetBadValues(x);
495 }
496}
497
498}
Base class for exceptions thrown by Cantera classes.
Base class for one-dimensional domains.
Definition Domain1D.h:29
size_t nComponents() const
Number of components at each grid point.
Definition Domain1D.h:148
Domain1D * left() const
Return a pointer to the left neighbor.
Definition Domain1D.h:451
string id() const
Returns the identifying tag for this domain.
Definition Domain1D.h:471
Domain1D * right() const
Return a pointer to the right neighbor.
Definition Domain1D.h:456
virtual string componentName(size_t n) const
Name of component n. May be overloaded.
Definition Domain1D.cpp:67
virtual void init()
Initialize.
Definition Domain1D.h:126
void initTimeInteg(double dt, const double *x0)
Performs the setup required before starting a time-stepping solution.
Definition Domain1D.h:282
void setSteadyMode()
Set the internally-stored reciprocal of the time step to 0.0, which is used to indicate that the prob...
Definition Domain1D.h:291
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:410
virtual double eval(double t) const
Evaluate the function.
Definition Func1.cpp:28
Class MultiJac evaluates the Jacobian of a system of equations defined by a residual function supplie...
Definition MultiJac.h:25
Newton iterator for multi-domain, one-dimensional problems.
Definition MultiNewton.h:24
void setOptions(int maxJacAge=5)
Set options.
int solve(double *x0, double *x1, int loglevel)
Solve F(x) = 0, where F(x) is the multi-domain residual function.
Definition OneDim.cpp:230
size_t start(size_t i) const
The index of the start of domain i in the solution vector.
Definition OneDim.h:107
int m_nsteps
Number of time steps taken in the current call to solve()
Definition OneDim.h:448
void init()
Initialize all domains.
Definition OneDim.cpp:385
size_t m_size
solution vector size
Definition OneDim.h:407
int m_nsteps_max
Maximum number of timesteps allowed per call to solve()
Definition OneDim.h:451
virtual void resize()
Call after one or more grids has changed size, for example after being refined.
Definition OneDim.cpp:172
void resetBadValues(double *x)
Reset values such as negative species concentrations in each domain.
Definition OneDim.cpp:491
void saveStats()
Save statistics on function and Jacobian evaluation, and reset the counters.
Definition OneDim.cpp:141
unique_ptr< MultiNewton > m_newt
Newton iterator.
Definition OneDim.h:402
double m_jacobianAbsPerturb
Absolute perturbation of each component in finite difference Jacobian.
Definition OneDim.h:458
size_t size() const
Total solution vector length;.
Definition OneDim.h:118
size_t loc(size_t jg)
Location in the solution vector of the first component of global point jg.
Definition OneDim.h:138
void eval(size_t j, double *x, double *r, double rdt=-1.0, int count=1)
Evaluate the multi-domain residual function.
Definition OneDim.cpp:261
double ssnorm(double *x, double *r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x.
Definition OneDim.cpp:339
void addDomain(shared_ptr< Domain1D > d)
Add a domain. Domains are added left-to-right.
Definition OneDim.cpp:65
double rdt() const
Reciprocal of the time step.
Definition OneDim.h:171
void initTimeInteg(double dt, double *x)
Prepare for time stepping beginning with solution x and timestep dt.
Definition OneDim.cpp:349
size_t nDomains() const
Number of domains.
Definition OneDim.h:76
vector< double > m_jacElapsed
Time [s] spent evaluating Jacobians on this grid.
Definition OneDim.h:469
Domain1D * right()
Pointer to right-most domain (last added).
Definition OneDim.h:128
std::tuple< string, size_t, 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:51
size_t bandwidth() const
Jacobian bandwidth.
Definition OneDim.h:147
double m_rdt
reciprocal of time step
Definition OneDim.h:403
double m_jacobianThreshold
Threshold for ignoring small elements in Jacobian.
Definition OneDim.h:454
shared_ptr< SystemJacobian > m_jac
Jacobian evaluator.
Definition OneDim.h:401
shared_ptr< vector< double > > m_state
Solution vector.
Definition OneDim.h:399
vector< double > m_funcElapsed
Time [s] spent on residual function evaluations on this grid (not counting evaluations used to constr...
Definition OneDim.h:477
vector< shared_ptr< Domain1D > > m_connect
All connector and boundary domains.
Definition OneDim.h:416
vector< int > m_mask
Transient mask. See transientMask().
Definition OneDim.h:433
Func1 * m_interrupt
Function called at the start of every call to eval.
Definition OneDim.h:442
vector< shared_ptr< Domain1D > > m_bulk
All bulk/flow domains.
Definition OneDim.h:419
vector< int > m_funcEvals
Number of residual function evaluations on this grid (not counting evaluations used to construct Jaco...
Definition OneDim.h:473
vector< size_t > m_loc
Location in the state vector of the first component of each point, across all domains.
Definition OneDim.h:430
double m_evaltime
Total time [s] spent in eval()
Definition OneDim.h:465
size_t nVars(size_t jg)
Number of solution components at global point jg.
Definition OneDim.h:133
size_t m_bw
Jacobian bandwidth.
Definition OneDim.h:406
double m_tfactor
factor time step is multiplied by if time stepping fails ( < 1 )
Definition OneDim.h:397
bool m_jac_ok
if true, Jacobian is current
Definition OneDim.h:404
int m_ts_jac_age
Maximum age of the Jacobian in time-stepping mode.
Definition OneDim.h:439
size_t domainIndex(const string &name)
Get the index of the domain named name.
Definition OneDim.cpp:41
double timeStep(int nsteps, double dt, double *x, double *r, int loglevel)
Take time steps using Backward Euler.
Definition OneDim.cpp:397
Domain1D * pointDomain(size_t i)
Return a pointer to the domain global point i belongs to.
Definition OneDim.cpp:249
void setJacAge(int ss_age, int ts_age=-1)
Set the maximum number of steps that can be taken using the same Jacobian before it must be re-evalua...
Definition OneDim.cpp:114
vector< size_t > m_gridpts
Number of grid points in this grid.
Definition OneDim.h:467
void evalSSJacobian(double *x, double *rsd)
Evaluate the steady-state Jacobian, accessible via jacobian()
Definition OneDim.cpp:240
void evalJacobian(double *x0)
Evaluates the Jacobian at x0 using finite differences.
Definition OneDim.cpp:293
double m_tmin
minimum timestep size
Definition OneDim.h:393
size_t points()
Total number of points.
Definition OneDim.h:159
double m_tmax
maximum timestep size
Definition OneDim.h:394
int m_ss_jac_age
Maximum age of the Jacobian in steady-state mode.
Definition OneDim.h:438
vector< size_t > m_nvars
Number of variables at each point, across all domains.
Definition OneDim.h:426
int m_nevals
Number of calls to eval()
Definition OneDim.h:464
bool m_init
Indicates whether one-time initialization for each domain has been completed.
Definition OneDim.h:422
void writeStats(int printTime=1)
Write statistics about the number of iterations and Jacobians at each grid level.
Definition OneDim.cpp:124
void clearStats()
Clear saved statistics.
Definition OneDim.cpp:159
void setSteadyMode()
Prepare to solve the steady-state problem.
Definition OneDim.cpp:368
void setLinearSolver(shared_ptr< SystemJacobian > solver)
Set the linear solver used to hold the Jacobian matrix and solve linear systems as part of each Newto...
Definition OneDim.cpp:105
size_t m_pts
Total number of points.
Definition OneDim.h:436
OneDim()
Default constructor.
Definition OneDim.cpp:20
vector< int > m_jacEvals
Number of Jacobian evaluations on this grid.
Definition OneDim.h:468
Func1 * m_time_step_callback
User-supplied function called after each successful timestep.
Definition OneDim.h:445
vector< int > m_timeSteps
Number of time steps taken in each call to solve() (for example, for each successive grid refinement)
Definition OneDim.h:481
Domain1D & domain(size_t i) const
Return a reference to domain i.
Definition OneDim.h:81
vector< shared_ptr< Domain1D > > m_dom
All domains comprising the system.
Definition OneDim.h:413
double m_jacobianRelPerturb
Relative perturbation of each component in finite difference Jacobian.
Definition OneDim.h:456
Domain1D * left()
Pointer to left-most domain (first added).
Definition OneDim.h:123
vector< double > m_work1
Work arrays used during Jacobian evaluation.
Definition OneDim.h:410
MultiNewton & newton()
Return a reference to the Newton iterator.
Definition OneDim.cpp:100
MultiJac & jacobian()
Return a reference to the Jacobian evaluator of an OneDim object.
Definition OneDim.cpp:88
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:154
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:171
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:180
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:173
shared_ptr< SystemJacobian > newSystemJacobian(const string &type)
Create a SystemJacobian object of the specified type.
offset
Offsets of solution components in the 1D solution array.
Definition Flow1D.h:24
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997