This commit is contained in:
Su Sun 2025-02-20 04:38:03 +00:00 committed by GitHub
commit 7f38f3fc95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1401 additions and 55 deletions

View File

@ -24,6 +24,8 @@ classdef Interface < handle & ThermoPhase & Kinetics
% Unit: kmol/m^2 for surface phases, kmol/m for edge phases.
siteDensity
coverages % Surface coverages of the species on an interface.
end
properties (SetAccess = protected)
@ -66,6 +68,10 @@ classdef Interface < handle & ThermoPhase & Kinetics
function delete(s)
% Delete :mat:class:`Interface` object.
if isempty(s.solnID)
return
end
ctFunc('soln_del', s.solnID);
end
@ -82,9 +88,7 @@ classdef Interface < handle & ThermoPhase & Kinetics
adj = Solution(id);
end
function c = coverages(s)
% Surface coverages of the species on an interface.
function c = get.coverages(s)
surfID = s.tpID;
nsp = s.nSpecies;
xx = zeros(1, nsp);
@ -107,10 +111,10 @@ classdef Interface < handle & ThermoPhase & Kinetics
c = pt.Value;
end
function setCoverages(s, cov, norm)
function set.coverages(s, val)
% Set surface coverages of the species on an interface.
%
% s.setCoverages(cov, norm)
% s.coverages = {cov, norm}
%
% :param s:
% Instance of class :mat:class:`Interface`
@ -126,10 +130,18 @@ classdef Interface < handle & ThermoPhase & Kinetics
% unphysical results, ``'nonorm'`` should be used only in rare cases, such
% as computing partial derivatives with respect to a species coverage.
if nargin == 3 && strcmp(norm, 'nonorm')
norm_flag = 0;
else
if iscell(val) && numel(val) >= 1 && numel(val) <= 2
cov = val{1};
norm_flag = 1;
if numel(val) == 2
norm = val{2};
if strcmp(norm, 'nonorm')
norm_flag = 0;
end
end
else
error('Input must be a cell array {cov} or {cov, norm}');
end
surfID = s.tpID;

View File

