Cantera 2.6.0
Boundary1D.cpp
Go to the documentation of this file.
1//! @file Boundary1D.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8#include "cantera/base/ctml.h"
10
11using namespace std;
12
13namespace Cantera
14{
15
16Boundary1D::Boundary1D() : Domain1D(1, 1, 0.0),
17 m_flow_left(0), m_flow_right(0),
18 m_ilr(0), m_left_nv(0), m_right_nv(0),
19 m_left_loc(0), m_right_loc(0),
20 m_left_points(0),
21 m_left_nsp(0), m_right_nsp(0),
22 m_sp_left(0), m_sp_right(0),
23 m_start_left(0), m_start_right(0),
24 m_phase_left(0), m_phase_right(0), m_temp(0.0), m_mdot(0.0)
25{
26 m_type = cConnectorType;
27}
28
29void Boundary1D::_init(size_t n)
30{
31 if (m_index == npos) {
32 throw CanteraError("Boundary1D::_init",
33 "install in container before calling init.");
34 }
35
36 // A boundary object contains only one grid point
37 resize(n,1);
38
39 m_left_nsp = 0;
40 m_right_nsp = 0;
41
42 // check for left and right flow objects
43 if (m_index > 0) {
44 Domain1D& r = container().domain(m_index-1);
45 if (!r.isConnector()) { // multi-point domain
46 m_left_nv = r.nComponents();
47 if (m_left_nv > c_offset_Y) {
48 m_left_nsp = m_left_nv - c_offset_Y;
49 } else {
50 m_left_nsp = 0;
51 }
52 m_left_loc = container().start(m_index-1);
53 m_left_points = r.nPoints();
54 m_flow_left = dynamic_cast<StFlow*>(&r);
55 if (m_flow_left != nullptr) {
56 m_phase_left = &m_flow_left->phase();
57 }
58 } else {
59 throw CanteraError("Boundary1D::_init",
60 "Boundary domains can only be connected on the left to flow "
61 "domains, not type {} domains.", r.domainType());
62 }
63 }
64
65 // if this is not the last domain, see what is connected on the right
66 if (m_index + 1 < container().nDomains()) {
67 Domain1D& r = container().domain(m_index+1);
68 if (!r.isConnector()) { // multi-point domain
69 m_right_nv = r.nComponents();
70 if (m_right_nv > c_offset_Y) {
71 m_right_nsp = m_right_nv - c_offset_Y;
72 } else {
73 m_right_nsp = 0;
74 }
75 m_right_loc = container().start(m_index+1);
76 m_flow_right = dynamic_cast<StFlow*>(&r);
77 if (m_flow_right != nullptr) {
78 m_phase_right = &m_flow_right->phase();
79 }
80 } else {
81 throw CanteraError("Boundary1D::_init",
82 "Boundary domains can only be connected on the right to flow "
83 "domains, not type {} domains.", r.domainType());
84 }
85 }
86}
87
88// ---------------- Inlet1D methods ----------------
89
90Inlet1D::Inlet1D()
91 : m_V0(0.0)
92 , m_nsp(0)
93 , m_flow(0)
94{
95 m_type = cInletType;
96 m_xstr = "";
97}
98
99void Inlet1D::showSolution(const double* x)
100{
101 writelog(" Mass Flux: {:10.4g} kg/m^2/s \n", m_mdot);
102 writelog(" Temperature: {:10.4g} K \n", m_temp);
103 if (m_flow) {
104 writelog(" Mass Fractions: \n");
105 for (size_t k = 0; k < m_flow->phase().nSpecies(); k++) {
106 if (m_yin[k] != 0.0) {
107 writelog(" {:>16s} {:10.4g} \n",
108 m_flow->phase().speciesName(k), m_yin[k]);
109 }
110 }
111 }
112 writelog("\n");
113}
114
115void Inlet1D::setMoleFractions(const std::string& xin)
116{
117 m_xstr = xin;
118 if (m_flow) {
119 m_flow->phase().setMoleFractionsByName(xin);
120 m_flow->phase().getMassFractions(m_yin.data());
121 needJacUpdate();
122 }
123}
124
125void Inlet1D::setMoleFractions(const double* xin)
126{
127 if (m_flow) {
128 m_flow->phase().setMoleFractions(xin);
129 m_flow->phase().getMassFractions(m_yin.data());
130 needJacUpdate();
131 }
132}
133
134void Inlet1D::init()
135{
136 _init(0);
137
138 // if a flow domain is present on the left, then this must be a right inlet.
139 // Note that an inlet object can only be a terminal object - it cannot have
140 // flows on both the left and right
141 if (m_flow_left) {
142 m_ilr = RightInlet;
143 m_flow = m_flow_left;
144 } else if (m_flow_right) {
145 m_ilr = LeftInlet;
146 m_flow = m_flow_right;
147 } else {
148 throw CanteraError("Inlet1D::init","no flow!");
149 }
150
151 // components = u, V, T, lambda, + mass fractions
152 m_nsp = m_flow->phase().nSpecies();
153 m_yin.resize(m_nsp, 0.0);
154 if (m_xstr != "") {
155 setMoleFractions(m_xstr);
156 } else {
157 m_yin[0] = 1.0;
158 }
159}
160
161void Inlet1D::eval(size_t jg, double* xg, double* rg,
162 integer* diagg, double rdt)
163{
164 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
165 return;
166 }
167
168 if (m_ilr == LeftInlet) {
169 // Array elements corresponding to the first point of the flow domain
170 double* xb = xg + m_flow->loc();
171 double* rb = rg + m_flow->loc();
172
173 // The first flow residual is for u. This, however, is not modified by
174 // the inlet, since this is set within the flow domain from the
175 // continuity equation.
176
177 // spreading rate. The flow domain sets this to V(0),
178 // so for finite spreading rate subtract m_V0.
179 rb[c_offset_V] -= m_V0;
180
181 if (m_flow->doEnergy(0)) {
182 // The third flow residual is for T, where it is set to T(0). Subtract
183 // the local temperature to hold the flow T to the inlet T.
184 rb[c_offset_T] -= m_temp;
185 }
186
187 if (m_flow->fixed_mdot()) {
188 // The flow domain sets this to -rho*u. Add mdot to specify the mass
189 // flow rate.
190 rb[c_offset_L] += m_mdot;
191 } else {
192 // if the flow is a freely-propagating flame, mdot is not specified.
193 // Set mdot equal to rho*u, and also set lambda to zero.
194 m_mdot = m_flow->density(0)*xb[0];
195 rb[c_offset_L] = xb[c_offset_L];
196 }
197
198 // add the convective term to the species residual equations
199 for (size_t k = 0; k < m_nsp; k++) {
200 if (k != m_flow_right->leftExcessSpecies()) {
201 rb[c_offset_Y+k] += m_mdot*m_yin[k];
202 }
203 }
204
205 } else {
206 // right inlet
207 // Array elements corresponding to the last point in the flow domain
208 double* rb = rg + loc() - m_flow->nComponents();
209 rb[c_offset_V] -= m_V0;
210 if (m_flow->doEnergy(m_flow->nPoints() - 1)) {
211 rb[c_offset_T] -= m_temp; // T
212 }
213 rb[c_offset_U] += m_mdot; // u
214 for (size_t k = 0; k < m_nsp; k++) {
215 if (k != m_flow_left->rightExcessSpecies()) {
216 rb[c_offset_Y+k] += m_mdot * m_yin[k];
217 }
218 }
219 }
220}
221
222XML_Node& Inlet1D::save(XML_Node& o, const double* const soln)
223{
224 XML_Node& inlt = Domain1D::save(o, soln);
225 inlt.addAttribute("type","inlet");
226 addFloat(inlt, "temperature", m_temp);
227 addFloat(inlt, "mdot", m_mdot);
228 for (size_t k=0; k < m_nsp; k++) {
229 addFloat(inlt, "massFraction", m_yin[k], "",
230 m_flow->phase().speciesName(k));
231 }
232 return inlt;
233}
234
235void Inlet1D::restore(const XML_Node& dom, double* soln, int loglevel)
236{
237 Domain1D::restore(dom, soln, loglevel);
238 m_mdot = getFloat(dom, "mdot");
239 m_temp = getFloat(dom, "temperature");
240
241 m_yin.assign(m_nsp, 0.0);
242
243 for (size_t i = 0; i < dom.nChildren(); i++) {
244 const XML_Node& node = dom.child(i);
245 if (node.name() == "massFraction") {
246 size_t k = m_flow->phase().speciesIndex(node.attrib("type"));
247 if (k != npos) {
248 m_yin[k] = node.fp_value();
249 }
250 }
251 }
252 resize(0, 1);
253}
254
255AnyMap Inlet1D::serialize(const double* soln) const
256{
257 AnyMap state = Boundary1D::serialize(soln);
258 state["type"] = "inlet";
259 state["temperature"] = m_temp;
260 state["mass-flux"] = m_mdot;
261 AnyMap Y;
262 for (size_t k = 0; k < m_nsp; k++) {
263 if (m_yin[k] != 0.0) {
264 Y[m_flow->phase().speciesName(k)] = m_yin[k];
265 }
266 }
267 state["mass-fractions"] = std::move(Y);
268 return state;
269}
270
271void Inlet1D::restore(const AnyMap& state, double* soln, int loglevel)
272{
273 Boundary1D::restore(state, soln, loglevel);
274 m_mdot = state["mass-flux"].asDouble();
275 m_temp = state["temperature"].asDouble();
276 const auto& Y = state["mass-fractions"].as<AnyMap>();
277 ThermoPhase& thermo = m_flow->phase();
278 for (size_t k = 0; k < m_nsp; k++) {
279 m_yin[k] = Y.getDouble(thermo.speciesName(k), 0.0);
280 }
281
282 // Warn about species not in the current phase
283 if (loglevel) {
284 for (auto& item : Y) {
285 if (thermo.speciesIndex(item.first) == npos) {
286 warn_user("Inlet1D::restore", "Phase '{}' does not contain a species "
287 "named '{}'\n which was specified as having a mass fraction of {}.",
288 thermo.name(), item.first, item.second.asDouble());
289 }
290 }
291 }
292}
293
294// ------------- Empty1D -------------
295
296void Empty1D::init()
297{
298 _init(0);
299}
300
301void Empty1D::eval(size_t jg, double* xg, double* rg,
302 integer* diagg, double rdt)
303{
304}
305
306XML_Node& Empty1D::save(XML_Node& o, const double* const soln)
307{
308 XML_Node& symm = Domain1D::save(o, soln);
309 symm.addAttribute("type","empty");
310 return symm;
311}
312
313void Empty1D::restore(const XML_Node& dom, double* soln, int loglevel)
314{
315 Domain1D::restore(dom, soln, loglevel);
316 resize(0, 1);
317}
318
319AnyMap Empty1D::serialize(const double* soln) const
320{
321 AnyMap state = Boundary1D::serialize(soln);
322 state["type"] = "empty";
323 return state;
324}
325
326// -------------- Symm1D --------------
327
328void Symm1D::init()
329{
330 _init(0);
331}
332
333void Symm1D::eval(size_t jg, double* xg, double* rg, integer* diagg,
334 double rdt)
335{
336 if (jg != npos && (jg + 2< firstPoint() || jg > lastPoint() + 2)) {
337 return;
338 }
339
340 // start of local part of global arrays
341 double* x = xg + loc();
342 double* r = rg + loc();
343 integer* diag = diagg + loc();
344
345 if (m_flow_right) {
346 size_t nc = m_flow_right->nComponents();
347 double* xb = x;
348 double* rb = r;
349 int* db = diag;
350 db[c_offset_V] = 0;
351 db[c_offset_T] = 0;
352 rb[c_offset_V] = xb[c_offset_V] - xb[c_offset_V + nc]; // zero dV/dz
353 if (m_flow_right->doEnergy(0)) {
354 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc]; // zero dT/dz
355 }
356 }
357
358 if (m_flow_left) {
359 size_t nc = m_flow_left->nComponents();
360 double* xb = x - nc;
361 double* rb = r - nc;
362 int* db = diag - nc;
363 db[c_offset_V] = 0;
364 db[c_offset_T] = 0;
365 rb[c_offset_V] = xb[c_offset_V] - xb[c_offset_V - nc]; // zero dV/dz
366 if (m_flow_left->doEnergy(m_flow_left->nPoints() - 1)) {
367 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T - nc]; // zero dT/dz
368 }
369 }
370}
371
372XML_Node& Symm1D::save(XML_Node& o, const double* const soln)
373{
374 XML_Node& symm = Domain1D::save(o, soln);
375 symm.addAttribute("type","symmetry");
376 return symm;
377}
378
379void Symm1D::restore(const XML_Node& dom, double* soln, int loglevel)
380{
381 Domain1D::restore(dom, soln, loglevel);
382 resize(0, 1);
383}
384
385AnyMap Symm1D::serialize(const double* soln) const
386{
387 AnyMap state = Boundary1D::serialize(soln);
388 state["type"] = "symmetry";
389 return state;
390}
391
392// -------- Outlet1D --------
393
394OutletRes1D::OutletRes1D()
395 : m_nsp(0)
396 , m_flow(0)
397{
398 m_type = cOutletResType;
399 m_xstr = "";
400}
401
403{
404 _init(0);
405
406 if (m_flow_right) {
407 m_flow_right->setViscosityFlag(false);
408 }
409 if (m_flow_left) {
410 m_flow_left->setViscosityFlag(false);
411 }
412}
413
414void Outlet1D::eval(size_t jg, double* xg, double* rg, integer* diagg,
415 double rdt)
416{
417 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
418 return;
419 }
420
421 // start of local part of global arrays
422 double* x = xg + loc();
423 double* r = rg + loc();
424 integer* diag = diagg + loc();
425
426 if (m_flow_right) {
427 size_t nc = m_flow_right->nComponents();
428 double* xb = x;
429 double* rb = r;
430 rb[c_offset_U] = xb[c_offset_L];
431 if (m_flow_right->doEnergy(0)) {
432 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc];
433 }
434 for (size_t k = c_offset_Y; k < nc; k++) {
435 rb[k] = xb[k] - xb[k + nc];
436 }
437 }
438
439 if (m_flow_left) {
440 size_t nc = m_flow_left->nComponents();
441 double* xb = x - nc;
442 double* rb = r - nc;
443 int* db = diag - nc;
444
445 // zero Lambda
446 if (m_flow_left->fixed_mdot()) {
447 rb[c_offset_U] = xb[c_offset_L];
448 }
449
450 if (m_flow_left->doEnergy(m_flow_left->nPoints()-1)) {
451 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T - nc]; // zero T gradient
452 }
453 size_t kSkip = c_offset_Y + m_flow_left->rightExcessSpecies();
454 for (size_t k = c_offset_Y; k < nc; k++) {
455 if (k != kSkip) {
456 rb[k] = xb[k] - xb[k - nc]; // zero mass fraction gradient
457 db[k] = 0;
458 }
459 }
460 }
461}
462
463XML_Node& Outlet1D::save(XML_Node& o, const double* const soln)
464{
465 XML_Node& outlt = Domain1D::save(o, soln);
466 outlt.addAttribute("type","outlet");
467 return outlt;
468}
469
470void Outlet1D::restore(const XML_Node& dom, double* soln, int loglevel)
471{
472 Domain1D::restore(dom, soln, loglevel);
473 resize(0, 1);
474}
475
476AnyMap Outlet1D::serialize(const double* soln) const
477{
478 AnyMap state = Boundary1D::serialize(soln);
479 state["type"] = "outlet";
480 return state;
481}
482
483// -------- OutletRes1D --------
484
485void OutletRes1D::setMoleFractions(const std::string& xres)
486{
487 m_xstr = xres;
488 if (m_flow) {
489 m_flow->phase().setMoleFractionsByName(xres);
490 m_flow->phase().getMassFractions(m_yres.data());
492 }
493}
494
495void OutletRes1D::setMoleFractions(const double* xres)
496{
497 if (m_flow) {
498 m_flow->phase().setMoleFractions(xres);
499 m_flow->phase().getMassFractions(m_yres.data());
501 }
502}
503
505{
506 _init(0);
507
508 if (m_flow_left) {
509 m_flow = m_flow_left;
510 } else if (m_flow_right) {
511 m_flow = m_flow_right;
512 } else {
513 throw CanteraError("OutletRes1D::init","no flow!");
514 }
515
516 m_nsp = m_flow->phase().nSpecies();
517 m_yres.resize(m_nsp, 0.0);
518 if (m_xstr != "") {
519 setMoleFractions(m_xstr);
520 } else {
521 m_yres[0] = 1.0;
522 }
523}
524
525void OutletRes1D::eval(size_t jg, double* xg, double* rg,
526 integer* diagg, double rdt)
527{
528 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
529 return;
530 }
531
532 // start of local part of global arrays
533 double* x = xg + loc();
534 double* r = rg + loc();
535 integer* diag = diagg + loc();
536
537 if (m_flow_right) {
538 size_t nc = m_flow_right->nComponents();
539 double* xb = x;
540 double* rb = r;
541
542 // this seems wrong...
543 // zero Lambda
544 rb[c_offset_U] = xb[c_offset_L];
545
546 if (m_flow_right->doEnergy(0)) {
547 // zero gradient for T
548 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc];
549 }
550
551 // specified mass fractions
552 for (size_t k = c_offset_Y; k < nc; k++) {
553 rb[k] = xb[k] - m_yres[k-c_offset_Y];
554 }
555 }
556
557 if (m_flow_left) {
558 size_t nc = m_flow_left->nComponents();
559 double* xb = x - nc;
560 double* rb = r - nc;
561 int* db = diag - nc;
562
563 if (!m_flow_left->fixed_mdot()) {
564 ;
565 } else {
566 rb[c_offset_U] = xb[c_offset_L]; // zero Lambda
567 }
568 if (m_flow_left->doEnergy(m_flow_left->nPoints()-1)) {
569 rb[c_offset_T] = xb[c_offset_T] - m_temp; // zero dT/dz
570 }
571 size_t kSkip = m_flow_left->rightExcessSpecies();
572 for (size_t k = c_offset_Y; k < nc; k++) {
573 if (k != kSkip) {
574 rb[k] = xb[k] - m_yres[k-c_offset_Y]; // fixed Y
575 db[k] = 0;
576 }
577 }
578 }
579}
580
581XML_Node& OutletRes1D::save(XML_Node& o, const double* const soln)
582{
583 XML_Node& outlt = Domain1D::save(o, soln);
584 outlt.addAttribute("type","outletres");
585 addFloat(outlt, "temperature", m_temp, "K");
586 for (size_t k=0; k < m_nsp; k++) {
587 addFloat(outlt, "massFraction", m_yres[k], "",
588 m_flow->phase().speciesName(k));
589 }
590 return outlt;
591}
592
593void OutletRes1D::restore(const XML_Node& dom, double* soln, int loglevel)
594{
595 Domain1D::restore(dom, soln, loglevel);
596 m_temp = getFloat(dom, "temperature");
597
598 m_yres.assign(m_nsp, 0.0);
599 for (size_t i = 0; i < dom.nChildren(); i++) {
600 const XML_Node& node = dom.child(i);
601 if (node.name() == "massFraction") {
602 size_t k = m_flow->phase().speciesIndex(node.attrib("type"));
603 if (k != npos) {
604 m_yres[k] = node.fp_value();
605 }
606 }
607 }
608
609 resize(0, 1);
610}
611
612AnyMap OutletRes1D::serialize(const double* soln) const
613{
614 AnyMap state = Boundary1D::serialize(soln);
615 state["type"] = "outlet-reservoir";
616 state["temperature"] = m_temp;
617 AnyMap Y;
618 for (size_t k = 0; k < m_nsp; k++) {
619 if (m_yres[k] != 0.0) {
620 Y[m_flow->phase().speciesName(k)] = m_yres[k];
621 }
622 }
623 state["mass-fractions"] = std::move(Y);
624 return state;
625}
626
627void OutletRes1D::restore(const AnyMap& state, double* soln, int loglevel)
628{
629 Boundary1D::restore(state, soln, loglevel);
630 m_temp = state["temperature"].asDouble();
631 const auto& Y = state["mass-fractions"].as<AnyMap>();
632 ThermoPhase& thermo = m_flow->phase();
633 for (size_t k = 0; k < m_nsp; k++) {
634 m_yres[k] = Y.getDouble(thermo.speciesName(k), 0.0);
635 }
636
637 // Warn about species not in the current phase
638 if (loglevel) {
639 for (auto& item : Y) {
640 if (thermo.speciesIndex(item.first) == npos) {
641 warn_user("OutletRes1D::restore", "Phase '{}' does not contain a "
642 "species named '{}'\nwhich was specified as having a mass "
643 "fraction of {}.",
644 thermo.name(), item.first, item.second.asDouble());
645 }
646 }
647 }
648}
649
650// -------- Surf1D --------
651
653{
654 _init(0);
655}
656
657void Surf1D::eval(size_t jg, double* xg, double* rg,
658 integer* diagg, double rdt)
659{
660 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
661 return;
662 }
663
664 // start of local part of global arrays
665 double* x = xg + loc();
666 double* r = rg + loc();
667
668 if (m_flow_right) {
669 double* rb = r;
670 double* xb = x;
671 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
672 }
673
674 if (m_flow_left) {
675 size_t nc = m_flow_left->nComponents();
676 double* rb = r - nc;
677 double* xb = x - nc;
678 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
679 }
680}
681
682XML_Node& Surf1D::save(XML_Node& o, const double* const soln)
683{
684 XML_Node& inlt = Domain1D::save(o, soln);
685 inlt.addAttribute("type","surface");
686 addFloat(inlt, "temperature", m_temp);
687 return inlt;
688}
689
690void Surf1D::restore(const XML_Node& dom, double* soln, int loglevel)
691{
692 Domain1D::restore(dom, soln, loglevel);
693 m_temp = getFloat(dom, "temperature");
694 resize(0, 1);
695}
696
697AnyMap Surf1D::serialize(const double* soln) const
698{
699 AnyMap state = Boundary1D::serialize(soln);
700 state["type"] = "surface";
701 state["temperature"] = m_temp;
702 return state;
703}
704
705void Surf1D::restore(const AnyMap& state, double* soln, int loglevel)
706{
707 Boundary1D::restore(state, soln, loglevel);
708 m_temp = state["temperature"].asDouble();
709}
710
711void Surf1D::showSolution_s(std::ostream& s, const double* x)
712{
713 s << "------------------- Surface " << domainIndex() << " ------------------- " << std::endl;
714 s << " temperature: " << m_temp << " K" << std::endl;
715}
716
717void Surf1D::showSolution(const double* x)
718{
719 writelog(" Temperature: {:10.4g} K \n\n", m_temp);
720}
721
722// -------- ReactingSurf1D --------
723
724ReactingSurf1D::ReactingSurf1D()
725 : m_kin(0)
726 , m_surfindex(0)
727 , m_nsp(0)
728{
729 m_type = cSurfType;
730}
731
732void ReactingSurf1D::setKineticsMgr(InterfaceKinetics* kin)
733{
734 m_kin = kin;
735 m_surfindex = kin->surfacePhaseIndex();
736 m_sphase = (SurfPhase*)&kin->thermo(m_surfindex);
737 m_nsp = m_sphase->nSpecies();
738 m_enabled = true;
739}
740
741string ReactingSurf1D::componentName(size_t n) const
742{
743 if (n < m_nsp) {
744 return m_sphase->speciesName(n);
745 } else {
746 return "<unknown>";
747 }
748}
749
751{
752 m_nv = m_nsp;
753 _init(m_nsp);
754 m_fixed_cov.resize(m_nsp, 0.0);
755 m_fixed_cov[0] = 1.0;
756 m_work.resize(m_kin->nTotalSpecies(), 0.0);
757
758 for (size_t n = 0; n < m_nsp; n++) {
759 setBounds(n, -1.0e-5, 2.0);
760 }
761}
762
764 double* x = xg + loc();
765 m_sphase->setCoverages(x);
766 m_sphase->getCoverages(x);
767}
768
769void ReactingSurf1D::eval(size_t jg, double* xg, double* rg,
770 integer* diagg, double rdt)
771{
772 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
773 return;
774 }
775
776 // start of local part of global arrays
777 double* x = xg + loc();
778 double* r = rg + loc();
779 integer* diag = diagg + loc();
780
781 // set the coverages
782 double sum = 0.0;
783 for (size_t k = 0; k < m_nsp; k++) {
784 m_work[k] = x[k];
785 sum += x[k];
786 }
787 m_sphase->setTemperature(m_temp);
788 m_sphase->setCoveragesNoNorm(m_work.data());
789
790 // set the left gas state to the adjacent point
791
792 size_t leftloc = 0, rightloc = 0;
793 size_t pnt = 0;
794
795 if (m_flow_left) {
796 leftloc = m_flow_left->loc();
797 pnt = m_flow_left->nPoints() - 1;
798 m_flow_left->setGas(xg + leftloc, pnt);
799 }
800
801 if (m_flow_right) {
802 rightloc = m_flow_right->loc();
803 m_flow_right->setGas(xg + rightloc, 0);
804 }
805
806 m_kin->getNetProductionRates(m_work.data());
807 double rs0 = 1.0/m_sphase->siteDensity();
808 size_t ioffset = m_kin->kineticsSpeciesIndex(0, m_surfindex);
809
810 if (m_enabled) {
811 for (size_t k = 0; k < m_nsp; k++) {
812 r[k] = m_work[k + ioffset] * m_sphase->size(k) * rs0;
813 r[k] -= rdt*(x[k] - prevSoln(k,0));
814 diag[k] = 1;
815 }
816 r[0] = 1.0 - sum;
817 diag[0] = 0;
818 } else {
819 for (size_t k = 0; k < m_nsp; k++) {
820 r[k] = x[k] - m_fixed_cov[k];
821 diag[k] = 0;
822 }
823 }
824
825 if (m_flow_right) {
826 double* rb = r + m_nsp;
827 double* xb = x + m_nsp;
828 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
829 }
830 if (m_flow_left) {
831 size_t nc = m_flow_left->nComponents();
832 const vector_fp& mwleft = m_phase_left->molecularWeights();
833 double* rb = r - nc;
834 double* xb = x - nc;
835 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
836 size_t nSkip = m_flow_left->rightExcessSpecies();
837 size_t l_offset = 0;
838 ThermoPhase* left_thermo = &m_flow_left->phase();
839 for (size_t nth = 0; nth < m_kin->nPhases(); nth++) {
840 if (&m_kin->thermo(nth) == left_thermo) {
841 l_offset = m_kin->kineticsSpeciesIndex(0, nth);
842 break;
843 }
844 }
845 for (size_t nl = 0; nl < m_left_nsp; nl++) {
846 if (nl != nSkip) {
847 rb[c_offset_Y+nl] += m_work[nl + l_offset]*mwleft[nl];
848 }
849 }
850 }
851}
852
853XML_Node& ReactingSurf1D::save(XML_Node& o, const double* const soln)
854{
855 const double* s = soln + loc();
856 XML_Node& dom = Domain1D::save(o, soln);
857 dom.addAttribute("type","surface");
858 addFloat(dom, "temperature", m_temp, "K");
859 for (size_t k=0; k < m_nsp; k++) {
860 addFloat(dom, "coverage", s[k], "", m_sphase->speciesName(k));
861 }
862 return dom;
863}
864
865void ReactingSurf1D::restore(const XML_Node& dom, double* soln,
866 int loglevel)
867{
868 Domain1D::restore(dom, soln, loglevel);
869 m_temp = getFloat(dom, "temperature");
870
871 m_fixed_cov.assign(m_nsp, 0.0);
872 for (size_t i = 0; i < dom.nChildren(); i++) {
873 const XML_Node& node = dom.child(i);
874 if (node.name() == "coverage") {
875 size_t k = m_sphase->speciesIndex(node.attrib("type"));
876 if (k != npos) {
877 m_fixed_cov[k] = soln[k] = node.fp_value();
878 }
879 }
880 }
881 m_sphase->setCoverages(&m_fixed_cov[0]);
882
883 resize(m_nsp, 1);
884}
885
886AnyMap ReactingSurf1D::serialize(const double* soln) const
887{
888 AnyMap state = Boundary1D::serialize(soln);
889 state["type"] = "reacting-surface";
890 state["temperature"] = m_temp;
891 state["phase"]["name"] = m_sphase->name();
892 AnyValue source =m_sphase->input().getMetadata("filename");
893 state["phase"]["source"] = source.empty() ? "<unknown>" : source.asString();
894 AnyMap cov;
895 for (size_t k = 0; k < m_nsp; k++) {
896 cov[m_sphase->speciesName(k)] = soln[k];
897 }
898 state["coverages"] = std::move(cov);
899
900 return state;
901}
902
903void ReactingSurf1D::restore(const AnyMap& state, double* soln, int loglevel)
904{
905 Boundary1D::restore(state, soln, loglevel);
906 m_temp = state["temperature"].asDouble();
907 const auto& cov = state["coverages"].as<AnyMap>();
908 for (size_t k = 0; k < m_nsp; k++) {
909 soln[k] = cov.getDouble(m_sphase->speciesName(k), 0.0);
910 }
911
912 // Warn about species not in the current phase
913 if (loglevel) {
914 for (auto& item : cov) {
915 if (m_sphase->speciesIndex(item.first) == npos) {
916 warn_user("OutletRes1D::restore", "Phase '{}' does not contain a "
917 "species named '{}'\nwhich was specified as having a coverage "
918 "of {}.",
919 m_sphase->name(), item.first, item.second.asDouble());
920 }
921 }
922 }
923}
924
925void ReactingSurf1D::showSolution(const double* x)
926{
927 writelog(" Temperature: {:10.4g} K \n", m_temp);
928 writelog(" Coverages: \n");
929 for (size_t k = 0; k < m_nsp; k++) {
930 writelog(" {:>20s} {:10.4g} \n", m_sphase->speciesName(k), x[k]);
931 }
932 writelog("\n");
933}
934}
Boundary objects for one-dimensional simulations.
const AnyValue & getMetadata(const std::string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
Definition: AnyMap.cpp:577
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:399
double getDouble(const std::string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition: AnyMap.cpp:1492
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:84
const std::string & asString() const
Return the held value, if it is a string.
Definition: AnyMap.cpp:716
bool empty() const
Return boolean indicating whether AnyValue is empty.
Definition: AnyMap.cpp:684
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
size_t lastPoint() const
The index of the last (that is, right-most) grid point belonging to this domain.
Definition: Domain1D.h:389
size_t domainIndex()
The left-to-right location of this domain.
Definition: Domain1D.h:58
size_t nComponents() const
Number of components at each grid point.
Definition: Domain1D.h:133
virtual AnyMap serialize(const double *soln) const
Save the state of this domain as an AnyMap.
Definition: Domain1D.cpp:172
size_t nPoints() const
Number of grid points in this domain.
Definition: Domain1D.h:155
virtual XML_Node & save(XML_Node &o, const doublereal *const sol)
Save the current solution for this domain into an XML_Node.
Definition: Domain1D.cpp:118
virtual void resize(size_t nv, size_t np)
Definition: Domain1D.cpp:41
virtual void restore(const XML_Node &dom, doublereal *soln, int loglevel)
Restore the solution for this domain from an XML_Node.
Definition: Domain1D.cpp:131
double prevSoln(size_t n, size_t j) const
Value of component n at point j in the previous solution.
Definition: Domain1D.h:424
size_t firstPoint() const
The index of the first (that is, left-most) grid point belonging to this domain.
Definition: Domain1D.h:381
void needJacUpdate()
Definition: Domain1D.cpp:110
virtual size_t loc(size_t j=0) const
Location of the start of the local solution vector in the global solution vector,.
Definition: Domain1D.h:373
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition: Kinetics.h:231
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition: Kinetics.h:171
virtual void getNetProductionRates(doublereal *wdot)
Species net production rates [kmol/m^3/s or kmol/m^2/s].
Definition: Kinetics.cpp:484
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:265
size_t nTotalSpecies() const
The total number of species in all phases participating in the kinetics mechanism.
Definition: Kinetics.h:243
virtual void init()
Definition: Boundary1D.cpp:402
virtual AnyMap serialize(const double *soln) const
Save the state of this domain as an AnyMap.
Definition: Boundary1D.cpp:476
virtual XML_Node & save(XML_Node &o, const double *const soln)
Save the current solution for this domain into an XML_Node.
Definition: Boundary1D.cpp:463
virtual void eval(size_t jg, double *xg, double *rg, integer *diagg, double rdt)
Evaluate the residual function at point j.
Definition: Boundary1D.cpp:414
virtual void restore(const XML_Node &dom, double *soln, int loglevel)
Restore the solution for this domain from an XML_Node.
Definition: Boundary1D.cpp:470
virtual void init()
Definition: Boundary1D.cpp:504
virtual void setMoleFractions(const std::string &xin)
Set the mole fractions by specifying a std::string.
Definition: Boundary1D.cpp:485
virtual AnyMap serialize(const double *soln) const
Save the state of this domain as an AnyMap.
Definition: Boundary1D.cpp:612
virtual XML_Node & save(XML_Node &o, const double *const soln)
Save the current solution for this domain into an XML_Node.
Definition: Boundary1D.cpp:581
virtual void eval(size_t jg, double *xg, double *rg, integer *diagg, double rdt)
Evaluate the residual function at point j.
Definition: Boundary1D.cpp:525
virtual void restore(const XML_Node &dom, double *soln, int loglevel)
Restore the solution for this domain from an XML_Node.
Definition: Boundary1D.cpp:593
virtual void setMoleFractions(const double *const x)
Set the mole fractions to the specified values.
Definition: Phase.cpp:339
std::string name() const
Return the name of the phase.
Definition: Phase.cpp:70
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:273
void setMoleFractionsByName(const compositionMap &xMap)
Set the species mole fractions by name.
Definition: Phase.cpp:380
const vector_fp & molecularWeights() const
Return a const reference to the internal vector of molecular weights.
Definition: Phase.cpp:509
std::string speciesName(size_t k) const
Name of the species with index k.
Definition: Phase.cpp:200
virtual void setTemperature(double temp)
Set the internally stored temperature of the phase (K).
Definition: Phase.h:719
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:187
void getMassFractions(double *const y) const
Get the species mass fractions.
Definition: Phase.cpp:585
virtual AnyMap serialize(const double *soln) const
Save the state of this domain as an AnyMap.
Definition: Boundary1D.cpp:886
virtual XML_Node & save(XML_Node &o, const double *const soln)
Save the current solution for this domain into an XML_Node.
Definition: Boundary1D.cpp:853
virtual std::string componentName(size_t n) const
Name of the nth component. May be overloaded.
Definition: Boundary1D.cpp:741
virtual void showSolution(const double *x)
Print the solution.
Definition: Boundary1D.cpp:925
virtual void eval(size_t jg, double *xg, double *rg, integer *diagg, double rdt)
Evaluate the residual function at point j.
Definition: Boundary1D.cpp:769
virtual void resetBadValues(double *xg)
Definition: Boundary1D.cpp:763
virtual void restore(const XML_Node &dom, double *soln, int loglevel)
Restore the solution for this domain from an XML_Node.
Definition: Boundary1D.cpp:865
size_t rightExcessSpecies() const
Index of the species on the right boundary with the largest mass fraction.
Definition: StFlow.h:281
void setGas(const doublereal *x, size_t j)
Set the gas object state to be consistent with the solution at point j.
Definition: StFlow.cpp:176
virtual void init()
Definition: Boundary1D.cpp:652
virtual AnyMap serialize(const double *soln) const
Save the state of this domain as an AnyMap.
Definition: Boundary1D.cpp:697
virtual XML_Node & save(XML_Node &o, const double *const soln)
Save the current solution for this domain into an XML_Node.
Definition: Boundary1D.cpp:682
virtual void showSolution(const double *x)
Print the solution.
Definition: Boundary1D.cpp:717
virtual void eval(size_t jg, double *xg, double *rg, integer *diagg, double rdt)
Evaluate the residual function at point j.
Definition: Boundary1D.cpp:657
virtual void restore(const XML_Node &dom, double *soln, int loglevel)
Restore the solution for this domain from an XML_Node.
Definition: Boundary1D.cpp:690
void setCoveragesNoNorm(const doublereal *theta)
Set the surface site fractions to a specified state.
Definition: SurfPhase.cpp:269
void getCoverages(doublereal *theta) const
Return a vector of surface coverages.
Definition: SurfPhase.cpp:277
virtual double size(size_t k) const
Returns the number of sites occupied by one molecule of species k.
Definition: SurfPhase.h:313
double siteDensity() const
Returns the site density.
Definition: SurfPhase.h:308
void setCoverages(const doublereal *theta)
Set the surface site fractions to a specified state.
Definition: SurfPhase.cpp:252
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:102
const AnyMap & input() const
Access input data associated with the phase description.
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:493
std::string name() const
Returns the name of the XML node.
Definition: xml.h:371
void addAttribute(const std::string &attrib, const std::string &value)
Add or modify an attribute of the current node.
Definition: xml.cpp:467
doublereal fp_value() const
Return the value of an XML node as a single double.
Definition: xml.cpp:457
size_t nChildren(bool discardComments=false) const
Return the number of children.
Definition: xml.cpp:557
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:547
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:164
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
doublereal getFloat(const XML_Node &parent, const std::string &name, const std::string &type="")
Get a floating-point value from a child element.
Definition: ctml.cpp:166
void addFloat(XML_Node &node, const std::string &titleString, const doublereal value, const std::string &unitsString="", const std::string &typeString="", const doublereal minval=Undef, const doublereal maxval=Undef)
This function adds a child node with the name, "float", with a value consisting of a single floating ...
Definition: ctml.cpp:21
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:184
void warn_user(const std::string &method, const std::string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition: global.h:245