/*
 * Adiabatic combustor
 * ===================
 *
 * Two separate streams - one pure methane and the other air, both at 300 K
 * and 1 atm flow into an adiabatic combustor where they mix. We are interested
 * in the steady-state burning solution. Since at 300 K no reaction will occur
 * between methane and air, we need to use an 'igniter' to initiate the
 * chemistry. A simple igniter is a pulsed flow of atomic hydrogen. After the
 * igniter is turned off, the system approaches the steady burning solution.
 *
 * .. tags:: C++, combustion, reactor network, well-stirred reactor
 */

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

#include "cantera/zerodim.h"
#include <fstream>

using namespace Cantera;

void runexample()
{
    // use reaction mechanism GRI-Mech 3.0
    auto sol = newSolution("gri30.yaml", "gri30", "none");
    auto gas = sol->thermo();

    // create a reservoir for the fuel inlet, and set to pure methane.
    gas->setState_TPX(300.0, OneAtm, "CH4:1.0");
    double fuel_mw = gas->meanMolecularWeight();
    auto fuel_in = newReservoir(sol);

    // create a reservoir for the air inlet
    auto air = newSolution("air.yaml", "air", "none");
    double air_mw = air->thermo()->meanMolecularWeight();
    auto air_in = newReservoir(air);

    // to ignite the fuel/air mixture, we'll introduce a pulse of radicals.
    // The steady-state behavior is independent of how we do this, so we'll
    // just use a stream of pure atomic hydrogen.
    gas->setState_TPX(300.0, OneAtm, "H:1.0");
    auto igniter = newReservoir(sol);

    // create the combustor, and fill it in initially with N2
    gas->setState_TPX(300.0, OneAtm, "N2:1.0");
    auto combustor = newReactor4("Reactor", sol);
    combustor->setInitialVolume(1.0);

    // create a reservoir for the exhaust. The initial composition doesn't matter.
    auto exhaust = newReservoir(sol);

    // lean combustion, phi = 0.5
    double equiv_ratio = 0.5;

    // compute fuel and air mass flow rates
    double factor = 0.1;
    double air_mdot = factor*9.52*air_mw;
    double fuel_mdot = factor*equiv_ratio*fuel_mw;

    // create and install the mass flow controllers. Controllers
    // m1 and m2 provide constant mass flow rates, and m3 provides
    // a short Gaussian pulse only to ignite the mixture
    MassFlowController m1(fuel_in, combustor);
    m1.setMassFlowRate(fuel_mdot);

    // Now create the air mass flow controller.  Note that this connects
    // two reactors with different reaction mechanisms and different
    // numbers of species. Downstream and upstream species are matched by name.
    MassFlowController m2(air_in, combustor);
    m2.setMassFlowRate(air_mdot);

    // The igniter will use a Gaussian 'functor' object to specify the
    // time-dependent igniter mass flow rate.
    double A = 0.1;
    double FWHM = 0.2;
    double t0 = 0.5;
    shared_ptr<Func1> igniter_mdot = newFunc1("Gaussian", {A, t0, FWHM});

    MassFlowController m3(igniter, combustor);
    m3.setTimeFunction(igniter_mdot);

    // put a valve on the exhaust line to regulate the pressure
    Valve v(combustor, exhaust);
    double Kv = 1.0;
    v.setValveCoeff(Kv);

    // the simulation only contains one reactor
    ReactorNet sim(combustor);

    // take single steps to 6 s, writing the results to a CSV file for later plotting.
    double tfinal = 1.0;
    double tnow = 0.0;
    double tres;

    std::ofstream f("combustor_cxx.csv");
    vector<size_t> k_out {
        gas->speciesIndex("CH4"),
        gas->speciesIndex("O2"),
        gas->speciesIndex("CO2"),
        gas->speciesIndex("H2O"),
        gas->speciesIndex("CO"),
        gas->speciesIndex("OH"),
        gas->speciesIndex("H"),
        gas->speciesIndex("C2H6")
    };

    while (tnow < tfinal) {
        tnow += 0.005;
        sim.advance(tnow);
        tres = combustor->mass()/v.massFlowRate();
        f << tnow << ", "
          << combustor->temperature() << ", "
          << tres << ", ";
        const auto& c = combustor->phase()->thermo();
        for (size_t i = 0; i < k_out.size(); i++) {
            f << c->moleFraction(k_out[i]) << ", ";
        }
        f << std::endl;
    }
}

int main()
{
    try {
        runexample();
        return 0;
    } catch (CanteraError& err) {
        // handle exceptions thrown by Cantera
        std::cout << err.what() << std::endl;
        std::cout << " terminating... " << std::endl;
        appdelete();
        return 1;
    }
}