@ -156,7 +156,7 @@ classdef Mixture < handle
end
% Create an empty mixture.
m.mixID = calllib(ct, 'mix_new');
m.mixID = ctFunc('mix_new');
m.phases = phases;
% If phases are supplied, add them
@ -172,8 +172,12 @@ classdef Mixture < handle
% column contains the mole numbers of each phase.
[np, nc] = size(phases);
if nc ~= 2
error('Cell array of phases should have each phase on a new row');
% If mole numbers are not defined, default to 1 for all phases.
if nc == 1
newColumn = num2cell(ones(np, 1));
phases = [phases, newColumn];
elseif nc < 1 || nc > 2
error('Phases should have one or two columns');
end
for n = 1:np
@ -190,7 +194,10 @@ classdef Mixture < handle
function delete(m)
% Delete the :mat:class:`Mixture` object.
calllib(ct, 'mix_del', m.mixID);
if isempty(m.mixID)
return
end
ctFunc('mix_del', m.mixID);
end
%% Mixture Utility methods
@ -198,13 +205,13 @@ classdef Mixture < handle
function display(m)
% Display the state of the mixture on the terminal.
calllib(ct, 'mix_updatePhases', m.mixID);
ctFunc('mix_updatePhases', m.mixID);
[np, nc] = size(m.phases);
for n = 1:np
s = [sprintf('\n******************* Phase %d', n) ...
sprintf(' ******************************\n\n Moles: %12.6g', ...
phaseMoles(m, n))];
m.phaseMoles(n))];
disp(s);
display(m.phases{n, 1});
end
@ -237,54 +244,54 @@ classdef Mixture < handle
error('Negative moles');
end
calllib(ct, 'mix_addPhase', m.mixID, phase.tp_id, moles);
ctFunc('mix_addPhase', m.mixID, phase.tpID, moles);
end
%% Mixture Get methods
function temperature = get.T(m)
temperature = calllib(ct, 'mix_temperature', m.mixID);
temperature = ctFunc('mix_temperature', m.mixID);
end
function pressure = get.P(m)
pressure = calllib(ct, 'mix_pressure', m.mixID);
pressure = ctFunc('mix_pressure', m.mixID);
end
function n = get.nAtoms(m, e)
n = calllib(ct, 'mix_nPhases', m.mixID, k - 1, e - 1);
n = ctFunc('mix_nPhases', m.mixID, k - 1, e - 1);
end
function n = get.nElements(m)
n = calllib(ct, 'mix_nElements', m.mixID);
n = ctFunc('mix_nElements', m.mixID);
end
function n = get.nPhases(m)
n = calllib(ct, 'mix_nPhases', m.mixID);
n = ctFunc('mix_nPhases', m.mixID);
end
function n = get.nSpecies(m)
n = calllib(ct, 'mix_nSpecies', m.mixID);
n = ctFunc('mix_nSpecies', m.mixID);
end
function n = get.elementIndex(m, name)
n = calllib(ct, 'mix_elementIndex', m.mixID, name) + 1;
n = ctFunc('mix_elementIndex', m.mixID, name) + 1;
end
function n = get.speciesIndex(m, k, p)
n = calllib(ct, 'mix_speciesIndex', m.mixID, k - 1, p - 1) + 1;
n = ctFunc('mix_speciesIndex', m.mixID, k - 1, p - 1) + 1;
end
function moles = get.elementMoles(m, e)
if nargin == 2
moles = calllib(ct, 'mix_elementMoles', m.mixID, e)
moles = ctFunc('mix_elementMoles', m.mixID, e)
elseif nargin == 1
nel = m.nElements;
moles = zeros(1, nel);
for i = 1:nel
moles(i) = calllib(ct, 'mix_elementMoles', m.mixID, i);
moles(i) = ctFunc('mix_elementMoles', m.mixID, i-1);
end
else error('wrong number of arguments');
@ -295,13 +302,13 @@ classdef Mixture < handle
function moles = get.phaseMoles(m, n)
if nargin == 2
moles = calllib(ct, 'mix_phaseMoles', m.mixID, n);
moles = ctFunc('mix_phaseMoles', m.mixID, n);
elseif nargin == 1
np = m.nPhases;
moles = zeros(1, np);
for i = 1:np
moles(i) = calllib(ct, 'mix_phaseMoles', m.mixID, i);
moles(i) = ctFunc('mix_phaseMoles', m.mixID, i-1);
end
else error('wrong number of arguments');
@ -309,16 +316,16 @@ classdef Mixture < handle
end
function moles = speciesMoles(m, k)
function moles = get.speciesMoles(m, k)
if nargin == 2
moles = calllib(ct, 'mix_speciesMoles', m.mixID, k);
moles = ctFunc('mix_speciesMoles', m.mixID, k);
elseif nargin == 1
nsp = m.nSpecies;
moles = zeros(1, nsp);
for i = 1:nsp
moles(i) = calllib(ct, 'mix_speciesMoles', m.mixID, i);
moles(i) = ctFunc('mix_speciesMoles', m.mixID, i-1);
end
else error('wrong number of arguments');
@ -330,18 +337,18 @@ classdef Mixture < handle
nsp = m.nSpecies;
xx = zeros(1, nsp);
ptr = libpointer('doublePtr', xx);
calllib(ct, 'mix_getChemPotential', m.mixID, nsp, ptr);
ctFunc('mix_getChemPotentials', m.mixID, nsp, ptr);
mu = ptr.Value;
end
%% Mixture Set methods
function m = set.T(m, temp)
calllib(ct, 'mix_setTemperature', m.mixID, temp);
ctFunc('mix_setTemperature', m.mixID, temp);
end
function m = set.P(m, pressure)
calllib(ct, 'mix_setPressure', m.mixID, pressure);
ctFunc('mix_setPressure', m.mixID, pressure);
end
function setPhaseMoles(m, n, moles)
@ -356,7 +363,7 @@ classdef Mixture < handle
% :param moles:
% Number of moles to add. Units: kmol.
calllib(ct, 'mix_setPhaseMoles', m.mixID, n - 1, moles);
ctFunc('mix_setPhaseMoles', m.mixID, n - 1, moles);
end
function setSpeciesMoles(m, moles)
@ -379,9 +386,9 @@ classdef Mixture < handle
if isa(moles, 'double')
l = length(moles);
calllib(ct, 'mix_setMoles', m.mixID, l, moles);
elseif isa(moles, 'string')
calllib(ct, 'mix_setMolesByName', m.mixID, moles);
ctFunc('mix_setMoles', m.mixID, l, moles);
elseif isa(moles, 'char')
ctFunc('mix_setMolesByName', m.mixID, moles);
else
error('The input must be a vector or string!');
end
@ -454,7 +461,7 @@ classdef Mixture < handle
XY = 'TP'
end
r = calllib(ct, 'mix_equilibrate', m.mixID, XY, err, ...
r = ctFunc('mix_equilibrate', m.mixID, XY, err, ...
maxsteps, maxiter, loglevel);
end

View File

@ -86,6 +86,10 @@ classdef Solution < handle & ThermoPhase & Kinetics & Transport
function delete(s)
% Delete :mat:class:`Solution` object.
if isempty(s.solnID)
return
end
ctFunc('soln_del', s.solnID);
end

View File

@ -4,6 +4,10 @@ classdef Func1 < handle
id
end
properties (SetAccess = protected)
type
end
methods
%% Func1 Class Constructor
@ -36,7 +40,7 @@ classdef Func1 < handle
% * Advanced functors: ``'polynomial3'``, ``'Fourier'``, ``'Gaussian'``,
% ``'Arrhenius'``. Use vector parameter, for example
%
% >> x = Func('polynomial3', [1 2 3]) % x^2 + 2x + 3
% >> x = Func1('polynomial3', [1 2 3]) % x^2 + 2x + 3
%
% * Tabulation functors: ``'tabulated-linear'``,
% ``'tabulated-previous'``. Use pair of vector parameters, for example
@ -134,6 +138,9 @@ classdef Func1 < handle
function delete(f)
% Delete the :mat:class:`Func1` object.
if isempty(f.id)
return
end
ctFunc('func_del', f.id);
end
@ -179,7 +186,7 @@ classdef Func1 < handle
r = Func1(id);
end
function s = type(f)
function s = get.type(f)
% Return function type.
s = ctString('func_type', f.id);
end

View File

@ -101,6 +101,9 @@ classdef Domain1D < handle
function delete(d)
% Delete the :mat:class:`Domain1D` object.
if isempty(d.domainID)
return
end
ctFunc('domain_del', d.domainID);
end

View File

@ -50,6 +50,9 @@ classdef Sim1D < handle
function delete(s)
% Delete the :mat:class:`Sim1D` object.
if isempty(s.stID)
return
end
ctFunc('sim1D_del', s.stID);
end

View File

@ -83,6 +83,9 @@ classdef FlowDevice < handle
function delete(f)
% Delete the :mat:class:`FlowDevice` object.
if isempty(f.id)
return
end
ctFunc('flowdev_del', f.id);
end

View File

@ -140,6 +140,9 @@ classdef Reactor < handle
function delete(r)
% Delete the :mat:class:`Reactor` object.
if isempty(r.id)
return
end
ctFunc('reactor_del', r.id);
end

View File

@ -85,6 +85,9 @@ classdef ReactorNet < handle
function delete(r)
% Delete the :mat:class:`ReactorNet` object object.
if isempty(r.id)
return
end
ctFunc('reactornet_del', r.id);
end

View File

@ -56,6 +56,9 @@ classdef ReactorSurface < handle
function delete(s)
% Delete the :mat:class:`ReactorSurface` object.
if isempty(s.surfID)
return
end
ctFunc('reactorsurface_del', s.surfID);
end

View File

@ -127,6 +127,9 @@ classdef Wall < handle
function delete(w)
% Clear the :mat:class:`Wall` object.
if isempty(w.id)
return
end
ctFunc('wall_del', w.id);
end

View File

@ -0,0 +1,22 @@
function phases = ctImportPhases(src, phasenames)
ctIsLoaded;
if nargin < 2
error('Please specify the source file and list of phases to import');
end
phases = {};
if ischar(phasenames)
phases = {Solution(src, phasenames)};
return
elseif iscell(phasenames)
for name = phasenames
phases = [phases; {Solution(src, name{:})}];
end
else
error (['Invalid type for phasenames, ' , ...
'expected string or a cell array of strings']);
end
end

View File

@ -4,7 +4,7 @@ function output = ctString(funcName, varargin)
err1 = -1;
buflen = calllib(ctLib, funcName, varargin{:}, 0, '');
buflen = calllib(ctLib, funcName, varargin{:}, 0, '') + 1;
if buflen > 0
aa = char(ones(1, buflen));
@ -25,7 +25,7 @@ function output = ctString(funcName, varargin)
error('Cantera:ctError', ctGetErr);
end
if iok == -err1
if iok == err1
error('Cantera:ctError', ctGetErr);
end

View File

@ -12,25 +12,22 @@ function calling from Cantera CLib.
since it's stand-alone.
3. For first time users, launch Matlab, then navigate to `/path/to/cantera/source/code`
(the folder containing `interfaces` and `samples`) using "Browse for Folder".
Note for Ubuntu users: Matlab must be launched from the terminal
using the following command:
`LD_PRELOAD='/usr/lib/x86_64-linux-gnu/libstdc++.so.6' matlab -softwareopengl`.
This is because Matlab does not load the correct GLIBC++ library on start-up and
will return an error when loading the Cantera toolbox.
4. In the Matlab command window, run
4. For Linux users: Matlab currently uses Intel MKL which uses 64-bit integer types
that are incompatible with the standard 32-bit integers used by the default version
of OpenBLAS that comes with Cantera. As such, the correct environment variables
need to be set to launch Matlab with the correct BLAS/LAPACK libraries loaded:
`export LD_PRELOAD=/path/to/openblas/library:/path/to/lapack/library`
then, launch Matlab in the terminal with:
`matlab -softwareopengl`.
5. In the Matlab command window, run
`addpath(genpath([pwd, '/interfaces/matlab_experimental']))` to add search path for
the experimental toolbox.
5. In the Matlab command window, run
6. In the Matlab command window, run
`cd([pwd, '/interfaces/matlab_experimental/Utility'])` to navigate to the Utility
folder.
6. Open the file named 'ctRoot.m', in the second line, edit `output=` to
7. Open the file named 'ctRoot.m', in the second line, edit `output=` to
`output='/path/to/conda/environment'`, then save the file. This sets the search path
for the `ctLoad` command to find the shared library file for Cantera.
7. Make sure the legacy (old) Matlab Toolbox for
Cantera (if it's already installed) and samples files are removed from
the Matlab search path. Having both the legacy and experimental version
of the toolbox in the search path will lead to conflicts.
The command to remove search path in Matlab is `rmpath`.
8. In the Matlab command window, run `savepath` to save all search paths.
9. To switch back to the legacy Matlab toolbox, revert the search paths.

View File

@ -0,0 +1,64 @@
phases:
- name: A
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [A-reactions]
- name: B
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [B-reactions]
- name: C
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [C-reactions]
- name: D
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [D-reactions]
- name: E
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [E-reactions]
- name: F
thermo: ideal-gas
species: [{h2o2.yaml/species: all}]
kinetics: gas
state: {T: 300.0, P: 1 atm, X: {O2: 0.21, N2: 0.79}}
reactions: [F-reactions]
A-reactions:
- equation: O + H2 <=> H + OH # Reaction 3
rate-constant: {A: 3.87e+04 cm^6/mol^2/s, b: 2.7, Ea: 6260.0}
B-reactions:
- equation: O + H2 <=> H + OH # Reaction 3
rate-constant: {A: 3.87e+04, b: 2.7, Ea: 6260.0 m}
C-reactions:
- equation: O + H2 <=> H + OH
D-reactions:
- equation: 2 OH (+M) <=> H2O2 (+M)
type: falloff
E-reactions:
- equation: O + H2 <=> H + OH
type: pressure-dependent-Arrhenius
F-reactions:
- equation: O + H2 <=> H + OH
rate-constant: {A: 3.87e+04, b: 2.7, Ea: 6260.0}
duplicate: true
- equation: O + H2 <=> H + OH
rate-constant: {A: -3.87e+04, b: 2.7, Ea: 6260.0}
duplicate: true

View File

@ -0,0 +1,82 @@
phases:
- name: A
thermo: ideal-gas
species: [{h2o2.yaml/species: [H, O2, H2O, HO2]}]
kinetics: gas
reactions: [A-reactions]
- name: B
thermo: ideal-gas
species: [{h2o2.yaml/species: [H, O2, H2O, HO2]}]
kinetics: gas
reactions: [B-reactions]
- name: C
thermo: ideal-gas
species:
- h2o2.yaml/species: [H, O2, H2O, HO2]
kinetics: gas
reactions:
- h2o2.yaml/reactions: declared-species
skip-undeclared-third-bodies: true
- name: D
thermo: ideal-gas
species:
- h2o2.yaml/species: [H, O2, HO2]
kinetics: gas
reactions:
- h2o2.yaml/reactions: declared-species
skip-undeclared-third-bodies: true
- name: E
thermo: ideal-gas
species: [{h2o2.yaml/species: [H, O2, H2O, HO2]}]
kinetics: gas
reactions: [E-reactions]
- name: F
thermo: ideal-gas
species: [{h2o2.yaml/species: [H, O2, H2O, HO2]}]
kinetics: gas
reactions: [F-reactions]
- name: G
thermo: ideal-gas
species: [{h2o2.yaml/species: [H, O2, H2O, HO2]}]
kinetics: gas
reactions: [G-reactions]
- name: gas
thermo: ideal-gas
species:
- gri30.yaml/species: [H2, H, O, OH, H2O, CO2]
- name: Pt_surf
thermo: ideal-surface
species:
- ptcombust.yaml/species: [PT(S), H(S), H2O(S), OH(S), CO2(S), CH2(S)s,
CH(S), C(S), O(S)]
kinetics: surface
reactions: [ptcombust.yaml/reactions: declared-species]
site-density: 2.7063e-09
A-reactions:
- equation: O + H2 <=> H + OH # Reaction 3
rate-constant: {A: 3.87e+04, b: 2.7, Ea: 6260.0}
B-reactions:
- equation: H + O2 + AR <=> HO2 + AR # Reaction 10
rate-constant: {A: 7.0e+17, b: -0.8, Ea: 0.0}
E-reactions:
- equation: H + O2 => HO2
rate-constant: {A: 1.255943e+13, b: -2.0, Ea: 5000.0 cal/mol}
orders:
H2O: 0.2
nonreactant-orders: true
F-reactions:
- equation: H + O2 => HO2
rate-constant: {A: 1.255943e+13, b: -2.0, Ea: 5000.0 cal/mol}
orders:
H2O: 0.2
G-reactions:
- equation: H + O2 => HO2
rate-constant: {A: 1.255943e+13, b: -2.0, Ea: 5000.0 cal/mol}
orders:
N2: 0.2
nonreactant-orders: true

View File

@ -0,0 +1,101 @@
classdef ctTestDuplicateReactions < matlab.unittest.TestCase
properties
phase
end
methods (TestClassSetup)
function testSetUp(self)
ctTestSetUp
copyfile('../data/duplicate-reactions.yaml', ...
'./duplicate-reactions.yaml');
end
end
methods (TestClassTeardown)
function testTearDown(self)
delete('./duplicate-reactions.yaml');
ctCleanUp
ctTestTearDown
end
end
methods (TestMethodTeardown)
function deleteSolution(self)
clear self.phase;
end
end
methods
function check(self, name)
try
self.phase = Solution('duplicate-reactions.yaml', name);
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'duplicate reaction');
end
end
end
methods (Test)
function testForwardMultiple(self)
self.check('A');
end
function testOpposite1(self)
self.check('B');
end
function testOpposite2(self)
self.check('C');
end
function testOpposite3(self)
self.check('D');
end
function testOpposite4(self)
self.check('E');
self.verifyEqual(self.phase.nReactions, 2);
end
function testCommonEfficiencies(self)
self.check('F');
end
function testDisjointEfficiencies(self)
self.check('G');
self.verifyEqual(self.phase.nReactions, 2);
end
function testDifferentType(self)
self.check('H');
self.verifyEqual(self.phase.nReactions, 2);
end
function testDeclaredDupliate(self)
self.check('I');
self.verifyEqual(self.phase.nReactions, 2);
end
function testUnmatchedDuplicate(self)
self.check('J');
end
function testNonreactingSpecies(self)
self.check('K');
self.verifyEqual(self.phase.nReactions, 3);
end
end
end

View File

@ -0,0 +1,235 @@
classdef ctTestEquilibrium < matlab.unittest.TestCase
properties
phase
mix
end
properties (SetAccess = immutable)
rtol = 1e-6;
atol = 1e-8;
end
methods (TestClassSetup)
function testSetUp(self)
ctTestSetUp
copyfile('../data/equilibrium.yaml', './equilibrium.yaml');
copyfile('../data/IdealSolidSolnPhaseExample.yaml', ...
'./IdealSolidSolnPhaseExample.yaml');
copyfile('../data/koh-equil-TP.csv', './koh-equil-TP.csv');
copyfile('../data/koh-equil-HP.csv', './koh-equil-HP.csv');
end
end
methods (TestClassTeardown)
function testTearDown(self)
delete('./equilibrium.yaml');
delete('./IdealSolidSolnPhaseExample.yaml');
delete('./koh-equil-TP.csv');
delete('./koh-equil-HP.csv');
ctCleanUp
ctTestTearDown
end
end
methods (TestMethodTeardown)
function deleteSolution(self)
clear self.phase self.mix
end
end
methods
function checkval(self, names, moles)
ntot = sum(moles);
xx = moles ./ ntot;
for i = 1:length(names)
val = self.phase.X(self.phase.speciesIndex(names{i}));
self.verifyEqual(val, xx(i), 'AbsTol', self.atol);
end
end
end
methods (Test)
function testEquilCompleteStoichiometric(self)
self.phase = Solution('equilibrium.yaml', 'complete');
self.phase.TPX = {298, 1.0e6, 'CH4:1.0, O2:2.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 0, 2, 1];
self.checkval(names, moles);
end
function testEquilCompleteLean(self)
self.phase = Solution('equilibrium.yaml', 'complete');
self.phase.TPX = {298, 1.0e6, 'CH4:1.0, O2:3.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 1, 2, 1];
self.checkval(names, moles);
end
function testEquilIncompleteStoichiometric(self)
self.phase = Solution('equilibrium.yaml', 'incomplete');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:2.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 0, 2, 1];
self.checkval(names, moles);
end
function testEquilIncompleteLean(self)
self.phase = Solution('equilibrium.yaml', 'incomplete');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:3.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 1, 2, 1];
self.checkval(names, moles);
end
function testEquilGriStoichiometric(self)
self.phase = Solution('gri30.yaml', '', 'none');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:2.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 0, 2, 1];
self.checkval(names, moles);
end
function testEquilGriLean(self)
self.phase = Solution('gri30.yaml', '', 'none');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:3.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 1, 2, 1];
self.checkval(names, moles);
end
function testEquilOverconstrained1(self)
self.phase = Solution('equilibrium.yaml', 'overconstrained-1');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:1.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2'};
moles = [1, 1];
self.checkval(names, moles);
end
function testEquilOverconstrained2(self)
self.phase = Solution('equilibrium.yaml', 'overconstrained-2');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:1.0'};
self.phase.equilibrate('TP');
names = {'CH4', 'O2'};
moles = [1, 1];
self.checkval(names, moles);
end
function testEquilGriStoichiometricGibbs(self)
self.phase = Solution('equilibrium.yaml', '', 'none');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:1.0'};
self.phase.equilibrate('TP', 'gibbs');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 0, 2, 1];
self.assumeFail('Skipping multi-phase equilibrium test');
self.checkval(names, moles);
end
function testEquilGriLeanGibbs(self)
self.phase = Solution('equilibrium.yaml', '', 'none');
self.phase.TPX = {301, 1.0e6, 'CH4:1.0, O2:3.0'};
self.phase.equilibrate('TP', 'gibbs');
names = {'CH4', 'O2', 'H2O', 'CO2'};
moles = [0, 1, 2, 1];
self.assumeFail('Skipping multi-phase equilibrium test');
self.checkval(names, moles);
end
function testKOHEquilTP(self)
phasenames = {'K_solid', 'K_liquid', ...
'KOH_a', 'KOH_b', 'KOH_liquid', ...
'K2O2_solid', 'K2O_solid', 'KO2_solid', ...
'ice', 'liquid_water', 'KOH_plasma'};
phases = ImportPhases('KOH.yaml', phasenames);
self.mix = Mixture(phases);
temp = 350:300:5000;
data = zeros(length(temp), self.mix.nSpecies + 1);
data(:, 1) = temp;
for i = 1:length(temp)
self.mix.T = temp(i);
self.mix.P = OneAtm;
self.mix.setSpeciesMoles('K:1.03, H2:2.12, O2:0.9');
self.mix.equilibrate('TP');
data(i, 2:end) = self.mix.speciesMoles;
end
refData = readmatrix('koh-equil-TP.csv');
self.verifySize(data, size(refData), ...
'Generated data does not match reference size');
self.verifyEqual(data, refData, 'AbsTol', self.atol);
end
function testKOHEquilHP(self)
phasenames = {'K_solid', 'K_liquid', ...
'KOH_a', 'KOH_b', 'KOH_liquid', ...
'K2O2_solid', 'K2O_solid', 'KO2_solid', ...
'ice', 'liquid_water', 'KOH_plasma'};
phases = ImportPhases('KOH.yaml', phasenames);
self.mix = Mixture(phases);
temp = 350:300:5000;
dT = 1;
data = zeros(length(temp), self.mix.nSpecies + 2);
data(:, 1) = temp;
self.mix.P = OneAtm;
for i = 1:length(temp)
self.mix.setSpeciesMoles('K:1.03, H2:2.12, O2:0.9');
self.mix.T = temp(i) - dT;
self.mix.equilibrate('TP');
self.mix.T = temp(i);
self.mix.equilibrate('HP');
data(i, 2) = self.mix.T;
data(i, 3:end) = self.mix.speciesMoles;
end
refData = readmatrix('koh-equil-HP.csv');
self.verifySize(data, size(refData), ...
'Generated data does not match reference size');
self.verifyEqual(data, refData, 'AbsTol', self.atol);
end
function testIdealSolidSolnPhaseEquil(self)
self.phase = Solution('IdealSolidSolnPhaseExample.yaml');
self.phase.TPX = {500, OneAtm, 'C2H2-graph: 1.0'};
self.phase.equilibrate('TP', 'element_potential');
self.verifyEqual(self.phase.X(self.phase.speciesIndex('C-graph')), ...
2.0/3.0, 'AbsTol', self.atol);
self.verifyEqual(self.phase.X(self.phase.speciesIndex('H2-solute')), ...
1.0/3.0, 'AbsTol', self.atol);
end
end
end

