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