// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
/* Author : willien at Tue Apr  1 14:50:50 2008
 * Generated by createNew
 */

#include "Utils/Utils.h"

#include <boost/shared_ptr.hpp>

#include "Utils/ItemGroupMap.h"
#include "Utils/ItemGroupBuilder.h"

#include "Numerics/DiscreteOperator/IDivKGradDiscreteOperator.h"
#include "Numerics/DiscreteOperator/IAdvectionOperator.h"

#include "NumericalModel/Utils/ICollector.h"
#include "NumericalModel/Utils/IOp.h"
#include "NumericalModel/Models/INumericalModel.h"
#include "NumericalModel/Operators/INumericalModelVisitor.h"
#include "NumericalModel/SubDomainModel/INumericalDomain.h"
#include "NumericalModel/Algorithms/ITimeIntegrator.h"
#include "NumericalModel/Algorithms/IIntegrator.h"
#include "NumericalModel/Algorithms/ICouplingModelSolver.h"

#include "NumericalModel/Algorithms/CouplingModelSolver/CouplingModelSolverT.h"

#include "NumericalModel/Utils/BaseCollector.h"
#include "NumericalModel/Utils/OpT.h"
#include "NumericalModel/SubDomainModel/NumericalDomain/NumericalDomainImpl.h"
#include "NumericalModel/SubDomainModel/SubDomainModelProperty.h"
#include "NumericalModel/SubDomainModel/CollectorT.h"
#include "NumericalModel/SubDomainModel/SDMBoundaryCondition.h"
#include "NumericalModel/SubDomainModel/SDMBoundaryConditionMng.h"
#include "NumericalModel/Models/ISubDomainModel.h"

#include "TimeUtils/ITimeMng.h"
#include "TimeUtils/ITimeStepMng.h"

#include "Mesh/GroupCreator/IGroupCreator.h"

#include "Appli/IAppServiceMng.h"

#include "Mesh/Geometry/IGeometryMng.h"

#include "ToyReactiveTransport/ToyReactiveTransportCouplageDtLocal/TypesDtLocal.h"

#include "NumericalModel/Models/IIterativeTimeModel.h"
#include "NumericalModel/Algorithms/TimeIntegrator/TimeIntegrator.h"
#include "NumericalModel/Algorithms/Integrator/IntegratorT.h"
#include "NumericalModel/FluxModel/FluxModel.h"
#include "NumericalModel/FluxModel/FluxModelT.h"

#include "ToyReactiveTransport/ToyReactiveTransportCouplageDtLocal/ToyReactiveTransportCouplageDtLocalModule.h"

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