View File

@ -0,0 +1,290 @@
classdef ctTestFunc1 < matlab.unittest.TestCase
properties (SetAccess = immutable)
rtol = 1e-6;
atol = 1e-8;
end
methods (TestClassSetup)
function testSetUp(self)
ctTestSetUp
end
end
methods (TestClassTeardown)
function testTearDown(self)
ctCleanUp
ctTestTearDown
end
end
methods (Test)
function testFunction(self)
f = Func1('sin', 1);
for i = [0, 0.1, 0.7]
self.verifyEqual(f(i), sin(i), 'AbsTol', self.atol);
end
clear f
end
function testLambda(self)
f1 = Func1('sin', 1);
f2 = Func1('pow', 0.5);
f = Func1('product', f1, f2);
for i = [0.1, 0.7, 4.5]
self.verifyEqual(f(i), sin(i)*sqrt(i), 'AbsTol', self.atol);
end
clear f1 f2 f
end
function testConstant(self)
f = Func1('constant', 5);
for i = [0.1, 0.7, 4.5]
self.verifyEqual(f(i), 5, 'AbsTol', self.atol);
end
clear f
end
function testSimple(self)
functors = {'sin', 'cos', 'exp', 'log'};
coeff = 2.34;
for name = functors
f = Func1(name{:}, coeff);
self.verifySubstring(f.write, name{:});
self.verifyEqual(f.type, name{:});
for val = [0.1, 1, 10]
self.verifyEqual(f(val), feval(name{:}, coeff*val), ...
'AbsTol', self.atol);
end
clear f
end
end
function testCompound(self)
f0 = 3.1415;
f1 = Func1('pow', 2);
f2 = Func1('sin');
val = [0.1, 1, 10];
functors = containers.Map(...
{'sum', 'diff', 'product', 'ratio'}, ...
{@(x, y) x + y, @(x, y) x - y, @(x, y) x * y, @(x, y) x / y});
for k = keys(functors)
func = Func1(k{:}, f1, f2);
f = functors(k{:});
self.verifyFalse(contains(func.write, k{:}));
self.verifyEqual(k{:}, func.type);
for v = val
x1 = func(v);
x2 = f(f1(v), f2(v));
self.verifyEqual(x1, x2, 'absTol', self.atol);
end
clear func
end
f3 = Func1('sum', f0, f1);
f4 = Func1('sum', f2, f0);
for v = val
self.verifyEqual(f3(v), f0 + f1(v), 'absTol', self.atol);
self.verifyEqual(f4(v), f0 + f2(v), 'absTol', self.atol);
end
try
f5 = Func1('sum', f0, f0);
catch ME
self.verifySubstring(ME.message, 'Invalid arguments');
end
try
f6 = Func1('sum', 'spam', 'eggs');
catch ME
self.verifySubstring(ME.message, 'Invalid arguments');
end
clear f1 f2 f3 f4
end
function testNewSum(self)
f1 = Func1('sin', 3);
f2 = f1 + 0;
self.verifyEqual(f2.type, 'sin');
self.verifyEqual(f2(0.5), sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = 0 + f1;
self.verifyEqual(f2.type, 'sin');
self.verifyEqual(f2(0.5), sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = f1 + Func1('constant', 1);
self.verifyEqual(f2.type, 'plus-constant');
self.verifyEqual(f2(0.5), sin(0.5 * 3) + 1, 'absTol', self.atol);
clear f2
f2 = f1 + f1;
self.verifyEqual(f2.type, 'times-constant');
self.verifyEqual(f2(0.5), sin(0.5 * 3) * 2, 'absTol', self.atol);
clear f2
clear f1
end
function testNewDiff(self)
f1 = Func1('sin', 3);
f2 = f1 - 0;
self.verifyEqual(f2.type, 'sin');
self.verifyEqual(f2(0.5), sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = 0 - f1;
self.verifyEqual(f2.type, 'times-constant');
self.verifyEqual(f2(0.5), -sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = f1 - Func1('constant', 1);
self.verifyEqual(f2.type, 'plus-constant');
self.verifyEqual(f2(0.5), sin(0.5 * 3) - 1, 'absTol', self.atol);
clear f2
f2 = f1 - f1;
self.verifyEqual(f2.type, 'constant');
self.verifyEqual(f2(0.5), 0, 'absTol', self.atol);
clear f2
clear f1
end
function testNewProd(self)
f1 = Func1('sin', 3);
f2 = f1 * Func1('constant', 1);
self.verifyEqual(f2.type, 'sin');
self.verifyEqual(f2(0.5), sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = f1 * 2;
self.verifyEqual(f2.type, 'times-constant');
self.verifyEqual(f2(0.5), sin(0.5 * 3) * 2, 'absTol', self.atol);
clear f2
f2 = f1 * 0;
self.verifyEqual(f2.type, 'constant');
self.verifyEqual(f2(0.5), 0, 'absTol', self.atol);
clear f2
clear f1
end
function testNewRatio(self)
f1 = Func1('sin', 3);
f2 = f1 / Func1('constant', 1);
self.verifyEqual(f2.type, 'sin');
self.verifyEqual(f2(0.5), sin(0.5 * 3), 'absTol', self.atol);
clear f2
f2 = 0 / f1;
self.verifyEqual(f2.type, 'constant');
self.verifyEqual(f2(0.5), 0, 'absTol', self.atol);
clear f2
f2 = f1 / 2;
self.verifyEqual(f2.type, 'times-constant');
self.verifyEqual(f2(0.5), sin(0.5 * 3) / 2, 'absTol', self.atol);
clear f2
try
f2 = f1 / 0;
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'Division by zero');
clear f2
end
clear f1
end
function testModified(self)
f1 = Func1('sin');
const = 2.34;
val = [0.1, 1, 10];
functors = containers.Map(...
{'plus-constant', 'times-constant'}, ...
{@(x, y) x + y, @(x, y) x * y});
for k = keys(functors)
func = Func1(k{:}, f1, const);
f = functors(k{:});
self.verifyFalse(contains(func.write, k{:}));
self.verifyEqual(k{:}, func.type);
for v = val
x1 = func(v);
x2 = f(f1(v), const);
self.verifyEqual(x1, x2, 'absTol', self.atol);
end
clear func
try
func = Func1(k{:}, const, f1);
catch ME
self.verifySubstring(ME.message, 'Invalid arguments');
end
end
end
function testTabulated(self)
t = [0, 1, 2];
fval = [2, 1, 0];
v1 = [-0.5, 0, 0.5, 1.0, 1.5, 2, 2.5];
v2 = [2.0, 2.0, 1.5, 1.0, 0.5, 0.0, 0.0];
v3 = [2.0, 2.0, 2.0, 2.0, 1.0, 0.0, 0.0];
func1 = Func1('tabulated-linear', t, fval);
self.verifyEqual(func1.type, 'tabulated-linear');
for i = length(v1)
self.verifyEqual(func1(v1(i)), v2(i), 'absTol', self.atol);
end
func2 = Func1('tabulated-previous', t, fval);
self.verifyEqual(func2.type, 'tabulated-previous');
for i = length(v1)
self.verifyEqual(func2(v1(i)), v3(i), 'absTol', self.atol);
end
try
func3 = Func1('tabulated-linear', 0:2, 0:3);
catch ME
self.verifySubstring(ME.message, 'even number of entries');
end
try
func3 = Func1('tabulated-linear', [], []);
catch ME
self.verifySubstring(ME.message, 'at least 4 entries');
end
try
func3 = Func1('tabulated-linear', [0, 1, 0.5, 2], [2, 1, 1, 0]);
catch ME
self.verifySubstring(ME.message, 'monotonically');
end
clear func1 func2
end
end
end

View File

@ -0,0 +1,97 @@
classdef ctTestInvalidInputs < matlab.unittest.TestCase
properties
phase
end
properties (SetAccess = immutable)
inputfile = 'invalid-inputs.yaml';
end
methods (TestClassSetup)
function testSetUp(self)
copyfile('../data/invalid-inputs.yaml', ...
'./invalid-inputs.yaml');
ctTestSetUp
end
end
methods (TestClassTeardown)
function testTearDown(self)
delete('./invalid-inputs.yaml');
ctCleanUp
ctTestTearDown
end
end
methods (TestMethodTeardown)
function deleteSolution(self)
clear self.phase;
end
end
methods (Test)
function testFailingConvert1(self)
try
self.phase = Solution(self.inputfile, 'A');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'UnitSystem::convert');
end
end
function testFailingConvert2(self)
try
self.phase = Solution(self.inputfile, 'B');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'UnitSystem::convertActivationEnergy');
end
end
function testFailingUnconfigured1(self)
try
self.phase = Solution(self.inputfile, 'C');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'ArrheniusBase::validate');
end
end
function testFailingUnconfigured2(self)
try
self.phase = Solution(self.inputfile, 'D');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'FalloffRate::validate');
end
end
function testFailingUnconfigured3(self)
try
self.phase = Solution(self.inputfile, 'E');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'PlogRate::validate');
end
end
function testFailingInvalidDuplicate(self)
try
self.phase = Solution(self.inputfile, 'F');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'ArrheniusBase::check');
self.verifySubstring(ME.message, 'negative pre-exponential factor');
end
end
end
end

