Cantera  4.0.0a2
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 <chrono>
13#include <fstream>
14
15using namespace std;
16
17namespace Cantera
18{
19
20OneDim::OneDim(span<const shared_ptr<Domain1D>> domains)
21{
22 for (const auto& dom : domains) {
23 addDomain(dom);
24 }
25 init();
26 resize();
27}
28
29size_t OneDim::domainIndex(const string& name) const
30{
31 for (size_t n = 0; n < m_dom.size(); n++) {
32 if (domain(n).id() == name) {
33 return n;
34 }
35 }
36 throw CanteraError("OneDim::domainIndex", "Domain '{}' not found", name);
37}
38
39std::tuple<string, size_t, string> OneDim::component(size_t i) const {
40 if (i >= m_size) {
41 throw IndexError("OneDim::component", "components", i, m_size);
42 }
43 const auto& [n, j, k] = m_componentInfo[i];
44 Domain1D& dom = domain(n);
45 return make_tuple(dom.id(), j, dom.componentName(k));
46}
47
48string OneDim::componentName(size_t i) const {
49 const auto& [dom, pt, comp] = component(i);
50 return fmt::format("domain {}, component {} at point {}", dom, comp, pt);
51}
52
53pair<string, string> OneDim::componentTableHeader() const
54{
55 return {"", "Domain Pt. Component"};
56}
57
58string OneDim::componentTableLabel(size_t i) const
59{
60 const auto& [dom, pt, comp] = component(i);
61 return fmt::format("{:8s} {:3d} {:<12s}", dom, pt, comp);
62}
63
64double OneDim::upperBound(size_t i) const
65{
66 const auto& [n, j, k] = m_componentInfo[i];
67 Domain1D& dom = domain(n);
68 return dom.upperBound(k);
69}
70
71double OneDim::lowerBound(size_t i) const
72{
73 const auto& [n, j, k] = m_componentInfo[i];
74 Domain1D& dom = domain(n);
75 return dom.lowerBound(k);
76}
77
78void OneDim::addDomain(shared_ptr<Domain1D> d)
79{
80 // if 'd' is not the first domain, link it to the last domain
81 // added (the rightmost one)
82 size_t n = m_dom.size();
83 if (n > 0) {
84 m_dom.back()->append(d.get());
85 }
86
87 // every other domain is a connector
88 if (n % 2 == 0) {
89 m_connect.push_back(d);
90 } else {
91 m_bulk.push_back(d);
92 }
93
94 // add it also to the global domain list, and set its container and position
95 m_dom.push_back(d);
96 d->setData(m_state);
97 d->setContainer(this, m_dom.size()-1);
98 resize();
99}
100
101double OneDim::weightedNorm(span<const double> step) const
102{
103 double sum = 0.0;
104 const double* x = m_state->data();
105 size_t nd = nDomains();
106 for (size_t n = 0; n < nd; n++) {
107 Domain1D& dom = domain(n);
108 double d_sum = 0.0;
109 size_t nv = dom.nComponents();
110 size_t np = dom.nPoints();
111 size_t dstart = start(n);
112
113 for (size_t i = 0; i < nv; i++) {
114 double esum = 0.0;
115 for (size_t j = 0; j < np; j++) {
116 esum += fabs(x[dstart + nv*j + i]);
117 }
118 double ewt = dom.rtol(i)*esum/np + dom.atol(i);
119 for (size_t j = 0; j < np; j++) {
120 double f = step[dstart + nv*j + i]/ewt;
121 d_sum += f*f;
122 }
123 }
124 sum += d_sum;
125 }
126 return sqrt(sum / size());
127}
128
129void OneDim::writeStats(int printTime)
130{
131 saveStats();
132 auto& gridpts = m_stats["grid_points"].asVector<long int>();
133 auto& steps = m_stats["steps"].asVector<long int>();
134 auto& fevals = m_stats["residual_evals"].asVector<long int>();
135 auto& ftime = m_stats["residual_time"].asVector<double>();
136 auto& jevals = m_stats["jacobian_evals"].asVector<long int>();
137 auto& jtime = m_stats["jacobian_time"].asVector<double>();
138 writelog("\nStatistics:\n\n Grid Timesteps Functions Time Jacobians Time\n");
139 for (size_t i = 0; i < gridpts.size(); i++) {
140 if (printTime) {
141 writelog("{:5d} {:5d} {:6d} {:9.4f} {:5d} {:9.4f}\n",
142 gridpts[i], steps[i], fevals[i], ftime[i], jevals[i], jtime[i]);
143 } else {
144 writelog("{:5d} {:5d} {:6d} NA {:5d} NA\n",
145 gridpts[i], steps[i], fevals[i], jevals[i]);
146 }
147 }
148}
149
151{
152 m_bw = 0;
153 m_nvars.clear();
154 m_loc.clear();
155 m_componentInfo.clear();
156 size_t lc = 0;
157
158 if (left()) {
159 left()->locate();
160 }
161
162 // save the statistics for the last grid
163 saveStats();
164 m_pts = 0;
165 for (size_t i = 0; i < nDomains(); i++) {
166 const auto& d = m_dom[i];
167
168 size_t np = d->nPoints();
169 size_t nv = d->nComponents();
170 for (size_t n = 0; n < np; n++) {
171 m_nvars.push_back(nv);
172 m_loc.push_back(lc);
173 lc += nv;
174 m_pts++;
175 for (size_t k = 0; k < nv; k++) {
176 m_componentInfo.emplace_back(i, n, k);
177 }
178 }
179
180 // update the Jacobian bandwidth
181
182 // bandwidth of the local block
183 size_t bw1 = d->bandwidth();
184 if (bw1 == npos) {
185 bw1 = std::max<size_t>(2*d->nComponents(), 1) - 1;
186 }
187 m_bw = std::max(m_bw, bw1);
188
189 // bandwidth of the block coupling the first point of this
190 // domain to the last point of the previous domain
191 if (i > 0) {
192 size_t bw2 = m_dom[i-1]->bandwidth();
193 if (bw2 == npos) {
194 bw2 = m_dom[i-1]->nComponents();
195 }
196 bw2 += d->nComponents() - 1;
197 m_bw = std::max(m_bw, bw2);
198 }
199 m_size = d->loc() + d->size();
200 }
201 if (!m_jac) {
202 m_jac = newSystemJacobian("banded-direct");
203 }
205}
206
208{
209 Domain1D* d = right();
210 while (d) {
211 if (d->loc() <= i) {
212 return d;
213 }
214 d = d->left();
215 }
216 return 0;
217}
218
219void OneDim::eval(size_t j, span<const double> x, span<double> r, double rdt, int count)
220{
221 auto t0 = std::chrono::steady_clock::now();
222 if (m_interrupt) {
224 }
225 fill(r.begin(), r.end(), 0.0);
226 if (j == npos) {
227 fill(m_mask.begin(), m_mask.end(), 0);
228 }
229 if (rdt < 0.0) {
230 rdt = m_rdt;
231 }
232
233 // iterate over the bulk domains first
234 for (const auto& d : m_bulk) {
235 d->eval(j, x, r, m_mask, rdt);
236 }
237
238 // then over the connector domains
239 for (const auto& d : m_connect) {
240 d->eval(j, x, r, m_mask, rdt);
241 }
242
243 // increment counter and time
244 if (count) {
245 m_evaltime += std::chrono::duration<double>(
246 std::chrono::steady_clock::now() - t0).count();
247 m_nevals++;
248 }
249}
250
251void OneDim::evalJacobian(span<const double> x0)
252{
253 m_jac->reset();
254 auto t0 = std::chrono::steady_clock::now();
255 m_work1.resize(size());
256 m_work2.resize(size());
257 eval(npos, x0, m_work1, 0.0, 0);
258 // In explicit "analytic" mode, fail fast if analytic evaluation cannot be
259 // delivered, before doing a full finite-difference pass. (Base residual
260 // eval above leaves each domain's thermo state valid for the capability
261 // probe.)
262 for (auto& dom : m_dom) {
263 dom->checkAnalyticJacobian();
264 }
265 vector<double> perturbed(x0.begin(), x0.end());
266 size_t ipt = 0;
267 for (size_t j = 0; j < points(); j++) {
268 size_t nv = nVars(j);
269 Domain1D* dom = pointDomain(ipt);
270 size_t jLocal = j - dom->firstPoint();
271 for (size_t n = 0; n < nv; n++) {
272 if (dom->hasAnalyticJacobian(jLocal, n)) {
273 // skip FD perturbation; analytic fill handled below
274 ipt++;
275 continue;
276 }
277 // perturb x(n); preserve sign(x(n))
278 double xsave = x0[ipt];
279 double dx = fabs(xsave) * m_jacobianRelPerturb + m_jacobianAbsPerturb;
280 if (xsave < 0) {
281 dx = -dx;
282 }
283 perturbed[ipt] = xsave + dx;
284 double rdx = 1.0 / (perturbed[ipt] - xsave);
285
286 // calculate perturbed residual
287 eval(j, perturbed, m_work2, 0.0, 0);
288
289 // compute nth column of Jacobian
290 for (size_t i = j - 1; i != j+2; i++) {
291 if (i != npos && i < points()) {
292 size_t mv = nVars(i);
293 size_t iloc = loc(i);
294 for (size_t m = 0; m < mv; m++) {
295 double delta = m_work2[m+iloc] - m_work1[m+iloc];
296 if (std::abs(delta) > m_jacobianThreshold || m+iloc == ipt) {
297 m_jac->setValue(m + iloc, ipt, delta * rdx);
298 }
299 }
300 }
301 }
302 perturbed[ipt] = xsave;
303 ipt++;
304 }
305 }
306
307 // analytic contributions for claimed columns
308 for (auto& dom : m_dom) {
309 dom->evalJacobianAnalytic(x0, *m_jac);
310 }
311
312 m_jac->updateElapsed(std::chrono::duration<double>(
313 std::chrono::steady_clock::now() - t0).count());
314 m_jac->incrementEvals();
315 m_jac->setAge(0);
316}
317
318void OneDim::initTimeInteg(double dt, span<const double> x)
319{
321 // iterate over all domains, preparing each one to begin time stepping
322 Domain1D* d = left();
323 while (d) {
324 d->initTimeInteg(dt, x);
325 d = d->right();
326 }
327}
328
330{
331 if (m_rdt == 0) {
332 return;
333 }
335 // iterate over all domains, preparing them for steady-state solution
336 Domain1D* d = left();
337 while (d) {
338 d->setSteadyMode();
339 d = d->right();
340 }
341}
342
344{
345 if (!m_init) {
346 Domain1D* d = left();
347 while (d) {
348 d->init();
349 d = d->right();
350 }
351 }
352 m_init = true;
353}
354
355void OneDim::resetBadValues(span<double> x)
356{
357 for (auto dom : m_dom) {
358 dom->resetBadValues(x);
359 }
360}
361
362}
Base class for exceptions thrown by Cantera classes.
Base class for one-dimensional domains.
Definition Domain1D.h:30
void initTimeInteg(double dt, span< const double > x0)
Performs the setup required before starting a time-stepping solution.
Definition Domain1D.h:335
size_t nComponents() const
Number of components at each grid point.
Definition Domain1D.h:143
double rtol(size_t n)
Relative tolerance of the nth component.
Definition Domain1D.h:224
Domain1D * left() const
Return a pointer to the left neighbor.
Definition Domain1D.h:615
string id() const
Returns the identifying tag for this domain.
Definition Domain1D.h:635
virtual bool hasAnalyticJacobian(size_t j, size_t n) const
Returns true if this domain computes the Jacobian column for component n at (domain-local) grid point...
Definition Domain1D.h:292
size_t nPoints() const
Number of grid points in this domain.
Definition Domain1D.h:148
double lowerBound(size_t n) const
Lower bound on the nth component.
Definition Domain1D.h:259
double upperBound(size_t n) const
Upper bound on the nth component.
Definition Domain1D.h:254
Domain1D * right() const
Return a pointer to the right neighbor.
Definition Domain1D.h:620
virtual string componentName(size_t n) const
Name of component n. May be overloaded.
Definition Domain1D.cpp:59
virtual void init()
Initialize.
Definition Domain1D.h:121
double atol(size_t n)
Absolute tolerance of the nth component.
Definition Domain1D.h:229
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:345
size_t firstPoint() const
The index of the first (that is, left-most) grid point belonging to this domain.
Definition Domain1D.h:585
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:580
void locate()
Find the index of the first grid point in this domain, and the start of its variables in the global s...
Definition Domain1D.cpp:187
virtual double eval(double t) const
Evaluate the function.
Definition Func1.cpp:28
An array index is out of range.
size_t start(size_t i) const
The index of the start of domain i in the solution vector.
Definition OneDim.h:90
void init()
Initialize all domains.
Definition OneDim.cpp:343
double weightedNorm(span< const double > step) const override
Compute the weighted norm of a step vector.
Definition OneDim.cpp:101
void resize() override
Call to set the size of internal data structures after first defining the system or if the problem si...
Definition OneDim.cpp:150
string componentName(size_t i) const override
Get the name of the i-th component of the state vector.
Definition OneDim.cpp:48
pair< string, string > componentTableHeader() const override
Get header lines describing the column names included in a component label.
Definition OneDim.cpp:53
void initTimeInteg(double dt, span< const double > x) override
Prepare for time stepping beginning with solution x and timestep dt.
Definition OneDim.cpp:318
size_t loc(size_t jg)
Location in the solution vector of the first component of global point jg.
Definition OneDim.h:116
double upperBound(size_t i) const override
Get the upper bound for global component i in the state vector.
Definition OneDim.cpp:64
void addDomain(shared_ptr< Domain1D > d)
Add a domain. Domains are added left-to-right.
Definition OneDim.cpp:78
string componentTableLabel(size_t i) const override
Get elements of the component name, aligned with the column headings given by componentTableHeader().
Definition OneDim.cpp:58
size_t nDomains() const
Number of domains.
Definition OneDim.h:59
Domain1D * right()
Pointer to right-most domain (last added).
Definition OneDim.h:106
size_t domainIndex(const string &name) const
Get the index of the domain named name.
Definition OneDim.cpp:29
OneDim()=default
Default constructor.
void setSteadyMode() override
Prepare to solve the steady-state problem.
Definition OneDim.cpp:329
vector< shared_ptr< Domain1D > > m_connect
All connector and boundary domains.
Definition OneDim.h:258
vector< std::tuple< size_t, size_t, size_t > > m_componentInfo
Domain, grid point, and component indices for each element of the global state vector.
Definition OneDim.h:276
vector< shared_ptr< Domain1D > > m_bulk
All bulk/flow domains.
Definition OneDim.h:261
vector< size_t > m_loc
Location in the state vector of the first component of each point, across all domains.
Definition OneDim.h:272
size_t nVars(size_t jg)
Number of solution components at global point jg.
Definition OneDim.h:111
void evalJacobian(span< const double > x0) override
Evaluates the Jacobian at x0 using finite differences.
Definition OneDim.cpp:251
std::tuple< string, size_t, string > component(size_t i) const
Return the domain, local point index, and component name for the i-th component of the global solutio...
Definition OneDim.cpp:39
Domain1D * pointDomain(size_t i)
Return a pointer to the domain global point i belongs to.
Definition OneDim.cpp:207
size_t points()
Total number of points.
Definition OneDim.h:138
vector< size_t > m_nvars
Number of variables at each point, across all domains.
Definition OneDim.h:268
bool m_init
Indicates whether one-time initialization for each domain has been completed.
Definition OneDim.h:264
void writeStats(int printTime=1)
Write statistics about the number of iterations and Jacobians at each grid level.
Definition OneDim.cpp:129
size_t m_pts
Total number of points.
Definition OneDim.h:279
void resetBadValues(span< double > x) override
Reset values such as negative species concentrations.
Definition OneDim.cpp:355
Domain1D & domain(size_t i) const
Return a reference to domain i.
Definition OneDim.h:64
vector< shared_ptr< Domain1D > > m_dom
All domains comprising the system.
Definition OneDim.h:255
Domain1D * left()
Pointer to left-most domain (first added).
Definition OneDim.h:101
double lowerBound(size_t i) const override
Get the lower bound for global component i in the state vector.
Definition OneDim.cpp:71
void eval(size_t j, span< const double > x, span< double > r, double rdt=-1.0, int count=1)
Evaluate the multi-domain residual function.
Definition OneDim.cpp:219
size_t m_size
Solution vector size
virtual void resize()
Call to set the size of internal data structures after first defining the system or if the problem si...
virtual void saveStats()
Commit statistics for the current grid into m_stats and reset the staging counters.
double m_jacobianAbsPerturb
Absolute perturbation of each component in finite difference Jacobian.
size_t size() const
Total solution vector length;.
double rdt() const
Reciprocal of the time step.
virtual void initTimeInteg(double dt, span< const double > x)
Prepare for time stepping beginning with solution x and timestep dt.
AnyMap m_stats
Committed per-grid statistics.
double m_rdt
Reciprocal of time step.
double m_jacobianThreshold
Threshold for ignoring small elements in Jacobian.
shared_ptr< SystemJacobian > m_jac
Jacobian evaluator.
shared_ptr< vector< double > > m_state
Solution vector.
vector< int > m_mask
Transient mask.
Func1 * m_interrupt
Function called at the start of every call to eval.
double m_evaltime
Wall time [s] in standalone residual evals.
size_t m_bw
Jacobian bandwidth.
int m_nevals
Standalone residual evaluations (not Jacobian fill)
virtual void setSteadyMode()
Prepare to solve the steady-state problem.
double m_jacobianRelPerturb
Relative perturbation of each component in finite difference Jacobian.
vector< double > m_work1
Work arrays used during Jacobian evaluation.
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:183
shared_ptr< SystemJacobian > newSystemJacobian(const string &type)
Create a SystemJacobian object of the specified type.