Skip to content

Commit

Permalink
[Python] Basic implementation of class Quantity
Browse files Browse the repository at this point in the history
  • Loading branch information
speth committed Aug 24, 2015
1 parent 6e138d0 commit 94f94dd
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions interfaces/cython/cantera/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ._cantera import *
from ._cantera import __version__, __sundials_version__
from .composite import *
from .liquidvapor import *
from .onedim import *
from .utils import *
Expand Down
104 changes: 104 additions & 0 deletions interfaces/cython/cantera/composite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from ._cantera import *

class Quantity(object):
"""
A class representing a specific quantity of a `Solution`.
"""
def __init__(self, phase, mass=None, moles=None, constant='UV'):
self.state = phase.TDY
self._phase = phase

if mass is not None:
self.mass = mass
elif moles is not None:
self.moles = moles
else:
self.mass = 1.0

assert constant in ('TP','TV','HP','SP','SV','UV')
self.constant = constant

@property
def phase(self):
self._phase.TDY = self.state
return self._phase

@property
def moles(self):
return self.mass / self.phase.mean_molecular_weight

@moles.setter
def moles(self, n):
self.mass = n * self.phase.mean_molecular_weight

@property
def volume(self):
return self.mass * self.phase.volume_mass

@property
def int_energy(self):
return self.mass * self.phase.int_energy_mass

@property
def enthalpy(self):
return self.mass * self.phase.enthalpy_mass

@property
def entropy(self):
return self.mass * self.phase.entropy_mass

@property
def gibbs(self):
return self.mass * self.phase.gibbs_mass

def __imul__(self, other):
self.mass *= other
return self

def __mul__(self, other):
return Quantity(self.phase, mass=self.mass * other)

def __rmul__(self, other):
return Quantity(self.phase, mass=self.mass * other)

def __iadd__(self, other):
assert(self.constant == other.constant)
a1,b1 = getattr(self.phase, self.constant)
a2,b2 = getattr(other.phase, self.constant)
m = self.mass + other.mass
a = (a1 * self.mass + a2 * other.mass) / m
b = (b1 * self.mass + b2 * other.mass) / m
self._phase.Y = (self.Y * self.mass + other.Y * other.mass) / m
setattr(self._phase, self.constant, (a,b))
self.state = self._phase.TDY
self.mass = m
return self

def __add__(self, other):
newquantity = Quantity(self.phase, mass=self.mass, constant=self.constant)
newquantity += other
return newquantity

# Synonyms for total properties
Quantity.V = Quantity.volume
Quantity.U = Quantity.int_energy
Quantity.H = Quantity.enthalpy
Quantity.S = Quantity.entropy
Quantity.G = Quantity.gibbs

# Add properties to act as pass-throughs for attributes of class Solution
def _prop(attr):
def getter(self):
return getattr(self.phase, attr)

def setter(self, value):
setattr(self.phase, attr, value)
self.state = self._phase.TDY

return property(getter, setter, doc=getattr(Solution, attr).__doc__)

for _attr in dir(Solution):
if _attr.startswith('_') or _attr in Quantity.__dict__:
continue
else:
setattr(Quantity, _attr, _prop(_attr))
69 changes: 69 additions & 0 deletions interfaces/cython/cantera/test/test_thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,3 +910,72 @@ def test_coeffs(self):
self.assertEqual(st.max_temp, 3500)
self.assertEqual(st.reference_pressure, 101325)
self.assertArrayNear(self.h2o_coeffs, st.coeffs)


class TestQuantity(utilities.CanteraTest):
@classmethod
def setUpClass(cls):
cls.gas = ct.Solution('gri30.xml')

def setUp(self):
self.gas.TPX = 300, 101325, 'O2:1.0, N2:3.76'

def test_mass_moles(self):
q1 = ct.Quantity(self.gas, mass=5)
self.assertNear(q1.mass, 5)
self.assertNear(q1.moles, 5 / q1.mean_molecular_weight)

q1.mass = 7
self.assertNear(q1.moles, 7 / q1.mean_molecular_weight)

q1.moles = 9
self.assertNear(q1.moles, 9)
self.assertNear(q1.mass, 9 * q1.mean_molecular_weight)

def test_extensive(self):
q1 = ct.Quantity(self.gas, mass=5)
self.assertNear(q1.mass, 5)

self.assertNear(q1.volume * q1.density, q1.mass)
self.assertNear(q1.V * q1.density, q1.mass)
self.assertNear(q1.int_energy, q1.moles * q1.int_energy_mole)
self.assertNear(q1.enthalpy, q1.moles * q1.enthalpy_mole)
self.assertNear(q1.entropy, q1.moles * q1.entropy_mole)
self.assertNear(q1.gibbs, q1.moles * q1.gibbs_mole)
self.assertNear(q1.int_energy, q1.U)
self.assertNear(q1.enthalpy, q1.H)
self.assertNear(q1.entropy, q1.S)
self.assertNear(q1.gibbs, q1.G)

def test_multiply(self):
q1 = ct.Quantity(self.gas, mass=5)
q2 = q1 * 2.5
self.assertNear(q1.mass * 2.5, q2.mass)
self.assertNear(q1.moles * 2.5, q2.moles)
self.assertNear(q1.entropy * 2.5, q2.entropy)
self.assertArrayNear(q1.X, q2.X)

def test_iadd(self):
q0 = ct.Quantity(self.gas, mass=5)
q1 = ct.Quantity(self.gas, mass=5)
q2 = ct.Quantity(self.gas, mass=5)
q2.TPX = 500, 101325, 'CH4:1.0'

q1 += q2
self.assertNear(q0.mass + q2.mass, q1.mass)
# addition is at constant UV
self.assertNear(q0.U + q2.U, q1.U)
self.assertNear(q0.V + q2.V, q1.V)
self.assertArrayNear(q0.X*q0.moles + q2.X*q2.moles, q1.X*q1.moles)

def test_add(self):
q1 = ct.Quantity(self.gas, mass=5)
q2 = ct.Quantity(self.gas, mass=5)
q2.TPX = 500, 101325, 'CH4:1.0'

q3 = q1 + q2
self.assertNear(q1.mass + q2.mass, q3.mass)
# addition is at constant UV
self.assertNear(q1.U + q2.U, q3.U)
self.assertNear(q1.V + q2.V, q3.V)
self.assertArrayNear(q1.X*q1.moles + q2.X*q2.moles, q3.X*q3.moles)

0 comments on commit 94f94dd

Please sign in to comment.