View File

@ -2,6 +2,9 @@ classdef ctTestKinetics < matlab.unittest.TestCase
properties
phase
end
properties (SetAccess = immutable)
rtol = 1e-6;
atol = 1e-8;
end
@ -10,6 +13,12 @@ classdef ctTestKinetics < matlab.unittest.TestCase
function testSetUp(self)
ctTestSetUp
copyfile('../data/air-no-reactions.yaml', ...
'./air-no-reactions.yaml');
copyfile('../data/chemically-activated-reaction.yaml', ...
'./chemically-activated-reaction.yaml');
copyfile('../data/addReactions_err_test.yaml', ...
'./addReactions_err_test.yaml');
end
end
@ -17,6 +26,9 @@ classdef ctTestKinetics < matlab.unittest.TestCase
methods (TestClassTeardown)
function testTearDown(self)
delete('./air-no-reactions.yaml');
delete('./chemically-activated-reaction.yaml');
delete('./addReactions_err_test.yaml');
ctCleanUp
ctTestTearDown
end
@ -150,6 +162,113 @@ classdef ctTestKinetics < matlab.unittest.TestCase
end
function testRatesOfProgress(self)
forROP = self.phase.forwardRatesOfProgress;
revROP = self.phase.reverseRatesOfProgress;
netROP = self.phase.netRatesOfProgress;
l = length(netROP);
tol = ones(1, l) .* self.atol;
self.verifyEqual(l, self.phase.nReactions);
self.verifyEqual(forROP - revROP, netROP, 'AbsTol', tol);
end
function testRateConstants(self)
kf = self.phase.forwardRateConstants;
kr = self.phase.reverseRateConstants;
Keq = self.phase.equilibriumConstants;
l = length(kf);
self.verifyEqual(l, self.phase.nReactions);
ix = find(kr ~= 0);
tol = ones(1, l) .* self.rtol;
self.verifyEqual(kf(ix) ./ kr(ix), Keq(ix), 'RelTol', tol);
end
function testSpeciesRates(self)
nu_p = self.phase.productStoichCoeffs;
nu_r = self.phase.reactantStoichCoeffs;
forROP = self.phase.forwardRatesOfProgress;
revROP = self.phase.reverseRatesOfProgress;
cr = full(sum(nu_p .* forROP, 2) + sum(nu_r .* revROP, 2))';
de = full(sum(nu_r .* forROP, 2) + sum(nu_p .* revROP, 2))';
l = length(self.phase.nSpecies);
tol = ones(1, l) .* self.rtol;
self.verifyEqual(self.phase.creationRates, cr, 'RelTol', tol);
self.verifyEqual(self.phase.destructionRates, de, 'RelTol', tol);
self.verifyEqual(self.phase.netProdRates, cr - de, 'RelTol', tol);
end
function testReactionDeltas(self)
H = self.phase.deltaEnthalpy;
S = self.phase.deltaEntropy;
G = self.phase.deltaGibbs;
Hs = self.phase.deltaStandardEnthalpy;
Ss = self.phase.deltaStandardEntropy;
Gs = self.phase.deltaStandardGibbs;
T = self.phase.T;
l = length(H);
tol = ones(1, l) .* self.rtol;
self.verifyEqual(H - S .* T, G, 'RelTol', tol);
self.verifyEqual(Hs - Ss .* T, Gs, 'RelTol', tol);
end
function testEmptyKinetics(self)
try
gas = Solution('air-no-reactions.yaml');
arr = zeros(1, gas.nSpecies);
tol = ones(1, gas.nSpecies) .* self.atol;
self.verifyEqual(gas.nReactions, 0);
self.verifyEqual(gas.creationRates, arr, 'AbsTol', tol);
self.verifyEqual(gas.destructionRates, arr, 'AbsTol', tol);
self.verifyEqual(gas.netProdRates, arr, 'AbsTol', tol);
clear gas
catch ME
clear gas
rethrow(ME);
end
end
function testChemicallyActivated(self)
try
gas = Solution('chemically-activated-reaction.yaml');
P = [2026.5, 202650.0, 10132500.0];
Rf = [2.851022e-04, 2.775924e+00, 2.481792e+03];
xx = [0.01, 0.01, 0.04, 0.10, 0.84];
for i = 1:length(P)
gas.TPX = {900.0, P(i), xx};
self.verifyEqual(gas.forwardRatesOfProgress(1), Rf(i), 'RelTol', 2.0e-05);
end
clear gas
catch ME
clear gas
rethrow(ME);
end
end
function testPdepError(self)
try
gas = Solution('addReactions_err_test.yaml');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'Invalid rate coefficient');
clear gas
end
end
end
end

