1import unittest
2import cantera as ct
3from . import utilities
4
5
6class TestMixture(utilities.CanteraTest):
7    @classmethod
8    def setUpClass(self):
9        utilities.CanteraTest.setUpClass()
10        self.phase1 = ct.Solution('h2o2.yaml', transport_model=None)
11        self.phase2 = ct.Solution('air.yaml')
12
13    def setUp(self):
14        self.mix = ct.Mixture([(self.phase1, 1.0), (self.phase2, 2.0)])
15
16    def test_sizes(self):
17        self.assertEqual(self.mix.n_phases, 2)
18
19        self.assertEqual(self.mix.n_species,
20                         self.phase1.n_species + self.phase2.n_species)
21
22        E = set(self.phase1.element_names) | set(self.phase2.element_names)
23        self.assertEqual(len(E), self.mix.n_elements)
24
25    def test_element_index(self):
26        m_H = self.mix.element_index('H')
27        self.assertEqual(m_H, self.mix.element_index(m_H))
28
29        with self.assertRaisesRegex(ValueError, 'No such element'):
30            self.mix.element_index('W')
31
32        with self.assertRaisesRegex(ValueError, 'No such element'):
33            self.mix.element_index(41)
34
35        with self.assertRaisesRegex(TypeError, 'must be a string or a number'):
36            self.mix.element_index(None)
37
38    def test_speciesIndex(self):
39        names = self.mix.species_names
40        kOH = names.index('OH')
41        kN2 = names.index('N2O')
42        self.assertEqual(self.mix.species_name(kOH), 'OH')
43        self.assertEqual(self.mix.species_name(kN2), 'N2O')
44
45        self.assertEqual(self.mix.species_index(0, 'OH'), kOH)
46        self.assertEqual(self.mix.species_index(self.phase1, 'OH'), kOH)
47        self.assertEqual(self.mix.species_index(self.phase1.name, 'OH'), kOH)
48        self.assertEqual(self.mix.species_index(0, self.phase1.species_index('OH')), kOH)
49        self.assertEqual(self.mix.species_index(1, self.phase2.species_index('N2O')), kN2)
50        self.assertEqual(self.mix.species_index(1, 'N2O'), kN2)
51
52        with self.assertRaisesRegex(IndexError, 'out of range'):
53            self.mix.species_index(3, 'OH')
54
55        with self.assertRaisesRegex(ValueError, 'No such species'):
56            self.mix.species_index(1, 'OH')
57
58        with self.assertRaisesRegex(ValueError, 'out of range'):
59            self.mix.species_index(0, -2)
60
61        with self.assertRaisesRegex(ValueError, 'No such species'):
62            self.mix.species_index(1, 'CO2')
63
64    def test_n_atoms(self):
65        names = self.mix.species_names
66        kOH = names.index('OH')
67        kN2 = names.index('N2')
68        mH = self.mix.element_index('H')
69        mN = self.mix.element_index('N')
70
71        self.assertEqual(self.mix.n_atoms(kOH, 'H'), 1)
72        self.assertEqual(self.mix.n_atoms(kOH, 'O'), 1)
73        self.assertEqual(self.mix.n_atoms(kOH, mH), 1)
74        self.assertEqual(self.mix.n_atoms(kOH, mN), 0)
75
76        self.assertEqual(self.mix.n_atoms(kN2, mN), 2)
77        self.assertEqual(self.mix.n_atoms(kN2, mH), 0)
78
79    def test_phase(self):
80        self.assertEqual(self.phase1, self.mix.phase(0))
81        self.assertEqual(self.phase2, self.mix.phase(1))
82
83        phaseNames = self.mix.phase_names
84        self.assertEqual(len(phaseNames), self.mix.n_phases)
85        self.assertEqual(phaseNames[0], self.phase1.name)
86        self.assertEqual(phaseNames[1], self.phase2.name)
87
88    def test_phase_index(self):
89        self.assertEqual(self.mix.phase_index(self.phase1), 0)
90        self.assertEqual(self.mix.phase_index(self.phase2), 1)
91        self.assertEqual(self.mix.phase_index(self.phase2.name), 1)
92        self.assertEqual(self.mix.phase_index(1), 1)
93
94        with self.assertRaises(KeyError):
95            self.mix.phase_index('foobar')
96
97        with self.assertRaises(IndexError):
98            self.mix.phase_index(2)
99
100    def test_properties(self):
101        self.mix.T = 350
102        self.assertEqual(self.mix.T, 350)
103
104        self.mix.P = 2e5
105        self.assertEqual(self.mix.P, 2e5)
106        self.assertEqual(self.mix.T, 350)
107
108        self.assertGreater(self.mix.max_temp, self.mix.min_temp)
109
110    def test_charge(self):
111        C = sum(self.mix.phase_charge(i) for i in range(self.mix.n_phases))
112        self.assertEqual(self.mix.charge, C)
113
114    def test_phase_moles(self):
115        M = self.mix.phase_moles()
116        self.assertEqual(M[0], self.mix.phase_moles(0))
117        self.assertEqual(M[1], self.mix.phase_moles('air'))
118
119        self.mix.set_phase_moles('air', 4)
120        self.assertEqual(self.mix.phase_moles(1), 4)
121
122    def test_species_moles(self):
123        self.mix.species_moles = 'H2:1.0, NO2:4.0'
124        P = self.mix.phase_moles()
125        S = self.mix.species_moles
126
127        self.assertEqual(P[0], 1)
128        self.assertEqual(P[1], 4)
129
130        self.assertEqual(S[self.mix.species_index(0, 'H2')], 1)
131        self.assertEqual(S[self.mix.species_index(1, 'NO2')], 4)
132
133        S[2] = 7
134        self.mix.species_moles = S
135        self.assertNear(self.mix.species_moles[2], S[2])
136        self.assertNear(self.mix.phase_moles(0), sum(S[:self.phase1.n_species]))
137
138        with self.assertRaises(ValueError):
139            self.mix.species_moles = (1,2,3)
140
141        with self.assertRaises(TypeError):
142            self.mix.species_moles = 9
143
144    def test_element_moles(self):
145        self.mix.species_moles = 'H2:1.0, OH:4.0'
146
147        self.assertNear(self.mix.element_moles('H'), 6)
148        self.assertNear(self.mix.element_moles('O'), 4)
149        self.assertNear(self.mix.element_moles('N'), 0)
150
151    def test_chemical_potentials(self):
152        C = self.mix.chemical_potentials
153        C1 = self.phase1.chemical_potentials
154        C2 = self.phase2.chemical_potentials
155
156        self.assertArrayNear(C[:self.phase1.n_species], C1)
157        self.assertArrayNear(C[self.phase1.n_species:], C2)
158
159    def test_equilibrate1(self):
160        self.mix.species_moles = 'H2:1.0, O2:0.5, N2:1.0'
161        self.mix.T = 400
162        self.mix.P = 2 * ct.one_atm
163
164        E1 = [self.mix.element_moles(m) for m in range(self.mix.n_elements)]
165        self.mix.equilibrate('TP', solver='vcs', estimate_equil=-1)
166
167        E2 = [self.mix.element_moles(m) for m in range(self.mix.n_elements)]
168        self.assertArrayNear(E1, E2)
169        self.assertNear(self.mix.T, 400)
170        self.assertNear(self.mix.P, 2 * ct.one_atm)
171
172    @unittest.expectedFailure  # See https://github.com/Cantera/cantera/issues/1023
173    def test_equilibrate2(self):
174        self.mix.species_moles = 'H2:1.0, O2:0.5, N2:1.0'
175        self.mix.T = 400
176        self.mix.P = 2 * ct.one_atm
177
178        E1 = [self.mix.element_moles(m) for m in range(self.mix.n_elements)]
179        self.mix.equilibrate('TP', solver='gibbs')
180
181        E2 = [self.mix.element_moles(m) for m in range(self.mix.n_elements)]
182        self.assertArrayNear(E1, E2)
183        self.assertNear(self.mix.T, 400)
184        self.assertNear(self.mix.P, 2 * ct.one_atm)
185
186    def test_invalid_property(self):
187        x = self.mix
188        with self.assertRaises(AttributeError):
189            x.foobar = 300
190        with self.assertRaises(AttributeError):
191            x.foobar
192
193    def test_invalid_phase_type(self):
194        water = ct.Water()
195        with self.assertRaisesRegex(ct.CanteraError, 'not compatible'):
196            self.mix = ct.Mixture([(self.phase1, 1.0), (water, 2.0)])
197