ToyReactiveTransportCouplageDtLocalModule::~ToyReactiveTransportCouplageDtLocalModule()
  {
    delete m_global_collector;
    delete m_reservoir_collector;
    delete m_coarse_window_collector;
    delete m_fine_window_collector;
    delete m_time_integrator;
    delete m_solver;
    delete m_u_concentration_fine;
    delete m_u_concentration_fine_tn;
    delete m_u_concentration_coarse;
    delete m_u_concentration_coarse_tn;
    delete m_v_concentration_fine;
    delete m_v_concentration_fine_tn;
    delete m_v_concentration_coarse;
    delete m_v_concentration_coarse_tn;
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::init()
  {
    info() << "init";
    m_output_level = options()->outputLevel();

    // lecture du choix de l'algo
    m_computation_opt = options()->computationOpt();

    IServiceMng* service_mng = subDomain()->serviceMng();
    IAppServiceMng* app_service_mng = IAppServiceMng::instance(service_mng);

    // lecture des sous domaines et schemas associes
    m_dom_global = options()->DomainGlobal();
    m_dom_global->init();
    m_dom_reservoir = options()->DomainReservoir();
    m_dom_reservoir->init();
    m_dom_window = options()->DomainWindow();
    m_dom_window->init();

    // initialisation

    //Domain management
    initModelDomains();

    //Time step management
    //  IAppServiceMng* app_service_mng = IAppServiceMng::instance(subDomain()->serviceMng()) ;
    m_coarse_time_mng = app_service_mng->find<ITimeMng> (true);
    m_fine_time_mng = options()->fineTimeMng();
    // m_fine_time_mng->init() ; /// pourquoi pas de init ? pris en charge dans les sous-boucles ?
    m_fine_time_mng->setParent(m_coarse_time_mng);

    m_time_step_mng = options()->timeStepMng();
    m_time_step_mng->init();

      {
        //
        // GLOBAL DOMAIN MODEL
        //
        IIterativeTimeModel* model =
            dynamic_cast<IIterativeTimeModel*> (m_dom_global);
        if (model)
          model->setTimeMng(m_coarse_time_mng);

        ISubDomainModel* sd_model =
            dynamic_cast<ISubDomainModel*> (m_dom_global);
        if (sd_model)
          {
            ISubDomainModel::FaceBoundaryConditionMng* bc_mng =
                sd_model->getFaceBoundaryConditionMng();

            setBoundaryCondition(sd_model, m_bc_group_g);

            m_global_collector = new ISubDomainModel::Collector(
                m_coarse_time_mng, bc_mng);
            m_dom_global->prepare(m_global_collector, CoarseDtSeq);
          }
      }

      {
        //
        // RESERVOIR DOMAIN MODEL
        //
        IIterativeTimeModel* model =
            dynamic_cast<IIterativeTimeModel*> (m_dom_reservoir);
        if (model)
          model->setTimeMng(m_coarse_time_mng);

        ISubDomainModel* sd_model =
            dynamic_cast<ISubDomainModel*> (m_dom_reservoir);
        if (sd_model)
          {
            ISubDomainModel::FaceBoundaryConditionMng* bc_mng =
                sd_model->getFaceBoundaryConditionMng();

            setBoundaryCondition(sd_model, m_bc_group_r);

            /// Collecteur spécialisé (au delà de l'interface ICollector)
            ISubDomainModel::Collector * reservoir_collector =
                new ISubDomainModel::Collector(m_coarse_time_mng, bc_mng);
            m_reservoir_collector = reservoir_collector;
            reservoir_collector->setRealVar(
                SubDomainModelProperty::UConcentration, &m_u_concentration); /// metier (UConcentration) dans interface ! utiliser ref du model métier itself

            switch (m_computation_opt)
              {
            case TypesDtLocal::GlobalCoarseDt:
              break;
            case TypesDtLocal::GlobalFineDt:
              break;
            case TypesDtLocal::DFVF1:
              {
                // CREATE INTERFACE BOUNDARY CONDITION BC0 for interface boundary and OPERATORS to init them
                // INTERFACE BOUNDARY CONDITION : OVERLAP DIRICHLET
                m_r_bc_dirichlet = new ISubDomainModel::FaceBoundaryCondition(
                    m_interface_id, SubDomainModelProperty::OverlapDirichlet,
                    m_interface_id);
                //Record to bc manager
                bc_mng->addNew(m_r_bc_dirichlet, new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_r_bc_dirichlet));
                m_r_bc_dirichlet->activate(true);

                /// Ok connection à des actions requises par le collecteur
                //
                // initialisation avec la solution au temps DT precedent
                // ------------------------------------------------------------------
                m_reservoir_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::initCoarsePressure));
                //
                // Mise a jour de la cdt raccord en temps  Dirichlet sur le reservoir
                // ------------------------------------------------------------------
                m_reservoir_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeDirichletOverlapForReservoir));
                m_reservoir_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeReservoirInterfaceFacePressure));
                //
                // Etape 3 : Resolution  dans reservoir avec une CL a pression imposee dans overlap
                // --------------------------------------------------------------------------------
                // mise a jour des CL
                reservoir_collector->addBCUpdateOp(
                    new ISubDomainModel::FaceBCOp(sd_model,
                        &ISubDomainModel::initBoundaryCondition,
                        m_r_bc_dirichlet)); /// utilise spécialisation du collecteur pour atteindre BC
              }
              break;
            case TypesDtLocal::DFVF2:
              {
                // CREATE INTERFACE BOUNDARY CONDITION BC1 for interface boundary and OPERATORS to init them
                m_r_bc_neumann = new ISubDomainModel::FaceBoundaryCondition(
                    m_interface_id, SubDomainModelProperty::Neumann,
                    m_interface_id);
                //Record to bc manager
                bc_mng->addNew(m_r_bc_neumann, new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_r_bc_neumann));
                m_r_bc_neumann->activate(true);
                //m_reservoir_collector->addOperator(ICollector::Start,new OpT<ToyReactiveTransportCouplageDtLocalModule>(this,&ToyReactiveTransportCouplageDtLocalModule::computePressureOnOverlap)) ;
                m_reservoir_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeAverageFluxOnOverlap));
                m_reservoir_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::initCoarsePressure));
                m_reservoir_collector->addOperator(
                    ICollector::Finalize,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::updateCoarsePressureOnOverlap));
                reservoir_collector->addBCUpdateOp(
                    new ISubDomainModel::FaceBCOp(sd_model,
                        &ISubDomainModel::initBoundaryCondition, m_r_bc_neumann)); /// acces à la spécialisation
              }
              break;
            case TypesDtLocal::VFVF1:
              m_r_bc_neumann->activate(true);
              break;
            case TypesDtLocal::VFVF2:
              m_r_bc_dirichlet->activate(true);
              break;
              }
            //
            // Mise a jour des solutions sur le reservoir
            // ------------------------------------------------------------------
            if (m_output_level > 3)
              m_reservoir_collector->addOperator(ICollector::FinalizeCompute,
                  new OpT<ToyReactiveTransportCouplageDtLocalModule> (this,
                      &ToyReactiveTransportCouplageDtLocalModule::writeoutput));

            m_reservoir_collector->addOperator(
                ICollector::Finalize,
                new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                    this,
                    &ToyReactiveTransportCouplageDtLocalModule::updatePressureFromReservoir));

            m_dom_reservoir->prepare(m_reservoir_collector, CoarseDtSeq);
            //m_dom_reservoir->addObs(new ReservoirInterfaceUpdater(this),CoarseDtSeq) ;
          }
      }
      {
        //
        // WINDOW DOMAIN MODEL
        //
        IIterativeTimeModel* model =
            dynamic_cast<IIterativeTimeModel*> (m_dom_window);
        if (model)
          model->setTimeMng(m_fine_time_mng);
        ISubDomainModel* sd_model =
            dynamic_cast<ISubDomainModel*> (m_dom_window);
        if (sd_model)
          {
            ISubDomainModel::FaceBoundaryConditionMng* bc_mng =
                sd_model->getFaceBoundaryConditionMng();

            setBoundaryCondition(sd_model, m_bc_group_w);

            ISubDomainModel::Collector* coarse_collector =
                new ISubDomainModel::Collector(m_coarse_time_mng, bc_mng);
            m_coarse_window_collector = coarse_collector;

            ISubDomainModel::Collector* fine_collector =
                new ISubDomainModel::Collector(m_fine_time_mng, bc_mng);
            m_fine_window_collector = fine_collector;
            m_time_integrator = new TimeIntegrator();
            fine_collector->setTimeIntegrator(m_time_integrator);
            fine_collector->activateTimeIntegrator(true);
            fine_collector->setRealVar(SubDomainModelProperty::UConcentration,
                &m_u_concentration);

            switch (m_computation_opt)
              {
            case TypesDtLocal::GlobalCoarseDt:
              break;
            case TypesDtLocal::GlobalFineDt:
              break;
            case TypesDtLocal::DFVF1:
              {
                // CREATE INTERFACE BOUNDARY CONDITION BC1 for interface boundary and OPERATORS to init them
                // INTERFACE BOUNDARY CONDITION : NEUNMANN
                m_w_bc_neumann = new ISubDomainModel::FaceBoundaryCondition(
                    m_interface_id, SubDomainModelProperty::Neumann,
                    m_interface_id);
                //Record to bc manager
                bc_mng->addNew(m_w_bc_neumann, new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_w_bc_neumann));
                m_w_bc_neumann->activate(true);
                m_fine_window_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computePressureOnOverlap));
                m_fine_window_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeFluxOnOverlap));
                m_fine_window_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::initFinePressure));
                fine_collector->addBCUpdateOp(new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_w_bc_neumann));
              }
              break;
            case TypesDtLocal::DFVF2:
              {
                // CREATE INTERFACE BOUNDARY CONDITION BC0 for interface boundary and OPERATORS to init them
                m_w_bc_dirichlet = new ISubDomainModel::FaceBoundaryCondition(
                    m_interface_id, SubDomainModelProperty::OverlapDirichlet,
                    m_interface_id);
                //Record to bc manager
                bc_mng->addNew(m_w_bc_dirichlet, new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_w_bc_dirichlet));
                m_w_bc_dirichlet->activate(true);
                m_fine_window_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::initAverageFluxOnOverlap));
                m_fine_window_collector->addOperator(
                    ICollector::Start,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::initFinePressure));
                m_fine_window_collector->addOperator(
                    ICollector::StartCompute,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeWindowDirichletCellPressure));
                m_fine_window_collector->addOperator(
                    ICollector::StartCompute,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeWindowInterfaceFacePressure));
                m_fine_window_collector->addOperator(
                    ICollector::FinalizeCompute,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::computeReservoirPressureOnOverlap));
                m_fine_window_collector->addOperator(
                    ICollector::FinalizeCompute,
                    new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                        this,
                        &ToyReactiveTransportCouplageDtLocalModule::integrateFluxOnOverlap));
                fine_collector->addBCUpdateOp(new ISubDomainModel::FaceBCOp(
                    sd_model, &ISubDomainModel::initBoundaryCondition,
                    m_w_bc_dirichlet));
              }
              break;
            case TypesDtLocal::VFVF1:
              m_fine_window_collector->addOperator(
                  ICollector::StartCompute,
                  new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                      this,
                      &ToyReactiveTransportCouplageDtLocalModule::computeVFDirichletOverlapForWindow));
              break;
            case TypesDtLocal::VFVF2:
              m_fine_window_collector->addOperator(
                  ICollector::StartCompute,
                  new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                      this,
                      &ToyReactiveTransportCouplageDtLocalModule::computeVFDirichletOverlapForWindow));
              break;
              }
            //
            // Mise a jour des solutions du domain window
            // ------------------------------------------------------------------

            if (m_output_level > 3)
              m_fine_window_collector->addOperator(ICollector::FinalizeCompute,
                  new OpT<ToyReactiveTransportCouplageDtLocalModule> (this,
                      &ToyReactiveTransportCouplageDtLocalModule::writeoutput));

            m_fine_window_collector->addOperator(
                ICollector::Finalize,
                new OpT<ToyReactiveTransportCouplageDtLocalModule> (
                    this,
                    &ToyReactiveTransportCouplageDtLocalModule::updatePressureFromWindow));

            m_dom_window->prepare(m_coarse_window_collector, CoarseDtSeq);
            m_dom_window->prepare(m_fine_window_collector, FineDtSeq);
            //m_dom_window->addObs(new WindowInterfaceUpdater(this),FineDtSeq) ;
          }
      }
      {
        // OVERLAP DOMAIN
        IAppServiceMng* app_service_mng = IAppServiceMng::instance(
            subDomain()->serviceMng());

        // Retrieve shared geometry service
        IGeometryMng* geometry = app_service_mng->find<IGeometryMng> (true);
        m_overlap_diff_flux_scheme = options()->overlapDiffFluxScheme();
        m_overlap_diff_flux_scheme->init();
        m_overlap_diff_flux_model = new DiffFluxModelType(m_overlap_domain,
            m_overlap_diff_flux_scheme, geometry);
        m_overlap_diff_flux_model->init();
        m_overlap_adv_flux_scheme = options()->overlapAdvFluxScheme();
        m_overlap_adv_flux_scheme->init();
        m_overlap_adv_flux_model = new AdvFluxModelType(m_overlap_domain,
            m_overlap_adv_flux_scheme, geometry);
        m_overlap_adv_flux_model->init();
        if (m_r_bc_dirichlet)
          {
            m_r_bc_dirichlet->setAdvFluxModel(m_overlap_adv_flux_model);
            m_r_bc_dirichlet->setDiffFluxModel(m_overlap_diff_flux_model);
          }
        if (m_w_bc_dirichlet)
          {
            m_w_bc_dirichlet->setAdvFluxModel(m_overlap_adv_flux_model);
            m_w_bc_dirichlet->setDiffFluxModel(m_overlap_diff_flux_model);
          }
      }
    switch (m_computation_opt)
      {
    case TypesDtLocal::DFVF1:
      {
        IIntOp
            * stop_criteria =
                new IntOpT<ToyReactiveTransportCouplageDtLocalModule> (
                    this,
                    &ToyReactiveTransportCouplageDtLocalModule::updateConvergenceOnReservoirOverlap);
        IOp* update_op = new OpT<ToyReactiveTransportCouplageDtLocalModule> (
            this, &ToyReactiveTransportCouplageDtLocalModule::updatePressureTN);
        m_solver = new CouplingModelSolverT<
            ToyReactiveTransportCouplageDtLocalModule> (this, stop_criteria,
            update_op, options()->maxIter());
        if (m_output_level > 0)
          m_solver->setVerbose(true);
      }
      break;
    case TypesDtLocal::DFVF2:
      {
        IIntOp
            * stop_criteria =
                new IntOpT<ToyReactiveTransportCouplageDtLocalModule> (
                    this,
                    &ToyReactiveTransportCouplageDtLocalModule::updateConvergenceOnWindowOverlap);
        IOp* update_op = new OpT<ToyReactiveTransportCouplageDtLocalModule> (
            this, &ToyReactiveTransportCouplageDtLocalModule::updatePressureTN);
        m_solver = new CouplingModelSolverT<
            ToyReactiveTransportCouplageDtLocalModule> (this, stop_criteria,
            update_op, options()->maxIter());
        if (m_output_level > 0)
          m_solver->setVerbose(true);
      }
      break;
    case TypesDtLocal::VFVF1:
    case TypesDtLocal::VFVF2:
    default:
//      fatal() << "Solver not yet defined";
      break;
      }

    //WINDOW VARIABLE MANAGEMENT

    m_u_concentration_fine = new PartialVariableCellReal(VariableBuildInfo(
        this, "FineUConcentration", m_window_group.itemFamily()->name(),
        m_window_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    m_u_concentration_fine_tn = new PartialVariableCellReal(VariableBuildInfo(
        this, "FineUConcentrationTN", m_window_group.itemFamily()->name(),
        m_window_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    //RESERVOIR VARIABLE MANAGEMENT
    m_u_concentration_coarse = new PartialVariableCellReal(VariableBuildInfo(
        this, "CoarseUConcentration", m_reservoir_group.itemFamily()->name(),
        m_reservoir_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    m_u_concentration_coarse_tn = new PartialVariableCellReal(
        VariableBuildInfo(this, "CoarseUConcentrationTN",
            m_reservoir_group.itemFamily()->name(), m_reservoir_group.name(),
            IVariable::PPrivate | IVariable::PTemporary));
    m_v_concentration_fine = new PartialVariableCellReal(VariableBuildInfo(
        this, "FineVConcentration", m_window_group.itemFamily()->name(),
        m_window_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    m_v_concentration_fine_tn = new PartialVariableCellReal(VariableBuildInfo(
        this, "FineVConcentrationTN", m_window_group.itemFamily()->name(),
        m_window_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    //RESERVOIR VARIABLE MANAGEMENT
    m_v_concentration_coarse = new PartialVariableCellReal(VariableBuildInfo(
        this, "CoarseVConcentration", m_reservoir_group.itemFamily()->name(),
        m_reservoir_group.name(), IVariable::PPrivate | IVariable::PTemporary));
    m_v_concentration_coarse_tn = new PartialVariableCellReal(
        VariableBuildInfo(this, "CoarseVConcentrationTN",
            m_reservoir_group.itemFamily()->name(), m_reservoir_group.name(),
            IVariable::PPrivate | IVariable::PTemporary));

    //OVERLAP VARIABLE MANAGEMENT
    m_u_concentration_overlap.init(m_overlap_group);
    m_old_u_concentration_overlap.init(m_overlap_group);
    m_coarse_u_concentration_overlap.init(m_overlap_group);
    m_coarse_u_concentration_overlap_tn.init(m_overlap_group);
    m_v_concentration_overlap.init(m_overlap_group);
    m_old_v_concentration_overlap.init(m_overlap_group);
    m_coarse_v_concentration_overlap.init(m_overlap_group);
    m_coarse_v_concentration_overlap_tn.init(m_overlap_group);
    m_flux_overlap.init(m_interface_face_group);
    m_flux_ratio.init(m_interface_face_group);
    m_average_flux.init(m_interface_face_group);
    m_integral_flux.init(m_interface_face_group);
    /*
     VariableBuildInfo overlap_flux_vb(this,
     "OverlapFlux",
     m_overlap_inner_group.itemFamily()->name(),
     m_overlap_inner_group.name(),
     IVariable::PPrivate|IVariable::PTemporary);
     m_flux_overlap =  new PartialVariableFaceReal(overlap_flux_vb);
     VariableBuildInfo average_flux_vb(this,
     "AverageFlux",
     m_overlap_inner_group.itemFamily()->name(),
     m_overlap_inner_group.name(),
     IVariable::PPrivate|IVariable::PTemporary);
     m_average_flux =  new PartialVariableFaceReal(average_flux_vb);*/

    //Initial state computation
    computeInitialState();

    //////////////////////////////
    //    START COMPUTATION     //
    //////////////////////////////
    m_error = 0;
    m_first_iter = true;
    m_dom_global->start();
    m_face_normal_flux_velocity.synchronize();
    m_dom_reservoir->start();
    m_face_normal_flux_velocity.synchronize();
    m_dom_window->start();
    m_face_normal_flux_velocity.synchronize();

    //OVERLAP FLUX MODEL
      {
        m_cell_permx.synchronize();
        m_cell_permy.synchronize();
        m_cell_permz.synchronize();
        const CellGroup& internal_cells = m_overlap_domain->internalCells();
        ENUMERATE_CELL(icell, internal_cells)
          {
            m_cell_perm_k[icell].x.x = m_cell_permx[icell];
            ;
            m_cell_perm_k[icell].y.y = m_cell_permy[icell];
            ;
            m_cell_perm_k[icell].z.z = m_cell_permz[icell];
            ;
          }
        m_overlap_diff_flux_model->start(m_cell_perm_k);
        m_face_normal_flux_velocity.synchronize();
        m_overlap_adv_flux_model->start(m_face_normal_flux_velocity);
      }

    ENUMERATE_CELL(icell, m_window_group)
      {
        m_domainwindow[icell] = 1.;
      }
    ENUMERATE_CELL(icell, m_reservoir_group)
      {
        m_domainreservoir[icell] = 1.;
      }
    ENUMERATE_CELL(icell, m_overlap_group)
      {
        m_domainoverlap[icell] = 1.;
      }

  }

/*---------------------------------------------------------------------------*/
// Lecture des groupes de mailles : reservoir et window
// puis construction du groupe de maille overlap et de tous les groupes de
// faces et de nodes  necessaires aux calculs
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::readGroup()
  {
    String window_str = options()->window();
    String reservoir_str = options()->reservoir();

    m_window_group = ownCells().itemFamily()->findGroup(window_str, false);
    m_reservoir_group
        = ownCells().itemFamily()->findGroup(reservoir_str, false);

    //m_window_group = options()->window();
    //m_reservoir_group = options()->reservoir();

    // Construction d'un groupe des cells global
    ItemGroupBuilder<Cell> cell_builderGlobal(allCells().mesh(),
        "GLOBALCELLGROUP");
    ItemGroupBuilder<Face> face_builderGlobal(allFaces().mesh(),
        "GLOBALFACEGROUP");

    // Construction d'un groupe des faces internes
    ItemGroupBuilder<Face> iface_builderGlobal(allFaces().mesh(),
        "GLOBALINTERNALFACEGROUP");
    ItemGroupBuilder<Face> bface_builderGlobal(allFaces().mesh(),
        "GLOBALBOUNDARYFACEGROUP");

    // ======== //
    //  WINDOW
    // ======== //

    // faces de la window
    ItemGroupBuilder<Face> face_builder(m_window_group.mesh(),
        "WINDOWFACEGROUP");
    ENUMERATE_CELL(icell, m_window_group)
      {
        const Cell &cell = *icell;
        cell_builderGlobal.add(cell);
        face_builder.add(cell.faces()); // on ajoute ttes les faces de  la cellule
        face_builderGlobal.add(cell.faces());
      }
    m_windowFaceGp = face_builder.buildGroup();

    // maille interne a la zone window
    ItemGroupMapT<Cell, Integer> isInnerCellW(m_window_group);
    // face interne et de bord
    ItemGroupBuilder<Face> b_window_builder(m_window_group.mesh(),
        "WINDOWBOUNDARYFACEGROUP");
    ItemGroupBuilder<Face> i_window_builder(m_window_group.mesh(),
        "WINDOWINTERNALFACEGROUP");

    ENUMERATE_FACE(iface, m_windowFaceGp)
      {
        const Face &face = *iface;
        if (isInnerCellW.hasKey(face.frontCell()) and isInnerCellW.hasKey(
            face.backCell()))
          {
            i_window_builder.add(face);
          }
        else
          {
            ARCANE_ASSERT( (isInnerCellW.hasKey(face.frontCell()) xor
                    isInnerCellW.hasKey(face.backCell()) ),
                ("group Internal and edge computation") );
            b_window_builder.add(face);
          }
      }
    m_window_boundary_face = b_window_builder.buildGroup();
    m_window_internal_face = i_window_builder.buildGroup();

    // ========== //
    // RESERVOIR  //
    // ========== //

    // faces  du reservoir
    ItemGroupBuilder<Face> faceR_builder(m_reservoir_group.mesh(),
        "RESERVOIRFACEGROUP");

    ENUMERATE_CELL(icell, m_reservoir_group)
      {
        const Cell &cell = *icell;
        cell_builderGlobal.add(cell);
        faceR_builder.add(cell.faces()); // on ajoute ttes les faces de  la cellule
        face_builderGlobal.add(cell.faces());
      }
    m_reservoirFaceGp = faceR_builder.buildGroup();

    // maille interne a la zone reservoir
    ItemGroupMapT<Cell, Integer> isInnerCellR(m_reservoir_group);
    ItemGroupBuilder<Face> b_reservoir_builder(m_reservoir_group.mesh(),
        "RESERVOIRBOUNDARYFACEGROUP");
    ItemGroupBuilder<Face> i_reservoir_builder(m_reservoir_group.mesh(),
        "RESERVOIRINTERNALFACEGROUP");

    ENUMERATE_FACE(iface, m_reservoirFaceGp)
      {
        const Face &face = *iface;
        if (isInnerCellR.hasKey(face.frontCell()) and isInnerCellR.hasKey(
            face.backCell()))
          {
            i_reservoir_builder.add(face);
          }
        else
          {
            ARCANE_ASSERT( (isInnerCellR.hasKey(face.frontCell()) xor
                    isInnerCellR.hasKey(face.backCell()) ),
                ("Reservoir internal face group problem") ) ;
            b_reservoir_builder.add(face);
          }
      }

    m_reservoir_boundary_face = b_reservoir_builder.buildGroup();
    m_reservoir_internal_face = i_reservoir_builder.buildGroup();

    //
    // maillage global :
    //
    m_global_cell_group = cell_builderGlobal.buildGroup();
    m_global_face_group = face_builderGlobal.buildGroup();
    // Ensemble des cellules internes (Integer est une valeur fictive)
    ItemGroupMapT<Cell, Integer> isInnerCellGlobal(m_global_cell_group);

    ENUMERATE_FACE(iface, m_global_face_group)
      {
        const Face & face = *iface;
        if (isInnerCellGlobal.hasKey(face.frontCell())
            and isInnerCellGlobal.hasKey(face.backCell()))
          {
            iface_builderGlobal.add(face);
          }
        else
          {
            ARCANE_ASSERT((isInnerCellGlobal.hasKey(face.frontCell()) xor
                    isInnerCellGlobal.hasKey(face.backCell())),
                ("Group edge computation") );
            bface_builderGlobal.add(face);
          }
      }
    m_global_internal_face = iface_builderGlobal.buildGroup();
    m_global_boundary_face = bface_builderGlobal.buildGroup();

    // ======= //
    // OVERLAP //
    // ======= //

    ItemGroupBuilder<Cell> overlap_builder(m_global_cell_group.mesh(),
        "OVERLAP");

    ENUMERATE_FACE(iface, m_global_internal_face)
      {
        const Face &face = *iface;
        if ((isInnerCellR.hasKey(face.frontCell()) and isInnerCellW.hasKey(
            face.backCell()) or (isInnerCellR.hasKey(face.backCell())
            and isInnerCellW.hasKey(face.frontCell()))))
          {
            overlap_builder.add(face.frontCell());
            overlap_builder.add(face.backCell());
          }
      }
    m_overlap_group = overlap_builder.buildGroup();

    // maintenant on construit a partir de l'overlap la zone de recouvrement

    ItemGroupBuilder<Face> faceInter_builder(m_overlap_group.mesh(),
        "OVERLAPFACEGROUP");

    ENUMERATE_CELL(icell, m_overlap_group)
      {
        const Cell &cell = *icell;
        faceInter_builder.add(cell.faces()); // on ajoute les cellules de la face overlap
      }
    m_overlapFaceGp = faceInter_builder.buildGroup();

    // on veut  les faces internes de l'overlap = les faces de l'interface

    // Ensemble des cellules internes (Integer est une valeur fictive)
    ItemGroupMapT<Cell, Integer> isInnerCell(m_overlap_group);
    // Construction d'un groupe des faces internes
    ItemGroupBuilder<Face> iface_builder(m_overlapFaceGp.mesh(),
        "OVERLAPINNERFACEGROUP");
    ItemGroupBuilder<Face> interface_face_builder(m_overlapFaceGp.mesh(),
        "INTERFACEFACEGROUP");
    ItemGroupBuilder<Face> bface_builder(m_overlapFaceGp.mesh(),
        "OVERLAPBORDERFACEGROUP");

    ENUMERATE_FACE(iface, m_overlapFaceGp)
      {
        const Face &face = *iface;
        /*if ((isInnerCellW.hasKey(face.frontCell()) and isInnerCellR.hasKey(face.backCell()))
         or
         (isInnerCellR.hasKey(face.frontCell()) and isInnerCellW.hasKey(face.backCell())))
         {
         iface_builder.add(face);
         }
         else if (not (isInnerCell.hasKey(face.frontCell()) xor
         isInnerCell.hasKey(face.backCell())))
         {
         //            ARCANE_ASSERT( (isInnerCell.hasKey(face.frontCell()) xor
         //                    isInnerCell.hasKey(face.backCell()) ),
         //                ("boundary face group problem") );

         bface_builder.add(face);
         }*/
        // OLD VERSION
        if (isInnerCell.hasKey(face.frontCell()) and isInnerCell.hasKey(
            face.backCell()))
          {
            iface_builder.add(face);
            if ((isInnerCellW.hasKey(face.frontCell()) and isInnerCellR.hasKey(
                face.backCell())) or (isInnerCellR.hasKey(face.frontCell())
                and isInnerCellW.hasKey(face.backCell())))
              {
                interface_face_builder.add(face);
              }
          }
        else
          {
            ARCANE_ASSERT( (isInnerCell.hasKey(face.frontCell()) xor
                    isInnerCell.hasKey(face.backCell()) ),
                ("boundary face group problem") );
            bface_builder.add(face);
          }
      }

    m_overlap_inner_group = iface_builder.buildGroup();
    m_overlap_border_group = bface_builder.buildGroup();
    m_interface_face_group = interface_face_builder.buildGroup();

    // il faut ajouter reservoir+overlap
    // OVERLAP + RESERVOIR
    // CELL
    ItemGroupBuilder<Cell> cell_builder(allCells().mesh(), "OVER_RES");

    ENUMERATE_CELL(icell, m_reservoir_group)
      {
        const Cell &cell = *icell;
        cell_builder.add(cell);
      }

    ENUMERATE_CELL(icell, m_overlap_group)
      {
        const Cell &cell = *icell;
        cell_builder.add(cell);
      }
    m_overlap_reservoirGp = cell_builder.buildGroup();

    // faces et nodes de reservoir+overlap
    //
    ItemGroupBuilder<Face> faceO_builder(m_overlap_reservoirGp.mesh(),
        IMPLICIT_NAME);

    ENUMERATE_CELL(icell, m_overlap_reservoirGp)
      {
        const Cell &cell = *icell;
        faceO_builder.add(cell.faces()); // on ajoute ttes les faces de  la cellule
      }
    m_overlap_reservoirFaceGp = faceO_builder.buildGroup();

    // a partir de m_overlap et m_window on veut recuperer les mailles interface pour
    // la reservoir et la window qui auront des CL de Dirichlet

    // maille interne a la zone reservoir
    //
    // maille interface pour la window : CL de Dirichlet
    ItemGroupBuilder<Cell> interfaceW_cell_builder(m_overlap_group.mesh(),
        IMPLICIT_NAME);
    // maille interface pour le reservoir : CL de Dirichlet
    ItemGroupBuilder<Cell> interfaceR_cell_builder(m_overlap_group.mesh(),
        IMPLICIT_NAME);

    ENUMERATE_CELL(icell, m_overlap_group)
      {
        const Cell &cell = *icell;
        // la cellule cell de l'overlap  est  dans la window  , c'est donc une maille a BC pour la reservoir
        if (isInnerCellW.hasKey(cell))
          {
            interfaceR_cell_builder.add(cell);
          }
        else
          {
            interfaceW_cell_builder.add(cell);
          }
      }
    m_cell_interfaceW_group = interfaceW_cell_builder.buildGroup();
    m_cell_interfaceR_group = interfaceR_cell_builder.buildGroup();

    m_r_interface_cells.init(m_interface_face_group);
    m_w_interface_cells.init(m_interface_face_group);
    ENUMERATE_FACE(iface,m_interface_face_group)
      {
        const Face& face = *iface;
        if (isInnerCellW.hasKey(face.frontCell()))
          {
            m_r_interface_cells[face] = face.frontCell();
            m_w_interface_cells[face] = face.backCell();
          }
        else
          {
            m_w_interface_cells[face] = face.frontCell();
            m_r_interface_cells[face] = face.backCell();
          }
      }

    //
    // CREATE USERDEFINED BOUNDARIES
    //
    ItemGroupMapT<Face, Integer> isBoundaryFaceW(m_window_boundary_face);
    ItemGroupMapT<Face, Integer> isBoundaryFaceR(m_reservoir_boundary_face);
    ItemGroupMapT<Face, Integer> isBoundaryFaceG(m_global_boundary_face);
    // boucle sur les conditions aux limites
    Integer nb_boundary_condition = options()->boundaryCondition.size();
    m_bc_group_w.resize(nb_boundary_condition + 1);
    m_bc_group_r.resize(nb_boundary_condition + 1);
    m_bc_group_g.resize(nb_boundary_condition + 1);
    for (int i = 0; i < nb_boundary_condition; ++i)
      {
        String face_group_str = options()->boundaryCondition[i]->surface();
        FaceGroup face_group = ownFaces().itemFamily()->findGroup(
            face_group_str, false);
        m_bc_group_g[i] = face_group;
        String w_name = face_group.name() + "_W" + i;
        String r_name = face_group.name() + "_R" + i;
        ItemGroupBuilder<Face> reservoir_builder(face_group.mesh(), r_name);
        ItemGroupBuilder<Face> window_builder(face_group.mesh(), w_name);
        ENUMERATE_FACE(iface,face_group)
          {
            const Face& face = *iface;
            if (isBoundaryFaceW.hasKey(face))
              {
                window_builder.add(face);
                isBoundaryFaceW[face] = 1;
              }
            else
              {
                reservoir_builder.add(face);
                isBoundaryFaceR[face] = 1;
              }
            isBoundaryFaceG[face] = 1;
          }
        m_bc_group_w[i] = window_builder.buildGroup();
        m_bc_group_r[i] = reservoir_builder.buildGroup();
      }

    //Mark interface boundary
    ENUMERATE_FACE(iface,m_overlap_inner_group)
      {
        const Face& face = *iface;
        if (isBoundaryFaceW.hasKey(face))
          isBoundaryFaceW[face] = 1;
        if (isBoundaryFaceR.hasKey(face))
          isBoundaryFaceR[face] = 1;
      }

    //CREATE DEFAULT BOUNDARY GROUP ( not defined in previous boundary and interface
    String w_name("defaultWINDOWBOUNDARY");
    ItemGroupBuilder<Face>
        window_builder(m_window_boundary_face.mesh(), w_name);
    ENUMERATE_FACE(iface,m_window_boundary_face)
      {
        const Face& face = *iface;
        if (isBoundaryFaceW[face] == 0)
          window_builder.add(face);
      }
    m_bc_group_w[nb_boundary_condition] = window_builder.buildGroup();
    String r_name("DefaultRESERVOIRBOUNDARY");
    ItemGroupBuilder<Face> reservoir_builder(m_reservoir_boundary_face.mesh(),
        r_name);
    ENUMERATE_FACE(iface,m_reservoir_boundary_face)
      {
        const Face& face = *iface;
        if (isBoundaryFaceR[face] == 0)
          reservoir_builder.add(face);
      }
    m_bc_group_r[nb_boundary_condition] = reservoir_builder.buildGroup();
    String g_name("DefaultGLOBALBOUNDARY");
    ItemGroupBuilder<Face>
        global_builder(m_global_boundary_face.mesh(), g_name);
    ENUMERATE_FACE(iface,m_global_boundary_face)
      {
        const Face& face = *iface;
        if (isBoundaryFaceG[face] == 0)
          global_builder.add(face);
      }
    m_bc_group_g[nb_boundary_condition] = global_builder.buildGroup();

    m_interface_id = nb_boundary_condition + 2;
  }
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::initModelDomains()
  {
    // lecture des groupes de mailles
    readGroup();
      {
        ISubDomainModel::NumericalDomain
            * domain =
                dynamic_cast<ISubDomainModel::NumericalDomain*> (m_dom_global->getINumericalDomain());
        if (domain)
          {
            domain->setName("GlobalDomain");
            domain->setInternalItems(m_global_cell_group,
                m_global_internal_face);
            domain->setBoundary(m_global_boundary_face);
            for (Integer i = 0; i < m_bc_group_g.size(); i++)
              if (m_bc_group_g[i].size() > 0)
                domain->addFaceBoundary(m_bc_group_g[i], i);
            //domain->setInterfaceItems(m_overlap_group, m_overlap_inner_group) ;

//            if (m_output_level > 1)
//              domain->printInfo();
          }
        else
          fatal() << "Numerical model should be a domain model";
      }

      {
        ISubDomainModel::NumericalDomain
            * domain =
                dynamic_cast<ISubDomainModel::NumericalDomain*> (m_dom_reservoir->getINumericalDomain());
        if (domain)
          {
            domain->setName("ReservoirDomain");
            domain->setInternalItems(m_reservoir_group,
                m_reservoir_internal_face);
            domain->setBoundary(m_reservoir_boundary_face);
            for (Integer i = 0; i < m_bc_group_r.size(); i++)
              if (m_bc_group_r[i].size() > 0)
                domain->addFaceBoundary(m_bc_group_r[i], i);
            domain->addFaceBoundary(m_interface_face_group, m_interface_id);
            domain->setInterfaceItems(m_overlap_group, m_overlap_inner_group,
                m_cell_interfaceR_group);
//            if (m_output_level > 1)
//              domain->printInfo();
          }
        else
          fatal() << "Numerical model should be a domain model";
      }

      {
        ISubDomainModel::NumericalDomain
            * domain =
                dynamic_cast<ISubDomainModel::NumericalDomain*> (m_dom_window->getINumericalDomain()); /// y a aussi getNumericalDomain : conflict ?
        if (domain)
          {
            domain->setName("WindowDomain");
            domain->setInternalItems(m_window_group, m_window_internal_face);
            domain->setBoundary(m_window_boundary_face);
            for (Integer i = 0; i < m_bc_group_w.size(); i++)
              if (m_bc_group_w[i].size() > 0)
                domain->addFaceBoundary(m_bc_group_w[i], i);
            domain->addFaceBoundary(m_interface_face_group, m_interface_id);
            domain->setInterfaceItems(m_overlap_group, m_overlap_inner_group,
                m_cell_interfaceW_group);
//            if (m_output_level > 1)
//              domain->printInfo();
          }
        else
          fatal() << "Numerical model should be a domain model";
      }
      {
        m_overlap_domain = new ISubDomainModel::NumericalDomain(traceMng());
        m_overlap_domain->setName("OverlapDomain");
        m_overlap_domain->setInternalItems(m_overlap_group,
            m_overlap_inner_group);
        m_overlap_domain->setBoundary(m_overlap_border_group);
//        if (m_output_level > 1)
//          m_overlap_domain->printInfo();
      }
  }

void ToyReactiveTransportCouplageDtLocalModule::setBoundaryCondition(
    ISubDomainModel* sd_model, Array<FaceGroup>& bc_group)
  {
    ISubDomainModel::FaceBoundaryConditionMng* bc_mng =
        sd_model->getFaceBoundaryConditionMng();
    // boucle sur les conditions aux limites
    Integer nb_boundary_condition = options()->boundaryCondition.size();
    for (int i = 0; i < nb_boundary_condition; ++i)
      {
        FaceGroup face_group = bc_group[i];
        if (face_group.size() > 0)
          {
            Real valeurBC = options()->boundaryCondition[i]->value();
            SubDomainModelProperty::eBoundaryConditionType typeBC =
                options()->boundaryCondition[i]->type();

            // CREATE BOUNDARY CONDITION BC0 for boundary 0 and OPERATORS to init them
            ISubDomainModel::FaceBoundaryCondition* bc =
                new ISubDomainModel::FaceBoundaryCondition(i, typeBC, i);
            bc->setValue(valeurBC);
            bc->activate(true);
            //Record to bc manager
            bc_mng->addNew(bc, new ISubDomainModel::FaceBCOp(sd_model,
                &ISubDomainModel::initBoundaryCondition, bc));
          }
      }
      {
        Integer i = nb_boundary_condition;
        FaceGroup face_group = bc_group[i];
        if (face_group.size() > 0)
          {
            SubDomainModelProperty::eBoundaryConditionType typeBC =
                SubDomainModelProperty::NullFlux;

            // CREATE BOUNDARY CONDITION BC0 for boundary 0 and OPERATORS to init them
            ISubDomainModel::FaceBoundaryCondition* bc =
                new ISubDomainModel::FaceBoundaryCondition(i, typeBC, i);
            bc->setValue(0);
            //Record to bc manager
            bc_mng->addNew(bc, new ISubDomainModel::FaceBCOp(sd_model,
                &ISubDomainModel::initBoundaryCondition, bc));
          }
      }
  }

void ToyReactiveTransportCouplageDtLocalModule::computeInitialState()
  {
    _copy(m_global_cell_group, m_u_concentration_tn, m_u_concentration);
    _copy(m_reservoir_group, *m_u_concentration_coarse, m_u_concentration);
    _copy(m_reservoir_group, *m_u_concentration_coarse_tn, m_u_concentration);
    _copy(m_window_group, *m_u_concentration_fine, m_u_concentration);
    _copy(m_window_group, *m_u_concentration_fine_tn, m_u_concentration);
    _copy(m_overlap_group, m_u_concentration_overlap, m_u_concentration);
    _copy(m_overlap_group, m_old_u_concentration_overlap, m_u_concentration);
    _copy(m_overlap_group, m_coarse_u_concentration_overlap, m_u_concentration);
    _copy(m_overlap_group, m_coarse_u_concentration_overlap_tn,
        m_u_concentration);

    _copy(m_global_cell_group, m_v_concentration_tn, m_v_concentration);
    _copy(m_reservoir_group, *m_v_concentration_coarse, m_v_concentration);
    _copy(m_reservoir_group, *m_v_concentration_coarse_tn, m_v_concentration);
    _copy(m_window_group, *m_v_concentration_fine, m_v_concentration);
    _copy(m_window_group, *m_v_concentration_fine_tn, m_v_concentration);
    _copy(m_overlap_group, m_v_concentration_overlap, m_v_concentration);
    _copy(m_overlap_group, m_old_v_concentration_overlap, m_v_concentration);
    _copy(m_overlap_group, m_coarse_v_concentration_overlap, m_v_concentration);
    _copy(m_overlap_group, m_coarse_v_concentration_overlap_tn,
        m_v_concentration);

  }
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::compute()
  {
    info() << "compute";
    //m_u_concentration_fine->synchronize();
    //m_u_concentration_coarse->synchronize();
    m_u_concentration.synchronize();
    //m_v_concentration_fine->synchronize();
    //m_v_concentration_coarse->synchronize();
    m_v_concentration.synchronize();
    m_permeability.synchronize();

    switch (m_computation_opt)
      {
    case TypesDtLocal::GlobalCoarseDt:
      computeGlobalDomainWithCoarseDt();
      break;
    case TypesDtLocal::GlobalFineDt:
      computeGlobalDomainWithFineDt();
      break;
    case TypesDtLocal::DFVF1:
      computeDFVF1();
      break;
    case TypesDtLocal::DFVF2:
      computeDFVF2();
      break;
    case TypesDtLocal::VFVF1:
      computeVFVF1();
      break;
    case TypesDtLocal::VFVF2:
      computeVFVF2();
      break;
      }
    m_first_iter = false;
  }

void ToyReactiveTransportCouplageDtLocalModule::computeDeltaT()
  {
    // Mise ? jour du pas de temps
    if (m_coarse_time_mng->isCurrentTimeStepOk())
      {
        Real deltat = m_coarse_time_mng->getCurrentTimeStep();
        bool timeStepOk = m_time_step_mng->manageTimeStep(&deltat);
        m_coarse_time_mng->setNewTimeStep(deltat);
        if (!timeStepOk)
          m_coarse_time_mng->disableCurrentTimeStep();
      }
  }

void ToyReactiveTransportCouplageDtLocalModule::computeGlobalDomainWithCoarseDt()
  {
    if (m_output_level > 0)
      {
        info() << " Global domain resolution with large time step ";
        info() << "############################################";
        info() << "          COARSE DT on GLOBAL MESH           ";
        info() << "############################################";
      }
    //
    // resolution a  m_coarse_dt;
    //
    m_dom_global->compute(CoarseDtSeq);
  }

void ToyReactiveTransportCouplageDtLocalModule::computeGlobalDomainWithFineDt()
  {
    //(choix == 0)
    if (m_output_level > 0)
      {
        info() << " Global domain resolution with small time step ";
        info() << "############################################";
        info() << "           FINE DT on GLOBAL MESH           ";
        info() << "############################################";
      }
    //
    // resolution a m_fine_dt
    //
    m_dom_global->compute(FineDtSeq);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
//
// Methode 1 DF/VF
//
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::computeDFVF1()
  {

    // on sauvegarde le temps courant
    if (m_output_level > 0)
      info() << " METHODE DF/VF 1";
    m_error = 0;
    m_first_solver_step = true;
    //
    // etape 1 : resolution sur le maillage global pour un grand pas de temps
    // ----------------------------------------------------------------------
    if (m_output_level > 0)
      info() << " COMPUTE GLOBAL DOMAIN ON COARSE TIME STEP";
    //    m_dom_global->compute(CoarseDtSeq);

    //
    // etape 2 : integration en temps sur le maillage window pour des sous pas de temps
    // --------------
    if (m_output_level > 0)
      info() << " INTEGRATE WINDOW DOMAIN ON FINE TIME STEP";
    //    m_dom_window->compute(FineDtSeq);

    if (m_output_level > 3)
      writeoutput();
    m_solver->solve(m_dom_reservoir, CoarseDtSeq, m_dom_window, FineDtSeq);
  }

void ToyReactiveTransportCouplageDtLocalModule::computeDFVF2()
  {

    // on sauvegarde le temps courant
    if (m_output_level > 0)
      info() << " METHODE DF/VF 2";
    m_error = 0;
    m_first_solver_step = true;
    //
    // etape 1 : resolution sur le maillage global pour un grand pas de temps
    // ----------------------------------------------------------------------
    if (m_output_level > 0)
      info() << " COMPUTE GLOBAL DOMAIN ON COARSE TIME STEP";
    //    initCoarsePressureOnOverlap();
    //    m_dom_global->compute(CoarseDtSeq);
    //    computePressureOnOverlap();
    //    updateCoarsePressureOnOverlap();

    if (m_output_level > 0)
      info() << " COUPLING SOLVER ON TWO SUBDOMAINS";
    if (m_output_level > 3)
      writeoutput();
    m_solver->solve(m_dom_window, FineDtSeq, m_dom_reservoir, CoarseDtSeq);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
// on met a jour le flux sur les faces internes de la zone de recouvrement
//
void ToyReactiveTransportCouplageDtLocalModule::computeFluxOnOverlap()
  {
    ItemGroupMapT<Face, Real> adv_flux_overlap;
    adv_flux_overlap.init(m_interface_face_group);
    ItemGroupMapT<Face, Real> diff_flux_overlap;
    diff_flux_overlap.init(m_interface_face_group);
    m_overlap_adv_flux_model->computeFlux<ItemGroupMapT<Face, Real> ,
        ItemGroupMapT<Cell, Real> > (adv_flux_overlap,
        m_u_concentration_overlap, m_interface_face_group);
    m_overlap_diff_flux_model->computeFlux<ItemGroupMapT<Face, Real> ,
        ItemGroupMapT<Cell, Real> > (diff_flux_overlap,
        m_u_concentration_overlap, m_interface_face_group);
    //    Real local_time_step = m_fine_time_mng->getCurrentTimeStep();
    //    Real global_time_step = m_coarse_time_mng->getCurrentTimeStep();
    //    Real time_step_ratio = local_time_step / global_time_step;
    ENUMERATE_FACE(iface,m_interface_face_group)
      {
        //        info() << "=======================";
        //        info() << "FLUXADV = " << adv_flux_overlap[iface];
        //        info() << "FLUXDIF = " << diff_flux_overlap[iface];
        m_flux_ratio[iface] = adv_flux_overlap[iface]
            * (adv_flux_overlap[iface] + diff_flux_overlap[iface]);
        //        m_flux_overlap[iface] = (adv_flux_overlap[iface] + diff_flux_overlap[iface])/(1.+ time_step_ratio);
        m_flux_overlap[iface] = (adv_flux_overlap[iface]
            + diff_flux_overlap[iface]);
      }
    //    if (m_w_bc_neumann)
    //      m_w_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
    //          IDiscreteVarTypes::Scalar> (m_flux_overlap);
    //    if (m_r_bc_neumann)
    //      m_r_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
    //          IDiscreteVarTypes::Scalar> (m_flux_overlap);
    ItemGroupMapT<Cell, Integer> isInnerCellW(m_window_group);
    ItemGroupMapT<Cell, Integer> isInnerCellR(m_reservoir_group);
    if (m_w_bc_neumann)
      {
        ENUMERATE_FACE(iface,m_interface_face_group)
          {
            const Face& F = *iface;
            if (isInnerCellW.hasKey(F.backCell()) && isInnerCellR.hasKey(F.frontCell()))
              m_flux_overlap[iface] = m_flux_overlap[iface];
            else
              m_flux_overlap[iface] = -m_flux_overlap[iface];
          }
        m_w_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
            IDiscreteVarTypes::Scalar> (m_flux_overlap);
      }
    if (m_r_bc_neumann)
      {
        ENUMERATE_FACE(iface,m_interface_face_group)
          {
            const Face& F = *iface;
            if (isInnerCellR.hasKey(F.backCell()) && isInnerCellW.hasKey(F.frontCell()))
              m_flux_overlap[iface] = m_flux_overlap[iface];
            else
              m_flux_overlap[iface] = -m_flux_overlap[iface];
          }
        m_r_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
            IDiscreteVarTypes::Scalar> (m_flux_overlap);
      }
  }

void ToyReactiveTransportCouplageDtLocalModule::initAverageFluxOnOverlap()
  {
    debug(Trace::Medium) << "initAverageFluxOnOverlap";
    m_integral_length = 0;
    ENUMERATE_FACE(iface,m_interface_face_group)
      {
        m_average_flux[iface] = 0.;
        m_integral_flux[iface] = 0.;
      }
  }
void ToyReactiveTransportCouplageDtLocalModule::integrateFluxOnOverlap()
  {
    debug(Trace::Medium) << "integrateFluxOnOverlap";
    ItemGroupMapT<Face, Real> adv_flux_overlap;
    adv_flux_overlap.init(m_interface_face_group);
    ItemGroupMapT<Face, Real> diff_flux_overlap;
    diff_flux_overlap.init(m_interface_face_group);
    m_overlap_adv_flux_model->computeFlux<ItemGroupMapT<Face, Real> ,
        ItemGroupMapT<Cell, Real> > (adv_flux_overlap,
        m_u_concentration_overlap, m_interface_face_group);
    m_overlap_diff_flux_model->computeFlux<ItemGroupMapT<Face, Real> ,
        ItemGroupMapT<Cell, Real> > (diff_flux_overlap,
        m_u_concentration_overlap, m_interface_face_group);
    Real local_time_step = m_fine_time_mng->getCurrentTimeStep();
    //    Real global_time_step = m_coarse_time_mng->getCurrentTimeStep();
    //    Real time_step_ratio = local_time_step / global_time_step;
    ENUMERATE_FACE(iface,m_interface_face_group)
      {
        //        info() << "=======================";
        //        info() << "FLUXADV = " << adv_flux_overlap[iface];
        //        info() << "FLUXDIF = " << diff_flux_overlap[iface];
        m_flux_ratio[iface] = adv_flux_overlap[iface]
            * (adv_flux_overlap[iface] + diff_flux_overlap[iface]);
        //        m_flux_overlap[iface] = (adv_flux_overlap[iface] + diff_flux_overlap[iface])/(1.+ time_step_ratio);
        m_flux_overlap[iface] = (adv_flux_overlap[iface]
            + diff_flux_overlap[iface]);
      }
    info() << " old integral length = " << m_integral_length << " adding "
        << local_time_step;
    m_integral_length += local_time_step;
    info() << " new integral length = " << m_integral_length;
    ENUMERATE_FACE(iface,m_interface_face_group)
      {
        m_integral_flux[iface] += m_flux_overlap[iface] * local_time_step;
        info() << "LOCAL TIME STEP = " << local_time_step;
        info() << "m_flux_overlap =  " << m_flux_overlap[iface];
      }
  }

void ToyReactiveTransportCouplageDtLocalModule::computeAverageFluxOnOverlap()
  {
    debug(Trace::Medium) << "computeAverageFluxOnOverlap";
    Real coarse_time_step = m_coarse_time_mng->getCurrentTimeStep();
    if (fabs(m_integral_length - coarse_time_step) > 1e-12)
      {
        info() << " m_integral_length " << m_integral_length;
        info() << " coarse_time_step " << coarse_time_step;
        info() << " integral difference " << m_integral_length
            - coarse_time_step;
        fatal() << "Inconsistent integral length";
      }
        ENUMERATE_FACE(iface,m_interface_face_group)
          {
            m_average_flux[iface] = m_integral_flux[iface] / coarse_time_step;
          }

    // ATTENTION : Flux est stocké selon l'orientation de la normale du point de vue global
    // dans m_average_flux.
    // Par contre, dans les sous-modèles, les flux sont toujours stockés
    // dans la direction sortant pour une face de bord

    ItemGroupMapT<Cell, Integer> isInnerCellW(m_window_group);
    ItemGroupMapT<Cell, Integer> isInnerCellR(m_reservoir_group);
    if (m_w_bc_neumann)
      {
        ENUMERATE_FACE(iface,m_interface_face_group)
          {
            const Face& F = *iface;
            if (isInnerCellW.hasKey(F.backCell()) && isInnerCellR.hasKey(F.frontCell()))
              m_average_flux[iface] = m_average_flux[iface];
            else
              m_average_flux[iface] = -m_average_flux[iface];
          }
        m_w_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
            IDiscreteVarTypes::Scalar> (m_average_flux);
      }
    if (m_r_bc_neumann)
      {
        ENUMERATE_FACE(iface,m_interface_face_group)
          {
            const Face& F = *iface;
            if (isInnerCellR.hasKey(F.backCell()) && isInnerCellW.hasKey(F.frontCell()))
              m_average_flux[iface] = m_average_flux[iface];
            else
              m_average_flux[iface] = -m_average_flux[iface];
          }
        m_r_bc_neumann->setValues<ItemGroupMapT<Face, Real> ,
            IDiscreteVarTypes::Scalar> (m_average_flux);
      }
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::computePressureOnOverlap()
  {
    info() << " computePressureOnOverlap ";
    _copy(m_overlap_group, m_u_concentration_overlap, m_u_concentration);
    _copy(m_overlap_group, m_v_concentration_overlap, m_v_concentration);
  }

void ToyReactiveTransportCouplageDtLocalModule::computeWindowPressureOnOverlap()
  {
    info() << "=========INIT COMPUTEWINDOWPRESSUREOVERLAP";
    _copy2(m_cell_interfaceW_group, m_u_concentration_overlap,
        m_u_concentration);
    _copy2(m_cell_interfaceW_group, m_v_concentration_overlap,
        m_v_concentration);

  }

void ToyReactiveTransportCouplageDtLocalModule::computeReservoirPressureOnOverlap()
  {
    info() << " computeReservoirPressureOnOverlap ";
    _copy2(m_cell_interfaceR_group, m_u_concentration_overlap,
        m_u_concentration);
    _copy2(m_cell_interfaceR_group, m_v_concentration_overlap,
        m_v_concentration);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::initCoarsePressureOnOverlap()
  {
    info() << "=========INIT INITCOARSEPRESSUREONOVERLAP";
    _copy(m_overlap_group, m_coarse_u_concentration_overlap,
        m_coarse_u_concentration_overlap_tn);
    _copy(m_overlap_group, m_coarse_v_concentration_overlap,
        m_coarse_v_concentration_overlap_tn);
  }
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::updateCoarsePressureOnOverlap()
  {
    info() << "=========INIT UPDATECOARSEPRESSUREONOVERLAP";
    _copysave(m_overlap_group, m_coarse_u_concentration_overlap_tn,
        m_coarse_u_concentration_overlap, m_u_concentration);
    _copysave(m_overlap_group, m_coarse_v_concentration_overlap_tn,
        m_coarse_v_concentration_overlap, m_v_concentration);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::restore()
  {
    info() << "restore";
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::computeInterfaceFacePressure(
    const FaceGroup& group, const ItemGroupMapT<Cell, Real>& u_concentration,
    ItemGroupMapT<Face, Cell>& exterior_cells,
    ItemGroupMapT<Face, Real>& face_u_concentration)
  {
    info() << "computeInterfaceFacePressure";
    ENUMERATE_FACE(iface, group)
      {
        face_u_concentration[iface] = u_concentration[exterior_cells[iface]];
      }
  }
void ToyReactiveTransportCouplageDtLocalModule::computeReservoirInterfaceFacePressure()
  {
    info() << "computeReservoirInterfaceFacePressure";
    ItemGroupMapT<Face, Real> face_u_concentration;
    face_u_concentration.init(m_interface_face_group);
    computeInterfaceFacePressure(m_interface_face_group,
        m_u_concentration_overlap, m_r_interface_cells, face_u_concentration);
    m_r_bc_dirichlet->setValues<ItemGroupMapT<Face, Real> ,
        IDiscreteVarTypes::Scalar> (face_u_concentration);
  }
void ToyReactiveTransportCouplageDtLocalModule::computeWindowInterfaceFacePressure()
  {
    info() << "computeWindowInterfaceFacePressure";
    ItemGroupMapT<Face, Real> face_u_concentration;
    face_u_concentration.init(m_interface_face_group);
    computeInterfaceFacePressure(m_interface_face_group,
        m_u_concentration_overlap, m_w_interface_cells, face_u_concentration);
    m_w_bc_dirichlet->setValues<ItemGroupMapT<Face, Real> ,
        IDiscreteVarTypes::Scalar> (face_u_concentration);
  }

void ToyReactiveTransportCouplageDtLocalModule::updatePressureFromWindow()
  {
    info() << "=========INIT UPDATEPRESSUREFROMWINDOW";
    //    writeoutput();
    _copy(m_window_group, *m_u_concentration_fine, m_u_concentration);
    _copy(m_overlap_group, m_old_u_concentration_overlap, m_u_concentration);
    _copy(m_window_group, *m_v_concentration_fine, m_v_concentration);
    _copy(m_overlap_group, m_old_v_concentration_overlap, m_v_concentration);
    //    writeoutput();
  }
void ToyReactiveTransportCouplageDtLocalModule::updatePressureFromReservoir()
  {
    info() << "=========INIT UPDATEPRESSUREFROMRESERVOIR";
    //    writeoutput();
    _copy(m_reservoir_group, *m_u_concentration_coarse, m_u_concentration);
    _copy(m_overlap_group, m_old_u_concentration_overlap, m_u_concentration);
    _copy(m_reservoir_group, *m_v_concentration_coarse, m_v_concentration);
    _copy(m_overlap_group, m_old_v_concentration_overlap, m_v_concentration);
    //    writeoutput();
  }

void ToyReactiveTransportCouplageDtLocalModule::updatePressureTN()
  {
    info() << "=========INIT UPDATEPRESSURETN";
    _copy(m_global_cell_group, m_u_concentration_tn, m_u_concentration);
    _copy(m_window_group, *m_u_concentration_fine_tn, m_u_concentration);
    _copy(m_reservoir_group, *m_u_concentration_coarse_tn, m_u_concentration);
    _copy(m_global_cell_group, m_v_concentration_tn, m_v_concentration);
    _copy(m_window_group, *m_v_concentration_fine_tn, m_v_concentration);
    _copy(m_reservoir_group, *m_v_concentration_coarse_tn, m_v_concentration);
  }

void ToyReactiveTransportCouplageDtLocalModule::initFinePressure()
  {
    info() << "=========INIT INITFINEPRESSURE";
    //    writeoutput();
    _copy(m_window_group, m_u_concentration, *m_u_concentration_fine_tn);
    _copy(m_window_group, m_v_concentration, *m_v_concentration_fine_tn);
    //    writeoutput();
  }
void ToyReactiveTransportCouplageDtLocalModule::initCoarsePressure()
  {
    info() << "=========INIT INITCOARSEPRESSURE";
    //    writeoutput();
    _copy(m_reservoir_group, m_u_concentration, *m_u_concentration_coarse_tn);
    _copy(m_reservoir_group, m_v_concentration, *m_v_concentration_coarse_tn);
    //    writeoutput();
  }

void ToyReactiveTransportCouplageDtLocalModule::computeWindowDirichletCellPressure()
  {
    if (m_first_solver_step)
      {
        m_val_limit1.resize(m_cell_interfaceW_group.size());
        m_val_limit2.resize(m_cell_interfaceW_group.size());
        m_teta = 1.;
      }
    computeDirichletCellPressure(m_val_limit1, m_val_limit2,
        m_cell_interfaceW_group);
  }
void ToyReactiveTransportCouplageDtLocalModule::computeReservoirDirichletCellPressure()
  {
    if (m_first_solver_step)
      {
        m_val_limit1.resize(m_cell_interfaceR_group.size());
        m_val_limit2.resize(m_cell_interfaceR_group.size());
        m_teta = 1.;
      }
    computeDirichletCellPressure(m_val_limit1, m_val_limit2,
        m_cell_interfaceR_group);
  }

void ToyReactiveTransportCouplageDtLocalModule::computeDirichletCellPressure(
    Array<Real>& Value_Limit1, Array<Real>& Value_Limit2,
    const CellGroup& interface_group)
  {
    Real coarse_current_time = m_coarse_time_mng->getCurrentTime();
    Real coarse_deltat = m_coarse_time_mng->getCurrentTimeStep();
    Real fine_current_time = m_fine_time_mng->getCurrentTime();

    //ATTENTION NE MARCHE PAS AVEC LES VARIABLES PARTIELS
    Integer ival = 0;
    ENUMERATE_CELL(icell, interface_group)
      {
        const Cell& cell = *icell;
        Value_Limit1[ival] = m_coarse_u_concentration_overlap_tn[cell];
        Value_Limit2[ival] = m_u_concentration[icell];
        m_u_concentration_overlap[cell] = Value_Limit1[ival]
            + (Value_Limit2[ival] - Value_Limit1[ival]) * ((fine_current_time
                - coarse_current_time) / coarse_deltat + 1.);
        ival++;
      }
    if (m_first_solver_step)
      {
        m_teta = 1.0;
        m_first_solver_step = false;
      }
    /* OLD VERSION
     //ATTENTION NE MARCHE PAS AVEC LES VARIABLES PARTIELS
     Integer ival = 0;
     ENUMERATE_CELL(icell, interface_group)
     {
     const Cell& cell = *icell ;
     // demi somme des valeurs aux temps N et N-1
     m_old_pressure_overlap[icell] = Value_Limit2[ival];
     Value_Limit1[ival] = m_coarse_pressure_overlap_tn[cell];
     Value_Limit2[ival] = m_teta * m_pressure[icell] + (1. - m_teta)
     *Value_Limit2[ival];
     m_pressure_overlap[cell] = Value_Limit2[ival];
     info()<<"OVERLAP DIRICHLET PRESSURE"<<m_pressure_overlap[cell];
     ival++;
     }
     if(m_first_solver_step)
     {
     m_teta = 0.5 ;
     m_first_solver_step = false ;
     }*/
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
//
// Schema  VF en Temps et VF en Espace pour la methode des pas de temps locaux 1
//
//         Window = CL en Flux et Reservoir CL en Pression
//
void ToyReactiveTransportCouplageDtLocalModule::computeVFVF1()
  {
    fatal() << "Not yet implemented";
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
//
// Methode 2 VF/VF
//
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::computeVFVF2()
  {
    fatal() << "Not yet implemented";
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/

void ToyReactiveTransportCouplageDtLocalModule::computeDirichletOverlapForReservoir()
  {
    Real teta = 1.;
    if (m_first_solver_step)
      {
        teta = 1.0;
        m_first_solver_step = false;
      }
    // on veut recuperer la pression a imposer sur la zone reservoir
    ENUMERATE_CELL(icell, m_cell_interfaceR_group)
      {
        const Cell &cell = *icell;
        m_u_concentration_overlap[cell] = (1. - teta)
            * m_u_concentration_overlap[cell] + teta * m_u_concentration[icell]; //  m_pressure[icell];
      }
    /* OLD VERSION
     Real teta =0.5;
     // on veut recuperer la pression a imposer sur la zone reservoir
     ENUMERATE_CELL(icell, m_cell_interfaceR_group)
     {
     const Cell &cell = *icell;
     m_old_pressure_overlap[cell] = m_pressure_overlap[cell];
     m_pressure_overlap[cell] = (1.-teta) * m_old_pressure_overlap[cell]
     + teta*m_pressure[icell]; //  m_pressure[icell];
     }*/
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
// calcul du critere de convergence de l'algorithme
//
Integer ToyReactiveTransportCouplageDtLocalModule::updateConvergenceOnReservoirOverlap()
  {
    Integer cv = updateConvergenceOnOverlap(m_cell_interfaceR_group);
    return 1 - cv;
  }

Integer ToyReactiveTransportCouplageDtLocalModule::updateConvergenceOnWindowOverlap()
  {
    Integer cv = updateConvergenceOnOverlap(m_cell_interfaceW_group);
    return 1 - cv;
  }

Integer ToyReactiveTransportCouplageDtLocalModule::updateConvergenceOnOverlap(
    const CellGroup& cell_group)
  {
    Integer CV = 0;
    Real stopping_criteria = options()->stoppingCriteria();
    Real Val = -10000.;
    Real ValTmp1 = -10000.;
    Real ValTmp2 = -10000.;
    ENUMERATE_CELL(icell, cell_group)
      {
        const Cell& cell = *icell;
        ValTmp1 = m_u_concentration_overlap[cell] - m_u_concentration[icell];
        ValTmp2 = m_old_u_concentration_overlap[cell]
            - m_u_concentration_overlap[cell];
        if (math::abs(ValTmp2) > Val)
          {
            Val = math::abs(ValTmp2);
          }
      }
    if (m_output_level > 0)
      {
        info();
        info() << "-------------------------------------------------";
        info() << "| Solver convergence criteria : " << Val;
        info() << "-------------------------------------------------";
        info();
      }
    if (Val <= stopping_criteria)
      CV = 1;
    return CV;
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::initPressureOnReservoir()
  {
    info() << "=========INIT PRESSUREONRESERVOIR";
    _copy(m_reservoir_group, m_u_concentration, *m_u_concentration_coarse_tn);
    _copy(m_reservoir_group, m_v_concentration, *m_v_concentration_coarse_tn);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ToyReactiveTransportCouplageDtLocalModule::initPressureOnWindow()
  {
    info() << "=========INIT PRESSUREONWINDOW";
    _copy(m_window_group, m_u_concentration, *m_u_concentration_fine_tn);
    _copy(m_window_group, m_v_concentration, *m_v_concentration_fine_tn);
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
Integer ToyReactiveTransportCouplageDtLocalModule::updateVFConvergenceOnOverlap(
    const CellGroup& cell_group)
  {
    Integer CV = 0;
    Real stopping_criteria = options()->stoppingCriteria();
    Real Val = -10000.;
    Real ValTmp1 = -10000.;
    Real ValTmp2 = -10000.;
    ENUMERATE_CELL(icell, cell_group)
      {
        const Cell& cell = *icell;
        ValTmp1 = math::abs(m_u_concentration_overlap[cell]
            - m_u_concentration[icell]);
        ValTmp2 = math::abs(m_old_u_concentration_overlap[cell]
            - m_u_concentration_overlap[cell]);
        if (ValTmp2 > Val)
          {
            Val = ValTmp2;
          }
      }
    if (Val <= stopping_criteria)
      CV = 1;
    return CV;
  }

/*-----------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------*/

//
//
void ToyReactiveTransportCouplageDtLocalModule::computeVFDirichletOverlapForReservoir()
  {
    Real teta = 0.5;
    // on veut recuperer la pression a imposer sur la zone reservoir
    //
    //
    ENUMERATE_CELL(icell, m_cell_interfaceR_group)
      {
        const Cell &cell = *icell;
        //florian m_old_pressure_overlap[cell] = m_pressure_overlap[cell];
        m_u_concentration_overlap[cell] = (1. - teta)
            * m_old_u_concentration_overlap[cell] + teta
            * m_average_u_concentration[cell]; //  m_pressure[icell];
      }
  }
//

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
//
// calcul de la condition de raccord de type Dirichlet au cours des petits pas de temps
// entre 2 valeurs limites données par la resolution reservoir a grand pas de temps
//
// _____|______________________ Val_Lim2   en T2  Val_Lim2 = 1/2(3*Val_X - Val_A)
//
//   x  | x
//   x  | x
//   x  | x
//   x  | x
//   x  | x          X                     en T1 + 1/2 DT
//   x  | x
//   x  | x
//   x  | x
// _____|______________________ Val_Lim1   en T1  Val_Lim1 = 1/2(Val_A + Val_X)
//
//   x  | x
//   x  | x
//   x  | x
//   x  | x
//   x  | x          A                     en T0 + 1/2 DT
//   x  | x
//   x  | x
//   x  | x
//
//
//   CL(x) = Val_Lim1 + (Val_Lim2 - Val_Lim1) * (current=_time/dt_local)
//
//

void ToyReactiveTransportCouplageDtLocalModule::computeVFDirichletOverlapForWindow()
  {
    Real current_time = m_fine_time_mng->getCurrentTime();
    Real dt_local = m_fine_time_mng->getCurrentTimeStep();
    computeVFDirichletOverlapForWindow(current_time, dt_local, m_val_limit1,
        m_val_limit2);
  }

void ToyReactiveTransportCouplageDtLocalModule::computeVFDirichletOverlapForWindow(
    Real current_time, Real dt_local, Array<Real> Val_Lim1,
    Array<Real> Val_Lim2)
  {
    Integer ival = 0;
    Real coarse_current_time = m_coarse_time_mng->getCurrentTime();
    Real coarse_deltat = m_coarse_time_mng->getCurrentTimeStep();
    // on veut recuperer la pression a imposer sur la zone window
    //
    ENUMERATE_CELL(icell, m_cell_interfaceW_group)
      {
        const Cell &cell = *icell;
        m_u_concentration_overlap[cell] = Val_Lim1[ival] + (Val_Lim2[ival]
            - Val_Lim1[ival]) * ((current_time - coarse_current_time)
            / coarse_deltat + 1.);

        ival++;
      }
  }

/*-----------------------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
// calcul de la pression moyenne au cours du temps dans la zone window
//  a la fin  des petits pas de temps, il faut diviser par le grand  pas de temps
//
// average_flux = Sommme_dt (dt * flux)
//
void ToyReactiveTransportCouplageDtLocalModule::computeAverageFluxOnOverlap(
    Real deltat)
  {
    // calcul des flux moyens sur la zone raffinee
    ENUMERATE_FACE(iface, m_interface_face_group)
      {
        m_average_flux[iface] += m_flux_overlap[iface] * deltat;
      }
  }

/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
// calcul de la pression moyenne au cours du temps dans la zone window
//  a la fin  des petits pas de temps, il faut diviser par le grand  pas de temps
//
// average_pressure = Sommme_dt (dt * pressure)
//
void ToyReactiveTransportCouplageDtLocalModule::computeAveragePressureOnWindow(
    Real deltat)
  {
    cout << " computeAveragePressureOnWindow " << endl;

    // calcul des pressions moyennes sur la zone raffinee
    ENUMERATE_CELL(icell, m_window_group)
      {
        const Cell &cell = *icell;
        m_average_u_concentration[cell] += (*m_u_concentration_fine)[icell]
            * deltat;
      }
  }
void ToyReactiveTransportCouplageDtLocalModule::writeoutput()
  {
/* DEBUG
    ENUMERATE_CELL(icell, m_global_cell_group)
      {
        const Cell &cell = *icell;
        cout << "MATLABUCONCENTRATION(" << m_iteration << ","
            << cell.uniqueId() << "+1" << ")=" << m_u_concentration[icell]
            << ";" << endl;
        cout << "MATLABVCONCENTRATION(" << m_iteration << ","
            << cell.uniqueId() << "+1" << ")=" << m_v_concentration[icell]
            << ";" << endl;
      }
*/
    cout << "MATLABTEMPS(" << m_iteration << ")=" << m_coarse_time_mng->getCurrentTime() << ";" << endl;
    m_iteration++;
  }
using namespace Arcane;
ARCANE_REGISTER_MODULE_TOYREACTIVETRANSPORTCOUPLAGEDTLOCAL
    ( ToyReactiveTransportCouplageDtLocalModule);


// Copyright (c) 2016 Richard Glennie, University of St Andrews
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files, to deal in the
// software without restriction, including without limitation the right to use,
// copy, modify, publish, distribute, sublicense, and/or sell copies of the software,
// and to permit persons to whom the software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE THE USE OR OTHER DEALINGS
// IN THE SOFTWARE
//
// ============================================================================
//
// Project: Distance Sampling with movement simulation engine
// File contains cpp code to be included using Rcpp
//
// ============================================================================
//
// [[Rcpp::depends(RcppArmadillo)]]
#include <iostream>
#include <fstream>
#include <cmath>
#include <armadillo>
#include <errno.h>
#include <RcppArmadillo.h>
#include <vector>
#include <string>
#include <sstream>

using arma::mat;
using arma::vec;
using arma::rowvec;
using arma::field;
using arma::cx_mat;
using arma::cx_cube;
using arma::randu;
using arma::randn;

using std::cout;
using std::string;

// Class Declarations
// ============================================================================
class SurveyRegion;
class LineTransect;
class Animal;
class Observer;

// The survey region is the area the population lives in and contains
// the total population.
// An region is specified by its width and length (it is assumed to be rectangular).
class SurveyRegion {
  public:
    // constructors
    SurveyRegion();
    SurveyRegion(double width, double length) : length_(length), width_(width) {}
    SurveyRegion(vec size) : length_(size(1)), width_(size(0)){}

    // accessors
    const double width() const {return width_;}
    const double length() const {return length_;}

  private:
    double length_;
    double width_;
};

// Line transect is tranversed by the observer.
// It is specified by its length and its width (full width, not half-width).
// For the simulation, it is assumed the transect is placed (w / 2, 0) where
// w is the width of the survey region. As animals are on average uniformly
// placed with respect to the line, where the line does not matter.
// Adding a starting coordinate for the line transect is an obvious extension.
class LineTransect {
  public:
    // Default constructor is empty (does nothing)
    LineTransect();
    LineTransect(double width, double length) : length_(length), width_(width) {}
    LineTransect(vec size) : length_(size(1)), width_(size(0)) {}

    // Accessor functions
    const double length() const {return length_;}
    const double width() const {return width_;}

  private:
    double length_;
    double width_;
};

// Animals are assumed to move according to behaviour:
//   0 = no movement
//   1 = random direction, constant speed
//   2 = OU process with home center
// Animals also have a accumulated hazard and a threshold hazard. An animal
// accumulates hazard by being surveyed by an observer. When the accumulated
// hazard exceeds the threshold, the animal is seen by an observer.
// The accumulation is setup for only one observer.
// Animals can only accumulate hazard when they are available for detection.
// It is assumed that animals are reflected off the boundary of the survey region.
class Animal {
  public:
    // constructors
    Animal(){}
    // place animal at (x,y), not moving
    Animal(double x, double y) : behaviour_(0), available_(true) {
      x_ = x;
      y_ = y;
      threshold_hazard_ = -log(arma::as_scalar(randu(1)));
      accumulated_hazard_ = 0;
    }
    // place animal at (x,y) moving in direction at velocity v
    Animal(double x, double y, double v, double direction) : x_(x), y_(y), v_(v),
    direction_(direction), behaviour_(1), available_(true) {
      threshold_hazard_ = -log(arma::as_scalar(randu(1)));
      accumulated_hazard_ = 0;
    }
    // place animal at home center (x_home, y_home) and
    // move as OU process with parameters theta = (tau, c)
    // See Gillespie et al. (1996) for parameterisation.
    Animal(double x_home, double y_home, vec theta) : x_(x_home), y_(y_home), behaviour_(2), available_(true), x_home_(x_home), y_home_(y_home), movement_parameter_(theta) {
      threshold_hazard_ = -log(arma::as_scalar(randu(1)));
      accumulated_hazard_ = 0;
    }
    // accessor functions
    const double x() const {return x_;}
    const double y() const {return y_;}
    const double v() const {return v_;}
    const double direction() const {return direction_;}
    const int behaviour() const {return behaviour_;}
    const double x_home() const {return x_home_;}
    const double y_home() const {return y_home_;}
    const bool available() const {return available_;}
    const double hazard() const {return accumulated_hazard_;}
    const double threshold_hazard() const {return threshold_hazard_;}
    const vec movement_parameter() const {return movement_parameter_;}

    // mutator functions
    void set_x(double x){x_ = x;}
    void set_y(double y){y_ = y;}
    void set_v(double v){v_ = v;}
    void set_direction(double direction){direction_ = direction;}
    void set_behaviour(int behaviour){behaviour_ = behaviour;}
    void set_home(double x_home, double y_home){x_home_ = x_home; y_home_ = y_home;}
    void set_available(bool available){available_ = available;}
    void set_movement_parameter(vec movement_parameter){movement_parameter_ = movement_parameter;}
    void reset_threshold() {threshold_hazard_ = -log(arma::as_scalar(randu(1)));}
    void reset_hazard(){accumulated_hazard_ = 0;}
    void accumulate_hazard(const Observer observer, double dt);
    void move(const SurveyRegion& region, double dt);
  private:
    double x_;
    double y_;
    double v_;
    double direction_;
    int behaviour_;
    double x_home_;
    double y_home_;
    bool available_;
    double accumulated_hazard_;
    double threshold_hazard_;
    vec movement_parameter_;
};

// Observer moves along transect, detecting animals.
// Assumes observer starts at beginning of transect
// Detection functions is 2D hazard, so detection_parameter = (c, b)
// See advanced distance sampling book p350 by Buckland et al.
class Observer {
  public:
    Observer();
    Observer(double v, vec detection_parameter) : x_(0), y_(0), v_(v), see_behind_(false), detection_parameter_(detection_parameter){}
    Observer(double v, vec detection_parameter, bool see_behind) : x_(0), y_(0), v_(v), see_behind_(see_behind), detection_parameter_(detection_parameter){}

    // accessor functions
    const double x() const {return x_;}
    const double y() const {return y_;}
    const double speed() const {return v_;}
    const bool see_behind() const {return see_behind_;}
    const vec detection_parameter() const {return detection_parameter_;}

    // mutator functions
    void set_x(double x){x_ = x;}
    void set_y(double y){y_ = y;}
    void set_speed(double v){v_ = v;}
    void set_detection_parameter(vec theta) {detection_parameter_ = theta;}

    void move(double dt);
    bool detect(Animal& animal, double dt);

  private:
    double x_;
    double y_;
    double v_;
    bool see_behind_;
    vec detection_parameter_;
};

// SurveyDat is a data structure used to package information
// together for brevity.
struct SurveyDat {
  int population_size;
  vec region_size;
  vec line_size;
  int behaviour;
  vec movement_parameter;
  double observer_speed;
  bool see_behind;
  vec detection_parameter;
  int num_transects;
};

// Class Member function definitions
// ============================================================================

// Accumulates hazard of detection based on relative distance between
// observer and animal.
// Inputs:
//   observer: an Observer object, the observer who is surveying
//   dt: time step for simulation
void Animal::accumulate_hazard(const Observer observer, double dt) {
  double rel_x = x() - observer.x();
  double rel_y = y() - observer.y();
  if (available() & (observer.see_behind() || (!observer.see_behind() && rel_y > 0))) {
    double r0 = rel_x * rel_x + rel_y * rel_y;
    double y1 = rel_y - observer.speed() * dt;
    // if y1 < 0 but y > 0, animal lies in front of observer now
    // but observeer will pass the animal abeam in this time step, so
    // only survey up until y1 = 0
    if (y1 < 0) y1 = 0;
    double r1 = rel_x * rel_x + y1 * y1;
    // if animal behind observer for whole time-step, then switch r0 and r1 as
    // you want to integrate from higher r to lower r
    if (rel_y < 0) {
      double temp = r0;
      r0 = r1;
      r1 = temp;
    }
    // animals at same position as observer causes overflow in hazard,
    // so handle seperately.
    if (r1 < 1e-10) accumulated_hazard_ = arma::datum::inf;
    else {
      double alpha = observer.detection_parameter()(0);
      double beta = observer.detection_parameter()(1);
      double abeta = 0.5 * (beta - 1);
      // hazard is integral over time, if x is small or
      // abeta is small, then integral differs from usual case
      if (fabs(rel_x) < 1e-10) {
        if (fabs(abeta) < 1e-10) accumulated_hazard_ += alpha * 0.5 * (log(r1) - log(r0));
        else accumulated_hazard_ += beta / (alpha - 1) * (1 / pow(r1, abeta) - 1 / pow(r0,abeta));
      }
      else {
        double hazard = R::pbeta(rel_x * rel_x / r1, abeta, 0.5, 1, 0) - R::pbeta(rel_x * rel_x / r0, abeta, 0.5, 1, 0);
        hazard *= R::beta(abeta, 0.5) * alpha / (2 * pow(fabs(rel_x), beta - 1));
        accumulated_hazard_ += hazard;
      }
    }
  }
}

// Move animal over a time interval of length dt
// Inputs:
//   region: a SurveyRegion object
//   dt: time step
void Animal::move(const SurveyRegion& region, double dt) {
  double new_x = x();
  double new_y = y();
  double new_direction = direction();
  switch (behaviour()) {
    // straight-line, random-direction, constant-speed movement
    case 1:
      new_x = x() + v() * cos(direction()) * dt;
      new_y = y() + v() * sin(direction()) * dt;
      break;
      // OU home-range process
    case 2:
      double var = movement_parameter()(1) * movement_parameter()(0) / 2;
      var *= 1 - exp(-2 * dt / movement_parameter()(0));
      var = sqrt(var);
      new_x = x_home() + (x() - x_home()) * exp(-dt / movement_parameter()(0));
      new_x += var * as_scalar(randn(1));
      new_y = y_home() + (y() - y_home()) * exp(-dt / movement_parameter()(0));
      new_y += var * as_scalar(randn(1));
      break;
  }
  // check if animal has moved outside region, if so, reflect off boundary
  // and reverse direction
  if (new_x > region.width()) {
    new_x = 2 * region.width() - new_x;
    if (behaviour() == 1) new_direction = fmod(new_direction + M_PI, 2 * M_PI);
  }
  if (new_y > region.length()) {
    new_y = 2 * region.length() - new_y;
    if (behaviour() == 1) new_direction = fmod(new_direction + M_PI, 2 * M_PI);
  }
  if (new_x < 0) {
    new_x *= -1;
    if (behaviour() == 1) new_direction = fmod(new_direction + M_PI, 2 * M_PI);
  }
  if (new_y < 0) {
    new_y *= -1;
    if (behaviour() == 1) new_direction = fmod(new_direction + M_PI, 2 * M_PI);
  }
  set_x(new_x);
  set_y(new_y);
  if (behaviour() == 1) set_direction(new_direction);
}

void Observer::move(double dt) {
  set_y(y() + speed() * dt);
}

bool Observer::detect(Animal& animal, double dt) {
  // if hazard > threshold already, the animal has already been detected,
  // you don't detect it again.
  if (animal.hazard() > animal.threshold_hazard()) return false;
  animal.accumulate_hazard(*this,  dt);
  // if hazard > threshold now, animal is seen at this time
  if (animal.hazard() > animal.threshold_hazard()) return true;
  return false;
}

// Standalone Functions
// ============================================================================

// Respawn animals for a new transect
// Inputs:
//   population: pointer to array of animals
//   num_animals: number of animals in population
//   region: SurveyRegion object
void SpawnAnimals(std::vector<Animal>& population, const int& num_animals, const SurveyRegion& region) {
  // animals are assumed to be randomly distributed about the region
  // (and hence line)
  vec randx = randu(num_animals) * region.width();
  vec randy = randu(num_animals) * region.length();
  for (int animal = 0; animal < num_animals; ++animal) {
    population[animal].set_x(randx(animal));
    population[animal].set_y(randy(animal));
    population[animal].reset_hazard();
    population[animal].reset_threshold();
    population[animal].set_available(true);
    switch (population[animal].behaviour()) {
      case 1:
        population[animal].set_direction(as_scalar(randu(1)) * M_PI * 2);
        population[animal].set_v(population[animal].movement_parameter()(0));
        break;
      case 2:
        // home ranges are assumed to be randomly distributed
        population[animal].set_home(randx(animal), randy(animal));
        break;
    }
  }
}

// Respawn observer for new transect. Observer is placed at (width/2, 0)
//  coordinates.
// Inputs:
//   observer: Observer object to be respawned
//   region: SurveyRegion object
void SpawnObserver(Observer& observer, const SurveyRegion& region) {
  observer.set_x(region.width() / 2);
  observer.set_y(0);
}

// Simulate distance sampling survey with moving animals
// Inputs:
//   simdat: SimulationDat object
//   dt: time step
// Output:
//   file "data.txt" is written to mcds directory with tabulated fields
//   "transect", "effort", "perpendicular distance" for each recorded detection.
void SimulateSurvey(SurveyDat survey_dat, double dt) {
  // create objects involved
  SurveyRegion region(survey_dat.region_size);
  LineTransect line(survey_dat.line_size);
  int num_animals = survey_dat.population_size;
  std::vector<Animal> population(num_animals);
  Observer observer(survey_dat.observer_speed, survey_dat.detection_parameter, survey_dat.see_behind);
  for (int animal = 0; animal < num_animals; ++animal) {
    population[animal].set_behaviour(survey_dat.behaviour);
    population[animal].set_movement_parameter(survey_dat.movement_parameter);
  }

  // open file to write data to
  std::ofstream dat("../mcds/data.txt");

  // perform survey
  double num_timesteps = floor(line.length() / (dt * observer.speed()));
  vec randx(num_animals);
  vec randy(num_animals);
  int num_detected;
  for (int transect = 0; transect < survey_dat.num_transects; ++transect) {
    SpawnAnimals(population, num_animals, region);
    SpawnObserver(observer, region);
    num_detected = 0;
    for (int t = 0; t < num_timesteps; ++t) {
      for (int animal = 0; animal < num_animals; ++animal) {
        // animal detection is recorded if animal is seen INSIDE transect only
        // (but notice this does not ignore animals seen outside transect, it merely doesn't record them)
        if (observer.detect(population[animal], dt) && fabs(population[animal].x() - observer.x()) <= line.width()/2.0 && population[animal].y() < line.length()) {
          dat << transect << "\t"  << line.length() << "\t" << fabs(population[animal].x() - observer.x()) << "\n";
          ++num_detected;
        }
        population[animal].move(region, dt);
      }
      observer.move(dt);
    }
    // if no animals seen on a transect, write a line to indicate this for MCDS
    if (num_detected == 0) dat << transect << "\t" << line.length() << "\n";
  }
}

// Fit distance sampling model
// Assumes there is a file named "data.csv" with fields
// transect, perpendicular distance.
// This function runs the external MCDS engine; the engine must
// be in the directory "./mcds", and the command file for the
// MCDS engine must be correctly filled out.
// Current runs MCDS using WINE!
// Outputs:
//   writes a file named "output.txt" with the model results
void FitModel() {
  int r = system("cd ../mcds; wine MCDS 0, command.txt");
}

// Save the data and fitted model of simulated survey to
// a subdirectory.
// The function renames "data.txt" to "../results/data_<num>.csv"
// and output from mcds "output.txt" to "../results/output_<num>.txt"
// where "<num>" is the simulation number.
// Note this function assumes the directiory "results" exists.
// Input:
//   num: the number to appended to each file name
//   save_log: if TRUE, the MCDS log files are saved also (to check for convergence problems)
void Clean(int num, bool save_log = false) {
  std::stringstream cmd;
  cmd << "mv ../mcds/data.txt ../results/data_" << num << ".txt";
  int r = system(cmd.str().c_str());
  cmd.str("");
  cmd << "mv ../mcds/output.txt ../results/output_" << num << ".txt";
  r = system(cmd.str().c_str());
  if (save_log) {
    cmd.str("");
    cmd << "mv ./mcds/log.txt ../results/log_" << num << ".txt";
    r = system(cmd.str().c_str());
  }
}

// Perform Simulation study
// Inputs:
//   num_simulations: number of simulated surveys to do
//   parameter: vector of detection parameters (c,d) and
//     if behaviour=1 (speed, angle sd)
//     if behaviour=2 (tau, sigma) of OU process
//   simulation_dat: vector containing (in this order)
//     population_size: size of population to simulate
//     region_size: (width, length) of rectangular study region
//     line_size: (width, length) of a single transect
//     behaviour: 0(no movement), 1(straight-line movement), 2(home-range movement)
//     observer_speed: speed of the observer
//     num_transects: number of transects per surveyed simulation
//   dt: time step
//   seed: random seed (set in Armadillo)
// [[Rcpp::export]]
void Simulate(int num_simulations, vec parameter, vec simulation_dat, double dt = 1, int seed = -1) {
  if (seed > 0) arma::arma_rng::set_seed(seed);
  // unpack simulation data from input and then
  // package into a data structure
  int population_size = simulation_dat(0);
  vec region_size = simulation_dat.rows(1,2);
  vec line_size = simulation_dat.rows(3,4);
  int behaviour = simulation_dat(5);
  double observer_speed = simulation_dat(6);
  int num_transects = simulation_dat(7);
  vec detection_parameter = parameter.rows(0,1);
  vec movement_parameter = arma::zeros<vec>(2);
  bool see_behind = simulation_dat(8);
  switch (behaviour) {
    case 1:
      movement_parameter(0) = parameter(2);
      break;
    case 2:
      movement_parameter = parameter.rows(2,3);
      break;
  }
  SurveyDat survey_dat = {population_size, region_size, line_size, behaviour, movement_parameter, observer_speed, see_behind, detection_parameter, num_transects};

  for (int sim = 0; sim < num_simulations; ++sim) {
    cout << sim + 1 << " / " << num_simulations << "\n";
    SimulateSurvey(survey_dat, dt);
    FitModel();
    Clean(sim + 1);
  }
}