/*
 *    vmg - a versatile multigrid solver
 *    Copyright (C) 2012 Institute for Numerical Simulation, University of Bonn
 *
 *  vmg is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  vmg is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see .
 */
/**
 * @file   mg.cpp
 * @author Julian Iseringhausen 
 * @date   Sat Jun 12 20:36:24 2010
 *
 * @brief  A multigrid solver
 *
 * This file contains the implementation of the main class for
 * a multigrid solver.
 *
 */
#ifdef HAVE_CONFIG_H
#include 
#endif
#ifdef OUTPUT_TIMING
#ifdef HAVE_MPI
#include 
#ifdef HAVE_MARMOT
#include 
#include 
#endif
#endif
#endif
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "base/command_list.hpp"
#include "base/discretization.hpp"
#include "base/factory.hpp"
#include "base/interface.hpp"
#include "base/timer.hpp"
#include "comm/comm.hpp"
#include "discretization/boundary_value_setter_open.hpp"
#include "cycles/cycle.hpp"
#include "grid/grid.hpp"
#include "grid/tempgrid.hpp"
#include "level/level_operator.hpp"
#include "smoother/smoother.hpp"
#include "solver/solver.hpp"
#include "mg.hpp"
using namespace VMG;
#define REGISTER_COMMAND(a) extern void Initialize##a();Initialize##a();
VMG::CommandFactory MG::command_factory;
static void VMGRegisterBuiltinCommands()
{
  REGISTER_COMMAND(VMGCommandCheckConsistency);
  REGISTER_COMMAND(VMGCommandCheckIterationCounter);
  REGISTER_COMMAND(VMGCommandCheckRelativeResidual);
  REGISTER_COMMAND(VMGCommandCheckResidual);
  REGISTER_COMMAND(VMGCommandClearCoarseLevels);
  REGISTER_COMMAND(VMGCommandClearGrid);
  REGISTER_COMMAND(VMGCommandComputeResidualNorm);
  REGISTER_COMMAND(VMGCommandCopyBoundary);
  REGISTER_COMMAND(VMGCommandExecuteCycle);
  REGISTER_COMMAND(VMGCommandExecuteCycleLoop);
  REGISTER_COMMAND(VMGCommandExecuteFullCycle);
  REGISTER_COMMAND(VMGCommandExecuteFullCycleLoop);
  REGISTER_COMMAND(VMGCommandExportSolution);
  REGISTER_COMMAND(VMGCommandForceDiscreteCompatibility);
  REGISTER_COMMAND(VMGCommandImportRightHandSide);
  REGISTER_COMMAND(VMGCommandInterpolateFMG);
  REGISTER_COMMAND(VMGCommandInitializeIterationCounter);
  REGISTER_COMMAND(VMGCommandInitializeResidualNorm);
  REGISTER_COMMAND(VMGCommandNOP);
  REGISTER_COMMAND(VMGCommandPrintAllSettings);
  REGISTER_COMMAND(VMGCommandPrintDefect);
  REGISTER_COMMAND(VMGCommandPrintGridStructure);
  REGISTER_COMMAND(VMGCommandPrintGrid);
  REGISTER_COMMAND(VMGCommandPrintResidualNorm);
  REGISTER_COMMAND(VMGCommandPrintRunningTime);
  REGISTER_COMMAND(VMGCommandProlongate);
  REGISTER_COMMAND(VMGCommandRestrict);
  REGISTER_COMMAND(VMGCommandSetAverageToZero);
  REGISTER_COMMAND(VMGCommandSetCoarserDirichletValues);
  REGISTER_COMMAND(VMGCommandSetLevel);
  REGISTER_COMMAND(VMGCommandSmooth);
  REGISTER_COMMAND(VMGCommandSolve);
}
MG::MG()
{
  state = 0;
  VMGRegisterBuiltinCommands();
}
MG::~MG()
{
  MG::Destroy();
}
// Brings Multigrid back to starting state.
void MG::Destroy()
{
  MG::Instance()->factories.clear();
  MG::Instance()->state = 0;
  Timer::Clear();
}
/*
 * Post init communication class
 */
void MG::PostInit()
{
  if (GetFactory().TestObject("COMM") && GetFactory().TestObject("INTERFACE")) {
    Comm& comm = *GetComm();
    Interface& interface = *GetInterface();
    comm.ComputeDomainDecomposition(interface);
    Multigrid* sol = new Multigrid(comm, interface);
    sol->Register("SOL");
    Multigrid* rhs = new Multigrid(comm, interface);
    rhs->Register("RHS");
    TempGrid* temp = new TempGrid();
    temp->Register("TEMPGRID");
    new ObjectStorage("GLOBAL_MAXLEVEL", sol->GlobalMaxLevel());
    new ObjectStorage("MINLEVEL", sol->MinLevel());
    new ObjectStorage("MAXLEVEL", sol->MaxLevel());
    if (GetFactory().TestObject("CYCLE"))
      GetCycle()->Generate();
    if (comm.BoundaryConditions()[0] == Open &&
        comm.BoundaryConditions()[1] == Open &&
        comm.BoundaryConditions()[2] == Open) {
      new BoundaryValueSetterOpen();
    }
    comm.PostInit(*GetSol(), *GetRhs());
  }
}
/**
 * Solves a given system with a multigrid method
 *
 */
