[Python] Remove the legacy Python module
This commit is contained in:
parent
07739c4b1f
commit
b5e540c903
298 changed files with 162 additions and 44361 deletions
95
SConstruct
95
SConstruct
|
|
@ -46,20 +46,15 @@ extraEnvArgs = {}
|
|||
if 'clean' in COMMAND_LINE_TARGETS:
|
||||
removeDirectory('build')
|
||||
removeDirectory('stage')
|
||||
removeDirectory('interfaces/python/build')
|
||||
removeDirectory('.sconf_temp')
|
||||
removeFile('.sconsign.dblite')
|
||||
removeFile('include/cantera/base/config.h')
|
||||
removeFile('interfaces/python/setup.py')
|
||||
removeFile('ext/f2c_libs/arith.h')
|
||||
removeFile('ext/f2c_libs/signal1.h')
|
||||
removeFile('ext/f2c_libs/sysdep1.h')
|
||||
for name in os.listdir('.'):
|
||||
if name.endswith('.msi'):
|
||||
removeFile(name)
|
||||
for name in os.listdir('interfaces/python/Cantera'):
|
||||
if name.startswith('_cantera') or name.startswith('cantera_shared'):
|
||||
removeFile('interfaces/python/Cantera/' + name)
|
||||
removeFile('interfaces/matlab/toolbox/cantera_shared.dll')
|
||||
for name in os.listdir('interfaces/matlab/toolbox'):
|
||||
if name.startswith('ctmethods.'):
|
||||
|
|
@ -189,8 +184,7 @@ compiler_options = [
|
|||
'The C++ compiler to use.',
|
||||
env['CXX']),
|
||||
('CC',
|
||||
"""The C compiler to use. This is only used to compile CVODE and
|
||||
the Python extension module.""",
|
||||
"""The C compiler to use. This is only used to compile CVODE.""",
|
||||
env['CC'])]
|
||||
opts.AddVariables(*compiler_options)
|
||||
opts.Update(env)
|
||||
|
|
@ -278,15 +272,13 @@ config_options = [
|
|||
EnumVariable(
|
||||
'python_package',
|
||||
"""If you plan to work in Python, or you want to use the graphical
|
||||
MixMaster application, then you need either the 'new' or 'full'
|
||||
Cantera Python Package. If, on the other hand, you will only use
|
||||
Cantera from some other language (e.g. MATLAB or Fortran 90/95) and
|
||||
only need Python to process .cti files, then you only need a
|
||||
'minimal' subset of the package (actually, only one file). The
|
||||
default behavior is to build the Python package if the required
|
||||
prerequisites (numpy) are installed. NOTE: The legacy 'full' option
|
||||
is deprecated in favor of the 'new' Python package. The legacy
|
||||
Python package will be removed in Cantera 2.2 """,
|
||||
MixMaster application, then you need the 'full' Cantera Python
|
||||
Package. If, on the other hand, you will only use Cantera from
|
||||
some other language (e.g. MATLAB or Fortran 90/95) and only need
|
||||
Python to process .cti files, then you only need a 'minimal'
|
||||
subset of the package (actually, only two files). The default
|
||||
behavior is to build the Python package if the required
|
||||
prerequisites (numpy) are installed.""",
|
||||
'default', ('new', 'full', 'minimal', 'none', 'default')),
|
||||
PathVariable(
|
||||
'python_cmd',
|
||||
|
|
@ -867,7 +859,11 @@ env = conf.Finish()
|
|||
# Python 2 Package Settings
|
||||
cython_min_version = LooseVersion('0.17')
|
||||
env['install_python2_action'] = ''
|
||||
if env['python_package'] in ('full','default','new'):
|
||||
if env['python_package'] == 'new':
|
||||
env['python_package'] = 'full' # Allow 'new' as a synonym for 'full'
|
||||
warnNoPython = False
|
||||
|
||||
if env['python_package'] in ('full','default'):
|
||||
# Check for Cython:
|
||||
try:
|
||||
import Cython
|
||||
|
|
@ -876,20 +872,18 @@ if env['python_package'] in ('full','default','new'):
|
|||
except ImportError:
|
||||
cython_version = LooseVersion('0.0.0')
|
||||
|
||||
if cython_version >= cython_min_version:
|
||||
have_cython2 = True
|
||||
else:
|
||||
if cython_version < cython_min_version:
|
||||
message = ("Cython not found or incompatible version: "
|
||||
"Found {0} but {1} or newer is required".format(cython_version, cython_min_version))
|
||||
if env['python_package'] == 'new':
|
||||
if env['python_package'] == 'full':
|
||||
print("ERROR: " + message)
|
||||
sys.exit(1)
|
||||
else:
|
||||
have_cython2 = False
|
||||
warnNoPython = True
|
||||
env['python_package'] = 'minimal'
|
||||
print ("WARNING: " + message)
|
||||
|
||||
# Test to see if we can import the specified array module
|
||||
warnNoPython = False
|
||||
if env['python_array_home']:
|
||||
sys.path.append(env['python_array_home'])
|
||||
try:
|
||||
|
|
@ -900,15 +894,8 @@ if env['python_package'] in ('full','default','new'):
|
|||
print """WARNING: Couldn't find include directory for Python array package"""
|
||||
env['python_array_include'] = ''
|
||||
|
||||
if env['python_package'] == 'default':
|
||||
if have_cython2:
|
||||
env['python_package'] = 'new'
|
||||
else:
|
||||
env['python_package'] = 'full'
|
||||
package_desc = 'new' if env['python_package'] == 'new' else 'legacy'
|
||||
print """INFO: Building the %s Python package using %s.""" % (package_desc, env['python_array'])
|
||||
except ImportError:
|
||||
if env['python_package'] in ('full', 'new'):
|
||||
if env['python_package'] == 'full':
|
||||
print ("""ERROR: Unable to find the array package """
|
||||
"""'%s' required by the Python package.""" % env['python_array'])
|
||||
sys.exit(1)
|
||||
|
|
@ -918,15 +905,20 @@ if env['python_package'] in ('full','default','new'):
|
|||
warnNoPython = True
|
||||
env['python_package'] = 'minimal'
|
||||
|
||||
if warnNoPython:
|
||||
env['python_package'] = 'minimal'
|
||||
else:
|
||||
env['python_package'] = 'full'
|
||||
print """INFO: Building the Python package using %s.""" % env['python_array']
|
||||
|
||||
try:
|
||||
import site
|
||||
env['python_usersitepackages'] = site.getusersitepackages()
|
||||
except AttributeError: # getusersitepackages is only in Python 2.7+
|
||||
env['python_usersitepackages'] = '<user site-packages directory>'
|
||||
|
||||
# Check for 3to2 if we're building the "new" Python module
|
||||
# See http://pypi.python.org/pypi/3to2
|
||||
if env['python_package'] == 'new':
|
||||
# Check for 3to2. See http://pypi.python.org/pypi/3to2
|
||||
if env['python_package'] == 'full':
|
||||
try:
|
||||
ret = getCommandOutput('3to2','-l')
|
||||
except OSError:
|
||||
|
|
@ -937,13 +929,7 @@ if env['python_package'] in ('full','default','new'):
|
|||
env['python_convert_examples'] = False
|
||||
print """WARNING: Couldn't find '3to2'. Python examples will not work correctly."""
|
||||
|
||||
if env['python_package'] == 'full':
|
||||
print ("WARNING: The 'python_package=full' option is deprecated. "
|
||||
"This legacy Python package will be removed in Cantera 2.2. "
|
||||
"The new Python package may be build using 'python_package=new'.")
|
||||
|
||||
else:
|
||||
warnNoPython = False
|
||||
env['python_array_include'] = ''
|
||||
env['python_module_loc'] = ''
|
||||
|
||||
|
|
@ -982,7 +968,7 @@ if env['python3_package'] in ('y', 'default'):
|
|||
elif cython_version < cython_min_version:
|
||||
message = ("Cython package for Python 3 not found or incompatible version: "
|
||||
"Found {0} but {1} or newer is required".format(cython_version, cython_min_version))
|
||||
if env['python3_package'] == 'new':
|
||||
if env['python3_package'] == 'y':
|
||||
print("ERROR: " + message)
|
||||
sys.exit(1)
|
||||
else:
|
||||
|
|
@ -1247,7 +1233,7 @@ for cti in mglob(env, 'data/inputs', 'cti'):
|
|||
outName = os.path.splitext(cti.name)[0] + '.xml'
|
||||
convertedInputFiles.add(outName)
|
||||
build(env.Command('build/data/%s' % outName, cti.path,
|
||||
'$python_cmd interfaces/python/ctml_writer.py $SOURCE $TARGET'))
|
||||
'$python_cmd interfaces/cython/cantera/ctml_writer.py $SOURCE $TARGET'))
|
||||
|
||||
|
||||
# Copy input files which are not present as cti:
|
||||
|
|
@ -1303,10 +1289,10 @@ if addInstallActions:
|
|||
pyExt = '.py' if env['OS'] == 'Windows' else ''
|
||||
install(env.InstallAs,
|
||||
pjoin('$inst_bindir','ck2cti%s' % pyExt),
|
||||
'interfaces/python/ck2cti.py')
|
||||
'interfaces/cython/cantera/ck2cti.py')
|
||||
install(env.InstallAs,
|
||||
pjoin('$inst_bindir','ctml_writer%s' % pyExt),
|
||||
'interfaces/python/ctml_writer.py')
|
||||
'interfaces/cython/cantera/ctml_writer.py')
|
||||
|
||||
# Copy external libaries for Windows installations
|
||||
if env['CC'] == 'cl' and env['use_boost_libs']:
|
||||
|
|
@ -1394,13 +1380,12 @@ if env['f90_interface'] == 'y':
|
|||
VariantDir('build/src', 'src', duplicate=0)
|
||||
SConscript('build/src/SConscript')
|
||||
|
||||
if env['python_package'] in ('full','minimal'):
|
||||
VariantDir('build/src/python', 'src/python', duplicate=0)
|
||||
SConscript('build/src/python/SConscript')
|
||||
|
||||
if env['python3_package'] == 'y' or env['python_package'] == 'new':
|
||||
if env['python3_package'] == 'y' or env['python_package'] == 'full':
|
||||
SConscript('interfaces/cython/SConscript')
|
||||
|
||||
if env['python_package'] == 'minimal':
|
||||
SConscript('interfaces/python_minimal/SConscript')
|
||||
|
||||
SConscript('build/src/apps/SConscript')
|
||||
|
||||
if env['OS'] != 'Windows':
|
||||
|
|
@ -1477,19 +1462,15 @@ File locations:
|
|||
|
||||
if env['python_package'] == 'full':
|
||||
print """
|
||||
Python 2 package (Cantera) %(python_module_loc)s""" % env,
|
||||
Python 2 package (cantera) %(python_module_loc)s
|
||||
Python 2 samples %(python_example_loc)s""" % env,
|
||||
elif warnNoPython:
|
||||
print """
|
||||
#################################################################
|
||||
WARNING: the Cantera Python package was not installed because a
|
||||
suitable array package (e.g. numpy) could not be found.
|
||||
WARNING: the Cantera Python package was not installed because
|
||||
the prerequisites (Cython and NumPy) could not be found.
|
||||
#################################################################"""
|
||||
|
||||
if env['python_package'] == 'new':
|
||||
print """
|
||||
Python 2 package (cantera) %(python_module_loc)s
|
||||
Python 2 samples %(python_example_loc)s""" % env,
|
||||
|
||||
if env['python3_package'] == 'y':
|
||||
print """
|
||||
Python 3 package (cantera) %(python3_module_loc)s
|
||||
|
|
|
|||
|
|
@ -183,10 +183,6 @@ redesigned API that simplifies many operations and aims to provide a more
|
|||
|
||||
Building the new Python module requires the Cython package for Python.
|
||||
|
||||
For compatibility, the legacy Python module is still available, though it will not
|
||||
receive feature updates, and will be removed in a future Cantera release. Users
|
||||
are encouraged to switch to the new Python module when possible.
|
||||
|
||||
The Cython module is compatible with the following Python versions: 2.6, 2.7,
|
||||
3.1, 3.2, and 3.3. Support for Python 2.6 and Python 3.1 requires the ``scipy``
|
||||
and ``unittest2`` packages to be installed as well (see :ref:`sec-dependencies`)
|
||||
|
|
@ -196,8 +192,8 @@ library in more recent versions.
|
|||
Building for Python 2
|
||||
.....................
|
||||
|
||||
By default, SCons will attempt to build the new Python module. To build the
|
||||
legacy Python module instead, use the SCons option ``python_package=full``.
|
||||
By default, SCons will attempt to build the Cython-based Python module for
|
||||
Python 2, if both Numpy and Cython are installed.
|
||||
|
||||
Building for Python 3
|
||||
.....................
|
||||
|
|
@ -424,7 +420,7 @@ Other Required Software
|
|||
* http://python.org/download/
|
||||
* Known to work with 2.6 and 2.7; Expected to work with versions >= 2.5
|
||||
* The Cython module supports Python 2.x and 3.x. However, SCons requires
|
||||
Python 2.x, so compilation of the Cython module requires two Python
|
||||
Python 2.x, so compilation of the Python 3 module requires two Python
|
||||
installations.
|
||||
|
||||
* Boost
|
||||
|
|
@ -458,11 +454,11 @@ Optional Programs
|
|||
|
||||
* `Scipy <http://scipy.org/install.html>`_
|
||||
|
||||
* Required in order to use the new Python module with Python 2.6 or 3.1.
|
||||
* Required in order to use the Python module with Python 2.6 or 3.1.
|
||||
|
||||
* Unittest2
|
||||
|
||||
* Required in order to run the test suite for the new Python module with
|
||||
* Required in order to run the test suite for the Python module with
|
||||
Python 2.6 or Python 3.1.
|
||||
* https://pypi.python.org/pypi/unittest2 (Python 2.6)
|
||||
* https://pypi.python.org/pypi/unittest2py3k (Python 3.1)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ if sys.version_info[0] == 3:
|
|||
sys.path.insert(0, os.path.abspath('../../build/python3'))
|
||||
else:
|
||||
sys.path.insert(0, os.path.abspath('../../build/python2'))
|
||||
sys.path.insert(0, os.path.abspath('../../interfaces/python'))
|
||||
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
sys.path.append(os.path.abspath('./exts'))
|
||||
|
|
|
|||
|
|
@ -3,132 +3,134 @@
|
|||
CTI Class Reference
|
||||
*******************
|
||||
|
||||
.. py:module:: ctml_writer
|
||||
.. py:module:: cantera.ctml_writer
|
||||
|
||||
.. py:currentmodule:: cantera.ctml_writer
|
||||
|
||||
Basic Classes & Functions
|
||||
=========================
|
||||
|
||||
.. autofunction:: ctml_writer.units
|
||||
.. autofunction:: units
|
||||
|
||||
.. autoclass:: ctml_writer.state
|
||||
.. autoclass:: state
|
||||
:no-undoc-members:
|
||||
|
||||
Phases of Matter
|
||||
================
|
||||
|
||||
.. autoclass:: ctml_writer.phase
|
||||
.. autoclass:: phase
|
||||
:no-members:
|
||||
|
||||
.. autoclass:: ctml_writer.ideal_gas
|
||||
.. autoclass:: ideal_gas
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.stoichiometric_solid
|
||||
.. autoclass:: stoichiometric_solid
|
||||
:no-members:
|
||||
|
||||
.. autoclass:: ctml_writer.stoichiometric_liquid
|
||||
.. autoclass:: stoichiometric_liquid
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.metal
|
||||
.. autoclass:: metal
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.semiconductor
|
||||
.. autoclass:: semiconductor
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.incompressible_solid
|
||||
.. autoclass:: incompressible_solid
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.lattice
|
||||
.. autoclass:: lattice
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.lattice_solid
|
||||
.. autoclass:: lattice_solid
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.liquid_vapor
|
||||
.. autoclass:: liquid_vapor
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.redlich_kwong
|
||||
.. autoclass:: redlich_kwong
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.ideal_interface
|
||||
.. autoclass:: ideal_interface
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.edge
|
||||
.. autoclass:: edge
|
||||
:no-undoc-members:
|
||||
|
||||
Elements and Species
|
||||
====================
|
||||
|
||||
.. autoclass:: ctml_writer.element
|
||||
.. autoclass:: element
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.species
|
||||
.. autoclass:: species
|
||||
:no-undoc-members:
|
||||
|
||||
Thermodynamic Properties
|
||||
========================
|
||||
|
||||
.. autoclass:: ctml_writer.Mu0_table
|
||||
.. autoclass:: Mu0_table
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.NASA
|
||||
.. autoclass:: NASA
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.NASA9
|
||||
.. autoclass:: NASA9
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.Shomate
|
||||
.. autoclass:: Shomate
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.Adsorbate
|
||||
.. autoclass:: Adsorbate
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.const_cp
|
||||
.. autoclass:: const_cp
|
||||
:no-undoc-members:
|
||||
|
||||
Transport Properties
|
||||
====================
|
||||
|
||||
.. autoclass:: ctml_writer.gas_transport
|
||||
.. autoclass:: gas_transport
|
||||
:no-undoc-members:
|
||||
|
||||
Reactions
|
||||
=========
|
||||
|
||||
.. autoclass:: ctml_writer.reaction
|
||||
.. autoclass:: reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.Arrhenius
|
||||
.. autoclass:: Arrhenius
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.three_body_reaction
|
||||
.. autoclass:: three_body_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.falloff_reaction
|
||||
.. autoclass:: falloff_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.chemically_activated_reaction
|
||||
.. autoclass:: chemically_activated_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.pdep_arrhenius
|
||||
.. autoclass:: pdep_arrhenius
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.chebyshev_reaction
|
||||
.. autoclass:: chebyshev_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.surface_reaction
|
||||
.. autoclass:: surface_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.edge_reaction
|
||||
.. autoclass:: edge_reaction
|
||||
:no-undoc-members:
|
||||
|
||||
Falloff Parameterizations
|
||||
-------------------------
|
||||
|
||||
.. autoclass:: ctml_writer.Troe
|
||||
.. autoclass:: Troe
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.SRI
|
||||
.. autoclass:: SRI
|
||||
:no-undoc-members:
|
||||
|
||||
.. autoclass:: ctml_writer.Lindemann
|
||||
.. autoclass:: Lindemann
|
||||
:no-undoc-members:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ Documentation
|
|||
cti/index
|
||||
reactors
|
||||
cython/index
|
||||
python/index
|
||||
matlab/index
|
||||
cxx-guide/index
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
Composite Classes
|
||||
=================
|
||||
|
||||
These classes are composite representations of a substance which has
|
||||
thermodynamic, chemical kinetic, and transport properties.
|
||||
|
||||
.. autoclass:: Cantera.Solution
|
||||
.. autoclass:: Cantera.Interface.Interface
|
||||
.. autoclass:: Cantera.Edge.Edge
|
||||
.. autoclass:: Cantera.Mixture
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Physical Constants
|
||||
==================
|
||||
|
||||
.. automodule:: Cantera.constants
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
Convenience Functions
|
||||
=====================
|
||||
|
||||
.. autofunction:: Cantera.gases.Air
|
||||
.. autofunction:: Cantera.gases.Argon
|
||||
.. autofunction:: Cantera.gases.GRI30
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
Error Handling
|
||||
==============
|
||||
|
||||
.. autofunction:: Cantera.getCanteraError
|
||||
|
||||
.. autoexception:: Cantera.exceptions.CanteraError
|
||||
.. autoexception:: Cantera.exceptions.OptionError
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
Func Module
|
||||
===========
|
||||
|
||||
.. py:currentmodule:: Func
|
||||
|
||||
Quick links:
|
||||
* :class:`Polynomial`
|
||||
* :class:`Gaussian`
|
||||
* :class:`Fourier`
|
||||
* :class:`Arrhenius`
|
||||
|
||||
.. automodule:: Cantera.Func
|
||||
:member-order: bysource
|
||||
:no-show-inheritance:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
Importing Phase Objects
|
||||
=======================
|
||||
|
||||
.. autofunction:: Cantera.importPhase
|
||||
.. autofunction:: Cantera.importPhases
|
||||
.. autofunction:: Cantera.importEdge
|
||||
.. autofunction:: Cantera.importInterface
|
||||
.. autofunction:: Cantera.IdealGasMix
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
.. Cantera documentation master file, created by
|
||||
sphinx-quickstart on Mon Mar 12 11:43:09 2012.
|
||||
|
||||
Python Module Documentation (Legacy)
|
||||
====================================
|
||||
|
||||
.. warning::
|
||||
|
||||
This version of the Cantera Python module is deprecated. The last version
|
||||
of Cantera that this module will be included with is Cantera 2.1. Starting
|
||||
with Cantera 2.1, a new Python module (based on the Cython package) is
|
||||
available. When compiling Cantera, the new module can be build by using the
|
||||
SCons option ``python_package=new``. Changes to the user interface are documented in :ref:`sec-cython-documentation`.
|
||||
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
importing
|
||||
thermo
|
||||
kinetics
|
||||
transport
|
||||
composite
|
||||
zerodim
|
||||
onedim
|
||||
func
|
||||
error-handling
|
||||
constants
|
||||
utilities
|
||||
convenience
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Chemical Kinetics
|
||||
=================
|
||||
|
||||
.. autoclass:: Cantera.Kinetics.Kinetics
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
One-Dimensional Reacting Flows
|
||||
==============================
|
||||
|
||||
.. automodule:: Cantera.OneD
|
||||
|
||||
.. autoclass:: Cantera.OneD.onedim.Domain1D
|
||||
.. autofunction:: Cantera.OneD.onedim.clearDomains
|
||||
.. autofunction:: Cantera.OneD.onedim.clearSim1D
|
||||
|
||||
============
|
||||
Flow Domains
|
||||
============
|
||||
.. autoclass:: Cantera.OneD.onedim.AxisymmetricFlow
|
||||
.. autoclass:: Cantera.OneD.StagnationFlow.StagnationFlow
|
||||
|
||||
==========
|
||||
Boundaries
|
||||
==========
|
||||
.. autoclass:: Cantera.OneD.onedim.Bdry1D
|
||||
.. autoclass:: Cantera.OneD.onedim.Inlet
|
||||
.. autoclass:: Cantera.OneD.onedim.Outlet
|
||||
.. autoclass:: Cantera.OneD.onedim.OutletRes
|
||||
.. autoclass:: Cantera.OneD.onedim.SymmPlane
|
||||
|
||||
=================
|
||||
Composite Domains
|
||||
=================
|
||||
.. autoclass:: Cantera.OneD.onedim.Stack
|
||||
.. autoclass:: Cantera.OneD.BurnerDiffFlame.BurnerDiffFlame
|
||||
.. autoclass:: Cantera.OneD.BurnerFlame.BurnerFlame
|
||||
.. autoclass:: Cantera.OneD.CounterFlame.CounterFlame
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
Thermodyamic Properties
|
||||
=======================
|
||||
|
||||
These classes are used to describe the thermodynamic state of a system.
|
||||
|
||||
.. autoclass:: Cantera.Phase.Phase
|
||||
.. autoclass:: Cantera.ThermoPhase.ThermoPhase
|
||||
.. autoclass:: Cantera.SurfacePhase.EdgePhase
|
||||
.. autoclass:: Cantera.SurfacePhase.SurfacePhase
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Transport Properties
|
||||
====================
|
||||
|
||||
.. automodule:: Cantera.Transport
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
Miscellaneous Utilities
|
||||
=======================
|
||||
|
||||
.. autofunction:: Cantera.addDirectory
|
||||
.. autofunction:: Cantera.reset
|
||||
.. autofunction:: Cantera.writeCSV
|
||||
.. autofunction:: Cantera.writeLogFile
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
Zero-Dimensional Reactors
|
||||
=========================
|
||||
|
||||
.. automodule:: Cantera.Reactor
|
||||
:no-members:
|
||||
|
||||
============
|
||||
Base Classes
|
||||
============
|
||||
.. autoclass:: Cantera.Reactor.ReactorBase
|
||||
.. autoclass:: Cantera.Reactor.Reactor
|
||||
.. autoclass:: Cantera.Reactor.FlowDevice
|
||||
|
||||
================
|
||||
Reactor Networks
|
||||
================
|
||||
.. autoclass:: Cantera.Reactor.ReactorNet
|
||||
|
||||
=============
|
||||
Flow Reactors
|
||||
=============
|
||||
.. autoclass:: Cantera.Reactor.FlowReactor
|
||||
.. autoclass:: Cantera.Reactor.ConstPressureReactor
|
||||
.. autoclass:: Cantera.Reactor.Reservoir
|
||||
|
||||
================
|
||||
Flow Controllers
|
||||
================
|
||||
.. autoclass:: Cantera.Reactor.MassFlowController
|
||||
.. autoclass:: Cantera.Reactor.Valve
|
||||
.. autoclass:: Cantera.Reactor.PressureController
|
||||
.. autoclass:: Cantera.Reactor.Wall
|
||||
2
interfaces/cython/.gitignore
vendored
2
interfaces/cython/.gitignore
vendored
|
|
@ -9,10 +9,8 @@ cantera/test/data/*.dat
|
|||
cantera/test/data/*.csv
|
||||
setup2.py
|
||||
setup3.py
|
||||
cantera/ctml_writer.py
|
||||
scripts/ctml_writer.py
|
||||
scripts/ctml_writer
|
||||
cantera/ck2cti.py
|
||||
scripts/ck2cti.py
|
||||
scripts/ck2cti
|
||||
scripts/mixmaster
|
||||
|
|
|
|||
|
|
@ -47,14 +47,6 @@ localenv['py_ctml_writer'] = repr('scripts/ctml_writer%s' % script_ext)
|
|||
localenv['py_ck2cti'] = repr('scripts/ck2cti%s' % script_ext)
|
||||
localenv['py_mixmaster'] = repr('scripts/mixmaster%s' % script_ext)
|
||||
|
||||
# The actual ctml_writer and ck2cti scripts
|
||||
build(env.Command('cantera/ctml_writer.py',
|
||||
'#interfaces/python/ctml_writer.py',
|
||||
Copy('$TARGET', '$SOURCE')))
|
||||
build(env.Command('cantera/ck2cti.py',
|
||||
'#interfaces/python/ck2cti.py',
|
||||
Copy('$TARGET', '$SOURCE')))
|
||||
|
||||
# thin wrappers
|
||||
scripts = []
|
||||
for script in mglob(env, 'scripts', 'py.in'):
|
||||
|
|
@ -177,7 +169,7 @@ if localenv['python3_package'] == 'y':
|
|||
|
||||
|
||||
# Cython module for Python 2.x
|
||||
if localenv['python_package'] == 'new':
|
||||
if localenv['python_package'] == 'full':
|
||||
module_ext, py2_version = configure_numpy(localenv['python_cmd'])
|
||||
make_setup = localenv.SubstFile('#interfaces/cython/setup2.py',
|
||||
'#interfaces/cython/setup.py.in')
|
||||
|
|
|
|||
0
interfaces/python/ck2cti.py → interfaces/cython/cantera/ck2cti.py
Executable file → Normal file
0
interfaces/python/ck2cti.py → interfaces/cython/cantera/ck2cti.py
Executable file → Normal file
3
interfaces/python/.gitignore
vendored
3
interfaces/python/.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
setup.py
|
||||
*.os
|
||||
*.pyd
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
"""
|
||||
Dusty Gas model for transport in porous media.
|
||||
|
||||
"""
|
||||
|
||||
from Cantera.Transport import Transport
|
||||
|
||||
class DustyGasTransport(Transport):
|
||||
"""The Dusty Gas transport model. This class implements a
|
||||
transport manager for the Dusty Gas model for the effective
|
||||
transport properties of a gas in a stationary, solid, porous
|
||||
medium. The only properties computed are the multicomponent
|
||||
diffusion coefficients. The model does not compute viscosity or
|
||||
thermal conductivity.
|
||||
|
||||
This class is a Python shadow class for Cantera C++ class
|
||||
DustyGasTransport.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, phase = None):
|
||||
"""
|
||||
phase - The object representing the gas phase within the
|
||||
pores.
|
||||
"""
|
||||
Transport.__init__(self, model = "DustyGas", phase = phase)
|
||||
|
||||
def setPorosity(self, porosity):
|
||||
"""Set the porosity. Internal. See: set"""
|
||||
self.setParameters(0, 0, [porosity, 0.0])
|
||||
|
||||
def setTortuosity(self, tortuosity):
|
||||
"""Set the tortuosity. Internal. See: set"""
|
||||
self.setParameters(1, 0, [tortuosity, 0.0])
|
||||
|
||||
def setMeanPoreRadius(self, pore_radius):
|
||||
"""Set the mean pore radius [m]. Internal. See: set"""
|
||||
self.setParameters(2, 0, [pore_radius, 0.0])
|
||||
|
||||
def setMeanParticleDiameter(self, diameter):
|
||||
"""Set the mean particle diameter [m]. Internal. See: set"""
|
||||
self.setParameters(3, 0, [diameter, 0.0])
|
||||
|
||||
def setPermeability(self, permeability):
|
||||
"""Set the permeability. If not called, the value for close-packed
|
||||
spheres is used. Internal."""
|
||||
self.setParameters(4, 0, [permeability, 0.0])
|
||||
|
||||
|
||||
def set(self, **p):
|
||||
"""Set model parameters. This is a convenience method that simply
|
||||
calls other methods depending on the keyword.
|
||||
|
||||
porosity - Porosity. Volume fraction of pores.
|
||||
|
||||
tortuosity - Tortuosity. A measure of the extent to which the
|
||||
pores are straight cylinders (tortuosity = 1), or are more
|
||||
tortuous.
|
||||
|
||||
pore_radius - The pore radius [m].
|
||||
|
||||
All keywords are optional.
|
||||
"""
|
||||
for o in p.keys():
|
||||
if o == "porosity":
|
||||
self.setPorosity(p[o])
|
||||
elif o == "tortuosity":
|
||||
self.setTortuosity(p[o])
|
||||
elif o == "pore_radius":
|
||||
self.setMeanPoreRadius(p[o])
|
||||
elif o == "diameter":
|
||||
self.setMeanParticleDiameter(p[o])
|
||||
elif o == "permeability":
|
||||
self.setPermeability(p[o])
|
||||
else:
|
||||
raise 'unknown parameter'
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import string
|
||||
import os
|
||||
|
||||
from constants import *
|
||||
from SurfacePhase import EdgePhase
|
||||
from Kinetics import Kinetics
|
||||
import XML
|
||||
|
||||
class Edge(EdgePhase, Kinetics):
|
||||
"""
|
||||
One-dimensional edge between two surfaces.
|
||||
|
||||
Instances of class Edge represent reacting 1D edges between
|
||||
between 2D surfaces. Class Edge defines no methods of its
|
||||
own. All of its methods derive from either :class:`.EdgePhase` or
|
||||
:class:`.Kinetics`.
|
||||
|
||||
Function :func:`.importInterface` should usually be used to build an
|
||||
Edge object from a CTI file definition, rather than calling
|
||||
the :class:`.Edge` constructor directly.
|
||||
"""
|
||||
def __init__(self, src="", root=None, surfaces=[]):
|
||||
"""
|
||||
:param src:
|
||||
CTML or CTI input file name. If more than one phase is
|
||||
defined in the file, src should be specified as ``filename#id``
|
||||
If the file is not CTML, it will be run through the CTI -> CTML
|
||||
preprocessor first.
|
||||
:param root:
|
||||
If a CTML tree has already been read in that contains
|
||||
the definition of this interface, the root of this tree can be
|
||||
specified instead of specifying *src*.
|
||||
:param phases:
|
||||
A list of all objects representing the neighboring
|
||||
surface phases which participate in the reaction mechanism.
|
||||
"""
|
||||
self.ckin = 0
|
||||
self._owner = 0
|
||||
self.verbose = 1
|
||||
|
||||
# src has the form '<filename>#<id>'
|
||||
fn = src.split('#')
|
||||
id = ""
|
||||
if len(fn) > 1:
|
||||
id = fn[1]
|
||||
fn = fn[0]
|
||||
|
||||
# read in the root element of the tree if not building from
|
||||
# an already-built XML tree. Enable preprocessing if the film
|
||||
# is a .cti file instead of XML.
|
||||
if src and not root:
|
||||
root = XML.XML_Node(name = 'doc', src = fn, preprocess = 1)
|
||||
|
||||
# If an 'id' tag was specified, find the node in the tree with
|
||||
# that tag
|
||||
if id:
|
||||
s = root.child(id = id)
|
||||
|
||||
# otherwise, find the first element with tag name 'phase'
|
||||
# (1D, 2D and 3D phases use the CTML tag name 'phase'
|
||||
else:
|
||||
s = root.child(name = "phase")
|
||||
|
||||
# build the surface phase
|
||||
EdgePhase.__init__(self, xml_phase=s)
|
||||
|
||||
# build the reaction mechanism. This object (representing the
|
||||
# surface phase) is added to the end of the list of phases
|
||||
Kinetics.__init__(self, xml_phase=s, phases=surfaces+[self])
|
||||
|
||||
|
||||
def __del__(self):
|
||||
"""Delete the Edge instance."""
|
||||
Kinetics.__del__(self)
|
||||
EdgePhase.__del__(self)
|
||||
|
|
@ -1,472 +0,0 @@
|
|||
"""
|
||||
The classes in this module are designed to allow constructing
|
||||
user-defined functions of one variable in Python that can be used with the
|
||||
Cantera C++ kernel. These classes are mostly shadow classes for
|
||||
corresponding classes in the C++ kernel.
|
||||
"""
|
||||
|
||||
from Cantera.num import array, asarray, ravel, shape, transpose
|
||||
import _cantera
|
||||
import types
|
||||
|
||||
|
||||
class Func1:
|
||||
"""
|
||||
Functors of one variable.
|
||||
|
||||
A Functor is an object that behaves like a function. :class:`Func1`
|
||||
is the base class from which several functor classes derive. These
|
||||
classes are designed to allow specifying functions of time from Python
|
||||
that can be used by the C++ kernel.
|
||||
|
||||
Functors can be added, multiplied, and divided to yield new functors.
|
||||
|
||||
>>> f1 = Polynomial([1.0, 0.0, 3.0]) # 3*t*t + 1
|
||||
>>> f1(2.0)
|
||||
13
|
||||
>>> f2 = Polynomial([-1.0, 2.0]) # 2*t - 1
|
||||
>>> f2(2.0)
|
||||
5
|
||||
>>> f3 = f1/f2 # (3*t*t + 1)/(2*t - 1)
|
||||
>>> f3(2.0)
|
||||
4.3333333
|
||||
"""
|
||||
|
||||
def __init__(self, typ, n, coeffs=[]):
|
||||
"""
|
||||
The constructor is meant to be called from constructors of subclasses
|
||||
of Func1: :class:`Polynomial`, :class:`Gaussian`, :class:`Arrhenius`,
|
||||
:class:`Fourier`, :class:`Const`, :class:`PeriodicFunction`.
|
||||
"""
|
||||
self.n = n
|
||||
self._own = 1
|
||||
self._func_id = 0
|
||||
self._typ = typ
|
||||
if _cantera.nummod == 'numpy':
|
||||
self.coeffs = array(coeffs, dtype=float, ndmin=1)
|
||||
else:
|
||||
self.coeffs = asarray(coeffs,'d')
|
||||
self._func_id = _cantera.func_new(typ, n, self.coeffs)
|
||||
|
||||
def __del__(self):
|
||||
if self._func_id and self._own:
|
||||
_cantera.func_del(self._func_id)
|
||||
|
||||
def __repr__(self):
|
||||
return self.write()
|
||||
|
||||
def __call__(self, t):
|
||||
"""Implements function syntax, so that F(t) is equivalent to
|
||||
F.value(t)."""
|
||||
if type(t) == types.NoneType:
|
||||
return self
|
||||
if type(t) == types.InstanceType:
|
||||
return CompositeFunction(self, t)
|
||||
else:
|
||||
return _cantera.func_value(self._func_id, t)
|
||||
|
||||
def __add__(self, other):
|
||||
"""Overloads operator '+'
|
||||
|
||||
Returns a new function self(t) + other(t)"""
|
||||
|
||||
# if 'other' is a number, then create a 'Const' functor for
|
||||
# it.
|
||||
if type(other) == types.FloatType:
|
||||
return SumFunction(self, Const(other))
|
||||
|
||||
return SumFunction(self, other)
|
||||
|
||||
def __radd__(self, other):
|
||||
"""Overloads operator '+'
|
||||
|
||||
Returns a new function other(t) + self(t)"""
|
||||
# if 'other' is a number, then create a 'Const' functor for
|
||||
# it.
|
||||
if type(other) == types.FloatType:
|
||||
return SumFunction(Const(other),self)
|
||||
return SumFunction(other, self)
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Overloads operator '-'
|
||||
|
||||
Returns a new function self(t) - other(t)"""
|
||||
|
||||
# if 'other' is a number, then create a 'Const' functor for
|
||||
# it.
|
||||
if type(other) != types.InstanceType:
|
||||
return DiffFunction(self, Const(other))
|
||||
|
||||
return DiffFunction(self, other)
|
||||
|
||||
def __rsub__(self, other):
|
||||
"""Overloads operator '-'
|
||||
|
||||
Returns a new function other(t) - self(t)"""
|
||||
|
||||
# if 'other' is a number, then create a 'Const' functor for
|
||||
# it.
|
||||
if type(other) != types.InstanceType:
|
||||
return DiffFunction(Const(other), self)
|
||||
return DiffFunction(other, self)
|
||||
|
||||
def __mul__(self, other):
|
||||
"""Overloads operator '*'
|
||||
|
||||
Return a new function self(t)*other(t)"""
|
||||
if type(other) != types.InstanceType:
|
||||
return ProdFunction(self, Const(other))
|
||||
return ProdFunction(self, other)
|
||||
|
||||
def __rmul__(self, other):
|
||||
"""Overloads operator '*'
|
||||
|
||||
Returns a new function other(t)*self(t)"""
|
||||
if type(other) != types.InstanceType:
|
||||
return ProdFunction(Const(other), self)
|
||||
return ProdFunction(other, self)
|
||||
|
||||
def __div__(self, other):
|
||||
"""Overloads operator '/'
|
||||
|
||||
Returns a new function self(t)/other(t)"""
|
||||
if type(other) != types.InstanceType:
|
||||
return RatioFunction(self, Const(other))
|
||||
return RatioFunction(self, other)
|
||||
|
||||
def __rdiv__(self, other):
|
||||
"""Overloads operator '/'
|
||||
|
||||
Returns a new function other(t)/self(t)"""
|
||||
if type(other) != types.InstanceType:
|
||||
return RatioFunction(Const(other), self)
|
||||
return RatioFunction(other, self)
|
||||
|
||||
def func_id(self):
|
||||
"""Internal. Return the integer index used internally to access the
|
||||
kernel-level object."""
|
||||
return self._func_id
|
||||
|
||||
def write(self, arg = 'x', length = 1000):
|
||||
return _cantera.func_write(self._func_id, length, arg)
|
||||
|
||||
|
||||
class Sin(Func1):
|
||||
def __init__(self,omega=1.0):
|
||||
Func1.__init__(self,100,1,omega)
|
||||
class Cos(Func1):
|
||||
def __init__(self, omega=1.0):
|
||||
Func1.__init__(self,102,1,omega)
|
||||
class Exp(Func1):
|
||||
def __init__(self,A=1.0):
|
||||
Func1.__init__(self,104,1,A)
|
||||
class Pow(Func1):
|
||||
def __init__(self, n):
|
||||
Func1.__init__(self,106,1,n)
|
||||
|
||||
class Polynomial(Func1):
|
||||
r"""
|
||||
A polynomial.
|
||||
Instances of class 'Polynomial' evaluate
|
||||
|
||||
.. math:: f(t) = \sum_{n = 0}^N a_n t^n .
|
||||
|
||||
The coefficients are supplied as a list, beginning with :math:`a_N` and
|
||||
ending with :math:`a_0`.
|
||||
|
||||
>>> p1 = Polynomial([1.0, -2.0, 3.0]) # 3t^2 - 2t + 1
|
||||
>>> p2 = Polynomial([6.0, 8.0]) # 8t + 6
|
||||
|
||||
"""
|
||||
def __init__(self, coeffs=[]):
|
||||
"""
|
||||
coeffs - polynomial coefficients
|
||||
"""
|
||||
Func1.__init__(self, 2, len(coeffs)-1, coeffs)
|
||||
|
||||
|
||||
|
||||
class Gaussian(Func1):
|
||||
r"""A Gaussian pulse. Instances of class 'Gaussian' evaluate
|
||||
|
||||
.. math:: f(t) = A \exp[-(t - t_0) / \tau]
|
||||
|
||||
where
|
||||
|
||||
.. math:: \tau = \frac{\mbox{FWHM}}{2.0\sqrt{\ln(2.0)}}
|
||||
|
||||
'FWHM' denotes the full width at half maximum.
|
||||
|
||||
As an example, here is how to create a Gaussian pulse with peak amplitude
|
||||
10.0, centered at time 2.0, with full-width at half max = 0.2:
|
||||
|
||||
>>> f = Gaussian(A = 10.0, t0 = 2.0, FWHM = 0.2)
|
||||
>>> f(2.0)
|
||||
10
|
||||
>>> f(1.9)
|
||||
5
|
||||
>>> f(2.1)
|
||||
5
|
||||
"""
|
||||
def __init__(self, A, t0, FWHM):
|
||||
coeffs = array([A, t0, FWHM], 'd')
|
||||
Func1.__init__(self, 4, 0, coeffs)
|
||||
|
||||
|
||||
class Fourier(Func1):
|
||||
r"""
|
||||
Fourier series. Instances of class 'Fourier' evaluate the Fourier series
|
||||
|
||||
.. math::
|
||||
|
||||
f(t) = \frac{a_0}{2} +
|
||||
\sum_{n=1}^N [a_n \cos(n\omega t) + b_n \sin(n \omega t)]
|
||||
|
||||
where
|
||||
|
||||
.. math::
|
||||
|
||||
a_n = \frac{\omega}{\pi}
|
||||
\int_{-\pi/\omega}^{\pi/\omega} f(t) \cos(n \omega t) dt
|
||||
|
||||
b_n = \frac{\omega}{\pi}
|
||||
\int_{-\pi/\omega}^{\pi/\omega} f(t) \sin(n \omega t) dt.
|
||||
|
||||
The function :math:`f(t)` is periodic, with period :math:`T = 2\pi/\omega`.
|
||||
|
||||
As an example, a function with Fourier components up to the second harmonic
|
||||
is constructed as follows:
|
||||
|
||||
>>> coeffs = [(a0, b0), (a1, b1), (a2, b2)]
|
||||
>>> f = Fourier(omega, coeffs)
|
||||
|
||||
Note that ``b0`` must be specified, but is not used. The value of ``b0``
|
||||
is arbitrary.
|
||||
"""
|
||||
def __init__(self, omega, coefficients):
|
||||
"""
|
||||
:param omega:
|
||||
fundamental frequency [radians/sec].
|
||||
:param coefficients:
|
||||
List of (a,b) pairs, beginning with n = 0.
|
||||
"""
|
||||
cc = asarray(coefficients,'d')
|
||||
n, m = cc.shape
|
||||
if m <> 2:
|
||||
raise CanteraError('provide (a, b) for each term')
|
||||
cc[0,1] = omega
|
||||
Func1.__init__(self, 1, n-1, ravel(transpose(cc)))
|
||||
|
||||
|
||||
class Arrhenius(Func1):
|
||||
r"""Sum of modified Arrhenius terms. Instances of class 'Arrhenius' evaluate
|
||||
|
||||
.. math:: f(T) = \sum_{n=1}^N A_n T^{b_n}\exp(-E_n/T)
|
||||
|
||||
Example:
|
||||
|
||||
>>> f = Arrhenius([(a0, b0, e0), (a1, b1, e1)])
|
||||
"""
|
||||
def __init__(self, coefficients):
|
||||
"""
|
||||
:param coefficients:
|
||||
sequence of (*A*, *b*, *E*) triplets.
|
||||
"""
|
||||
cc = asarray(coefficients,'d')
|
||||
n, m = cc.shape
|
||||
if m <> 3:
|
||||
raise CanteraError('Three Arrhenius parameters (A, b, E) required.')
|
||||
Func1.__init__(self, 3, n, ravel(cc))
|
||||
|
||||
|
||||
class Const(Func1):
|
||||
"""Constant function.
|
||||
Objects created by function Const act as functions that have a constant
|
||||
value. These are used internally whenever a statement like
|
||||
|
||||
>>> f = Gausian(2.0, 1.0, 0.1) + 4.0
|
||||
|
||||
is encountered. The addition operator of class Func1 is defined so that
|
||||
this is equivalent to
|
||||
|
||||
>>> f = SumFunction(Gaussian(2.0, 1.0, 0.1), Const(4.0))
|
||||
|
||||
Function Const returns instances of class Polynomial that have
|
||||
degree zero, with the constant term set to the desired value.
|
||||
"""
|
||||
def __init__(self, value):
|
||||
Func1.__init__(self,110,1,value)
|
||||
#return Polynomial([value])
|
||||
|
||||
|
||||
class PeriodicFunction(Func1):
|
||||
"""Converts a function into a periodic function with period T."""
|
||||
def __init__(self, func, T):
|
||||
"""
|
||||
:param func:
|
||||
initial non-periodic function
|
||||
:param T:
|
||||
period [s]
|
||||
"""
|
||||
Func1.__init__(self, 50, func.func_id(), array([T],'d'))
|
||||
func._own = 0
|
||||
|
||||
|
||||
# functions that combine two functions
|
||||
|
||||
class ComboFunc1(Func1):
|
||||
"""
|
||||
Combines two functions.
|
||||
This class is the base class for functors that combine two
|
||||
other functors in a binary operation.
|
||||
"""
|
||||
def __init__(self, typ, f1, f2):
|
||||
self._own = 1
|
||||
self._func_id = 0
|
||||
self._typ = typ
|
||||
if type(f1) == types.IntType:
|
||||
f1 = Const(f1)
|
||||
if type(f2) == types.IntType:
|
||||
f2 = Const(f2)
|
||||
self.f1 = f1
|
||||
self.f2 = f2
|
||||
self.f1._own = 0
|
||||
self.f2._own = 0
|
||||
self._func_id = _cantera.func_newcombo(typ, f1.func_id(), f2.func_id())
|
||||
|
||||
|
||||
class SumFunction(ComboFunc1):
|
||||
"""Sum of two functions.
|
||||
Instances of class SumFunction evaluate the sum of two supplied functors.
|
||||
It is not necessary to explicitly create an instance of SumFunction, since
|
||||
the addition operator of the base class is overloaded to return a SumFunction
|
||||
instance.
|
||||
|
||||
>>> f1 = Polynomial([2.0, 1.0])
|
||||
>>> f2 = Polynomial([3.0, -5.0])
|
||||
>>> f3 = f1 + f2 # functor to evaluate (2t + 1) + (3t - 5)
|
||||
|
||||
In this example, object 'f3' is a functor of class'SumFunction' that calls
|
||||
f1 and f2 and returns their sum.
|
||||
"""
|
||||
|
||||
def __init__(self, f1, f2):
|
||||
"""
|
||||
:param f1:
|
||||
first functor.
|
||||
:param f2:
|
||||
second functor.
|
||||
"""
|
||||
ComboFunc1.__init__(self, 20, f1, f2)
|
||||
|
||||
|
||||
class DiffFunction(ComboFunc1):
|
||||
"""Difference of two functions.
|
||||
Instances of class DiffFunction evaluate the difference of two supplied
|
||||
functors. It is not necessary to explicitly create an instance of
|
||||
DiffFunction, since the subtraction operator of the base class is
|
||||
overloaded to return a DiffFunction instance.
|
||||
|
||||
>>> f1 = Polynomial([2.0, 1.0])
|
||||
>>> f2 = Polynomial([3.0, -5.0])
|
||||
>>> f3 = f1 - f2 # functor to evaluate (2t + 1) - (3t - 5)
|
||||
|
||||
In this example, object 'f3' is a functor of class'DiffFunction' that
|
||||
calls f1 and f2 and returns their difference.
|
||||
"""
|
||||
|
||||
def __init__(self, f1, f2):
|
||||
"""
|
||||
:param f1:
|
||||
first functor.
|
||||
:param f2:
|
||||
second functor.
|
||||
"""
|
||||
ComboFunc1.__init__(self, 25, f1, f2)
|
||||
|
||||
class ProdFunction(ComboFunc1):
|
||||
"""Product of two functions. Instances of class ProdFunction
|
||||
evaluate the product of two supplied functors. It is not
|
||||
necessary to explicitly create an instance of 'ProdFunction',
|
||||
since the multiplication operator of the base class is overloaded
|
||||
to return a 'ProdFunction' instance.
|
||||
|
||||
>>> f1 = Polynomial([2.0, 1.0])
|
||||
>>> f2 = Polynomial([3.0, -5.0])
|
||||
>>> f3 = f1 * f2 # functor to evaluate (2t + 1)*(3t - 5)
|
||||
|
||||
In this example, object 'f3' is a functor of class'ProdFunction'
|
||||
that calls f1 and f2 and returns their product.
|
||||
"""
|
||||
def __init__(self, f1, f2):
|
||||
"""
|
||||
:param f1:
|
||||
first functor.
|
||||
:param f2:
|
||||
second functor.
|
||||
"""
|
||||
ComboFunc1.__init__(self, 30, f1, f2)
|
||||
|
||||
|
||||
class RatioFunction(ComboFunc1):
|
||||
"""Ratio of two functions.
|
||||
Instances of class RatioFunction evaluate the ratio of two supplied functors.
|
||||
It is not necessary to explicitly create an instance of 'RatioFunction', since
|
||||
the division operator of the base class is overloaded to return a RatioFunction
|
||||
instance.
|
||||
|
||||
>>> f1 = Polynomial([2.0, 1.0])
|
||||
>>> f2 = Polynomial([3.0, -5.0])
|
||||
>>> f3 = f1 / f2 # functor to evaluate (2t + 1)/(3t - 5)
|
||||
|
||||
In this example, object 'f3' is a functor of class'RatioFunction' that
|
||||
calls f1 and f2 and returns their ratio.
|
||||
"""
|
||||
def __init__(self, f1, f2):
|
||||
"""
|
||||
:param f1:
|
||||
first functor.
|
||||
:param f2:
|
||||
second functor.
|
||||
"""
|
||||
ComboFunc1.__init__(self, 40, f1, f2)
|
||||
|
||||
class CompositeFunction(ComboFunc1):
|
||||
"""
|
||||
Function of a function.
|
||||
Instances of class CompositeFunction evaluate f(g(t)) for two supplied
|
||||
functors f and g. It is not necessary to explicitly create an instance
|
||||
of 'CompositeFunction', since the () operator of the base class is
|
||||
overloaded to return a CompositeFunction when called with a functor
|
||||
argument.
|
||||
|
||||
>>> f1 = Polynomial([2.0, 1.0])
|
||||
>>> f2 = Polynomial([3.0, -5.0])
|
||||
>>> f3 = f1(f2) # functor to evaluate 2(3t - 5) + 1
|
||||
|
||||
In this example, object 'f3' is a functor of class'CompositeFunction'
|
||||
that calls f1 and f2 and returns f1(f2(t)).
|
||||
"""
|
||||
def __init__(self, f1, f2):
|
||||
"""
|
||||
:param f1:
|
||||
first functor.
|
||||
:param f2:
|
||||
second functor.
|
||||
"""
|
||||
ComboFunc1.__init__(self, 60, f1, f2)
|
||||
|
||||
|
||||
class DerivativeFunction(Func1):
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
#f._own = 0
|
||||
self._own = 1
|
||||
self._func_id = _cantera.func_derivative(f.func_id())
|
||||
|
||||
|
||||
def derivative(f):
|
||||
"""
|
||||
Take the derivative of a functor *f*
|
||||
"""
|
||||
return DerivativeFunction(f)
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import string
|
||||
import os
|
||||
|
||||
from constants import *
|
||||
from SurfacePhase import SurfacePhase, EdgePhase
|
||||
from Kinetics import Kinetics
|
||||
import XML
|
||||
|
||||
class Interface(SurfacePhase, Kinetics):
|
||||
"""
|
||||
Two-dimensional interfaces.
|
||||
|
||||
Instances of class Interface represent reacting 2D interfaces
|
||||
between bulk 3D phases. Class Interface defines no methods of its
|
||||
own. All of its methods derive from either :class:`.SurfacePhase` or
|
||||
:class:`.Kinetics`.
|
||||
|
||||
Function :func:`.importInterface` should usually be used to build an
|
||||
Interface object from a CTI file definition, rather than calling
|
||||
the Interface constructor directly.
|
||||
"""
|
||||
def __init__(self, src="", root=None, phases=[], debug = 0):
|
||||
"""
|
||||
:param src:
|
||||
CTML or CTI input file name. If more than one phase is
|
||||
defined in the file, src should be specified as ``filename#id``
|
||||
If the file is not CTML, it will be run through the CTI -> CTML
|
||||
preprocessor first.
|
||||
:param root:
|
||||
If a CTML tree has already been read in that contains the
|
||||
definition of this interface, the root of this tree can be
|
||||
specified instead of specifying *src*.
|
||||
:param phases:
|
||||
A list of all objects representing the neighboring phases which
|
||||
participate in the reaction mechanism.
|
||||
"""
|
||||
self.ckin = 0
|
||||
self._owner = 0
|
||||
self.verbose = 1
|
||||
|
||||
# src has the form '<filename>#<id>'
|
||||
fn = src.split('#')
|
||||
id = ""
|
||||
if len(fn) > 1:
|
||||
id = fn[1]
|
||||
fn = fn[0]
|
||||
|
||||
# read in the root element of the tree if not building from
|
||||
# an already-built XML tree. Enable preprocessing if the film
|
||||
# is a .cti file instead of XML.
|
||||
if src and not root:
|
||||
root = XML.XML_Node(name = 'doc', src = fn, preprocess = 1, debug = debug)
|
||||
|
||||
# If an 'id' tag was specified, find the node in the tree with
|
||||
# that tag
|
||||
if id:
|
||||
s = root.child(id = id)
|
||||
|
||||
# otherwise, find the first element with tag name 'phase'
|
||||
# (both 2D and 3D phases use the CTML tag name 'phase'
|
||||
else:
|
||||
s = root.child(name = "phase")
|
||||
|
||||
# build the surface phase
|
||||
SurfacePhase.__init__(self, xml_phase=s)
|
||||
|
||||
# build the reaction mechanism. This object (representing the
|
||||
# surface phase) is added to the end of the list of phases
|
||||
Kinetics.__init__(self, xml_phase=s, phases=phases+[self])
|
||||
|
||||
|
||||
def __del__(self):
|
||||
"""Delete the Interface instance."""
|
||||
Kinetics.__del__(self)
|
||||
SurfacePhase.__del__(self)
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
"""
|
||||
Kinetics managers.
|
||||
"""
|
||||
|
||||
from Cantera.exceptions import CanteraError, getCanteraError
|
||||
from Cantera.ThermoPhase import ThermoPhase
|
||||
from Cantera.XML import XML_Node
|
||||
from Cantera.num import zeros
|
||||
import _cantera
|
||||
|
||||
class Kinetics:
|
||||
"""
|
||||
Kinetics managers. Instances of class Kinetics are responsible for
|
||||
evaluating reaction rates of progress, species production rates,
|
||||
and other quantities pertaining to a reaction mechanism.
|
||||
"""
|
||||
|
||||
def __init__(self, kintype=-1, thrm=0, xml_phase=None, id=None, phases=[]):
|
||||
"""
|
||||
Build a kinetics manager from an XML specification.
|
||||
|
||||
:param kintype:
|
||||
Integer specifying the type of kinetics manager to create.
|
||||
:param root:
|
||||
Root of a CTML tree
|
||||
:param id:
|
||||
id of the 'kinetics' node within the tree that contains the
|
||||
specification of the parameters.
|
||||
"""
|
||||
np = len(phases)
|
||||
self._sp = []
|
||||
self._phnum = {}
|
||||
|
||||
# p0 through p4 are the integer indices of the phase objects
|
||||
# corresponding to the input sequence of phases
|
||||
self._end = [0]
|
||||
p0 = phases[0].thermophase()
|
||||
p1 = -1
|
||||
p2 = -1
|
||||
p3 = -1
|
||||
p4 = -1
|
||||
if np >= 2:
|
||||
p1 = phases[1].thermophase()
|
||||
if np >= 3:
|
||||
p2 = phases[2].thermophase()
|
||||
if np >= 4:
|
||||
p3 = phases[3].thermophase()
|
||||
if np >= 5:
|
||||
p4 = phases[4].thermophase()
|
||||
if np >= 6:
|
||||
raise CanteraError("a maximum of 4 neighbor phases allowed")
|
||||
|
||||
self.ckin = _cantera.KineticsFromXML(xml_phase,
|
||||
p0, p1, p2, p3, p4)
|
||||
|
||||
self._np = self.nPhases()
|
||||
for nn in range(self._np):
|
||||
p = self.phase(nn)
|
||||
self._phnum[p.thermophase()] = nn
|
||||
self._end.append(self._end[-1]+p.nSpecies())
|
||||
for k in range(p.nSpecies()):
|
||||
self._sp.append(p.speciesName(k))
|
||||
|
||||
def __del__(self):
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
"""Delete the kinetics manager."""
|
||||
if self.ckin > 0:
|
||||
_cantera.kin_delete(self.ckin)
|
||||
|
||||
def kin_index(self):
|
||||
print "kin_index is deprecated. Use kinetics_hndl."
|
||||
return self.ckin
|
||||
|
||||
def kinetics_hndl(self):
|
||||
return self.ckin
|
||||
|
||||
def kineticsType(self):
|
||||
"""Kinetics manager type."""
|
||||
return _cantera.kin_type(self.ckin)
|
||||
|
||||
def kineticsSpeciesIndex(self, name, phase):
|
||||
"""The index of a species.
|
||||
|
||||
:param name:
|
||||
species name
|
||||
:param phase:
|
||||
phase name
|
||||
|
||||
Kinetics managers for heterogeneous reaction mechanisms
|
||||
maintain a list of all species in all phases. The order of the
|
||||
species in this list determines the ordering of the arrays of
|
||||
production rates. This method returns the index for the
|
||||
specified species of the specified phase, and is used to
|
||||
locate the entry for a particular species in the production
|
||||
rate arrays.
|
||||
|
||||
"""
|
||||
return _cantera.kin_speciesIndex(self.ckin, name, phase)
|
||||
|
||||
def kineticsStart(self, n):
|
||||
"""The starting location of phase n in production rate arrays."""
|
||||
return _cantera.kin_start(self.ckin, n)
|
||||
|
||||
def nPhases(self):
|
||||
"""Number of phases."""
|
||||
return _cantera.kin_nPhases(self.ckin)
|
||||
|
||||
def reactionPhaseIndex(self):
|
||||
"""The phase in which the reactions take place."""
|
||||
return _cantera.kin_reactionPhaseIndex(self)
|
||||
|
||||
def phase(self, n):
|
||||
"""Return an object representing the nth phase."""
|
||||
return ThermoPhase(index = _cantera.kin_phase(self.ckin, n))
|
||||
|
||||
def nReactions(self):
|
||||
"""Number of reactions."""
|
||||
return _cantera.kin_nreactions(self.ckin)
|
||||
|
||||
def isReversible(self,i):
|
||||
"""
|
||||
True (1) if reaction number *i* is reversible,
|
||||
and false (0) otherwise.
|
||||
"""
|
||||
return _cantera.kin_isreversible(self.ckin,i)
|
||||
|
||||
def reactionType(self,i):
|
||||
"""Type of reaction *i*"""
|
||||
return _cantera.kin_rxntype(self.ckin,i)
|
||||
|
||||
def reactionEqn(self,i):
|
||||
"""The equation for the specified reaction. If a list of equation numbers
|
||||
is given, then a list of equation strings is returned."""
|
||||
try:
|
||||
eqs = []
|
||||
for rxn in i:
|
||||
eqs.append(self.reactionString(rxn))
|
||||
return eqs
|
||||
except:
|
||||
return self.reactionString(i)
|
||||
|
||||
def reactionString(self, i):
|
||||
"""Reaction string for reaction number *i*"""
|
||||
s = ''
|
||||
nsp = _cantera.kin_nspecies(self.ckin)
|
||||
for k in range(nsp):
|
||||
nur = _cantera.kin_rstoichcoeff(self.ckin,k,i)
|
||||
if nur <> 0.0:
|
||||
if nur <> 1.0:
|
||||
if nur <> round(nur):
|
||||
s += str(nur)+' '
|
||||
else:
|
||||
s += `int(nur)`+' '
|
||||
s += self._sp[k]+' + '
|
||||
s = s[:-2]
|
||||
if self.isReversible(i):
|
||||
s += ' <=> '
|
||||
else:
|
||||
s += ' => '
|
||||
for k in range(nsp):
|
||||
nup = _cantera.kin_pstoichcoeff(self.ckin,k,i)
|
||||
if nup <> 0.0:
|
||||
if nup <> 1.0:
|
||||
if nup <> round(nup):
|
||||
s += str(nup)+' '
|
||||
else:
|
||||
s += `int(nup)`+' '
|
||||
s += self._sp[k]+' + '
|
||||
s = s[:-2]
|
||||
return s
|
||||
|
||||
def reactantStoichCoeff(self,k,i):
|
||||
"""The stoichiometric coefficient of species *k* as a reactant in reaction *i*."""
|
||||
return _cantera.kin_rstoichcoeff(self.ckin,k,i)
|
||||
|
||||
def reactantStoichCoeffs(self):
|
||||
"""The array of reactant stoichiometric coefficients. Element
|
||||
[k,i] of this array is the reactant stoichiometric
|
||||
coefficient of species k in reaction i."""
|
||||
nsp = _cantera.kin_nspecies(self.ckin)
|
||||
nr = _cantera.kin_nreactions(self.ckin)
|
||||
nu = zeros((nsp,nr),'d')
|
||||
for i in range(nr):
|
||||
for k in range(nsp):
|
||||
nu[k,i] = _cantera.kin_rstoichcoeff(self.ckin,k,i)
|
||||
return nu
|
||||
|
||||
def productStoichCoeff(self,k,i):
|
||||
"""The stoichiometric coefficient of species *k* as a product in reaction *i*."""
|
||||
return _cantera.kin_pstoichcoeff(self.ckin,k,i)
|
||||
|
||||
def productStoichCoeffs(self):
|
||||
"""The array of product stoichiometric coefficients. Element
|
||||
[k,i] of this array is the product stoichiometric
|
||||
coefficient of species *k* in reaction *i*."""
|
||||
nsp = _cantera.kin_nspecies(self.ckin)
|
||||
nr = _cantera.kin_nreactions(self.ckin)
|
||||
nu = zeros((nsp,nr),'d')
|
||||
for i in range(nr):
|
||||
for k in range(nsp):
|
||||
nu[k,i] = _cantera.kin_pstoichcoeff(self.ckin,k,i)
|
||||
return nu
|
||||
|
||||
def fwdRatesOfProgress(self):
|
||||
"""Forward rates of progress of the reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,10)
|
||||
|
||||
def revRatesOfProgress(self):
|
||||
"""Reverse rates of progress of the reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,20)
|
||||
|
||||
def netRatesOfProgress(self):
|
||||
"""Net rates of progress of the reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,30)
|
||||
|
||||
def equilibriumConstants(self):
|
||||
"""Equilibrium constants in concentration units for all reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,40)
|
||||
|
||||
def activationEnergies(self):
|
||||
"""Activation energies in Kelvin for all reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,32)
|
||||
|
||||
def fwdRateConstants(self):
|
||||
"""Forward rate constants for all reactions."""
|
||||
return _cantera.kin_getarray(self.ckin,34)
|
||||
|
||||
def revRateConstants(self, doIrreversible = 0):
|
||||
"""Reverse rate constants for all reactions."""
|
||||
if doIrreversible:
|
||||
return _cantera.kin_getarray(self.ckin,35)
|
||||
else:
|
||||
return _cantera.kin_getarray(self.ckin,36)
|
||||
|
||||
def creationRates(self, phase = None):
|
||||
c = _cantera.kin_getarray(self.ckin,50)
|
||||
if phase:
|
||||
kp = phase.thermophase()
|
||||
if self._phnum.has_key(kp):
|
||||
n = self._phnum[kp]
|
||||
return c[self._end[n]:self._end[n+1]]
|
||||
else:
|
||||
raise CanteraError('unknown phase')
|
||||
else:
|
||||
return c
|
||||
|
||||
|
||||
def destructionRates(self, phase = None):
|
||||
d = _cantera.kin_getarray(self.ckin,60)
|
||||
if phase:
|
||||
kp = phase.thermophase()
|
||||
if self._phnum.has_key(kp):
|
||||
n = self._phnum[kp]
|
||||
return d[self._end[n]:self._end[n+1]]
|
||||
else:
|
||||
raise CanteraError('unknown phase')
|
||||
else:
|
||||
return d
|
||||
|
||||
|
||||
def netProductionRates(self, phase = None):
|
||||
w = _cantera.kin_getarray(self.ckin,70)
|
||||
if phase:
|
||||
kp = phase.thermophase()
|
||||
if self._phnum.has_key(kp):
|
||||
n = self._phnum[kp]
|
||||
return w[self._end[n]:self._end[n+1]]
|
||||
else:
|
||||
raise CanteraError('unknown phase')
|
||||
else:
|
||||
return w
|
||||
|
||||
def sourceTerms(self):
|
||||
return _cantera.kin_getarray(self.ckin,80)
|
||||
|
||||
def delta_H(self):
|
||||
return _cantera.kin_getarray(self.ckin,90)
|
||||
|
||||
def delta_G(self):
|
||||
return _cantera.kin_getarray(self.ckin,91)
|
||||
|
||||
def delta_S(self):
|
||||
return _cantera.kin_getarray(self.ckin,92)
|
||||
|
||||
def delta_H0(self):
|
||||
return _cantera.kin_getarray(self.ckin,93)
|
||||
|
||||
def delta_G0(self):
|
||||
return _cantera.kin_getarray(self.ckin,94)
|
||||
|
||||
def delta_S0(self):
|
||||
return _cantera.kin_getarray(self.ckin,95)
|
||||
|
||||
def multiplier(self,i):
|
||||
return _cantera.kin_multiplier(self.ckin,i)
|
||||
|
||||
def setMultiplier(self, value = 0.0, reaction = -1):
|
||||
if reaction < 0:
|
||||
nr = self.nReactions()
|
||||
for i in range(nr):
|
||||
_cantera.kin_setMultiplier(self.ckin,i,value)
|
||||
else:
|
||||
_cantera.kin_setMultiplier(self.ckin,reaction,value)
|
||||
|
||||
def advanceCoverages(self,dt):
|
||||
return _cantera.kin_advanceCoverages(self.ckin,dt)
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
from onedim import *
|
||||
from Cantera.num import array, zeros
|
||||
|
||||
class BurnerDiffFlame(Stack):
|
||||
"""A burner-stabilized flat flame."""
|
||||
|
||||
def __init__(self, gas = None, burner = None, outlet = None, grid = None):
|
||||
"""
|
||||
:param gas:
|
||||
object to use to evaluate all gas properties and reaction
|
||||
rates. Required
|
||||
:param burner:
|
||||
Inlet object representing the burner. Optional; if not supplied,
|
||||
one will be created with name 'burner'
|
||||
:param outlet:
|
||||
Outlet object representing the outlet. Optional; if not supplied,
|
||||
one will be created with name 'outlet'
|
||||
:param grid:
|
||||
array of initial grid points
|
||||
|
||||
A domain of type :class:`.AxisymmetricFlow` named 'flame' will be
|
||||
created to represent the flame. The three domains comprising the stack
|
||||
are stored as ``self.burner``, ``self.flame``, and ``self.outlet``.
|
||||
"""
|
||||
|
||||
if burner:
|
||||
self.burner = burner
|
||||
else:
|
||||
self.burner = Inlet('burner')
|
||||
self.gas = gas
|
||||
self.burner.set(temperature = gas.temperature())
|
||||
if outlet:
|
||||
self.outlet = outlet
|
||||
else:
|
||||
self.outlet = OutletRes('outletres')
|
||||
|
||||
self.flame = AxisymmetricFlow('flame',gas = gas)
|
||||
self.flame.setupGrid(grid)
|
||||
Stack.__init__(self, [self.burner, self.flame, self.outlet])
|
||||
self.setRefineCriteria()
|
||||
|
||||
|
||||
def init(self):
|
||||
"""Set the initial guess for the solution. The adiabatic flame
|
||||
temperature and equilibrium composition are computed for the
|
||||
burner gas composition. The temperature profile rises linearly
|
||||
in the first 20% of the flame to Tad, then is flat. The mass
|
||||
fraction profiles are set similarly.
|
||||
"""
|
||||
self.getInitialSoln()
|
||||
gas = self.gas
|
||||
nsp = gas.nSpecies()
|
||||
yin = zeros(nsp, 'd')
|
||||
for k in range(nsp):
|
||||
yin[k] = self.burner.massFraction(k)
|
||||
gas.setState_TPY(self.burner.temperature(), self.flame.pressure(), yin)
|
||||
u0 = self.burner.mdot()/gas.density()
|
||||
t0 = self.burner.temperature()
|
||||
|
||||
# get adiabatic flame temperature and composition
|
||||
gas.equilibrate('HP')
|
||||
teq = gas.temperature()
|
||||
yeq = gas.massFractions()
|
||||
u1 = self.burner.mdot()/gas.density()
|
||||
|
||||
z1 = 0.2
|
||||
locs = array([0.0, z1, 1.0],'d')
|
||||
self.setProfile('u', locs, [u0, u1, u1])
|
||||
self.setProfile('T', locs, [t0, teq, teq])
|
||||
for n in range(nsp):
|
||||
self.setProfile(gas.speciesName(n), locs, [yin[n], yeq[n], yeq[n]])
|
||||
self._initialized = 1
|
||||
|
||||
|
||||
def solve(self, loglevel = 1, refine_grid = 1):
|
||||
if not self._initialized: self.init()
|
||||
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
|
||||
|
||||
|
||||
def setRefineCriteria(self, ratio = 10.0, slope = 0.8,
|
||||
curve = 0.8, prune = 0.0):
|
||||
Stack.setRefineCriteria(self, domain = self.flame,
|
||||
ratio = ratio, slope = slope, curve = curve,
|
||||
prune = prune)
|
||||
|
||||
def setGridMin(self, gridmin):
|
||||
Stack.setGridMin(self, self.flame, gridmin)
|
||||
|
||||
def setProfile(self, component, locs, vals):
|
||||
self._initialized = 1
|
||||
Stack.setProfile(self, self.flame, component, locs, vals)
|
||||
|
||||
def set(self, tol = None, energy = '', tol_time = None):
|
||||
"""Set parameters.
|
||||
|
||||
:param tol:
|
||||
(rtol, atol) for steady-state
|
||||
:param tol_time:
|
||||
(rtol, atol) for time stepping
|
||||
:param energy:
|
||||
``'on'`` or ``'off'`` to enable or disable the energy equation
|
||||
"""
|
||||
if tol:
|
||||
self.flame.setTolerances(default = tol)
|
||||
if tol_time:
|
||||
self.flame.setTolerances(default = tol_time, time = 1)
|
||||
if energy:
|
||||
self.flame.set(energy = energy)
|
||||
|
||||
def T(self, point = -1):
|
||||
"""Temperature profile or value at one point."""
|
||||
return self.solution('T', point)
|
||||
|
||||
def u(self, point = -1):
|
||||
"""Axial velocity profile or value at one point."""
|
||||
return self.solution('u', point)
|
||||
|
||||
def V(self, point = -1):
|
||||
"""Radial velocity profile or value at one point."""
|
||||
return self.solution('V', point)
|
||||
|
||||
def solution(self, component = '', point = -1):
|
||||
"""Solution component at one point, or full profile if no
|
||||
point specified."""
|
||||
if point >= 0: return self.value(self.flame, component, point)
|
||||
else: return self.profile(self.flame, component)
|
||||
|
||||
def setGasState(self, j):
|
||||
"""Set the state of the object representing the gas to the
|
||||
current solution at grid point *j*."""
|
||||
nsp = self.gas.nSpecies()
|
||||
y = zeros(nsp, 'd')
|
||||
for n in range(nsp):
|
||||
nm = self.gas.speciesName(n)
|
||||
y[n] = self.solution(nm, j)
|
||||
self.gas.setState_TPY(self.T(j), self.flame.pressure(), y)
|
||||
|
||||
fix_docs(BurnerDiffFlame)
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
from onedim import *
|
||||
from Cantera.num import array, zeros
|
||||
|
||||
class BurnerFlame(Stack):
|
||||
"""A burner-stabilized flat flame."""
|
||||
|
||||
def __init__(self, gas = None, burner = None, outlet = None, grid = None):
|
||||
"""
|
||||
:param gas:
|
||||
object to use to evaluate all gas properties and reaction
|
||||
rates. Required
|
||||
:param burner:
|
||||
Inlet object representing the burner. Optional;
|
||||
if not supplied, one will be created with name ``burner``
|
||||
:param outlet:
|
||||
Outlet object representing the outlet. Optional;
|
||||
if not supplied, one will be created with name ``outlet``
|
||||
:param grid:
|
||||
array of initial grid points
|
||||
|
||||
A domain of type :class:`.AxisymmetricFlow` named ``flame`` will be
|
||||
created to represent the flame. The three domains comprising the stack
|
||||
are stored as ``self.burner``, ``self.flame``, and ``self.outlet``.
|
||||
"""
|
||||
|
||||
if burner:
|
||||
self.burner = burner
|
||||
else:
|
||||
self.burner = Inlet('burner')
|
||||
self.gas = gas
|
||||
self.burner.set(temperature = gas.temperature())
|
||||
if outlet:
|
||||
self.outlet = outlet
|
||||
else:
|
||||
self.outlet = Outlet('outlet')
|
||||
self.flame = AxisymmetricFlow('flame',gas = gas)
|
||||
self.flame.setupGrid(grid)
|
||||
Stack.__init__(self, [self.burner, self.flame, self.outlet])
|
||||
self.setRefineCriteria()
|
||||
|
||||
|
||||
def init(self):
|
||||
"""Set the initial guess for the solution. The adiabatic flame
|
||||
temperature and equilibrium composition are computed for the
|
||||
burner gas composition. The temperature profile rises linearly
|
||||
in the first 20% of the flame to Tad, then is flat. The mass
|
||||
fraction profiles are set similarly.
|
||||
"""
|
||||
self.getInitialSoln()
|
||||
gas = self.gas
|
||||
nsp = gas.nSpecies()
|
||||
yin = zeros(nsp, 'd')
|
||||
for k in range(nsp):
|
||||
yin[k] = self.burner.massFraction(k)
|
||||
gas.setState_TPY(self.burner.temperature(), self.flame.pressure(), yin)
|
||||
u0 = self.burner.mdot()/gas.density()
|
||||
t0 = self.burner.temperature()
|
||||
|
||||
# get adiabatic flame temperature and composition
|
||||
gas.equilibrate('HP',solver=1)
|
||||
teq = gas.temperature()
|
||||
yeq = gas.massFractions()
|
||||
u1 = self.burner.mdot()/gas.density()
|
||||
|
||||
z1 = 0.2
|
||||
locs = array([0.0, z1, 1.0],'d')
|
||||
self.setProfile('u', locs, [u0, u1, u1])
|
||||
self.setProfile('T', locs, [t0, teq, teq])
|
||||
for n in range(nsp):
|
||||
self.setProfile(gas.speciesName(n), locs, [yin[n], yeq[n], yeq[n]])
|
||||
self._initialized = 1
|
||||
|
||||
|
||||
def solve(self, loglevel = 1, refine_grid = 1):
|
||||
if not self._initialized: self.init()
|
||||
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
|
||||
|
||||
|
||||
def setRefineCriteria(self, ratio = 10.0, slope = 0.8,
|
||||
curve = 0.8, prune = 0.0):
|
||||
Stack.setRefineCriteria(self, domain = self.flame,
|
||||
ratio = ratio, slope = slope, curve = curve,
|
||||
prune = prune)
|
||||
|
||||
def setGridMin(self, gridmin):
|
||||
Stack.setGridMin(self, self.flame, gridmin)
|
||||
|
||||
def setProfile(self, component, locs, vals):
|
||||
self._initialized = 1
|
||||
Stack.setProfile(self, self.flame, component, locs, vals)
|
||||
|
||||
def set(self, tol = None, energy = '', tol_time = None):
|
||||
"""Set parameters.
|
||||
|
||||
:param tol:
|
||||
(rtol, atol) for steady-state
|
||||
:param tol_time:
|
||||
(rtol, atol) for time stepping
|
||||
:param energy:
|
||||
``'on'`` or ``'off'`` to enable or disable the energy equation
|
||||
"""
|
||||
if tol:
|
||||
self.flame.setTolerances(default = tol)
|
||||
if tol_time:
|
||||
self.flame.setTolerances(default = tol_time, time = 1)
|
||||
if energy:
|
||||
self.flame.set(energy = energy)
|
||||
|
||||
def T(self, point = -1):
|
||||
"""Temperature profile or value at one point."""
|
||||
return self.solution('T', point)
|
||||
|
||||
def u(self, point = -1):
|
||||
"""Axial velocity profile or value at one point."""
|
||||
return self.solution('u', point)
|
||||
|
||||
def V(self, point = -1):
|
||||
"""Radial velocity profile or value at one point."""
|
||||
return self.solution('V', point)
|
||||
|
||||
def solution(self, component = '', point = -1):
|
||||
"""Solution component at one point, or full profile if no
|
||||
point specified."""
|
||||
if point >= 0: return self.value(self.flame, component, point)
|
||||
else: return self.profile(self.flame, component)
|
||||
|
||||
def setGasState(self, j):
|
||||
"""Set the state of the object representing the gas to the
|
||||
current solution at grid point *j*."""
|
||||
nsp = self.gas.nSpecies()
|
||||
y = zeros(nsp, 'd')
|
||||
for n in range(nsp):
|
||||
nm = self.gas.speciesName(n)
|
||||
y[n] = self.solution(nm, j)
|
||||
self.gas.setState_TPY(self.T(j), self.flame.pressure(), y)
|
||||
|
||||
fix_docs(BurnerFlame)
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
"""A counterflow flame."""
|
||||
|
||||
from onedim import *
|
||||
from Cantera.num import zeros
|
||||
import math
|
||||
|
||||
def erfc(x):
|
||||
"""The complementary error function."""
|
||||
exp = math.exp
|
||||
|
||||
p = 0.3275911
|
||||
a1 = 0.254829592
|
||||
a2 = -0.284496736
|
||||
a3 = 1.421413741
|
||||
a4 = -1.453152027
|
||||
a5 = 1.061405429
|
||||
|
||||
t = 1.0 / (1.0 + p*x)
|
||||
erfcx = ( (a1 + (a2 + (a3 +
|
||||
(a4 + a5*t)*t)*t)*t)*t ) * exp(-x*x)
|
||||
return erfcx
|
||||
|
||||
def erf(x):
|
||||
"""The error function."""
|
||||
if x < 0:
|
||||
return -(1.0 - erfc(-x))
|
||||
else:
|
||||
return 1.0 - erfc(x)
|
||||
|
||||
|
||||
|
||||
class CounterFlame(Stack):
|
||||
"""A non-premixed counterflow flame."""
|
||||
|
||||
def __init__(self, gas = None, grid = None):
|
||||
"""
|
||||
The domains are::
|
||||
|
||||
[self.fuel_inlet, # class Inlet,
|
||||
self.flame, # class AxisymmetricFlow,
|
||||
self.oxidizer_inlet] # class Inlet
|
||||
"""
|
||||
|
||||
self.fuel_inlet = Inlet('fuel inlet')
|
||||
self.oxidizer_inlet = Inlet('oxidizer inlet')
|
||||
self.gas = gas
|
||||
self.fuel_inlet.set(temperature = gas.temperature())
|
||||
self.oxidizer_inlet.set(temperature = gas.temperature())
|
||||
self.flame = AxisymmetricFlow('flame',gas = gas)
|
||||
self.flame.setupGrid(grid)
|
||||
Stack.__init__(self, [self.fuel_inlet, self.flame,
|
||||
self.oxidizer_inlet])
|
||||
self.setRefineCriteria()
|
||||
|
||||
|
||||
def init(self, fuel = '', oxidizer = 'O2', stoich = -1.0):
|
||||
"""Set the initial guess for the solution. The fuel species
|
||||
must be specified, and the oxidizer may be
|
||||
|
||||
>>> f.init(fuel='CH4')
|
||||
|
||||
The initial guess is generated by assuming infinitely-fast
|
||||
chemistry."""
|
||||
self.getInitialSoln()
|
||||
gas = self.gas
|
||||
nsp = gas.nSpecies()
|
||||
wt = gas.molecularWeights()
|
||||
|
||||
# find the fuel and oxidizer species
|
||||
iox = gas.speciesIndex(oxidizer)
|
||||
ifuel = gas.speciesIndex(fuel)
|
||||
|
||||
# if no stoichiometric ratio was input, compute it
|
||||
if stoich < 0.0:
|
||||
if oxidizer == 'O2':
|
||||
nh = gas.nAtoms(fuel, 'H')
|
||||
nc = gas.nAtoms(fuel, 'C')
|
||||
stoich = 1.0*nc + 0.25*nh
|
||||
else:
|
||||
raise CanteraError('oxidizer/fuel stoichiometric ratio must'+
|
||||
' be specified, since the oxidizer is not O2')
|
||||
|
||||
s = stoich*wt[iox]/wt[ifuel]
|
||||
y0f = self.fuel_inlet.massFraction(ifuel)
|
||||
y0ox = self.oxidizer_inlet.massFraction(iox)
|
||||
phi = s*y0f/y0ox
|
||||
zst = 1.0/(1.0 + phi)
|
||||
pressure = self.flame.pressure()
|
||||
|
||||
yin_f = zeros(nsp, 'd')
|
||||
yin_o = zeros(nsp, 'd')
|
||||
yst = zeros(nsp, 'd')
|
||||
for k in range(nsp):
|
||||
yin_f[k] = self.fuel_inlet.massFraction(k)
|
||||
yin_o[k] = self.oxidizer_inlet.massFraction(k)
|
||||
yst[k] = zst*yin_f[k] + (1.0 - zst)*yin_o[k]
|
||||
|
||||
gas.setState_TPY(self.fuel_inlet.temperature(), pressure, yin_f)
|
||||
mdotf = self.fuel_inlet.mdot()
|
||||
u0f = mdotf/gas.density()
|
||||
t0f = self.fuel_inlet.temperature()
|
||||
|
||||
gas.setState_TPY(self.oxidizer_inlet.temperature(),
|
||||
pressure, yin_o)
|
||||
mdoto = self.oxidizer_inlet.mdot()
|
||||
u0o = mdoto/gas.density()
|
||||
t0o = self.oxidizer_inlet.temperature()
|
||||
|
||||
# get adiabatic flame temperature and composition
|
||||
tbar = 0.5*(t0o + t0f)
|
||||
gas.setState_TPY(tbar, pressure, yst)
|
||||
gas.equilibrate('HP')
|
||||
teq = gas.temperature()
|
||||
yeq = gas.massFractions()
|
||||
|
||||
# estimate strain rate
|
||||
zz = self.flame.grid()
|
||||
dz = zz[-1] - zz[0]
|
||||
a = (u0o + u0f)/dz
|
||||
diff = gas.mixDiffCoeffs()
|
||||
f = math.sqrt(a/(2.0*diff[iox]))
|
||||
|
||||
x0 = mdotf*dz/(mdotf + mdoto)
|
||||
nz = len(zz)
|
||||
|
||||
y = zeros([nz,nsp],'d')
|
||||
t = zeros(nz,'d')
|
||||
for j in range(nz):
|
||||
x = zz[j]
|
||||
zeta = f*(x - x0)
|
||||
zmix = 0.5*(1.0 - erf(zeta))
|
||||
if zmix > zst:
|
||||
for k in range(nsp):
|
||||
y[j,k] = yeq[k] + (zmix - zst)*(yin_f[k]
|
||||
- yeq[k])/(1.0 - zst)
|
||||
t[j] = teq + (t0f - teq)*(zmix - zst)/(1.0 - zst)
|
||||
else:
|
||||
for k in range(nsp):
|
||||
y[j,k] = yin_o[k] + zmix*(yeq[k] - yin_o[k])/zst
|
||||
t[j] = t0o + (teq - t0o)*zmix/zst
|
||||
|
||||
t[0] = t0f
|
||||
t[-1] = t0o
|
||||
zrel = zz/dz
|
||||
self.setProfile('u', [0.0, 1.0], [u0f, -u0o])
|
||||
self.setProfile('V', [0.0, x0/dz, 1.0], [0.0, a, 0.0])
|
||||
self.setProfile('T', zrel, t)
|
||||
for k in range(nsp):
|
||||
self.setProfile(gas.speciesName(k), zrel, y[:,k])
|
||||
|
||||
self._initialized = 1
|
||||
|
||||
|
||||
def solve(self, loglevel = 1, refine_grid = 1):
|
||||
if not self._initialized: self.init()
|
||||
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
|
||||
|
||||
|
||||
def setRefineCriteria(self, ratio = 10.0, slope = 0.8, curve = 0.8,
|
||||
prune = 0.0):
|
||||
Stack.setRefineCriteria(self, domain = self.flame,
|
||||
ratio = ratio, slope = slope, curve = curve,
|
||||
prune = prune)
|
||||
|
||||
def setGridMin(self, gridmin):
|
||||
Stack.setGridMin(self, self.flame, gridmin)
|
||||
|
||||
def setProfile(self, component, locs, vals):
|
||||
self._initialized = 1
|
||||
Stack.setProfile(self, self.flame, component, locs, vals)
|
||||
|
||||
def set(self, tol = None, energy = '', tol_time = None):
|
||||
"""Set parameters.
|
||||
|
||||
:param tol:
|
||||
(rtol, atol) for steady-state
|
||||
:param tol_time:
|
||||
(rtol, atol) for time stepping
|
||||
:param energy:
|
||||
'on' or 'off' to enable or disable the energy equation
|
||||
"""
|
||||
if tol:
|
||||
self.flame.setTolerances(default = tol)
|
||||
if tol_time:
|
||||
self.flame.setTolerances(default = tol_time, time = 1)
|
||||
if energy:
|
||||
self.flame.set(energy = energy)
|
||||
|
||||
def T(self, point = -1):
|
||||
"""The temperature [K]"""
|
||||
return self.solution('T', point)
|
||||
|
||||
def u(self, point = -1):
|
||||
"""The axial velocity [m/s]"""
|
||||
return self.solution('u', point)
|
||||
|
||||
def V(self, point = -1):
|
||||
"""The radial velocity divided by radius [s^-1]"""
|
||||
return self.solution('V', point)
|
||||
|
||||
def solution(self, component = '', point = -1):
|
||||
"""The solution for one specified component. If a point number
|
||||
is given, return the value of component 'component' at this
|
||||
point. Otherwise, return the entire profile for this
|
||||
component."""
|
||||
if point >= 0: return self.value(self.flame, component, point)
|
||||
else: return self.profile(self.flame, component)
|
||||
|
||||
def setGasState(self, j):
|
||||
"""Set the state of the object representing the gas to the
|
||||
current solution at grid point j."""
|
||||
nsp = self.gas.nSpecies()
|
||||
y = zeros(nsp, 'd')
|
||||
for n in range(nsp):
|
||||
nm = self.gas.speciesName(n)
|
||||
y[n] = self.solution(nm, j)
|
||||
self.gas.setState_TPY(self.T(j), self.flame.pressure(), y)
|
||||
|
||||
fix_docs(CounterFlame)
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
from onedim import *
|
||||
from Cantera import _cantera
|
||||
|
||||
from Cantera.num import array, zeros
|
||||
|
||||
class FreeFlame(Stack):
|
||||
"""A freely-propagating flat flame."""
|
||||
|
||||
def __init__(self, gas = None, grid = None, tfix = 500.0):
|
||||
"""
|
||||
:param gas:
|
||||
object to use to evaluate all gas properties and reaction
|
||||
rates. Required
|
||||
:param grid:
|
||||
array of initial grid points
|
||||
|
||||
A domain of type FreeFlame named 'flame' will be created to
|
||||
represent the flame. The three domains comprising the stack
|
||||
are stored as ``self.inlet``, ``self.flame``, and ``self.outlet``.
|
||||
"""
|
||||
|
||||
self.inlet = Inlet('burner')
|
||||
self.gas = gas
|
||||
self.inlet.set(temperature = gas.temperature())
|
||||
self.outlet = Outlet('outlet')
|
||||
|
||||
# type 2 is Cantera C++ class FreeFlame
|
||||
self.flame = AxisymmetricFlow('flame',gas = gas,type=2)
|
||||
|
||||
self.flame.setupGrid(grid)
|
||||
Stack.__init__(self, [self.inlet, self.flame, self.outlet])
|
||||
self.setRefineCriteria()
|
||||
self.tfix = tfix
|
||||
|
||||
|
||||
def init(self):
|
||||
"""Set the initial guess for the solution. The adiabatic flame
|
||||
temperature and equilibrium composition are computed for the
|
||||
inlet gas composition. The temperature profile rises linearly
|
||||
in the first 20% of the flame to Tad, then is flat. The mass
|
||||
fraction profiles are set similarly.
|
||||
"""
|
||||
self.getInitialSoln()
|
||||
gas = self.gas
|
||||
nsp = gas.nSpecies()
|
||||
yin = zeros(nsp, 'd')
|
||||
for k in range(nsp):
|
||||
yin[k] = self.inlet.massFraction(k)
|
||||
gas.setState_TPY(self.inlet.temperature(), self.flame.pressure(), yin)
|
||||
u0 = self.inlet.mdot()/gas.density()
|
||||
t0 = self.inlet.temperature()
|
||||
|
||||
# get adiabatic flame temperature and composition
|
||||
gas.equilibrate('HP',solver=1)
|
||||
teq = gas.temperature()
|
||||
yeq = gas.massFractions()
|
||||
u1 = self.inlet.mdot()/gas.density()
|
||||
|
||||
z1 = 0.5
|
||||
locs = array([0.0, 0.3, z1, 1.0],'d')
|
||||
self.setProfile('u', locs, [u0, u0, u1, u1])
|
||||
self.setProfile('T', locs, [t0, t0, teq, teq])
|
||||
self.setFixedTemperature(self.tfix)
|
||||
for n in range(nsp):
|
||||
self.setProfile(gas.speciesName(n), locs, [yin[n], yin[n],
|
||||
yeq[n], yeq[n]])
|
||||
self._initialized = 1
|
||||
|
||||
|
||||
def solve(self, loglevel = 1, refine_grid = 1):
|
||||
if not self._initialized: self.init()
|
||||
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
|
||||
|
||||
|
||||
def setRefineCriteria(self, ratio = 10.0, slope = 0.8,
|
||||
curve = 0.8, prune = 0.0):
|
||||
Stack.setRefineCriteria(self, domain = self.flame,
|
||||
ratio = ratio, slope = slope, curve = curve,
|
||||
prune = prune)
|
||||
|
||||
def setGridMin(self, gridmin):
|
||||
Stack.setGridMin(self, self.flame, gridmin)
|
||||
|
||||
def setFixedTemperature(self, temp):
|
||||
_cantera.sim1D_setFixedTemperature(self._hndl, temp)
|
||||
|
||||
def setProfile(self, component, locs, vals):
|
||||
self._initialized = 1
|
||||
Stack.setProfile(self, self.flame, component, locs, vals)
|
||||
|
||||
def set(self, tol = None, energy = '', tol_time = None):
|
||||
"""Set parameters.
|
||||
:param tol:
|
||||
(rtol, atol) for steady-state
|
||||
:param tol_time:
|
||||
(rtol, atol) for time stepping
|
||||
:param energy:
|
||||
'on' or 'off' to enable or disable the energy equation
|
||||
"""
|
||||
if tol:
|
||||
self.flame.setTolerances(default = tol)
|
||||
if tol_time:
|
||||
self.flame.setTolerances(default = tol_time, time = 1)
|
||||
if energy:
|
||||
self.flame.set(energy = energy)
|
||||
|
||||
def T(self, point = -1):
|
||||
"""Temperature profile or value at one point."""
|
||||
return self.solution('T', point)
|
||||
|
||||
def u(self, point = -1):
|
||||
"""Axial velocity profile or value at one point."""
|
||||
return self.solution('u', point)
|
||||
|
||||
def V(self, point = -1):
|
||||
"""Radial velocity profile or value at one point."""
|
||||
return self.solution('V', point)
|
||||
|
||||
def solution(self, component = '', point = -1):
|
||||
"""Solution component at one point, or full profile if no
|
||||
point specified."""
|
||||
if point >= 0: return self.value(self.flame, component, point)
|
||||
else: return self.profile(self.flame, component)
|
||||
|
||||
def setGasState(self, j):
|
||||
"""Set the state of the object representing the gas to the
|
||||
current solution at grid point j."""
|
||||
nsp = self.gas.nSpecies()
|
||||
y = zeros(nsp, 'd')
|
||||
for n in range(nsp):
|
||||
nm = self.gas.speciesName(n)
|
||||
y[n] = self.solution(nm, j)
|
||||
self.gas.setState_TPY(self.T(j), self.flame.pressure(), y)
|
||||
|
||||
fix_docs(FreeFlame)
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
from onedim import *
|
||||
from Cantera.num import array, zeros
|
||||
|
||||
class StagnationFlow(Stack):
|
||||
"""An axisymmetric flow impinging on a surface at normal incidence."""
|
||||
|
||||
def __init__(self, gas = None, surfchem = None, grid = None):
|
||||
"""
|
||||
:param gas:
|
||||
object to use to evaluate all gas properties and reaction
|
||||
rates. Required.
|
||||
:param surfchem:
|
||||
object used to evaluate surface reaction rates. If omitted,
|
||||
surface will be treated as inert.
|
||||
:param grid:
|
||||
array of initial grid points
|
||||
|
||||
A domain of type :class:`.AxisymmetricFlow` named ``flow`` will be
|
||||
created to represent the flow, and one of type :class:`.Surface` named
|
||||
``surface`` will be created to represent the surface. The three domains
|
||||
comprising the stack are stored as ``self.inlet``, ``self.flow``,
|
||||
and ``self.surface``.
|
||||
"""
|
||||
self.inlet = Inlet('inlet')
|
||||
self.gas = gas
|
||||
self.surfchem = surfchem
|
||||
self.inlet.set(temperature = gas.temperature())
|
||||
self.surface = Surface(id = 'surface', surface_mech = surfchem)
|
||||
self.flow = AxisymmetricFlow('flow',gas = gas)
|
||||
self.flow.setupGrid(grid)
|
||||
Stack.__init__(self, [self.inlet, self.flow, self.surface])
|
||||
self.setRefineCriteria()
|
||||
|
||||
|
||||
def init(self, products = 'inlet'):
|
||||
"""Set the initial guess for the solution. If products = 'equil',
|
||||
then the equilibrium composition at the adiabatic flame temperature
|
||||
will be used to form the initial guess. Otherwise the inlet composition
|
||||
will be used."""
|
||||
self.getInitialSoln()
|
||||
gas = self.gas
|
||||
nsp = gas.nSpecies()
|
||||
yin = zeros(nsp, 'd')
|
||||
for k in range(nsp):
|
||||
yin[k] = self.inlet.massFraction(k)
|
||||
gas.setState_TPY(self.inlet.temperature(), self.flow.pressure(), yin)
|
||||
u0 = self.inlet.mdot()/gas.density()
|
||||
t0 = self.inlet.temperature()
|
||||
V0 = 0.0
|
||||
|
||||
tsurf = self.surface.temperature()
|
||||
|
||||
zz = self.flow.grid()
|
||||
dz = zz[-1] - zz[0]
|
||||
|
||||
if products == 'equil':
|
||||
gas.equilibrate('HP')
|
||||
teq = gas.temperature()
|
||||
yeq = gas.massFractions()
|
||||
locs = array([0.0, 0.3, 0.7, 1.0],'d')
|
||||
self.setProfile('T', locs, [t0, teq, teq, tsurf])
|
||||
for n in range(nsp):
|
||||
self.setProfile(gas.speciesName(n), locs, [yin[n], yeq[n], yeq[n], yeq[n]])
|
||||
else:
|
||||
locs = array([0.0, 1.0],'d')
|
||||
self.setProfile('T', locs, [t0, tsurf])
|
||||
for n in range(nsp):
|
||||
self.setProfile(gas.speciesName(n), locs, [yin[n], yin[n]])
|
||||
|
||||
locs = array([0.0, 1.0],'d')
|
||||
self.setProfile('u', locs, [u0, 0.0])
|
||||
self.setProfile('V', locs, [V0, V0])
|
||||
|
||||
self._initialized = 1
|
||||
|
||||
|
||||
def solve(self, loglevel = 1, refine_grid = 1):
|
||||
if not self._initialized: self.init()
|
||||
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
|
||||
|
||||
|
||||
def setRefineCriteria(self, ratio = 10.0, slope = 0.8,
|
||||
curve = 0.8, prune = 0.0):
|
||||
Stack.setRefineCriteria(self, domain = self.flow,
|
||||
ratio = ratio, slope = slope, curve = curve,
|
||||
prune = prune)
|
||||
|
||||
def setGridMin(self, gridmin):
|
||||
Stack.setGridMin(self, self.flow, gridmin)
|
||||
|
||||
def setProfile(self, component, locs, vals):
|
||||
self._initialized = 1
|
||||
Stack.setProfile(self, self.flow, component, locs, vals)
|
||||
|
||||
def set(self, tol = None, energy = '', tol_time = None):
|
||||
"""Set parameters.
|
||||
|
||||
:param tol:
|
||||
(rtol, atol) for steady-state
|
||||
:param tol_time:
|
||||
(rtol, atol) for time stepping
|
||||
:param energy:
|
||||
'on' or 'off' to enable or disable the energy equation
|
||||
"""
|
||||
if tol:
|
||||
self.flow.setTolerances(default = tol)
|
||||
if tol_time:
|
||||
self.flow.setTolerances(default = tol_time, time = 1)
|
||||
if energy:
|
||||
self.flow.set(energy = energy)
|
||||
|
||||
def T(self, point = -1):
|
||||
"""The temperature [K]"""
|
||||
return self.solution('T', point)
|
||||
|
||||
def u(self, point = -1):
|
||||
"""The axial velocity [m/s]"""
|
||||
return self.solution('u', point)
|
||||
|
||||
def V(self, point = -1):
|
||||
"""The radial velocity divided by radius [s^-1]"""
|
||||
return self.solution('V', point)
|
||||
|
||||
def solution(self, component = '', point = -1):
|
||||
"""The solution for one specified component. If a point number
|
||||
is given, return the value of component *component* at this
|
||||
point. Otherwise, return the entire profile for this
|
||||
component."""
|
||||
if point >= 0: return self.value(self.flow, component, point)
|
||||
else: return self.profile(self.flow, component)
|
||||
|
||||
def coverages(self):
|
||||
"""The coverages of the surface species."""
|
||||
nsurf = self.surfchem.nSpecies()
|
||||
cov = zeros(nsurf,'d')
|
||||
for n in range(nsurf):
|
||||
nm = self.surfchem.speciesName(n)
|
||||
cov[n] = self.value(self.surface, nm, 0)
|
||||
return cov
|
||||
|
||||
def setGasState(self, j):
|
||||
"""Set the state of the object representing the gas to the
|
||||
current solution at grid point *j*."""
|
||||
nsp = self.gas.nSpecies()
|
||||
y = zeros(nsp, 'd')
|
||||
for n in range(nsp):
|
||||
nm = self.gas.speciesName(n)
|
||||
y[n] = self.solution(nm, j)
|
||||
self.gas.setState_TPY(self.T(j), self.flow.pressure(), y)
|
||||
|
||||
fix_docs(StagnationFlow)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
"""
|
||||
The classes in this package implement one-dimensional reacting flow problems.
|
||||
"""
|
||||
from onedim import *
|
||||
from BurnerFlame import BurnerFlame
|
||||
from BurnerDiffFlame import BurnerDiffFlame
|
||||
from CounterFlame import CounterFlame
|
||||
from StagnationFlow import StagnationFlow
|
||||
|
|
@ -1,746 +0,0 @@
|
|||
from Cantera import *
|
||||
from Cantera import _cantera
|
||||
from Cantera.num import asarray, zeros
|
||||
|
||||
_onoff = {'on':1, 'yes':1, 'off':0, 'no':0, 1:1, 0:0}
|
||||
|
||||
class Domain1D:
|
||||
"""Base class for one-dimensional domains."""
|
||||
|
||||
def __init__(self):
|
||||
self._hndl = 0
|
||||
|
||||
def __del__(self):
|
||||
_cantera.domain_del(self._hndl)
|
||||
|
||||
def domain_hndl(self):
|
||||
"""Integer used to reference the kernel object."""
|
||||
return self._hndl
|
||||
|
||||
def type(self):
|
||||
"""Domain type. Integer."""
|
||||
return _cantera.domain_type(self._hndl)
|
||||
|
||||
def index(self):
|
||||
"""Index of this domain in a stack. Returns -1 if this domain
|
||||
is not part of a stack."""
|
||||
return _cantera.domain_index(self._hndl)
|
||||
|
||||
def nComponents(self):
|
||||
"""Number of solution components at each grid point."""
|
||||
return _cantera.domain_nComponents(self._hndl)
|
||||
|
||||
def nPoints(self):
|
||||
"""Number of grid points belonging to this domain."""
|
||||
return _cantera.domain_nPoints(self._hndl)
|
||||
|
||||
def componentName(self, n):
|
||||
"""Name of the nth component."""
|
||||
return _cantera.domain_componentName(self._hndl, n)
|
||||
|
||||
def componentNames(self):
|
||||
"""List of the names of all components of this domain."""
|
||||
names = []
|
||||
for n in range(self.nComponents()):
|
||||
names.append(self.componentName(n))
|
||||
return names
|
||||
|
||||
def componentIndex(self, name):
|
||||
"""Index of the component with name 'name'"""
|
||||
return _cantera.domain_componentIndex(self._hndl, name)
|
||||
|
||||
def setBounds(self, **bounds):
|
||||
"""Set the lower and upper bounds on the solution.
|
||||
|
||||
The argument list should consist of keyword/value pairs, with
|
||||
component names as keywords and (lower_bound, upper_bound)
|
||||
tuples as the values. The keyword *default* may be used to
|
||||
specify default bounds for all unspecified components. The
|
||||
keyword *Y* can be used to stand for all species mass
|
||||
fractions in flow domains.
|
||||
|
||||
>>> d.setBounds(default=(0, 1),
|
||||
... Y=(-1.0e-5, 2.0))
|
||||
"""
|
||||
|
||||
d = {}
|
||||
if bounds.has_key('default'):
|
||||
for n in range(self.nComponents()):
|
||||
d[self.componentName(n)] = bounds['default']
|
||||
del bounds['default']
|
||||
|
||||
for b in bounds.keys():
|
||||
if b == 'Y':
|
||||
if self.type >= 50:
|
||||
nc = self.nComponents()
|
||||
for n in range(4, nc):
|
||||
d[self.componentName(n)] = bounds[b]
|
||||
else:
|
||||
raise CanteraError('Y can only be specified in flow domains.')
|
||||
else:
|
||||
d[b] = bounds[b]
|
||||
for b in d.keys():
|
||||
n = self.componentIndex(b)
|
||||
_cantera.domain_setBounds(self._hndl, n, d[b][0], d[b][1])
|
||||
|
||||
def bounds(self, component):
|
||||
"""Return the (lower, upper) bounds for a solution component.
|
||||
|
||||
>>> d.bounds('T')
|
||||
(200.0, 5000.0)
|
||||
"""
|
||||
|
||||
ic = self.componentIndex(component)
|
||||
lower = _cantera.domain_lowerBound(self._hndl, ic)
|
||||
upper = _cantera.domain_upperBound(self._hndl, ic)
|
||||
return (lower, upper)
|
||||
|
||||
def tolerances(self, component):
|
||||
"""Return the (relative, absolute) error tolerances for
|
||||
a solution component.
|
||||
|
||||
>>> (r, a) = d.tolerances('u')
|
||||
"""
|
||||
ic = self.componentIndex(component)
|
||||
r = _cantera.domain_rtol(self._hndl, ic)
|
||||
a = _cantera.domain_atol(self._hndl, ic)
|
||||
return (r, a)
|
||||
|
||||
def setTolerances(self, **tol):
|
||||
"""Set the error tolerances. If *time* is present and
|
||||
non-zero, then the values entered will apply to the transient
|
||||
problem. Otherwise, they will apply to the steady-state
|
||||
problem.
|
||||
|
||||
The argument list should consist of keyword/value pairs, with
|
||||
component names as keywords and (rtol, atol) tuples as the
|
||||
values. The keyword *default* may be used to specify default
|
||||
bounds for all unspecified components. The keyword *Y* can be
|
||||
used to stand for all species mass fractions in flow domains.
|
||||
|
||||
>>> d.setTolerances(Y=(1.0e-5, 1.0e-9),
|
||||
... default=(1.0e-7, 1.0e-12),
|
||||
... time=1)
|
||||
"""
|
||||
|
||||
d = {}
|
||||
if tol.has_key('default'):
|
||||
for n in range(self.nComponents()):
|
||||
d[self.componentName(n)] = tol['default']
|
||||
del tol['default']
|
||||
|
||||
itime = 0
|
||||
for b in tol.keys():
|
||||
if b == 'time': itime = -1
|
||||
elif b == 'steady': itime = 1
|
||||
elif b == 'Y':
|
||||
if self.type >= 50:
|
||||
nc = self.nComponents()
|
||||
for n in range(4, nc):
|
||||
d[self.componentName(n)] = tol[b]
|
||||
else:
|
||||
raise CanteraError('Y can only be specified in flow domains.')
|
||||
else:
|
||||
d[b] = tol[b]
|
||||
for b in d.keys():
|
||||
n = self.componentIndex(b)
|
||||
# print 'setting tol for ',b,' itime = ',itime
|
||||
_cantera.domain_setTolerances(self._hndl, n, d[b][0], d[b][1], itime)
|
||||
|
||||
|
||||
def setupGrid(self, grid):
|
||||
"""Specify the grid.
|
||||
|
||||
>>> d.setupGrid([0.0, 0.1, 0.2])
|
||||
|
||||
"""
|
||||
return _cantera.domain_setupGrid(self._hndl, asarray(grid))
|
||||
|
||||
def setID(self, id):
|
||||
return _cantera.domain_setID(self._hndl, id)
|
||||
|
||||
def setDesc(self, desc):
|
||||
"""Set the description of this domain."""
|
||||
return _cantera.domain_setDesc(self._hndl, desc)
|
||||
|
||||
def grid(self, n = -1):
|
||||
""" If *n* >= 0, return the value of the nth grid point
|
||||
from the left in this domain. If n is not supplied, return
|
||||
the entire grid.
|
||||
|
||||
>>> z4 = d.grid(4)
|
||||
>>> z_array = d.grid()
|
||||
|
||||
"""
|
||||
if n >= 0:
|
||||
return _cantera.domain_grid(self._hndl, n)
|
||||
else:
|
||||
g = zeros(self.nPoints(),'d')
|
||||
for j in range(len(g)):
|
||||
g[j] = _cantera.domain_grid(self._hndl, j)
|
||||
return g
|
||||
|
||||
def set(self, **options):
|
||||
"""
|
||||
convenient function to invoke other methods.
|
||||
Parameters that can be set:
|
||||
|
||||
grid, name, desc
|
||||
|
||||
>>> d.set(name='flame', grid=z)
|
||||
"""
|
||||
self._set(options)
|
||||
|
||||
|
||||
def _set(self, options):
|
||||
for opt in options.keys():
|
||||
v = options[opt]
|
||||
if opt == 'grid':
|
||||
self.setupGrid(v)
|
||||
elif opt == 'name':
|
||||
self.setID(v)
|
||||
elif opt == 'desc':
|
||||
self.setDesc(v)
|
||||
#elif opt == 'bounds':
|
||||
# lower, upper = self._dict2arrays(v)
|
||||
# self.setBounds(lower,upper)
|
||||
#elif opt == 'tol':
|
||||
# self.setTolerances(v[0],v[1])
|
||||
#else:
|
||||
# raise CanteraError('unknown attribute: '+opt)
|
||||
|
||||
def _dict2arrays(self, d = None, array1 = None, array2 = None):
|
||||
nc = self.nComponents()
|
||||
if d.has_key('default'):
|
||||
a1 = zeros(nc,'d') + d['default'][0]
|
||||
a2 = zeros(nc,'d') + d['default'][1]
|
||||
del d['default']
|
||||
else:
|
||||
if array1: a1 = array(array1)
|
||||
else: a1 = zeros(nc,'d')
|
||||
if array2: a2 = array(array2)
|
||||
else: a2 = zeros(nc,'d')
|
||||
|
||||
for k in d.keys():
|
||||
c = self.componentIndex(k)
|
||||
if c >= 0:
|
||||
a1[self.componentIndex(k)] = d[k][0]
|
||||
a2[self.componentIndex(k)] = d[k][1]
|
||||
else:
|
||||
raise CanteraError('unknown component '+k)
|
||||
return (a1, a2)
|
||||
|
||||
|
||||
|
||||
class Bdry1D(Domain1D):
|
||||
"""Base class for boundary domains."""
|
||||
|
||||
def __init__(self):
|
||||
Domain1D.__init__(self)
|
||||
|
||||
def setMdot(self, mdot):
|
||||
"""Set the mass flow rate per unit area [kg/m2]."""
|
||||
_cantera.bdry_setMdot(self._hndl, mdot)
|
||||
|
||||
def setTemperature(self, t):
|
||||
"""Set the temperature [K]"""
|
||||
_cantera.bdry_setTemperature(self._hndl, t)
|
||||
|
||||
def setMoleFractions(self, x):
|
||||
"""set the mole fraction values. """
|
||||
_cantera.bdry_setMoleFractions(self._hndl, x)
|
||||
|
||||
def temperature(self):
|
||||
"""Set the temperature [K]."""
|
||||
return _cantera.bdry_temperature(self._hndl)
|
||||
|
||||
def massFraction(self, k):
|
||||
"""The mass fraction of species k."""
|
||||
return _cantera.bdry_massFraction(self._hndl, k)
|
||||
|
||||
def mdot(self):
|
||||
"""The mass flow rate per unit area [kg/m2/s"""
|
||||
return _cantera.bdry_mdot(self._hndl)
|
||||
|
||||
def set(self, **options):
|
||||
"""Set parameters:
|
||||
mdot or massflux
|
||||
temperature or T
|
||||
mole_fractions or X
|
||||
"""
|
||||
for opt in options.keys():
|
||||
v = options[opt]
|
||||
if opt == 'mdot' or opt == 'massflux':
|
||||
self.setMdot(v)
|
||||
del options[opt]
|
||||
elif opt == 'temperature' or opt == 'T':
|
||||
self.setTemperature(v)
|
||||
del options[opt]
|
||||
elif opt == 'mole_fractions' or opt == 'X':
|
||||
self.setMoleFractions(v)
|
||||
del options[opt]
|
||||
self._set(options)
|
||||
|
||||
|
||||
class Inlet(Bdry1D):
|
||||
"""A one-dimensional inlet.
|
||||
Note that an inlet can only be a terminal domain - it must be
|
||||
either the leftmost or rightmost domain in a stack.
|
||||
"""
|
||||
def __init__(self, id = 'inlet'):
|
||||
Bdry1D.__init__(self)
|
||||
self._hndl = _cantera.inlet_new()
|
||||
if id: self.setID(id)
|
||||
|
||||
def setSpreadRate(self, V0 = 0.0):
|
||||
"""Set the spead rate, defined as the value of V = v/r at the inlet."""
|
||||
_cantera.inlet_setSpreadRate(self._hndl, V0)
|
||||
|
||||
|
||||
class Outlet(Bdry1D):
|
||||
"""A one-dimensional outlet. An outlet imposes a
|
||||
zero-gradient boundary condition on the flow."""
|
||||
|
||||
def __init__(self, id = 'outlet'):
|
||||
Bdry1D.__init__(self)
|
||||
self._hndl = _cantera.outlet_new()
|
||||
if id: self.setID(id)
|
||||
|
||||
class OutletRes(Bdry1D):
|
||||
"""A one-dimensional outlet into a reservoir."""
|
||||
|
||||
def __init__(self, id = 'outletres'):
|
||||
Bdry1D.__init__(self)
|
||||
self._hndl = _cantera.outletres_new()
|
||||
if id: self.setID(id)
|
||||
|
||||
|
||||
class SymmPlane(Bdry1D):
|
||||
"""A symmetry plane."""
|
||||
def __init__(self, id = 'symmetry_plane'):
|
||||
Bdry1D.__init__(self)
|
||||
self._hndl = _cantera.symm_new()
|
||||
if id: self.setID(id)
|
||||
|
||||
class Surface(Bdry1D):
|
||||
"""A surface (possibly reacting)."""
|
||||
def __init__(self, id = 'surface', surface_mech = None):
|
||||
Bdry1D.__init__(self)
|
||||
if surface_mech:
|
||||
self._hndl = _cantera.reactingsurf_new()
|
||||
self.setKineticsMgr(surface_mech)
|
||||
else:
|
||||
self._hndl = _cantera.surf_new()
|
||||
if id: self.setID(id)
|
||||
|
||||
|
||||
def setKineticsMgr(self, kin):
|
||||
"""Set the kinetics manager (surface reaction mechanism object)."""
|
||||
_cantera.reactingsurf_setkineticsmgr(self._hndl,
|
||||
kin.kinetics_hndl())
|
||||
|
||||
def setCoverageEqs(self, onoff='on'):
|
||||
"""Turn solving the surface coverage equations on or off."""
|
||||
if onoff == 'on':
|
||||
_cantera.reactingsurf_enableCoverageEqs(self._hndl, 1)
|
||||
else:
|
||||
_cantera.reactingsurf_enableCoverageEqs(self._hndl, 0)
|
||||
|
||||
|
||||
class AxisymmetricFlow(Domain1D):
|
||||
"""An axisymmetric flow domain.
|
||||
|
||||
In an axisymmetric flow domain, the equations solved are the
|
||||
similarity equations for the flow in a finite-height gap of
|
||||
infinite radial extent. The solution variables are
|
||||
|
||||
*u*
|
||||
axial velocity
|
||||
*V*
|
||||
radial velocity divided by radius
|
||||
*T*
|
||||
temperature
|
||||
*lambda*
|
||||
(1/r)(dP/dr)
|
||||
*Y_k*
|
||||
species mass fractions
|
||||
|
||||
It may be shown that if the boundary conditions on these variables
|
||||
are independent of radius, then a similarity solution to the exact
|
||||
governing equations exists in which these variables are all
|
||||
independent of radius. This solution holds only in in
|
||||
low-Mach-number limit, in which case (dP/dz) = 0, and lambda is a
|
||||
constant. (Lambda is treated as a spatially-varying solution
|
||||
variable for numerical reasons, but in the final solution it is
|
||||
always independent of z.) As implemented here, the governing
|
||||
equations assume an ideal gas mixture. Arbitrary chemistry is
|
||||
allowed, as well as arbitrary variation of the transport
|
||||
properties.
|
||||
"""
|
||||
def __init__(self, id = 'axisymmetric_flow', gas = None, type = 1):
|
||||
Domain1D.__init__(self)
|
||||
iph = gas.thermo_hndl()
|
||||
ikin = gas.kinetics_hndl()
|
||||
itr = gas.transport_hndl()
|
||||
self._hndl = _cantera.stflow_new(iph, ikin, itr, type)
|
||||
if id: self.setID(id)
|
||||
self.setPressure(gas.pressure())
|
||||
self.solveEnergyEqn()
|
||||
|
||||
def setPressure(self, p):
|
||||
"""Set the pressure [Pa]. The pressure is a constant, since
|
||||
the governing equations are those for the low-Mach-number limit."""
|
||||
_cantera.stflow_setPressure(self._hndl, p)
|
||||
|
||||
def setTransportModel(self, transp, withSoret = 0):
|
||||
"""Set the transport model. The argument must be a transport
|
||||
manager for the 'gas' object."""
|
||||
itr = transp.transport_hndl()
|
||||
_cantera.stflow_setTransport(self._hndl, itr, withSoret)
|
||||
|
||||
def enableSoret(self, withSoret = 1):
|
||||
"""Include or exclude thermal diffusion (Soret effect) when computing
|
||||
diffusion velocities. If withSoret is not supplied or is positive,
|
||||
thermal diffusion is enabled; otherwise it is disabled."""
|
||||
_cantera.stflow_enableSoret(self._hndl, withSoret)
|
||||
|
||||
def pressure(self):
|
||||
"""Pressure [Pa]."""
|
||||
return _cantera.stflow_pressure(self._hndl)
|
||||
|
||||
def setFixedTempProfile(self, pos, temp):
|
||||
"""Set the fixed temperature profile. This profile is used
|
||||
whenever the energy equation is disabled.
|
||||
|
||||
:param pos:
|
||||
arrray of relative positions from 0 to 1
|
||||
:param temp:
|
||||
array of temperature values
|
||||
|
||||
>>> d.setFixedTempProfile(array([0.0, 0.5, 1.0]),
|
||||
... array([500.0, 1500.0, 2000.0])
|
||||
"""
|
||||
return _cantera.stflow_setFixedTempProfile(self._hndl, pos, temp)
|
||||
|
||||
def solveSpeciesEqs(self, flag = 1):
|
||||
"""Enable or disable solving the species equations. If invoked
|
||||
with no arguments or with a non-zero argument, the species
|
||||
equations will be solved. If invoked with a zero argument,
|
||||
they will not be, and instead the species profiles will be
|
||||
held at their initial values. Default: species equations
|
||||
enabled."""
|
||||
return _cantera.stflow_solveSpeciesEqs(self._hndl, _onoff[flag])
|
||||
|
||||
def solveEnergyEqn(self, flag = 1):
|
||||
"""Enable or disable solving the energy equation. If invoked
|
||||
with no arguments or with a non-zero argument, the energy
|
||||
equations will be solved. If invoked with a zero argument,
|
||||
it will not be, and instead the temperature profiles will be
|
||||
held to the one specified by the call to :meth:`.setFixedTempProfile`.
|
||||
Default: energy equation enabled."""
|
||||
return _cantera.stflow_solveEnergyEqn(self._hndl, _onoff[flag])
|
||||
|
||||
def set(self, **opt):
|
||||
"""Set parameters.
|
||||
In addition to the parameters that may be set by Domain1D.set,
|
||||
this method can be used to set the pressure and energy flag
|
||||
|
||||
>>> d.set(pressure=OneAtm, energy='on')
|
||||
"""
|
||||
for o in opt.keys():
|
||||
v = opt[o]
|
||||
if o == 'P' or o == 'pressure':
|
||||
self.setPressure(v)
|
||||
del opt[o]
|
||||
elif o == 'energy':
|
||||
self.solveEnergyEqn(flag = _onoff[v])
|
||||
else:
|
||||
self._set(opt)
|
||||
|
||||
|
||||
class Stack:
|
||||
""" Class Stack is a container for one-dimensional domains. It
|
||||
also holds the multi-domain solution vector, and controls the
|
||||
process of finding the solution.
|
||||
|
||||
Domains are ordered left-to-right, with domain number 0 at the left.
|
||||
|
||||
This class is largely a shadow class for C++ kernel class Sim1D.
|
||||
"""
|
||||
def __init__(self, domains = None):
|
||||
self._hndl = 0
|
||||
nd = len(domains)
|
||||
hndls = zeros(nd,'i')
|
||||
for n in range(nd):
|
||||
hndls[n] = domains[n].domain_hndl()
|
||||
self._hndl = _cantera.sim1D_new(hndls)
|
||||
self._domains = domains
|
||||
self._initialized = False
|
||||
|
||||
def __del__(self):
|
||||
_cantera.sim1D_del(self._hndl)
|
||||
|
||||
def setValue(self, dom, comp, localPoint, value):
|
||||
"""Set the value of one component in one domain at one point
|
||||
to 'value'.
|
||||
|
||||
:param dom:
|
||||
domain object
|
||||
:param comp:
|
||||
component number
|
||||
:param localPoint:
|
||||
grid point number within domain *dom* starting with zero on the left
|
||||
:param value:
|
||||
numerical value
|
||||
|
||||
>>> s.set(d, 3, 5, 6.7)
|
||||
"""
|
||||
idom = dom.domain_hndl()
|
||||
_cantera.sim1D_setValue(self._hndl, idom,
|
||||
comp, localPoint, value)
|
||||
|
||||
def setProfile(self, dom, comp, pos, v):
|
||||
"""Set an initial estimate for a profile of one component in
|
||||
one domain.
|
||||
|
||||
:param dom:
|
||||
domain object
|
||||
:param comp:
|
||||
component name
|
||||
:param pos:
|
||||
sequence of relative positions, from 0 on the left to 1 on the right
|
||||
:param v:
|
||||
sequence of values at the relative positions specified in 'pos'
|
||||
|
||||
>>> s.setProfile(d, 'T', [0.0, 0.2, 1.0], [400.0, 800.0, 1500.0])
|
||||
"""
|
||||
|
||||
idom = dom.index()
|
||||
icomp = dom.componentIndex(comp)
|
||||
_cantera.sim1D_setProfile(self._hndl, idom, icomp,
|
||||
asarray(pos), asarray(v))
|
||||
|
||||
def setFlatProfile(self, dom, comp, v):
|
||||
"""Set a flat profile for one component in one domain.
|
||||
|
||||
:param dom:
|
||||
domain object
|
||||
:param comp:
|
||||
component name
|
||||
:param v:
|
||||
value
|
||||
|
||||
>>> s.setFlatProfile(d, 'u', -3.0)
|
||||
"""
|
||||
idom = dom.index()
|
||||
icomp = dom.componentIndex(comp)
|
||||
_cantera.sim1D_setFlatProfile(self._hndl, idom, icomp, v)
|
||||
|
||||
def showSolution(self, fname='-'):
|
||||
"""Show the current solution. If called with no argument,
|
||||
the solution is printed to the screen. If a filename is
|
||||
supplied, it is written to the file.
|
||||
|
||||
>>> s.showSolution()
|
||||
>>> s.showSolution('soln.txt')
|
||||
"""
|
||||
if not self._initialized:
|
||||
self.init()
|
||||
_cantera.sim1D_showSolution(self._hndl, fname)
|
||||
|
||||
def setTimeStep(self, stepsize, nsteps):
|
||||
"""Set the sequence of time steps to try when Newton fails.
|
||||
|
||||
:param stepsize:
|
||||
initial time step size [s]
|
||||
:param nsteps:
|
||||
sequence of integer step numbers
|
||||
|
||||
>>> s.setTimeStep(1.0e-5, [1, 2, 5, 10])
|
||||
"""
|
||||
# 3/20/09
|
||||
# The use of asarray seems to set the nsteps array to be of
|
||||
# type double. This needs to be checked out further.
|
||||
# Probably a function of python version and Numerics version
|
||||
_cantera.sim1D_setTimeStep(self._hndl, stepsize, asarray(nsteps))
|
||||
|
||||
def getInitialSoln(self):
|
||||
"""Load the initial solution from each domain into the global
|
||||
solution vector."""
|
||||
_cantera.sim1D_getInitialSoln(self._hndl)
|
||||
|
||||
def solve(self, loglevel=1, refine_grid=1):
|
||||
"""Solve the problem.
|
||||
|
||||
:param loglevel:
|
||||
integer flag controlling the amount of diagnostic output. Zero
|
||||
suppresses all output, and 5 produces very verbose output. Default: 1
|
||||
:param refine_grid:
|
||||
if non-zero, enable grid refinement."""
|
||||
|
||||
return _cantera.sim1D_solve(self._hndl, loglevel, refine_grid)
|
||||
|
||||
def refine(self, loglevel=1):
|
||||
"""Refine the grid, adding points where solution is not
|
||||
adequately resolved."""
|
||||
return _cantera.sim1D_refine(self._hndl, loglevel)
|
||||
|
||||
def setRefineCriteria(self, domain = None, ratio = 10.0, slope = 0.8,
|
||||
curve = 0.8, prune = 0.05):
|
||||
"""Set the criteria used to refine one domain.
|
||||
|
||||
:param domain:
|
||||
domain object
|
||||
:param ratio:
|
||||
additional points will be added if the ratio of the spacing
|
||||
on either side of a grid point exceeds this value
|
||||
:param slope:
|
||||
maximum difference in value between two adjacent points, scaled by
|
||||
the maximum difference in the profile (0.0 < slope < 1.0). Adds
|
||||
points in regions of high slope.
|
||||
:param curve:
|
||||
maximum difference in slope between two adjacent intervals, scaled
|
||||
by the maximum difference in the profile (0.0 < curve < 1.0). Adds
|
||||
points in regions of high curvature.
|
||||
:param prune:
|
||||
if the slope or curve criteria are satisfied to the level of
|
||||
'prune', the grid point is assumed not to be needed and is removed.
|
||||
Set prune significantly smaller than 'slope' and 'curve'. Set to
|
||||
zero to disable pruning the grid.
|
||||
|
||||
>>> s.setRefineCriteria(d, ratio=5.0, slope=0.2, curve=0.3,
|
||||
... prune=0.03)
|
||||
"""
|
||||
idom = domain.index()
|
||||
return _cantera.sim1D_setRefineCriteria(self._hndl,
|
||||
idom, ratio, slope, curve, prune)
|
||||
|
||||
def setGridMin(self, domain, gridmin):
|
||||
"""
|
||||
Set the minimum allowable grid spacing in a domain.
|
||||
|
||||
:param domain:
|
||||
domain object
|
||||
:param gridmin:
|
||||
The minimum allowable grid spacing [m] for this domain
|
||||
"""
|
||||
idom = domain.index()
|
||||
return _cantera.sim1D_setGridMin(self._hndl, idom, gridmin)
|
||||
|
||||
def save(self, file = 'soln.xml', id = 'solution', desc = 'none'):
|
||||
"""Save the solution in XML format.
|
||||
|
||||
>>> s.save(file='save.xml', id='energy_off',
|
||||
... desc='solution with energy eqn. disabled')
|
||||
|
||||
"""
|
||||
return _cantera.sim1D_save(self._hndl, file, id, desc)
|
||||
|
||||
def restore(self, file = 'soln.xml', id = 'solution'):
|
||||
"""Set the solution vector to a previously-saved solution.
|
||||
|
||||
:param file:
|
||||
solution file
|
||||
:param id:
|
||||
solution name within the file
|
||||
|
||||
>>> s.restore(file = 'save.xml', id = 'energy_off')
|
||||
"""
|
||||
self._initialized = True
|
||||
return _cantera.sim1D_restore(self._hndl, file, id)
|
||||
|
||||
def showStats(self, printTime = 1):
|
||||
"""Show the statistics for the last solution.
|
||||
If invoked with no arguments or with a non-zero argument, the
|
||||
timing statistics will be printed. If invoked with a zero argument,
|
||||
the timing will not be printed.
|
||||
Default: print timing enabled.
|
||||
"""
|
||||
return _cantera.sim1D_writeStats(self._hndl, _onoff[printTime])
|
||||
|
||||
def domainIndex(self, name):
|
||||
"""Integer index of the domain with name 'name'"""
|
||||
return _cantera.sim1D_domainIndex(self._hndl, name)
|
||||
|
||||
def value(self, domain, component, localPoint):
|
||||
"""Solution value at one point.
|
||||
|
||||
:param domain:
|
||||
domain object
|
||||
:param component:
|
||||
component name
|
||||
:param localPoint:
|
||||
grid point number in the domain, starting with zero at the left
|
||||
|
||||
>>> t = s.value(flow, 'T', 6)
|
||||
"""
|
||||
icomp = domain.componentIndex(component)
|
||||
idom = domain.index()
|
||||
return _cantera.sim1D_value(self._hndl, idom, icomp, localPoint)
|
||||
|
||||
def profile(self, domain, component):
|
||||
"""Spatial profile of one component in one domain.
|
||||
|
||||
>>> print s.profile(flow, 'T')
|
||||
"""
|
||||
np = domain.nPoints()
|
||||
x = zeros(np,'d')
|
||||
for n in range(np):
|
||||
x[n] = self.value(domain, component, n)
|
||||
return x
|
||||
|
||||
def workValue(self, dom, icomp, localPoint):
|
||||
"""Internal work array value at one point. After calling eval,
|
||||
this array contains the values of the residual function.
|
||||
|
||||
:param domain:
|
||||
domain object
|
||||
:param component:
|
||||
component name
|
||||
:param localPoint:
|
||||
grid point number in the domain, starting with zero at the left
|
||||
|
||||
>>> t = s.value(flow, 'T', 6)
|
||||
"""
|
||||
idom = dom.index()
|
||||
return _cantera.sim1D_workValue(self._hndl, idom, icomp, localPoint)
|
||||
|
||||
def eval(self, rdt, count=1):
|
||||
"""Evaluate the residual function. If count = 0, do is 'silently',
|
||||
without adding to the function evaluation counter"""
|
||||
return _cantera.sim1D_eval(self._hndl, rdt, count)
|
||||
|
||||
def setMaxJacAge(self, ss_age, ts_age):
|
||||
"""Set the maximum number of times the Jacobian will be used
|
||||
before it must be re-evaluated.
|
||||
|
||||
:param ss_age:
|
||||
age criterion during steady-state mode
|
||||
:param ts_age:
|
||||
age criterion during time-stepping mode
|
||||
"""
|
||||
return _cantera.sim1D_setMaxJacAge(self._hndl, ss_age, ts_age)
|
||||
|
||||
def timeStepFactor(self, tfactor):
|
||||
"""Set the factor by which the time step will be increased
|
||||
after a successful step, or decreased after an unsuccessful one.
|
||||
|
||||
>>> s.timeStepFactor(3.0)
|
||||
"""
|
||||
return _cantera.sim1D_timeStepFactor(self._hndl, tfactor)
|
||||
|
||||
def setTimeStepLimits(self, tsmin, tsmax):
|
||||
"""Set the maximum and minimum time steps."""
|
||||
return _cantera.sim1D_setTimeStepLimits(self._hndl, tsmin, tsmax)
|
||||
|
||||
def setFixedTemperature(self, temp):
|
||||
"""This is a temporary fix."""
|
||||
_cantera.sim1D_setFixedTemperature(self._hndl, temp)
|
||||
|
||||
def clearDomains():
|
||||
"""Clear all domains."""
|
||||
_cantera.domain_clear()
|
||||
|
||||
def clearSim1D():
|
||||
"""Clear all stacks."""
|
||||
_cantera.sim1D_clear()
|
||||
|
|
@ -1,351 +0,0 @@
|
|||
"""Cantera.Phase
|
||||
|
||||
This module provides class Phase.
|
||||
|
||||
"""
|
||||
|
||||
import _cantera
|
||||
|
||||
import types
|
||||
from Cantera.num import asarray
|
||||
from exceptions import CanteraError
|
||||
|
||||
# return true is x is a sequence
|
||||
def _isseq(n, x):
|
||||
try:
|
||||
y = x[n-1]
|
||||
return 1
|
||||
except:
|
||||
return 0
|
||||
|
||||
class Phase:
|
||||
|
||||
"""Phases of matter.
|
||||
|
||||
Class Phase manages basic state and constituent property
|
||||
information for a homogeneous phase of matter. It handles only
|
||||
those properties that do not require the equation of state, namely
|
||||
the temperature, density, chemical composition, and attributes of
|
||||
the elements and species.
|
||||
|
||||
It does not know about the pressure, or any other thermodynamic property
|
||||
requiring the equation of state -- class ThermoPhase derives from Phase
|
||||
and adds those properties.
|
||||
|
||||
Class Phase is not usually instantiated directly. It is used as a
|
||||
base class for class ThermoPhase.
|
||||
|
||||
"""
|
||||
|
||||
#def __init__(self, index = -1):
|
||||
# pass
|
||||
|
||||
def phase_id(self):
|
||||
"""The integer index used to access the kernel-level object.
|
||||
Internal."""
|
||||
return self._phase_id
|
||||
|
||||
def nElements(self):
|
||||
"""Number of elements."""
|
||||
return _cantera.phase_nelements(self._phase_id)
|
||||
|
||||
def atomicWeights(self, elements = []):
|
||||
"""Array of element molar masses [kg/kmol].
|
||||
|
||||
If a sequence of element symbols is supplied, only the values
|
||||
for those elements are returned, ordered as in the
|
||||
list. Otherwise, the values are for all elements in the phase,
|
||||
ordered as in the input file. """
|
||||
atw = _cantera.phase_getarray(self._phase_id,1)
|
||||
if elements:
|
||||
ae = []
|
||||
m = 0
|
||||
for e in elements:
|
||||
m = self.elementIndex(e)
|
||||
ae.append(atw[m])
|
||||
return asarray(ae)
|
||||
else:
|
||||
return atw
|
||||
|
||||
def nSpecies(self):
|
||||
"""Number of species."""
|
||||
return _cantera.phase_nspecies(self._phase_id)
|
||||
|
||||
def nAtoms(self, species = None, element = None):
|
||||
"""Number of atoms of element *element* in species *species*.
|
||||
The element and species may be specified by name or by number.
|
||||
|
||||
>>> ph.nAtoms('CH4','H')
|
||||
4
|
||||
|
||||
"""
|
||||
try:
|
||||
m = self.elementIndex(element)
|
||||
k = self.speciesIndex(species)
|
||||
na = _cantera.phase_natoms(self._phase_id, k, m)
|
||||
#if na < 0: return 0
|
||||
return na
|
||||
except CanteraError:
|
||||
return 0
|
||||
|
||||
def temperature(self):
|
||||
"""Temperature [K]."""
|
||||
return _cantera.phase_temperature(self._phase_id)
|
||||
|
||||
def density(self):
|
||||
"""Mass density [kg/m^3]."""
|
||||
return _cantera.phase_density(self._phase_id)
|
||||
|
||||
def volume_mass(self):
|
||||
"""Specific volume [m^3/kg]."""
|
||||
return 1.0/_cantera.phase_density(self._phase_id)
|
||||
|
||||
def molarDensity(self):
|
||||
"""Molar density [kmol/m^3]."""
|
||||
return _cantera.phase_molardensity(self._phase_id)
|
||||
|
||||
def meanMolecularWeight(self):
|
||||
"""Mean molar mass [kg/kmol]."""
|
||||
return _cantera.phase_meanmolwt(self._phase_id)
|
||||
|
||||
def meanMolarMass(self):
|
||||
"""Mean molar mass [kg/kmol]."""
|
||||
return _cantera.phase_meanmolwt(self._phase_id)
|
||||
|
||||
def molarMasses(self, species = None):
|
||||
"""Array of species molar masses [kg/kmol]."""
|
||||
mm = _cantera.phase_getarray(self._phase_id,22)
|
||||
return self.selectSpecies(mm, species)
|
||||
|
||||
def molecularWeights(self, species = None):
|
||||
"""Array of species molar masses [kg/kmol]."""
|
||||
return self.molarMasses(species)
|
||||
|
||||
def moleFractions(self, species = None):
|
||||
"""Species mole fraction array.
|
||||
If optional argument *species* is supplied, then only the values
|
||||
for the selected species are returned.
|
||||
|
||||
>>> x1 = ph.moleFractions() # all species
|
||||
>>> x2 = ph.moleFractions(['OH', 'CH3'. 'O2'])
|
||||
"""
|
||||
x = _cantera.phase_getarray(self._phase_id,20)
|
||||
return self.selectSpecies(x, species)
|
||||
|
||||
def moleFraction(self, species):
|
||||
"""Mole fraction of a species, referenced by name or index number.
|
||||
|
||||
>>> ph.moleFraction(4)
|
||||
>>> ph.moleFraction('CH4')
|
||||
"""
|
||||
k = self.speciesIndex(species)
|
||||
return _cantera.phase_molefraction(self._phase_id,k)
|
||||
|
||||
|
||||
def massFractions(self, species = None):
|
||||
"""Species mass fraction array.
|
||||
If optional argument *species* is supplied, then only the values for
|
||||
the selected species are returned.
|
||||
|
||||
>>> y1 = ph.massFractions() # all species
|
||||
>>> y2 = ph.massFractions(['OH', 'CH3'. 'O2'])
|
||||
"""
|
||||
y = _cantera.phase_getarray(self._phase_id,21)
|
||||
return self.selectSpecies(y, species)
|
||||
|
||||
|
||||
def massFraction(self, species):
|
||||
"""Mass fraction of one species, referenced by name or
|
||||
index number.
|
||||
|
||||
>>> ph.massFraction(4)
|
||||
>>> ph.massFraction('CH4')
|
||||
"""
|
||||
k = self.speciesIndex(species)
|
||||
return _cantera.phase_massfraction(self._phase_id,k)
|
||||
|
||||
|
||||
def elementName(self,m):
|
||||
"""Name of the element with index number *m*."""
|
||||
return _cantera.phase_getstring(self._phase_id,1,m)
|
||||
|
||||
def elementNames(self):
|
||||
"""Return a tuple of all element names."""
|
||||
nel = self.nElements()
|
||||
return map(self.elementName,range(nel))
|
||||
|
||||
def elementIndex(self, element):
|
||||
"""The index of element *element*, which may be specified as
|
||||
a string or an integer index. In the latter case, the index is
|
||||
checked for validity and returned. If no such element is
|
||||
present, an exception is thrown."""
|
||||
|
||||
nel = self.nElements()
|
||||
if type(element) == types.IntType:
|
||||
m = element
|
||||
else:
|
||||
m = _cantera.phase_elementindex(self._phase_id, element)
|
||||
if m < 0 or m >= nel:
|
||||
raise CanteraError("""Element """+element+""" not in set """
|
||||
+`self.elementNames()`)
|
||||
return m
|
||||
|
||||
|
||||
def speciesName(self,k):
|
||||
"""Name of the species with index *k*."""
|
||||
return _cantera.phase_getstring(self._phase_id,2,k)
|
||||
|
||||
|
||||
def speciesNames(self):
|
||||
"""Return a tuple of all species names."""
|
||||
nsp = self.nSpecies()
|
||||
return map(self.speciesName,range(nsp))
|
||||
|
||||
|
||||
def speciesIndex(self, species):
|
||||
"""The index of species *species*, which may be specified as
|
||||
a string or an integer index. In the latter case, the index is
|
||||
checked for validity and returned. If no such species is
|
||||
present, an exception is thrown."""
|
||||
nsp = self.nSpecies()
|
||||
if type(species) == types.ListType:
|
||||
s = []
|
||||
for sp in species:
|
||||
s.append(self.speciesIndex(sp))
|
||||
return s
|
||||
|
||||
if type(species) == types.IntType or type(species) == types.FloatType:
|
||||
k = species
|
||||
else:
|
||||
k = _cantera.phase_speciesindex(self._phase_id,species)
|
||||
if k < 0 or k >= nsp:
|
||||
raise CanteraError("""Species """+`species`+""" not in set """
|
||||
+`self.speciesNames()`)
|
||||
return k
|
||||
|
||||
|
||||
def setTemperature(self, t):
|
||||
"""Set the temperature [K]."""
|
||||
_cantera.phase_setfp(self._phase_id,1,t)
|
||||
|
||||
def setDensity(self, rho):
|
||||
"""Set the density [kg/m3]."""
|
||||
_cantera.phase_setfp(self._phase_id,2,rho)
|
||||
|
||||
def setMolarDensity(self, n):
|
||||
"""Set the density [kmol/m3]."""
|
||||
_cantera.phase_setfp(self._phase_id,3,n)
|
||||
|
||||
def setMoleFractions(self, x, norm = 1):
|
||||
"""Set the mole fractions.
|
||||
|
||||
:param x:
|
||||
string or array of mole fraction values
|
||||
:param norm:
|
||||
If non-zero (default), array values will be scaled to sum to 1.0.
|
||||
|
||||
>>> ph.setMoleFractions('CO:1, H2:7, H2O:7.8')
|
||||
>>> x = [1.0]*ph.nSpecies()
|
||||
>>> ph.setMoleFractions(x)
|
||||
>>> ph.setMoleFractions(x, norm = 0) # don't normalize values
|
||||
"""
|
||||
if type(x) == types.StringType:
|
||||
_cantera.phase_setstring(self._phase_id,1,x)
|
||||
elif _isseq(self.nSpecies(), x):
|
||||
_cantera.phase_setarray(self._phase_id,1,norm,asarray(x))
|
||||
else:
|
||||
raise CanteraError('mole fractions must be a string or array')
|
||||
|
||||
|
||||
def setMassFractions(self, x, norm = 1):
|
||||
"""Set the mass fractions.
|
||||
See :meth:`~.Phase.setMoleFractions`
|
||||
"""
|
||||
if type(x) == types.StringType:
|
||||
_cantera.phase_setstring(self._phase_id,2,x)
|
||||
elif _isseq(self.nSpecies(), x):
|
||||
_cantera.phase_setarray(self._phase_id,2,norm,asarray(x))
|
||||
else:
|
||||
raise CanteraError('mass fractions must be a string or array')
|
||||
|
||||
|
||||
def setState_TRX(self, t, rho, x):
|
||||
"""Set the temperature, density, and mole fractions. The mole
|
||||
fractions may be entered as a string or array,
|
||||
>>> ph.setState_TRX(600.0, 2.0e-3, 'CH4:0.4, O2:0.6')
|
||||
"""
|
||||
|
||||
self.setTemperature(t)
|
||||
self.setMoleFractions(x)
|
||||
self.setDensity(rho)
|
||||
|
||||
def setState_TNX(self, t, n, x):
|
||||
"""Set the temperature, molardensity, and mole fractions. The mole
|
||||
fractions may be entered as a string or array,
|
||||
|
||||
>>> ph.setState_TNX(600.0, 2.0e-3, 'CH4:0.4, O2:0.6')
|
||||
"""
|
||||
|
||||
self.setTemperature(t)
|
||||
self.setMoleFractions(x)
|
||||
self.setMolarDensity(n)
|
||||
|
||||
def setState_TRY(self, t, rho, y):
|
||||
"""Set the temperature, density, and mass fractions."""
|
||||
self.setTemperature(t)
|
||||
self.setMassFractions(y)
|
||||
self.setDensity(rho)
|
||||
|
||||
def setState_TR(self, t, rho):
|
||||
"""Set the temperature and density, leaving the composition
|
||||
unchanged."""
|
||||
self.setTemperature(t)
|
||||
self.setDensity(rho)
|
||||
|
||||
def selectSpecies(self, f, species):
|
||||
"""Given an array *f* of floating-point species properties, return
|
||||
those values corresponding to species listed in *species*. Returns an
|
||||
array if *species* is a sequence, or a scalar if *species* is a
|
||||
scalar. This method is used internally to implement species selection
|
||||
in methods like moleFractions, massFractions, etc.
|
||||
|
||||
>>> f = ph.chemPotentials()
|
||||
>>> muo2, muh2 = ph.selectSpecies(f, ['O2', 'H2'])
|
||||
>>> muh2 = ph.selectSpecies(f, 'H2')
|
||||
"""
|
||||
|
||||
if isinstance(species, types.StringTypes):
|
||||
k = self.speciesIndex(species)
|
||||
return f[k]
|
||||
elif species:
|
||||
fs = []
|
||||
k = 0
|
||||
for s in species:
|
||||
k = self.speciesIndex(s)
|
||||
fs.append(f[k])
|
||||
return asarray(fs)
|
||||
else:
|
||||
return asarray(f)
|
||||
|
||||
def selectElements(self, f, elements):
|
||||
"""Given an array *f* of floating-point element properties, return a
|
||||
those values corresponding to elements listed in *elements*. Returns an
|
||||
array if *elements* is a sequence, or a scalar if *elements* is a
|
||||
scalar.
|
||||
|
||||
>>> f = ph.elementPotentials()
|
||||
>>> lam_o, lam_h = ph.selectElements(f, ['O', 'H'])
|
||||
>>> lam_h = ph.selectElements(f, 'H')
|
||||
"""
|
||||
if isinstance(elements, types.StringTypes):
|
||||
m = self.elementIndex(elements)
|
||||
return f[m]
|
||||
if elements:
|
||||
fs = []
|
||||
k = 0
|
||||
for s in elements:
|
||||
k = self.elementIndex(s)
|
||||
fs.append(f[k])
|
||||
return asarray(fs)
|
||||
else:
|
||||
return asarray(f)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,32 +0,0 @@
|
|||
"""
|
||||
Transport properties for solids.
|
||||
|
||||
This class implements a simple model for the diffusion coefficients and
|
||||
the thermal conductivity of a solid. The diffusion coefficients have
|
||||
modified Arrhenius form, and the thermal conductivity is constant.
|
||||
All parameters are user-specified, not computed from a physical model.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> tr = SolidTransport(solid_phase)
|
||||
>>> tr.setThermalConductivity(0.5) # W/m/K
|
||||
>>> tr.setDiffCoeff(species = "OxygenIon", A = 2.0, n = 0.0, E = 700.0)
|
||||
|
||||
Note that the diffusion coefficient is computed from D = A * T^n *
|
||||
exp(-E/t) in m^2/s. Diffusion coefficients for unspecified species are
|
||||
set to zero.
|
||||
|
||||
"""
|
||||
|
||||
from Cantera.Transport import Transport
|
||||
|
||||
class SolidTransport(Transport):
|
||||
def __init__(self, phase = None):
|
||||
Transport.__init__(self, model = "Solid", phase = phase)
|
||||
|
||||
def setThermalConductivity(self, lamb):
|
||||
self.setParameters(1, 0, [lamb, 0.0])
|
||||
|
||||
def setDiffCoeff(self, species = "", A = 0.0, n = 0.0, E = 0.0):
|
||||
k = self._phase.speciesIndex(species)
|
||||
self.setParameters(0, k, [A, n, E])
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
|
||||
from ThermoPhase import ThermoPhase
|
||||
from exceptions import CanteraError
|
||||
from Cantera.num import asarray
|
||||
import _cantera
|
||||
|
||||
class SurfacePhase(ThermoPhase):
|
||||
"""A class for surface phases."""
|
||||
|
||||
def __init__(self, xml_phase=None, index=-1):
|
||||
ThermoPhase.__init__(self, xml_phase=xml_phase, index=index)
|
||||
|
||||
def setSiteDensity(self, n0):
|
||||
"""Set the site density."""
|
||||
_cantera.surf_setsitedensity(self._phase_id, n0)
|
||||
|
||||
def siteDensity(self):
|
||||
"""Site density [kmol/m2]"""
|
||||
return _cantera.surf_sitedensity(self._phase_id)
|
||||
|
||||
def setCoverages(self, theta):
|
||||
"""Set the surface coverages to the values in array *theta*."""
|
||||
nt = len(theta)
|
||||
if nt == self.nSpecies():
|
||||
_cantera.surf_setcoverages(self._phase_id,
|
||||
asarray(theta,'d'))
|
||||
else:
|
||||
raise CanteraError('expected '+`self.nSpecies()`+
|
||||
' coverage values, but got '+`nt`)
|
||||
|
||||
def coverages(self):
|
||||
"""Return the array of surface coverages."""
|
||||
return _cantera.surf_getcoverages(self._phase_id)
|
||||
|
||||
def setConcentrations(self, conc):
|
||||
"""Set the surface concentrations to the values in
|
||||
array *conc*."""
|
||||
_cantera.surf_setconcentrations(self._phase_id, conc)
|
||||
|
||||
def concentrations(self):
|
||||
"""Return the array of surface concentrations [kmol/m2]."""
|
||||
return _cantera.surf_getconcentrations(self._phase_id)
|
||||
|
||||
|
||||
class EdgePhase(SurfacePhase):
|
||||
"""A one-dimensonal edge."""
|
||||
|
||||
def __init__(self, xml_phase=None, index=-1):
|
||||
SurfacePhase.__init__(self, xml_phase=xml_phase, index=index)
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
"""
|
||||
This module implements class ThermoPhase, a class representing
|
||||
thermodynamic phases.
|
||||
"""
|
||||
from Cantera.num import zeros
|
||||
from Cantera.Phase import Phase
|
||||
|
||||
import _cantera
|
||||
import types
|
||||
|
||||
class ThermoPhase(Phase):
|
||||
"""
|
||||
A phase with an equation of state.
|
||||
|
||||
Class ThermoPhase may be used to represent the intensive
|
||||
thermodynamic state of a phase of matter, which might be a gas,
|
||||
liquid, or solid. Class ThermoPhase extends class Phase by
|
||||
providing methods that require knowledge of the equation of state.
|
||||
|
||||
Class ThermoPhase is not usually instantiated directly. It is used
|
||||
as base class for classes :class:`~Cantera.Solution` and
|
||||
:class:`~Cantera.Interface.Interface`.
|
||||
"""
|
||||
|
||||
# used in the 'equilibrate' method
|
||||
_equilmap = {'TP':104,'TV':100,'HP':101,'SP':102,'SV':107,'UV':105,
|
||||
'PT':104,'VT':100,'PH':101,'PS':102,'VS':107,'VU':105}
|
||||
|
||||
|
||||
def __init__(self, xml_phase=None, index=-1):
|
||||
"""
|
||||
:param xml_phase:
|
||||
CTML node specifying the attributes of this phase
|
||||
:param index:
|
||||
optional. If positive, create only a Python wrapper for an existing
|
||||
kernel object, instead of creating a new kernel object. The value
|
||||
of *index* is the integer index number to reference the existing
|
||||
kernel object.
|
||||
"""
|
||||
|
||||
self._phase_id = 0
|
||||
self._owner = 0
|
||||
self.idtag = ""
|
||||
|
||||
if index >= 0:
|
||||
# create a Python wrapper for an existing kernel
|
||||
# ThermoPhase instance
|
||||
self._phase_id = index
|
||||
|
||||
elif xml_phase:
|
||||
# create a new kernel instance from an XML specification
|
||||
self._phase_id = _cantera.ThermoFromXML(xml_phase._xml_id)
|
||||
self.idtag = xml_phase["id"]
|
||||
self._owner = 1
|
||||
|
||||
else:
|
||||
raise CanteraError('either xml_phase or index must be specified')
|
||||
|
||||
|
||||
def __del__(self):
|
||||
"""Delete the object. If it is the owner of the kernel object,
|
||||
this is also deleted."""
|
||||
if self._owner:
|
||||
_cantera.thermo_delete(self._phase_id)
|
||||
|
||||
def name(self):
|
||||
"""The name assigned to the phase. The default value is the name
|
||||
attribute from the CTI file. But method setName can be used to
|
||||
set the name to anything desired, e.g. 'gas at inlet' or 'exhaust'
|
||||
"""
|
||||
return self.idtag
|
||||
|
||||
def setName(self, name):
|
||||
""" Set the name attribute. This can be any string"""
|
||||
self.idtag = name
|
||||
|
||||
def refPressure(self):
|
||||
"""Reference pressure [Pa].
|
||||
All standard-state thermodynamic properties are for this pressure.
|
||||
"""
|
||||
return _cantera.thermo_refpressure(self._phase_id)
|
||||
|
||||
def minTemp(self, sp=None):
|
||||
""" Minimum temperature for which thermodynamic property fits
|
||||
are valid. If a species is specified (by name or number),
|
||||
then the minimum temperature is for only this
|
||||
species. Otherwise it is the lowest temperature for which the
|
||||
properties are valid for all species. """
|
||||
if not sp:
|
||||
return _cantera.thermo_mintemp(self._phase_id, -1)
|
||||
else:
|
||||
return _cantera.thermo_mintemp(self._phase_id,
|
||||
self.speciesIndex(sp))
|
||||
|
||||
def maxTemp(self, sp=None):
|
||||
""" Maximum temperature for which thermodynamic property fits
|
||||
are valid. If a species is specified (by name or number),
|
||||
then the maximum temperature is for only this
|
||||
species. Otherwise it is the highest temperature for which the
|
||||
properties are valid for all species. """
|
||||
if not sp:
|
||||
return _cantera.thermo_maxtemp(self._phase_id, -1)
|
||||
else:
|
||||
return _cantera.thermo_maxtemp(self._phase_id,
|
||||
self.speciesIndex(sp))
|
||||
|
||||
def enthalpy_mole(self):
|
||||
""" The molar enthalpy [J/kmol]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,1)
|
||||
|
||||
def intEnergy_mole(self):
|
||||
""" The molar internal energy [J/kmol]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,2)
|
||||
|
||||
def entropy_mole(self):
|
||||
""" The molar entropy [J/kmol/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,3)
|
||||
|
||||
def gibbs_mole(self):
|
||||
""" The molar Gibbs function [J/kmol]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,4)
|
||||
|
||||
def cp_mole(self):
|
||||
""" The molar heat capacity at constant pressure [J/kmol/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,5)
|
||||
|
||||
def cv_mole(self):
|
||||
""" The molar heat capacity at constant volume [J/kmol/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,6)
|
||||
|
||||
def pressure(self):
|
||||
""" The pressure [Pa]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,7)
|
||||
|
||||
def electricPotential(self):
|
||||
"""Electric potential [V]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,25)
|
||||
|
||||
def chemPotentials(self, species = []):
|
||||
"""Species chemical potentials.
|
||||
|
||||
This method returns an array containing the species
|
||||
chemical potentials [J/kmol]. The expressions used to
|
||||
compute these depend on the model implemented by the
|
||||
underlying kernel thermo manager."""
|
||||
mu = _cantera.thermo_getarray(self._phase_id,20)
|
||||
return self.selectSpecies(mu, species)
|
||||
|
||||
def elementPotentials(self, elements = []):
|
||||
"""Element potentials of the elements.
|
||||
|
||||
This method returns an array containing the element potentials
|
||||
[J/kmol]. The element potentials are only defined for
|
||||
equilibrium states. This method first sets the composition to
|
||||
a state of equilibrium holding T and P constant, then computes
|
||||
the element potentials for this equilibrium state. """
|
||||
|
||||
lamb = _cantera.thermo_getarray(self._phase_id,21)
|
||||
return self.selectElements(lamb, elements)
|
||||
|
||||
def enthalpies_RT(self, species = []):
|
||||
"""Pure species non-dimensional reference state enthalpies.
|
||||
|
||||
This method returns an array containing the pure-species
|
||||
standard-state enthalpies divided by RT. For gaseous species,
|
||||
these values are ideal gas enthalpies."""
|
||||
hrt = _cantera.thermo_getarray(self._phase_id,23)
|
||||
return self.selectSpecies(hrt, species)
|
||||
|
||||
def entropies_R(self, species = []):
|
||||
"""Pure species non-dimensional entropies.
|
||||
|
||||
This method returns an array containing the pure-species
|
||||
standard-state entropies divided by R. For gaseous species,
|
||||
these values are ideal gas entropies."""
|
||||
sr = _cantera.thermo_getarray(self._phase_id,24)
|
||||
return self.selectSpecies(sr, species)
|
||||
|
||||
def gibbs_RT(self, species = []):
|
||||
"""Pure species non-dimensional Gibbs free energies.
|
||||
|
||||
This method returns an array containing the pure-species
|
||||
standard-state Gibbs free energies divided by R.
|
||||
For gaseous species, these are ideal gas values."""
|
||||
grt = (_cantera.thermo_getarray(self._phase_id,23)
|
||||
- _cantera.thermo_getarray(self._phase_id,24))
|
||||
return self.selectSpecies(grt, species)
|
||||
|
||||
def cp_R(self, species = []):
|
||||
"""Pure species non-dimensional heat capacities
|
||||
at constant pressure.
|
||||
|
||||
This method returns an array containing the pure-species
|
||||
standard-state heat capacities divided by R. For gaseous
|
||||
species, these values are ideal gas heat capacities."""
|
||||
cpr = _cantera.thermo_getarray(self._phase_id,25)
|
||||
return self.selectSpecies(cpr, species)
|
||||
|
||||
|
||||
def setPressure(self, p):
|
||||
"""Set the pressure [Pa]."""
|
||||
_cantera.thermo_setfp(self._phase_id,1,p,0.0)
|
||||
|
||||
def enthalpy_mass(self):
|
||||
"""Specific enthalpy [J/kg]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,8)
|
||||
|
||||
def intEnergy_mass(self):
|
||||
"""Specific internal energy [J/kg]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,9)
|
||||
|
||||
def entropy_mass(self):
|
||||
"""Specific entropy [J/kg/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,10)
|
||||
|
||||
def gibbs_mass(self):
|
||||
"""Specific Gibbs free energy [J/kg]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,11)
|
||||
|
||||
def cp_mass(self):
|
||||
"""Specific heat at constant pressure [J/kg/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,12)
|
||||
|
||||
def cv_mass(self):
|
||||
"""Specific heat at constant volume [J/kg/K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,13)
|
||||
|
||||
def setState_TPX(self, t, p, x):
|
||||
"""Set the temperature [K], pressure [Pa], and
|
||||
mole fractions."""
|
||||
self.setTemperature(t)
|
||||
self.setMoleFractions(x)
|
||||
self.setPressure(p)
|
||||
|
||||
def setState_TPY(self, t, p, y):
|
||||
"""Set the temperature [K], pressure [Pa], and
|
||||
mass fractions."""
|
||||
self.setTemperature(t)
|
||||
self.setMassFractions(y)
|
||||
self.setPressure(p)
|
||||
|
||||
def setState_TP(self, t, p):
|
||||
"""Set the temperature [K] and pressure [Pa]."""
|
||||
self.setTemperature(t)
|
||||
self.setPressure(p)
|
||||
|
||||
def setState_PX(self, p, x):
|
||||
"""Set the pressure [Pa], and mole fractions."""
|
||||
self.setMoleFractions(x)
|
||||
self.setPressure(p)
|
||||
|
||||
def setState_PY(self, p, y):
|
||||
"""Set the pressure [Pa], and mass fractions."""
|
||||
self.setMassFractions(y)
|
||||
self.setPressure(p)
|
||||
|
||||
def setState_HP(self, h, p):
|
||||
"""Set the state by specifying the specific enthalpy and
|
||||
the pressure."""
|
||||
_cantera.thermo_setfp(self._phase_id, 2, h, p)
|
||||
|
||||
def setState_UV(self, u, v):
|
||||
"""Set the state by specifying the specific internal
|
||||
energy and the specific volume."""
|
||||
_cantera.thermo_setfp(self._phase_id, 3, u, v)
|
||||
|
||||
def setState_SV(self, s, v):
|
||||
"""Set the state by specifying the specific entropy
|
||||
and the specific volume."""
|
||||
_cantera.thermo_setfp(self._phase_id, 4, s, v)
|
||||
|
||||
def setState_SP(self, s, p):
|
||||
"""Set the state by specifying the specific entropy
|
||||
energy and the pressure."""
|
||||
_cantera.thermo_setfp(self._phase_id, 5, s, p)
|
||||
|
||||
def setElectricPotential(self, v):
|
||||
"""Set the electric potential."""
|
||||
_cantera.thermo_setfp(self._phase_id, 6, v, 0);
|
||||
|
||||
def equilibrate(self, XY, solver = -1, rtol = 1.0e-9,
|
||||
maxsteps = 1000, maxiter = 100, loglevel = 0):
|
||||
"""
|
||||
Set to a state of chemical equilibrium holding property pair
|
||||
*XY* constant.
|
||||
|
||||
:param XY:
|
||||
A two-letter string, which must be one of the set::
|
||||
|
||||
['TP','TV','HP','SP','SV','UV','PT','VT','PH','PS','VS','VU']
|
||||
|
||||
If H, U, S, or V is specified, the value must be the specific
|
||||
value (per unit mass)
|
||||
:param solver:
|
||||
Specifies the equilibrium solver to use. If solver = 0, a fast
|
||||
solver using the element potential method will be used. If
|
||||
solver > 0, a slower but more robust Gibbs minimization solver
|
||||
will be used. If solver < 0 or unspecified, the fast solver will
|
||||
be tried first, then if it fails the other will be tried.
|
||||
:param rtol:
|
||||
the relative error tolerance.
|
||||
:param maxsteps:
|
||||
maximum number of steps in composition to take to find a converged
|
||||
solution.
|
||||
:param maxiter:
|
||||
For the Gibbs minimization solver only, this specifies the number
|
||||
of 'outer' iterations on T or P when some property pair other than
|
||||
TP is specified.
|
||||
:param loglevel:
|
||||
Set to a value > 0 to write diagnostic output to a file in HTML
|
||||
format. Larger values generate more detailed information. The file
|
||||
will be named ``equilibrate_log.html.`` Subsequent files will be
|
||||
named ``equilibrate_log1.html``, etc., so that log files are
|
||||
not overwritten.
|
||||
"""
|
||||
_cantera.thermo_equil(self._phase_id, XY, solver,
|
||||
rtol, maxsteps, maxiter, loglevel)
|
||||
|
||||
def saveState(self):
|
||||
"""Return an array with state information that can later be
|
||||
used to restore the state."""
|
||||
state = zeros(self.nSpecies()+2,'d')
|
||||
state[0] = self.temperature()
|
||||
state[1] = self.density()
|
||||
state[2:] = self.massFractions()
|
||||
return state
|
||||
|
||||
def restoreState(self, s):
|
||||
"""Restore the state to that stored in array s."""
|
||||
self.setState_TRY(s[0], s[1], s[2:])
|
||||
|
||||
def thermophase(self):
|
||||
"""Return the integer index that is used to
|
||||
reference the kernel object. For internal use."""
|
||||
return self._phase_id
|
||||
|
||||
def thermo_hndl(self):
|
||||
"""Return the integer index that is used to
|
||||
reference the kernel object. For internal use."""
|
||||
return self._phase_id
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
""" Cantera provides a set of 'transport manager' classes that manage
|
||||
the computation of transport properties. Every object representing a
|
||||
phase of matter for which transport properties are needed has a
|
||||
transport manager assigned to it. The transport manager has only one
|
||||
job: to compute the values of the transport properties of its assigned
|
||||
phase.
|
||||
|
||||
A transport manager may do things not apparent to the user in order to
|
||||
improve the speed of transport property evaluation. For example, it
|
||||
may cache intermediate results that depend only on temperature, so
|
||||
that if it happens to be called again at the same temperature (a
|
||||
common occurrence) it can skip over computing the stored
|
||||
temperature-dependent intermediate properties. This is why we use the
|
||||
term 'manager' rather than 'calculator.'
|
||||
|
||||
In the Cantera kernel, each different transport model is implemented
|
||||
by a different class derived from the base class Transport. A
|
||||
highly simplified class structure is used in the Python interface --
|
||||
there is only one class. """
|
||||
|
||||
import _cantera
|
||||
from Cantera.num import asarray
|
||||
import exceptions
|
||||
|
||||
class Transport:
|
||||
"""Transport properties.
|
||||
|
||||
This class provides the Python interface to the family of
|
||||
transport manager classes in the Cantera C++ kernel. A transport
|
||||
manager has one job: to compute transport properties of a phase of
|
||||
matter assigned to it. The phase is represented by an object
|
||||
belonging to a class derived from ThermoPhase.
|
||||
|
||||
In the C++ kernel, a transport manager implements a single
|
||||
transport model, and is an instance of a subclass of the base
|
||||
class ``Transport``. The structure in Python is a little
|
||||
different. A single class ``Transport`` represents any kernel-level
|
||||
transport manager. In addition, multiple kernel-kevel transport
|
||||
managers may be installed in one Python transport manager,
|
||||
although only one is active at any one time. This feature allows
|
||||
switching between transport models."""
|
||||
|
||||
def __init__(self, xml_phase=None,
|
||||
phase=None, model = "", loglevel=0):
|
||||
"""Create a transport property manager.
|
||||
|
||||
:param xml_phase:
|
||||
XML phase element
|
||||
:param phase:
|
||||
:class:`.ThermoPhase` instance representing the phase that the
|
||||
transport properties are for
|
||||
:param model:
|
||||
String specifying transport model. If omitted or set to ``Default``,
|
||||
the model will be read from the input file.
|
||||
:param loglevel:
|
||||
controls the amount of diagnostic output
|
||||
"""
|
||||
|
||||
# if the transport model is not specified, look for attribute
|
||||
# 'model' of the XML 'transport' element
|
||||
if model == "" or model == "Default" or model == "default":
|
||||
try:
|
||||
self.model = xml_phase.child('transport')['model']
|
||||
except:
|
||||
self.model = ""
|
||||
else:
|
||||
self.model = model
|
||||
|
||||
self.__tr_id = 0
|
||||
self.__tr_id = _cantera.Transport(self.model,
|
||||
phase._phase_id, loglevel)
|
||||
self.trnsp = phase.nSpecies()
|
||||
self._phase_id = phase._phase_id
|
||||
|
||||
# dictionary holding all installed transport managers
|
||||
self._models = {}
|
||||
self._models[self.model] = self.__tr_id
|
||||
|
||||
def __del__(self):
|
||||
"""Delete all installed transport models."""
|
||||
if hasattr(self,'_models'):
|
||||
for m in self._models.keys():
|
||||
try:
|
||||
_cantera.tran_delete(self._models[m])
|
||||
except:
|
||||
pass
|
||||
|
||||
def addTransportModel(self, model, loglevel=1):
|
||||
"""Add a new transport model. Note that if *model* is the
|
||||
name of an already-installed transport model, the new
|
||||
transport manager will take the place of the old one, which
|
||||
will no longer be accessible. This method does not change the
|
||||
active model."""
|
||||
new_id = _cantera.Transport(model,
|
||||
self._phase_id, loglevel)
|
||||
self._models[model] = new_id
|
||||
|
||||
|
||||
def switchTransportModel(self, model):
|
||||
"""Switch to a different transport model."""
|
||||
if self._models.has_key(model):
|
||||
self.__tr_id = self._models[model]
|
||||
self.model = model
|
||||
else:
|
||||
raise CanteraError("Transport model "+model+" not defined. Use "
|
||||
+"method addTransportModel first.")
|
||||
|
||||
def desc(self):
|
||||
"""A short description of the active model."""
|
||||
if self.model == 'Multi':
|
||||
return 'Multicomponent'
|
||||
elif self.model == 'Mix':
|
||||
return 'Mixture-averaged'
|
||||
else:
|
||||
return self.model
|
||||
|
||||
def transport_id(self):
|
||||
"""For internal use."""
|
||||
return self.__tr_id
|
||||
|
||||
def transport_hndl(self):
|
||||
"""For internal use."""
|
||||
return self.__tr_id
|
||||
|
||||
def viscosity(self):
|
||||
"Viscosity [Pa-s]."""
|
||||
return _cantera.tran_viscosity(self.__tr_id)
|
||||
|
||||
def electricalConductivity(self):
|
||||
"""electrical conductivity. [S/m]."""
|
||||
return _cantera.tran_electricalConductivity(self.__tr_id)
|
||||
|
||||
def thermalConductivity(self):
|
||||
"""Thermal conductivity. [W/m/K]."""
|
||||
return _cantera.tran_thermalConductivity(self.__tr_id)
|
||||
|
||||
def thermalDiffCoeffs(self):
|
||||
"""Return a one-dimensional array of the species thermal diffusion
|
||||
coefficients. Not implemented in all transport models."""
|
||||
return _cantera.tran_thermalDiffCoeffs(self.__tr_id,
|
||||
self.trnsp)
|
||||
|
||||
def binaryDiffCoeffs(self):
|
||||
"""Two-dimensional array of species binary diffusion coefficients."""
|
||||
return _cantera.tran_binaryDiffCoeffs(self.__tr_id,
|
||||
self.trnsp)
|
||||
|
||||
def diffusionCoeffs(self):
|
||||
"""Species diffusion coefficients. (m^2/s)."""
|
||||
return self.mixDiffCoeffs()
|
||||
|
||||
|
||||
def mixDiffCoeffs(self):
|
||||
"""Mixture-averaged diffusion coefficients."""
|
||||
return _cantera.tran_mixDiffCoeffs(self.__tr_id,
|
||||
self.trnsp)
|
||||
|
||||
def multiDiffCoeffs(self):
|
||||
"""Two-dimensional array of species multicomponent diffusion
|
||||
coefficients. Not implemented in all transport managers."""
|
||||
return _cantera.tran_multiDiffCoeffs(self.__tr_id,
|
||||
self.trnsp)
|
||||
|
||||
def setParameters(self, type, k, params):
|
||||
"""Set model-specific parameters."""
|
||||
return _cantera.tran_setParameters(self.__tr_id,
|
||||
type, k, asarray(params))
|
||||
|
||||
|
||||
def molarFluxes(self, state1, state2, delta):
|
||||
return _cantera.tran_getMolarFluxes(self.__tr_id, self.trnsp,
|
||||
asarray(state1), asarray(state2),
|
||||
delta)
|
||||
|
||||
def massFluxes(self, state1, state2, delta):
|
||||
return _cantera.tran_getMassFluxes(self.__tr_id, self.trnsp,
|
||||
asarray(state1), asarray(state2),
|
||||
delta)
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
"""
|
||||
This module provides the Python interface to C++ class XML_Node.
|
||||
"""
|
||||
|
||||
import _cantera
|
||||
import types
|
||||
import tempfile
|
||||
import string
|
||||
import exceptions
|
||||
|
||||
class XML_Node:
|
||||
"""A node in an XML tree."""
|
||||
def __init__(self, name="--", src="", wrap=0, root=None, preprocess=0, debug=0):
|
||||
"""
|
||||
Return an instance representing a node in an XML tree.
|
||||
|
||||
If 'src' is specified, then the XML tree found in file 'src' is
|
||||
constructed, and this node forms the root of the tree. The XML tree
|
||||
is saved, and a second call with the same value for 'src' will use
|
||||
the XML tree already read in, instead of reading it in again.
|
||||
|
||||
If 'wrap' is greater than zero, then only a Python wrapper is
|
||||
created - no new kernel object results.
|
||||
"""
|
||||
self._xml_id = 0
|
||||
self.wrap = wrap
|
||||
|
||||
# create a wrapper for an existing kernel object
|
||||
if wrap > 0:
|
||||
self._xml_id = wrap
|
||||
|
||||
# create an XML tree by parsing a file, and possibly
|
||||
# preprocessing it first
|
||||
elif src:
|
||||
self._xml_id = _cantera.xml_get_XML_File(src, debug)
|
||||
self.wrap = 1 # disable deleting
|
||||
|
||||
# create a new empty node
|
||||
else:
|
||||
self._xml_id = _cantera.xml_new(name)
|
||||
|
||||
def __del__(self):
|
||||
"""Delete the node. Does nothing if this node is only a wrapper."""
|
||||
if not self.wrap:
|
||||
_cantera.xml_del(self._xml_id)
|
||||
|
||||
def tag(self):
|
||||
return _cantera.xml_tag(self._xml_id)
|
||||
|
||||
def id(self):
|
||||
"""Return the id attribute if one exists, or else the empty string."""
|
||||
try:
|
||||
return self['id']
|
||||
except:
|
||||
return ''
|
||||
|
||||
def nChildren(self):
|
||||
"""Number of child elements."""
|
||||
return _cantera.xml_nChildren(self._xml_id)
|
||||
|
||||
def children(self,tag=""):
|
||||
"""Return a list of all child elements, or just those with a specified
|
||||
tag name.
|
||||
"""
|
||||
nch = self.nChildren()
|
||||
children = []
|
||||
for n in range(nch):
|
||||
m = _cantera.xml_childbynumber(self._xml_id, n)
|
||||
ch = XML_Node(wrap = m)
|
||||
if (tag == "" or ch.tag() == tag):
|
||||
children.append(ch)
|
||||
return children
|
||||
|
||||
def removeChild(self, child):
|
||||
"""Remove a child and all its descendants."""
|
||||
_cantera.xml_removeChild(self._xml_id, child._xml_id)
|
||||
|
||||
def addChild(self, name, value=""):
|
||||
"""Add a child with tag 'name', and set its value if the value
|
||||
parameter is supplied."""
|
||||
if type(value) <> types.StringType:
|
||||
v = `value`
|
||||
else:
|
||||
v = value
|
||||
m = _cantera.xml_addChild(self._xml_id, name, v)
|
||||
return XML_Node(wrap = m)
|
||||
|
||||
def hasAttrib(self, key):
|
||||
x = self.attrib(key)
|
||||
if x: return 1
|
||||
else: return 0
|
||||
|
||||
def attrib(self, key):
|
||||
"""Return attribute 'key', or the empty string if this attribute
|
||||
does not exist."""
|
||||
try:
|
||||
return _cantera.xml_attrib(self._xml_id, key)
|
||||
except:
|
||||
return ''
|
||||
|
||||
def addAttrib(self, key, value):
|
||||
"""Add attribute 'key' with value 'value'."""
|
||||
_cantera.xml_addAttrib(self._xml_id, key, value)
|
||||
|
||||
def addComment(self, comment):
|
||||
"""Add a comment."""
|
||||
_cantera.xml_addComment(self._xml_id, comment)
|
||||
|
||||
def value(self, loc=""):
|
||||
"""Return the value of this node, or, if
|
||||
the loc argument is supplied, of the node with relative
|
||||
address 'loc'."""
|
||||
if loc:
|
||||
node = self.child(loc)
|
||||
return node.value()
|
||||
else:
|
||||
return _cantera.xml_value(self._xml_id)
|
||||
|
||||
def child(self, loc="", id="", name=""):
|
||||
if loc:
|
||||
m = _cantera.xml_child(self._xml_id, loc)
|
||||
elif id:
|
||||
m = _cantera.xml_findID(self._xml_id, id)
|
||||
elif name:
|
||||
m = _cantera.xml_findByName(self._xml_id, name)
|
||||
|
||||
ch = XML_Node(wrap=m)
|
||||
return ch
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Get an attribute using the syntax node[key]"""
|
||||
return self.attrib(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Set a new attribute using the syntax node[key] = value."""
|
||||
return self.addAttrib(key, value)
|
||||
|
||||
def __int__(self):
|
||||
"""Conversion to integer."""
|
||||
return self._xml_id
|
||||
|
||||
def __call__(self, loc=''):
|
||||
"""Get the value using the syntax node(loc)."""
|
||||
return self.value(loc)
|
||||
|
||||
def write(self, file):
|
||||
_cantera.xml_write(self._xml_id, file)
|
||||
|
||||
def __repr__(self):
|
||||
tmp = tempfile.mktemp('.xml')
|
||||
self.write(tmp)
|
||||
f = open(tmp)
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
s = ''
|
||||
for line in lines:
|
||||
s += line
|
||||
return s
|
||||
|
||||
def clear_XML():
|
||||
_cantera.xml_clear()
|
||||
|
||||
|
||||
def getFloatArray(node, convert_units=0):
|
||||
sz = int(node['size'])
|
||||
return _cantera.ctml_getFloatArray(node._xml_id, convert_units, sz)
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
"""
|
||||
Cantera provides capabilities for simulating problems involving
|
||||
chemical kinetics and transport processes.
|
||||
"""
|
||||
|
||||
import types
|
||||
import _cantera
|
||||
from num import *
|
||||
from constants import *
|
||||
from exceptions import *
|
||||
from gases import *
|
||||
from set import set
|
||||
from importFromFile import *
|
||||
|
||||
import os as _os
|
||||
import sys as _sys
|
||||
|
||||
__version__ = _cantera.ct_get_version()
|
||||
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"\nThis version of the Cantera Python module is deprecated and will not be\n"
|
||||
"available in Cantera 2.2 or later. For details on the new module, see\n"
|
||||
"http://cantera.github.io/dev-docs/sphinx/html/cython/index.html\n",
|
||||
stacklevel=2)
|
||||
|
||||
if not os.getenv('PYTHON_CMD'):
|
||||
# Setting PYTHON_CMD here avoids issues with .cti -> .xml conversions
|
||||
# in cases where the python interpreter isn't in the system path.
|
||||
os.environ['PYTHON_CMD'] = _sys.executable
|
||||
|
||||
def writeCSV(f, lst):
|
||||
"""
|
||||
Write list items to file *f* in
|
||||
comma-separated-value format. Strings will be written as-is, and
|
||||
other types of objects will be converted to strings and then
|
||||
written. Each call to writeCSV writes one line of the file.
|
||||
"""
|
||||
for i,item in enumerate(lst):
|
||||
if type(item) == types.StringType:
|
||||
f.write(item)
|
||||
else:
|
||||
f.write(repr(item))
|
||||
if i != len(lst)-1:
|
||||
f.write(',')
|
||||
|
||||
f.write('\n')
|
||||
|
||||
|
||||
def table(keys, values):
|
||||
"""Create a map with the keys and values specified."""
|
||||
x = {}
|
||||
pairs = map(None, keys, values)
|
||||
for p in pairs:
|
||||
k, v = p
|
||||
x[k] = v
|
||||
return x
|
||||
|
||||
def getCanteraError():
|
||||
"""Return the Cantera error message, if any."""
|
||||
return _cantera.get_Cantera_Error()
|
||||
|
||||
def refCount(a):
|
||||
"""Return the reference count for an object."""
|
||||
return _cantera.ct_refcnt(a)
|
||||
|
||||
def addDirectory(dir):
|
||||
"""Add a directory to search for Cantera data files."""
|
||||
return _cantera.ct_addDirectory(dir)
|
||||
|
||||
def writeLogFile(file):
|
||||
return _cantera.ct_writelogfile(file)
|
||||
|
||||
|
||||
def reset():
|
||||
"""Release all cached Cantera data. Equivalent to
|
||||
starting a fresh session."""
|
||||
_cantera.ct_appdelete()
|
||||
|
||||
def fix_docs(cls):
|
||||
"""
|
||||
Inherit method docstrings from parent class if none is specified on the
|
||||
child. Usable as a decorator in Python >= 2.6.
|
||||
"""
|
||||
for name, func in vars(cls).items():
|
||||
if not func.__doc__:
|
||||
for parent in cls.__bases__:
|
||||
parfunc = getattr(parent, name)
|
||||
if parfunc and getattr(parfunc, '__doc__', None):
|
||||
func.__doc__ = parfunc.__doc__
|
||||
break
|
||||
return cls
|
||||
|
||||
|
||||
# workaround for case problems in CVS repository file Mixture.py. On some
|
||||
# systems it appears as mixture.py, and on others as Mixture.py
|
||||
try:
|
||||
from Mixture import Mixture
|
||||
except:
|
||||
from mixture import Mixture
|
||||
|
||||
from num import *
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import _cantera
|
||||
|
||||
"""
|
||||
Convert a Chemkin-format input file to CTI format.
|
||||
Parameters:
|
||||
|
||||
infile - name of the Chemkin-format input file.
|
||||
|
||||
thermodb - Thermodynamic database. This may be a standard
|
||||
Chemkin-format thermo database, or may be any
|
||||
Chemkin-format input file containing a THERMO section.
|
||||
|
||||
trandb - Transport database. File containing species transport
|
||||
parameters in Chemkin format. If this argument is omitted,
|
||||
the CTI file will not contain transport property information.
|
||||
|
||||
idtag - ID tag. Used to identify the ideal_gas entry in the CTI file. Optional.
|
||||
|
||||
debug - If set to 1, extra debugging output will be written. This
|
||||
should only be used if ck2cti fails, in order to view
|
||||
intermediate output of the parser. Default: off (0).
|
||||
|
||||
validate - If set to 1, the mechanism will be checked for errors. This
|
||||
is recommended, but for very large mechanisms may slow down
|
||||
the conversion process. Default: on (1).
|
||||
|
||||
The translated file is written to the standard output.
|
||||
"""
|
||||
|
||||
def ck2cti(infile = "chem.inp", thermodb = "", trandb
|
||||
= "", idtag = "", debug = 0, validate = 1):
|
||||
_cantera.ct_ck2cti(infile,
|
||||
thermodb, trandb, idtag, debug, validate)
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
"""
|
||||
Physical Constants
|
||||
|
||||
These values are the same as those in the C++ header file ct_defs.h in
|
||||
the Cantera kernel.
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
#: One atmosphere in Pascals
|
||||
OneAtm = 101325.0
|
||||
|
||||
#: The ideal gas constant in J/kmo-K
|
||||
GasConstant = 8314.4621
|
||||
|
||||
#: Avogadro's Number, /kmol
|
||||
Avogadro = 6.02214129e26
|
||||
|
||||
#: The ideal gas constant in cal/mol-K
|
||||
GasConst_cal_mol_K = GasConstant / 4184.0
|
||||
|
||||
#: Boltzmann-s constant
|
||||
Boltzmann = GasConstant / Avogadro
|
||||
|
||||
#: The Stefan-Boltzmann constant, W/m^2K^4
|
||||
StefanBoltz = 5.670373e-8
|
||||
|
||||
#: The charge on an electron (C)
|
||||
ElectronCharge = 1.602176565e-19
|
||||
|
||||
#: The mass of an electron (kg)
|
||||
ElectronMass = 9.10938291e-31
|
||||
|
||||
Pi = math.pi
|
||||
|
||||
#: Faraday's constant, C/kmol
|
||||
Faraday = ElectronCharge * Avogadro
|
||||
|
||||
#: Planck's constant (J/s)
|
||||
Planck = 6.62607009e-34
|
||||
|
||||
#: Speed of Light (m/s).
|
||||
lightSpeed = 299792458.0
|
||||
|
||||
#: Permeability of free space :math:`\mu_0` in N/A^2.
|
||||
permeability_0 = 4.0e-7*Pi ## N/A^2
|
||||
|
||||
#: Permittivity of free space
|
||||
epsilon_0 = 1.0 / (lightSpeed*lightSpeed*permeability_0) ## Farads/m = C^2/N/m^2
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
"""
|
||||
Atomic elements.
|
||||
|
||||
"""
|
||||
|
||||
def elementMoles(s, element):
|
||||
"""Number of moles of an element in one mole of a solution.
|
||||
|
||||
s -- an object representing a solution.
|
||||
element -- the symbol for an element in 's'.
|
||||
"""
|
||||
# see if 'element' corresponds to a symbol for one of the elements
|
||||
# in s. If it does not, return zero moles.
|
||||
try:
|
||||
m = s.elementIndex(element)
|
||||
if m < 0.0: return 0.0
|
||||
except:
|
||||
return 0.0
|
||||
|
||||
x = s.moleFractions()
|
||||
moles = 0.0
|
||||
for k in range(s.nSpecies()):
|
||||
moles += x[k]*s.nAtoms(k,m)
|
||||
return moles
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
"""EXCEL CSV file utilities."""
|
||||
|
||||
def write_CSV_data(fname, names, npts, nvar, append, data):
|
||||
"""
|
||||
Write CSV data that can be imported into Excel
|
||||
|
||||
fname -- file name
|
||||
names -- sequence of variable names
|
||||
npts -- number of data points
|
||||
nvar -- number of variables
|
||||
append -- if > 0, append to plot file, otherwise overwrite
|
||||
data -- object to generate plot data. This object must have a
|
||||
method 'value', defined so that data.value(j,n) returns
|
||||
the value of variable n at point j.
|
||||
"""
|
||||
|
||||
if append > 0:
|
||||
f = open(fname,'a')
|
||||
else:
|
||||
f = open(fname,'w')
|
||||
for nm in names:
|
||||
f.write(nm+',')
|
||||
f.write('\n')
|
||||
for j in range(npts):
|
||||
for n in range(nvar):
|
||||
f.write('%10.4e, ' % data.value(j,n))
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
"""
|
||||
Cantera exceptions
|
||||
"""
|
||||
|
||||
import _cantera
|
||||
|
||||
def getCanteraError():
|
||||
"""
|
||||
Get an error message generated when Cantera throws an exception.
|
||||
"""
|
||||
return _cantera.get_Cantera_Error()
|
||||
|
||||
class CanteraError(Exception):
|
||||
def __init__(self, msg = ""):
|
||||
if msg == "":
|
||||
msg = _cantera.get_Cantera_Error()
|
||||
self.msg = msg
|
||||
def __str__(self):
|
||||
print '\n\n\n####################### CANTERA ERROR ######################\n'
|
||||
print ' ',self.msg
|
||||
print '\n##############################################################\n'
|
||||
|
||||
|
||||
class OptionError(CanteraError):
|
||||
def __init__(self, msg):
|
||||
self.msg = 'Unknown option: '+msg
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
"""Gas mixtures.
|
||||
|
||||
These functions all return instances of class Solution that represent
|
||||
gas mixtures.
|
||||
|
||||
"""
|
||||
# for pydoc
|
||||
import solution, constants
|
||||
|
||||
from constants import *
|
||||
from Cantera.solution import Solution
|
||||
|
||||
#import _cantera
|
||||
import os
|
||||
|
||||
def IdealGasMix(src="", id = "", loglevel = 0):
|
||||
"""Return a :class:`.Solution` object representing an ideal gas mixture.
|
||||
|
||||
:param src:
|
||||
input file
|
||||
:param id:
|
||||
XML id tag for phase
|
||||
"""
|
||||
return Solution(src=src,id=id,loglevel=loglevel)
|
||||
|
||||
|
||||
def GRI30(transport = ""):
|
||||
"""Return a :class:`.Solution` instance implementing reaction mechanism
|
||||
GRI-Mech 3.0."""
|
||||
if transport == "":
|
||||
return Solution(src="gri30.cti", id="gri30")
|
||||
elif transport == "Mix":
|
||||
return Solution(src="gri30.cti", id="gri30_mix")
|
||||
elif transport == "Multi":
|
||||
return Solution(src="gri30.cti", id="gri30_multi")
|
||||
|
||||
|
||||
def Air():
|
||||
"""Return a :class:`.Solution` instance implementing the O/N/Ar portion of
|
||||
reaction mechanism GRI-Mech 3.0. The initial composition is set to
|
||||
that of air"""
|
||||
return Solution(src="air.cti", id="air")
|
||||
|
||||
|
||||
def Argon():
|
||||
"""Return a :class:`.Solution` instance representing pure argon."""
|
||||
return Solution(src="argon.cti", id="argon")
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
"""Functions to import phase and interface definitions from CTI or
|
||||
CTML files. This module is imported when the Cantera package is
|
||||
imported, and therfore does not need to be explicitly imported in
|
||||
application programs."""
|
||||
|
||||
import solution
|
||||
import Interface
|
||||
import Edge
|
||||
import XML
|
||||
|
||||
def importPhase(file, name = '', loglevel = 0, debug = 0):
|
||||
"""Import one phase from an input file. If 'name' is specified, the
|
||||
phase definition with this name will be imported, otherwise the first
|
||||
phase definition in the file will be imported. If 'loglevel' is set to
|
||||
a positive integer, additional information will be printed or written
|
||||
to log files about the details of the object constructed.
|
||||
"""
|
||||
return importPhases(file, [name], loglevel, debug)[0]
|
||||
|
||||
def importPhases(file, names = [], loglevel = 0, debug = 0):
|
||||
"""Import multiple phases from one file. The phase names should be
|
||||
entered as a list of strings. See: importPhase """
|
||||
s = []
|
||||
for nm in names:
|
||||
s.append(solution.Solution(src=file,id=nm,loglevel=loglevel,debug=debug))
|
||||
return s
|
||||
|
||||
def importInterface(file, name = '', phases = []):
|
||||
"""Import an interface definition from input file 'file', and return
|
||||
an instance of class Interface implementing this definition. If 'name'
|
||||
is specified, the definition by this name will be imported; otherwise, the
|
||||
first interface definition in the file will be imported.
|
||||
|
||||
The 'phases' argument is a list of objects representing the other phases
|
||||
that participate in the interfacial reactions, for example an object
|
||||
representing a gas phase or a solid.
|
||||
|
||||
>>> gas1, cryst1 = importPhases('diamond.cti', ['gas', 'solid'])
|
||||
>>> diamond_surf = importInterface('diamond.cti', [gas1, cryst1])
|
||||
|
||||
Note the difference between the lists in the argument lists of these
|
||||
two functions. In importPhases, a list of name strings is entered,
|
||||
which are used to identify the appropriate definitions in the input
|
||||
file to build the objects. In importInterface, the list is of the
|
||||
objects that were built by importPhases. The reason these objects must be
|
||||
given as inputs is that these objects will be queried when phase
|
||||
properties (temperature, pressure, composition,
|
||||
electric potential) are needed to compute the reaction rates of progress.
|
||||
"""
|
||||
if name:
|
||||
src = file+'#'+name
|
||||
else:
|
||||
src = file
|
||||
return Interface.Interface(src = src, phases = phases)
|
||||
|
||||
|
||||
def importEdge(file, name = '', surfaces = []):
|
||||
if name:
|
||||
src = file+'#'+name
|
||||
else:
|
||||
src = file
|
||||
return Edge.Edge(src = src, surfaces = surfaces)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
|
||||
def interp(z0, z, f):
|
||||
"""
|
||||
Linear interpolation.
|
||||
|
||||
Sequences z and f must be of the same length,
|
||||
and the entries in z must be monotonically increasing.
|
||||
|
||||
Example:
|
||||
>>> z = [0.0, 0.2, 0.5, 1.2, 2.1]
|
||||
>>> f = [3.0, 2.0, 1.0, 0.0, -1.0]
|
||||
>>>print interp(-2, z, f), interp(0.5, z, f), interp(6, z, f)
|
||||
3.0 7.0 -9.0
|
||||
"""
|
||||
|
||||
n = len(z)
|
||||
|
||||
# if z0 is outside the range of z, then return the endpoint value,
|
||||
# instead of extrapolating.
|
||||
if z0 <= z[0]:
|
||||
return f[0]
|
||||
elif z0 > z[-1]:
|
||||
return f[-1]
|
||||
|
||||
for i in range(1,n):
|
||||
if z0 <= z[i]:
|
||||
return f[i-1] + (f[i] - f[i-1])*(z0 - z[i-1])/(z[i] - z[i-1])
|
||||
|
||||
# if this statement is reached, then there is an error.
|
||||
raise 'interpolation error!'
|
||||
|
||||
|
||||
def quadInterp(z0, z, f):
|
||||
|
||||
n = len(z)
|
||||
|
||||
# if z0 is outside the range of z, then return the endpoint value,
|
||||
# instead of extrapolating.
|
||||
if z0 <= z[0]:
|
||||
return f[0]
|
||||
elif z0 > z[-1]:
|
||||
return f[-1]
|
||||
|
||||
for i in range(1,n):
|
||||
if z0 <= z[i]:
|
||||
j = max(2,i)
|
||||
dx21 = z[j-1] - z[j-2]
|
||||
dx32 = z[j] - z[j-1]
|
||||
dx31 = dx21 + dx32
|
||||
dy32 = f[j] - f[j-1]
|
||||
dy21 = f[j-1] - f[j-2]
|
||||
a = (dx21*dy32 - dy21*dx32)/(dx21*dx31*dx32)
|
||||
return a*(z0 - z[j-2])*(z0 - z[j-1]) + (
|
||||
(dy21/dx21)*(z0 - z[j-1]) + f[j-1])
|
||||
|
||||
# if this statement is reached, then there is an error.
|
||||
raise 'interpolation error!'
|
||||
|
||||
## if __name__ == '__main__':
|
||||
## z = [0.0, 0.2, 0.5, 1.2, 2.1]
|
||||
## f = [3.0, 5.0, 11.0, 0.0, -9.0]
|
||||
|
||||
## print interp(-2, z, f), interp(0.3, z, f), interp(6, z, f)
|
||||
## print quadInterp(-2, z, f), quadInterp(0.3, z, f), quadInterp(6, z, f)
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
"""Fluids with complete liquid/vapor equations of state..
|
||||
|
||||
These functions are defined for convenience only. They simply call
|
||||
function 'importPhase' to import the phase definition from file
|
||||
'liquidvapor.cti' """
|
||||
|
||||
from importFromFile import importPhase
|
||||
|
||||
import os
|
||||
|
||||
from constants import *
|
||||
from ThermoPhase import ThermoPhase
|
||||
from set import setByName
|
||||
import XML
|
||||
import _cantera
|
||||
|
||||
|
||||
class PureFluid(ThermoPhase):
|
||||
"""
|
||||
A class for chemically-reacting solutions.
|
||||
|
||||
Instances can be created to represent any type of solution -- a
|
||||
mixture of gases, a liquid solution, or a solid solution, for
|
||||
example.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, src="", id=""):
|
||||
|
||||
self.ckin = 0
|
||||
self._owner = 0
|
||||
self.verbose = 1
|
||||
fname = os.path.basename(src)
|
||||
ff = os.path.splitext(fname)
|
||||
|
||||
if src:
|
||||
root = XML.XML_Node(name = 'doc', src = src, preprocess = 1)
|
||||
|
||||
if id:
|
||||
s = root.child(id = id)
|
||||
|
||||
else:
|
||||
s = root.child(name = "phase")
|
||||
|
||||
self._name = s['id']
|
||||
|
||||
# initialize the equation of state
|
||||
ThermoPhase.__init__(self, xml_phase=s)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
ThermoPhase.__del__(self)
|
||||
|
||||
def __repr__(self):
|
||||
return _cantera.phase_report(self._phase_id, self.verbose)
|
||||
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def set(self, **options):
|
||||
"""Set various properties.
|
||||
T --- temperature [K]
|
||||
P --- pressure [Pa]
|
||||
Rho --- density [kg/m3]
|
||||
V --- specific volume [m3/kg]
|
||||
H --- specific enthalpy [J/kg]
|
||||
U --- specific internal energy [J/kg]
|
||||
S --- specific entropy [J/kg/K]
|
||||
X --- mole fractions (string or array)
|
||||
Y --- mass fractions (string or array)
|
||||
Vapor --- saturated vapor fraction
|
||||
Liquid --- saturated liquid fraction
|
||||
"""
|
||||
setByName(self, options)
|
||||
|
||||
def critTemperature(self):
|
||||
"""Critical temperature [K]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,50)
|
||||
|
||||
def critPressure(self):
|
||||
"""Critical pressure [Pa]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,51)
|
||||
|
||||
def critDensity(self):
|
||||
"""Critical density [kg/m3]."""
|
||||
return _cantera.thermo_getfp(self._phase_id,52)
|
||||
|
||||
def vaporFraction(self):
|
||||
"""Vapor fraction."""
|
||||
return _cantera.thermo_getfp(self._phase_id,53)
|
||||
|
||||
def setState_Psat(self, p, vaporFraction):
|
||||
"""Set the state of a saturated liquid/vapor mixture by
|
||||
specifying the pressure and vapor fraction."""
|
||||
_cantera.thermo_setfp(self._phase_id,8, p, vaporFraction)
|
||||
|
||||
def setState_Tsat(self, t, vaporFraction):
|
||||
"""Set the state of a saturated liquid/vapor mixture by
|
||||
specifying the temperature and vapor fraction."""
|
||||
_cantera.thermo_setfp(self._phase_id,7, t, vaporFraction)
|
||||
|
||||
|
||||
|
||||
def Water():
|
||||
return PureFluid('liquidvapor.cti','water')
|
||||
|
||||
def Nitrogen():
|
||||
return PureFluid('liquidvapor.cti','nitrogen')
|
||||
|
||||
def Methane():
|
||||
return PureFluid('liquidvapor.cti','methane')
|
||||
|
||||
def Hydrogen():
|
||||
return PureFluid('liquidvapor.cti','hydrogen')
|
||||
|
||||
def Oxygen():
|
||||
return PureFluid('liquidvapor.cti','oxygen')
|
||||
|
||||
def HFC134a():
|
||||
return PureFluid('liquidvapor.cti','hfc134a')
|
||||
|
||||
def CarbonDioxide():
|
||||
return PureFluid('liquidvapor.cti','carbondioxide')
|
||||
|
||||
def Heptane():
|
||||
return PureFluid('liquidvapor.cti','heptane')
|
||||
|
|
@ -1,406 +0,0 @@
|
|||
"""
|
||||
Multiphase mixtures.
|
||||
"""
|
||||
|
||||
import _cantera
|
||||
import types
|
||||
from Cantera.num import zeros, array, asarray
|
||||
from exceptions import CanteraError
|
||||
from Cantera import writeLogFile
|
||||
|
||||
class Mixture:
|
||||
"""
|
||||
Multiphase mixtures. Class Mixture represents
|
||||
mixtures of one or more phases of matter. To construct a mixture,
|
||||
supply a list of phases to the constructor, each paired with the
|
||||
number of moles for that phase:
|
||||
|
||||
>>> gas = importPhase('gas.cti')
|
||||
>>> gas.speciesNames()
|
||||
['H2', 'H', 'O2', 'O', 'OH']
|
||||
>>> graphite = importPhase('graphite.cti')
|
||||
>>> graphite.speciesNames()
|
||||
['C(g)']
|
||||
>>> mix = Mixture([(gas, 1.0), (graphite, 0.1)])
|
||||
>>> mix.speciesNames()
|
||||
['H2', 'H', 'O2', 'O', 'OH', 'C(g)']
|
||||
|
||||
Note that the objects representing each phase compute only the
|
||||
intensive state of the phase -- they do not store any information
|
||||
on the amount of this phase. Mixture objects, on the other hand, represent
|
||||
the full extensive state.
|
||||
|
||||
Mixture objects are 'lightweight' in the sense that they do not
|
||||
store parameters needed to compute thermodynamic or kinetic
|
||||
properties of the phases. These are contained in the
|
||||
('heavyweight') phase objects. Multiple mixture objects may be
|
||||
constructed using the same set of phase objects. Each one stores
|
||||
its own state information locally, and synchronizes the phases
|
||||
objects whenever it requires phase properties.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, phases=[]):
|
||||
self.__mixid = _cantera.mix_new()
|
||||
self._spnames = []
|
||||
self._phases = []
|
||||
if phases:
|
||||
for p in phases:
|
||||
try:
|
||||
ph = p[0]
|
||||
moles = p[1]
|
||||
except:
|
||||
ph = p
|
||||
if p == phases[0]:
|
||||
moles = 1
|
||||
else:
|
||||
moles = 0
|
||||
self._addPhase(ph, moles)
|
||||
self._phases.append(ph)
|
||||
_cantera.mix_init(self.__mixid)
|
||||
self.setTemperature(self._phases[0].temperature())
|
||||
self.setPressure(self._phases[0].pressure())
|
||||
|
||||
def __del__(self):
|
||||
"""Delete the Mixture instance. The phase objects are not deleted."""
|
||||
_cantera.mix_del(self.__mixid)
|
||||
|
||||
def __str__(self):
|
||||
s = ''
|
||||
for p in range(len(self._phases)):
|
||||
s += '\n******************* Phase '+self._phases[p].name()+' ******************************\n'
|
||||
s += '\n Moles: '+`self.phaseMoles(p)`+'\n'
|
||||
s += self._phases[p].__repr__()+'\n\n'
|
||||
return s
|
||||
|
||||
def _addPhase(self, phase = None, moles = 0.0):
|
||||
"""Add a phase to the mixture."""
|
||||
for k in range(phase.nSpecies()):
|
||||
self._spnames.append(phase.speciesName(k))
|
||||
_cantera.mix_addPhase(self.__mixid, phase.thermo_hndl(), moles)
|
||||
|
||||
def nPhases(self):
|
||||
"""Total number of phases defined for the mixture."""
|
||||
return len(self._phases)
|
||||
|
||||
def phase(self, n):
|
||||
"""Return the object representing the nth phase in the mixture."""
|
||||
return self._phases[n]
|
||||
|
||||
def phaseName(self, n):
|
||||
"""Name of phase *n*."""
|
||||
return self._phases[n].name()
|
||||
|
||||
def phaseNames(self):
|
||||
"""Names of all phases in the order added."""
|
||||
np = self.nPhases()
|
||||
nm = []
|
||||
for n in range(np):
|
||||
nm.append(self.phaseName(n))
|
||||
return nm
|
||||
|
||||
def phaseIndex(self, phase):
|
||||
"""Index of phase with name *phase*"""
|
||||
np = self.nPhases()
|
||||
if type(phase) <> types.StringType:
|
||||
return phase
|
||||
for n in range(np):
|
||||
if self.phaseName(n) == phase:
|
||||
return n
|
||||
return -1
|
||||
|
||||
def nElements(self):
|
||||
"""Total number of elements present in the mixture."""
|
||||
return _cantera.mix_nElements(self.__mixid)
|
||||
|
||||
def elementIndex(self, element):
|
||||
"""Index of element with name 'element'.
|
||||
|
||||
>>> mix.elementIndex('H')
|
||||
2
|
||||
"""
|
||||
if type(element) == types.StringType:
|
||||
return _cantera.mix_elementIndex(self.__mixid, element)
|
||||
else:
|
||||
return element
|
||||
|
||||
def nSpecies(self):
|
||||
"""Total number of species present in the mixture. This is the
|
||||
sum of the numbers of species in each phase."""
|
||||
return _cantera.mix_nSpecies(self.__mixid)
|
||||
|
||||
def speciesName(self, k):
|
||||
"""Name of the species with index *k*. Note that index numbers
|
||||
are assigned in order as phases are added."""
|
||||
return self._spnames[k]
|
||||
|
||||
def speciesNames(self):
|
||||
n = self.nSpecies()
|
||||
s = []
|
||||
for k in range(n):
|
||||
s.append(self.speciesName(k))
|
||||
return s
|
||||
|
||||
def speciesIndex(self, species):
|
||||
"""Index of species with name *species*. If *species* is not a string,
|
||||
then it is simply returned."""
|
||||
if type(species) == types.StringType:
|
||||
return self._spnames.index(species)
|
||||
else:
|
||||
return species
|
||||
|
||||
def nAtoms(self, k, m):
|
||||
"""Number of atoms of element *m* in species *k*. Both the species and
|
||||
the element may be referenced either by name or by index number.
|
||||
|
||||
>>> n = mix.nAtoms('CH4','H')
|
||||
4.0
|
||||
|
||||
"""
|
||||
kk = self.speciesIndex(k)
|
||||
mm = self.elementIndex(m)
|
||||
return _cantera.mix_nAtoms(self.__mixid, kk, mm)
|
||||
|
||||
def setTemperature(self, t):
|
||||
"""Set the temperature [K]. The temperatures of all phases are
|
||||
set to this value, holding the pressure fixed."""
|
||||
return _cantera.mix_setTemperature(self.__mixid, t)
|
||||
|
||||
def temperature(self):
|
||||
"""The temperature [K]."""
|
||||
return _cantera.mix_temperature(self.__mixid)
|
||||
|
||||
def minTemp(self):
|
||||
"""The minimum temperature for which all species in
|
||||
multi-species solutions have valid thermo data. Stoichiometric
|
||||
phases are not considered in determining minTemp. """
|
||||
return _cantera.mix_minTemp(self.__mixid)
|
||||
|
||||
def maxTemp(self):
|
||||
"""The maximum temperature for which all species in
|
||||
multi-species solutions have valid thermo data. Stoichiometric
|
||||
phases are not considered in determining maxTemp. """
|
||||
return _cantera.mix_maxTemp(self.__mixid)
|
||||
|
||||
def charge(self):
|
||||
"""The total charge in Coulombs, summed over all phases."""
|
||||
return _cantera.mix_charge(self.__mixid)
|
||||
|
||||
def phaseCharge(self, p):
|
||||
"""The charge of phase *p* (Coulombs)."""
|
||||
return _cantera.mix_phaseCharge(self.__mixid, p)
|
||||
|
||||
def setPressure(self, p):
|
||||
"""Set the pressure [Pa]. The pressures of all phases are set
|
||||
to the specified value, holding the temperature fixed."""
|
||||
return _cantera.mix_setPressure(self.__mixid, p)
|
||||
|
||||
def pressure(self):
|
||||
"""The pressure [Pa]."""
|
||||
return _cantera.mix_pressure(self.__mixid)
|
||||
|
||||
def phaseMoles(self, n = -1):
|
||||
"""Moles of phase *n*."""
|
||||
if n == -1:
|
||||
np = self.nPhases()
|
||||
moles = zeros(np,'d')
|
||||
for m in range(np):
|
||||
moles[m] = _cantera.mix_phaseMoles(self.__mixid, m)
|
||||
return moles
|
||||
else:
|
||||
return _cantera.mix_phaseMoles(self.__mixid, n)
|
||||
|
||||
def setPhaseMoles(self, n, moles):
|
||||
"""Set the number of moles of phase *n*."""
|
||||
_cantera.mix_setPhaseMoles(self.__mixid, n, moles)
|
||||
|
||||
def setSpeciesMoles(self, moles):
|
||||
"""Set the moles of the species [kmol]. The moles may be
|
||||
specified either as a string, or as an array. If an array is
|
||||
used, it must be dimensioned at least as large as the total
|
||||
number of species in the mixture. Note that the species may
|
||||
belong to any phase, and unspecified species are set to zero.
|
||||
|
||||
>>> mix.setSpeciesMoles('C(s):1.0, CH4:2.0, O2:0.2')
|
||||
|
||||
"""
|
||||
if type(moles) == types.StringType:
|
||||
_cantera.mix_setMolesByName(self.__mixid, moles)
|
||||
else:
|
||||
_cantera.mix_setMoles(self.__mixid, asarray(moles))
|
||||
|
||||
def speciesMoles(self, species = ""):
|
||||
"""Moles of species k."""
|
||||
moles = zeros(self.nSpecies(),'d')
|
||||
for k in range(self.nSpecies()):
|
||||
moles[k] = _cantera.mix_speciesMoles(self.__mixid, k)
|
||||
return self.selectSpecies(moles, species)
|
||||
|
||||
def elementMoles(self, m):
|
||||
"""Total number of moles of element *m*, summed over all species.
|
||||
The element may be referenced either by index number or by name.
|
||||
"""
|
||||
mm = self.elementIndex(m)
|
||||
return _cantera.mix_elementMoles(self.__mixid, mm)
|
||||
|
||||
def chemPotentials(self, species=[]):
|
||||
"""The chemical potentials of all species [J/kmol]."""
|
||||
mu = zeros(self.nSpecies(),'d')
|
||||
_cantera.mix_getChemPotentials(self.__mixid, mu)
|
||||
return self.selectSpecies(mu, species)
|
||||
|
||||
def set(self, **p):
|
||||
for o in p.keys():
|
||||
v = p[o]
|
||||
if o == 'T' or o == 'Temperature':
|
||||
self.setTemperature(v)
|
||||
elif o == 'P' or o == 'Pressure':
|
||||
self.setPressure(v)
|
||||
elif o == 'Moles' or o == 'N':
|
||||
self.setSpeciesMoles(v)
|
||||
else:
|
||||
raise CanteraError("unknown property: "+o)
|
||||
|
||||
def equilibrate(self, XY = "TP", err = 1.0e-9,
|
||||
maxsteps = 1000, maxiter = 200, loglevel = 0):
|
||||
"""Set the mixture to a state of chemical equilibrium.
|
||||
|
||||
This method uses a version of the VCS algorithm to find the
|
||||
composition that minimizes the total Gibbs free energy of the
|
||||
mixture, subject to element conservation constraints. For a
|
||||
description of the theory, see Smith and Missen, "Chemical
|
||||
Reaction Equilibrium." The VCS algorithm is implemented in
|
||||
Cantera kernel class ``MultiPhaseEquil``.
|
||||
|
||||
The VCS algorithm solves for the equilibrium composition for
|
||||
specified temperature and pressure. If any other property pair
|
||||
other than ``TP`` is specified, then an outer iteration loop is
|
||||
used to adjust T and/or P so that the specified property
|
||||
values are obtained.
|
||||
|
||||
:param XY:
|
||||
Two-letter string specifying the two properties to hold fixed.
|
||||
Currently, ``'TP'``, ``'HP'``, and ``'SP'`` are implemented.
|
||||
Default: ``'TP'``.
|
||||
:param err:
|
||||
Error tolerance. Iteration will continue until (Delta mu)/RT is
|
||||
less than this value for each reaction. Default: 1.0e-9. Note that
|
||||
this default is very conservative, and good equilibrium solutions
|
||||
may be obtained with larger error tolerances.
|
||||
:param maxsteps:
|
||||
Maximum number of steps to take while solving the equilibrium
|
||||
problem for specified *T* and *P*. Default: 1000.
|
||||
:param maxiter:
|
||||
Maximum number of temperature and/or pressure iterations.
|
||||
This is only relevant if a property pair other than (T,P) is
|
||||
specified. Default: 200.
|
||||
:param loglevel:
|
||||
Controls the amount of diagnostic output. If loglevel = 0, no
|
||||
diagnostic output is written. For values > 0, more detailed
|
||||
information is written to the log file as loglevel increases.
|
||||
The default is loglevel = 0.
|
||||
The logfile is written in HTML format, and may be viewed with
|
||||
any web browser. The default log file name is
|
||||
``equilibrium_log.html``, but if this file exists, the log
|
||||
information will be written to "equilibrium_log{n}.html", where
|
||||
{n} is an integer chosen so that the log file does not already
|
||||
exist. Therefore, if 'equilibrate' is called multiple times,
|
||||
multiple log files will be written, with names
|
||||
``equilibrate_log.html``, ``equilibrate_log1.html``,
|
||||
``equilibrate_log2.html``, and so on. Existing log files will
|
||||
not be overwritten.
|
||||
|
||||
>>> mix.equilibrate('TP')
|
||||
>>> mix.equilibrate('TP', err = 1.0e-6, maxiter = 500)
|
||||
|
||||
"""
|
||||
i = _cantera.mix_equilibrate(self.__mixid, XY, err, maxsteps,
|
||||
maxiter, loglevel)
|
||||
|
||||
def vcs_equilibrate(self, XY = "TP", estimateEquil = 0, printLvl = 0,
|
||||
solver = 2, rtol = 1.0e-9,
|
||||
maxsteps = 1000, maxiter = 1000, loglevel = 0):
|
||||
"""Set the mixture to a state of chemical equilibrium.
|
||||
|
||||
This method uses a version of the VCS algorithm to find the
|
||||
composition that minimizes the total Gibbs free energy of the
|
||||
mixture, subject to element conservation constraints. For a
|
||||
description of the theory, see Smith and Missen, "Chemical
|
||||
Reaction Equilibrium." The VCS algorithm is implemented in
|
||||
Cantera kernel class MultiPhaseEquil.
|
||||
|
||||
The VCS algorithm solves for the equilibrium composition for
|
||||
specified temperature and pressure. If any other property pair
|
||||
other than ``'TP'`` is specified, then an outer iteration loop is
|
||||
used to adjust T and/or P so that the specified property
|
||||
values are obtained.
|
||||
|
||||
:param XY:
|
||||
Two-letter string specifying the two properties to hold fixed.
|
||||
Currently, ``'TP'``, ``'HP'``, and ``'SP'`` are implemented.
|
||||
Default: ``'TP'``.
|
||||
:param printLvl:
|
||||
Controls the amount of diagnostic output written to cout. If
|
||||
printLvl = 0, no diagnostic output is written. For values > 0,
|
||||
more detailed information is written to cout.
|
||||
The default is printLvl = 0.
|
||||
:param solver:
|
||||
Determines which solver is used.
|
||||
- 1 MultiPhaseEquil solver
|
||||
- 2 VCSnonideal Solver (default)
|
||||
:param err:
|
||||
Error tolerance. Iteration will continue until (Delta mu)/RT is
|
||||
less than this value for each reaction. Default: 1.0e-9. Note that
|
||||
this default is very conservative, and good equilibrium solutions
|
||||
May be obtained with larger error tolerances.
|
||||
:param maxsteps:
|
||||
Maximum number of steps to take while solving the equilibrium
|
||||
problem for specified T and P. Default: 1000.
|
||||
:param maxiter:
|
||||
Maximum number of temperature and/or pressure iterations. This is
|
||||
only relevant if a property pair other than (T,P) is specified.
|
||||
Default: 200.
|
||||
:param loglevel:
|
||||
Controls the amount of diagnostic output written to html. If
|
||||
loglevel = 0, no diagnostic output is written. For values > 0,
|
||||
more detailed information is written to the log file as
|
||||
loglevel increases. The default is loglevel = 0.
|
||||
The logfile is written in HTML format, and may be viewed with
|
||||
any web browser. The default log file name is
|
||||
"equilibrium_log.html", but if this file exists, the log
|
||||
information will be written to "equilibrium_log{n}.html",
|
||||
where {n} is an integer chosen so that the log file does not
|
||||
already exist. Therefore, if 'equilibrate' is called multiple
|
||||
times, multiple log files will be written, with names
|
||||
"equilibrate_log.html", "equilibrate_log1.html",
|
||||
"equilibrate_log2.html", and so on. Existing log files will
|
||||
not be overwritten.
|
||||
"""
|
||||
i = _cantera.mix_vcs_equilibrate(self.__mixid, XY, estimateEquil,
|
||||
printLvl, solver, rtol, maxsteps,
|
||||
maxiter, loglevel)
|
||||
|
||||
def selectSpecies(self, f, species):
|
||||
"""Given an array *f* of floating-point species properties,
|
||||
return an array of those values corresponding to species
|
||||
listed in *species*. This method is used internally to implement
|
||||
species selection in methods like :meth:`~.Phase.moleFractions`,
|
||||
:meth:`~.Phase.massFractions`, etc.
|
||||
|
||||
>>> f = mix.chemPotentials()
|
||||
>>> muo2, muh2 = mix.selectSpecies(f, ['O2', 'H2'])
|
||||
"""
|
||||
sp = []
|
||||
if species:
|
||||
if type(species) == types.StringType:
|
||||
sp = [species]
|
||||
else:
|
||||
sp = species
|
||||
fs = []
|
||||
k = 0
|
||||
for s in sp:
|
||||
k = self.speciesIndex(s)
|
||||
fs.append(f[k])
|
||||
return asarray(fs)
|
||||
else:
|
||||
return f
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import _cantera
|
||||
nummodule = None
|
||||
|
||||
try:
|
||||
if _cantera.nummod == 'numpy':
|
||||
import numpy
|
||||
nummodule = numpy
|
||||
elif _cantera.nummod == 'numarray':
|
||||
import numarray
|
||||
nummodule = numarray
|
||||
else:
|
||||
import Numeric
|
||||
nummodule = Numeric
|
||||
except:
|
||||
print """
|
||||
|
||||
ERROR: """+_cantera.nummod+""" not found!
|
||||
|
||||
Cantera uses a set of numerical extensions to Python, but these do
|
||||
not appear to be present on your system. To install the required
|
||||
package, go to http://sourceforge.net/projects/numpy, and install
|
||||
either the """+_cantera.nummod+""" package for your system. If you are
|
||||
using a Windows system, use the binary installer to install the
|
||||
selected package for you automatically.
|
||||
|
||||
"""
|
||||
raise "could not import "+_cantera.nummod
|
||||
|
||||
zeros = nummodule.zeros
|
||||
array = nummodule.array
|
||||
asarray = nummodule.asarray
|
||||
transpose = nummodule.transpose
|
||||
ravel = nummodule.ravel
|
||||
shape = nummodule.shape
|
||||
ones = nummodule.ones
|
||||
log10 = nummodule.log10
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
"""
|
||||
Reaction path diagrams.
|
||||
|
||||
To create a simple reaction path diagram:
|
||||
|
||||
>>> import rxnpath
|
||||
>>> element = 'C'
|
||||
>>> rxnpath.write(gas, element, file)
|
||||
|
||||
Object 'gas' must an instance of a class derived from class 'Kinetics'
|
||||
(for example class IdealGasMix). The diagram layout is written to
|
||||
'file'. The output must be postprocessed with program 'dot', which is
|
||||
part of the GraphViz package. To create a Postscript plot:
|
||||
|
||||
dot -Tps rp.dot > rp.ps
|
||||
|
||||
Other output formats are also supported by dot, including gif, pcl,
|
||||
jpg, png, and svg
|
||||
|
||||
For more control over the graph properties, create a PathDiagam object
|
||||
and pass it to the 'write' procedure.
|
||||
|
||||
PathDiagram keyword options:
|
||||
|
||||
diagram type:
|
||||
-- detailed 'true' or 'false'
|
||||
-- type 'both' or 'net' (forward and reverse arrows,
|
||||
or net arrow)
|
||||
-- dot_options options passed through to 'dot'
|
||||
|
||||
colors:
|
||||
-- normal_color color for normal-weight lines
|
||||
-- bold_color color for bold-weight lines
|
||||
-- dashed_color color for dashed lines
|
||||
|
||||
thresholds:
|
||||
-- threshold min relative strength for a path to be shown
|
||||
-- normal_threshold min relative strength for normal-weight path
|
||||
Below this value, paths are dashed.
|
||||
-- bold_threshold min relative strength for bold-weight path
|
||||
|
||||
"""
|
||||
|
||||
import _cantera
|
||||
|
||||
class PathDiagram:
|
||||
def __init__(self, **options):
|
||||
self.__rdiag_id = _cantera.rdiag_new()
|
||||
self.setOptions({"detailed":"true",
|
||||
"dashed_color":"gray",
|
||||
"bold_color":"red",
|
||||
"normal_color":"steelblue",
|
||||
"scale":-1,
|
||||
"dot_options":'center=1;margin=0;size="5,6";page="5,6";ratio=compress;fontname=Arial;',
|
||||
"title":"-",
|
||||
"arrow_width":-5,
|
||||
"threshold":0.001,
|
||||
"bold_threshold":0.2,
|
||||
"normal_threshold":0.01,
|
||||
"label_threshold":0.001,
|
||||
"flow_type":"net"}
|
||||
)
|
||||
self.setOptions(options)
|
||||
|
||||
def __del__(self):
|
||||
_cantera.rdiag_del(self.__rdiag_id)
|
||||
|
||||
def id(self):
|
||||
return self.__rdiag_id
|
||||
|
||||
def write(self, fmt, file):
|
||||
_cantera.rdiag_write(self.__rdiag_id, fmt, file)
|
||||
|
||||
def add(self, other):
|
||||
_cantera.rdiag_add(self.__rdiag_id, other.id())
|
||||
|
||||
def findMajorPaths(self, a, threshold = 0.0):
|
||||
_cantera.rdiag_findMajor(self.__rdiag_id, threshold, a)
|
||||
|
||||
def displayOnly(self, node=-1):
|
||||
_cantera.rdiag_displayOnly(self.__rdiag_id, node)
|
||||
|
||||
def setOptions(self, options):
|
||||
for o in options.keys():
|
||||
v = options[o]
|
||||
if o == "detailed":
|
||||
if v == "true":
|
||||
_cantera.rdiag_detailed(self.__rdiag_id)
|
||||
elif v == "false":
|
||||
_cantera.rdiag_brief(self.__rdiag_id)
|
||||
elif o == "dashed_color":
|
||||
_cantera.rdiag_setDashedColor(self.__rdiag_id, v)
|
||||
elif o == "bold_color":
|
||||
_cantera.rdiag_setBoldColor(self.__rdiag_id, v)
|
||||
elif o == "normal_color":
|
||||
_cantera.rdiag_setNormalColor(self.__rdiag_id, v)
|
||||
elif o == "scale":
|
||||
_cantera.rdiag_setScale(self.__rdiag_id, v)
|
||||
elif o == "dot_options":
|
||||
_cantera.rdiag_setDotOptions(self.__rdiag_id, v)
|
||||
elif o == "title":
|
||||
_cantera.rdiag_setTitle(self.__rdiag_id, v)
|
||||
elif o == "arrow_width":
|
||||
_cantera.rdiag_setArrowWidth(self.__rdiag_id, v)
|
||||
elif o == "threshold":
|
||||
_cantera.rdiag_setThreshold(self.__rdiag_id, v)
|
||||
elif o == "bold_threshold":
|
||||
_cantera.rdiag_setBoldThreshold(self.__rdiag_id, v)
|
||||
elif o == "normal_threshold":
|
||||
_cantera.rdiag_setNormalThreshold(self.__rdiag_id, v)
|
||||
elif o == "label_threshold":
|
||||
_cantera.rdiag_setLabelThreshold(self.__rdiag_id, v)
|
||||
elif o == "font":
|
||||
_cantera.rdiag_setFont(self.__rdiag_id, v)
|
||||
elif o == "flow_type":
|
||||
if v == "one_way":
|
||||
_cantera.rdiag_setFlowType(self.__rdiag_id, 0)
|
||||
else:
|
||||
_cantera.rdiag_setFlowType(self.__rdiag_id, 1)
|
||||
else:
|
||||
raise("unknown attribute "+o)
|
||||
|
||||
class PathBuilder:
|
||||
|
||||
def __init__(self, kin, logfile=""):
|
||||
if logfile == "":
|
||||
logfile = "rxnpath.log"
|
||||
self.__rbuild_id = _cantera.rbuild_new()
|
||||
self.kin = kin
|
||||
_cantera.rbuild_init(self.__rbuild_id, logfile, kin.ckin)
|
||||
|
||||
def __del__(self):
|
||||
_cantera.rbuild_del(self.__rbuild_id)
|
||||
|
||||
def build(self, diagram = None, element = "C",
|
||||
dotfile = "rxnpaths.dot", format="dot"):
|
||||
if diagram == None:
|
||||
diagram = PathDiagram()
|
||||
_cantera.rbuild_build(self.__rbuild_id, self.kin.ckin, element,
|
||||
"buildlog", diagram.id(), 1)
|
||||
if format == "dot":
|
||||
diagram.write(0, dotfile)
|
||||
diagram.write(1, "rp.txt")
|
||||
elif format == "plain":
|
||||
diagram.write(1, dotfile)
|
||||
|
||||
|
||||
def write(g, el, file, d=None, format="dot"):
|
||||
b = PathBuilder(g)
|
||||
b.build(element = el, diagram = d, dotfile = file, format = format)
|
||||
|
||||
|
||||
def view(url):
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from Cantera.gases import GRI30
|
||||
gas = GRI30()
|
||||
x = [1.0] * gas.nSpecies()
|
||||
gas.setState_TPX(1800.0, 1.01325e5, x)
|
||||
write(gas, 'C', 'c:/users/dgg/test.dot')
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
from exceptions import CanteraError
|
||||
|
||||
def setByName(a, options):
|
||||
"""Set properties of phase 'a' by specifying keywords. Either the full
|
||||
property name or the short form may be given. The capitalization must
|
||||
be exactly as shown here.
|
||||
|
||||
Note: all extensive property values are specified for a unit mass
|
||||
- i.e., the *specific* (not molar) property value should be
|
||||
specified.
|
||||
|
||||
keyword short form property
|
||||
--------------------------------------------
|
||||
Pressure P pressure
|
||||
Density Rho density
|
||||
Temperature T temperature
|
||||
Volume V specific volume
|
||||
MoleFractions X mole fractions
|
||||
MassFracttions Y mass fractions
|
||||
Enthalpy H specific enthalpy
|
||||
IntEnergy U specific internal energy
|
||||
Entropy S specific entropy
|
||||
Vapor Vap vapor fraction in a two-phase mixture
|
||||
Liquid Liq liquid fraction in a two-phase mixture
|
||||
|
||||
|
||||
"""
|
||||
|
||||
tval = None
|
||||
pval = None
|
||||
hval = None
|
||||
uval = None
|
||||
sval = None
|
||||
vval = None
|
||||
qval = None
|
||||
|
||||
np = 0
|
||||
nt = 0
|
||||
nv = 0
|
||||
nx = 0
|
||||
ny = 0
|
||||
ns = 0
|
||||
nh = 0
|
||||
nu = 0
|
||||
nq = 0
|
||||
|
||||
for o in options.keys():
|
||||
val = options[o]
|
||||
if o == 'Temperature' or o == 'T':
|
||||
nt += 1
|
||||
tval = val
|
||||
elif o == 'Density' or o == 'Rho':
|
||||
nv += 1
|
||||
vval = 1.0/val
|
||||
elif o == 'Volume' or o == 'V':
|
||||
nv += 1
|
||||
vval = val
|
||||
elif o == 'MoleFractions' or o == 'X':
|
||||
nx += 1
|
||||
a.setMoleFractions(val)
|
||||
elif o == 'MassFractions' or o == 'Y':
|
||||
ny += 1
|
||||
a.setMassFractions(val)
|
||||
elif o == 'Pressure' or o == 'P':
|
||||
pval = val
|
||||
np += 1
|
||||
elif o == 'Enthalpy' or o == 'H':
|
||||
hval = val
|
||||
nh += 1
|
||||
elif o == 'IntEnergy' or o == 'U':
|
||||
uval = val
|
||||
nu += 1
|
||||
elif o == 'Entropy' or o == 'S':
|
||||
sval = val
|
||||
ns += 1
|
||||
elif o == 'Vapor' or o == 'Vap':
|
||||
nq += 1
|
||||
qval = val
|
||||
elif o == 'Liquid' or o == 'Liq':
|
||||
nq += 1
|
||||
qval = 1.0 - val
|
||||
|
||||
else:
|
||||
raise CanteraError('unknown property: '+o)
|
||||
|
||||
if nx + ny > 1:
|
||||
raise CanteraError('composition specified multiple times')
|
||||
|
||||
nn = [nt, np, nv, ns, nh, nu, nq]
|
||||
for n in nn:
|
||||
if n > 1:
|
||||
raise CanteraError('property specified multiple times')
|
||||
|
||||
ntot = nt + np + nv + ns + nh + nu + nq
|
||||
|
||||
# set individual properties
|
||||
if ntot == 1:
|
||||
if nt == 1:
|
||||
a.setTemperature(tval)
|
||||
elif nv == 1:
|
||||
a.setDensity(1.0/vval)
|
||||
elif np == 1:
|
||||
a.setPressure(pval)
|
||||
else:
|
||||
props = options.keys()
|
||||
raise CanteraError('property '+props[0]+
|
||||
' can only be set in combination with '
|
||||
+'another property')
|
||||
# set property pairs
|
||||
elif ntot == 2:
|
||||
if np == 1 and nh == 1:
|
||||
a.setState_HP(hval, pval)
|
||||
elif nu == 1 and nv == 1:
|
||||
a.setState_UV(uval, vval)
|
||||
elif ns == 1 and np == 1:
|
||||
a.setState_SP(sval, pval)
|
||||
elif ns == 1 and nv == 1:
|
||||
a.setState_SV(sval, vval)
|
||||
elif nt == 1 and np == 1:
|
||||
a.setState_TP(tval, pval)
|
||||
elif nt == 1 and nv == 1:
|
||||
a.setState_TR(tval, 1.0/vval)
|
||||
elif nt == 1 and nq == 1:
|
||||
a.setState_Tsat(tval, qval)
|
||||
elif np == 1 and nq == 1:
|
||||
a.setState_Psat(pval, qval)
|
||||
else:
|
||||
raise CanteraError('unimplemented property pair')
|
||||
|
||||
|
||||
def set(a, **options):
|
||||
setByName(a, options)
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
"""
|
||||
"""
|
||||
|
||||
import string
|
||||
import os
|
||||
|
||||
from constants import *
|
||||
from ThermoPhase import ThermoPhase
|
||||
from Kinetics import Kinetics
|
||||
from SolidTransport import SolidTransport
|
||||
import XML
|
||||
import _cantera
|
||||
|
||||
class Solid(ThermoPhase, Kinetics, SolidTransport):
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, src="", root=None):
|
||||
|
||||
self.ckin = 0
|
||||
self._owner = 0
|
||||
self.verbose = 1
|
||||
|
||||
# get the 'phase' element
|
||||
s = XML.find_XML(src=src, root=root, name="phase")
|
||||
|
||||
# get the equation of state model
|
||||
ThermoPhase.__init__(self, xml_phase=s)
|
||||
|
||||
# get the kinetics model
|
||||
Kinetics.__init__(self, xml_phase=s, phases=[self])
|
||||
|
||||
SolidTransport.__init__(self, phase=self)
|
||||
|
||||
#self.setState_TP(300.0, OneAtm)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return _cantera.phase_report(self._phase_id, self.verbose)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
SolidTransport.__del__(self)
|
||||
Kinetics.__del__(self)
|
||||
ThermoPhase.__del__(self)
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
"""This module defines classes and functions used to model gas mixtures."""
|
||||
|
||||
from solution import Solution
|
||||
|
||||
def Solid(src="",
|
||||
kmodel=1, transport=None):
|
||||
return Solution(import_file=import_file,
|
||||
thermo_db="",
|
||||
eos=0,
|
||||
id=id,
|
||||
kmodel=kmodel,
|
||||
trmodel=transport,
|
||||
validate=0)
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
|
||||
import os
|
||||
|
||||
from constants import *
|
||||
from ThermoPhase import ThermoPhase
|
||||
from Kinetics import Kinetics
|
||||
from Transport import Transport
|
||||
from set import setByName
|
||||
import XML
|
||||
import _cantera
|
||||
|
||||
class Solution(ThermoPhase, Kinetics, Transport):
|
||||
"""
|
||||
A class for chemically-reacting solutions.
|
||||
|
||||
Instances can be created to represent any type of solution -- a
|
||||
mixture of gases, a liquid solution, or a solid solution, for
|
||||
example.
|
||||
|
||||
Class Solution derives from classes :class:`.ThermoPhase`, :class:`.Kinetics`,
|
||||
and :class:`.Transport`. It defines very few methods of its own, and is
|
||||
provided largely for convenience, so that a single object can be
|
||||
used to compute thermodynamic, kinetic, and transport properties
|
||||
of a solution. Functions like :func:`.IdealGasMix` and others defined in
|
||||
module gases return objects of class :class:`.Solution`.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, src="", id="", loglevel = 0, debug = 0):
|
||||
|
||||
self.ckin = 0
|
||||
self._owner = 0
|
||||
self.verbose = 1
|
||||
fname = os.path.basename(src)
|
||||
ff = os.path.splitext(fname)
|
||||
|
||||
if src:
|
||||
root = XML.XML_Node(name = 'doc', src = src,
|
||||
preprocess = 1, debug = debug)
|
||||
|
||||
if id:
|
||||
s = root.child(id = id)
|
||||
|
||||
else:
|
||||
s = root.child(name = "phase")
|
||||
|
||||
self._name = s['id']
|
||||
|
||||
# initialize the equation of state
|
||||
ThermoPhase.__init__(self, xml_phase=s)
|
||||
|
||||
# initialize the kinetics model
|
||||
ph = [self]
|
||||
Kinetics.__init__(self, xml_phase=s, phases=ph)
|
||||
|
||||
# initialize the transport model
|
||||
Transport.__init__(self, xml_phase=s, phase=self,
|
||||
model = '', loglevel=loglevel)
|
||||
|
||||
def __del__(self):
|
||||
Transport.__del__(self)
|
||||
Kinetics.__del__(self)
|
||||
ThermoPhase.__del__(self)
|
||||
|
||||
def __repr__(self):
|
||||
return _cantera.phase_report(self._phase_id, self.verbose)
|
||||
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def set(self, **options):
|
||||
"""Set various properties.
|
||||
|
||||
:param T:
|
||||
temperature [K]
|
||||
:param P:
|
||||
pressure [Pa]
|
||||
:param Rho:
|
||||
density [kg/m3]
|
||||
:param V:
|
||||
specific volume [m3/kg]
|
||||
:param H:
|
||||
specific enthalpy [J/kg]
|
||||
:param U:
|
||||
specific internal energy [J/kg]
|
||||
:param S:
|
||||
specific entropy [J/kg/K]
|
||||
:param X:
|
||||
mole fractions (string or array)
|
||||
:param Y:
|
||||
mass fractions (string or array)
|
||||
:param Vapor:
|
||||
saturated vapor fraction
|
||||
:param Liquid:
|
||||
saturated liquid fraction
|
||||
"""
|
||||
setByName(self, options)
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
""" Solve a steady-state problem by combined damped Newton iteration
|
||||
and time integration. Function solve is no longer used, now that the
|
||||
functional equivalent has been added to the Cantera C++ kernel. """
|
||||
|
||||
from Cantera import CanteraError
|
||||
from Cantera.num import array
|
||||
import math, types
|
||||
|
||||
print
|
||||
"""
|
||||
module solve is deprecated, and may be removed in a future release. If you
|
||||
use it and do not want it removed, send an e-mail to cantera-help@caltech.edu.
|
||||
"""
|
||||
|
||||
def solve(sim, loglevel = 0, refine_grid = 1, plotfile = '', savefile = ''):
|
||||
"""
|
||||
Solve a steady-state problem by combined damped Newton iteration
|
||||
and time integration.
|
||||
"""
|
||||
|
||||
new_points = 1
|
||||
|
||||
# get options
|
||||
dt = sim.option('timestep')
|
||||
ft = sim.option('ftime')
|
||||
|
||||
# sequence of timesteps
|
||||
_steps = sim.option('nsteps')
|
||||
if type(_steps) == types.IntType: _steps = [_steps]
|
||||
|
||||
len_nsteps = len(_steps)
|
||||
dt = sim.option('timestep')
|
||||
|
||||
ll = loglevel
|
||||
soln_number = -1
|
||||
max_timestep = sim.option('max_timestep')
|
||||
|
||||
sim.collect()
|
||||
|
||||
# loop until refine adds no more points
|
||||
while new_points > 0:
|
||||
|
||||
istep = 0
|
||||
nsteps = _steps[istep]
|
||||
|
||||
# loop until Newton iteration succeeds
|
||||
ok = 0
|
||||
while ok == 0:
|
||||
|
||||
# Try to solve the steady-state problem by damped
|
||||
# Newton iteration.
|
||||
try:
|
||||
if loglevel > 0:
|
||||
print 'Attempt Newton solution of ',\
|
||||
'steady-state problem...',
|
||||
sim.newton_solve(loglevel-1)
|
||||
|
||||
if loglevel > 0:
|
||||
print 'success.\n\n'
|
||||
print '%'*79+'\n'
|
||||
print 'Problem solved on ',sim.npts,' point grid(s).\n'
|
||||
print '%'*79+'\n'
|
||||
ok = 1
|
||||
soln_number += 1
|
||||
sim.finish()
|
||||
|
||||
|
||||
except CanteraError:
|
||||
|
||||
# Newton iteration failed.
|
||||
if loglevel > 0: print '\n'
|
||||
|
||||
# Take nsteps time steps, starting with step size
|
||||
# dt. The final dt may be smaller than the initial
|
||||
# value if one or more steps fail.
|
||||
|
||||
if loglevel == 1:
|
||||
print 'Take',nsteps,' timesteps',
|
||||
|
||||
dt = sim.py_timeStep(nsteps,dt,loglevel=ll-1)
|
||||
if loglevel == 1: print dt, math.log10(sim.ssnorm())
|
||||
istep += 1
|
||||
if istep >= len_nsteps:
|
||||
nsteps = _steps[-1]
|
||||
dt *= 2.0
|
||||
else:
|
||||
nsteps = _steps[istep]
|
||||
if dt > max_timestep: dt = max_timestep
|
||||
|
||||
|
||||
|
||||
# A converged solution was found. Save and/or plot it, then
|
||||
# check whether the grid should be refined.
|
||||
|
||||
# Add the solution to the plot file
|
||||
if plotfile:
|
||||
sim.outputTEC(plotfile,"flame","p"+`sim.npts`,append=soln_number)
|
||||
|
||||
# If a filename has been specified for a save file, add
|
||||
# the solution to this file
|
||||
if savefile:
|
||||
sim.save(savefile, soln_name+'_'+`sim.npts`+'_points')
|
||||
|
||||
if loglevel > 2: sim.show()
|
||||
|
||||
if refine_grid:
|
||||
|
||||
# Call refine to add new points, if needed
|
||||
new_points = sim.refine(loglevel = loglevel - 1)
|
||||
|
||||
else:
|
||||
new_points = 0
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
from Cantera import exceptions
|
||||
from Cantera.num import array
|
||||
from Cantera.elements import elementMoles
|
||||
|
||||
def det3(A):
|
||||
"""Determinant of a 3x3 matrix."""
|
||||
return (A[0,0]*(A[1,1]*A[2,2] - A[1,2]*A[2,1])
|
||||
- A[0,1]*(A[1,0]*A[2,2] - A[1,2]*A[2,0])
|
||||
+ A[0,2]*(A[1,0]*A[2,1] - A[2,0]*A[1,1]))
|
||||
|
||||
def stoich_fuel_to_oxidizer(mix, fuel, oxidizer):
|
||||
"""Fuel to oxidizer ratio for stoichiometric combustion.
|
||||
|
||||
This function only works for fuels composed of carbon, hydrogen,
|
||||
and/or oxygen. The fuel to oxidizer ratio is returned that results in
|
||||
|
||||
"""
|
||||
|
||||
# fuel
|
||||
mix.setMoleFractions(fuel)
|
||||
f_carbon = elementMoles(mix, 'C')
|
||||
f_oxygen = elementMoles(mix, 'O')
|
||||
f_hydrogen = elementMoles(mix, 'H')
|
||||
|
||||
#oxidizer
|
||||
mix.setMoleFractions(oxidizer)
|
||||
o_carbon = elementMoles(mix, 'C')
|
||||
o_oxygen = elementMoles(mix, 'O')
|
||||
o_hydrogen = elementMoles(mix, 'H')
|
||||
|
||||
B = array([f_carbon, f_hydrogen, f_oxygen],'d')
|
||||
A = array([[1.0, 0.0, -o_carbon],
|
||||
[0.0, 2.0, -o_hydrogen],
|
||||
[2.0, 1.0, -o_oxygen]], 'd')
|
||||
|
||||
num = array(A,'d')
|
||||
num[:,2] = B
|
||||
r = det3(num)/det3(A)
|
||||
if r <= 0.0:
|
||||
raise CanteraError('negative or zero computed stoichiometric fuel/oxidizer ratio!')
|
||||
return 1.0/r
|
||||
|
||||
if __name__ == "__main__":
|
||||
g = GRI30()
|
||||
print stoich_fuel_to_oxidizer(g, 'CH4:1', 'O2:1')
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
"""TECPLOT utilities."""
|
||||
|
||||
def write_TECPLOT_zone(fname, title, zone, names, npts, nvar, append, data):
|
||||
"""
|
||||
Write a TECPLOT zone specification to generate line plots of multiple
|
||||
variables.
|
||||
fname -- file name
|
||||
title -- plot title
|
||||
zone -- zone name
|
||||
names -- sequence of variable names
|
||||
npts -- number of data points
|
||||
nvar -- number of variables
|
||||
append -- if > 0, append to plot file, otherwise overwrite
|
||||
data -- object to generate plot data. This object must have a
|
||||
method 'value', defined so that data.value(j,n) returns
|
||||
the value of variable n at point j.
|
||||
"""
|
||||
|
||||
if append > 0:
|
||||
f = open(fname,'a')
|
||||
else:
|
||||
f = open(fname,'w')
|
||||
f.write('TITLE = "' + title + '"\n')
|
||||
f.write('VARIABLES = \n')
|
||||
for nm in names:
|
||||
f.write('"'+nm+'"\n')
|
||||
f.write('ZONE T="'+zone+'"\n')
|
||||
f.write(' I='+`npts`+',J=1,K=1,F=POINT\n')
|
||||
f.write('DT=(')
|
||||
for n in range(nvar):
|
||||
f.write('SINGLE ')
|
||||
f.write(')\n')
|
||||
for j in range(npts):
|
||||
for n in range(nvar):
|
||||
f.write('%10.4e ' % data[j,n])
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
"""Conversion factors to SI (m, kg, kmol, s)"""
|
||||
|
||||
from constants import Avogadro, GasConstant
|
||||
|
||||
kmol = 1.0
|
||||
mol = 1.e-3
|
||||
molecule = kmol/Avogadro
|
||||
|
||||
m = 1.0
|
||||
cm = 0.01
|
||||
mm = 0.001
|
||||
|
||||
m2 = 1.0
|
||||
cm2 = 1.e-4
|
||||
mm2 = 1.e-6
|
||||
A2 = 1.e-20
|
||||
|
||||
m3 = 1.0
|
||||
cm3 = 1.e-6
|
||||
mm3 = 1.e-9
|
||||
|
||||
J = 1.0
|
||||
kJ = 1000.0
|
||||
cal = 4.184
|
||||
kcal = 4184.0
|
||||
|
||||
K = 1.0
|
||||
|
||||
kJ_per_mol = kJ/mol
|
||||
cal_per_mol = cal/mol
|
||||
kcal_per_mol = kcal/mol
|
||||
|
||||
mol_per_cm2 = mol/cm/cm
|
||||
molecule_per_cm2 = molecule/cm/cm
|
||||
|
||||
# pressure
|
||||
Pa = 1.0
|
||||
kPa = 1000.0
|
||||
atm = 1.01325e5
|
||||
bar = 1.0e5
|
||||
torr = atm/760.0
|
||||
|
||||
# mass
|
||||
kg = 1.0
|
||||
gm = 1000.0
|
||||
|
||||
# mass flux
|
||||
kg_per_m2_per_s = 1.0
|
||||
g_per_cm2_per_s = gm/(cm*cm)
|
||||
|
||||
_lengthdict = {'m':m, 'cm':cm, 'mm':mm}
|
||||
def length(u):
|
||||
return _lengthdict[u]
|
||||
|
||||
_moldict = {'kmol':kmol, 'mol':mol, 'molecule':molecule}
|
||||
def mole(u):
|
||||
return _moldict[u]
|
||||
|
||||
_eadict = {'kJ_per_mol':kJ_per_mol,
|
||||
'kcal_per_mol':kcal_per_mol,
|
||||
'cal_per_mol':cal_per_mol,
|
||||
'K':GasConstant}
|
||||
|
||||
def actEnergy(u):
|
||||
return _eadict[u]/GasConstant
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
from Tkinter import *
|
||||
from Cantera import *
|
||||
|
||||
from SpeciesInfo import SpeciesInfo
|
||||
#from KineticsFrame import KineticsFrame
|
||||
|
||||
_CUTOFF = 1.e-15
|
||||
_ATOL = 1.e-15
|
||||
_RTOL = 1.e-7
|
||||
|
||||
class CompFrame(Frame):
|
||||
def __init__(self,master):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=FLAT, bd=4)
|
||||
self.top = self.master.top
|
||||
self.controls=Frame(self)
|
||||
self.hide = IntVar()
|
||||
self.hide.set(0)
|
||||
self.comp = IntVar()
|
||||
self.comp.set(0)
|
||||
self.controls.grid(column=1,row=0,sticky=W+E+N)
|
||||
self.makeControls()
|
||||
mf = self.master
|
||||
|
||||
def makeControls(self):
|
||||
Radiobutton(self.controls,text='Moles',
|
||||
variable=self.comp,value=0,
|
||||
command=self.show).grid(column=0,row=0,sticky=W)
|
||||
Radiobutton(self.controls,text='Mass',
|
||||
variable=self.comp,value=1,
|
||||
command=self.show).grid(column=0,row=1,sticky=W)
|
||||
Radiobutton(self.controls,text='Concentration',
|
||||
variable=self.comp,value=2,
|
||||
command=self.show).grid(column=0,row=2,sticky=W)
|
||||
Button(self.controls,text='Clear',
|
||||
command=self.zero).grid(column=0,row=4,sticky=W+E)
|
||||
Button(self.controls,text='Normalize',
|
||||
command=self.norm).grid(column=0,row=5,sticky=W+E)
|
||||
Checkbutton(self.controls,text='Hide Missing\nSpecies',
|
||||
variable=self.hide,onvalue=1,
|
||||
offvalue=0,command=self.master.redo).grid(column=0,
|
||||
row=3,
|
||||
sticky=W)
|
||||
|
||||
def norm(self):
|
||||
mf = self.master
|
||||
mf.update()
|
||||
|
||||
data = mf.comp
|
||||
sum = 0.0
|
||||
for sp in data:
|
||||
sum += sp
|
||||
for i in range(len(mf.comp)):
|
||||
mf.comp[i] /= sum
|
||||
self.show()
|
||||
|
||||
def set(self):
|
||||
c = self.comp.get()
|
||||
mix = self.top.mix
|
||||
mf = self.master
|
||||
g = mix.g
|
||||
if c == 0:
|
||||
mix.setMoles(mf.comp)
|
||||
|
||||
elif c == 1:
|
||||
mix.setMass(mf.comp)
|
||||
|
||||
elif c == 2:
|
||||
pass
|
||||
self.top.thermo.setState()
|
||||
self.top.kinetics.show()
|
||||
|
||||
def show(self):
|
||||
mf = self.master
|
||||
mf.active = self
|
||||
c = self.comp.get()
|
||||
mix = self.top.mix
|
||||
g = mix.g
|
||||
if c == 0:
|
||||
mf.var.set("Moles")
|
||||
#mf.data = spdict(mix.g, mix.moles())
|
||||
mf.comp = mix.moles()
|
||||
|
||||
elif c == 1:
|
||||
mf.var.set("Mass")
|
||||
#mf.data = spdict(mix.g,mix.mass())
|
||||
mf.comp = mix.mass()
|
||||
|
||||
elif c == 2:
|
||||
mf.var.set("Concentration")
|
||||
mf.comp = mix.concentrations()
|
||||
#mf.data = spdict(mix,mix,mf.comp)
|
||||
|
||||
for s in mf.variable.keys():
|
||||
try:
|
||||
k = g.speciesIndex(s)
|
||||
if mf.comp[k] > _CUTOFF:
|
||||
mf.variable[s].set(mf.comp[k])
|
||||
else:
|
||||
mf.variable[s].set(0.0)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def zero(self):
|
||||
mf = self.master
|
||||
mf.comp *= 0.0
|
||||
self.show()
|
||||
|
||||
|
||||
|
||||
class MixtureFrame(Frame):
|
||||
def __init__(self,master,top):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.top.mixframe = self
|
||||
self.g = self.top.mix.g
|
||||
#self.scroll = Scrollbar(self)
|
||||
self.entries=Frame(self)
|
||||
#self.scroll.config(command=self.entries.xview)
|
||||
#self.scroll.grid(column=0,row=1)
|
||||
self.var = StringVar()
|
||||
self.var.set("Moles")
|
||||
self.comp = array(self.top.mix.moles())
|
||||
self.names = self.top.mix.speciesNames()
|
||||
self.nsp = len(self.names)
|
||||
#self.data = self.top.mix.moleDict()
|
||||
self.makeControls()
|
||||
self.makeEntries()
|
||||
self.entries.bind('<Double-l>',self.minimize)
|
||||
self.ctype = 0
|
||||
self.newcomp = 0
|
||||
|
||||
def makeControls(self):
|
||||
self.c = CompFrame(self)
|
||||
#self.k = KineticsFrame(self)
|
||||
self.active = self.c
|
||||
self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
#self.k.grid(column=2,row=0,sticky=E+W+N+S)
|
||||
|
||||
def update(self):
|
||||
self.newcomp = 0
|
||||
for s in self.variable.keys():
|
||||
k = self.g.speciesIndex(s)
|
||||
current = self.comp[k]
|
||||
val = self.variable[s].get()
|
||||
dv = abs(val - current)
|
||||
if dv > _RTOL*abs(current) + _ATOL:
|
||||
self.comp[k] = val
|
||||
self.newcomp = 1
|
||||
|
||||
def show(self):
|
||||
self.active.show()
|
||||
## for k in range(self.nsp):
|
||||
## sp = self.names[k]
|
||||
## if self.comp[k] > _CUTOFF:
|
||||
## self.variable[sp].set(self.comp[k])
|
||||
## else:
|
||||
## self.variable[sp].set(0.0)
|
||||
|
||||
def redo(self):
|
||||
self.update()
|
||||
self.entries.destroy()
|
||||
self.entries=Frame(self)
|
||||
self.makeEntries()
|
||||
|
||||
def minimize(self,Event=None):
|
||||
self.c.hide.set(1)
|
||||
self.redo()
|
||||
self.c.grid_forget()
|
||||
self.entries.bind("<Double-1>",self.maximize)
|
||||
|
||||
def maximize(self,Event=None):
|
||||
self.c.hide.set(0)
|
||||
self.redo()
|
||||
self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
self.entries.bind("<Double-1>",self.minimize)
|
||||
|
||||
def up(self, x):
|
||||
self.update()
|
||||
if self.newcomp:
|
||||
self.c.set()
|
||||
self.c.show()
|
||||
self.top.update()
|
||||
#thermo.showState()
|
||||
#self.top.kinetics.show()
|
||||
|
||||
def makeEntries(self):
|
||||
self.entries.grid(row=0,column=0,sticky=W+N+S+E)
|
||||
self.entries.config(relief=FLAT,bd=4)
|
||||
DATAKEYS = self.top.species
|
||||
self.variable = {}
|
||||
|
||||
n=0
|
||||
ncol = 3
|
||||
col = 0
|
||||
row = 60
|
||||
|
||||
equil = 0
|
||||
if self.top.thermo:
|
||||
equil = self.top.thermo.equil.get()
|
||||
|
||||
for sp in DATAKEYS:
|
||||
s = sp # self.top.species[sp]
|
||||
k = s.index
|
||||
if row > 25:
|
||||
row = 0
|
||||
col = col + 2
|
||||
l = Label(self.entries,text='Species')
|
||||
l.grid(column=col,row=row,sticky=E+W)
|
||||
e1 = Entry(self.entries)
|
||||
e1.grid(column=col+1,row=row,sticky=E+W)
|
||||
e1['textvariable'] = self.var
|
||||
e1.config(state=DISABLED)
|
||||
e1.config(bg='lightyellow',relief=RIDGE)
|
||||
row = row + 1
|
||||
|
||||
spname = s.name
|
||||
val = self.comp[k]
|
||||
if not self.c.hide.get() or val: showit = 1
|
||||
else: showit = 0
|
||||
|
||||
l=SpeciesInfo(self.entries,species=s,
|
||||
text=spname,relief=FLAT,justify=RIGHT,
|
||||
fg='darkblue')
|
||||
entry1 = Entry(self.entries)
|
||||
self.variable[spname] = DoubleVar()
|
||||
self.variable[spname].set(self.comp[k])
|
||||
entry1['textvariable']=self.variable[spname]
|
||||
entry1.bind('<Any-Leave>',self.up)
|
||||
if showit:
|
||||
l.grid(column= col ,row=row,sticky=E)
|
||||
entry1.grid(column=col+1,row=row)
|
||||
n=n+1
|
||||
row = row + 1
|
||||
if equil == 1:
|
||||
entry1.config(state=DISABLED,bg='lightgray')
|
||||
## if self.c.hide.get():
|
||||
## b=Button(self.entries,height=1,command=self.maximize)
|
||||
## else:
|
||||
## b=Button(self.entries,command=self.minimize)
|
||||
## b.grid(column=col,columnspan=2, row=row+1)
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
from types import *
|
||||
from Tkinter import *
|
||||
from ScrolledText import ScrolledText
|
||||
#import datawindow
|
||||
#import filewindow
|
||||
|
||||
def ff():
|
||||
print ' hi '
|
||||
|
||||
class ControlWindow(Frame):
|
||||
fncs = [ff]*10
|
||||
|
||||
def __init__(self, title, master=None):
|
||||
self.app = master
|
||||
Frame.__init__(self,master)
|
||||
self.grid(row=0,column=0,sticky=E+W+N+S)
|
||||
self.master.title(title)
|
||||
|
||||
|
||||
def addButtons(self, label, funcs):
|
||||
self.buttonholder = Frame(self, relief=FLAT, bd=2)
|
||||
self.buttonholder.pack(side=TOP,anchor=W)
|
||||
b = Label(self.buttonholder,text=label)
|
||||
b.pack(side=LEFT,fill=X)
|
||||
for f in funcs:
|
||||
b=Button(self.buttonholder,
|
||||
text=f[0],command=f[1], padx=1,pady=1)
|
||||
b.pack(side=LEFT,fill=X)
|
||||
|
||||
def disableButtons(self, *buttons):
|
||||
for button in self.buttonholder.slaves():
|
||||
if (button.cget('text') in buttons):
|
||||
try:
|
||||
button.config(state=DISABLED)
|
||||
except:
|
||||
pass
|
||||
|
||||
def enableButtons(self, *buttons):
|
||||
for button in self.buttonholder.slaves():
|
||||
if (button.cget('text') in buttons):
|
||||
try:
|
||||
button.config(state=NORMAL)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def newFrame(self, label, var):
|
||||
fr = Frame(self, relief = RIDGE, bd = 2)
|
||||
fr.pack(side=TOP,fill=X)
|
||||
c = Checkbutton(fr, variable=var)
|
||||
c.pack(side = LEFT, fill = X)
|
||||
b = Label(fr,text=label,foreground="NavyBlue")
|
||||
b.pack(side=LEFT,fill=X)
|
||||
return fr
|
||||
|
||||
##creates a new Toplevel object
|
||||
##options: transient=<callback for window close>,
|
||||
## placement=(<screen x-coord>, <screen y-coord>)
|
||||
def newWindow(self, master, title, **options):
|
||||
new = Toplevel(master)
|
||||
new.title(title)
|
||||
#new.config(takefocus=0)
|
||||
if 'transient' in options.keys():
|
||||
new.transient(master)
|
||||
if options['transient']:
|
||||
new.protocol('WM_DELETE_WINDOW', options['transient'])
|
||||
if 'placement' in options.keys():
|
||||
new.geometry("+%d+%d" % tuple(options['placement']))
|
||||
return new
|
||||
|
||||
##routes mouse and keyboard events to the window and
|
||||
##waits for it to close before returning
|
||||
def makemodal(self, window):
|
||||
window.focus_set()
|
||||
window.grab_set()
|
||||
window.wait_window()
|
||||
return
|
||||
|
||||
|
||||
def PlotMenu(self, fr, label, funcs):
|
||||
filebutton = Menubutton(fr,text=label, padx=3,pady=1)
|
||||
filebutton.pack(side=LEFT)
|
||||
filemenu = Menu(filebutton,tearoff=TRUE)
|
||||
i = 0
|
||||
for f in funcs:
|
||||
filemenu.add_command(label=f[0], command=f[1])
|
||||
i = i + 1
|
||||
filebutton['menu']=filemenu
|
||||
return filemenu
|
||||
|
||||
def testevent(event):
|
||||
print 'event ',event.value
|
||||
|
||||
def make_menu(name, menubar, list):
|
||||
nc = len(name)
|
||||
button=Menubutton(menubar, text=name, width=nc+4, padx=3,pady=1)
|
||||
button.pack(side=LEFT)
|
||||
menu = Menu(button,tearoff=FALSE)
|
||||
m = menu
|
||||
i = 0
|
||||
for entry in list:
|
||||
i += 1
|
||||
if entry == 'separator':
|
||||
menu.add_separator({})
|
||||
elif type(entry)==ListType:
|
||||
for num in entry:
|
||||
menu.entryconfig(num,state=DISABLED)
|
||||
elif type(entry[1]) != ListType:
|
||||
if i == 20:
|
||||
i = 0
|
||||
submenu = Menu(button,tearoff=FALSE)
|
||||
m.add_cascade(label='More...',
|
||||
menu=submenu)
|
||||
m = submenu
|
||||
if len(entry) == 2 or entry[2] == 'command':
|
||||
m.add_command(label=entry[0],
|
||||
command=entry[1])
|
||||
elif entry[2] == 'check':
|
||||
entry[3].set(0)
|
||||
if len(entry) >= 5: val = entry[4]
|
||||
else: val = 1
|
||||
m.add_checkbutton(label=entry[0],
|
||||
command=entry[1],
|
||||
variable = entry[3],
|
||||
onvalue=val)
|
||||
else:
|
||||
submenu=make_menu(entry[0], menu, entry[1])
|
||||
m.add_cascade(label=entry[0],
|
||||
menu=submenu)
|
||||
button['menu']=menu
|
||||
return button
|
||||
|
||||
def menuitem_state(button, *statelist):
|
||||
for menu in button.children.keys():
|
||||
if isinstance(button.children[menu], Menu):
|
||||
for (commandnum, onoff) in statelist:
|
||||
if onoff==0:
|
||||
button.children[menu].entryconfig(commandnum,state=DISABLED)
|
||||
if onoff==1:
|
||||
button.children[menu].entryconfig(commandnum,state=NORMAL)
|
||||
else:
|
||||
pass
|
||||
|
||||
class ArgumentWindow(Toplevel):
|
||||
import tkMessageBox
|
||||
def __init__(self, sim, **options):
|
||||
Toplevel.__init__(self, sim.cwin)
|
||||
self.resizable(FALSE,FALSE)
|
||||
self.protocol("WM_DELETE_WINDOW", lambda:0) #self.cancelled)
|
||||
self.transient(sim.cwin)
|
||||
if 'placement' in options.keys():
|
||||
self.geometry("+%d+%d" % tuple(options['placement']))
|
||||
self.title('Thermal Model Initialization')
|
||||
self.sim = sim
|
||||
|
||||
self.make_options()
|
||||
|
||||
buttonframe = Frame(self)
|
||||
buttonframe.pack(side=BOTTOM)
|
||||
b1=Button(buttonframe, text='OK', command=self.callback)
|
||||
b1.pack(side=LEFT)
|
||||
#b2=Button(buttonframe, text='Cancel', command=self.cancelled)
|
||||
#b2.pack(side=LEFT)
|
||||
self.bind("<Return>", self.callback)
|
||||
#self.bind("<Escape>", self.cancelled)
|
||||
|
||||
self.initial_focus = self
|
||||
self.initial_focus.focus_set()
|
||||
#self.wait_window(self)
|
||||
|
||||
def make_options(self):
|
||||
pass
|
||||
|
||||
### must override this function ###
|
||||
### with the entry forms ###
|
||||
### be sure to use pack or a ###
|
||||
### frame that is packed into self ###
|
||||
|
||||
def getArguments(self):
|
||||
pass
|
||||
|
||||
### must override this function ###
|
||||
### with the validation checking ###
|
||||
### must return None if error, ###
|
||||
### and non_null if ok ###
|
||||
|
||||
|
||||
def callback(self, event=None):
|
||||
g=self.getArguments()
|
||||
if not g:
|
||||
self.initial_focus.focus_set()
|
||||
return
|
||||
self.withdraw()
|
||||
self.update_idletasks()
|
||||
|
||||
self.assign(g)
|
||||
self.cancelled()
|
||||
|
||||
def assign(self, obj):
|
||||
pass
|
||||
|
||||
### must override this function ###
|
||||
### to do the assignment in sim ###
|
||||
|
||||
def cancelled(self,event=None):
|
||||
self.sim.cwin.focus_set()
|
||||
self.destroy()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
t = Tk()
|
||||
ControlWindow(t).mainloop()
|
||||
|
|
@ -1,359 +0,0 @@
|
|||
import os, math, string
|
||||
from Tkinter import *
|
||||
from Cantera import *
|
||||
from Cantera.num import *
|
||||
from Cantera import num
|
||||
from tkFileDialog import askopenfilename
|
||||
from GraphFrame import Graph
|
||||
from DataGraph import DataGraph, plotLimits
|
||||
from ControlPanel import make_menu
|
||||
|
||||
|
||||
U_LOC = 1
|
||||
V_LOC = 2
|
||||
T_LOC = 3
|
||||
P_LOC = 4
|
||||
Y_LOC = 5
|
||||
|
||||
def testit(e = None):
|
||||
pass
|
||||
|
||||
class DataFrame(Frame):
|
||||
|
||||
def __init__(self,master,top):
|
||||
# if master==None:
|
||||
self.master = Toplevel()
|
||||
self.master.protocol("WM_DELETE_WINDOW",self.hide)
|
||||
#else:
|
||||
# self.master = master
|
||||
|
||||
#self.vis = vis
|
||||
Frame.__init__(self,self.master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.mix = self.top.mix
|
||||
self.g = self.top.mix.g
|
||||
self.data = None
|
||||
self.zdata = None
|
||||
self.ydata = None
|
||||
self.plt = None
|
||||
#self.pltwhat = None
|
||||
self.datasets = []
|
||||
self.vars = []
|
||||
self.whichsoln = IntVar()
|
||||
self.loc = IntVar()
|
||||
# self.loc.set(1)
|
||||
self.lastloc = T_LOC # self.loc.get()
|
||||
self.datafile = StringVar()
|
||||
self.solnid = StringVar()
|
||||
self.gr = Frame(self)
|
||||
self.n = IntVar()
|
||||
|
||||
self.scframe = Frame(self)
|
||||
self.sc = Scale(self.scframe, variable = self.n,
|
||||
orient='horizontal',digits=0,
|
||||
length=300,resolution=1,command=self.updateplot)
|
||||
self.sc.config(cnf={'from':0,'to':1})
|
||||
Label(self.scframe,text='Grid Point').grid(column=0,row=0)
|
||||
self.sc.grid(row=0,column=1)
|
||||
self.sc.bind('<ButtonRelease-1>',self.updateState)
|
||||
self.gr.grid(row=4,column=0,columnspan=10)
|
||||
|
||||
self.grid(column=0,row=10)
|
||||
self.makeMenu()
|
||||
self.hide()
|
||||
|
||||
|
||||
def makeMenu(self):
|
||||
self.menubar = Frame(self, relief=GROOVE,bd=2)
|
||||
self.menubar.grid(row=0,column=0,sticky=N+W+E,columnspan=10)
|
||||
f = [('Open...',self.browseForDatafile)]
|
||||
#make_menu('File',self.menubar,items)
|
||||
make_menu('File',self.menubar,f)
|
||||
make_menu('Dataset',self.menubar,self.datasets)
|
||||
make_menu('Plot',self.menubar,self.vars)
|
||||
|
||||
|
||||
def browseForDatafile(self, e=None):
|
||||
pathname = askopenfilename(
|
||||
filetypes=[("Data Files", ("*.xml","*.csv","*.dat")),
|
||||
("All Files", "*.*")])
|
||||
if pathname:
|
||||
self.datafile.set(pathname)
|
||||
self.show()
|
||||
self.getSoln()
|
||||
|
||||
def getSoln(self):
|
||||
fname = os.path.basename(self.datafile.get())
|
||||
ff = os.path.splitext(fname)
|
||||
self.datasets = []
|
||||
if len(ff) == 2 and (ff[1] == '.xml' or ff[1] == '.ctml'):
|
||||
|
||||
x = XML.XML_Node('root',src=self.datafile.get())
|
||||
c = x.child('ctml')
|
||||
self.solns = c.children('simulation')
|
||||
if len(self.solns) > 1:
|
||||
i = 0
|
||||
for soln in self.solns:
|
||||
self.datasets.append((soln['id'],self.pickSoln,
|
||||
'check',self.whichsoln,i))
|
||||
i += 1
|
||||
self.solnid.set(self.solns[-1]['id'])
|
||||
self.soln = self.solns[-1]
|
||||
|
||||
self.importData()
|
||||
|
||||
elif len(ff) == 2 and (ff[1] == '.csv' or ff[1] == '.CSV'):
|
||||
self.importCSV()
|
||||
|
||||
self.makeMenu()
|
||||
if self.loc.get() <= 0:
|
||||
self.loc.set(self.lastloc)
|
||||
|
||||
|
||||
def importCSV(self):
|
||||
self.lastloc = self.loc.get()
|
||||
if self.lastloc <= 0: self.lastloc = T_LOC
|
||||
self.vars = []
|
||||
self.zdata = None
|
||||
self.ydata = None
|
||||
if self.plt:
|
||||
self.plt.destroy()
|
||||
|
||||
f = open(self.datafile.get(),'r')
|
||||
lines = f.readlines()
|
||||
vars = string.split(lines[0],',')
|
||||
nlines = len(lines)
|
||||
self.np = nlines - 1
|
||||
nv = len(vars)
|
||||
vv = []
|
||||
for n in range(nv):
|
||||
nm = vars[n].split()
|
||||
if n < nv - 1 or (len(nm) > 0 and nm[0].isalnum()):
|
||||
vv.append(nm[0])
|
||||
else:
|
||||
break
|
||||
nv = len(vv)
|
||||
vars = vv
|
||||
fdata = zeros((nv, self.np),'d')
|
||||
for n in range(self.np):
|
||||
v = string.split(lines[n+1],',')
|
||||
for j in range(nv):
|
||||
try:
|
||||
fdata[j,n] = float(v[j])
|
||||
except:
|
||||
fdata[j,n] = 0.0
|
||||
|
||||
self.nsp = self.g.nSpecies()
|
||||
self.y = zeros(self.nsp,'d')
|
||||
self.data = zeros((self.nsp+6,self.np),'d')
|
||||
self.data[0,:] = fdata[0,:]
|
||||
self.label = ['-']*(self.nsp+6)
|
||||
self.label[0] = vars[0]
|
||||
w = []
|
||||
for n in range(1,nv-1):
|
||||
try:
|
||||
k = self.g.speciesIndex(vars[n])
|
||||
except:
|
||||
k = -1
|
||||
v2 = vars[n]
|
||||
if v2 == 'T':
|
||||
self.data[T_LOC,:] = fdata[n,:]
|
||||
self.label[T_LOC] = vars[n]
|
||||
w.append(('T', self.newplot, 'check', self.loc, T_LOC))
|
||||
elif v2 == 'P':
|
||||
self.data[P_LOC,:] = fdata[n,:]
|
||||
self.label[P_LOC] = vars[n]
|
||||
w.append((vars[n], self.newplot, 'check', self.loc, P_LOC))
|
||||
elif v2 == 'u':
|
||||
self.data[U_LOC,:] = fdata[n,:]
|
||||
self.label[U_LOC] = vars[n]
|
||||
w.append((vars[n], self.newplot, 'check', self.loc, U_LOC))
|
||||
elif v2 == 'V':
|
||||
self.data[V_LOC,:] = fdata[n,:]
|
||||
self.label[V_LOC] = vars[n]
|
||||
w.append((vars[n], self.newplot, 'check', self.loc, V_LOC))
|
||||
elif k >= 0:
|
||||
self.data[k+Y_LOC,:] = fdata[n,:]
|
||||
self.label[k+Y_LOC] = vars[n]
|
||||
w.append((vars[n], self.newplot, 'check', self.loc, k + Y_LOC))
|
||||
|
||||
if self.data[P_LOC,0] == 0.0:
|
||||
self.data[P_LOC,:] = ones(self.np,'d')*OneAtm
|
||||
print 'Warning: no pressure data. P set to 1 atm.'
|
||||
|
||||
self.sc.config(cnf={'from':0,'to':self.np-1})
|
||||
if self.loc.get() <= 0:
|
||||
self.loc.set(self.lastloc)
|
||||
self.updateplot()
|
||||
|
||||
self.vars = w
|
||||
#self.makeMenu()
|
||||
self.scframe.grid(row=5,column=0,columnspan=10)
|
||||
|
||||
|
||||
def pickSoln(self):
|
||||
self.solnid.set(self.solns[self.whichsoln.get()]['id'])
|
||||
self.soln = self.solns[self.whichsoln.get()]
|
||||
# self.t.destroy()
|
||||
self.importData()
|
||||
|
||||
|
||||
def importData(self):
|
||||
|
||||
self.lastloc = self.loc.get()
|
||||
if self.lastloc <= 0: self.lastloc = T_LOC
|
||||
self.vars = []
|
||||
self.zdata = None
|
||||
self.ydata = None
|
||||
if self.plt:
|
||||
self.plt.destroy()
|
||||
|
||||
self.nsp = self.g.nSpecies()
|
||||
self.label = ['-']*(self.nsp + 6)
|
||||
|
||||
self.y = zeros(self.nsp,'d')
|
||||
gdata = self.soln.child('flowfield/grid_data')
|
||||
xp = self.soln.child('flowfield').children('float')
|
||||
p = 0.0
|
||||
for x in xp:
|
||||
if x['title'] == 'pressure':
|
||||
p = float(x.value())
|
||||
fa = gdata.children('floatArray')
|
||||
self.np = int(fa[0]['size'])
|
||||
|
||||
self.data = zeros((self.nsp+6,self.np),'d')
|
||||
w = []
|
||||
for f in fa:
|
||||
t = f['title']
|
||||
try:
|
||||
k = self.g.speciesIndex(t)
|
||||
except:
|
||||
k = -1
|
||||
v = XML.getFloatArray(f)
|
||||
if t == 'z' or t == 't':
|
||||
self.data[0,:] = v
|
||||
self.label[0] = t
|
||||
elif k >= 0:
|
||||
self.data[k + Y_LOC] = v
|
||||
self.label[k + Y_LOC] = t
|
||||
w.append((t, self.newplot, 'check', self.loc, k + Y_LOC))
|
||||
elif t == 'T':
|
||||
self.data[T_LOC,:] = v
|
||||
self.label[T_LOC] = t
|
||||
w.append((t, self.newplot, 'check', self.loc, T_LOC))
|
||||
elif t == 'u':
|
||||
self.data[U_LOC,:] = v
|
||||
self.label[U_LOC] = t
|
||||
w.append((t, self.newplot, 'check', self.loc, U_LOC))
|
||||
elif t == 'V':
|
||||
self.data[V_LOC,:] = v
|
||||
self.label[V_LOC] = t
|
||||
w.append((t, self.newplot, 'check', self.loc, V_LOC))
|
||||
|
||||
self.data[P_LOC,:] = ones(self.np,'d')*p
|
||||
self.label[P_LOC] = 'P (Pa)'
|
||||
self.sc.config(cnf={'from':0,'to':self.np-1})
|
||||
if self.loc.get() <= 0:
|
||||
self.loc.set(self.lastloc)
|
||||
self.updateplot()
|
||||
|
||||
self.vars = w
|
||||
self.scframe.grid(row=5,column=0,columnspan=10)
|
||||
|
||||
|
||||
def hide(self):
|
||||
#self.vis.set(0)
|
||||
self.master.withdraw()
|
||||
#if self.pltwhat: self.pltwhat.withdraw()
|
||||
|
||||
def show(self, e=None):
|
||||
self.master.deiconify()
|
||||
|
||||
def updateState(self, e=None):
|
||||
n = self.n.get()
|
||||
if self.plt: self.plt.update()
|
||||
|
||||
for k in range(self.nsp):
|
||||
self.y[k] = self.data[k+Y_LOC,n]
|
||||
|
||||
self.top.thermo.checkTPBoxes()
|
||||
self.mix.setMass(self.y)
|
||||
self.mix.set(temperature = self.data[T_LOC,n],
|
||||
pressure = self.data[P_LOC,n])
|
||||
|
||||
self.top.update()
|
||||
|
||||
def newplot(self,e=0):
|
||||
loc = self.loc.get()
|
||||
self.zdata = self.data[0,:]
|
||||
self.ydata = self.data[loc,:]
|
||||
npts = len(self.zdata)
|
||||
|
||||
ylog = 0
|
||||
if loc >= Y_LOC:
|
||||
for n in range(npts):
|
||||
if self.ydata[n] <= 0.0:
|
||||
#print n, self.ydata[n]
|
||||
self.ydata[n] = 1.0e-20
|
||||
self.ydata = num.log10(self.ydata)
|
||||
ylog = 1
|
||||
|
||||
self.gdata = []
|
||||
zmin = self.zdata[0]
|
||||
zmax = self.zdata[-1]
|
||||
for n in range(npts):
|
||||
self.gdata.append((self.zdata[n],self.ydata[n]))
|
||||
|
||||
ymin, ymax, dtick = plotLimits(self.ydata)
|
||||
if loc > 0:
|
||||
self.plt = DataGraph(self.gr,self.data, 0, loc,
|
||||
title='',
|
||||
label=(self.label[0],self.label[loc]),
|
||||
logscale=(0,ylog),
|
||||
pixelX=500,pixelY=400)
|
||||
self.plt.canvas.config(bg='white')
|
||||
self.plt.grid(row=1,column=0,columnspan=2,sticky=W+E)
|
||||
n = self.n.get()
|
||||
self.gdot = self.plt.plot(n,'red')
|
||||
|
||||
def updateplot(self,event=None):
|
||||
if self.data == None: return
|
||||
|
||||
if self.zdata == None:
|
||||
self.newplot()
|
||||
|
||||
n = self.n.get()
|
||||
self.pnt = self.zdata[n], self.ydata[n]
|
||||
if hasattr(self, 'gdot'):
|
||||
self.plt.delete(self.gdot)
|
||||
self.gdot = self.plt.plot(n,'red')
|
||||
|
||||
|
||||
def plotLimits(self, xy):
|
||||
ymax = -1.e10
|
||||
ymin = 1.e10
|
||||
for x, y in xy:
|
||||
if y > ymax: ymax = y
|
||||
if y < ymin: ymin = y
|
||||
|
||||
dy = abs(ymax - ymin)
|
||||
if dy < 0.2*ymin:
|
||||
ymin = ymin*.9
|
||||
ymax = ymax*1.1
|
||||
dy = abs(ymax - ymin)
|
||||
else:
|
||||
ymin = ymin - 0.1*dy
|
||||
ymax = ymax + 0.1*dy
|
||||
dy = abs(ymax - ymin)
|
||||
|
||||
p10 = math.floor(math.log10(0.1*dy))
|
||||
fctr = math.pow(10.0, p10)
|
||||
mm = [2.0, 2.5, 2.0]
|
||||
i = 0
|
||||
while dy/fctr > 5:
|
||||
fctr = mm[i % 3]*fctr
|
||||
i = i + 1
|
||||
ymin = fctr*math.floor(ymin/fctr)
|
||||
ymax = fctr*(math.floor(ymax/fctr + 1))
|
||||
return (ymin, ymax, fctr)
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
|
||||
from Tkinter import *
|
||||
import math
|
||||
from Cantera.num import *
|
||||
|
||||
def plotLimits(ypts, f=0.0, ndiv=5, logscale=0):
|
||||
"""Return plot limits that"""
|
||||
if logscale:
|
||||
threshold = 1.0e-19
|
||||
else:
|
||||
threshold = -1.0e20
|
||||
ymax = -1.e20
|
||||
ymin = 1.e20
|
||||
for y in ypts:
|
||||
if y > ymax: ymax = y
|
||||
if y < ymin and y > threshold: ymin = y
|
||||
|
||||
dy = abs(ymax - ymin)
|
||||
|
||||
if logscale:
|
||||
ymin = math.floor(math.log10(ymin))
|
||||
ymax = math.floor(math.log10(ymax))+1
|
||||
fctr = 1.0
|
||||
|
||||
## if dy < 0.2*ymin:
|
||||
## ymin = ymin*.9
|
||||
## ymax = ymax*1.1
|
||||
## dy = abs(ymax - ymin)
|
||||
## else:
|
||||
else:
|
||||
ymin = ymin - f*dy
|
||||
ymax = ymax + f*dy
|
||||
dy = abs(ymax - ymin)
|
||||
|
||||
try:
|
||||
p10 = math.floor(math.log10(0.1*dy))
|
||||
fctr = math.pow(10.0, p10)
|
||||
except:
|
||||
return (ymin -1.0, ymax + 1.0, 1.0)
|
||||
mm = [2.0, 2.5, 2.0]
|
||||
i = 0
|
||||
while dy/fctr > ndiv:
|
||||
fctr = mm[i % 3]*fctr
|
||||
i = i + 1
|
||||
ymin = fctr*math.floor(ymin/fctr)
|
||||
ymax = fctr*(math.floor(ymax/fctr+0.999))
|
||||
|
||||
return (ymin, ymax, fctr)
|
||||
|
||||
|
||||
class DataGraph(Frame):
|
||||
def __init__(self,master,
|
||||
data, ix=0, iy=0,
|
||||
title='',
|
||||
label = ('x-axis','y-axis'),
|
||||
logscale = (0,0),
|
||||
pixelX=500,
|
||||
pixelY=500):
|
||||
self.logscale = logscale
|
||||
self.data = data
|
||||
self.ix = ix
|
||||
self.iy = iy
|
||||
self.minX, self.maxX, self.dx = plotLimits(data[ix,:],
|
||||
logscale=self.logscale[0])
|
||||
self.minY, self.maxY, self.dy = plotLimits(data[iy,:],
|
||||
logscale=self.logscale[1])
|
||||
|
||||
Frame.__init__(self,master, relief=RIDGE, bd=2)
|
||||
self.title = Label(self,text=' ')
|
||||
self.title.grid(row=0,column=1,sticky=W+E)
|
||||
self.graph_w, self.graph_h = pixelX - 120, pixelY - 70
|
||||
self.origin = (100, 20)
|
||||
self.canvas = Canvas(self,
|
||||
width=pixelX,
|
||||
height=pixelY,
|
||||
relief=SUNKEN,bd=1)
|
||||
id = self.canvas.create_rectangle(self.origin[0],self.origin[1],
|
||||
pixelX-20,pixelY-50)
|
||||
self.canvas.grid(row=1,column=1,rowspan=2,sticky=N+S+E+W)
|
||||
self.last_points=[]
|
||||
self.ticks(self.minX, self.maxX, self.dx,
|
||||
self.minY, self.maxY, self.dy, 10)
|
||||
self.screendata()
|
||||
self.draw()
|
||||
self.canvas.create_text(self.origin[0] + self.graph_w/2,
|
||||
self.origin[1] + self.graph_h + 30,
|
||||
text=label[0],anchor=N)
|
||||
self.canvas.create_text(self.origin[0] - 50,
|
||||
self.origin[1] + self.graph_h/2,
|
||||
text=label[1],anchor=E)
|
||||
|
||||
|
||||
def writeValue(self, y):
|
||||
yval = '%15.4f' % (y)
|
||||
self.title.config(text = yval)
|
||||
|
||||
def delete(self, ids):
|
||||
for id in ids:
|
||||
self.canvas.delete(id)
|
||||
|
||||
def screendata(self):
|
||||
self.xdata = array(self.data[self.ix,:])
|
||||
self.ydata = array(self.data[self.iy,:])
|
||||
npts = len(self.ydata)
|
||||
if self.logscale[0] > 0:
|
||||
self.xdata = log10(self.xdata)
|
||||
if self.logscale[1] > 0:
|
||||
self.ydata = log10(self.ydata)
|
||||
f = float(self.graph_w)/(self.maxX-self.minX)
|
||||
self.xdata = (self.xdata - self.minX)*f + self.origin[0]
|
||||
f = float(self.graph_h)/(self.maxY-self.minY)
|
||||
self.ydata = (self.maxY - self.ydata)*f + self.origin[1]
|
||||
|
||||
def toscreen(self,x,y):
|
||||
if self.logscale[0] > 0:
|
||||
x = log10(x)
|
||||
if self.logscale[1] > 0:
|
||||
y = log10(y)
|
||||
f = float(self.graph_w)/(self.maxX-self.minX)
|
||||
xx = (x - self.minX)*f + self.origin[0]
|
||||
f = float(self.graph_h)/(self.maxY-self.minY)
|
||||
yy = (self.maxY - y)*f + self.origin[1]
|
||||
return (xx, yy)
|
||||
|
||||
def move(self, id, newpos, oldpos):
|
||||
dxpt = (newpos[0] - oldpos[0])/(self.maxX-self.minX)*self.graph_w
|
||||
dypt = -(newpos[1] - oldpos[1])/(self.maxY-self.minY)*self.graph_h
|
||||
self.canvas.move(id, dxpt, dypt)
|
||||
self.writeValue(newpos[1])
|
||||
|
||||
def plot(self,n,color='black'):
|
||||
xpt, ypt = self.toscreen(self.data[self.ix,n],
|
||||
self.data[self.iy,n])
|
||||
#xpt = (x-self.minX)/(self.maxX-self.minX)*float(self.graph_w) + self.origin[0]
|
||||
#ypt = (self.maxY-y)/(self.maxY-self.minY)*float(self.graph_h) + self.origin[1]
|
||||
id_ycross = self.canvas.create_line(xpt,self.graph_h+self.origin[1],xpt,self.origin[1],fill = 'gray')
|
||||
id_xcross = self.canvas.create_line(self.origin[0],ypt,self.graph_w+self.origin[0],ypt,fill = 'gray')
|
||||
id = self.canvas.create_oval(xpt-2,ypt-2,xpt+2,ypt+2,fill=color)
|
||||
#self.writeValue(y)
|
||||
s = '(%g, %g)' % (self.data[self.ix,n],self.data[self.iy,n])
|
||||
if n > 0 and self.data[self.iy,n] > self.data[self.iy,n-1]:
|
||||
idt = self.canvas.create_text(xpt+5,ypt+5,text=s,anchor=NW)
|
||||
else:
|
||||
idt = self.canvas.create_text(xpt+5,ypt-5,text=s,anchor=SW)
|
||||
|
||||
return [id,id_xcross,id_ycross, idt]
|
||||
|
||||
def draw(self,color='red'):
|
||||
npts = len(self.xdata)
|
||||
for n in range(1,npts):
|
||||
self.canvas.create_line(self.xdata[n-1],self.ydata[n-1],
|
||||
self.xdata[n],self.ydata[n],fill=color)
|
||||
|
||||
def addLabel(self, y, orient=0):
|
||||
if orient==0:
|
||||
xpt, ypt = self.toscreen(y, 1.0)
|
||||
ypt = self.origin[1] + self.graph_h + 5
|
||||
self.canvas.create_text(xpt,ypt,text=y,anchor=N)
|
||||
else:
|
||||
xpt, ypt = self.toscreen(self.minX, y)
|
||||
xpt = self.origin[0] - 5
|
||||
self.canvas.create_text(xpt,ypt,text=y,anchor=E)
|
||||
def addLegend(self,text,color=None):
|
||||
m=Message(self,text=text,width=self.graph_w-10)
|
||||
m.pack(side=BOTTOM)
|
||||
if color:
|
||||
m.config(fg=color)
|
||||
|
||||
def pauseWhenFinished(self):
|
||||
self.wait_window()
|
||||
|
||||
def minorTicks(self, x0, x1, y, n, size, orient=0):
|
||||
xtick = x0
|
||||
dx = (x1 - x0)/float(n)
|
||||
if orient == 0:
|
||||
while xtick <= x1:
|
||||
xx, yy = self.toscreen(xtick, y)
|
||||
self.canvas.create_line(xx,yy,
|
||||
xx,yy-size)
|
||||
xtick += dx
|
||||
else:
|
||||
while xtick <= x1:
|
||||
xx, yy = self.toscreen(y, xtick)
|
||||
self.canvas.create_line(xx,yy,
|
||||
xx+size,yy)
|
||||
xtick += dx
|
||||
|
||||
|
||||
def ticks(self, xmin, xmax, dx, ymin, ymax, dy, size):
|
||||
|
||||
if self.logscale[0]:
|
||||
xmin = math.pow(10.0,xmin)
|
||||
xmax = math.pow(10.0,xmax)
|
||||
if self.logscale[1]:
|
||||
ymin = math.pow(10.0,ymin)
|
||||
ymax = math.pow(10.0,ymax)
|
||||
|
||||
n = 5
|
||||
ytick = ymin
|
||||
while ytick <= ymax:
|
||||
xx, yy = self.toscreen(xmin, ytick)
|
||||
self.canvas.create_line(xx, yy, xx + size,yy)
|
||||
self.addLabel(ytick,1)
|
||||
xx, yy = self.toscreen(xmax, ytick)
|
||||
self.canvas.create_line(xx, yy, xx - size,yy)
|
||||
ytick0 = ytick
|
||||
if self.logscale[1]:
|
||||
ytick *= 10.0
|
||||
n = 10
|
||||
else: ytick = ytick + dy
|
||||
if ytick <= ymax:
|
||||
self.minorTicks(ytick0, ytick, xmin, n, 5, 1)
|
||||
self.minorTicks(ytick0, ytick, xmax, n, -5, 1)
|
||||
|
||||
n = 5
|
||||
xtick = xmin
|
||||
while xtick <= xmax:
|
||||
xx, yy = self.toscreen(xtick, ymin)
|
||||
self.canvas.create_line(xx, yy, xx, yy - size)
|
||||
self.addLabel(xtick,0)
|
||||
xx, yy = self.toscreen(xtick, ymax)
|
||||
self.canvas.create_line(xx, yy, xx, yy + size)
|
||||
if self.logscale[0]:
|
||||
xtick *= 10.0
|
||||
n = 10
|
||||
else: xtick = xtick + dx
|
||||
if xtick <= xmax:
|
||||
self.minorTicks(xtick - dx, xtick, ymin, n, 5, 0)
|
||||
self.minorTicks(xtick - dx, xtick, ymax, n, -5, 0)
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
from ElementFrame import getElements
|
||||
from utilities import handleError
|
||||
from Cantera import *
|
||||
from config import *
|
||||
from SpeciesFrame import getSpecies
|
||||
|
||||
def testit():
|
||||
pass
|
||||
|
||||
class EditFrame(Frame):
|
||||
|
||||
def redraw(self):
|
||||
try:
|
||||
self.eframe.destroy()
|
||||
self.sframe.destroy()
|
||||
self.rframe.destroy()
|
||||
except:
|
||||
pass
|
||||
self.addElementFrame()
|
||||
self.addSpeciesFrame()
|
||||
self.addReactionFrame()
|
||||
|
||||
def __init__(self, master, app):
|
||||
Frame.__init__(self, master)
|
||||
self.mix = app.mix
|
||||
print self.mix, dir(self.mix)
|
||||
self.app = app
|
||||
self.master = master
|
||||
self.master.title("Cantera Mechanism Editor")
|
||||
self.redraw()
|
||||
|
||||
def addReactionFrame(self):
|
||||
self.rframe = Frame(self)
|
||||
self.rframe.config(relief=GROOVE,bd=4)
|
||||
self.rframe.grid(row=2,column=0,columnspan=10,sticky=E+W)
|
||||
b=Button(self.rframe,text='Reactions',command=testit)
|
||||
b.grid(column=5, row=0)
|
||||
|
||||
def addElementFrame(self):
|
||||
self.eframe = Frame(self)
|
||||
self.eframe.config(relief=GROOVE,bd=4)
|
||||
self.eframe.grid(row=0,column=0,columnspan=10,sticky=E+W)
|
||||
self.element_labels = []
|
||||
n = 0
|
||||
for el in self.mix._mech.elementNames():
|
||||
x = Label(self.eframe,text=el,fg='darkblue')
|
||||
x.grid(column = n, row=0)
|
||||
self.element_labels.append(x)
|
||||
n = n + 1
|
||||
b=Button(self.eframe,text='Element',command=self.chooseElements, default=ACTIVE)
|
||||
b.grid(column=0, row=1, columnspan=10)
|
||||
|
||||
|
||||
def addSpeciesFrame(self):
|
||||
self.sframe = Frame(self)
|
||||
self.sframe.config(relief=GROOVE,bd=4)
|
||||
self.sframe.grid(row=1,column=0,columnspan=10,sticky=E+W)
|
||||
r = 0
|
||||
c = 0
|
||||
splist = self.app.species
|
||||
self.spcheck = []
|
||||
self.spec = []
|
||||
for i in range(self.app.mech.nSpecies()):
|
||||
self.spec.append(IntVar())
|
||||
self.spec[i].set(1)
|
||||
self.spcheck.append( Checkbutton(self.sframe,
|
||||
text=splist[i].name,
|
||||
variable=self.spec[i],
|
||||
onvalue = 1, offvalue = 0) )
|
||||
self.spcheck[i].grid(row = r, column = c, sticky = N+W)
|
||||
self.spcheck[i].bind("<Button-3>", self.editSpecies)
|
||||
c = c + 1
|
||||
if c > 4:
|
||||
c, r = 0, r + 1
|
||||
|
||||
def getspecies(self):
|
||||
print getSpecies(self.mix.speciesNames(),
|
||||
self.mix.speciesNames())
|
||||
|
||||
def editSpecies(self, event=None):
|
||||
e = Toplevel(event.widget.master)
|
||||
w = event.widget
|
||||
txt = w.cget('text')
|
||||
sp = self.app.mix.species[txt]
|
||||
|
||||
# name, etc.
|
||||
e1 = Frame(e, relief=FLAT)
|
||||
self.addEntry(e1,'Name',0,0,sp.name)
|
||||
self.addEntry(e1,'ID Tag',1,0,sp.id)
|
||||
self.addEntry(e1,'Phase',2,0,sp.phase)
|
||||
e1.grid(row=0,column=0)
|
||||
|
||||
# elements
|
||||
elframe = Frame(e)
|
||||
elframe.grid(row=1,column=0)
|
||||
Label(elframe,text='Elemental Composition').grid(row=0,column=0,columnspan=2,sticky=E+W)
|
||||
|
||||
i = 0
|
||||
for el in self.app.mech.elementNames():
|
||||
self.addEntry(elframe,el,i,0,self.mech.nAtoms(sp, el))
|
||||
i = i + 1
|
||||
|
||||
# thermo
|
||||
thframe = Frame(e)
|
||||
thframe.grid(row=0,rowspan=2,column=1)
|
||||
thframe.config(relief=GROOVE,bd=4)
|
||||
i = 0
|
||||
Label(thframe,text='Thermodynamic Properties').grid(row=0,
|
||||
column=0, columnspan=4, sticky=E+W)
|
||||
if isinstance(sp.thermoParam(),NasaPolynomial):
|
||||
Label(thframe,text='Parametrization:').grid(row=1,column=1)
|
||||
self.addEntry(thframe,'',2,0,'NasaPolynomial')
|
||||
Label(thframe,text='Temperatures (min, mid, max):').grid(row=3,column=1)
|
||||
self.addEntry(thframe,'',4,0,`sp.minTemp`)
|
||||
self.addEntry(thframe,'',5,0,`sp.midTemp`)
|
||||
self.addEntry(thframe,'',6,0,`sp.maxTemp`)
|
||||
low = Frame(thframe)
|
||||
low.config(relief=GROOVE,bd=4)
|
||||
low.grid(row=1,rowspan=6,column=3,columnspan=2)
|
||||
Label(low,text='Coefficients for the Low\n Temperature Range').grid(row=0,column=0,columnspan=2,sticky=E+W)
|
||||
c = sp.thermoParam().coefficients(sp.minTemp)
|
||||
for j in range(7):
|
||||
self.addEntry(low,'a'+`j`,j+3,0,`c[j]`)
|
||||
high = Frame(thframe)
|
||||
high.config(relief=GROOVE,bd=4)
|
||||
high.grid(row=1,rowspan=6,column=5,columnspan=2)
|
||||
Label(high,text='Coefficients for the High\n Temperature Range').grid(row=0,column=0,columnspan=2,sticky=E+W)
|
||||
c = sp.thermoParam().coefficients(sp.maxTemp)
|
||||
for j in range(7):
|
||||
self.addEntry(high,'a'+`j`,j+3,0,`c[j]`)
|
||||
|
||||
com = Frame(e)
|
||||
com.grid(row=10,column=0,columnspan=5)
|
||||
ok = Button(com,text='OK',default=ACTIVE)
|
||||
ok.grid(row=0,column=0)
|
||||
ok.bind('<1>',self.modifySpecies)
|
||||
Button(com,text='Cancel',command=e.destroy).grid(row=0,column=1)
|
||||
self.especies = e
|
||||
|
||||
def modifySpecies(self,event=None):
|
||||
button = event.widget
|
||||
e = self.especies
|
||||
for fr in e.children.values():
|
||||
for item in fr.children.values():
|
||||
try:
|
||||
print item.cget('selection')
|
||||
except:
|
||||
pass
|
||||
e.destroy()
|
||||
|
||||
def addEntry(self,master,name,row,column,text):
|
||||
if name:
|
||||
Label(master, text=name).grid(row=row, column=column)
|
||||
nm = Entry(master)
|
||||
nm.grid(row=row, column=column+1)
|
||||
nm.insert(END,text)
|
||||
|
||||
def chooseElements(self):
|
||||
oldel = self.mix.g.elementNames()
|
||||
newel = getElements(self.mix.g.elementNames())
|
||||
removeList = []
|
||||
for el in oldel:
|
||||
if not el in newel:
|
||||
removeList.append(el)
|
||||
#self.app.mech.removeElements(removeList)
|
||||
addList = []
|
||||
for el in newel:
|
||||
if not el in oldel:
|
||||
addList.append(el)
|
||||
#self.app.mech.addElements(addList)
|
||||
try:
|
||||
self.redraw()
|
||||
self.app.makeWindows()
|
||||
except:
|
||||
handleError('Edit err')
|
||||
|
||||
self.app.mix = IdealGasMixture(self.app.mech)
|
||||
self.mix = self.app.mix
|
||||
nn = self.mix.speciesList[0].name
|
||||
self.mix.set(temperature = 300.0, pressure = 101325.0, moles = {nn:1.0})
|
||||
for label in self.element_labels:
|
||||
label.destroy()
|
||||
self.element_labels = []
|
||||
n = 0
|
||||
for el in self.mix._mech.elementList():
|
||||
x = Label(self.eframe,text=el.symbol(),fg='darkblue')
|
||||
x.grid(column = n, row=0)
|
||||
self.element_labels.append(x)
|
||||
n = n + 1
|
||||
|
||||
self.app.makeWindows()
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
#
|
||||
# function getElements displays a periodic table, and returns a list of
|
||||
# the selected elements
|
||||
#
|
||||
|
||||
from Tkinter import *
|
||||
from types import *
|
||||
import tkMessageBox
|
||||
import string
|
||||
|
||||
from Cantera import *
|
||||
|
||||
# (row,column) positions in the periodic table
|
||||
_pos = {'H':(1,1), 'He':(1,18),
|
||||
'Li':(2,1), 'Be':(2,2),
|
||||
'B':(2,13), 'C':(2,14), 'N':(2,15), 'O':(2,16), 'F':(2,17), 'Ne':(2,18),
|
||||
'Na':(3,1), 'Mg':(3,2),
|
||||
'Al':(3,13), 'Si':(3,14), 'P':(3,15), 'S':(3,16), 'Cl':(3,17), 'Ar':(3,18),
|
||||
'K':(4,1), 'Ca':(4,2),
|
||||
'Sc':(4,3), 'Ti':(4,4), 'V':(4,5), 'Cr':(4,6), 'Mn':(4,7), 'Fe':(4,8),
|
||||
'Co':(4,9), 'Ni':(4,10), 'Cu':(4,11), 'Zn':(4,12),
|
||||
'Ga':(4,13), 'Ge':(4,14), 'As':(4,15), 'Se':(4,16), 'Br':(4,17), 'Kr':(4,18),
|
||||
'Rb':(5,1), 'Sr':(5,2),
|
||||
'Y':(5,3), 'Zr':(5,4), 'Nb':(5,5), 'Mo':(5,6), 'Tc':(5,7), 'Ru':(5,8),
|
||||
'Rh':(5,9), 'Pd':(5,10), 'Ag':(5,11), 'Cd':(5,12),
|
||||
'In':(5,13), 'Sn':(5,14), 'Sb':(5,15), 'Te':(5,16), 'I':(5,17), 'Xe':(5,18)
|
||||
}
|
||||
|
||||
class PeriodicTable(Frame):
|
||||
|
||||
def __init__(self, master, selected=[]):
|
||||
Frame.__init__(self,master)
|
||||
self.master = master
|
||||
self.control = Frame(self)
|
||||
self.control.config(relief=GROOVE,bd=4)
|
||||
Button(self.control, text = 'Display',command=self.show).pack(fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = 'Clear',command=self.clear).pack(fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = ' OK ',command=self.get).pack(side=BOTTOM,
|
||||
fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = 'Cancel',command=self.master.quit).pack(side=BOTTOM,
|
||||
fill=X,pady=3, padx=10)
|
||||
self.entries = Frame(self)
|
||||
self.entries.pack(side=LEFT)
|
||||
self.control.pack(side=RIGHT,fill=Y)
|
||||
self.c = {}
|
||||
self.element = {}
|
||||
self.selected = selected
|
||||
n=0
|
||||
ncol = 8
|
||||
for el in _pos.keys():
|
||||
self.element[el] = Frame(self.entries)
|
||||
self.element[el].config(relief=GROOVE, bd=4, bg=self.color(el))
|
||||
self.c[el] = Button(self.element[el],text=el,bg=self.color(el),width=3,relief=FLAT)
|
||||
self.c[el].pack()
|
||||
self.c[el].bind("<Button-1>",self.setColors)
|
||||
self.element[el].grid(row=_pos[el][0]-1, column = _pos[el][1]-1,sticky=W+N+E+S)
|
||||
n = n + 1
|
||||
Label(self.entries,text='select the elements to be included, and then press OK.\nTo view the properties of the selected elements, press Display ').grid(row=0, column=2, columnspan=10, sticky=W)
|
||||
|
||||
|
||||
def select(self, el):
|
||||
e = string.capitalize(el)
|
||||
self.c[e]['relief'] = RAISED
|
||||
self.c[e]['bg'] = self.color(e, sel=1)
|
||||
|
||||
def deselect(self, el):
|
||||
e = string.capitalize(el)
|
||||
self.c[e]['relief'] = FLAT
|
||||
self.c[e]['bg'] = self.color(e, sel=0)
|
||||
|
||||
def selectElements(self,ellist):
|
||||
for el in ellist:
|
||||
ename = el
|
||||
self.select(ename)
|
||||
|
||||
def setColors(self,event):
|
||||
el = event.widget['text']
|
||||
if event.widget['relief'] == RAISED:
|
||||
event.widget['relief'] = FLAT
|
||||
back = self.color(el, sel=0)
|
||||
elif event.widget['relief'] == FLAT:
|
||||
event.widget['relief'] = RAISED
|
||||
back = self.color(el, sel=1)
|
||||
event.widget['bg'] = back
|
||||
|
||||
def color(self, el, sel=0):
|
||||
_normal = ['#88dddd','#dddd88','#dd8888']
|
||||
_selected = ['#aaffff','#ffffaa','#ffaaaa']
|
||||
row, column = _pos[el]
|
||||
if sel: list = _selected
|
||||
else: list = _normal
|
||||
if column < 3:
|
||||
return list[0]
|
||||
elif column > 12:
|
||||
return list[1]
|
||||
else:
|
||||
return list[2]
|
||||
|
||||
def show(self):
|
||||
elnames = _pos.keys()
|
||||
elnames.sort()
|
||||
selected = []
|
||||
for el in elnames:
|
||||
if self.c[el]['relief'] == RAISED:
|
||||
selected.append(periodicTable[el])
|
||||
showElementProperties(selected)
|
||||
|
||||
def get(self):
|
||||
self.selected = []
|
||||
names = _pos.keys()
|
||||
names.sort()
|
||||
for el in names:
|
||||
if self.c[el]['relief'] == RAISED:
|
||||
self.selected.append(periodicTable[el])
|
||||
#self.master.quit()'
|
||||
self.master.destroy()
|
||||
|
||||
def clear(self):
|
||||
for el in _pos.keys():
|
||||
self.c[el]['bg'] = self.color(el, sel=0)
|
||||
self.c[el]['relief'] = FLAT
|
||||
|
||||
class ElementPropertyFrame(Frame):
|
||||
def __init__(self,master,ellist):
|
||||
Frame.__init__(self,master)
|
||||
n = 1
|
||||
ellist.sort()
|
||||
Label(self,text='Name').grid(column=0,row=0,sticky=W+S,padx=10,pady=10)
|
||||
Label(self,text='Atomic \nNumber').grid(column=1,row=0,sticky=W+S,padx=10,pady=10)
|
||||
Label(self,
|
||||
text='Atomic \nWeight').grid(column=2,
|
||||
row=0,
|
||||
sticky=W+S,
|
||||
padx=10,
|
||||
pady=10)
|
||||
for el in ellist:
|
||||
Label(self,
|
||||
text=el.name).grid(column=0,
|
||||
row=n,
|
||||
sticky=W,
|
||||
padx=10)
|
||||
Label(self,
|
||||
text=`el.atomicNumber`).grid(column=1,
|
||||
row=n,
|
||||
sticky=W,
|
||||
padx=10)
|
||||
Label(self,
|
||||
text=`el.atomicWeight`).grid(column=2,
|
||||
row=n,
|
||||
sticky=W,
|
||||
padx=10)
|
||||
n = n + 1
|
||||
|
||||
|
||||
# utility functions
|
||||
|
||||
def getElements(ellist=None):
|
||||
master = Toplevel()
|
||||
master.title('Periodic Table of the Elements')
|
||||
t = PeriodicTable(master)
|
||||
if ellist: t.selectElements(ellist)
|
||||
t.pack()
|
||||
t.focus_set()
|
||||
t.grab_set()
|
||||
t.wait_window()
|
||||
try:
|
||||
master.destroy()
|
||||
except TclError:
|
||||
pass
|
||||
return t.selected
|
||||
|
||||
|
||||
# display table of selected element properties in a window
|
||||
def showElementProperties(ellist):
|
||||
m = Tk()
|
||||
m.title('Element Properties')
|
||||
elem = []
|
||||
ElementPropertyFrame(m, ellist).pack()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print getElements()
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
|
||||
from Tkinter import *
|
||||
import math
|
||||
|
||||
class Graph(Frame):
|
||||
def __init__(self,master,title,minX,maxX,minY,maxY,pixelX=250,pixelY=250):
|
||||
Frame.__init__(self,master, relief=RIDGE, bd=2)
|
||||
# self.pack()
|
||||
self.title = Label(self,text=' ')
|
||||
self.title.grid(row=0,column=1,sticky=W+E)
|
||||
self.graph_w, self.graph_h = pixelX, pixelY
|
||||
self.maxX, self.maxY = maxX, maxY #float(math.floor(maxX + 1)), \
|
||||
#float(math.floor(maxY + 1))
|
||||
self.minX, self.minY = minX, minY # float(math.floor(minX)), float(math.floor(minY))
|
||||
self.canvas = Canvas(self,
|
||||
width=self.graph_w,
|
||||
height=self.graph_h,
|
||||
relief=SUNKEN,bd=1)
|
||||
ymintext = "%8.1f" % (self.minY)
|
||||
ymaxtext = "%8.1f" % (self.maxY)
|
||||
self.ml=Label(self, text=ymintext)
|
||||
self.mr=Label(self, text=ymaxtext)
|
||||
self.ml.grid(row=2,column=0,sticky=S+E)
|
||||
self.mr.grid(row=1,column=0,sticky=N+E)
|
||||
self.canvas.grid(row=1,column=1,rowspan=2,sticky=N+S+E+W)
|
||||
self.last_points=[]
|
||||
|
||||
|
||||
def writeValue(self, y):
|
||||
yval = '%15.4f' % (y)
|
||||
self.title.config(text = yval)
|
||||
|
||||
def delete(self, ids):
|
||||
for id in ids:
|
||||
self.canvas.delete(id)
|
||||
|
||||
def move(self, id, newpos, oldpos):
|
||||
dxpt = (newpos[0] - oldpos[0])/(self.maxX-self.minX)*self.graph_w
|
||||
dypt = -(newpos[1] - oldpos[1])/(self.maxY-self.minY)*self.graph_h
|
||||
self.canvas.move(id, dxpt, dypt)
|
||||
self.writeValue(newpos[1])
|
||||
|
||||
def plot(self,x,y,color='black'):
|
||||
xpt = (x-self.minX)/(self.maxX-self.minX)*float(self.graph_w) + 1.5
|
||||
ypt = (self.maxY-y)/(self.maxY-self.minY)*float(self.graph_h) - 1.5
|
||||
id_ycross = self.canvas.create_line(xpt,self.graph_h,xpt,0,fill = 'gray')
|
||||
id_xcross = self.canvas.create_line(0,ypt,self.graph_w,ypt,fill = 'gray')
|
||||
id = self.canvas.create_oval(xpt-2,ypt-2,xpt+2,ypt+2,fill=color)
|
||||
self.writeValue(y)
|
||||
return [id,id_xcross,id_ycross]
|
||||
|
||||
def reset(self,minX,maxX,minY,maxY):
|
||||
self.maxX, self.maxY = maxX, maxY
|
||||
self.minX, self.minY = minX, minY
|
||||
self.canvas.destroy()
|
||||
self.canvas = Canvas(self,
|
||||
width=self.graph_w,
|
||||
height=self.graph_h,
|
||||
relief=SUNKEN,bd=1)
|
||||
self.canvas.create_text(4,2,text=self.maxY,anchor=NW)
|
||||
self.canvas.create_text(4,self.graph_h,text=self.minY,anchor=SW)
|
||||
self.ml["text"] = `minX`
|
||||
self.mr["text"] = `maxX`
|
||||
self.canvas.pack()
|
||||
self.last_points = []
|
||||
|
||||
def join(self,point_list):
|
||||
i = 0
|
||||
for pt in point_list:
|
||||
x, y, color = pt
|
||||
if self.last_points == []:
|
||||
last_x, last_y, last_color = pt
|
||||
else:
|
||||
last_x, last_y, last_color = self.last_points[i]
|
||||
i = i + 1
|
||||
xpt = (x - self.minX)/(float(self.maxX - self.minX)/self.graph_w) + 1.5
|
||||
ypt = (self.maxY-y)/(float(self.maxY - self.minY)/self.graph_h) - 1.5
|
||||
last_xpt = (last_x - self.minX)/(float(self.maxX - self.minX)/self.graph_w) + 1.5
|
||||
last_ypt = (self.maxY-last_y)/(float(self.maxY - self.minY)/self.graph_h) - 1.5
|
||||
self.canvas.create_line(last_xpt,last_ypt,
|
||||
xpt,ypt,fill=color)
|
||||
self.last_points = point_list
|
||||
self.canvas.update()
|
||||
return
|
||||
|
||||
|
||||
def addLegend(self,text,color=None):
|
||||
m=Message(self,text=text,width=self.graph_w-10)
|
||||
m.pack(side=BOTTOM)
|
||||
if color:
|
||||
m.config(fg=color)
|
||||
|
||||
def pauseWhenFinished(self):
|
||||
self.wait_window()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
root= Tk()
|
||||
g = Graph(root,'graph1',0,10,0.01,120)
|
||||
h = Graph(root,'graph2',0,15,0,20000)
|
||||
g.pack(side=LEFT)
|
||||
h.pack(side=RIGHT)
|
||||
|
||||
#root.protocol("WM_DELETE_WINDOW", root.destroy())
|
||||
j = Graph(root,'Graph',0,1000,0,2000)
|
||||
j.pack()
|
||||
|
||||
j.plot(0, 0, color='red')
|
||||
j.last_points = [ (0, 0, 'red') ]
|
||||
for i in range(100):
|
||||
j.join( [ ( (i*10),(i*10+500), 'red' ) ] )
|
||||
|
||||
|
||||
g.addLegend('An example of the GraphFrame')
|
||||
h.addLegend('This is where the legend goes')
|
||||
for i in range(100):
|
||||
if root:
|
||||
x,y = float(i)/10, i
|
||||
g.plot(x,y,color='red')
|
||||
h.plot(i,i**2)#(0,0)
|
||||
#h.join([(i,i**2,'black')])
|
||||
else:
|
||||
break
|
||||
|
||||
#print("finished")
|
||||
g.pauseWhenFinished()
|
||||
h.pauseWhenFinished()
|
||||
print g
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
import os, math
|
||||
from Tkinter import *
|
||||
from Cantera import *
|
||||
#from Cantera.ck2ctml import ck2ctml
|
||||
from tkFileDialog import askopenfilename
|
||||
|
||||
class ImportFrame(Frame):
|
||||
def __init__(self,top):
|
||||
self.master = Toplevel()
|
||||
self.master.title('Convert and Import CK File')
|
||||
self.master.protocol("WM_DELETE_WINDOW",self.hide)
|
||||
|
||||
Frame.__init__(self,self.master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.infile = StringVar()
|
||||
|
||||
Label(self,text="Input File").grid(row=0,column=0)
|
||||
Entry(self, width=40,
|
||||
textvariable=self.infile).grid(column=1,row=0)
|
||||
Button(self, text='Browse',
|
||||
command=self.browseForInput).grid(row=0,column=2)
|
||||
|
||||
self.thermo = StringVar()
|
||||
Label(self,text="Thermodynamic Database").grid(row=1,column=0)
|
||||
Entry(self, width=40,
|
||||
textvariable=self.thermo).grid(column=1,row=1)
|
||||
Button(self, text='Browse',
|
||||
command=self.browseForThermo).grid(row=1,column=2)
|
||||
|
||||
|
||||
self.transport = StringVar()
|
||||
Label(self,text="Transport Database").grid(row=2,column=0)
|
||||
Entry(self, width=40,
|
||||
textvariable=self.transport).grid(column=1,row=2)
|
||||
Button(self, text='Browse',
|
||||
command=self.browseForTransport).grid(row=2,column=2)
|
||||
|
||||
bframe = Frame(self)
|
||||
bframe.config(relief=GROOVE, bd=1)
|
||||
bframe.grid(row=100,column=0)
|
||||
Button(bframe, text='OK', width=8, command=self.importfile).grid(row=0,column=0)
|
||||
self.grid(column=0,row=0)
|
||||
Button(bframe, text='Cancel', width=8, command=self.hide).grid(row=0,column=1)
|
||||
self.grid(column=0,row=0)
|
||||
self.hide()
|
||||
|
||||
def browseForInput(self, e=None):
|
||||
pathname = askopenfilename(
|
||||
filetypes=[("Reaction Mechanism Files",
|
||||
("*.inp","*.mech","*.ck2")),
|
||||
("All Files", "*.*")])
|
||||
if pathname:
|
||||
self.infile.set(pathname)
|
||||
self.show()
|
||||
|
||||
def browseForThermo(self, e=None):
|
||||
pathname = askopenfilename(
|
||||
filetypes=[("Thermodynamic Databases",
|
||||
("*.dat","*.inp","*.therm")),
|
||||
("All Files", "*.*")])
|
||||
if pathname:
|
||||
self.thermo.set(pathname)
|
||||
self.show()
|
||||
|
||||
def browseForTransport(self, e=None):
|
||||
pathname = askopenfilename(
|
||||
filetypes=[("Transport Databases", "*.dat"),
|
||||
("All Files", "*.*")])
|
||||
if pathname:
|
||||
self.transport.set(pathname)
|
||||
self.show()
|
||||
|
||||
|
||||
def importfile(self):
|
||||
ckfile = self.infile.get()
|
||||
thermdb = self.thermo.get()
|
||||
trandb = self.transport.get()
|
||||
p = os.path.normpath(os.path.dirname(ckfile))
|
||||
fname = os.path.basename(ckfile)
|
||||
ff = os.path.splitext(fname)
|
||||
nm = ""
|
||||
if len(ff) > 1: nm = ff[0]
|
||||
else: nm = ff
|
||||
outfile = p+os.sep+nm+'.xml'
|
||||
try:
|
||||
print 'not supported.'
|
||||
#ck2ctml(infile = ckfile, thermo = thermdb,
|
||||
# transport = trandb, outfile = outfile,
|
||||
# id = nm)
|
||||
self.hide()
|
||||
return
|
||||
|
||||
except:
|
||||
print 'Errors were encountered. See log file ck2ctml.log'
|
||||
self.hide()
|
||||
return
|
||||
|
||||
self.top.loadmech(nm,outfile,1)
|
||||
self.hide()
|
||||
|
||||
## cmd = 'ck2ctml -i '+ckfile+' -o '+outfile
|
||||
## if thermdb <> "":
|
||||
## cmd += ' -t '+thermdb
|
||||
## if trandb <> "":
|
||||
## cmd += ' -tr '+trandb
|
||||
## cmd += ' -id '+nm
|
||||
## ok = os.system(cmd)
|
||||
## if ok == 0:
|
||||
## self.top.loadmech(nm,outfile,1)
|
||||
|
||||
|
||||
def hide(self):
|
||||
#self.vis.set(0)
|
||||
self.master.withdraw()
|
||||
|
||||
def show(self):
|
||||
#v = self.vis.get()
|
||||
#if v == 0:
|
||||
# self.hide()
|
||||
# return
|
||||
|
||||
self.master.deiconify()
|
||||
|
|
@ -1,417 +0,0 @@
|
|||
import os, math
|
||||
from Tkinter import *
|
||||
from Cantera import *
|
||||
|
||||
from SpeciesInfo import SpeciesInfo
|
||||
from Cantera import rxnpath
|
||||
import webbrowser
|
||||
|
||||
_CUTOFF = 1.e-15
|
||||
_ATOL = 1.e-15
|
||||
_RTOL = 1.e-7
|
||||
|
||||
def showsvg():
|
||||
f = open('_rp_svg.html','w')
|
||||
f.write('<embed src="rxnpath.svg" name="rxnpath" height=500\n')
|
||||
f.write('type="image/svg-xml" pluginspage="http://www.adobe.com/svg/viewer/install/">\n')
|
||||
f.close()
|
||||
webbrowser.open('file:///'+os.getcwd()+'/_rp_svg.html')
|
||||
|
||||
def showpng():
|
||||
f = open('_rp_png.html','w')
|
||||
f.write('<img src="rxnpath.png" height=500/>\n')
|
||||
f.close()
|
||||
webbrowser.open('file:///'+os.getcwd()+'/_rp_png.html')
|
||||
|
||||
|
||||
class KineticsFrame(Frame):
|
||||
def __init__(self,master):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=FLAT, bd=4)
|
||||
self.top = self.master.top
|
||||
self.controls=Frame(self)
|
||||
self.hide = IntVar()
|
||||
self.hide.set(0)
|
||||
self.comp = IntVar()
|
||||
self.comp.set(2)
|
||||
self.controls.grid(column=1,row=0,sticky=W+E+N)
|
||||
self.makeControls()
|
||||
mf = self.master
|
||||
|
||||
def makeControls(self):
|
||||
Radiobutton(self.controls,text='Creation Rates',
|
||||
variable=self.comp,value=0,
|
||||
command=self.show).grid(column=0,row=0,sticky=W)
|
||||
Radiobutton(self.controls,text='Destruction Rates',
|
||||
variable=self.comp,value=1,
|
||||
command=self.show).grid(column=0,row=1,sticky=W)
|
||||
Radiobutton(self.controls,text='Net Production Rates',
|
||||
variable=self.comp,value=2,
|
||||
command=self.show).grid(column=0,row=2,sticky=W)
|
||||
|
||||
def show(self):
|
||||
mf = self.master
|
||||
mf.active = self
|
||||
c = self.comp.get()
|
||||
mix = self.top.mix
|
||||
g = mix.g
|
||||
if c == 0:
|
||||
mf.var.set("Creation Rates")
|
||||
#mf.data = spdict(mix.g, mix.moles())
|
||||
mf.comp = g.creationRates()
|
||||
|
||||
elif c == 1:
|
||||
mf.var.set("Destruction Rates")
|
||||
#mf.data = spdict(mix.g,mix.mass())
|
||||
mf.comp = g.destructionRates()
|
||||
|
||||
elif c == 2:
|
||||
mf.var.set("Net Production Rates")
|
||||
mf.comp = g.netProductionRates()
|
||||
#mf.data = spdict(mix,mix,mf.comp)
|
||||
|
||||
for s in mf.variable.keys():
|
||||
try:
|
||||
k = g.speciesIndex(s)
|
||||
if mf.comp[k] > _CUTOFF or -mf.comp[k] > _CUTOFF:
|
||||
mf.variable[s].set(mf.comp[k])
|
||||
else:
|
||||
mf.variable[s].set(0.0)
|
||||
except:
|
||||
pass
|
||||
|
||||
class SpeciesKineticsFrame(Frame):
|
||||
def __init__(self,master,top):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.top.kinetics = self
|
||||
self.g = self.top.mix.g
|
||||
self.entries=Frame(self)
|
||||
self.var = StringVar()
|
||||
self.var.set("Net Production Rates")
|
||||
self.names = self.top.mix.speciesNames()
|
||||
self.nsp = len(self.names)
|
||||
self.comp = [0.0]*self.nsp
|
||||
self.makeControls()
|
||||
self.makeEntries()
|
||||
self.entries.bind('<Double-l>',self.minimize)
|
||||
self.ctype = 0
|
||||
|
||||
def makeControls(self):
|
||||
self.c = KineticsFrame(self)
|
||||
#self.rr = ReactionKineticsFrame(self, self.top)
|
||||
self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
#self.rr.grid(column=0,row=1,sticky=E+W+N+S)
|
||||
|
||||
def show(self):
|
||||
self.c.show()
|
||||
|
||||
def redo(self):
|
||||
self.update()
|
||||
self.entries.destroy()
|
||||
self.entries=Frame(self)
|
||||
self.makeEntries()
|
||||
|
||||
def minimize(self,Event=None):
|
||||
self.c.hide.set(1)
|
||||
self.redo()
|
||||
self.c.grid_forget()
|
||||
self.entries.bind("<Double-1>",self.maximize)
|
||||
|
||||
def maximize(self,Event=None):
|
||||
self.c.hide.set(0)
|
||||
self.redo()
|
||||
self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
self.entries.bind("<Double-1>",self.minimize)
|
||||
|
||||
def up(self, x):
|
||||
self.update()
|
||||
|
||||
def makeEntries(self):
|
||||
self.entries.grid(row=0,column=0,sticky=W+N+S+E)
|
||||
self.entries.config(relief=FLAT,bd=4)
|
||||
DATAKEYS = self.top.species
|
||||
self.variable = {}
|
||||
|
||||
n=0
|
||||
ncol = 3
|
||||
col = 0
|
||||
row = 60
|
||||
|
||||
for sp in DATAKEYS:
|
||||
s = sp
|
||||
k = s.index
|
||||
if row > 15:
|
||||
row = 0
|
||||
col = col + 2
|
||||
l = Label(self.entries,text='Species')
|
||||
l.grid(column=col,row=row,sticky=E+W)
|
||||
e1 = Entry(self.entries)
|
||||
e1.grid(column=col+1,row=row,sticky=E+W)
|
||||
e1['textvariable'] = self.var
|
||||
e1.config(state=DISABLED)
|
||||
e1.config(bg='lightyellow',relief=RIDGE)
|
||||
row = row + 1
|
||||
|
||||
spname = s.name
|
||||
val = self.comp[k]
|
||||
if not self.c.hide.get() or val: showit = 1
|
||||
else: showit = 0
|
||||
|
||||
l=SpeciesInfo(self.entries,species=s,
|
||||
text=spname,relief=FLAT,justify=RIGHT,
|
||||
fg='darkblue')
|
||||
entry1 = Entry(self.entries)
|
||||
self.variable[spname] = DoubleVar()
|
||||
self.variable[spname].set(self.comp[k])
|
||||
entry1['textvariable']=self.variable[spname]
|
||||
entry1.bind('<Any-Leave>',self.up)
|
||||
if showit:
|
||||
l.grid(column= col ,row=row,sticky=E)
|
||||
entry1.grid(column=col+1,row=row)
|
||||
n=n+1
|
||||
row = row + 1
|
||||
entry1.config(state=DISABLED,bg='lightgray')
|
||||
|
||||
|
||||
class ReactionKineticsFrame(Frame):
|
||||
def __init__(self,vis,top):
|
||||
self.master = Toplevel()
|
||||
self.master.protocol("WM_DELETE_WINDOW",self.hide)
|
||||
self.vis = vis
|
||||
Frame.__init__(self,self.master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.g = self.top.mix.g
|
||||
nr = self.g.nReactions()
|
||||
self.eqs=Text(self,width=40,height=30)
|
||||
self.data = []
|
||||
self.start = DoubleVar()
|
||||
if nr > 30:
|
||||
self.end = self.start.get()+30
|
||||
else:
|
||||
self.end = self.start.get()+nr
|
||||
|
||||
for i in range(4):
|
||||
self.data.append(Text(self,width=15,height=30))
|
||||
|
||||
for n in range(nr):
|
||||
s = self.g.reactionEqn(n)
|
||||
self.eqs.insert(END,s+'\n')
|
||||
self.eqs.grid(column=0,row=1,sticky=W+E+N)
|
||||
for i in range(4):
|
||||
self.data[i].grid(column=i+1,row=1,sticky=W+E+N)
|
||||
Label(self, text='Reaction').grid(column=0,row=0,sticky=W+E+N)
|
||||
Label(self, text='Fwd ROP').grid(column=1,row=0,sticky=W+E+N)
|
||||
Label(self, text='Rev ROP').grid(column=2,row=0,sticky=W+E+N)
|
||||
Label(self, text='Net ROP').grid(column=3,row=0,sticky=W+E+N)
|
||||
Label(self, text='Kp').grid(column=4,row=0,sticky=W+E+N)
|
||||
|
||||
self.scfr = Frame(self)
|
||||
self.scfr.config(relief=GROOVE,bd=4)
|
||||
|
||||
## self.sc = Scrollbar(self.scfr,command=self.show,
|
||||
## variable = self.start,
|
||||
## orient='horizontal',length=400)
|
||||
self.sc = Scale(self.scfr,command=self.show,
|
||||
variable=self.start,
|
||||
orient='vertical',length=400)
|
||||
# self.sc.config(cnf={'from':0,'to':nr},variable = self.start)
|
||||
#self.sc.bind('<Any-Enter>',self.couple)
|
||||
#self.scfr.bind('<Any-Leave>',self.decouple)
|
||||
self.sc.pack(side=RIGHT,fill=Y)
|
||||
self.scfr.grid(row=0,column=6,rowspan=10,sticky=N+E+W)
|
||||
self.grid(column=0,row=0)
|
||||
|
||||
self.hide()
|
||||
|
||||
## def decouple(self,event=None):
|
||||
## d = DoubleVar()
|
||||
## xx = self.start.get()
|
||||
## d.set(xx)
|
||||
## self.sc.config(variable = d)
|
||||
|
||||
## def couple(self,event=None):
|
||||
## self.sc.config(variable = self.start)
|
||||
|
||||
def hide(self):
|
||||
# self.vis.set(0)
|
||||
self.master.withdraw()
|
||||
|
||||
def show(self,e=None,b=None,c=None):
|
||||
v = self.vis.get()
|
||||
print e,b,c
|
||||
#if v == 0:
|
||||
# self.hide()
|
||||
# return
|
||||
|
||||
self.master.deiconify()
|
||||
nr = self.g.nReactions()
|
||||
frop = self.g.fwdRatesOfProgress()
|
||||
rrop = self.g.revRatesOfProgress()
|
||||
kp = self.g.equilibriumConstants()
|
||||
self.data[0].delete(1.0,END)
|
||||
self.data[1].delete(1.0,END)
|
||||
self.data[2].delete(1.0,END)
|
||||
self.data[3].delete(1.0,END)
|
||||
self.eqs.delete(1.0,END)
|
||||
|
||||
n0 = int(self.start.get())
|
||||
nn = nr - n0
|
||||
if nn > 30: nn = 30
|
||||
for n in range(n0, nn+n0):
|
||||
s = '%12.5e \n' % (frop[n],)
|
||||
self.data[0].insert(END,s)
|
||||
s = '%12.5e \n' % (rrop[n],)
|
||||
self.data[1].insert(END,s)
|
||||
s = '%12.5e \n' % (frop[n] - rrop[n],)
|
||||
self.data[2].insert(END,s)
|
||||
s = '%12.5e \n' % (kp[n],)
|
||||
self.data[3].insert(END,s)
|
||||
self.eqs.insert(END, self.g.reactionEqn(n)+'\n')
|
||||
|
||||
class ReactionPathFrame(Frame):
|
||||
|
||||
def __init__(self,top):
|
||||
self.master = Toplevel()
|
||||
self.master.protocol("WM_DELETE_WINDOW",self.hide)
|
||||
#self.vis = vis
|
||||
Frame.__init__(self,self.master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.grid(column=0,row=0)
|
||||
self.top = top
|
||||
self.g = self.top.mix.g
|
||||
self.el = IntVar()
|
||||
self.el.set(0)
|
||||
self.thresh = DoubleVar()
|
||||
|
||||
scframe = Frame(self)
|
||||
self.sc = Scale(scframe,variable = self.thresh,
|
||||
orient='horizontal',digits=3,length=300,resolution=0.01)
|
||||
self.sc.config(cnf={'from':-6,'to':0})
|
||||
Label(scframe,text='log10 Threshold').grid(column=0,row=0)
|
||||
self.sc.grid(row=0,column=1,columnspan=10)
|
||||
self.sc.bind('<ButtonRelease-1>',self.show)
|
||||
scframe.grid(row=3,column=0,columnspan=10)
|
||||
|
||||
enames = self.g.elementNames()
|
||||
self.nel = len(enames)
|
||||
|
||||
i = 1
|
||||
eframe = Frame(self)
|
||||
Label(eframe,text='Element').grid(column=0,row=0,sticky=W)
|
||||
for e in enames:
|
||||
Radiobutton(eframe,text=e,
|
||||
variable=self.el,value=i-1,
|
||||
command=self.show).grid(column=i,row=0,sticky=W)
|
||||
i += 1
|
||||
eframe.grid(row=0,column=0)
|
||||
|
||||
self.detailed = IntVar()
|
||||
Checkbutton(self, text = 'Show details', variable=self.detailed,
|
||||
command=self.show).grid(column=1,row=0)
|
||||
self.net = IntVar()
|
||||
Checkbutton(self, text = 'Show net flux',
|
||||
variable=self.net,
|
||||
command=self.show).grid(column=2,row=0)
|
||||
self.local = StringVar()
|
||||
Label(self,text='Species').grid(column=1,row=1,sticky=E)
|
||||
sp = Entry(self, textvariable=self.local,
|
||||
width=15)
|
||||
sp.grid(column=2,row=1)
|
||||
sp.bind('<Any-Leave>',self.show)
|
||||
|
||||
self.b = rxnpath.PathBuilder(self.g)
|
||||
|
||||
self.fmt = StringVar()
|
||||
self.fmt.set('svg')
|
||||
i = 1
|
||||
fmtframe = Frame(self)
|
||||
fmtframe.config(relief=GROOVE, bd=4)
|
||||
self.browser = IntVar()
|
||||
self.browser.set(0)
|
||||
Checkbutton(fmtframe, text = 'Display in Web Browser',
|
||||
variable=self.browser,
|
||||
command=self.show).grid(column=0,columnspan=6,row=0)
|
||||
Label(fmtframe,text='Format').grid(column=0,row=1,sticky=W)
|
||||
for e in ['svg', 'png', 'gif', 'jpg']:
|
||||
Radiobutton(fmtframe,text=e,
|
||||
variable=self.fmt,value=e,
|
||||
command=self.show).grid(column=i,row=1,sticky=W)
|
||||
i += 1
|
||||
fmtframe.grid(row=5,column=0,columnspan=10,sticky=E+W)
|
||||
|
||||
self.cv = Canvas(self,relief=SUNKEN,bd=1)
|
||||
self.cv.grid(column=0,row=4,sticky=W+E+N,columnspan=10)
|
||||
|
||||
pframe = Frame(self)
|
||||
pframe.config(relief=GROOVE, bd=4)
|
||||
self.dot = StringVar()
|
||||
self.dot.set('dot -Tgif rxnpath.dot > rxnpath.gif')
|
||||
Label(pframe,text='DOT command:').grid(column=0,row=0,sticky=W)
|
||||
Entry(pframe,width=60,textvariable=self.dot).grid(column=0,
|
||||
row=1,sticky=W)
|
||||
pframe.grid(row=6,column=0,columnspan=10,sticky=E+W)
|
||||
|
||||
self.thresh.set(-2.0)
|
||||
self.hide()
|
||||
|
||||
def hide(self):
|
||||
#self.vis.set(0)
|
||||
self.master.withdraw()
|
||||
|
||||
def show(self,e=None):
|
||||
|
||||
self.master.deiconify()
|
||||
el = self.g.elementName(self.el.get())
|
||||
det = 'false'
|
||||
if self.detailed.get() == 1: det = 'true'
|
||||
flow = 'one_way'
|
||||
if self.net.get() == 1: flow = 'net'
|
||||
self.d = rxnpath.PathDiagram(arrow_width=-2,
|
||||
flow_type=flow,
|
||||
detailed = det,
|
||||
threshold=math.pow(10.0,
|
||||
self.thresh.get()))
|
||||
node = self.local.get()
|
||||
try:
|
||||
k = self.g.speciesIndex(node)
|
||||
self.d.displayOnly(k)
|
||||
except:
|
||||
self.d.displayOnly()
|
||||
|
||||
self.b.build(element = el, diagram = self.d,
|
||||
dotfile = 'rxnpath.dot', format = 'dot')
|
||||
#self.b.build(element = el, diagram = self.d,
|
||||
# dotfile = 'rxnpath.txt', format = 'plain')
|
||||
|
||||
if self.browser.get() == 1:
|
||||
fmt = self.fmt.get()
|
||||
os.system('dot -T'+fmt+' rxnpath.dot > rxnpath.'+fmt)
|
||||
if fmt == 'svg': showsvg()
|
||||
elif fmt == 'png': showpng()
|
||||
else:
|
||||
path = 'file:///'+os.getcwd()+'/rxnpath.'+fmt
|
||||
webbrowser.open(path)
|
||||
try:
|
||||
self.cv.delete(self.image)
|
||||
except:
|
||||
pass
|
||||
self.cv.configure(width=0, height=0)
|
||||
else:
|
||||
os.system(self.dot.get())
|
||||
self.rp = None
|
||||
try:
|
||||
self.cv.delete(self.image)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
self.rp = PhotoImage(file='rxnpath.gif')
|
||||
self.cv.configure(width=self.rp.width(),
|
||||
height=self.rp.height())
|
||||
|
||||
self.image = self.cv.create_image(0,0,anchor=NW,
|
||||
image=self.rp)
|
||||
except:
|
||||
pass
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
from Cantera import *
|
||||
from Tkinter import *
|
||||
from ControlPanel import ControlWindow
|
||||
from ControlPanel import make_menu, menuitem_state
|
||||
#from Cantera.Examples.Tk import _mechdir
|
||||
import os
|
||||
|
||||
# automatically-loaded mechanisms
|
||||
_autoload = [
|
||||
(' GRI-Mech 3.0', 'gri30.cti'),
|
||||
(' Air', 'air.cti'),
|
||||
(' H/O/Ar', 'h2o2.cti')
|
||||
]
|
||||
|
||||
def testit():
|
||||
pass
|
||||
|
||||
class MechManager(Frame):
|
||||
|
||||
def __init__(self,master,app):
|
||||
Frame.__init__(self,master)
|
||||
#self.config(relief=GROOVE, bd=4)
|
||||
self.app = app
|
||||
self.master = master
|
||||
self.mechindx = IntVar()
|
||||
self.mechindx.set(1)
|
||||
|
||||
#m = Label(self, text = 'Loaded Mechanisms')
|
||||
#m.grid(column=0,row=0)
|
||||
# m.bind('<Double-1>',self.show)
|
||||
# self.mechindx.set(0)
|
||||
self.mechanisms = []
|
||||
self.mlist = [ [] ]
|
||||
i = 1
|
||||
#for m in self.mechanisms:
|
||||
# self.mlist.append((m[0], self.setMechanism, 'check', self.mechindx, i))
|
||||
# i = i + 1
|
||||
#self.mlist.append([])
|
||||
|
||||
self.mechmenu = make_menu('Mixtures', self, self.mlist)
|
||||
self.mechmenu.grid(row=0,column=0,sticky=W)
|
||||
|
||||
self.mfr = None
|
||||
|
||||
def addMechanism(self, name, mech):
|
||||
self.mechanisms.append((name, mech))
|
||||
il = len(self.mechanisms)
|
||||
self.mlist[-1] = (name, self.setMechanism, 'check', self.mechindx, il)
|
||||
self.mlist.append([])
|
||||
|
||||
self.mechmenu = make_menu('Mixtures', self, self.mlist)
|
||||
self.mechindx.set(il)
|
||||
self.mechmenu.grid(row=0,column=0,sticky=W)
|
||||
|
||||
|
||||
def delMechanism(self, mech):
|
||||
self.mechanisms.remove(mech)
|
||||
self.show()
|
||||
|
||||
## def show(self,event=None):
|
||||
## print 'show'
|
||||
## if self.mfr:
|
||||
## self.mfr.destroy()
|
||||
## self.mfr = Frame(self)
|
||||
## self.mfr.grid(row=1,column=0)
|
||||
## self.mfr.config(relief=GROOVE, bd=4)
|
||||
## Label(self.mfr,text='jkl').grid(row=0,column=0)
|
||||
## i = 0
|
||||
## for name, mech in self.mechanisms:
|
||||
## Radiobutton(self.mfr, text=name, variable=self.mechindx,
|
||||
## value = i,
|
||||
## command=self.setMechanism).grid(row=i,column=0)
|
||||
## i = i + 1
|
||||
## print 'end'
|
||||
|
||||
|
||||
def setMechanism(self, event=None):
|
||||
i = self.mechindx.get()
|
||||
self.app.mech = self.mechanisms[i-1][1]
|
||||
self.app.makeMix()
|
||||
self.app.makeWindows()
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
from Cantera import GasConstant, OneAtm
|
||||
from Cantera.num import zeros, ones
|
||||
from utilities import handleError
|
||||
|
||||
def spdict(phase, x):
|
||||
nm = phase.speciesNames()
|
||||
data = {}
|
||||
for k in range(len(nm)):
|
||||
data[nm[k]] = x[k]
|
||||
return data
|
||||
|
||||
class Species:
|
||||
def __init__(self,g,name):
|
||||
self.g = g
|
||||
t = g.temperature()
|
||||
p = g.pressure()
|
||||
x = g.moleFractions()
|
||||
self.name = name
|
||||
self.symbol = name
|
||||
self.index = g.speciesIndex(name)
|
||||
self.minTemp = g.minTemp(self.index)
|
||||
self.maxTemp = g.maxTemp(self.index)
|
||||
self.molecularWeight = g.molecularWeights()[self.index]
|
||||
self.c = []
|
||||
self.e = g.elementNames()
|
||||
self.hf0 = self.enthalpy_RT(298.15)*GasConstant*298.15
|
||||
g.setState_TPX(t,p,x)
|
||||
for n in range(len(self.e)):
|
||||
na = g.nAtoms(self.index, n)
|
||||
if na > 0:
|
||||
self.c.append((self.e[n],na))
|
||||
|
||||
def composition(self):
|
||||
return self.c
|
||||
|
||||
def enthalpy_RT(self,t):
|
||||
self.g.setTemperature(t)
|
||||
return self.g.enthalpies_RT()[self.index]
|
||||
|
||||
def cp_R(self,t):
|
||||
self.g.setTemperature(t)
|
||||
return self.g.cp_R()[self.index]
|
||||
|
||||
def entropy_R(self,t):
|
||||
self.g.setTemperature(t)
|
||||
return self.g.entropies_R()[self.index]
|
||||
|
||||
class Mix:
|
||||
def __init__(self,g):
|
||||
self.g = g
|
||||
self._mech = g
|
||||
self.nsp = g.nSpecies()
|
||||
self._moles = zeros(self.nsp,'d')
|
||||
self.wt = g.molecularWeights()
|
||||
|
||||
def setMoles(self, m):
|
||||
self._moles = m
|
||||
self.g.setMoleFractions(self._moles)
|
||||
|
||||
def moles(self):
|
||||
return self._moles
|
||||
|
||||
def totalMoles(self):
|
||||
sum = 0.0
|
||||
for k in range(self.nsp):
|
||||
sum += self._moles[k]
|
||||
return sum
|
||||
|
||||
def totalMass(self):
|
||||
sum = 0.0
|
||||
for k in range(self.nsp):
|
||||
sum += self._moles[k]*self.wt[k]
|
||||
return sum
|
||||
|
||||
def moleDict(self):
|
||||
d = {}
|
||||
nm = self.g.speciesNames()
|
||||
for e in range(self.nsp):
|
||||
d[nm[e]] = self._moles[e]
|
||||
return d
|
||||
|
||||
def setMass(self, m):
|
||||
self.setMoles( m/self.wt)
|
||||
|
||||
def mass(self):
|
||||
return self.wt*self._moles
|
||||
|
||||
def speciesNames(self):
|
||||
return self.g.speciesNames()
|
||||
|
||||
def massDict(self):
|
||||
d = {}
|
||||
nm = self.g.speciesNames()
|
||||
for e in range(self.nsp):
|
||||
d[nm[e]] = self._moles[e]*self.wt[e]
|
||||
return d
|
||||
|
||||
def set(self, temperature = None, pressure = None,
|
||||
density = None, enthalpy = None,
|
||||
entropy = None, intEnergy = None, equil = 0):
|
||||
total_mass = self.totalMass()
|
||||
|
||||
if temperature and pressure:
|
||||
self.g.setState_TP(temperature, pressure)
|
||||
if equil:
|
||||
self.g.equilibrate('TP',solver=0)
|
||||
|
||||
elif temperature and density:
|
||||
self.g.setState_TR(temperature, density)
|
||||
if equil:
|
||||
self.g.equilibrate('TV',solver=0)
|
||||
|
||||
elif pressure and enthalpy:
|
||||
self.g.setState_HP(enthalpy, pressure)
|
||||
if equil:
|
||||
self.g.equilibrate('HP',solver=0)
|
||||
|
||||
elif pressure and entropy:
|
||||
self.g.setState_SP(entropy, pressure)
|
||||
if equil:
|
||||
self.g.equilibrate('SP',solver=0)
|
||||
|
||||
elif density and entropy:
|
||||
self.g.setState_SV(entropy, 1.0/density)
|
||||
if equil:
|
||||
self.g.equilibrate('SV',solver=0)
|
||||
|
||||
elif density and intEnergy:
|
||||
self.g.setState_UV(intEnergy, 1.0/density)
|
||||
if equil:
|
||||
self.g.equilibrate('UV',solver=0)
|
||||
|
||||
# else:
|
||||
# handleError('unsupported property pair', warning=1)
|
||||
|
||||
|
||||
total_moles = total_mass/self.g.meanMolecularWeight()
|
||||
self._moles = self.g.moleFractions()*total_moles
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
from Tkinter import *
|
||||
from Cantera import *
|
||||
|
||||
from SpeciesInfo import SpeciesInfo
|
||||
|
||||
_CUTOFF = 1.e-15
|
||||
_ATOL = 1.e-15
|
||||
_RTOL = 1.e-7
|
||||
|
||||
class NewFlowFrame(Frame):
|
||||
def __init__(self,master):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.app = self.master.app
|
||||
self.controls=Frame(self)
|
||||
self.hide = IntVar()
|
||||
self.hide.set(0)
|
||||
self.p = DoubleVar()
|
||||
#self.comp.set(1.0)
|
||||
self.controls.grid(column=1,row=0,sticky=W+E+N)
|
||||
#self.makeControls()
|
||||
mf = self.master
|
||||
|
||||
e1 = Entry(self)
|
||||
e1.grid(column=0,row=0,sticky=E+W)
|
||||
e1['textvariable'] = self.p
|
||||
#e1.config(state=ENABLED)
|
||||
e1.config(relief=RIDGE)
|
||||
|
||||
## def makeControls(self):
|
||||
## Radiobutton(self.controls,text='Moles',
|
||||
## variable=self.comp,value=0,command=self.show).grid(column=0,row=0,sticky=W)
|
||||
## Radiobutton(self.controls,text='Mass',variable=self.comp,value=1,command=self.show).grid(column=0,row=1,sticky=W)
|
||||
## Radiobutton(self.controls,text='Concentration',variable=self.comp,value=2,command=self.show).grid(column=0,row=2,sticky=W)
|
||||
## Button(self.controls,text='Clear',command=self.zero).grid(column=0,row=4,sticky=W+E)
|
||||
## Button(self.controls,text='Normalize',command=self.norm).grid(column=0,row=5,sticky=W+E)
|
||||
## Checkbutton(self.controls,text='Hide Missing\nSpecies',
|
||||
## variable=self.hide,onvalue=1,offvalue=0,command=self.master.redo).grid(column=0,row=3,sticky=W)
|
||||
|
||||
|
||||
## def makeControls(self):
|
||||
## self.c = CompFrame(self)
|
||||
## self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
|
||||
## def redo(self):
|
||||
## self.update()
|
||||
## self.entries.destroy()
|
||||
## self.entries=Frame(self)
|
||||
## self.makeEntries()
|
||||
|
||||
## def minimize(self,Event=None):
|
||||
## self.c.hide.set(1)
|
||||
## self.redo()
|
||||
## self.c.grid_forget()
|
||||
## self.entries.bind("<Double-1>",self.maximize)
|
||||
|
||||
## def maximize(self,Event=None):
|
||||
## self.c.hide.set(0)
|
||||
## self.redo()
|
||||
## self.c.grid(column=1,row=0,sticky=E+W+N+S)
|
||||
## self.entries.bind("<Double-1>",self.minimize)
|
||||
|
||||
|
||||
## def makeEntries(self):
|
||||
## self.entries.grid(row=0,column=0,sticky=W+N+S+E)
|
||||
## self.entries.config(relief=GROOVE,bd=4)
|
||||
## DATAKEYS = self.top.species
|
||||
## self.variable = {}
|
||||
|
||||
## n=0
|
||||
## ncol = 3
|
||||
## col = 0
|
||||
## row = 60
|
||||
|
||||
## presbox =
|
||||
|
||||
## for sp in DATAKEYS:
|
||||
## s = sp # self.top.species[sp]
|
||||
## k = s.index
|
||||
## if row > 15:
|
||||
## row = 0
|
||||
## col = col + 2
|
||||
## l = Label(self.entries,text='Species')
|
||||
## l.grid(column=col,row=row,sticky=E+W)
|
||||
## e1 = Entry(self.entries)
|
||||
## e1.grid(column=col+1,row=row,sticky=E+W)
|
||||
## e1['textvariable'] = self.var
|
||||
## e1.config(state=DISABLED)
|
||||
## e1.config(bg='lightyellow',relief=RIDGE)
|
||||
## row = row + 1
|
||||
|
||||
## spname = s.name
|
||||
## val = self.comp[k]
|
||||
## if not self.c.hide.get() or val: showit = 1
|
||||
## else: showit = 0
|
||||
|
||||
## l=SpeciesInfo(self.entries,species=s,
|
||||
## text=spname,relief=FLAT,justify=RIGHT,
|
||||
## fg='darkblue')
|
||||
## entry1 = Entry(self.entries)
|
||||
## self.variable[spname] = DoubleVar()
|
||||
## self.variable[spname].set(self.comp[k])
|
||||
## entry1['textvariable']=self.variable[spname]
|
||||
## entry1.bind('<Any-Leave>',self.up)
|
||||
## if showit:
|
||||
## l.grid(column= col ,row=row,sticky=E)
|
||||
## entry1.grid(column=col+1,row=row)
|
||||
## n=n+1
|
||||
## row = row + 1
|
||||
## if self.c.hide.get():
|
||||
## b=Button(self.entries,height=1,command=self.maximize)
|
||||
## else:
|
||||
## b=Button(self.entries,command=self.minimize)
|
||||
## b.grid(column=col,columnspan=2, row=row+1)
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
#
|
||||
# function getElements displays a periodic table, and returns a list of
|
||||
# the selected elements
|
||||
#
|
||||
|
||||
from Tkinter import *
|
||||
from types import *
|
||||
import tkMessageBox
|
||||
|
||||
from Cantera import *
|
||||
|
||||
class SpeciesFrame(Frame):
|
||||
|
||||
def __init__(self, master, speciesList = [], selected=[]):
|
||||
Frame.__init__(self,master)
|
||||
self.master = master
|
||||
self.control = Frame(self)
|
||||
self.species = {}
|
||||
for sp in speciesList:
|
||||
self.species[sp.name] = sp
|
||||
|
||||
self.control.config(relief=GROOVE,bd=4)
|
||||
Button(self.control, text = 'Display',command=self.show).pack(fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = 'Clear',command=self.clear).pack(fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = ' OK ',command=self.get).pack(side=BOTTOM,
|
||||
fill=X,pady=3, padx=10)
|
||||
Button(self.control, text = 'Cancel',command=self.master.quit).pack(side=BOTTOM,
|
||||
fill=X,pady=3, padx=10)
|
||||
self.entries = Frame(self)
|
||||
self.entries.pack(side=LEFT)
|
||||
self.control.pack(side=RIGHT,fill=Y)
|
||||
self.c = {}
|
||||
self.selected = selected
|
||||
n=0
|
||||
ncol = 8
|
||||
rw = 1
|
||||
col = 0
|
||||
list = self.species.values()
|
||||
list.sort()
|
||||
for sp in list:
|
||||
el = sp.name
|
||||
self.species[el] = Frame(self.entries)
|
||||
self.species[el].config(relief=GROOVE, bd=4, bg=self.color(el))
|
||||
self.c[el] = Button(self.species[el],text=el,bg=self.color(el),width=6,relief=FLAT)
|
||||
self.c[el].pack()
|
||||
self.c[el].bind("<Button-1>",self.setColors)
|
||||
self.species[el].grid(row= rw, column = col,sticky=W+N+E+S)
|
||||
col = col + 1
|
||||
if col > ncol:
|
||||
rw = rw + 1
|
||||
col = 0
|
||||
Label(self.entries,text='select the species to be included, and then press OK.\nTo view the properties of the selected species, press Display ').grid(row=0, column=2, columnspan=10, sticky=W)
|
||||
|
||||
|
||||
def select(self, el):
|
||||
self.c[el]['relief'] = RAISED
|
||||
self.c[el]['bg'] = self.color(el, sel=1)
|
||||
|
||||
def deselect(self, el):
|
||||
self.c[el]['relief'] = FLAT
|
||||
self.c[el]['bg'] = self.color(el, sel=0)
|
||||
|
||||
def selectSpecies(self,splist):
|
||||
for sp in splist:
|
||||
spname = sp.name
|
||||
self.select(spname)
|
||||
|
||||
def setColors(self,event):
|
||||
el = event.widget['text']
|
||||
if event.widget['relief'] == RAISED:
|
||||
event.widget['relief'] = FLAT
|
||||
back = self.color(el, sel=0)
|
||||
fore = '#ffffff'
|
||||
elif event.widget['relief'] == FLAT:
|
||||
event.widget['relief'] = RAISED
|
||||
fore = '#000000'
|
||||
back = self.color(el, sel=1)
|
||||
event.widget['bg'] = back
|
||||
event.widget['fg'] = fore
|
||||
|
||||
def color(self, el, sel=0):
|
||||
_normal = ['#88dddd','#005500','#dd8888']
|
||||
_selected = ['#aaffff','#88dd88','#ffaaaa']
|
||||
#row, column = _pos[el]
|
||||
if sel: list = _selected
|
||||
else: list = _normal
|
||||
return list[1]
|
||||
#if column < 3:
|
||||
# return list[0]
|
||||
#elif column > 12:
|
||||
# return list[1]
|
||||
#else:
|
||||
# return list[2]
|
||||
|
||||
def show(self):
|
||||
selected = []
|
||||
for sp in self.species.values():
|
||||
if self.c[sp.name]['relief'] == RAISED:
|
||||
selected.append(sp)
|
||||
#showElementProperties(selected)
|
||||
|
||||
def get(self):
|
||||
self.selected = []
|
||||
for sp in self.species.values():
|
||||
if self.c[sp.name]['relief'] == RAISED:
|
||||
self.selected.append(sp)
|
||||
#self.master.quit()'
|
||||
self.master.destroy()
|
||||
|
||||
def clear(self):
|
||||
for sp in self.species.values():
|
||||
self.c[sp]['bg'] = self.color(sp, sel=0)
|
||||
self.c[sp]['relief'] = FLAT
|
||||
|
||||
## class ElementPropertyFrame(Frame):
|
||||
## def __init__(self,master,ellist):
|
||||
## Frame.__init__(self,master)
|
||||
## n = 1
|
||||
## ellist.sort()
|
||||
## Label(self,text='Name').grid(column=0,row=0,sticky=W+S,padx=10,pady=10)
|
||||
## Label(self,text='Atomic \nNumber').grid(column=1,row=0,sticky=W+S,padx=10,pady=10)
|
||||
## Label(self,
|
||||
## text='Atomic \nWeight').grid(column=2,
|
||||
## row=0,
|
||||
## sticky=W+S,
|
||||
## padx=10,
|
||||
## pady=10)
|
||||
## for el in ellist:
|
||||
## Label(self,
|
||||
## text=el.name).grid(column=0,
|
||||
## row=n,
|
||||
## sticky=W,
|
||||
## padx=10)
|
||||
## Label(self,
|
||||
## text=`el.atomicNumber`).grid(column=1,
|
||||
## row=n,
|
||||
## sticky=W,
|
||||
## padx=10)
|
||||
## Label(self,
|
||||
## text=`el.atomicWeight`).grid(column=2,
|
||||
## row=n,
|
||||
## sticky=W,
|
||||
## padx=10)
|
||||
## n = n + 1
|
||||
|
||||
|
||||
# utility functions
|
||||
|
||||
def getSpecies(splist=[],selected=[]):
|
||||
master = Toplevel()
|
||||
master.title('Species')
|
||||
t = SpeciesFrame(master,splist,selected)
|
||||
if splist: t.selectSpecies(splist)
|
||||
t.pack()
|
||||
t.focus_set()
|
||||
t.grab_set()
|
||||
t.wait_window()
|
||||
try:
|
||||
master.destroy()
|
||||
except TclError:
|
||||
pass
|
||||
return t.selected
|
||||
|
||||
|
||||
# display table of selected element properties in a window
|
||||
def showElementProperties(ellist):
|
||||
m = Tk()
|
||||
m.title('Element Properties')
|
||||
elem = []
|
||||
ElementPropertyFrame(m, ellist).pack()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print getSpecies()
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
from Tkinter import *
|
||||
import re, math
|
||||
from Cantera import *
|
||||
from Units import temperature, specificEnergy, specificEntropy
|
||||
from UnitChooser import UnitVar
|
||||
from GraphFrame import Graph
|
||||
|
||||
def testit():
|
||||
pass
|
||||
|
||||
class SpeciesInfo(Label):
|
||||
def __init__(self,master,phase=None,species=None,**opt):
|
||||
Label.__init__(self,master,opt)
|
||||
self.sp = species
|
||||
self.phase = phase
|
||||
self.bind('<Double-1>', self.show)
|
||||
self.bind('<Button-3>', self.show)
|
||||
self.bind('<Any-Enter>', self.highlight)
|
||||
self.bind('<Any-Leave>', self.nohighlight)
|
||||
|
||||
|
||||
def highlight(self, event=None):
|
||||
self.config(fg='yellow')
|
||||
|
||||
def nohighlight(self, event=None):
|
||||
self.config(fg='darkblue')
|
||||
|
||||
def show(self, event):
|
||||
self.new=Toplevel()
|
||||
self.new.title(self.sp.symbol)
|
||||
#self.new.transient(self.master)
|
||||
self.new.bind("<Return>", self.update,"+")
|
||||
self.cpr = 0.0
|
||||
self.t = 0.0
|
||||
self.cpl = 0.0
|
||||
self.tl = 0.0
|
||||
self.cpp = [[(0.0, 0.0, 'red')]]
|
||||
|
||||
# elemental composition
|
||||
self.eframe = Frame(self.new)
|
||||
self.eframe.config(relief=GROOVE,bd=4)
|
||||
self.eframe.grid(row=0,column=0,columnspan=10,sticky=E+W)
|
||||
r = 1
|
||||
Label(self.eframe,text='Atoms:')\
|
||||
.grid(row=0,column=0,sticky=N+W)
|
||||
for el, c in self.sp.composition():
|
||||
Label(self.eframe,text=`int(c)`+' '+el).grid(row=0,column=r)
|
||||
r = r + 1
|
||||
|
||||
|
||||
# thermodynamic properties
|
||||
self.thermo = Frame(self.new)
|
||||
self.thermo.config(relief=GROOVE,bd=4)
|
||||
self.thermo.grid(row=1,column=0,columnspan=10,sticky=N+E+W)
|
||||
Label(self.thermo,text = 'Standard Heat of Formation at 298 K: ').grid(row=0, column=0, sticky=W)
|
||||
Label(self.thermo,text = '%8.2f kJ/mol' % (self.sp.hf0*1.0e-6)).grid(row=0, column=1, sticky=W)
|
||||
Label(self.thermo,text = 'Molar Mass: ').grid(row=1, column=0, sticky=W)
|
||||
Label(self.thermo,text = self.sp.molecularWeight).grid(row=1, column=1, sticky=W)
|
||||
labels = ['Temperature', 'c_p', 'Enthalpy', 'Entropy']
|
||||
units = [temperature, specificEntropy, specificEnergy, specificEntropy]
|
||||
whichone = [0, 1, 1, 1]
|
||||
|
||||
r = 2
|
||||
self.prop = []
|
||||
for prop in labels:
|
||||
Label(self.thermo,text=prop).grid(row=r,column=0,sticky=W)
|
||||
p = UnitVar(self.thermo,units[r-2],whichone[r-2])
|
||||
p.grid(row=r,column=1,sticky=W)
|
||||
p.v.config(state=DISABLED,bg='lightgray')
|
||||
self.prop.append(p)
|
||||
r = r + 1
|
||||
|
||||
tmin = self.sp.minTemp
|
||||
tmax = self.sp.maxTemp
|
||||
cp = self.sp.cp_R(tmin)
|
||||
hh = self.sp.enthalpy_RT(tmin)
|
||||
ss = self.sp.entropy_R(tmin)
|
||||
|
||||
self.prop[0].bind("<Any-Enter>", self.decouple)
|
||||
self.prop[0].bind("<Any-Leave>", self.update)
|
||||
self.prop[0].bind("<Key>", self.update)
|
||||
self.prop[0].v.config(state=NORMAL,bg='white')
|
||||
self.prop[0].set(300.0)
|
||||
|
||||
self.graphs = Frame(self.new)
|
||||
self.graphs.config(relief=GROOVE,bd=4)
|
||||
self.graphs.grid(row=2,column=0,columnspan=10,sticky=E+W)
|
||||
|
||||
self.cpdata = []
|
||||
self.hdata = []
|
||||
self.sdata = []
|
||||
t = tmin
|
||||
n = int((tmax - tmin)/100.0)
|
||||
while t <= tmax:
|
||||
self.cpdata.append((t,self.sp.cp_R(t)))
|
||||
self.hdata.append((t,self.sp.enthalpy_RT(t)))
|
||||
self.sdata.append((t,self.sp.entropy_R(t)))
|
||||
t = t + n
|
||||
|
||||
# specific heat
|
||||
|
||||
Label(self.graphs,text='c_p/R').grid(row=0,column=0,sticky=W+E)
|
||||
ymin, ymax, dtick = self.plotLimits(self.cpdata)
|
||||
self.cpg = Graph(self.graphs,'',tmin,tmax,ymin,ymax,
|
||||
pixelX=150,pixelY=150)
|
||||
self.cpg.canvas.config(bg='white')
|
||||
self.cpg.grid(row=1,column=0,columnspan=2,sticky=W+E)
|
||||
self.ticks(ymin, ymax, dtick, tmin, tmax, self.cpg)
|
||||
|
||||
# enthalpy
|
||||
Label(self.graphs,text='enthalpy/RT').grid(row=0,column=3,sticky=W+E)
|
||||
ymin, ymax, dtick = self.plotLimits(self.hdata)
|
||||
self.hg = Graph(self.graphs,'',tmin,tmax,ymin,ymax,
|
||||
pixelX=150,pixelY=150)
|
||||
self.hg.canvas.config(bg='white')
|
||||
self.hg.grid(row=1,column=3,columnspan=2,sticky=W+E)
|
||||
self.ticks(ymin, ymax, dtick, tmin, tmax, self.hg)
|
||||
|
||||
# entropy
|
||||
Label(self.graphs,text='entropy/R').grid(row=0,column=5,sticky=W+E)
|
||||
ymin, ymax, dtick = self.plotLimits(self.sdata)
|
||||
self.sg = Graph(self.graphs,'',tmin,tmax,ymin,ymax,
|
||||
pixelX=150,pixelY=150)
|
||||
self.sg.canvas.config(bg='white')
|
||||
self.sg.grid(row=1,column=5,columnspan=2,sticky=W+E)
|
||||
self.ticks(ymin, ymax, dtick, tmin, tmax, self.sg)
|
||||
|
||||
n = int((tmax - tmin)/100.0)
|
||||
t = tmin
|
||||
self.cpp = []
|
||||
|
||||
for t, cp in self.cpdata:
|
||||
self.cpg.join([(t,cp,'red')])
|
||||
for t, h in self.hdata:
|
||||
self.hg.join([(t,h,'green')])
|
||||
for t, s in self.sdata:
|
||||
self.sg.join([(t,s,'blue')])
|
||||
|
||||
self.cpdot = self.cpg.plot(tmin,cp,'red')
|
||||
self.hdot = self.hg.plot(tmin,hh,'green')
|
||||
self.sdot = self.sg.plot(tmin,ss,'blue')
|
||||
|
||||
b=Button(self.new,text=' OK ',command=self.finished, default=ACTIVE)
|
||||
#ed=Button(self.new,text='Edit',command=testit)
|
||||
b.grid(column=0, row=4,sticky=W)
|
||||
#ed.grid(column=1,row=4,sticky=W)
|
||||
|
||||
self.scfr = Frame(self.new)
|
||||
self.scfr.config(relief=GROOVE,bd=4)
|
||||
self.scfr.grid(row=3,column=0,columnspan=10,sticky=N+E+W)
|
||||
self.sc = Scale(self.scfr,command=self.update,variable = self.prop[0].x,
|
||||
orient='horizontal',digits=7,length=400)
|
||||
self.sc.config(cnf={'from':tmin,'to':tmax})
|
||||
self.sc.bind('<Any-Enter>',self.couple)
|
||||
self.scfr.bind('<Any-Leave>',self.decouple)
|
||||
self.sc.grid(row=0,column=0,columnspan=10)
|
||||
|
||||
def decouple(self,event=None):
|
||||
d = DoubleVar()
|
||||
xx = self.prop[0].get()
|
||||
d.set(xx)
|
||||
self.sc.config(variable = d)
|
||||
|
||||
def couple(self,event=None):
|
||||
self.sc.config(variable = self.prop[0].x)
|
||||
#self.update()
|
||||
|
||||
def update(self,event=None):
|
||||
try:
|
||||
tmp = self.prop[0].get()
|
||||
cnd = self.sp.cp_R(tmp)
|
||||
cc = cnd*GasConstant
|
||||
self.prop[1].set(cc)
|
||||
hnd = self.sp.enthalpy_RT(tmp)
|
||||
hh = hnd*tmp*GasConstant
|
||||
self.prop[2].set(hh)
|
||||
snd = self.sp.entropy_R(tmp)
|
||||
ss = snd*tmp*GasConstant
|
||||
self.prop[3].set(ss)
|
||||
|
||||
|
||||
self.cppoint = tmp, cnd
|
||||
self.hpoint = tmp, hnd
|
||||
self.spoint = tmp, snd
|
||||
if hasattr(self, 'cpdot'):
|
||||
self.cpg.delete(self.cpdot)
|
||||
self.cpdot = self.cpg.plot(self.cppoint[0], self.cppoint[1],'red')
|
||||
self.hg.delete(self.hdot)
|
||||
self.hdot = self.hg.plot(self.hpoint[0], self.hpoint[1],'green')
|
||||
self.sg.delete(self.sdot)
|
||||
self.sdot = self.sg.plot(self.spoint[0], self.spoint[1],'blue')
|
||||
except:
|
||||
pass
|
||||
|
||||
def plotLimits(self, xy):
|
||||
ymax = -1.e10
|
||||
ymin = 1.e10
|
||||
for x, y in xy:
|
||||
if y > ymax: ymax = y
|
||||
if y < ymin: ymin = y
|
||||
|
||||
dy = abs(ymax - ymin)
|
||||
if dy < 0.2*ymin:
|
||||
ymin = ymin*.9
|
||||
ymax = ymax*1.1
|
||||
dy = abs(ymax - ymin)
|
||||
else:
|
||||
ymin = ymin - 0.1*dy
|
||||
ymax = ymax + 0.1*dy
|
||||
dy = abs(ymax - ymin)
|
||||
|
||||
p10 = math.floor(math.log10(0.1*dy))
|
||||
fctr = math.pow(10.0, p10)
|
||||
mm = [2.0, 2.5, 2.0]
|
||||
i = 0
|
||||
while dy/fctr > 5:
|
||||
fctr = mm[i % 3]*fctr
|
||||
i = i + 1
|
||||
ymin = fctr*math.floor(ymin/fctr)
|
||||
ymax = fctr*(math.floor(ymax/fctr + 1))
|
||||
return (ymin, ymax, fctr)
|
||||
|
||||
def ticks(self, ymin, ymax, dtick, tmin, tmax, plot):
|
||||
ytick = ymin
|
||||
eps = 1.e-3
|
||||
while ytick <= ymax:
|
||||
if abs(ytick) < eps:
|
||||
plot.join([(tmin, ytick, 'gray')])
|
||||
plot.join([(tmax, ytick, 'gray')])
|
||||
plot.last_points = []
|
||||
else:
|
||||
plot.join([(tmin, ytick, 'gray')])
|
||||
plot.join([(tmin + 0.05*(tmax - tmin), ytick, 'gray')])
|
||||
plot.last_points = []
|
||||
plot.join([(2.0*tmax, ytick, 'gray')])
|
||||
plot.join([(tmax - 0.05*(tmax - tmin), ytick, 'gray')])
|
||||
plot.last_points = []
|
||||
|
||||
ytick = ytick + dtick
|
||||
|
||||
def finished(self,event=None):
|
||||
self.new.destroy()
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
|
||||
|
||||
from Cantera import *
|
||||
from Tkinter import *
|
||||
|
||||
from Units import temperature, pressure, density, specificEnergy, specificEntropy
|
||||
from UnitChooser import UnitVar
|
||||
from ThermoProp import ThermoProp
|
||||
from utilities import handleError
|
||||
|
||||
_PRESSURE = 1
|
||||
_TEMPERATURE = 0
|
||||
_DENSITY = 2
|
||||
_INTENERGY = 3
|
||||
_ENTHALPY = 4
|
||||
_ENTROPY = 5
|
||||
|
||||
class ThermoFrame(Frame):
|
||||
def __init__(self,master,top):
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
self.top = top
|
||||
self.mix = self.top.mix
|
||||
self.warn = 0
|
||||
self.internal = Frame(self)
|
||||
self.internal.pack(side=LEFT,anchor=N+W,padx=2,pady=2)
|
||||
self.controls=Frame(self.internal)
|
||||
self.controls.pack(side=LEFT,anchor=N+W,padx=4,pady=5)
|
||||
|
||||
self.entries=Frame(self.internal)
|
||||
self.entries.pack(side=LEFT,anchor=N,padx=4,pady=2)
|
||||
self.makeEntries()
|
||||
self.makeControls()
|
||||
self.showState()
|
||||
|
||||
def makeControls(self):
|
||||
Button(self.controls,text='Set State', width=15,
|
||||
command=self.setState).grid(column=0,row=0)
|
||||
self.equil = IntVar()
|
||||
self.equil.set(0)
|
||||
Button(self.controls,text='Equilibrate', width=15,
|
||||
command=self.eqset).grid(column=0,row=1)
|
||||
## Radiobutton(self.controls,text='Frozen',variable = self.equil,
|
||||
## command=self.freeze,value=0).grid(column=0,row=2,sticky='W')
|
||||
## Radiobutton(self.controls,text='Equilibrium',
|
||||
## variable=self.equil,
|
||||
## command=self.eqset,value=1).grid(column=0,row=3,sticky='W')
|
||||
|
||||
def eqset(self):
|
||||
self.equil.set(1)
|
||||
self.setState()
|
||||
self.equil.set(0)
|
||||
#if self.top.mixframe:
|
||||
# self.top.mixframe.redo()
|
||||
|
||||
def freeze(self):
|
||||
self.equil.set(0)
|
||||
if self.top.mixframe:
|
||||
self.top.mixframe.redo()
|
||||
|
||||
def makeEntries(self):
|
||||
self.entries.pack()
|
||||
self.variable = {}
|
||||
self.prop = []
|
||||
props = ['Temperature', 'Pressure', 'Density',
|
||||
'Internal Energy', 'Enthalpy', 'Entropy']
|
||||
units = [temperature, pressure, density, specificEnergy,
|
||||
specificEnergy, specificEntropy]
|
||||
defaultunit = [0, 2, 0, 1, 1, 1]
|
||||
for i in range(len(props)):
|
||||
self.prop.append(ThermoProp(self.entries, self, i, props[i],
|
||||
0.0, units[i], defaultunit[i]))
|
||||
#self.prop[-1].entry.bind("<Any-Leave>",self.setState)
|
||||
self.last2 = self.prop[3]
|
||||
self.last1 = self.prop[2]
|
||||
self.prop[0].checked.set(1)
|
||||
self.prop[0].check()
|
||||
self.prop[1].checked.set(1)
|
||||
self.prop[1].check()
|
||||
self.showState()
|
||||
|
||||
def checkTPBoxes(self):
|
||||
if not self.prop[0].isChecked():
|
||||
self.prop[0].checked.set(1)
|
||||
self.prop[0].check()
|
||||
if not self.prop[1].isChecked():
|
||||
self.prop[1].checked.set(1)
|
||||
self.prop[1].check()
|
||||
|
||||
def showState(self):
|
||||
self.prop[_TEMPERATURE].set(self.mix.g.temperature())
|
||||
self.prop[_PRESSURE].set(self.mix.g.pressure())
|
||||
self.prop[_DENSITY].set(self.mix.g.density())
|
||||
self.prop[_INTENERGY].set(self.mix.g.intEnergy_mass())
|
||||
self.prop[_ENTHALPY].set(self.mix.g.enthalpy_mass())
|
||||
self.prop[_ENTROPY].set(self.mix.g.entropy_mass())
|
||||
|
||||
def setState(self,event=None):
|
||||
if event:
|
||||
self.warn = 0
|
||||
else:
|
||||
self.warn = 1
|
||||
self.top.mixfr.update()
|
||||
i = self.equil.get()
|
||||
optlist = ['frozen','equilibrium']
|
||||
opt = [optlist[i]]
|
||||
|
||||
if self.prop[_PRESSURE].isChecked() \
|
||||
and self.prop[_TEMPERATURE].isChecked():
|
||||
self.mix.set(
|
||||
temperature = self.prop[_TEMPERATURE].get(),
|
||||
pressure = self.prop[_PRESSURE].get(),
|
||||
equil=i)
|
||||
|
||||
elif self.prop[_DENSITY].isChecked() \
|
||||
and self.prop[_TEMPERATURE].isChecked():
|
||||
self.mix.set(
|
||||
temperature = self.prop[_TEMPERATURE].get(),
|
||||
density = self.prop[_DENSITY].get(),
|
||||
equil=i)
|
||||
|
||||
elif self.prop[_ENTROPY].isChecked() \
|
||||
and self.prop[_PRESSURE].isChecked():
|
||||
self.mix.set(pressure = self.prop[_PRESSURE].get(),
|
||||
entropy = self.prop[_ENTROPY].get(),
|
||||
equil=i)
|
||||
|
||||
elif self.prop[_ENTHALPY].isChecked() \
|
||||
and self.prop[_PRESSURE].isChecked():
|
||||
self.mix.set(pressure = self.prop[_PRESSURE].get(),
|
||||
enthalpy = self.prop[_ENTHALPY].get(),
|
||||
equil=i)
|
||||
|
||||
elif self.prop[_INTENERGY].isChecked() \
|
||||
and self.prop[_DENSITY].isChecked():
|
||||
self.mix.set(density = self.prop[_DENSITY].get(),
|
||||
intEnergy = self.prop[_INTENERGY].get(),
|
||||
equil=i)
|
||||
else:
|
||||
if self.warn > 0:
|
||||
handleError("unsupported property pair")
|
||||
|
||||
self.top.update()
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
from Tkinter import *
|
||||
from UnitChooser import UnitVar
|
||||
|
||||
_tv = ['Temperature','Internal Energy','Enthalpy']
|
||||
_pv = ['Pressure', 'Density']
|
||||
|
||||
def badpair(a,b):
|
||||
if a.name in _tv:
|
||||
if not b.name in _pv:
|
||||
return 1
|
||||
else:
|
||||
if not b.name in _tv:
|
||||
return 1
|
||||
|
||||
class ThermoProp:
|
||||
def __init__(self, master, thermoframe, row, name, value, units, defaultunit=0):
|
||||
self.value = DoubleVar()
|
||||
self.thermoframe = thermoframe
|
||||
self.entry = UnitVar(master,units,defaultunit)
|
||||
self.entry.grid(column=1,row=row,sticky=W)
|
||||
self.entry.v.config(state=DISABLED,bg='lightgray')
|
||||
self.checked=IntVar()
|
||||
self.checked.set(0)
|
||||
self.name = name
|
||||
self.c=Checkbutton(master,
|
||||
text=name,
|
||||
variable=self.checked,
|
||||
onvalue=1,
|
||||
offvalue=0,
|
||||
command=self.check
|
||||
)
|
||||
self.c.grid(column=0,row=row, sticky=W+N)
|
||||
|
||||
def check(self):
|
||||
if self == self.thermoframe.last1:
|
||||
self.checked.set(1)
|
||||
return
|
||||
elif self == self.thermoframe.last2:
|
||||
self.checked.set(1)
|
||||
self.thermoframe.last2 = self.thermoframe.last1
|
||||
self.thermoframe.last1 = self
|
||||
return
|
||||
# elif badpair(self, self.thermoframe.last1):
|
||||
# self.checked.set(0)
|
||||
# return
|
||||
|
||||
self._check()
|
||||
self.thermoframe.last2.checked.set(0)
|
||||
self.thermoframe.last2._check()
|
||||
self.thermoframe.last2 = self.thermoframe.last1
|
||||
self.thermoframe.last1 = self
|
||||
|
||||
def _check(self):
|
||||
if self.isChecked():
|
||||
self.entry.v.config(state=NORMAL,bg='white')
|
||||
else:
|
||||
self.entry.v.config(state=DISABLED,bg='lightgray')
|
||||
|
||||
def isChecked(self):
|
||||
return self.checked.get()
|
||||
|
||||
def set(self, value):
|
||||
self.entry.set(value)
|
||||
|
||||
def get(self):
|
||||
return self.entry.get()
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
from Tkinter import *
|
||||
|
||||
class TransportFrame(Frame):
|
||||
def show(self, i, frame, row, col):
|
||||
if self.checked[i].get():
|
||||
frame.grid(row=row,column=col,sticky=N+E+S+W)
|
||||
else:
|
||||
frame.grid_forget()
|
||||
|
||||
def showcomp(self):
|
||||
self.show(0, self.top.mixfr, 8, 0)
|
||||
|
||||
def showthermo(self):
|
||||
self.show(1, self.top.thermo, 7, 0)
|
||||
|
||||
def __init__(self,master,top):
|
||||
self.top = top
|
||||
self.c = []
|
||||
self.checked = []
|
||||
Frame.__init__(self,master)
|
||||
self.config(relief=GROOVE, bd=4)
|
||||
lbl = ['multicomponent', 'mixture-averaged']
|
||||
cmds = [self.showcomp, self.showthermo]
|
||||
for i in range(2):
|
||||
self.checked.append(IntVar())
|
||||
self.checked[i].set(0)
|
||||
self.c.append(Checkbutton(self,
|
||||
text=lbl[i],
|
||||
variable=self.checked[i],
|
||||
onvalue=1,
|
||||
offvalue=0,
|
||||
command=cmds[i]
|
||||
))
|
||||
self.c[i].grid(column=i,row=0, sticky=W+N)
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
from Tkinter import *
|
||||
import re
|
||||
|
||||
class UnitVar(Frame):
|
||||
def __init__(self,master,unitmod,defaultunit=0):
|
||||
Frame.__init__(self,master)
|
||||
self.x = DoubleVar()
|
||||
self.xsi = 0.0
|
||||
self.x.set(0.0)
|
||||
self.unitmod = unitmod
|
||||
try:
|
||||
self.unitlist = self.unitmod.units
|
||||
except:
|
||||
self.unitlist = []
|
||||
unitlist=dir(self.unitmod)
|
||||
for it in unitlist:
|
||||
if it[0] != '_':
|
||||
self.unitlist.append(it)
|
||||
self.v = Entry(self,textvariable=self.x)
|
||||
self.s = StringVar()
|
||||
tmp = re.sub('__',' / ',self.unitlist[defaultunit])
|
||||
self.s.set(tmp)
|
||||
self.conv = eval('self.unitmod.'+re.sub(' / ','__',self.s.get())).value
|
||||
self.u = Label(self)
|
||||
self.u.config(textvariable=self.s,fg='darkblue')
|
||||
self.u.bind('<Double-1>', self.select)
|
||||
self.u.bind('<Any-Enter>',self.highlight)
|
||||
self.u.bind('<Any-Leave>',self.nohighlight)
|
||||
self.v.grid(row=0,column=0)
|
||||
self.u.grid(row=0,column=1)
|
||||
|
||||
def highlight(self, event=None):
|
||||
self.u.config(fg='yellow')
|
||||
|
||||
def nohighlight(self, event=None):
|
||||
self.u.config(fg='darkblue')
|
||||
|
||||
def select(self, event):
|
||||
self.new=Toplevel()
|
||||
self.new.title("Units")
|
||||
self.new.transient(self.master)
|
||||
self.new.bind("<Return>", self.finished,"+")
|
||||
|
||||
r=0
|
||||
c=0
|
||||
for each in self.unitlist:
|
||||
if each[0] != '_' and each[:1] != '__' and each != 'SI':
|
||||
each = re.sub('__',' / ',each)
|
||||
Radiobutton(self.new,
|
||||
text=each,
|
||||
variable=self.u['textvariable'],
|
||||
value=each,
|
||||
command=self.update,
|
||||
).grid(column=c, row=r, sticky=W)
|
||||
r=r+1
|
||||
if (r>10):
|
||||
r=0
|
||||
c=c+1
|
||||
r=r+1
|
||||
|
||||
b=Button(self.new,text='OK',command=self.finished, default=ACTIVE)
|
||||
b.grid(column=c, row=r)
|
||||
|
||||
self.new.grab_set()
|
||||
self.new.focus_set()
|
||||
self.new.wait_window()
|
||||
|
||||
def finished(self,event=None):
|
||||
self.new.destroy()
|
||||
|
||||
def update(self):
|
||||
self.xsi = self.x.get() * self.conv
|
||||
self.conv = eval('self.unitmod.'+re.sub(' / ','__',self.s.get())).value
|
||||
self.x.set(self.xsi/self.conv)
|
||||
|
||||
def get(self):
|
||||
self.xsi = self.x.get() * self.conv
|
||||
return self.xsi
|
||||
|
||||
def set(self,value):
|
||||
self.xsi = value
|
||||
self.x.set(value/self.conv)
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
from unit import unit, dimensionless
|
||||
|
||||
#
|
||||
# The basic SI units
|
||||
#
|
||||
meter = unit(1.0, (1, 0, 0, 0, 0, 0, 0))
|
||||
kilogram = unit(1.0, (0, 1, 0, 0, 0, 0, 0))
|
||||
second = unit(1.0, (0, 0, 1, 0, 0, 0, 0))
|
||||
ampere = unit(1.0, (0, 0, 0, 1, 0, 0, 0))
|
||||
kelvin = unit(1.0, (0, 0, 0, 0, 1, 0, 0))
|
||||
mole = unit(1.0, (0, 0, 0, 0, 0, 1, 0))
|
||||
candela = unit(1.0, (0, 0, 0, 0, 0, 0, 1))
|
||||
|
||||
#
|
||||
# The 21 derived SI units with special names
|
||||
#
|
||||
radian = dimensionless # plane angle
|
||||
steradian = dimensionless # solid angle
|
||||
|
||||
hertz = 1/second # frequency
|
||||
|
||||
newton = meter*kilogram/second**2 # force
|
||||
pascal = newton/meter**2 # pressure
|
||||
joule = newton*meter # work, heat
|
||||
watt = joule/second # power, radiant flux
|
||||
|
||||
coulomb = ampere*second # electric charge
|
||||
volt = watt/ampere # electric potential difference
|
||||
farad = coulomb/volt # capacitance
|
||||
ohm = volt/ampere # electric resistance
|
||||
siemens = ampere/volt # electric conductance
|
||||
weber = volt*second # magnetic flux
|
||||
tesla = weber/meter**2 # magnetic flux density
|
||||
henry = weber/ampere # inductance
|
||||
|
||||
celsius = kelvin # Celsius temperature
|
||||
|
||||
lumen = candela*steradian # luminous flux
|
||||
lux = lumen/meter**2 # illuminance
|
||||
|
||||
becquerel = 1/second # radioactivity
|
||||
gray = joule/kilogram # absorbed dose
|
||||
sievert = joule/kilogram # dose equivalent
|
||||
|
||||
#
|
||||
# The prefixes
|
||||
#
|
||||
|
||||
yotta = 1e24
|
||||
zetta = 1e21
|
||||
exa = 1e18
|
||||
peta = 1e15
|
||||
tera = 1e12
|
||||
giga = 1e9
|
||||
mega = 1e6
|
||||
kilo = 1000
|
||||
hecto = 100
|
||||
deka = 10
|
||||
deci = .1
|
||||
centi = .01
|
||||
milli = .001
|
||||
micro = 1e-6
|
||||
nano = 1e-9
|
||||
pico = 1e-12
|
||||
femto = 1e-15
|
||||
atto = 1e-18
|
||||
zepto = 1e-21
|
||||
yocto = 1e-24
|
||||
|
||||
|
||||
#
|
||||
# Test
|
||||
#
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print "The 7 base SI units:"
|
||||
print " meter: %s" % meter
|
||||
print " kilogram: %s" % kilogram
|
||||
print " second: %s" % second
|
||||
print " ampere: %s" % ampere
|
||||
print " kelvin: %s" % kelvin
|
||||
print " mole: %s" % mole
|
||||
print " candela: %s" % candela
|
||||
print
|
||||
print "The 21 SI derived units with special names:"
|
||||
print " radian: %s" % radian
|
||||
print " steradian: %s" % steradian
|
||||
print " hertz: %s" % hertz
|
||||
|
||||
print " newton: %s" % newton
|
||||
print " pascal: %s" % pascal
|
||||
print " joule: %s" % joule
|
||||
print " watt: %s" % watt
|
||||
|
||||
print " coulomb: %s" % coulomb
|
||||
print " volt: %s" % volt
|
||||
print " farad: %s" % farad
|
||||
print " ohm: %s" % ohm
|
||||
print " siemens: %s" % siemens
|
||||
print " weber: %s" % weber
|
||||
print " tesla: %s" % tesla
|
||||
print " henry: %s" % henry
|
||||
|
||||
print " degree Celsius: %s" % celsius
|
||||
|
||||
print " lumen: %s" % lumen
|
||||
print " lux: %s" % lux
|
||||
|
||||
print " becquerel: %s" % becquerel
|
||||
print " gray: %s" % gray
|
||||
print " sievert: %s" % sievert
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from length import meter, centimeter, inch, foot, mile
|
||||
|
||||
#
|
||||
# Definitions of common area units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
square_meter = meter**2
|
||||
square_centimeter = centimeter**2
|
||||
|
||||
square_foot = foot**2
|
||||
square_inch = inch**2
|
||||
square_mile = mile**2
|
||||
|
||||
acre = 43560 * square_foot
|
||||
hectare = 1000 * square_meter
|
||||
|
||||
barn = 1e-28 * square_meter
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import SI, math
|
||||
|
||||
#
|
||||
# Definitions of common density units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
units = ['kg__m3', 'g__m3', 'g__cm3']
|
||||
|
||||
kg__m3 = SI.kilogram/(SI.meter * SI.meter * SI.meter)
|
||||
g__m3 = 1.e-3*kg__m3
|
||||
g__cm3 = 1.e3*kg__m3
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from SI import joule
|
||||
|
||||
#
|
||||
# Definitions of common energy units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
|
||||
Btu = 1055 * joule
|
||||
erg = 1e-7 * joule
|
||||
foot_pound = 1.356 * joule
|
||||
horse_power_hour = 2.685e6 * joule
|
||||
|
||||
calorie = 4.186 * joule
|
||||
Calorie = 1000 * calorie
|
||||
kilowatt_hour = 3.6e6 * joule
|
||||
|
||||
electron_volt = 1.602e-19 * joule
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
from SI import meter
|
||||
|
||||
#
|
||||
# Definitions of common force units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
from SI import meter
|
||||
from SI import nano, milli, centi, kilo
|
||||
|
||||
#
|
||||
# Definitions of common length units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
|
||||
nanometer = nano * meter
|
||||
millimeter = milli * meter
|
||||
centimeter = centi * meter
|
||||
kilometer = kilo * meter
|
||||
|
||||
inch = 2.540 * centimeter
|
||||
foot = 12 * inch
|
||||
yard = 3 * foot
|
||||
mile = 5280 * foot
|
||||
|
||||
fathom = 6 * foot
|
||||
nautical_mile = 6076 * foot
|
||||
|
||||
angstrom = 1e-10 * meter
|
||||
fermi = 1e-15 * meter
|
||||
light_year = 9.460e12 * kilometer
|
||||
parsec = 3.084e13 * kilometer
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from SI import kilogram
|
||||
|
||||
#
|
||||
# Definitions of common mass units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
gram = 1e-3 * kilogram
|
||||
|
||||
metric_ton = 1000 * kilogram
|
||||
|
||||
ounce = 28.35 * gram
|
||||
pound = 16 * ounce
|
||||
ton = 2000 * pound
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from SI import watt, kilo
|
||||
|
||||
#
|
||||
# Definitions of common power units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
kilowatt = kilo * watt
|
||||
horsepower = 745.7 * watt
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import SI
|
||||
|
||||
#
|
||||
# Definitions of common pressure units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
|
||||
bar = 1e5 * SI.pascal
|
||||
mbar = 100 * SI.pascal
|
||||
Pa = SI.pascal
|
||||
torr = 133.3 * SI.pascal
|
||||
atm = 1.01325e5 * SI.pascal
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import SI, energy, mass
|
||||
|
||||
#
|
||||
# Definitions of common energy units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
units = ['J__kg', 'kJ__kg', 'Btu__lbm', 'cal__g', 'kcal__g', 'kcal__kg']
|
||||
|
||||
J__kg = SI.joule/SI.kilogram
|
||||
kJ__kg = 1000.0*J__kg
|
||||
Btu__lbm = energy.Btu/mass.pound
|
||||
cal__g = energy.calorie/mass.gram
|
||||
kcal__g = 1000.0*cal__g
|
||||
kcal__kg = cal__g
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import SI, energy, mass
|
||||
|
||||
units = ['J__kg_K', 'kJ__kg_K', 'cal__g_K']
|
||||
|
||||
J__kg_K = SI.joule/(SI.kilogram * SI.kelvin)
|
||||
kJ__kg_K = 1000.0*J__kg_K
|
||||
cal__g_K = energy.calorie/(mass.gram * SI.kelvin)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from time import hour
|
||||
from length import nautical_mile
|
||||
|
||||
#
|
||||
# Definitions of common speed units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
|
||||
knot = nautical_mile/hour
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import SI
|
||||
|
||||
K = SI.kelvin
|
||||
R = (9.0/5.0)*K
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from SI import second
|
||||
|
||||
#
|
||||
# Definitions of common time units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
minute = 60 * second
|
||||
hour = 60 * minute
|
||||
day = 24 * hour
|
||||
year = 365.25 * day
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
import operator
|
||||
|
||||
class unit:
|
||||
|
||||
_zero = (0,) * 7
|
||||
_negativeOne = (-1, ) * 7
|
||||
|
||||
_labels = ('m', 'kg', 's', 'A', 'K', 'mol', 'cd')
|
||||
|
||||
|
||||
def __init__(self, value, derivation):
|
||||
self.value = value
|
||||
self.derivation = derivation
|
||||
return
|
||||
|
||||
|
||||
def __add__(self, other):
|
||||
if not self.derivation == other.derivation:
|
||||
raise ImcompatibleUnits(self, other)
|
||||
|
||||
return unit(self.value + other.value, self.derivation)
|
||||
|
||||
|
||||
def __sub__(self, other):
|
||||
if not self.derivation == other.derivation:
|
||||
raise ImcompatibleUnits(self, other)
|
||||
|
||||
return unit(self.value - other.value, self.derivation)
|
||||
|
||||
|
||||
def __mul__(self, other):
|
||||
if type(other) == type(0) or type(other) == type(0.0):
|
||||
return unit(other*self.value, self.derivation)
|
||||
|
||||
value = self.value * other.value
|
||||
derivation = tuple(map(operator.add, self.derivation, other.derivation))
|
||||
|
||||
return unit(value, derivation)
|
||||
|
||||
|
||||
def __div__(self, other):
|
||||
if type(other) == type(0) or type(other) == type(0.0):
|
||||
return unit(self.value/other, self.derivation)
|
||||
|
||||
value = self.value / other.value
|
||||
derivation = tuple(map(operator.sub, self.derivation, other.derivation))
|
||||
|
||||
return unit(value, derivation)
|
||||
|
||||
|
||||
def __pow__(self, other):
|
||||
if type(other) != type(0) and type(other) != type(0.0):
|
||||
raise BadOperation
|
||||
|
||||
value = self.value ** other
|
||||
derivation = tuple(map(operator.mul, [other]*7, self.derivation))
|
||||
|
||||
return unit(value, derivation)
|
||||
|
||||
|
||||
def __pos__(self): return self
|
||||
|
||||
|
||||
def __neg__(self): return unit(-self.value, self.derivation)
|
||||
|
||||
|
||||
def __abs__(self): return unit(abs(self.value), self.derivation)
|
||||
|
||||
|
||||
def __invert__(self):
|
||||
value = 1./self.value
|
||||
derivation = tuple(map(operator.mul, self._negativeOne, self.derivation))
|
||||
return unit(value, derivation)
|
||||
|
||||
|
||||
def __rmul__(self, other):
|
||||
return unit.__mul__(self, other)
|
||||
|
||||
def __rdiv__(self, other):
|
||||
if type(other) != type(0) and type(other) != type(0.0):
|
||||
raise BadOperation(self, other)
|
||||
|
||||
value = other/self.value
|
||||
derivation = tuple(map(operator.mul, self._negativeOne, self.derivation))
|
||||
|
||||
return unit(value, derivation)
|
||||
|
||||
|
||||
def __float__(self):
|
||||
return self.value
|
||||
#if self.derivation == self._zero: return self.value
|
||||
#raise BadConversion(self)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
str = "%g" % self.value
|
||||
for i in range(0, 7):
|
||||
exponent = self.derivation[i]
|
||||
if exponent == 0: continue
|
||||
if exponent == 1:
|
||||
str = str + " %s" % (self._labels[i])
|
||||
else:
|
||||
str = str + " %s^%d" % (self._labels[i], exponent)
|
||||
|
||||
return str
|
||||
|
||||
dimensionless = unit(1, unit._zero)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from length import meter, centimeter, foot, inch
|
||||
|
||||
#
|
||||
# Definitions of common volume units
|
||||
# Data taken from Appendix F of Halliday, Resnick, Walker, "Fundamentals of Physics",
|
||||
# fourth edition, John Willey and Sons, 1993
|
||||
|
||||
cubic_meter = meter**3
|
||||
cubic_centimeter = centimeter**3
|
||||
cubic_foot = foot**3
|
||||
cubic_inch = inch**3
|
||||
|
||||
liter = 1000 * cubic_centimeter
|
||||
|
||||
us_fluid_ounce = 231./128 * cubic_inch
|
||||
us_pint = 16 * us_fluid_ounce
|
||||
us_fluid_quart = 2 * us_pint
|
||||
us_fluid_gallon = 4 * us_fluid_quart
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
# from Cantera import *
|
||||
|
||||
from main import MixMaster
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
from Cantera import *
|
||||
|
||||
# thermo parametrizations
|
||||
#from Cantera.Species.Thermo.NasaPolynomial import NasaPolynomial
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue