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')
        self.pressure = gas.pressure()

        # 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._initialized = 0
        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.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):
        """Solve the flame. See :meth:`.Stack.solve`"""
        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):
        """See :meth:`.Stack.setRefineCriteria`"""
        Stack.setRefineCriteria(self, domain = self.flame,
                                ratio = ratio, slope = slope, curve = curve,
                                prune = prune)

    def setFixedTemperature(self, temp):
        _cantera.sim1D_setFixedTemperature(self._hndl, temp)

    def setProfile(self, component, locs, vals):
        """Set a profile in the flame"""
        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.pressure, y)
