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
6 #include "cantera/oneD/Boundary1D.h"
7 #include "cantera/oneD/OneDim.h"
8 #include "cantera/base/ctml.h"
9 #include "cantera/oneD/StFlow.h"
10
11 using namespace std;
12
13 namespace Cantera
14 {
15
Boundary1D()16 Boundary1D::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
_init(size_t n)29 void 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()) { // flow domain
46 m_flow_left = (StFlow*)&r;
47 m_left_nv = m_flow_left->nComponents();
48 m_left_points = m_flow_left->nPoints();
49 m_left_loc = container().start(m_index-1);
50 m_left_nsp = m_left_nv - c_offset_Y;
51 m_phase_left = &m_flow_left->phase();
52 } else {
53 throw CanteraError("Boundary1D::_init",
54 "Boundary domains can only be connected on the left to flow "
55 "domains, not type {} domains.", r.domainType());
56 }
57 }
58
59 // if this is not the last domain, see what is connected on the right
60 if (m_index + 1 < container().nDomains()) {
61 Domain1D& r = container().domain(m_index+1);
62 if (!r.isConnector()) { // flow domain
63 m_flow_right = (StFlow*)&r;
64 m_right_nv = m_flow_right->nComponents();
65 m_right_loc = container().start(m_index+1);
66 m_right_nsp = m_right_nv - c_offset_Y;
67 m_phase_right = &m_flow_right->phase();
68 } else {
69 throw CanteraError("Boundary1D::_init",
70 "Boundary domains can only be connected on the right to flow "
71 "domains, not type {} domains.", r.domainType());
72 }
73 }
74 }
75
76 // ---------------- Inlet1D methods ----------------
77
Inlet1D()78 Inlet1D::Inlet1D()
79 : m_V0(0.0)
80 , m_nsp(0)
81 , m_flow(0)
82 {
83 m_type = cInletType;
84 m_xstr = "";
85 }
86
showSolution(const double * x)87 void Inlet1D::showSolution(const double* x)
88 {
89 writelog(" Mass Flux: {:10.4g} kg/m^2/s \n", m_mdot);
90 writelog(" Temperature: {:10.4g} K \n", m_temp);
91 if (m_flow) {
92 writelog(" Mass Fractions: \n");
93 for (size_t k = 0; k < m_flow->phase().nSpecies(); k++) {
94 if (m_yin[k] != 0.0) {
95 writelog(" {:>16s} {:10.4g} \n",
96 m_flow->phase().speciesName(k), m_yin[k]);
97 }
98 }
99 }
100 writelog("\n");
101 }
102
setMoleFractions(const std::string & xin)103 void Inlet1D::setMoleFractions(const std::string& xin)
104 {
105 m_xstr = xin;
106 if (m_flow) {
107 m_flow->phase().setMoleFractionsByName(xin);
108 m_flow->phase().getMassFractions(m_yin.data());
109 needJacUpdate();
110 }
111 }
112
setMoleFractions(const double * xin)113 void Inlet1D::setMoleFractions(const double* xin)
114 {
115 if (m_flow) {
116 m_flow->phase().setMoleFractions(xin);
117 m_flow->phase().getMassFractions(m_yin.data());
118 needJacUpdate();
119 }
120 }
121
init()122 void Inlet1D::init()
123 {
124 _init(0);
125
126 // if a flow domain is present on the left, then this must be a right inlet.
127 // Note that an inlet object can only be a terminal object - it cannot have
128 // flows on both the left and right
129 if (m_flow_left) {
130 m_ilr = RightInlet;
131 m_flow = m_flow_left;
132 } else if (m_flow_right) {
133 m_ilr = LeftInlet;
134 m_flow = m_flow_right;
135 } else {
136 throw CanteraError("Inlet1D::init","no flow!");
137 }
138
139 // components = u, V, T, lambda, + mass fractions
140 m_nsp = m_flow->phase().nSpecies();
141 m_yin.resize(m_nsp, 0.0);
142 if (m_xstr != "") {
143 setMoleFractions(m_xstr);
144 } else {
145 m_yin[0] = 1.0;
146 }
147 }
148
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)149 void Inlet1D::eval(size_t jg, double* xg, double* rg,
150 integer* diagg, double rdt)
151 {
152 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
153 return;
154 }
155
156 if (m_ilr == LeftInlet) {
157 // Array elements corresponding to the first point of the flow domain
158 double* xb = xg + m_flow->loc();
159 double* rb = rg + m_flow->loc();
160
161 // The first flow residual is for u. This, however, is not modified by
162 // the inlet, since this is set within the flow domain from the
163 // continuity equation.
164
165 // spreading rate. The flow domain sets this to V(0),
166 // so for finite spreading rate subtract m_V0.
167 rb[c_offset_V] -= m_V0;
168
169 if (m_flow->doEnergy(0)) {
170 // The third flow residual is for T, where it is set to T(0). Subtract
171 // the local temperature to hold the flow T to the inlet T.
172 rb[c_offset_T] -= m_temp;
173 }
174
175 if (m_flow->fixed_mdot()) {
176 // The flow domain sets this to -rho*u. Add mdot to specify the mass
177 // flow rate.
178 rb[c_offset_L] += m_mdot;
179 } else {
180 // if the flow is a freely-propagating flame, mdot is not specified.
181 // Set mdot equal to rho*u, and also set lambda to zero.
182 m_mdot = m_flow->density(0)*xb[0];
183 rb[c_offset_L] = xb[c_offset_L];
184 }
185
186 // add the convective term to the species residual equations
187 for (size_t k = 0; k < m_nsp; k++) {
188 if (k != m_flow_right->leftExcessSpecies()) {
189 rb[c_offset_Y+k] += m_mdot*m_yin[k];
190 }
191 }
192
193 } else {
194 // right inlet
195 // Array elements corresponding to the flast point in the flow domain
196 double* rb = rg + loc() - m_flow->nComponents();
197 rb[c_offset_V] -= m_V0;
198 if (m_flow->doEnergy(m_flow->nPoints() - 1)) {
199 rb[c_offset_T] -= m_temp; // T
200 }
201 rb[c_offset_U] += m_mdot; // u
202 for (size_t k = 0; k < m_nsp; k++) {
203 if (k != m_flow_left->rightExcessSpecies()) {
204 rb[c_offset_Y+k] += m_mdot * m_yin[k];
205 }
206 }
207 }
208 }
209
save(XML_Node & o,const double * const soln)210 XML_Node& Inlet1D::save(XML_Node& o, const double* const soln)
211 {
212 XML_Node& inlt = Domain1D::save(o, soln);
213 inlt.addAttribute("type","inlet");
214 addFloat(inlt, "temperature", m_temp);
215 addFloat(inlt, "mdot", m_mdot);
216 for (size_t k=0; k < m_nsp; k++) {
217 addFloat(inlt, "massFraction", m_yin[k], "",
218 m_flow->phase().speciesName(k));
219 }
220 return inlt;
221 }
222
restore(const XML_Node & dom,double * soln,int loglevel)223 void Inlet1D::restore(const XML_Node& dom, double* soln, int loglevel)
224 {
225 Domain1D::restore(dom, soln, loglevel);
226 m_mdot = getFloat(dom, "mdot");
227 m_temp = getFloat(dom, "temperature");
228
229 m_yin.assign(m_nsp, 0.0);
230
231 for (size_t i = 0; i < dom.nChildren(); i++) {
232 const XML_Node& node = dom.child(i);
233 if (node.name() == "massFraction") {
234 size_t k = m_flow->phase().speciesIndex(node.attrib("type"));
235 if (k != npos) {
236 m_yin[k] = node.fp_value();
237 }
238 }
239 }
240 resize(0, 1);
241 }
242
243 // ------------- Empty1D -------------
244
init()245 void Empty1D::init()
246 {
247 _init(0);
248 }
249
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)250 void Empty1D::eval(size_t jg, double* xg, double* rg,
251 integer* diagg, double rdt)
252 {
253 }
254
save(XML_Node & o,const double * const soln)255 XML_Node& Empty1D::save(XML_Node& o, const double* const soln)
256 {
257 XML_Node& symm = Domain1D::save(o, soln);
258 symm.addAttribute("type","empty");
259 return symm;
260 }
261
restore(const XML_Node & dom,double * soln,int loglevel)262 void Empty1D::restore(const XML_Node& dom, double* soln, int loglevel)
263 {
264 Domain1D::restore(dom, soln, loglevel);
265 resize(0, 1);
266 }
267
268 // -------------- Symm1D --------------
269
init()270 void Symm1D::init()
271 {
272 _init(0);
273 }
274
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)275 void Symm1D::eval(size_t jg, double* xg, double* rg, integer* diagg,
276 double rdt)
277 {
278 if (jg != npos && (jg + 2< firstPoint() || jg > lastPoint() + 2)) {
279 return;
280 }
281
282 // start of local part of global arrays
283 double* x = xg + loc();
284 double* r = rg + loc();
285 integer* diag = diagg + loc();
286
287 if (m_flow_right) {
288 size_t nc = m_flow_right->nComponents();
289 double* xb = x;
290 double* rb = r;
291 int* db = diag;
292 db[c_offset_V] = 0;
293 db[c_offset_T] = 0;
294 rb[c_offset_V] = xb[c_offset_V] - xb[c_offset_V + nc]; // zero dV/dz
295 if (m_flow_right->doEnergy(0)) {
296 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc]; // zero dT/dz
297 }
298 }
299
300 if (m_flow_left) {
301 size_t nc = m_flow_left->nComponents();
302 double* xb = x - nc;
303 double* rb = r - nc;
304 int* db = diag - nc;
305 db[c_offset_V] = 0;
306 db[c_offset_T] = 0;
307 rb[c_offset_V] = xb[c_offset_V] - xb[c_offset_V - nc]; // zero dV/dz
308 if (m_flow_left->doEnergy(m_flow_left->nPoints() - 1)) {
309 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T - nc]; // zero dT/dz
310 }
311 }
312 }
313
save(XML_Node & o,const double * const soln)314 XML_Node& Symm1D::save(XML_Node& o, const double* const soln)
315 {
316 XML_Node& symm = Domain1D::save(o, soln);
317 symm.addAttribute("type","symmetry");
318 return symm;
319 }
320
restore(const XML_Node & dom,double * soln,int loglevel)321 void Symm1D::restore(const XML_Node& dom, double* soln, int loglevel)
322 {
323 Domain1D::restore(dom, soln, loglevel);
324 resize(0, 1);
325 }
326
327 // -------- Outlet1D --------
328
OutletRes1D()329 OutletRes1D::OutletRes1D()
330 : m_nsp(0)
331 , m_flow(0)
332 {
333 m_type = cOutletResType;
334 m_xstr = "";
335 }
336
init()337 void Outlet1D::init()
338 {
339 _init(0);
340
341 if (m_flow_right) {
342 m_flow_right->setViscosityFlag(false);
343 }
344 if (m_flow_left) {
345 m_flow_left->setViscosityFlag(false);
346 }
347 }
348
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)349 void Outlet1D::eval(size_t jg, double* xg, double* rg, integer* diagg,
350 double rdt)
351 {
352 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
353 return;
354 }
355
356 // start of local part of global arrays
357 double* x = xg + loc();
358 double* r = rg + loc();
359 integer* diag = diagg + loc();
360
361 if (m_flow_right) {
362 size_t nc = m_flow_right->nComponents();
363 double* xb = x;
364 double* rb = r;
365 rb[c_offset_U] = xb[c_offset_L];
366 if (m_flow_right->doEnergy(0)) {
367 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc];
368 }
369 for (size_t k = c_offset_Y; k < nc; k++) {
370 rb[k] = xb[k] - xb[k + nc];
371 }
372 }
373
374 if (m_flow_left) {
375 size_t nc = m_flow_left->nComponents();
376 double* xb = x - nc;
377 double* rb = r - nc;
378 int* db = diag - nc;
379
380 // zero Lambda
381 if (m_flow_left->fixed_mdot()) {
382 rb[c_offset_U] = xb[c_offset_L];
383 }
384
385 if (m_flow_left->doEnergy(m_flow_left->nPoints()-1)) {
386 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T - nc]; // zero T gradient
387 }
388 size_t kSkip = c_offset_Y + m_flow_left->rightExcessSpecies();
389 for (size_t k = c_offset_Y; k < nc; k++) {
390 if (k != kSkip) {
391 rb[k] = xb[k] - xb[k - nc]; // zero mass fraction gradient
392 db[k] = 0;
393 }
394 }
395 }
396 }
397
save(XML_Node & o,const double * const soln)398 XML_Node& Outlet1D::save(XML_Node& o, const double* const soln)
399 {
400 XML_Node& outlt = Domain1D::save(o, soln);
401 outlt.addAttribute("type","outlet");
402 return outlt;
403 }
404
restore(const XML_Node & dom,double * soln,int loglevel)405 void Outlet1D::restore(const XML_Node& dom, double* soln, int loglevel)
406 {
407 Domain1D::restore(dom, soln, loglevel);
408 resize(0, 1);
409 }
410
411 // -------- OutletRes1D --------
412
setMoleFractions(const std::string & xres)413 void OutletRes1D::setMoleFractions(const std::string& xres)
414 {
415 m_xstr = xres;
416 if (m_flow) {
417 m_flow->phase().setMoleFractionsByName(xres);
418 m_flow->phase().getMassFractions(m_yres.data());
419 needJacUpdate();
420 }
421 }
422
setMoleFractions(const double * xres)423 void OutletRes1D::setMoleFractions(const double* xres)
424 {
425 if (m_flow) {
426 m_flow->phase().setMoleFractions(xres);
427 m_flow->phase().getMassFractions(m_yres.data());
428 needJacUpdate();
429 }
430 }
431
init()432 void OutletRes1D::init()
433 {
434 _init(0);
435
436 if (m_flow_left) {
437 m_flow = m_flow_left;
438 } else if (m_flow_right) {
439 m_flow = m_flow_right;
440 } else {
441 throw CanteraError("OutletRes1D::init","no flow!");
442 }
443
444 m_nsp = m_flow->phase().nSpecies();
445 m_yres.resize(m_nsp, 0.0);
446 if (m_xstr != "") {
447 setMoleFractions(m_xstr);
448 } else {
449 m_yres[0] = 1.0;
450 }
451 }
452
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)453 void OutletRes1D::eval(size_t jg, double* xg, double* rg,
454 integer* diagg, double rdt)
455 {
456 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
457 return;
458 }
459
460 // start of local part of global arrays
461 double* x = xg + loc();
462 double* r = rg + loc();
463 integer* diag = diagg + loc();
464
465 if (m_flow_right) {
466 size_t nc = m_flow_right->nComponents();
467 double* xb = x;
468 double* rb = r;
469
470 // this seems wrong...
471 // zero Lambda
472 rb[c_offset_U] = xb[c_offset_L];
473
474 if (m_flow_right->doEnergy(0)) {
475 // zero gradient for T
476 rb[c_offset_T] = xb[c_offset_T] - xb[c_offset_T + nc];
477 }
478
479 // specified mass fractions
480 for (size_t k = c_offset_Y; k < nc; k++) {
481 rb[k] = xb[k] - m_yres[k-c_offset_Y];
482 }
483 }
484
485 if (m_flow_left) {
486 size_t nc = m_flow_left->nComponents();
487 double* xb = x - nc;
488 double* rb = r - nc;
489 int* db = diag - nc;
490
491 if (!m_flow_left->fixed_mdot()) {
492 ;
493 } else {
494 rb[c_offset_U] = xb[c_offset_L]; // zero Lambda
495 }
496 if (m_flow_left->doEnergy(m_flow_left->nPoints()-1)) {
497 rb[c_offset_T] = xb[c_offset_T] - m_temp; // zero dT/dz
498 }
499 size_t kSkip = m_flow_left->rightExcessSpecies();
500 for (size_t k = c_offset_Y; k < nc; k++) {
501 if (k != kSkip) {
502 rb[k] = xb[k] - m_yres[k-c_offset_Y]; // fixed Y
503 db[k] = 0;
504 }
505 }
506 }
507 }
508
save(XML_Node & o,const double * const soln)509 XML_Node& OutletRes1D::save(XML_Node& o, const double* const soln)
510 {
511 XML_Node& outlt = Domain1D::save(o, soln);
512 outlt.addAttribute("type","outletres");
513 addFloat(outlt, "temperature", m_temp, "K");
514 for (size_t k=0; k < m_nsp; k++) {
515 addFloat(outlt, "massFraction", m_yres[k], "",
516 m_flow->phase().speciesName(k));
517 }
518 return outlt;
519 }
520
restore(const XML_Node & dom,double * soln,int loglevel)521 void OutletRes1D::restore(const XML_Node& dom, double* soln, int loglevel)
522 {
523 Domain1D::restore(dom, soln, loglevel);
524 m_temp = getFloat(dom, "temperature");
525
526 m_yres.assign(m_nsp, 0.0);
527 for (size_t i = 0; i < dom.nChildren(); i++) {
528 const XML_Node& node = dom.child(i);
529 if (node.name() == "massFraction") {
530 size_t k = m_flow->phase().speciesIndex(node.attrib("type"));
531 if (k != npos) {
532 m_yres[k] = node.fp_value();
533 }
534 }
535 }
536
537 resize(0, 1);
538 }
539
540 // -------- Surf1D --------
541
init()542 void Surf1D::init()
543 {
544 _init(0);
545 }
546
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)547 void Surf1D::eval(size_t jg, double* xg, double* rg,
548 integer* diagg, double rdt)
549 {
550 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
551 return;
552 }
553
554 // start of local part of global arrays
555 double* x = xg + loc();
556 double* r = rg + loc();
557
558 if (m_flow_right) {
559 double* rb = r;
560 double* xb = x;
561 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
562 }
563
564 if (m_flow_left) {
565 size_t nc = m_flow_left->nComponents();
566 double* rb = r - nc;
567 double* xb = x - nc;
568 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
569 }
570 }
571
save(XML_Node & o,const double * const soln)572 XML_Node& Surf1D::save(XML_Node& o, const double* const soln)
573 {
574 XML_Node& inlt = Domain1D::save(o, soln);
575 inlt.addAttribute("type","surface");
576 addFloat(inlt, "temperature", m_temp);
577 return inlt;
578 }
579
restore(const XML_Node & dom,double * soln,int loglevel)580 void Surf1D::restore(const XML_Node& dom, double* soln, int loglevel)
581 {
582 Domain1D::restore(dom, soln, loglevel);
583 m_temp = getFloat(dom, "temperature");
584 resize(0, 1);
585 }
586
showSolution_s(std::ostream & s,const double * x)587 void Surf1D::showSolution_s(std::ostream& s, const double* x)
588 {
589 s << "------------------- Surface " << domainIndex() << " ------------------- " << std::endl;
590 s << " temperature: " << m_temp << " K" << std::endl;
591 }
592
showSolution(const double * x)593 void Surf1D::showSolution(const double* x)
594 {
595 writelog(" Temperature: {:10.4g} K \n\n", m_temp);
596 }
597
598 // -------- ReactingSurf1D --------
599
ReactingSurf1D()600 ReactingSurf1D::ReactingSurf1D()
601 : m_kin(0)
602 , m_surfindex(0)
603 , m_nsp(0)
604 {
605 m_type = cSurfType;
606 }
607
setKineticsMgr(InterfaceKinetics * kin)608 void ReactingSurf1D::setKineticsMgr(InterfaceKinetics* kin)
609 {
610 m_kin = kin;
611 m_surfindex = kin->surfacePhaseIndex();
612 m_sphase = (SurfPhase*)&kin->thermo(m_surfindex);
613 m_nsp = m_sphase->nSpecies();
614 m_enabled = true;
615 }
616
componentName(size_t n) const617 string ReactingSurf1D::componentName(size_t n) const
618 {
619 if (n < m_nsp) {
620 return m_sphase->speciesName(n);
621 } else {
622 return "<unknown>";
623 }
624 }
625
init()626 void ReactingSurf1D::init()
627 {
628 m_nv = m_nsp;
629 _init(m_nsp);
630 m_fixed_cov.resize(m_nsp, 0.0);
631 m_fixed_cov[0] = 1.0;
632 m_work.resize(m_kin->nTotalSpecies(), 0.0);
633
634 for (size_t n = 0; n < m_nsp; n++) {
635 setBounds(n, -1.0e-5, 2.0);
636 }
637 }
638
resetBadValues(double * xg)639 void ReactingSurf1D::resetBadValues(double* xg) {
640 double* x = xg + loc();
641 m_sphase->setCoverages(x);
642 m_sphase->getCoverages(x);
643 }
644
eval(size_t jg,double * xg,double * rg,integer * diagg,double rdt)645 void ReactingSurf1D::eval(size_t jg, double* xg, double* rg,
646 integer* diagg, double rdt)
647 {
648 if (jg != npos && (jg + 2 < firstPoint() || jg > lastPoint() + 2)) {
649 return;
650 }
651
652 // start of local part of global arrays
653 double* x = xg + loc();
654 double* r = rg + loc();
655 integer* diag = diagg + loc();
656
657 // set the coverages
658 double sum = 0.0;
659 for (size_t k = 0; k < m_nsp; k++) {
660 m_work[k] = x[k];
661 sum += x[k];
662 }
663 m_sphase->setTemperature(m_temp);
664 m_sphase->setCoveragesNoNorm(m_work.data());
665
666 // set the left gas state to the adjacent point
667
668 size_t leftloc = 0, rightloc = 0;
669 size_t pnt = 0;
670
671 if (m_flow_left) {
672 leftloc = m_flow_left->loc();
673 pnt = m_flow_left->nPoints() - 1;
674 m_flow_left->setGas(xg + leftloc, pnt);
675 }
676
677 if (m_flow_right) {
678 rightloc = m_flow_right->loc();
679 m_flow_right->setGas(xg + rightloc, 0);
680 }
681
682 m_kin->getNetProductionRates(m_work.data());
683 double rs0 = 1.0/m_sphase->siteDensity();
684 size_t ioffset = m_kin->kineticsSpeciesIndex(0, m_surfindex);
685
686 if (m_enabled) {
687 for (size_t k = 0; k < m_nsp; k++) {
688 r[k] = m_work[k + ioffset] * m_sphase->size(k) * rs0;
689 r[k] -= rdt*(x[k] - prevSoln(k,0));
690 diag[k] = 1;
691 }
692 r[0] = 1.0 - sum;
693 diag[0] = 0;
694 } else {
695 for (size_t k = 0; k < m_nsp; k++) {
696 r[k] = x[k] - m_fixed_cov[k];
697 diag[k] = 0;
698 }
699 }
700
701 if (m_flow_right) {
702 double* rb = r + m_nsp;
703 double* xb = x + m_nsp;
704 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
705 }
706 if (m_flow_left) {
707 size_t nc = m_flow_left->nComponents();
708 const vector_fp& mwleft = m_phase_left->molecularWeights();
709 double* rb = r - nc;
710 double* xb = x - nc;
711 rb[c_offset_T] = xb[c_offset_T] - m_temp; // specified T
712 size_t nSkip = m_flow_left->rightExcessSpecies();
713 size_t l_offset = 0;
714 ThermoPhase* left_thermo = &m_flow_left->phase();
715 for (size_t nth = 0; nth < m_kin->nPhases(); nth++) {
716 if (&m_kin->thermo(nth) == left_thermo) {
717 l_offset = m_kin->kineticsSpeciesIndex(0, nth);
718 break;
719 }
720 }
721 for (size_t nl = 0; nl < m_left_nsp; nl++) {
722 if (nl != nSkip) {
723 rb[c_offset_Y+nl] += m_work[nl + l_offset]*mwleft[nl];
724 }
725 }
726 }
727 }
728
save(XML_Node & o,const double * const soln)729 XML_Node& ReactingSurf1D::save(XML_Node& o, const double* const soln)
730 {
731 const double* s = soln + loc();
732 XML_Node& dom = Domain1D::save(o, soln);
733 dom.addAttribute("type","surface");
734 addFloat(dom, "temperature", m_temp, "K");
735 for (size_t k=0; k < m_nsp; k++) {
736 addFloat(dom, "coverage", s[k], "", m_sphase->speciesName(k));
737 }
738 return dom;
739 }
740
restore(const XML_Node & dom,double * soln,int loglevel)741 void ReactingSurf1D::restore(const XML_Node& dom, double* soln,
742 int loglevel)
743 {
744 Domain1D::restore(dom, soln, loglevel);
745 m_temp = getFloat(dom, "temperature");
746
747 m_fixed_cov.assign(m_nsp, 0.0);
748 for (size_t i = 0; i < dom.nChildren(); i++) {
749 const XML_Node& node = dom.child(i);
750 if (node.name() == "coverage") {
751 size_t k = m_sphase->speciesIndex(node.attrib("type"));
752 if (k != npos) {
753 m_fixed_cov[k] = soln[k] = node.fp_value();
754 }
755 }
756 }
757 m_sphase->setCoverages(&m_fixed_cov[0]);
758
759 resize(m_nsp, 1);
760 }
761
showSolution(const double * x)762 void ReactingSurf1D::showSolution(const double* x)
763 {
764 writelog(" Temperature: {:10.4g} K \n", m_temp);
765 writelog(" Coverages: \n");
766 for (size_t k = 0; k < m_nsp; k++) {
767 writelog(" {:>20s} {:10.4g} \n", m_sphase->speciesName(k), x[k]);
768 }
769 writelog("\n");
770 }
771 }
772