/*
 *    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   comm.hpp
 * @author Julian Iseringhausen 
 * @date   Wed Jun 16 13:21:06 2010
 *
 * @brief  Base class for communication.
 *
 */
#ifdef HAVE_CONFIG_H
#include 
#endif
#ifdef HAVE_BOOST_FILESYSTEM_PATH_HPP
#include 
namespace fs = boost::filesystem;
#endif
#include "base/discretization.hpp"
#include "base/helper.hpp"
#include "base/stencil.hpp"
#include "comm/comm.hpp"
#include "comm/domain_decomposition.hpp"
#include "grid/grid.hpp"
#include "mg.hpp"
using namespace VMG;
vmg_float Comm::ComputeResidualNorm(Multigrid& sol_mg, Multigrid& rhs_mg)
{
  Stencil::iterator stencil_iter;
  vmg_float norm = 0.0;
  const vmg_float prefactor = MG::GetDiscretization()->OperatorPrefactor(sol_mg());
  const Stencil& A = MG::GetDiscretization()->GetStencil();
  this->CommToGhosts(sol_mg());
  if (sol_mg().Global().BoundaryType() == LocallyRefined)
    MG::GetDiscretization()->SetInnerBoundary(sol_mg(), rhs_mg(), sol_mg(sol_mg.Level()-1));
  const Grid& sol = sol_mg();
  const Grid& rhs = rhs_mg();
#ifdef DEBUG_MATRIX_CHECKS
  sol.IsCompatible(rhs);
  sol.IsConsistent();
  rhs.IsConsistent();
#endif
  for (int i=rhs.Local().Begin().X(); iVal() * sol.GetVal(i + stencil_iter->Disp().X(),
							      j + stencil_iter->Disp().Y(),
							      k + stencil_iter->Disp().Z());
	norm += val*val;
      }
  norm = GlobalSum(norm);
  norm = std::sqrt(sol.Extent().MeshWidth().Product() * norm);
  return norm;
}
Grid& Comm::GetParticleGrid()
{
  if (particle_grid != NULL)
    return *particle_grid;
  const Grid& grid = (*MG::GetRhs())(MG::GetRhs()->MaxLevel());
  LocalIndices local = grid.Local();
  GlobalIndices global = grid.Global();
  SpatialExtent extent = grid.Extent();
  const int& near_field_cells = MG::GetFactory().GetObjectStorageVal("PARTICLE_NEAR_FIELD_CELLS");
  for (int i=0; i<3; ++i) {
    if (BoundaryConditions()[i] == Open || BoundaryConditions()[i] == Dirichlet) {
      if (global.LocalBegin()[i] == global.GlobalBegin()[i])
	global.LocalBegin()[i] += 1;
      if (global.LocalEnd()[i] == global.GlobalEnd()[i])
	global.LocalEnd()[i] -= 1;
      global.LocalSize()[i] = global.LocalEnd()[i] - global.LocalBegin()[i];
      global.GlobalBegin()[i] += 1;
      global.GlobalEnd()[i] -= 1;
      global.GlobalSize()[i] = global.GlobalEnd()[i] - global.GlobalBegin()[i];
      extent.Begin()[i] += extent.MeshWidth()[i];
      extent.End()[i] -= extent.MeshWidth()[i];
      extent.Size()[i] = extent.End()[i] - extent.Begin()[i];
    }
  }
  local.BoundaryBegin1() = local.BoundaryEnd1() = local.BoundarySize1() = 0;
  local.BoundaryBegin2() = local.BoundaryEnd2() = local.BoundarySize2() = 0;
  local.End() -= local.Begin();
  local.Begin() = 0;
  // Set grid size of intermediate temporary grid
  for (int i=0; i<3; ++i) {
    if (local.HaloSize1()[i] > 0) {
      local.HaloBegin1()[i] = 0;
      local.HaloEnd1()[i] = near_field_cells;
      local.HaloSize1()[i] = near_field_cells;
      local.Begin()[i] = near_field_cells;
      local.End()[i] = local.Begin()[i] + local.Size()[i];
    }
    if (local.HaloSize2()[i] > 0) {
      local.HaloBegin2()[i] = local.End()[i];
      local.HaloEnd2()[i] = local.HaloBegin2()[i] + near_field_cells;
      local.HaloSize2()[i] = near_field_cells;
    }
  }
  local.SizeTotal() = local.Size() + local.HaloSize1() + local.HaloSize2();
  particle_grid = new Grid(global, local, extent);
  return *particle_grid;
}
Comm::~Comm()
{
  delete dd;
  delete particle_grid;
}
const std::string& Comm::OutputPath()
{
  if (!output_directory_is_created) {
    output_path_str = CreateOutputDirectory();
    output_directory_is_created = true;
  }
  return output_path_str;
}