View File

@ -0,0 +1,174 @@
classdef ctTestKineticsRepeatability < matlab.unittest.TestCase
properties
phase
X0
X1
end
properties (SetAccess = immutable)
T0 = 1200;
T1 = 1300;
rho0 = 2.4;
rho1 = 3.1;
P0 = 1.4e+05;
P1 = 3.7e+06;
rtol = 1e-6;
atol = 1e-8;
end
methods (TestClassSetup)
function testSetUp(self)
ctTestSetUp
copyfile('../data/pdep-test.yaml', ...
'./pdep-test.yaml');
end
end
methods (TestClassTeardown)
function testTearDown(self)
delete('./pdep-test.yaml');
ctCleanUp
ctTestTearDown
end
end
methods (TestMethodTeardown)
function deleteSolution(self)
clear self.phase;
end
end
methods
function setup_phase(self, mech)
clear self.phase
self.phase = Solution(mech);
self.X0 = 1 + sin(1:self.phase.nSpecies);
self.X1 = 1 + sin(2:self.phase.nSpecies + 1);
end
function checkRatesX(self, mech)
self.setup_phase(mech);
self.phase.TDX = {self.T0, self.rho0, self.X0};
w1 = self.phase.netProdRates;
self.phase.TDX = {self.T1, self.rho1, self.X1};
w2 = self.phase.netProdRates;
self.phase.TDX = {self.T0, self.rho0, self.X1};
w3 = self.phase.netProdRates;
self.phase.TDX = {self.T0, self.rho0, self.X0};
w4 = self.phase.netProdRates;
self.verifyEqual(w1, w4, 'RelTol', self.rtol);
end
function checkRatesT1(self, mech)
self.setup_phase(mech);
self.phase.TDX = {self.T0, self.rho0, self.X0};
w1 = self.phase.netProdRates;
self.phase.TDX = {self.T1, self.rho1, self.X1};
w2 = self.phase.netProdRates;
self.phase.TDX = {self.T1, self.rho0, self.X0};
w3 = self.phase.netProdRates;
self.phase.TDX = {self.T0, self.rho0, self.X0};
w4 = self.phase.netProdRates;
self.verifyEqual(w1, w4, 'RelTol', self.rtol);
end
function checkRatesT2(self, mech)
self.setup_phase(mech);
self.phase.TPX = {self.T0, self.P0, self.X0};
w1 = self.phase.netProdRates;
self.phase.TPX = {self.T1, self.P1, self.X1};
w2 = self.phase.netProdRates;
self.phase.TPX = {self.T1, self.P0, self.X0};
w3 = self.phase.netProdRates;
self.phase.TPX = {self.T0, self.P0, self.X0};
w4 = self.phase.netProdRates;
self.verifyEqual(w1, w4, 'RelTol', self.rtol);
end
function checkRatesP(self, mech)
self.setup_phase(mech);
self.phase.TPX = {self.T0, self.P0, self.X0};
w1 = self.phase.netProdRates;
self.phase.TPX = {self.T1, self.P1, self.X1};
w2 = self.phase.netProdRates;
self.phase.TPX = {self.T0, self.P1, self.X0};
w3 = self.phase.netProdRates;
self.phase.TPX = {self.T0, self.P0, self.X0};
w4 = self.phase.netProdRates;
self.verifyEqual(w1, w4, 'RelTol', self.rtol);
end
end
methods (Test)
function testGRI30X(self)
self.checkRatesX('gri30.yaml');
end
function testGRI30T(self)
self.checkRatesT1('gri30.yaml');
self.checkRatesT2('gri30.yaml');
end
function testGRI30P(self)
self.checkRatesP('gri30.yaml');
end
function testH2O2X(self)
self.checkRatesX('h2o2.yaml');
end
function testH2O2T(self)
self.checkRatesT1('h2o2.yaml');
self.checkRatesT2('h2o2.yaml');
end
function testH2O2P(self)
self.checkRatesP('h2o2.yaml');
end
function testPdepX(self)
self.checkRatesX('pdep-test.yaml');
end
function testPdepT(self)
self.checkRatesT1('pdep-test.yaml');
self.checkRatesT2('pdep-test.yaml');
end
function testPdepP(self)
self.checkRatesP('pdep-test.yaml');
end
end
end

