/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2015 Frederik Heber. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder 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 2 of the License, or
* (at your option) any later version.
*
* MoleCuilder 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 MoleCuilder. If not, see .
*/
/*
* ChargeSmearer.cpp
*
* Created on: Sep 2, 2015
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "CodePatterns/MemDebug.hpp"
#include "ChargeSmearer.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Singleton_impl.hpp"
#include
#include
#include
ChargeSmearer::ChargeSmearer() :
nfc(0),
meshwidth(0.),
vals(NULL),
int_val(0.)
{}
ChargeSmearer::~ChargeSmearer()
{
delete[] vals;
}
void ChargeSmearer::initializeSplineArray(
const VMG::Particle::BSpline &_spl,
const unsigned int _nfc,
const double _meshwidth)
{
// check whether it is changed, if not, do nothing
if ((nfc == _nfc) && (meshwidth == _meshwidth))
return;
nfc = _nfc;
meshwidth = _meshwidth;
// reallocate memory for spline values
delete[] vals;
vals = new double[VMG::Helper::intpow(2*nfc+1,3)];
// recalculate spline values
unsigned int c=0;
int_val = 0.;
std::pair bounds[3];
for (int dim=0;dim<3;++dim) {
bounds[dim].first = -1*nfc;
bounds[dim].second = nfc;
}
for (int i=bounds[0].first; i<=bounds[0].second; ++i)
for (int j=bounds[1].first; j<=bounds[1].second; ++j)
for (int k=bounds[2].first; k<=bounds[2].second; ++k) {
const double dir_x = (double)i*meshwidth;
const double dir_y = (double)j*meshwidth;
const double dir_z = (double)k*meshwidth;
// Compute distance from grid point to particle
const double distance = std::sqrt(dir_x*dir_x+dir_y*dir_y+dir_z*dir_z);
const double temp_val = _spl.EvaluateSpline(distance);
vals[c++] = temp_val;
int_val += temp_val;
LOG(4, "DEBUG: Spline evaluated at " << distance << " is " << temp_val);
}
/// then integrate
int_val = 1.0 / int_val;
LOG(3, "DEBUG: Spline for smearing electronic charge distribution has norm factor of " << int_val);
}
void ChargeSmearer::visitBSplineDomain(
const VMG::GridIterator &_iter,
const visitor_t &_visitor
) const
{
unsigned int c = 0;
VMG::Index index;
const VMG::Index end = _iter.GetIndex() + (int)nfc;
for (index[0] = _iter.GetIndex()[0] - (int)nfc; index[0]<=end[0]; ++index[0])
for (index[1] = _iter.GetIndex()[1] - (int)nfc; index[1]<=end[1]; ++index[1])
for (index[2] = _iter.GetIndex()[2] - (int)nfc; index[2]<=end[2]; ++index[2]) {
if (index.IsInBounds(_iter.GetBegin(), _iter.GetEnd()))
_visitor(index, vals[c++]);
else
++c;
}
assert( c == (unsigned int)VMG::Helper::intpow(2*nfc+1,3) );
}
static void DomainIntegrator(
const VMG::Index & _index,
const double _val,
double &_int_val)
{
_int_val += _val;
}
static void SplineSetter(
const VMG::Index & _index,
const double _val,
const double _factor,
VMG::Grid& _grid)
{
_grid(_index) += _val * _factor;
}
void ChargeSmearer::operator()(
VMG::Grid& _grid,
const VMG::GridIterator &_iter,
const double _charge) const
{
// check whether we go over whole bspline support
bool renormalize = false;
for (int dim=0;dim<3;++dim) {
renormalize |= (_iter.GetBegin()[dim] > _iter.GetIndex()[dim] - (int)nfc);
renormalize |= (_iter.GetEnd()[dim] < _iter.GetIndex()[dim] + (int)nfc);
}
/// renormalize bspline if necessary (bounds don't fit in window)
double temp_int_val = 0.;
if (renormalize) {
const visitor_t integrator = boost::bind(DomainIntegrator, _1, _2, boost::ref(temp_int_val));
visitBSplineDomain(_iter, integrator);
temp_int_val = 1.0 / temp_int_val;
} else {
temp_int_val = int_val;
}
/// then transfer to grid in bounded window
{
const visitor_t setter = boost::bind(SplineSetter, _1, _2, temp_int_val * _charge, boost::ref(_grid));
visitBSplineDomain(_iter, setter);
}
}
CONSTRUCT_SINGLETON(ChargeSmearer)