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