Changed location of tests

This commit is contained in:
ssun30 2023-06-07 22:11:03 -04:00 committed by Ingmar Schoegl
parent d1f2902624
commit 7693f50b40
4 changed files with 513 additions and 472 deletions

View File

@ -1,466 +0,0 @@
classdef ctMatlabTestThermo < matlab.unittest.TestCase
properties
phase
rtol = 1e-6;
atol = 1e-8;
end
methods (TestClassTeardown)
function testTearDown(testCase)
% Clean up Cantera
ctCleanUp
end
end
methods (TestMethodSetup)
% Setup for each test
function createPhase(testCase)
src = 'h2o2.yaml';
id = 'ohmech';
transport = 'none';
testCase.phase = Solution(src, id, transport);
end
end
methods (TestMethodTeardown)
% Destroy object
function deleteSolution(testCase)
clear testCase.phase;
end
end
methods
% Generic function to check whether a value is near expected value (relative).
function verifyNearRelative(testCase, val, exp)
diff = max(abs((val - exp) ./ exp));
testCase.verifyLessThanOrEqual(diff, testCase.rtol);
end
% Generic function to check whether a value is near expected value (absolute).
function verifyNearAbsolute(testCase, val, exp)
diff = max(abs(val - exp));
testCase.verifyLessThanOrEqual(diff, testCase.atol);
end
% Generic function to set invalid values to attribute and verify errors
function setInvalidValue(testCase, attr, val, errMessage)
try
testCase.phase.(attr) = val;
catch ME
testCase.verifySubstring(ME.message, errMessage);
end
end
% Generic function to get invalid values of an attribute and verify errors
function val = getInvalidValue(testCase, attr, args, errMessage)
try
if nargin == 3
val = testCase.phase.(attr);
else
val = testCase.phase.(attr)(args{:});
end
catch ME
testCase.verifySubstring(ME.message, errMessage);
end
end
% Generic function to get an invalid property
function a = getInvalidProperty(testCase)
a = testCase.phase.foobar;
end
% Generic function to set an invalid property
function setInvalidProperty(testCase, val)
testCase.phase.foobar = val;
end
% Check state
function checkState(testCase, T, D, Y)
testCase.verifyNearRelative(testCase.phase.T, T);
testCase.verifyNearRelative(testCase.phase.D, D);
testCase.verifyNearAbsolute(testCase.phase.Y, Y);
end
% Check multi properties
function checkMultiProperties(testCase, str)
val = testCase.phase.(str);
for i = 1:length(str)
attr = str(i);
exp = testCase.phase.(attr);
testCase.verifyNearAbsolute(val{i}, exp);
end
end
% Check getter
function checkGetters(testCase)
testCase.checkMultiProperties('TD');
testCase.checkMultiProperties('TDX');
testCase.checkMultiProperties('TDY');
testCase.checkMultiProperties('TP');
testCase.checkMultiProperties('TPX');
testCase.checkMultiProperties('TPY');
testCase.checkMultiProperties('HP');
testCase.checkMultiProperties('HPX');
testCase.checkMultiProperties('HPY');
testCase.checkMultiProperties('UV');
testCase.checkMultiProperties('UVX');
testCase.checkMultiProperties('UVY');
testCase.checkMultiProperties('SP');
testCase.checkMultiProperties('SPX');
testCase.checkMultiProperties('SPY');
testCase.checkMultiProperties('SV');
testCase.checkMultiProperties('SVX');
testCase.checkMultiProperties('SVY');
testCase.checkMultiProperties('DP');
testCase.checkMultiProperties('DPX');
testCase.checkMultiProperties('DPY');
end
% Check setter
function checkSetters(testCase, T1, D1, Y1)
val = testCase.phase.TDY;
T0 = val{1};
D0 = val{2};
Y0 = val{3};
testCase.phase.TDY = {T1, D1, Y1};
X1 = testCase.phase.X;
P1 = testCase.phase.P;
H1 = testCase.phase.H;
S1 = testCase.phase.S;
U1 = testCase.phase.U;
V1 = testCase.phase.V;
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.TPY = {T1, P1, Y1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.UVY = {U1, V1, Y1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.HPY = {H1, P1, Y1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.SPY = {S1, P1, Y1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.TPX = {T1, P1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.UVX = {U1, V1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.HPX = {H1, P1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.SPX = {S1, P1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.SVX = {S1, V1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.SVY = {S1, V1, Y1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.DPX = {D1, P1, X1};
testCase.checkState(T1, D1, Y1);
testCase.phase.TDY = {T0, D0, Y0};
testCase.phase.DPY = {D1, P1, Y1};
testCase.checkState(T1, D1, Y1);
end
end
methods (Test)
% Test methods
function testBaseAttributes(testCase)
testCase.verifyInstanceOf(testCase.phase.solnName, ...
'char');
testCase.verifyInstanceOf(testCase.phase.phaseName, ...
'char');
testCase.phase.phaseName = 'spam';
testCase.verifyMatches(testCase.phase.phaseName, 'spam');
testCase.verifyGreaterThanOrEqual(testCase.phase.tpID, 0);
testCase.verifyMatches(testCase.phase.basis, 'molar');
testCase.phase.basis = 'mass';
testCase.verifyMatches(testCase.phase.basis, 'mass');
end
function testPhases(testCase)
% Note: ThermoPhase.phaseOfMatter is not implemented in Clib
testCase.verifyEqual(testCase.phase.nPhases, 1);
end
function testSpecies(testCase)
testCase.verifyEqual(testCase.phase.nSpecies, 10);
names = testCase.phase.speciesNames;
for i = 1:10
n = testCase.phase.speciesName(i);
m = testCase.phase.speciesIndex(n{:});
testCase.verifyMatches(n{:}, names{i});
testCase.verifyEqual(i, m);
end
testCase.getInvalidValue('speciesNames', {11}, 'must not exceed');
end
function testElements(testCase)
% Note: ThermoPhase.elementName is not implemented in Clib
testCase.verifyEqual(testCase.phase.nElements, 4);
end
function testNAtoms(testCase)
data = {{1, 'O', 'O'}, {2, 'O', 'O2'}, {1, 'H', 'OH'},...
{2, 'H', 'H2O'}, {2, 'O', 'H2O2'}, {1, 'Ar', 'AR'},...
{0, 'O', 'H'}, {0, 'H', 'AR'}, {0, 'Ar', 'HO2'}};
for i = 1:length(data)
n = data{i}{1};
element = data{i}{2};
species = data{i}{3};
mElem = testCase.phase.elementIndex(element);
kSpec = testCase.phase.speciesIndex(species);
n1 = testCase.phase.nAtoms(species, element);
n2 = testCase.phase.nAtoms(kSpec, mElem);
testCase.verifyEqual(n1, n);
testCase.verifyEqual(n2, n);
testCase.getInvalidValue('nAtoms', {'C', 'H2'}, 'no such species');
testCase.getInvalidValue('nAtoms', {'H', 'CH4'}, 'no such element');
end
end
function testElementalMassFraction(testCase)
testCase.phase.Y = 'H2O:0.5, O2:0.5';
Zo = testCase.phase.elementalMassFraction('O');
Zh = testCase.phase.elementalMassFraction('H');
Zar = testCase.phase.elementalMassFraction('Ar');
exp1 = 0.5 + 0.5 * (15.999 / 18.015);
exp2 = 0.5 * (2.016 / 18.015);
exp3 = 0.0;
testCase.verifyNearRelative(Zo, exp1);
testCase.verifyNearRelative(Zh, exp2);
testCase.verifyNearAbsolute(Zar, exp3);
testCase.getInvalidValue('elementalMassFraction', {'C'}, 'No such element');
testCase.getInvalidValue('elementalMassFraction', {5}, 'No such element');
end
function testWeights(testCase)
aw = testCase.phase.atomicMasses;
mw = testCase.phase.molecularWeights;
testCase.verifyEqual(length(aw), testCase.phase.nElements);
testCase.verifyEqual(length(mw), testCase.phase.nSpecies);
for i = 1:length(mw)
testWeight = 0.0;
for j = 1:length(aw)
testWeight = testWeight + ...
aw(j) * testCase.phase.nAtoms(i, j);
end
testCase.verifyNearRelative(testWeight, mw(i));
end
end
function testCharges(testCase)
chargePhase = Solution('gri30_ion.yaml', 'gas');
charges = chargePhase.charges;
test = {{'E',-1}, {'N2',0}, {'H3O+',1}};
for i = 1:length(test)
species = test{i}{1};
charge = test{i}{2};
flag = sum(ismember(chargePhase.speciesNames, species));
testCase.verifyGreaterThan(flag, 0);
idx = chargePhase.speciesIndex(species);
testCase.verifyEqual(charges(idx), charge);
end
clear chargePhase
end
function testReport(testCase)
str = testCase.phase.report;
testCase.verifySubstring(str, testCase.phase.phaseName);
testCase.verifySubstring(str, 'temperature');
for i = 1:testCase.phase.nSpecies
name = testCase.phase.speciesName(i);
testCase.verifySubstring(str, name{:});
end
end
function testRefInfo(testCase)
testCase.verifyNearRelative(testCase.phase.refPressure, OneAtm);
testCase.verifyNearRelative(testCase.phase.minTemp, 300);
testCase.verifyNearRelative(testCase.phase.maxTemp, 3500);
end
function testSingleGetters(testCase)
val = testCase.phase.T;
exp = 300;
testCase.verifyNearRelative(val, exp);
testCase.verifyGreaterThan(testCase.phase.maxTemp, 0);
testCase.verifyGreaterThan(testCase.phase.minTemp, 0);
val = testCase.phase.P;
exp = OneAtm;
testCase.verifyNearRelative(val, exp);
val = testCase.phase.D;
exp = testCase.phase.P * testCase.phase.meanMolecularWeight / ...
(GasConstant * testCase.phase.T);
testCase.verifyNearRelative(val, exp);
testCase.phase.basis = 'mass';
val = testCase.phase.V;
exp = 1/exp;
testCase.verifyNearRelative(val, exp);
testCase.phase.basis = 'molar';
val = testCase.phase.V;
exp = exp * testCase.phase.meanMolecularWeight;
testCase.verifyNearRelative(val, exp);
val = testCase.phase.molarDensity;
exp = testCase.phase.D/testCase.phase.meanMolecularWeight;
testCase.verifyNearRelative(val, exp);
testCase.verifyMatches(testCase.phase.eosType, 'ideal-gas');
testCase.verifyTrue(testCase.phase.isIdealGas);
val = testCase.phase.X;
exp = zeros(1, 10);
exp(1) = 1.0;
testCase.verifyNearAbsolute(val, exp);
val = testCase.phase.Y;
testCase.verifyNearAbsolute(val, exp);
val1 = [testCase.phase.H, testCase.phase.S, ...
testCase.phase.U, testCase.phase.G, ...
testCase.phase.cp, testCase.phase.cv];
testCase.phase.basis = 'mass';
val2 = [testCase.phase.H, testCase.phase.S, ...
testCase.phase.U, testCase.phase.G, ...
testCase.phase.cp, testCase.phase.cv];
exp = val2.*testCase.phase.meanMolecularWeight;
testCase.verifyNearRelative(val1, exp);
val = testCase.phase.isothermalCompressibility;
exp = 1.0 / testCase.phase.P;
testCase.verifyNearRelative(val, exp);
val = testCase.phase.thermalExpansionCoeff;
exp = 1.0 / testCase.phase.T;
testCase.verifyNearRelative(val, exp);
end
function testGetStateMole(testCase)
testCase.phase.TDX = {350.0, 0.01, 'H2:0.1, O2:0.3, AR:0.6'};
testCase.checkGetters;
end
function testGetStateMass(testCase)
testCase.phase.basis = 'mass';
testCase.phase.TDY = {350.0, 0.7, 'H2:0.1, H2O2:0.1, AR:0.8'};
testCase.checkGetters;
end
function testSetComposition(testCase)
xx = zeros(1, testCase.phase.nSpecies);
xx(3) = 1.0;
testCase.phase.X = xx;
yy = testCase.phase.Y;
testCase.verifyNearAbsolute(xx, yy)
end
function testSetCompositionBadLength(testCase)
xx = zeros(1, 5);
testCase.setInvalidValue('X', [], 'cannot be empty');
testCase.setInvalidValue('X', xx, 'must be equal');
testCase.setInvalidValue('Y', xx, 'must be equal');
end
function testSetCompositionString(testCase)
testCase.phase.X = 'h2:1.0, o2:1.0';
xx = testCase.phase.X;
diff1 = abs(xx(1) - 0.5)/0.5;
diff2 = abs(xx(4) - 0.5)/0.5;
testCase.verifyLessThanOrEqual(diff1, testCase.rtol);
testCase.verifyLessThanOrEqual(diff2, testCase.rtol);
end
function testSetCompositionStringBad(testCase)
testCase.setInvalidValue('X', 'H2:1.0,CO2:1.5', 'Unknown species');
testCase.setInvalidValue('X', 'H2:1.0,O2:asdf', 'Trouble processing');
testCase.setInvalidValue('X', 'H2:1.e-x4', 'Trouble processing');
testCase.setInvalidValue('X', 'H2:1e-1.4', 'decimal point in exponent');
testCase.setInvalidValue('X', 'H2:0.5,O2:1.0,H2:0.1', 'Duplicate key');
testCase.setInvalidValue('X', '', 'cannot be empty');
end
function testSetStateMole(testCase)
testCase.checkSetters(750, 0.07, [0.2, 0.1, 0.0, 0.3, 0.1, ...
0.0, 0.0, 0.2, 0.1, 0.0]);
end
function testSetStateMass(testCase)
testCase.phase.basis = 'mass';
testCase.checkSetters(500, 1.5, [0.1, 0.0, 0.0, 0.1, 0.4, ...
0.2, 0.0, 0.0, 0.2, 0.0]);
end
function testSetterErrors(testCase)
testCase.setInvalidValue('TD', 400, 'not supported');
testCase.setInvalidValue('TP', {300, 101325, 'CH4:1.0'}, ...
'incorrect number');
testCase.setInvalidValue('HPY', {1.2e6, 101325}, ...
'incorrect number');
testCase.setInvalidValue('UVX', {-4e5, 4.4, 'H2:1.0', -1}, ...
'incorrect number');
end
function testInvalidProperty(testCase)
testCase.verifyError(@() testCase.getInvalidProperty(),...
'MATLAB:noSuchMethodOrField');
testCase.verifyError(@() testCase.setInvalidProperty(300),...
'MATLAB:noPublicFieldForClass');
end
end
end

View File

@ -1,11 +1,16 @@
% Load Cantera without changing rootDir
rootDir = '/home/ssun30/anaconda3/envs/ct-matlab';
ctName = '/lib/libcantera_shared.so';
clear all
% Copy library to test folder
ctTestPath;
% Load Cantera
rootDir = fullfile(pwd);
ctName = '/test/matlab_experimental/libcantera_shared.so';
% Load Cantera
if ~libisloaded('libcantera_shared')
[~, warnings] = loadlibrary([rootDir, ctName], ...
[rootDir, '/include/cantera/clib/ctmatlab.h'], ...
'includepath', [rootDir '/include'], ...
'includepath', [rootDir, '/include'], ...
'addheader', 'ct', 'addheader', 'ctfunc', ...
'addheader', 'ctmultiphase', 'addheader', ...
'ctonedim', 'addheader', 'ctreactor', ...
@ -15,8 +20,10 @@ end
disp('Cantera is loaded for test');
% Run all tests
runtests('ctMatlabTestThermo');
results1 = runtests('ctTestThermo')
% Unload Cantera
% Unload Cantera and remove temporary library file
unloadlibrary('libcantera_shared');
delete([rootDir, ctName]);
disp('Cantera has been unloaded');

View File

@ -0,0 +1,44 @@
% ctTestPath.m
% Set up environment for testing the Cantera Matlab interface
% from within the Cantera source tree. Run this file from the
% root of the Cantera source tree, for example:
%
% cd ~/src/cantera
% run interfaces/matlab_experimental/testpath.m
% get the list of directories on the Matlab path
dirs = regexp(path, ['([^' pathsep ']*)'], 'match');
% if 'cantera' is already in the path, remove it
for i = 1:length(dirs)
if strfind(dirs{i}, 'Cantera')
rmpath(dirs{i});
continue;
end
if strfind(dirs{i}, 'cantera')
rmpath(dirs{i});
end
end
cantera_root = fullfile(pwd);
% Copy the Cantera shared library from the build directory if necessary
if ispc
libname = 'cantera_shared.dll';
elseif ismac
libname = 'libcantera_shared.dylib';
elseif isunix
libname = 'libcantera_shared.so';
end
copyfile(fullfile(cantera_root, 'build', 'lib', libname), ...
fullfile(pwd, 'test', 'matlab_experimental'));
% Add the Cantera toolbox to the Matlab path
addpath(genpath([cantera_root, '/interfaces/matlab_experimental']));
addpath(genpath([cantera_root, '/test/matlab_experimental']));
% Set path to Python module
if strcmp(getenv('PYTHONPATH'), '')
setenv('PYTHONPATH', fullfile(cantera_root, 'build', 'python'))
end

View File

@ -0,0 +1,456 @@
classdef ctTestThermo < matlab.unittest.TestCase
properties
phase
rtol = 1e-6;
atol = 1e-8;
end
methods (TestClassTeardown)
function testTearDown(self)
% Clean up Cantera
ctCleanUp
end
end
methods (TestMethodSetup)
% Setup for each test
function createPhase(self)
src = 'h2o2.yaml';
id = 'ohmech';
transport = 'none';
self.phase = Solution(src, id, transport);
end
end
methods (TestMethodTeardown)
% Destroy object
function deleteSolution(self)
clear self.phase;
end
end
methods
% Generic function to set invalid values to attribute and verify errors
function setInvalidValue(self, attr, val, errMessage)
try
self.phase.(attr) = val;
catch ME
self.verifySubstring(ME.message, errMessage);
end
end
% Generic function to get invalid values of an attribute and verify errors
function val = getInvalidValue(self, attr, args, errMessage)
try
if nargin == 3
val = self.phase.(attr);
else
val = self.phase.(attr)(args{:});
end
catch ME
self.verifySubstring(ME.message, errMessage);
end
end
% Generic function to get an invalid property
function a = getInvalidProperty(self)
a = self.phase.foobar;
end
% Generic function to set an invalid property
function setInvalidProperty(self, val)
self.phase.foobar = val;
end
% Check state
function checkState(self, T, D, Y)
self.verifyEqual(self.phase.T, T, 'RelTol', self.rtol);
self.verifyEqual(self.phase.D, D, 'RelTol', self.rtol);
self.verifyEqual(self.phase.Y, Y, 'AbsTol', self.atol);
end
% Check multi properties
function checkMultiProperties(self, str)
val = self.phase.(str);
for i = 1:length(str)
attr = str(i);
exp = self.phase.(attr);
self.verifyEqual(val{i}, exp, 'RelTol', self.rtol);
end
end
% Check getter
function checkGetters(self)
self.checkMultiProperties('TD');
self.checkMultiProperties('TDX');
self.checkMultiProperties('TDY');
self.checkMultiProperties('TP');
self.checkMultiProperties('TPX');
self.checkMultiProperties('TPY');
self.checkMultiProperties('HP');
self.checkMultiProperties('HPX');
self.checkMultiProperties('HPY');
self.checkMultiProperties('UV');
self.checkMultiProperties('UVX');
self.checkMultiProperties('UVY');
self.checkMultiProperties('SP');
self.checkMultiProperties('SPX');
self.checkMultiProperties('SPY');
self.checkMultiProperties('SV');
self.checkMultiProperties('SVX');
self.checkMultiProperties('SVY');
self.checkMultiProperties('DP');
self.checkMultiProperties('DPX');
self.checkMultiProperties('DPY');
end
% Check setter
function checkSetters(self, T1, D1, Y1)
val = self.phase.TDY;
T0 = val{1};
D0 = val{2};
Y0 = val{3};
self.phase.TDY = {T1, D1, Y1};
X1 = self.phase.X;
P1 = self.phase.P;
H1 = self.phase.H;
S1 = self.phase.S;
U1 = self.phase.U;
V1 = self.phase.V;
self.phase.TDY = {T0, D0, Y0};
self.phase.TPY = {T1, P1, Y1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.UVY = {U1, V1, Y1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.HPY = {H1, P1, Y1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.SPY = {S1, P1, Y1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.TPX = {T1, P1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.UVX = {U1, V1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.HPX = {H1, P1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.SPX = {S1, P1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.SVX = {S1, V1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.SVY = {S1, V1, Y1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.DPX = {D1, P1, X1};
self.checkState(T1, D1, Y1);
self.phase.TDY = {T0, D0, Y0};
self.phase.DPY = {D1, P1, Y1};
self.checkState(T1, D1, Y1);
end
end
methods (Test)
% Test methods
function testBaseAttributes(self)
self.verifyInstanceOf(self.phase.solnName, ...
'char');
self.verifyInstanceOf(self.phase.phaseName, ...
'char');
self.phase.phaseName = 'spam';
self.verifyMatches(self.phase.phaseName, 'spam');
self.verifyGreaterThanOrEqual(self.phase.tpID, 0);
self.verifyMatches(self.phase.basis, 'molar');
self.phase.basis = 'mass';
self.verifyMatches(self.phase.basis, 'mass');
end
function testPhases(self)
% Note: ThermoPhase.phaseOfMatter is not implemented in Clib
self.verifyEqual(self.phase.nPhases, 1);
end
function testSpecies(self)
self.verifyEqual(self.phase.nSpecies, 10);
names = self.phase.speciesNames;
for i = 1:10
n = self.phase.speciesName(i);
m = self.phase.speciesIndex(n{:});
self.verifyMatches(n{:}, names{i});
self.verifyEqual(i, m);
end
self.getInvalidValue('speciesNames', {11}, 'must not exceed');
end
function testElements(self)
% Note: ThermoPhase.elementName is not implemented in Clib
self.verifyEqual(self.phase.nElements, 4);
end
function testNAtoms(self)
data = {{1, 'O', 'O'}, {2, 'O', 'O2'}, {1, 'H', 'OH'},...
{2, 'H', 'H2O'}, {2, 'O', 'H2O2'}, {1, 'Ar', 'AR'},...
{0, 'O', 'H'}, {0, 'H', 'AR'}, {0, 'Ar', 'HO2'}};
for i = 1:length(data)
n = data{i}{1};
element = data{i}{2};
species = data{i}{3};
mElem = self.phase.elementIndex(element);
kSpec = self.phase.speciesIndex(species);
n1 = self.phase.nAtoms(species, element);
n2 = self.phase.nAtoms(kSpec, mElem);
self.verifyEqual(n1, n);
self.verifyEqual(n2, n);
self.getInvalidValue('nAtoms', {'C', 'H2'}, 'no such species');
self.getInvalidValue('nAtoms', {'H', 'CH4'}, 'no such element');
end
end
function testElementalMassFraction(self)
self.phase.Y = 'H2O:0.5, O2:0.5';
Zo = self.phase.elementalMassFraction('O');
Zh = self.phase.elementalMassFraction('H');
Zar = self.phase.elementalMassFraction('Ar');
exp1 = 0.5 + 0.5 * (15.999 / 18.015);
exp2 = 0.5 * (2.016 / 18.015);
exp3 = 0.0;
self.verifyEqual(Zo, exp1, 'AbsTol', self.atol);
self.verifyEqual(Zh, exp2, 'AbsTol', self.atol);
self.verifyEqual(Zar, exp3, 'AbsTol', self.atol);
self.getInvalidValue('elementalMassFraction', {'C'}, 'No such element');
self.getInvalidValue('elementalMassFraction', {5}, 'No such element');
end
function testWeights(self)
aw = self.phase.atomicMasses;
mw = self.phase.molecularWeights;
self.verifyEqual(length(aw), self.phase.nElements);
self.verifyEqual(length(mw), self.phase.nSpecies);
for i = 1:length(mw)
testWeight = 0.0;
for j = 1:length(aw)
testWeight = testWeight + ...
aw(j) * self.phase.nAtoms(i, j);
end
self.verifyEqual(testWeight, mw(i), 'RelTol', self.rtol);
end
end
function testCharges(self)
chargePhase = Solution('gri30_ion.yaml', 'gas');
charges = chargePhase.charges;
test = {{'E',-1}, {'N2',0}, {'H3O+',1}};
for i = 1:length(test)
species = test{i}{1};
charge = test{i}{2};
flag = sum(ismember(chargePhase.speciesNames, species));
self.verifyGreaterThan(flag, 0);
idx = chargePhase.speciesIndex(species);
self.verifyEqual(charges(idx), charge);
end
clear chargePhase
end
function testReport(self)
str = self.phase.report;
self.verifySubstring(str, self.phase.phaseName);
self.verifySubstring(str, 'temperature');
for i = 1:self.phase.nSpecies
name = self.phase.speciesName(i);
self.verifySubstring(str, name{:});
end
end
function testRefInfo(self)
self.verifyEqual(self.phase.refPressure, OneAtm, 'RelTol', self.rtol);
self.verifyEqual(self.phase.minTemp, 300, 'RelTol', self.rtol);
self.verifyEqual(self.phase.maxTemp, 3500, 'RelTol', self.rtol);
end
function testSingleGetters(self)
val = self.phase.T;
exp = 300;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
self.verifyGreaterThan(self.phase.maxTemp, 0);
self.verifyGreaterThan(self.phase.minTemp, 0);
val = self.phase.P;
exp = OneAtm;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
val = self.phase.D;
exp = self.phase.P * self.phase.meanMolecularWeight / ...
(GasConstant * self.phase.T);
self.verifyEqual(val, exp, 'RelTol', self.rtol);
self.phase.basis = 'mass';
val = self.phase.V;
exp = 1/exp;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
self.phase.basis = 'molar';
val = self.phase.V;
exp = exp * self.phase.meanMolecularWeight;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
val = self.phase.molarDensity;
exp = self.phase.D/self.phase.meanMolecularWeight;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
self.verifyMatches(self.phase.eosType, 'ideal-gas');
self.verifyTrue(self.phase.isIdealGas);
val = self.phase.X;
exp = zeros(1, 10);
exp(1) = 1.0;
tol = ones(1, 10).*self.rtol;
self.verifyEqual(val, exp, 'RelTol', tol);
val = self.phase.Y;
self.verifyEqual(val, exp, 'RelTol', tol);
val1 = [self.phase.H, self.phase.S, ...
self.phase.U, self.phase.G, ...
self.phase.cp, self.phase.cv];
self.phase.basis = 'mass';
val2 = [self.phase.H, self.phase.S, ...
self.phase.U, self.phase.G, ...
self.phase.cp, self.phase.cv];
exp = val2.*self.phase.meanMolecularWeight;
tol = ones(1, 9).*self.rtol;
self.verifyEqual(val1, exp, 'RelTol', tol);
val = self.phase.isothermalCompressibility;
exp = 1.0 / self.phase.P;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
val = self.phase.thermalExpansionCoeff;
exp = 1.0 / self.phase.T;
self.verifyEqual(val, exp, 'RelTol', self.rtol);
end
function testGetStateMole(self)
self.phase.TDX = {350.0, 0.01, 'H2:0.1, O2:0.3, AR:0.6'};
self.checkGetters;
end
function testGetStateMass(self)
self.phase.basis = 'mass';
self.phase.TDY = {350.0, 0.7, 'H2:0.1, H2O2:0.1, AR:0.8'};
self.checkGetters;
end
function testSetComposition(self)
xx = zeros(1, self.phase.nSpecies);
xx(3) = 1.0;
self.phase.X = xx;
yy = self.phase.Y;
tol = ones(1, 10).*self.atol;
self.verifyEqual(xx, yy, 'AbsTol', tol)
end
% This segment is disabled since it gets a SegFault error without patching
% the Cantera Matlab Toolbox.
% function testSetCompositionBadLength(self)
% xx = zeros(1, 5);
% self.setInvalidValue('X', [], 'cannot be empty');
% self.setInvalidValue('X', xx, 'must be equal');
% self.setInvalidValue('Y', xx, 'must be equal');
% end
function testSetCompositionString(self)
self.phase.X = 'h2:1.0, o2:1.0';
xx = self.phase.X;
self.verifyEqual(xx(1), 0.5, 'AbsTol', self.atol);
self.verifyEqual(xx(4), 0.5, 'AbsTol', self.atol);
end
function testSetCompositionStringBad(self)
self.setInvalidValue('X', 'H2:1.0,CO2:1.5', 'Unknown species');
self.setInvalidValue('X', 'H2:1.0,O2:asdf', 'Trouble processing');
self.setInvalidValue('X', 'H2:1.e-x4', 'Trouble processing');
self.setInvalidValue('X', 'H2:1e-1.4', 'decimal point in exponent');
self.setInvalidValue('X', 'H2:0.5,O2:1.0,H2:0.1', 'Duplicate key');
self.setInvalidValue('X', '', 'cannot be empty');
end
function testSetStateMole(self)
self.checkSetters(750, 0.07, [0.2, 0.1, 0.0, 0.3, 0.1, ...
0.0, 0.0, 0.2, 0.1, 0.0]);
end
function testSetStateMass(self)
self.phase.basis = 'mass';
self.checkSetters(500, 1.5, [0.1, 0.0, 0.0, 0.1, 0.4, ...
0.2, 0.0, 0.0, 0.2, 0.0]);
end
function testSetterErrors(self)
self.setInvalidValue('TD', 400, 'not supported');
self.setInvalidValue('TP', {300, 101325, 'CH4:1.0'}, ...
'incorrect number');
self.setInvalidValue('HPY', {1.2e6, 101325}, ...
'incorrect number');
self.setInvalidValue('UVX', {-4e5, 4.4, 'H2:1.0', -1}, ...
'incorrect number');
end
function testInvalidProperty(self)
self.verifyError(@() self.getInvalidProperty(),...
'MATLAB:noSuchMethodOrField');
self.verifyError(@() self.setInvalidProperty(300),...
'MATLAB:noPublicFieldForClass');
end
end
end