.. _sec-python-migration: Migrating from the Old Python Module ************************************ With the introduction of the new Cython-based Python module in Cantera 2.1, there are a number of changes to the interface which require modifications to scripts in order for them to work with the new module. Broadly speaking, the changes to the interface are intended to make the Cantera Python module easier to use, and provide a more "Pythonic" interface by making use of common Python language idioms, language features, and style guidelines. This document describes the changes to the Python module which are likely to require modifications to existing code. Importing the Python Module --------------------------- The name of the Python module is now ``cantera`` with a lowercase "c". This change is made partly for compliance with `PEP8 `_. Furthermore, the various submodules, e.g. ``Cantera.Reactor`` have been eliminated. All classes and functions are available directly in the ``cantera`` module. To avoid the namespace clutter introduced by using ``import *``, the following syntax is preferred:: >>> import cantera as ct Naming Conventions ------------------ Generally, the names used in the Cantera Python module have been changed to follow the recommendations of PEP8. This means that the names of methods and properties are generally written as ``lowercase_with_underscores`` instead of ``capitalizingEachWord``. Also, some abbreviated names have been expanded. For example, the following function calls:: >>> gas.speciesName(0) >>> gas.nAtoms('H2', 'H') >>> gas.reactionEqn(3) should be replaced with:: >>> gas.species_name(0) >>> gas.n_atoms('H2', 'H') >>> gas.reaction_equation(3) Importing Phases ---------------- The functions ``importPhase`` and ``IdealGasMix`` have been removed. `Solution` objects, which represent the phase (regardless of the underlying thermodynamic model) as well as providing access to kinetics and transport properties, are created directly using the `Solution` class. For example:: >>> gas = Solution('h2o2.xml') Creates an object which represents an ``IdealGasPhase`` mixture with a ``GasKinetics`` reaction mechansm and a ``MixTransport`` transport model, based on the parameters specified in the input file. For importing multiple phases from a single file, the ``importPhases`` function has been retained with the new name ``import_phases``:: >>> gas, anode_bulk, oxide = ct.import_phases('sofc.cti', ['gas', 'metal', 'oxide_bulk']) Interfaces and edges are created using the `Interface` class, which represents both 1D and 2D interfaces, rather than using the ``importEdge`` and ``importInterface`` functions:: >>> anode_surf = ct.Interface('sofc.cti', 'metal_surface', [gas]) >>> oxide_surf = ct.Interface('sofc.cti', 'oxide_surface', [gas, oxide]) >>> tpb = ct.Interface('sofc.cti', 'tpb', [anode_bulk, anode_surf, oxide_surf]) Accessing Properties -------------------- Most methods for accessing and setting the properties of objects have been replaced with Python "properties" which do not need to be "called" in order to accessed or changed. For example, the following:: >>> u = gas.intEnergy_mass() >>> Wmx = gas.meanMolecularWeight() >>> kf = gas.fwdRateConstants() >>> gas.setName('foo') should be replaced with:: >>> u = gas.int_energy_mass >>> Wmx = gas.mean_molecular_weight >>> kf = gas.forward_rate_constants >>> gas.name = 'foo' Some common properties have been renamed according to the variable that is typically used to represent them:: >>> gas.temperature() >>> gas.pressure() >>> gas.massFractions() should be replaced with:: >>> gas.T >>> gas.P >>> gas.Y For pure fluid phases, the property ``X`` refers to the vapor mass fraction or "quality" of the phase. The following:: >>> w = Cantera.liquidvapor.Water() >>> w.set(T=400, Vapor=0.5) should be replaced with:: >>> w = ct.Water() >>> w.TX = 400, 0.5 Setting Thermodyamic State -------------------------- The ``set`` method has been removed in favor of property pairs or triplets. The following:: >>> gas.setMoleFractions('CH4:1.0, O2:0.1') >>> gas.set(X='CH4:1.0, O2:0.1') >>> gas.set(U=-1.1e6, V=5.5) >>> gas.set(T=300, P=101325, Y='H2:1.0') should be replaced with:: >>> gas.X = 'CH4:1.0, O2:0.1' >>> gas.X = 'CH4:1.0, O2:0.1' >>> gas.UV = -1.1e6, 5.5 >>> gas.TPY = 300, 101325, 'H2:1.0' The ``saveState`` and ``restoreState`` methods have been removed. Their functionality can be replicated as follows:: >>> state = gas.TDY >>> # (operations that modify gas) >>> gas.TDY = state Printing Phase Summaries ------------------------ `Solution` objects no longer print out a verbose summary as their string representation. Instead, the summary report can be generated using the `report()` method, which returns a string, or by calling the `Solution` object to print the report to the screen. The following are equivalent:: >>> print(gas.report()) >>> gas() Getting Properties for a Subset of Species ------------------------------------------ Some methods previously accepted an optional list of species as a filter, e.g.:: >>> gas.massFractions(['OH','H']) This is not compatible with the Python "property" syntax, so the following alternative is used instead:: >>> gas['OH','H2'].Y array([ 0., 1.]) This works for any property which returns a value for each species, and works with species names, indices, and index ranges:: >>> gas[1,2,6].partial_molar_cp array([ 20786.15525072, 21900.30946418, 34929.99146762]) >>> gas[3:6].species_names ['O2', 'OH', 'H2O'] Furthermore, the "sliced" object itself can be saved and used without needing to specify the species list again:: >>> reactants = gas['H2','O2'] >>> reactants.X array([ 1., 0.]) Transport Models ---------------- The old method for setting the transport model, `switchTransportModel` has been replaced with the `transport_model` property. To use the multicomponent transport model:: >>> gas.transport_model = 'Multi' Note that unlike the previous implementation, only one transport model can be associated with a `Solution` object at a time, so there is a larger cost with switching models. If you need to alternate between transport models, it is generally better to use two different `Solution` objects. Reactor Networks ---------------- As with the `Solution` class, properties are now used to get and set most parameters of reactors, flow devices, walls, etc. The following old code:: >>> Y = reactor.massFractions() >>> X = reactor.contents().moleFractions() >>> wall.setArea(2.0) >>> net.setTolerances(1e-8, 1e-14) should be replaced with:: >>> Y = reactor.Y >>> X = reactor.thermo.X >>> wall.area = 2.0 >>> net.rtol = 1e-8 >>> net.atol = 1e-14 Time-varying parameters have not been replaced with properties, since they need to be evaluated at a particular time. Elimination of the ``Func`` Module ---------------------------------- The ``Func`` module is no longer necessary, as the Cython module allows any callable Python object (lambda, function, or class) to be used in places where a function of a single variable are needed. For example, to set the velocity of a wall as a function of time, the following are equivalent:: >>> wall.set_velocity(lambda t: np.cos(3*t)) >>> def myfunc(z): ... return np.cos(3*z) >>> wall.set_velocity(myfunc) One-Dimensional Reacting Flows ------------------------------ As elsewhere, the ``set`` method has been eliminated. The following old usage:: >>> f.fuel_inlet.set(massflux=mdot_f, >>> mole_fractions=comp_f, >>> temperature=tin_f) >>> f.set(energy = 'off') should be replaced with:: >>> f.fuel_inlet.mdot = mdot_f >>> f.fuel_inlet.X = comp_f >>> f.fuel_inlet.T = tin_f >>> f.energy_enabled = False However, the methods for setting tolerances and refinement criteria have been retained in slightly modified forms. The following:: >>> f.set(tol=tol_ss, tol_time=tol_ts) >>> f.setRefineCriteria(ratio=4, slope=0.2, curve=0.3, prune=0.04) should be replaced with:: >>> f.flame.set_steady_tolerances(default=tol_ss) >>> f.flame.set_transient_tolerances(default=tol_ts) >>> f.set_refine_criteria(ratio=4, slope=0.2, curve=0.3, prune=0.04) To change the transport model and enbale calculation of the Soret diffusion term, the following:: >>> gas.addTransportModel('Multi') >>> gas.switchTransportModel('Multi') >>> f.flame.setTransportModel(gas) >>> f.flame.enableSoret() should be replaced with:: >>> f.transport_model = 'Multi' >>> f.soret_enabled = True