Cantera  3.1.0a4
Loading...
Searching...
No Matches
Sim1D.cpp
Go to the documentation of this file.
1/**
2 * @file Sim1D.cpp
3 */
4
5// This file is part of Cantera. See License.txt in the top-level directory or
6// at https://cantera.org/license.txt for license and copyright information.
7
10#include "cantera/oneD/Flow1D.h"
12#include "cantera/oneD/refine.h"
17#include <limits>
18#include <fstream>
19
20using namespace std;
21
22namespace Cantera
23{
24
25Sim1D::Sim1D(vector<shared_ptr<Domain1D>>& domains) :
26 OneDim(domains),
27 m_steady_callback(0)
28{
29 // resize the internal solution vector and the work array, and perform
30 // domain-specific initialization of the solution vector.
31 resize();
32 for (size_t n = 0; n < nDomains(); n++) {
33 domain(n)._getInitialSoln(m_state->data() + start(n));
34 }
35
36 // set some defaults
37 m_tstep = 1.0e-5;
38 m_steps = { 10 };
39}
40
41void Sim1D::setInitialGuess(const string& component, vector<double>& locs,
42 vector<double>& vals)
43{
44 for (size_t dom=0; dom<nDomains(); dom++) {
45 Domain1D& d = domain(dom);
46 size_t ncomp = d.nComponents();
47 for (size_t comp=0; comp<ncomp; comp++) {
48 if (d.componentName(comp)==component) {
49 setProfile(dom,comp,locs,vals);
50 }
51 }
52 }
53}
54
55void Sim1D::setValue(size_t dom, size_t comp, size_t localPoint, double value)
56{
57 size_t iloc = domain(dom).loc() + domain(dom).index(comp, localPoint);
58 AssertThrowMsg(iloc < m_state->size(), "Sim1D::setValue",
59 "Index out of bounds: {} > {}", iloc, m_state->size());
60 (*m_state)[iloc] = value;
61}
62
63double Sim1D::value(size_t dom, size_t comp, size_t localPoint) const
64{
65 size_t iloc = domain(dom).loc() + domain(dom).index(comp, localPoint);
66 AssertThrowMsg(iloc < m_state->size(), "Sim1D::value",
67 "Index out of bounds: {} > {}", iloc, m_state->size());
68 return (*m_state)[iloc];
69}
70
71double Sim1D::workValue(size_t dom, size_t comp, size_t localPoint) const
72{
73 size_t iloc = domain(dom).loc() + domain(dom).index(comp, localPoint);
74 AssertThrowMsg(iloc < m_state->size(), "Sim1D::workValue",
75 "Index out of bounds: {} > {}", iloc, m_state->size());
76 return m_xnew[iloc];
77}
78
79void Sim1D::setProfile(size_t dom, size_t comp,
80 const vector<double>& pos, const vector<double>& values)
81{
82 if (pos.front() != 0.0 || pos.back() != 1.0) {
83 throw CanteraError("Sim1D::setProfile",
84 "`pos` vector must span the range [0, 1]. Got a vector spanning "
85 "[{}, {}] instead.", pos.front(), pos.back());
86 }
87 Domain1D& d = domain(dom);
88 double z0 = d.zmin();
89 double z1 = d.zmax();
90 for (size_t n = 0; n < d.nPoints(); n++) {
91 double zpt = d.z(n);
92 double frac = (zpt - z0)/(z1 - z0);
93 double v = linearInterp(frac, pos, values);
94 setValue(dom, comp, n, v);
95 }
96}
97
98void Sim1D::save(const string& fname, const string& name, const string& desc,
99 bool overwrite, int compression, const string& basis)
100{
101 size_t dot = fname.find_last_of(".");
102 string extension = (dot != npos) ? toLowerCopy(fname.substr(dot+1)) : "";
103 if (extension == "csv") {
104 for (auto dom : m_dom) {
105 auto arr = dom->asArray(m_state->data() + dom->loc());
106 if (dom->size() > 1) {
107 arr->writeEntry(fname, overwrite, basis);
108 break;
109 }
110 }
111 return;
112 }
113 if (basis != "") {
114 warn_user("Sim1D::save",
115 "Species basis '{}' not implemented for HDF5 or YAML output.", basis);
116 }
117 if (extension == "h5" || extension == "hdf" || extension == "hdf5") {
118 SolutionArray::writeHeader(fname, name, desc, overwrite);
119 for (auto dom : m_dom) {
120 auto arr = dom->asArray(m_state->data() + dom->loc());
121 arr->writeEntry(fname, name, dom->id(), overwrite, compression);
122 }
123 return;
124 }
125 if (extension == "yaml" || extension == "yml") {
126 // Check for an existing file and load it if present
127 AnyMap data;
128 if (std::ifstream(fname).good()) {
129 data = AnyMap::fromYamlFile(fname);
130 }
131 SolutionArray::writeHeader(data, name, desc, overwrite);
132
133 for (auto dom : m_dom) {
134 auto arr = dom->asArray(m_state->data() + dom->loc());
135 arr->writeEntry(data, name, dom->id(), overwrite);
136 }
137
138 // Write the output file and remove the now-outdated cached file
139 std::ofstream out(fname);
140 out << data.toYamlString();
142 return;
143 }
144 throw CanteraError("Sim1D::save", "Unsupported file format '{}'.", extension);
145}
146
147void Sim1D::saveResidual(const string& fname, const string& name,
148 const string& desc, bool overwrite, int compression)
149{
150 vector<double> res(m_state->size(), -999);
151 OneDim::eval(npos, m_state->data(), &res[0], 0.0);
152 // Temporarily put the residual into m_state, since this is the vector that the
153 // save() function reads.
154 vector<double> backup(*m_state);
155 *m_state = res;
156 save(fname, name, desc, overwrite, compression);
157 *m_state = backup;
158}
159
160namespace { // restrict scope of helper function to local translation unit
161
162//! convert data format used by Python h5py export (Cantera < 3.0)
163AnyMap legacyH5(shared_ptr<SolutionArray> arr, const AnyMap& header={})
164{
165 auto meta = arr->meta();
166 AnyMap out;
167
168 map<string, string> meta_pairs = {
169 {"type", "Domain1D_type"},
170 {"name", "name"},
171 {"emissivity-left", "emissivity_left"},
172 {"emissivity-right", "emissivity_right"},
173 };
174 for (const auto& [newName, oldName] : meta_pairs) {
175 if (meta.hasKey(oldName)) {
176 out[newName] = meta[oldName];
177 }
178 }
179
180 map<string, string> tol_pairs = {
181 {"transient-abstol", "transient_abstol"},
182 {"steady-abstol", "steady_abstol"},
183 {"transient-reltol", "transient_reltol"},
184 {"steady-reltol", "steady_reltol"},
185 };
186 for (const auto& [newName, oldName] : tol_pairs) {
187 if (meta.hasKey(oldName)) {
188 out["tolerances"][newName] = meta[oldName];
189 }
190 }
191
192 if (meta.hasKey("phase")) {
193 out["phase"]["name"] = meta["phase"]["name"];
194 out["phase"]["source"] = meta["phase"]["source"];
195 }
196
197 if (arr->size() <= 1) {
198 return out;
199 }
200
201 map<string, string> header_pairs = {
202 {"transport-model", "transport_model"},
203 {"radiation-enabled", "radiation_enabled"},
204 {"energy-enabled", "energy_enabled"},
205 {"Soret-enabled", "soret_enabled"},
206 {"species-enabled", "species_enabled"},
207 };
208 for (const auto& [newName, oldName] : header_pairs) {
209 if (header.hasKey(oldName)) {
210 out[newName] = header[oldName];
211 }
212 }
213
214 map<string, string> refiner_pairs = {
215 {"ratio", "ratio"},
216 {"slope", "slope"},
217 {"curve", "curve"},
218 {"prune", "prune"},
219 // {"grid-min", "???"}, // missing
220 {"max-points", "max_grid_points"},
221 };
222 for (const auto& [newName, oldName] : refiner_pairs) {
223 if (header.hasKey(oldName)) {
224 out["refine-criteria"][newName] = header[oldName];
225 }
226 }
227
228 if (header.hasKey("fixed_temperature")) {
229 double temp = header.getDouble("fixed_temperature", -1.);
230 auto profile = arr->getComponent("T").as<vector<double>>();
231 int ix = 0;
232 while (profile[ix] <= temp && ix < arr->size()) {
233 ix++;
234 }
235 if (ix != 0) {
236 auto grid = arr->getComponent("grid").as<vector<double>>();
237 out["fixed-point"]["location"] = grid[ix - 1];
238 out["fixed-point"]["temperature"] = temp;
239 }
240 }
241
242 return out;
243}
244
245} // end unnamed namespace
246
247AnyMap Sim1D::restore(const string& fname, const string& name)
248{
249 size_t dot = fname.find_last_of(".");
250 string extension = (dot != npos) ? toLowerCopy(fname.substr(dot+1)) : "";
251 if (extension == "xml") {
252 throw CanteraError("Sim1D::restore",
253 "Restoring from XML is no longer supported.");
254 }
255 AnyMap header;
256 if (extension == "h5" || extension == "hdf" || extension == "hdf5") {
257 map<string, shared_ptr<SolutionArray>> arrs;
258 header = SolutionArray::readHeader(fname, name);
259
260 for (auto dom : m_dom) {
261 auto arr = SolutionArray::create(dom->solution());
262 try {
263 arr->readEntry(fname, name, dom->id());
264 } catch (CanteraError& err) {
265 throw CanteraError("Sim1D::restore",
266 "Encountered exception when reading entry '{}' from '{}':\n{}",
267 name, fname, err.getMessage());
268 }
269 dom->resize(dom->nComponents(), arr->size());
270 if (!header.hasKey("generator")) {
271 arr->meta() = legacyH5(arr, header);
272 }
273 arrs[dom->id()] = arr;
274 }
275 resize();
276 m_xlast_ts.clear();
277 for (auto dom : m_dom) {
278 try {
279 dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc());
280 } catch (CanteraError& err) {
281 throw CanteraError("Sim1D::restore",
282 "Encountered exception when restoring domain '{}' from HDF:\n{}",
283 dom->id(), err.getMessage());
284 }
285 }
286 finalize();
287 } else if (extension == "yaml" || extension == "yml") {
288 AnyMap root = AnyMap::fromYamlFile(fname);
289 map<string, shared_ptr<SolutionArray>> arrs;
290 header = SolutionArray::readHeader(root, name);
291
292 for (auto dom : m_dom) {
293 auto arr = SolutionArray::create(dom->solution());
294 try {
295 arr->readEntry(root, name, dom->id());
296 } catch (CanteraError& err) {
297 throw CanteraError("Sim1D::restore",
298 "Encountered exception when reading entry '{}' from '{}':\n{}",
299 name, fname, err.getMessage());
300 }
301 dom->resize(dom->nComponents(), arr->size());
302 arrs[dom->id()] = arr;
303 }
304 resize();
305 m_xlast_ts.clear();
306 for (auto dom : m_dom) {
307 try {
308 dom->fromArray(*arrs[dom->id()], m_state->data() + dom->loc());
309 } catch (CanteraError& err) {
310 throw CanteraError("Sim1D::restore",
311 "Encountered exception when restoring domain '{}' from YAML:\n{}",
312 dom->id(), err.getMessage());
313 }
314 }
315 finalize();
316 } else {
317 throw CanteraError("Sim1D::restore",
318 "Unknown file extension '{}'; supported extensions include "
319 "'h5'/'hdf'/'hdf5' and 'yml'/'yaml'.", extension);
320 }
321 return header;
322}
323
324void Sim1D::setFlatProfile(size_t dom, size_t comp, double v)
325{
326 size_t np = domain(dom).nPoints();
327 for (size_t n = 0; n < np; n++) {
328 setValue(dom, comp, n, v);
329 }
330}
331
332void Sim1D::show(ostream& s)
333{
334 for (size_t n = 0; n < nDomains(); n++) {
335 if (domain(n).type() != "empty") {
336 domain(n).show(s, m_state->data() + start(n));
337 }
338 }
339}
340
342{
343 for (size_t n = 0; n < nDomains(); n++) {
344 if (domain(n).type() != "empty") {
345 writelog("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> "+domain(n).id()
346 +" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n");
347 domain(n).show(m_state->data() + start(n));
348 }
349 }
350}
351
353{
354 if (m_xlast_ts.empty()) {
355 throw CanteraError("Sim1D::restoreTimeSteppingSolution",
356 "No successful time steps taken on this grid.");
357 }
359}
360
362{
363 if (m_xlast_ss.empty()) {
364 throw CanteraError("Sim1D::restoreSteadySolution",
365 "No successful steady state solution");
366 }
368 for (size_t n = 0; n < nDomains(); n++) {
369 vector<double>& z = m_grid_last_ss[n];
370 domain(n).setupGrid(z.size(), z.data());
371 }
372}
373
374void Sim1D::getInitialSoln()
375{
376 for (size_t n = 0; n < nDomains(); n++) {
377 domain(n)._getInitialSoln(m_state->data() + start(n));
378 }
379}
380
382{
383 for (size_t n = 0; n < nDomains(); n++) {
384 domain(n)._finalize(m_state->data() + start(n));
385 }
386}
387
388void Sim1D::setTimeStep(double stepsize, size_t n, const int* tsteps)
389{
390 m_tstep = stepsize;
391 m_steps.resize(n);
392 for (size_t i = 0; i < n; i++) {
393 m_steps[i] = tsteps[i];
394 }
395}
396
397int Sim1D::newtonSolve(int loglevel)
398{
399 int m = OneDim::solve(m_state->data(), m_xnew.data(), loglevel);
400 if (m >= 0) {
401 *m_state = m_xnew;
402 return 0;
403 } else if (m > -10) {
404 return -1;
405 } else {
406 throw CanteraError("Sim1D::newtonSolve",
407 "ERROR: OneDim::solve returned m = {}", m);
408 }
409}
410
411void Sim1D::solve(int loglevel, bool refine_grid)
412{
413 int new_points = 1;
414 double dt = m_tstep;
415 m_nsteps = 0;
416 finalize();
417
418 // For debugging outputs
419 int attempt_counter = 0;
420 const int max_history = 10; // Store up to 10 previous solutions
421 if (loglevel > 6) {
423 }
424
425 while (new_points > 0) {
426 size_t istep = 0;
427 int nsteps = m_steps[istep];
428
429 bool ok = false;
430 if (loglevel > 0) {
431 writeline('.', 78, true, true);
432 }
433 while (!ok) {
434 // Keep the attempt_counter in the range of [1, max_history]
435 attempt_counter = (attempt_counter % max_history) + 1;
436
437 // Attempt to solve the steady problem
439 newton().setOptions(m_ss_jac_age);
440 debuglog("\nAttempt Newton solution of steady-state problem.", loglevel);
441 int status = newtonSolve(loglevel);
442
443 if (status == 0) {
444 if (loglevel > 0) {
445 writelog("\nNewton steady-state solve succeeded.\n\n");
446 writelog("Problem solved on [");
447 for (size_t mm = 1; mm < nDomains(); mm+=2) {
448 writelog("{}", domain(mm).nPoints());
449 if (mm + 2 < nDomains()) {
450 writelog(", ");
451 }
452 }
453 writelog("] point grid(s).\n");
454 }
455 if (m_steady_callback) {
457 }
458 writeDebugInfo("NewtonSuccess", "After successful Newton solve",
459 loglevel, attempt_counter);
460
461 ok = true;
462 } else {
463 debuglog("\nNewton steady-state solve failed.\n", loglevel);
464 writeDebugInfo("NewtonFail", "After unsuccessful Newton solve",
465 loglevel, attempt_counter);
466
467 if (loglevel > 0) {
468 writelog("\nAttempt {} timesteps.", nsteps);
469 }
470
471 dt = timeStep(nsteps, dt, m_state->data(), m_xnew.data(), loglevel-1);
473 writeDebugInfo("Timestepping", "After timestepping", loglevel,
474 attempt_counter);
475
476 // Repeat the last timestep's data for logging purposes
477 if (loglevel == 1) {
478 writelog("\nFinal timestep info: dt= {:<10.4g} log(ss)= {:<10.4g}\n", dt,
479 log10(ssnorm(m_state->data(), m_xnew.data())));
480 }
481 istep++;
482 if (istep >= m_steps.size()) {
483 nsteps = m_steps.back();
484 } else {
485 nsteps = m_steps[istep];
486 }
487 dt = std::min(dt, m_tmax);
488 }
489 }
490 if (loglevel > 0) {
491 writeline('.', 78, true, true);
492 }
493 if (loglevel > 3) {
494 show();
495 }
496
497 if (refine_grid) {
498 new_points = refine(loglevel);
499 if (new_points) {
500 // If the grid has changed, preemptively reduce the timestep
501 // to avoid multiple successive failed time steps.
502 dt = m_tstep;
503 }
504 writeDebugInfo("Regridding", "After regridding", loglevel, attempt_counter);
505 } else {
506 debuglog("grid refinement disabled.\n", loglevel);
507 new_points = 0;
508 }
509 }
510 if (new_points < 0) {
511 // If the solver finished after removing grid points, do one final evaluation
512 // of the governing equations to update internal arrays in each domain that may
513 // be used for data saved in output files.
514 for (auto dom : m_dom) {
515 dom->eval(npos, m_state->data() + dom->loc(), m_xnew.data() + dom->loc(),
516 m_mask.data());
517 }
518 }
519}
520
521int Sim1D::refine(int loglevel)
522{
523 int added = 0;
524 int discarded = 0;
525 vector<double> znew, xnew;
526 vector<size_t> dsize;
527
529 m_grid_last_ss.clear();
530
531 for (size_t n = 0; n < nDomains(); n++) {
532 Domain1D& d = domain(n);
533 Refiner& r = d.refiner();
534
535 // Save the old grid corresponding to the converged solution
536 m_grid_last_ss.push_back(d.grid());
537
538 // determine where new points are needed
539 r.analyze(d.grid().size(), d.grid().data(), m_state->data() + start(n));
540
541 if (loglevel > 0) {
542 r.show();
543 }
544
545 added += r.nNewPoints();
546 size_t comp = d.nComponents();
547
548 // loop over points in the current grid
549 size_t npnow = d.nPoints();
550 size_t nstart = znew.size();
551 for (size_t m = 0; m < npnow; m++) {
552 if (r.keepPoint(m)) {
553 // add the current grid point to the new grid
554 znew.push_back(d.grid(m));
555
556 // do the same for the solution at this point
557 for (size_t i = 0; i < comp; i++) {
558 xnew.push_back(value(n, i, m));
559 }
560
561 // now check whether a new point is needed in the interval to
562 // the right of point m, and if so, add entries to znew and xnew
563 // for this new point
564 if (r.newPointNeeded(m) && m + 1 < npnow) {
565 // add new point at midpoint
566 double zmid = 0.5*(d.grid(m) + d.grid(m+1));
567 znew.push_back(zmid);
568 added++;
569
570 // for each component, linearly interpolate
571 // the solution to this point
572 for (size_t i = 0; i < comp; i++) {
573 double xmid = 0.5*(value(n, i, m) + value(n, i, m+1));
574 xnew.push_back(xmid);
575 }
576 }
577 } else {
578 discarded++;
579 if (loglevel > 0) {
580 writelog("refine: discarding point at {}\n", d.grid(m));
581 }
582 }
583 }
584 dsize.push_back(znew.size() - nstart);
585 }
586
587 // At this point, the new grid znew and the new solution vector xnew have
588 // been constructed, but the domains themselves have not yet been modified.
589 // Now update each domain with the new grid.
590
591 size_t gridstart = 0, gridsize;
592 for (size_t n = 0; n < nDomains(); n++) {
593 Domain1D& d = domain(n);
594 gridsize = dsize[n];
595 if (gridsize != 0) {
596 d.setupGrid(gridsize, &znew[gridstart]);
597 }
598 gridstart += gridsize;
599 }
600
601 // Replace the current solution vector with the new one
602 *m_state = xnew;
603 resize();
604 finalize();
605 return added || -discarded;
606}
607
609{
610 std::filesystem::remove("debug_sim1d.yaml");
611}
612
613void Sim1D::writeDebugInfo(const string& header_suffix, const string& message,
614 int loglevel, int attempt_counter)
615{
616 string file_header;
617 if (loglevel > 6) {
618 file_header = fmt::format("solution_{}_{}", attempt_counter, header_suffix);
619 save("debug_sim1d.yaml", file_header, message, true);
620 }
621 if (loglevel > 7) {
622 file_header = fmt::format("residual_{}_{}", attempt_counter, header_suffix);
623 saveResidual("debug_sim1d.yaml", file_header, message, true);
624 }
625}
626
628{
629 int np = 0;
630 vector<double> znew, xnew;
631 double zfixed = 0.0;
632 double z1 = 0.0, z2 = 0.0;
633 vector<size_t> dsize;
634
635 for (size_t n = 0; n < nDomains(); n++) {
636 Domain1D& d = domain(n);
637 size_t comp = d.nComponents();
638 size_t mfixed = npos;
639
640 // loop over current grid to determine where new point is needed
641 Flow1D* d_free = dynamic_cast<Flow1D*>(&domain(n));
642 size_t npnow = d.nPoints();
643 size_t nstart = znew.size();
644 if (d_free && d_free->isFree()) {
645 for (size_t m = 0; m < npnow - 1; m++) {
646 bool fixedpt = false;
647 double t1 = value(n, c_offset_T, m);
648 double t2 = value(n, c_offset_T, m + 1);
649 // threshold to avoid adding new point too close to existing point
650 double thresh = min(1., 1.e-1 * (t2 - t1));
651 z1 = d.grid(m);
652 z2 = d.grid(m + 1);
653 if (fabs(t - t1) <= thresh) {
654 zfixed = z1;
655 fixedpt = true;
656 } else if (fabs(t2 - t) <= thresh) {
657 zfixed = z2;
658 fixedpt = true;
659 } else if ((t1 < t) && (t < t2)) {
660 mfixed = m;
661 zfixed = (z1 - z2) / (t1 - t2) * (t - t2) + z2;
662 fixedpt = true;
663 }
664
665 if (fixedpt) {
666 d_free->m_zfixed = zfixed;
667 d_free->m_tfixed = t;
668 break;
669 }
670 }
671 }
672
673 // copy solution domain and push back values
674 for (size_t m = 0; m < npnow; m++) {
675 // add the current grid point to the new grid
676 znew.push_back(d.grid(m));
677
678 // do the same for the solution at this point
679 for (size_t i = 0; i < comp; i++) {
680 xnew.push_back(value(n, i, m));
681 }
682 if (m == mfixed) {
683 // add new point at zfixed (mfixed is not npos)
684 znew.push_back(zfixed);
685 np++;
686 double interp_factor = (zfixed - z2) / (z1 - z2);
687 // for each component, linearly interpolate
688 // the solution to this point
689 for (size_t i = 0; i < comp; i++) {
690 double xmid = interp_factor*(value(n, i, m) - value(n, i, m+1)) + value(n,i,m+1);
691 xnew.push_back(xmid);
692 }
693 }
694 }
695 dsize.push_back(znew.size() - nstart);
696 }
697
698 // At this point, the new grid znew and the new solution vector xnew have
699 // been constructed, but the domains themselves have not yet been modified.
700 // Now update each domain with the new grid.
701 size_t gridstart = 0;
702 for (size_t n = 0; n < nDomains(); n++) {
703 Domain1D& d = domain(n);
704 size_t gridsize = dsize[n];
705 d.setupGrid(gridsize, &znew[gridstart]);
706 gridstart += gridsize;
707 }
708
709 // Replace the current solution vector with the new one
710 *m_state = xnew;
711
712 resize();
713 finalize();
714 return np;
715}
716
718{
719 double t_fixed = std::numeric_limits<double>::quiet_NaN();
720 for (size_t n = 0; n < nDomains(); n++) {
721 Flow1D* d = dynamic_cast<Flow1D*>(&domain(n));
722 if (d && d->isFree() && d->m_tfixed > 0) {
723 t_fixed = d->m_tfixed;
724 break;
725 }
726 }
727 return t_fixed;
728}
729
731{
732 double z_fixed = std::numeric_limits<double>::quiet_NaN();
733 for (size_t n = 0; n < nDomains(); n++) {
734 Flow1D* d = dynamic_cast<Flow1D*>(&domain(n));
735 if (d && d->isFree() && d->m_tfixed > 0) {
736 z_fixed = d->m_zfixed;
737 break;
738 }
739 }
740 return z_fixed;
741}
742
743void Sim1D::setLeftControlPoint(double temperature)
744{
745 bool two_point_domain_found = false;
746 for (size_t n = 0; n < nDomains(); n++) {
747 Domain1D& d = domain(n);
748
749 // Skip if the domain type doesn't match
750 if (d.domainType() != "axisymmetric-flow") {
751 continue;
752 }
753
754 Flow1D& d_axis = dynamic_cast<Flow1D&>(domain(n));
755 size_t np = d_axis.nPoints();
756
757 // Skip if two-point control is not enabled
758 if (!d_axis.twoPointControlEnabled()) {
759 continue;
760 }
761 two_point_domain_found = true;
762
763 double current_val, next_val;
764 for (size_t m = 0; m < np-1; m++) {
765 current_val = value(n,c_offset_T,m);
766 next_val = value(n,c_offset_T,m+1);
767 if ((current_val - temperature) * (next_val - temperature) < 0.0) {
768 // Pick the coordinate of the point with the temperature closest
769 // to the desired temperature
770 size_t index = 0;
771 if (std::abs(current_val - temperature) <
772 std::abs(next_val - temperature)) {
773 index = m;
774 } else {
775 index = m+1;
776 }
777 d_axis.setLeftControlPointCoordinate(d_axis.grid(index));
779 return;
780 }
781 }
782 }
783
784 if (!two_point_domain_found) {
785 throw CanteraError("Sim1D::setLeftControlPoint",
786 "No domain with two-point control enabled was found.");
787 } else {
788 throw CanteraError("Sim1D::setLeftControlPoint",
789 "No control point with temperature {} was able to be found in the"
790 "flame's temperature range.", temperature);
791 }
792}
793
794void Sim1D::setRightControlPoint(double temperature)
795{
796 bool two_point_domain_found = false;
797 for (size_t n = 0; n < nDomains(); n++) {
798 Domain1D& d = domain(n);
799
800 // Skip if the domain type doesn't match
801 if (d.domainType() != "axisymmetric-flow") {
802 continue;
803 }
804
805 Flow1D& d_axis = dynamic_cast<Flow1D&>(domain(n));
806 size_t np = d_axis.nPoints();
807
808 // Skip if two-point control is not enabled
809 if (!d_axis.twoPointControlEnabled()) {
810 continue;
811 }
812 two_point_domain_found = true;
813
814 double current_val, next_val;
815 for (size_t m = np-1; m > 0; m--) {
816 current_val = value(n,c_offset_T,m);
817 next_val = value(n,c_offset_T,m-1);
818 if ((current_val - temperature) * (next_val - temperature) < 0.0) {
819 // Pick the coordinate of the point with the temperature closest
820 // to the desired temperature
821 size_t index = 0;
822 if (std::abs(current_val - temperature) <
823 std::abs(next_val - temperature)) {
824 index = m;
825 } else {
826 index = m-1;
827 }
828 d_axis.setRightControlPointCoordinate(d_axis.grid(index));
830 return;
831 }
832 }
833 }
834
835 if (!two_point_domain_found) {
836 throw CanteraError("Sim1D::setRightControlPoint",
837 "No domain with two-point control enabled was found.");
838 } else {
839 throw CanteraError("Sim1D::setRightControlPoint",
840 "No control point with temperature {} was able to be found in the"
841 "flame's temperature range.", temperature);
842 }
843
844}
845
846void Sim1D::setRefineCriteria(int dom, double ratio,
847 double slope, double curve, double prune)
848{
849 if (dom >= 0) {
850 Refiner& r = domain(dom).refiner();
851 r.setCriteria(ratio, slope, curve, prune);
852 } else {
853 for (size_t n = 0; n < nDomains(); n++) {
854 Refiner& r = domain(n).refiner();
855 r.setCriteria(ratio, slope, curve, prune);
856 }
857 }
858}
859
860vector<double> Sim1D::getRefineCriteria(int dom)
861{
862 if (dom >= 0) {
863 Refiner& r = domain(dom).refiner();
864 return r.getCriteria();
865 } else {
866 throw CanteraError("Sim1D::getRefineCriteria",
867 "Must specify domain to get criteria from");
868 }
869}
870
871void Sim1D::setGridMin(int dom, double gridmin)
872{
873 if (dom >= 0) {
874 Refiner& r = domain(dom).refiner();
875 r.setGridMin(gridmin);
876 } else {
877 for (size_t n = 0; n < nDomains(); n++) {
878 Refiner& r = domain(n).refiner();
879 r.setGridMin(gridmin);
880 }
881 }
882}
883
884void Sim1D::setMaxGridPoints(int dom, int npoints)
885{
886 if (dom >= 0) {
887 Refiner& r = domain(dom).refiner();
888 r.setMaxPoints(npoints);
889 } else {
890 for (size_t n = 0; n < nDomains(); n++) {
891 Refiner& r = domain(n).refiner();
892 r.setMaxPoints(npoints);
893 }
894 }
895}
896
897size_t Sim1D::maxGridPoints(size_t dom)
898{
899 Refiner& r = domain(dom).refiner();
900 return r.maxPoints();
901}
902
903double Sim1D::jacobian(int i, int j)
904{
905 return OneDim::jacobian().value(i,j);
906}
907
908void Sim1D::evalSSJacobian()
909{
910 OneDim::evalSSJacobian(m_state->data(), m_xnew.data());
911}
912
913void Sim1D::solveAdjoint(const double* b, double* lambda)
914{
915 for (auto& D : m_dom) {
916 D->forceFullUpdate(true);
917 }
918 evalSSJacobian();
919 for (auto& D : m_dom) {
920 D->forceFullUpdate(false);
921 }
922
923 // Form J^T
924 size_t bw = bandwidth();
925 BandMatrix Jt(size(), bw, bw);
926 for (size_t i = 0; i < size(); i++) {
927 size_t j1 = (i > bw) ? i - bw : 0;
928 size_t j2 = (i + bw >= size()) ? size() - 1: i + bw;
929 for (size_t j = j1; j <= j2; j++) {
930 Jt(j,i) = m_jac->value(i,j);
931 }
932 }
933
934 Jt.solve(b, lambda);
935}
936
938{
940 m_xnew.resize(size(), 0.0);
941}
942
943}
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
double getDouble(const string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition AnyMap.cpp:1580
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
static void clearCachedFile(const string &filename)
Remove the specified file from the input cache if it is present.
Definition AnyMap.cpp:1816
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
A class for banded matrices, involving matrix inversion processes.
Definition BandMatrix.h:37
int solve(const double *const b, double *const x)
Solve the matrix problem Ax = b.
double & value(size_t i, size_t j)
Return a changeable reference to element (i,j).
Base class for exceptions thrown by Cantera classes.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
Base class for one-dimensional domains.
Definition Domain1D.h:28
size_t nComponents() const
Number of components at each grid point.
Definition Domain1D.h:143
virtual void _finalize(const double *x)
In some cases, a domain may need to set parameters that depend on the initial solution estimate.
Definition Domain1D.h:538
size_t nPoints() const
Number of grid points in this domain.
Definition Domain1D.h:165
virtual string domainType() const
Domain type flag.
Definition Domain1D.h:44
Refiner & refiner()
Return a reference to the grid refiner.
Definition Domain1D.h:138
virtual string componentName(size_t n) const
Name of the nth component. May be overloaded.
Definition Domain1D.cpp:67
string type() const
String indicating the domain implemented.
Definition Domain1D.h:49
virtual void _getInitialSoln(double *x)
Writes some or all initial solution values into the global solution array, beginning at the location ...
Definition Domain1D.cpp:284
size_t index(size_t n, size_t j) const
Returns the index of the solution vector, which corresponds to component n at grid point j.
Definition Domain1D.h:330
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:412
virtual void show(std::ostream &s, const double *x)
Print the solution.
Definition Domain1D.h:488
virtual void setupGrid(size_t n, const double *z)
called to set up initial grid, and after grid refinement
Definition Domain1D.cpp:225
This class represents 1D flow domains that satisfy the one-dimensional similarity solution for chemic...
Definition Flow1D.h:46
void setLeftControlPointTemperature(double temperature)
Sets the temperature of the left control point.
Definition Flow1D.cpp:1198
void setLeftControlPointCoordinate(double z_left)
Sets the coordinate of the left control point.
Definition Flow1D.cpp:1213
void setRightControlPointCoordinate(double z_right)
Sets the coordinate of the right control point.
Definition Flow1D.cpp:1268
bool twoPointControlEnabled() const
Returns the status of the two-point control.
Definition Flow1D.h:313
void setRightControlPointTemperature(double temperature)
Sets the temperature of the right control point.
Definition Flow1D.cpp:1253
double m_tfixed
Temperature at the point used to fix the flame location.
Definition Flow1D.h:912
double m_zfixed
Location of the point where temperature is fixed.
Definition Flow1D.h:909
bool isFree() const
Retrieve flag indicating whether flow is freely propagating.
Definition Flow1D.h:342
virtual double eval(double t) const
Evaluate the function.
Definition Func1.cpp:28
void setOptions(int maxJacAge=5)
Set options.
Container class for multiple-domain 1D problems.
Definition OneDim.h:27
int solve(double *x0, double *x1, int loglevel)
Solve F(x) = 0, where F(x) is the multi-domain residual function.
Definition OneDim.cpp:212
size_t start(size_t i) const
The index of the start of domain i in the solution vector.
Definition OneDim.h:94
int m_nsteps
Number of time steps taken in the current call to solve()
Definition OneDim.h:376
virtual void resize()
Call after one or more grids has changed size, for example after being refined.
Definition OneDim.cpp:154
size_t size() const
Total solution vector length;.
Definition OneDim.h:105
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:245
double ssnorm(double *x, double *r)
Steady-state max norm (infinity norm) of the residual evaluated using solution x.
Definition OneDim.cpp:277
size_t nDomains() const
Number of domains.
Definition OneDim.h:63
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
size_t bandwidth() const
Jacobian bandwidth.
Definition OneDim.h:135
shared_ptr< vector< double > > m_state
Solution vector.
Definition OneDim.h:345
unique_ptr< MultiJac > m_jac
Jacobian evaluator.
Definition OneDim.h:347
double timeStep(int nsteps, double dt, double *x, double *r, int loglevel)
Take time steps using Backward Euler.
Definition OneDim.cpp:335
double m_tmax
maximum timestep size
Definition OneDim.h:340
void setSteadyMode()
Prepare to solve the steady-state problem.
Definition OneDim.cpp:306
Domain1D & domain(size_t i) const
Return a reference to domain i.
Definition OneDim.h:68
MultiNewton & newton()
Return a reference to the Newton iterator.
Definition OneDim.cpp:91
Refine Domain1D grids so that profiles satisfy adaptation tolerances.
Definition refine.h:17
size_t maxPoints() const
Returns the maximum number of points allowed in the domain.
Definition refine.h:57
vector< double > getCriteria()
Get the grid refinement criteria.
Definition refine.h:42
void setMaxPoints(int npmax)
Set the maximum number of points allowed in the domain.
Definition refine.h:52
void setCriteria(double ratio=10.0, double slope=0.8, double curve=0.8, double prune=-0.1)
Set grid refinement criteria.
Definition refine.cpp:21
int analyze(size_t n, const double *z, const double *x)
Determine locations in the grid that need additional grid points.
Definition refine.cpp:43
void setGridMin(double gridmin)
Set the minimum allowable spacing between adjacent grid points [m].
Definition refine.h:62
void restoreTimeSteppingSolution()
Set the current solution vector to the last successful time-stepping solution.
Definition Sim1D.cpp:352
void resize() override
Call after one or more grids has changed size, for example after being refined.
Definition Sim1D.cpp:937
void saveResidual(const string &fname, const string &name, const string &desc, bool overwrite=false, int compression=0)
Save the residual of the current solution to a container file.
Definition Sim1D.cpp:147
vector< double > m_xnew
a work array used to hold the residual or the new solution
Definition Sim1D.h:350
void setProfile(size_t dom, size_t comp, const vector< double > &pos, const vector< double > &values)
Specify a profile for one component of one domain.
Definition Sim1D.cpp:79
double fixedTemperatureLocation()
Return location of the point where temperature is fixed.
Definition Sim1D.cpp:730
vector< vector< double > > m_grid_last_ss
the grids for each domain after the last successful steady-state solve (stored before grid refinement...
Definition Sim1D.h:347
void finalize()
Calls method _finalize in each domain.
Definition Sim1D.cpp:381
void setValue(size_t dom, size_t comp, size_t localPoint, double value)
Set a single value in the solution vector.
Definition Sim1D.cpp:55
void writeDebugInfo(const string &header_suffix, const string &message, int loglevel, int attempt_counter)
Write solver debugging information to a YAML file based on the specified log level.
Definition Sim1D.cpp:613
int refine(int loglevel=0)
Refine the grid in all domains.
Definition Sim1D.cpp:521
void show()
Show logging information on current solution for all domains.
Definition Sim1D.cpp:341
double fixedTemperature()
Return temperature at the point used to fix the flame location.
Definition Sim1D.cpp:717
vector< double > m_xlast_ss
the solution vector after the last successful steady-state solve (stored before grid refinement)
Definition Sim1D.h:343
void setMaxGridPoints(int dom, int npoints)
Set the maximum number of grid points in the domain.
Definition Sim1D.cpp:884
int setFixedTemperature(double t)
Add node for fixed temperature point of freely propagating flame.
Definition Sim1D.cpp:627
void clearDebugFile()
Deletes a debug_sim1d.yaml file if it exists.
Definition Sim1D.cpp:608
void setInitialGuess(const string &component, vector< double > &locs, vector< double > &vals)
Set initial guess for one component for all domains.
Definition Sim1D.cpp:41
int newtonSolve(int loglevel)
Wrapper around the Newton solver.
Definition Sim1D.cpp:397
vector< int > m_steps
array of number of steps to take before re-attempting the steady-state solution
Definition Sim1D.h:357
double m_tstep
timestep
Definition Sim1D.h:353
void solveAdjoint(const double *b, double *lambda)
Solve the equation .
Definition Sim1D.cpp:913
AnyMap restore(const string &fname, const string &name)
Retrieve data and settings from a previously saved simulation.
Definition Sim1D.cpp:247
Func1 * m_steady_callback
User-supplied function called after a successful steady-state solve.
Definition Sim1D.h:360
void restoreSteadySolution()
Set the current solution vector and grid to the last successful steady- state solution.
Definition Sim1D.cpp:361
size_t maxGridPoints(size_t dom)
Get the maximum number of grid points in this domain.
Definition Sim1D.cpp:897
void setFlatProfile(size_t dom, size_t comp, double v)
Set component 'comp' of domain 'dom' to value 'v' at all points.
Definition Sim1D.cpp:324
void setRightControlPoint(double temperature)
Set the right control point location using the specified temperature.
Definition Sim1D.cpp:794
double value(size_t dom, size_t comp, size_t localPoint) const
Get one entry in the solution vector.
Definition Sim1D.cpp:63
vector< double > getRefineCriteria(int dom)
Get the grid refinement criteria.
Definition Sim1D.cpp:860
Sim1D()
Default constructor.
Definition Sim1D.h:29
void setGridMin(int dom, double gridmin)
Set the minimum grid spacing in the specified domain(s).
Definition Sim1D.cpp:871
void setRefineCriteria(int dom=-1, double ratio=10.0, double slope=0.8, double curve=0.8, double prune=-0.1)
Set grid refinement criteria.
Definition Sim1D.cpp:846
vector< double > m_xlast_ts
the solution vector after the last successful timestepping
Definition Sim1D.h:339
void save(const string &fname, const string &name, const string &desc, bool overwrite=false, int compression=0, const string &basis="")
Save current simulation data to a container file or CSV format.
Definition Sim1D.cpp:98
void setLeftControlPoint(double temperature)
Set the left control point location using the specified temperature.
Definition Sim1D.cpp:743
static AnyMap readHeader(const string &fname, const string &name)
Read header information from a HDF container file.
static void writeHeader(const string &fname, const string &name, const string &desc, bool overwrite=false)
Write header data to a HDF container file.
static shared_ptr< SolutionArray > create(const shared_ptr< Solution > &sol, int size=0, const AnyMap &meta={})
Instantiate a new SolutionArray reference.
Header for a file containing miscellaneous numerical functions.
string toLowerCopy(const string &input)
Convert to lower case.
MultiJac & jacobian()
Return a reference to the Jacobian evaluator of an OneDim object.
Definition OneDim.cpp:87
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:158
void writelog(const string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition global.h:175
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition utilities.h:82
double linearInterp(double x, const vector< double > &xpts, const vector< double > &fpts)
Linearly interpolate a function defined on a discrete grid.
Definition funcs.cpp:13
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:267
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
@ c_offset_T
temperature [kelvin]
Definition Flow1D.h:27
Contains declarations for string manipulation functions within Cantera.