void MG::Solve()
{
#ifdef OUTPUT_TIMING
  GetComm()->Barrier();
  Timer::Start("CompleteRunningTime");
#endif
  CommandList* cl_init = MG::GetFactory().Get("COMMANDLIST_INIT")->Cast();
  CommandList* cl_loop = MG::GetFactory().Get("COMMANDLIST_LOOP")->Cast();
  CommandList* cl_finalize = MG::GetFactory().Get("COMMANDLIST_FINALIZE")->Cast();
  cl_init->ExecuteList();
  while (cl_loop->ExecuteList() == Continue);
  cl_finalize->ExecuteList();
#ifdef OUTPUT_TIMING
  GetComm()->Barrier();
  Timer::Stop("CompleteRunningTime");
#ifdef HAVE_MPI
  Timer::PrintGlobal();
#else
  Timer::Print();
#endif
#endif
}
void MG::SetState(const int& state_)
{
  MG::Instance()->state = state_;
}
VMG::Factory& MG::GetFactory()
{
  std::map::iterator iter = MG::Instance()->factories.find(MG::Instance()->state);
  if (iter == MG::Instance()->factories.end())
    iter = MG::Instance()->factories.insert(std::make_pair(MG::Instance()->state, Factory())).first;
  assert(iter != MG::Instance()->factories.end());
  return iter->second;
}
VMG::CommandFactory& MG::GetCommands()
{
  return MG::command_factory;
}
Comm* MG::GetComm()
{
  return MG::GetFactory().Get("COMM")->Cast();
}
Cycle* MG::GetCycle()
{
  return MG::GetFactory().Get("CYCLE")->Cast();
}
Discretization* MG::GetDiscretization()
{
  return MG::GetFactory().Get("DISCRETIZATION")->Cast();
}
LevelOperator* MG::GetLevelOperator()
{
  return MG::GetFactory().Get("LEVEL_OPERATOR")->Cast();
}
Multigrid* MG::GetRhs()
{
  return MG::GetFactory().Get("RHS")->Cast();
}
Multigrid* MG::GetSol()
{
  return MG::GetFactory().Get("SOL")->Cast();
}
VMG::Grid& MG::GetRhsMaxLevel()
{
  return (*MG::GetRhs())(MG::GetRhs()->MaxLevel());
}
VMG::Grid& MG::GetSolMaxLevel()
{
  return (*MG::GetSol())(MG::GetSol()->MaxLevel());
}
Smoother* MG::GetSmoother()
{
  return MG::GetFactory().Get("SMOOTHER")->Cast();
}
Solver* MG::GetSolver()
{
  return MG::GetFactory().Get("SOLVER")->Cast();
}
TempGrid* MG::GetTempGrid()
{
  return MG::GetFactory().Get("TEMPGRID")->Cast();
}
Interface* MG::GetInterface()
{
  return MG::GetFactory().Get("INTERFACE")->Cast();
}
BoundaryValueSetter* MG::GetBoundaryValueSetter()
{
  return MG::GetFactory().Get("BOUNDARY_VALUE_SETTER")->Cast();
}
bool MG::IsInitialized()
{
  const Factory& f = GetFactory();
  bool init = true;
  init &= f.TestObject("COMM");
  init &= f.TestObject("LEVEL_OPERATOR");
  init &= f.TestObject("RHS");
  init &= f.TestObject("SOL");
  init &= f.TestObject("SOLVER");
  init &= f.TestObject("SMOOTHER");
  init &= f.TestObject("DISCRETIZATION");
  init &= f.TestObject("MAX_ITERATION");
  init &= f.TestObject("PRECISION");
  init &= f.TestObject("PRESMOOTHSTEPS");
  init &= f.TestObject("POSTSMOOTHSTEPS");
  init &= f.TestObject("COMMANDLIST_INIT");
  init &= f.TestObject("COMMANDLIST_LOOP");
  init &= f.TestObject("COMMANDLIST_FINALIZE");
  init &= f.TestObject("MINLEVEL");
  init &= f.TestObject("MAXLEVEL");
  init &= f.TestObject("GLOBAL_MAXLEVEL");
  init &= f.TestObject("INTERFACE");
  return init;
}