View File

@ -21,3 +21,4 @@ cantera_root = getenv('CANTERA_ROOT');
% Add the Cantera toolbox to the Matlab path
addpath(genpath([cantera_root, '/interfaces/matlab_experimental']));
addpath(genpath([cantera_root, '/test/matlab_experimental']));
addpath(genpath([cantera_root, '/test/data']));

View File

@ -2,6 +2,9 @@ classdef ctTestThermo < matlab.unittest.TestCase
properties
phase
end
properties (SetAccess = immutable)
rtol = 1e-6;
atol = 1e-8;
end

View File

@ -0,0 +1,110 @@
classdef ctTestUndeclared < matlab.unittest.TestCase
properties
phase
end
properties (SetAccess = immutable)
inputfile = 'undeclared-tests.yaml';
end
methods (TestClassSetup)
function testSetUp(self)
copyfile('../data/undeclared-tests.yaml', ...
'./undeclared-tests.yaml');
ctTestSetUp
end
end
methods (TestClassTeardown)
function testTearDown(self)
delete('./undeclared-tests.yaml');
ctCleanUp
ctTestTearDown
end
end
methods (TestMethodTeardown)
function deleteSolution(self)
clear self.phase;
end
end
methods (Test)
function testRaiseUndeclaredSpecies(self)
try
self.phase = Solution(self.inputfile, 'A');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'contains undeclared species');
end
end
function testRaiseUndeclaredThirdBodies(self)
try
self.phase = Solution(self.inputfile, 'B');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'three-body reaction with undeclared');
end
end
function testSkipUndeclaredThirdBodies1(self)
self.phase = Solution(self.inputfile, 'C');
self.verifyEqual(self.phase.nReactions, 3);
end
function testSkipUndeclaredThirdBodies2(self)
self.phase = Solution(self.inputfile, 'D');
rxns = self.phase.reactionEquations;
self.verifyTrue(ismember('H + O2 + M <=> HO2 + M', rxns));
end
function testSkipUndeclaredOrders(self)
self.phase = Solution(self.inputfile, 'E');
self.verifyEqual(self.phase.nReactions, 1);
end
function testRaiseNonreactantOrders(self)
try
self.phase = Solution(self.inputfile, 'F');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'Reaction order specified');
end
end
function testRaiseUndeclaredOrders(self)
try
self.phase = Solution(self.inputfile, 'G');
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
self.verifySubstring(ME.message, 'reaction orders for undeclared');
end
end
function testSkipUndeclaredSurfSpecies(self)
try
gas = Solution(self.inputfile, 'gas');
surf = Interface(self.inputfile, 'Pt_surf', gas);
self.verifyEqual(surf.nReactions, 14);
clear gas
clear surf
catch ME
self.verifySubstring(ME.identifier, 'Cantera:ctError');
clear gas
clear surf
end
end
end
end