if (ign.ignited())
{
    // progress variable
    // ~~~~~~~~~~~~~~~~~
    volScalarField c = scalar(1) - b;

    // Unburnt gas density
    // ~~~~~~~~~~~~~~~~~~~
    volScalarField rhou = thermo.rhou();

    // Calculate flame normal etc.
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    volVectorField n = fvc::grad(b);

    volScalarField mgb = mag(n);

    dimensionedScalar dMgb = 1.0e-3*
        (b*c*mgb)().weightedAverage(mesh.V())
       /((b*c)().weightedAverage(mesh.V()) + SMALL)
      + dimensionedScalar("ddMgb", mgb.dimensions(), SMALL);

    mgb += dMgb;

    surfaceVectorField SfHat = mesh.Sf()/mesh.magSf();
    surfaceVectorField nfVec = fvc::interpolate(n);
    nfVec += SfHat*(fvc::snGrad(b) - (SfHat & nfVec));
    nfVec /= (mag(nfVec) + dMgb);
    surfaceScalarField nf = (mesh.Sf() & nfVec);
    n /= mgb;


    #include <engine/StCorr.H>

    // Calculate turbulent flame speed flux
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    surfaceScalarField phiSt = fvc::interpolate(rhou*StCorr*Su*Xi)*nf;

    scalar StCoNum = max
    (
        mesh.surfaceInterpolation::deltaCoeffs()
       *mag(phiSt)/(fvc::interpolate(rho)*mesh.magSf())
    ).value()*runTime.deltaT().value();

    Info<< "Max St-Courant Number = " << StCoNum << endl;

    // Create b equation
    // ~~~~~~~~~~~~~~~~~
    fvScalarMatrix bEqn
    (
        fvm::ddt(rho, b)
      + mvConvection->fvmDiv(phi, b)
      + fvm::div(phiSt, b, "div(phiSt,bprog)")
      - fvm::Sp(fvc::div(phiSt), b)
      - fvm::laplacian(turbulence->alphaEff(), b)
    );


    // Add ignition cell contribution to b-equation
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #include <engine/ignite.H>


    // Solve for b
    // ~~~~~~~~~~~
    bEqn.solve();

    Info<< "min(bprog) = " << min(b).value() << endl;


    // Calculate coefficients for Gulder's flame speed correlation
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    volScalarField up = uPrimeCoef*sqrt((2.0/3.0)*turbulence->k());
  //volScalarField up = sqrt(mag(diag(n * n) & diag(turbulence->r())));

    volScalarField epsilon = pow(uPrimeCoef, 3)*turbulence->epsilon();

    volScalarField tauEta = sqrt(thermo.muu()/(rhou*epsilon));

    volScalarField Reta = up/
    (
        sqrt(epsilon*tauEta) + dimensionedScalar("1e-8", up.dimensions(), 1e-8)
    );

  //volScalarField l = 0.337*k*sqrt(k)/epsilon;
  //Reta *= max((l - dimensionedScalar("dl", dimLength, 1.5e-3))/l, 0.0);

    // Calculate Xi flux
    // ~~~~~~~~~~~~~~~~~
    surfaceScalarField phiXi =
        phiSt
      - fvc::interpolate(fvc::laplacian(turbulence->alphaEff(), b)/mgb)*nf
      + fvc::interpolate(rho)*fvc::interpolate(Su*(1.0/Xi - Xi))*nf;


    // Calculate mean and turbulent strain rates
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    volVectorField Ut = U + Su*Xi*n;
    volScalarField sigmat = (n & n)*fvc::div(Ut) - (n & fvc::grad(Ut) & n);

    volScalarField sigmas =
        ((n & n)*fvc::div(U) - (n & fvc::grad(U) & n))/Xi
      + (
            (n & n)*fvc::div(Su*n)
          - (n & fvc::grad(Su*n) & n)
        )*(Xi + scalar(1))/(2*Xi);


    // Calculate the unstrained laminar flame speed
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    volScalarField Su0 = unstrainedLaminarFlameSpeed()();


    // Calculate the laminar flame speed in equilibrium with the applied strain
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    volScalarField SuInf = Su0*max(scalar(1) - sigmas/sigmaExt, scalar(0.01));

    if (SuModel == "unstrained")
    {
        Su == Su0;
    }
    else if (SuModel == "equilibrium")
    {
        Su == SuInf;
    }
    else if (SuModel == "transport")
    {
        // Solve for the strained laminar flame speed
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        volScalarField Rc =
            (sigmas*SuInf*(Su0 - SuInf) + sqr(SuMin)*sigmaExt)
           /(sqr(Su0 - SuInf) + sqr(SuMin));

        fvScalarMatrix SuEqn
        (
            fvm::ddt(rho, Su)
          + fvm::div(phi + phiXi, Su, "div(phiXi,Su)")
          - fvm::Sp(fvc::div(phiXi), Su)
          ==
          - fvm::SuSp(-rho*Rc*Su0/Su, Su)
          - fvm::SuSp(rho*(sigmas + Rc), Su)
        );

        SuEqn.relax();
        SuEqn.solve();

        // Limit the maximum Su
        // ~~~~~~~~~~~~~~~~~~~~
        Su.min(SuMax);
        Su.max(SuMin);
    }
    else
    {
        FatalError
            << args.executable() << " : Unknown Su model " << SuModel
            << abort(FatalError);
    }


    // Calculate Xi according to the selected flame wrinkling model
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if (XiModel == "fixed")
    {
        // Do nothing, Xi is fixed!
    }
    else if (XiModel == "algebraic")
    {
        // Simple algebraic model for Xi based on Gulders correlation
        // with a linear correction function to give a plausible profile for Xi
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Xi == scalar(1) +
            (scalar(1) + (2*XiShapeCoef)*(scalar(0.5) - b))
           *XiCoef*sqrt(up/(Su + SuMin))*Reta;
    }
    else if (XiModel == "transport")
    {
        // Calculate Xi transport coefficients based on Gulders correlation
        // and DNS data for the rate of generation
        // with a linear correction function to give a plausible profile for Xi
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        volScalarField XiEqStar =
            scalar(1.001) + XiCoef*sqrt(up/(Su + SuMin))*Reta;

        volScalarField XiEq =
            scalar(1.001)
          + (scalar(1) + (2*XiShapeCoef)*(scalar(0.5) - b))
           *(XiEqStar - scalar(1.001));

        volScalarField Gstar = 0.28/tauEta;
        volScalarField R = Gstar*XiEqStar/(XiEqStar - scalar(1));
        volScalarField G = R*(XiEq - scalar(1.001))/XiEq;

        //R *= (Gstar + 2*mag(dev(symm(fvc::grad(U)))))/Gstar;

        // Solve for the flame wrinkling
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        fvScalarMatrix XiEqn
        (
            fvm::ddt(rho, Xi)
          + fvm::div(phi + phiXi, Xi, "div(phiXi,Xi)")
          - fvm::Sp(fvc::div(phiXi), Xi)
         ==
            rho*R
          - fvm::Sp(rho*(R - G), Xi)
          - fvm::Sp
            (
                rho*max
                (
                    sigmat - sigmas,
                    dimensionedScalar("0", sigmat.dimensions(), 0)
                ),
                Xi
            )
        );

        XiEqn.relax();
        XiEqn.solve();

        // Correct boundedness of Xi
        // ~~~~~~~~~~~~~~~~~~~~~~~~~
        Xi.max(1.0);
        Info<< "max(Xi) = " << max(Xi).value() << endl;
        Info<< "max(XiEq) = " << max(XiEq).value() << endl;
    }
    else
    {
        FatalError
            << args.executable() << " : Unknown Xi model " << XiModel
            << abort(FatalError);
    }

    Info<< "Combustion progress = "
        << 100*(scalar(1) - b)().weightedAverage(mesh.V()).value() << "%"
        << endl;

    St = Xi*Su;
}

// ************************ vim: set sw=4 sts=4 et: ************************ //
