Cantera  3.2.0a5
Loading...
Searching...
No Matches
Reactor.cpp
Go to the documentation of this file.
1//! @file Reactor.cpp A zero-dimensional reactor
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
16
17#include <boost/math/tools/roots.hpp>
18
19using namespace std;
20namespace bmt = boost::math::tools;
21
22namespace Cantera
23{
24
25// TODO: After Cantera 3.2, this method can be replaced by delegating to
26// Reactor::Reactor(sol, true, name)
27Reactor::Reactor(shared_ptr<Solution> sol, const string& name)
28 : ReactorBase(sol, name)
29{
30 m_kin = m_solution->kinetics().get();
31 setChemistryEnabled(m_kin->nReactions() > 0);
32 m_vol = 1.0; // By default, the volume is set to 1.0 m^3.
33}
34
35Reactor::Reactor(shared_ptr<Solution> sol, bool clone, const string& name)
36 : ReactorBase(sol, clone, name)
37{
38 m_kin = m_solution->kinetics().get();
39 setChemistryEnabled(m_kin->nReactions() > 0);
40 m_vol = 1.0; // By default, the volume is set to 1.0 m^3.
41}
42
43void Reactor::setDerivativeSettings(AnyMap& settings)
44{
45 m_kin->setDerivativeSettings(settings);
46 // translate settings to surfaces
47 for (auto S : m_surfaces) {
48 S->kinetics()->setDerivativeSettings(settings);
49 }
50}
51
52void Reactor::setKinetics(Kinetics& kin)
53{
54 warn_deprecated("Reactor::setKinetics",
55 "After Cantera 3.2, a change of reactor contents after instantiation "
56 "will be disabled.");
57 m_kin = &kin;
58 setChemistryEnabled(m_kin->nReactions() > 0);
59}
60
61void Reactor::getState(double* y)
62{
63 if (m_thermo == 0) {
64 throw CanteraError("Reactor::getState",
65 "Error: reactor is empty.");
66 }
67 m_thermo->restoreState(m_state);
68
69 // set the first component to the total mass
70 m_mass = m_thermo->density() * m_vol;
71 y[0] = m_mass;
72
73 // set the second component to the total volume
74 y[1] = m_vol;
75
76 // set the third component to the total internal energy
77 y[2] = m_thermo->intEnergy_mass() * m_mass;
78
79 // set components y+3 ... y+K+2 to the mass fractions of each species
80 m_thermo->getMassFractions(y+3);
81
82 // set the remaining components to the surface species
83 // coverages on the walls
84 getSurfaceInitialConditions(y + m_nsp + 3);
85}
86
87void Reactor::getSurfaceInitialConditions(double* y)
88{
89 size_t loc = 0;
90 for (auto& S : m_surfaces) {
91 S->getCoverages(y + loc);
92 loc += S->thermo()->nSpecies();
93 }
94}
95
96void Reactor::initialize(double t0)
97{
98 if (!m_thermo || (m_chem && !m_kin)) {
99 throw CanteraError("Reactor::initialize", "Reactor contents not set"
100 " for reactor '" + m_name + "'.");
101 }
102 m_thermo->restoreState(m_state);
103 m_sdot.resize(m_nsp, 0.0);
104 m_wdot.resize(m_nsp, 0.0);
105 updateConnected(true);
106
107 for (size_t n = 0; n < m_wall.size(); n++) {
108 WallBase* W = m_wall[n];
109 W->initialize();
110 }
111
112 m_nv = m_nsp + 3;
113 m_nv_surf = 0;
114 size_t maxnt = 0;
115 for (auto& S : m_surfaces) {
116 m_nv_surf += S->thermo()->nSpecies();
117 size_t nt = S->kinetics()->nTotalSpecies();
118 maxnt = std::max(maxnt, nt);
119 }
120 m_nv += m_nv_surf;
121 m_work.resize(maxnt);
122}
123
124size_t Reactor::nSensParams() const
125{
126 size_t ns = m_sensParams.size();
127 for (auto& S : m_surfaces) {
128 ns += S->nSensParams();
129 }
130 return ns;
131}
132
133void Reactor::updateState(double* y)
134{
135 // The components of y are [0] the total mass, [1] the total volume,
136 // [2] the total internal energy, [3...K+3] are the mass fractions of each
137 // species, and [K+3...] are the coverages of surface species on each wall.
138 m_mass = y[0];
139 m_vol = y[1];
140 m_thermo->setMassFractions_NoNorm(y+3);
141
142 if (m_energy) {
143 double U = y[2];
144 // Residual function: error in internal energy as a function of T
145 auto u_err = [this, U](double T) {
146 m_thermo->setState_TD(T, m_mass / m_vol);
147 return m_thermo->intEnergy_mass() * m_mass - U;
148 };
149
150 double T = m_thermo->temperature();
151 boost::uintmax_t maxiter = 100;
152 pair<double, double> TT;
153 try {
154 TT = bmt::bracket_and_solve_root(
155 u_err, T, 1.2, true, bmt::eps_tolerance<double>(48), maxiter);
156 } catch (std::exception&) {
157 // Try full-range bisection if bracketing fails (for example, near
158 // temperature limits for the phase's equation of state)
159 try {
160 TT = bmt::bisect(u_err, m_thermo->minTemp(), m_thermo->maxTemp(),
161 bmt::eps_tolerance<double>(48), maxiter);
162 } catch (std::exception& err2) {
163 // Set m_thermo back to a reasonable state if root finding fails
164 m_thermo->setState_TD(T, m_mass / m_vol);
165 throw CanteraError("Reactor::updateState",
166 "{}\nat U = {}, rho = {}", err2.what(), U, m_mass / m_vol);
167 }
168 }
169 if (fabs(TT.first - TT.second) > 1e-7*TT.first) {
170 throw CanteraError("Reactor::updateState", "root finding failed");
171 }
172 m_thermo->setState_TD(TT.second, m_mass / m_vol);
173 } else {
174 m_thermo->setDensity(m_mass/m_vol);
175 }
176
177 updateConnected(true);
178 updateSurfaceState(y + m_nsp + 3);
179}
180
181void Reactor::updateSurfaceState(double* y)
182{
183 size_t loc = 0;
184 for (auto& S : m_surfaces) {
185 S->setCoverages(y+loc);
186 loc += S->thermo()->nSpecies();
187 }
188}
189
190void Reactor::updateConnected(bool updatePressure) {
191 // save parameters needed by other connected reactors
192 m_enthalpy = m_thermo->enthalpy_mass();
193 if (updatePressure) {
194 m_pressure = m_thermo->pressure();
195 }
196 try {
197 m_intEnergy = m_thermo->intEnergy_mass();
198 } catch (NotImplementedError&) {
199 m_intEnergy = NAN;
200 }
201 m_thermo->saveState(m_state);
202
203 // Update the mass flow rate of connected flow devices
204 double time = 0.0;
205 if (m_net != nullptr) {
206 time = (timeIsIndependent()) ? m_net->time() : m_net->distance();
207 }
208 for (size_t i = 0; i < m_outlet.size(); i++) {
209 m_outlet[i]->setSimTime(time);
210 m_outlet[i]->updateMassFlowRate(time);
211 }
212 for (size_t i = 0; i < m_inlet.size(); i++) {
213 m_inlet[i]->setSimTime(time);
214 m_inlet[i]->updateMassFlowRate(time);
215 }
216 for (size_t i = 0; i < m_wall.size(); i++) {
217 m_wall[i]->setSimTime(time);
218 }
219}
220
221void Reactor::eval(double time, double* LHS, double* RHS)
222{
223 double& dmdt = RHS[0];
224 double* mdYdt = RHS + 3; // mass * dY/dt
225
226 evalWalls(time);
227 m_thermo->restoreState(m_state);
228 const vector<double>& mw = m_thermo->molecularWeights();
229 const double* Y = m_thermo->massFractions();
230
231 evalSurfaces(LHS + m_nsp + 3, RHS + m_nsp + 3, m_sdot.data());
232 // mass added to gas phase from surface reactions
233 double mdot_surf = dot(m_sdot.begin(), m_sdot.end(), mw.begin());
234 dmdt = mdot_surf;
235
236 // volume equation
237 RHS[1] = m_vdot;
238
239 if (m_chem) {
240 m_kin->getNetProductionRates(&m_wdot[0]); // "omega dot"
241 }
242
243 for (size_t k = 0; k < m_nsp; k++) {
244 // production in gas phase and from surfaces
245 mdYdt[k] = (m_wdot[k] * m_vol + m_sdot[k]) * mw[k];
246 // dilution by net surface mass flux
247 mdYdt[k] -= Y[k] * mdot_surf;
248 LHS[k+3] = m_mass;
249 }
250
251 // Energy equation.
252 // @f[
253 // \dot U = -P\dot V + A \dot q + \dot m_{in} h_{in} - \dot m_{out} h.
254 // @f]
255 if (m_energy) {
256 RHS[2] = - m_thermo->pressure() * m_vdot + m_Qdot;
257 } else {
258 RHS[2] = 0.0;
259 }
260
261 // add terms for outlets
262 for (auto outlet : m_outlet) {
263 double mdot = outlet->massFlowRate();
264 dmdt -= mdot; // mass flow out of system
265 if (m_energy) {
266 RHS[2] -= mdot * m_enthalpy;
267 }
268 }
269
270 // add terms for inlets
271 for (auto inlet : m_inlet) {
272 double mdot = inlet->massFlowRate();
273 dmdt += mdot; // mass flow into system
274 for (size_t n = 0; n < m_nsp; n++) {
275 double mdot_spec = inlet->outletSpeciesMassFlowRate(n);
276 // flow of species into system and dilution by other species
277 mdYdt[n] += (mdot_spec - mdot * Y[n]);
278 }
279 if (m_energy) {
280 RHS[2] += mdot * inlet->enthalpy_mass();
281 }
282 }
283}
284
285void Reactor::evalWalls(double t)
286{
287 // time is currently unused
288 m_vdot = 0.0;
289 m_Qdot = 0.0;
290 for (size_t i = 0; i < m_wall.size(); i++) {
291 int f = 2 * m_lr[i] - 1;
292 m_vdot -= f * m_wall[i]->expansionRate();
293 m_Qdot += f * m_wall[i]->heatRate();
294 }
295}
296
297void Reactor::evalSurfaces(double* LHS, double* RHS, double* sdot)
298{
299 fill(sdot, sdot + m_nsp, 0.0);
300 size_t loc = 0; // offset into ydot
301
302 for (auto S : m_surfaces) {
303 Kinetics* kin = S->kinetics();
304 SurfPhase* surf = S->thermo();
305
306 double rs0 = 1.0/surf->siteDensity();
307 size_t nk = surf->nSpecies();
308 double sum = 0.0;
309 S->restoreState();
310 kin->getNetProductionRates(&m_work[0]);
311 for (size_t k = 1; k < nk; k++) {
312 RHS[loc + k] = m_work[k] * rs0 * surf->size(k);
313 sum -= RHS[loc + k];
314 }
315 RHS[loc] = sum;
316 loc += nk;
317
318 size_t bulkloc = kin->kineticsSpeciesIndex(m_thermo->speciesName(0));
319 double wallarea = S->area();
320 for (size_t k = 0; k < m_nsp; k++) {
321 sdot[k] += m_work[bulkloc + k] * wallarea;
322 }
323 }
324}
325
326vector<size_t> Reactor::steadyConstraints() const
327{
328 if (!energyEnabled()) {
329 throw CanteraError("Reactor::steadyConstraints", "Steady state solver cannot"
330 " be used with {0} when energy equation is disabled."
331 "\nConsider using IdealGas{0} instead.\n"
332 "See https://github.com/Cantera/enhancements/issues/234", type());
333 }
334 if (nSurfs() != 0) {
335 throw CanteraError("Reactor::steadyConstraints", "Steady state solver cannot"
336 " currently be used when reactor surfaces are present.\n"
337 "See https://github.com/Cantera/enhancements/issues/234.");
338 }
339 return {1}; // volume
340}
341
342Eigen::SparseMatrix<double> Reactor::finiteDifferenceJacobian()
343{
344 if (m_nv == 0) {
345 throw CanteraError("Reactor::finiteDifferenceJacobian",
346 "Reactor must be initialized first.");
347 }
348 // clear former jacobian elements
349 m_jac_trips.clear();
350
351 Eigen::ArrayXd yCurrent(m_nv);
352 getState(yCurrent.data());
353 double time = (m_net != nullptr) ? m_net->time() : 0.0;
354
355 Eigen::ArrayXd yPerturbed = yCurrent;
356 Eigen::ArrayXd lhsPerturbed(m_nv), lhsCurrent(m_nv);
357 Eigen::ArrayXd rhsPerturbed(m_nv), rhsCurrent(m_nv);
358 lhsCurrent = 1.0;
359 rhsCurrent = 0.0;
360 updateState(yCurrent.data());
361 eval(time, lhsCurrent.data(), rhsCurrent.data());
362
363 double rel_perturb = std::sqrt(std::numeric_limits<double>::epsilon());
364 double atol = (m_net != nullptr) ? m_net->atol() : 1e-15;
365
366 for (size_t j = 0; j < m_nv; j++) {
367 yPerturbed = yCurrent;
368 double delta_y = std::max(std::abs(yCurrent[j]), 1000 * atol) * rel_perturb;
369 yPerturbed[j] += delta_y;
370
371 updateState(yPerturbed.data());
372 lhsPerturbed = 1.0;
373 rhsPerturbed = 0.0;
374 eval(time, lhsPerturbed.data(), rhsPerturbed.data());
375
376 // d ydot_i/dy_j
377 for (size_t i = 0; i < m_nv; i++) {
378 double ydotPerturbed = rhsPerturbed[i] / lhsPerturbed[i];
379 double ydotCurrent = rhsCurrent[i] / lhsCurrent[i];
380 if (ydotCurrent != ydotPerturbed) {
381 m_jac_trips.emplace_back(
382 static_cast<int>(i), static_cast<int>(j),
383 (ydotPerturbed - ydotCurrent) / delta_y);
384 }
385 }
386 }
387 updateState(yCurrent.data());
388
389 Eigen::SparseMatrix<double> jac(m_nv, m_nv);
390 jac.setFromTriplets(m_jac_trips.begin(), m_jac_trips.end());
391 return jac;
392}
393
394
395void Reactor::evalSurfaces(double* RHS, double* sdot)
396{
397 fill(sdot, sdot + m_nsp, 0.0);
398 size_t loc = 0; // offset into ydot
399
400 for (auto S : m_surfaces) {
401 Kinetics* kin = S->kinetics();
402 SurfPhase* surf = S->thermo();
403
404 double rs0 = 1.0/surf->siteDensity();
405 size_t nk = surf->nSpecies();
406 double sum = 0.0;
407 S->restoreState();
408 kin->getNetProductionRates(&m_work[0]);
409 for (size_t k = 1; k < nk; k++) {
410 RHS[loc + k] = m_work[k] * rs0 * surf->size(k);
411 sum -= RHS[loc + k];
412 }
413 RHS[loc] = sum;
414 loc += nk;
415
416 size_t bulkloc = kin->kineticsSpeciesIndex(m_thermo->speciesName(0));
417 double wallarea = S->area();
418 for (size_t k = 0; k < m_nsp; k++) {
419 sdot[k] += m_work[bulkloc + k] * wallarea;
420 }
421 }
422}
423
424void Reactor::addSensitivityReaction(size_t rxn)
425{
426 if (!m_chem || rxn >= m_kin->nReactions()) {
427 throw CanteraError("Reactor::addSensitivityReaction",
428 "Reaction number out of range ({})", rxn);
429 }
430
431 size_t p = network().registerSensitivityParameter(
432 name()+": "+m_kin->reaction(rxn)->equation(), 1.0, 1.0);
433 m_sensParams.emplace_back(
434 SensitivityParameter{rxn, p, 1.0, SensParameterType::reaction});
435}
436
437void Reactor::addSensitivitySpeciesEnthalpy(size_t k)
438{
439 if (k >= m_thermo->nSpecies()) {
440 throw CanteraError("Reactor::addSensitivitySpeciesEnthalpy",
441 "Species index out of range ({})", k);
442 }
443
444 size_t p = network().registerSensitivityParameter(
445 name() + ": " + m_thermo->speciesName(k) + " enthalpy",
446 0.0, GasConstant * 298.15);
447 m_sensParams.emplace_back(
448 SensitivityParameter{k, p, m_thermo->Hf298SS(k),
449 SensParameterType::enthalpy});
450}
451
452size_t Reactor::speciesIndex(const string& nm) const
453{
454 // check for a gas species name
455 size_t k = m_thermo->speciesIndex(nm, false);
456 if (k != npos) {
457 return k;
458 }
459
460 // check for a wall species
461 size_t offset = m_nsp;
462 for (auto& S : m_surfaces) {
463 ThermoPhase* th = S->thermo();
464 k = th->speciesIndex(nm, false);
465 if (k != npos) {
466 return k + offset;
467 } else {
468 offset += th->nSpecies();
469 }
470 }
471 throw CanteraError("Reactor::speciesIndex",
472 "Species '{}' not found", nm);
473}
474
475size_t Reactor::componentIndex(const string& nm) const
476{
477 if (nm == "mass") {
478 return 0;
479 }
480 if (nm == "volume") {
481 return 1;
482 }
483 if (nm == "int_energy") {
484 return 2;
485 }
486 try {
487 return speciesIndex(nm) + 3;
488 } catch (const CanteraError&) {
489 throw CanteraError("Reactor::componentIndex",
490 "Component '{}' not found", nm);
491 }
492}
493
494string Reactor::componentName(size_t k) {
495 if (k == 0) {
496 return "mass";
497 } else if (k == 1) {
498 return "volume";
499 } else if (k == 2) {
500 return "int_energy";
501 } else if (k >= 3 && k < neq()) {
502 k -= 3;
503 if (k < m_thermo->nSpecies()) {
504 return m_thermo->speciesName(k);
505 } else {
506 k -= m_thermo->nSpecies();
507 }
508 for (auto& S : m_surfaces) {
509 ThermoPhase* th = S->thermo();
510 if (k < th->nSpecies()) {
511 return th->speciesName(k);
512 } else {
513 k -= th->nSpecies();
514 }
515 }
516 }
517 throw IndexError("Reactor::componentName", "component", k, m_nv);
518}
519
520double Reactor::upperBound(size_t k) const {
521 if (k == 0) {
522 return BigNumber; // mass
523 } else if (k == 1) {
524 return BigNumber; // volume
525 } else if (k == 2) {
526 return BigNumber; // internal energy
527 } else if (k >= 3 && k < m_nv) {
528 return 1.0; // species mass fraction or surface coverage
529 } else {
530 throw CanteraError("Reactor::upperBound", "Index {} is out of bounds.", k);
531 }
532}
533
534double Reactor::lowerBound(size_t k) const {
535 if (k == 0) {
536 return 0; // mass
537 } else if (k == 1) {
538 return 0; // volume
539 } else if (k == 2) {
540 return -BigNumber; // internal energy
541 } else if (k >= 3 && k < m_nv) {
542 return -Tiny; // species mass fraction or surface coverage
543 } else {
544 throw CanteraError("Reactor::lowerBound", "Index {} is out of bounds.", k);
545 }
546}
547
548void Reactor::resetBadValues(double* y) {
549 for (size_t k = 3; k < m_nv; k++) {
550 y[k] = std::max(y[k], 0.0);
551 }
552}
553
554void Reactor::applySensitivity(double* params)
555{
556 if (!params) {
557 return;
558 }
559 for (auto& p : m_sensParams) {
560 if (p.type == SensParameterType::reaction) {
561 p.value = m_kin->multiplier(p.local);
562 m_kin->setMultiplier(p.local, p.value*params[p.global]);
563 } else if (p.type == SensParameterType::enthalpy) {
564 m_thermo->modifyOneHf298SS(p.local, p.value + params[p.global]);
565 }
566 }
567 for (auto& S : m_surfaces) {
568 S->setSensitivityParameters(params);
569 }
570 m_thermo->invalidateCache();
571 if (m_kin) {
572 m_kin->invalidateCache();
573 }
574}
575
576void Reactor::resetSensitivity(double* params)
577{
578 if (!params) {
579 return;
580 }
581 for (auto& p : m_sensParams) {
582 if (p.type == SensParameterType::reaction) {
583 m_kin->setMultiplier(p.local, p.value);
584 } else if (p.type == SensParameterType::enthalpy) {
585 m_thermo->resetHf298(p.local);
586 }
587 }
588 for (auto& S : m_surfaces) {
589 S->resetSensitivityParameters();
590 }
591 m_thermo->invalidateCache();
592 if (m_kin) {
593 m_kin->invalidateCache();
594 }
595}
596
597void Reactor::setAdvanceLimits(const double *limits)
598{
599 if (m_thermo == 0) {
600 throw CanteraError("Reactor::setAdvanceLimits",
601 "Error: reactor is empty.");
602 }
603 m_advancelimits.assign(limits, limits + m_nv);
604
605 // resize to zero length if no limits are set
606 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
607 [](double val){return val>0;})) {
608 m_advancelimits.resize(0);
609 }
610}
611
612bool Reactor::getAdvanceLimits(double *limits) const
613{
614 bool has_limit = hasAdvanceLimits();
615 if (has_limit) {
616 std::copy(m_advancelimits.begin(), m_advancelimits.end(), limits);
617 } else {
618 std::fill(limits, limits + m_nv, -1.0);
619 }
620 return has_limit;
621}
622
623void Reactor::setAdvanceLimit(const string& nm, const double limit)
624{
625 size_t k = componentIndex(nm);
626
627 if (m_thermo == 0) {
628 throw CanteraError("Reactor::setAdvanceLimit",
629 "Error: reactor is empty.");
630 }
631 if (m_nv == 0) {
632 if (m_net == 0) {
633 throw CanteraError("Reactor::setAdvanceLimit",
634 "Cannot set limit on a reactor that is not "
635 "assigned to a ReactorNet object.");
636 } else {
637 m_net->initialize();
638 }
639 } else if (k > m_nv) {
640 throw CanteraError("Reactor::setAdvanceLimit",
641 "Index out of bounds.");
642 }
643 m_advancelimits.resize(m_nv, -1.0);
644 m_advancelimits[k] = limit;
645
646 // resize to zero length if no limits are set
647 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
648 [](double val){return val>0;})) {
649 m_advancelimits.resize(0);
650 }
651}
652
653}
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Header file for class ReactorSurface.
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for base class WallBase.
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
Public interface for kinetics managers.
Definition Kinetics.h:126
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition Kinetics.h:304
virtual void getNetProductionRates(double *wdot)
Species net production rates [kmol/m^3/s or kmol/m^2/s].
Definition Kinetics.cpp:489
An error indicating that an unimplemented function has been called.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:270
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:174
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:147
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition SurfPhase.h:114
double size(size_t k) const
Returns the number of sites occupied by one molecule of species k.
Definition SurfPhase.h:237
double siteDensity() const
Returns the site density.
Definition SurfPhase.h:232
Base class for a phase with thermodynamic properties.
Base class for 'walls' (walls, pistons, etc.) connecting reactors.
Definition Wall.h:23
virtual void initialize()
Called just before the start of integration.
Definition Wall.h:68
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition utilities.h:82
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:120
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
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:173
offset
Offsets of solution components in the 1D solution array.
Definition Flow1D.h:25
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997
const double BigNumber
largest number to compare to inf.
Definition ct_defs.h:160
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...