/* edcalc.cpp: Electron density calculation implementation */
//C Copyright (C) 2000-2006 Kevin Cowtan and University of York
//L
//L  This library is free software and is distributed under the terms
//L  and conditions of version 2.1 of the GNU Lesser General Public
//L  Licence (LGPL) with the following additional clause:
//L
//L     `You may also combine or link a "work that uses the Library" to
//L     produce a work containing portions of the Library, and distribute
//L     that work under terms of your choice, provided that you give
//L     prominent notice with each copy of the work that the specified
//L     version of the Library is used in it, and that you include or
//L     provide public access to the complete corresponding
//L     machine-readable source code for the Library including whatever
//L     changes were used in the work. (i.e. If you make changes to the
//L     Library you must distribute those, but you do not need to
//L     distribute source or object code to those portions of the work
//L     not covered by this licence.)'
//L
//L  Note that this clause grants an additional right and does not impose
//L  any additional restriction, and so does not affect compatibility
//L  with the GNU General Public Licence (GPL). If you wish to negotiate
//L  other terms, please contact the maintainer.
//L
//L  You can redistribute it and/or modify the library under the terms of
//L  the GNU Lesser General Public License as published by the Free Software
//L  Foundation; either version 2.1 of the License, or (at your option) any
//L  later version.
//L
//L  This library is distributed in the hope that it will be useful, but
//L  WITHOUT ANY WARRANTY; without even the implied warranty of
//L  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//L  Lesser General Public License for more details.
//L
//L  You should have received a copy of the CCP4 licence and/or GNU
//L  Lesser General Public License along with this library; if not, write
//L  to the CCP4 Secretary, Daresbury Laboratory, Warrington WA4 4AD, UK.
//L  The GNU Lesser General Public can also be obtained by writing to the
//L  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//L  MA 02111-1307 USA


#include "edcalc.h"

#include "../core/atomsf.h"


namespace clipper {


template<class T> bool EDcalc_mask<T>::operator() ( Xmap<T>& xmap, const Atom_list& atoms ) const
{
  xmap = 0.0;
  const Cell& cell          = xmap.cell();
  const Grid_sampling& grid = xmap.grid_sampling();

  Coord_orth xyz;
  Coord_grid g0, g1;
  Grid_range gd( cell, grid, radius_ );
  typename Xmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    xyz = atoms[i].coord_orth();
    g0 = xmap.coord_map( xyz ).coord_grid() + gd.min();
    g1 = xmap.coord_map( xyz ).coord_grid() + gd.max();
    i0 = typename Xmap<T>::Map_reference_coord( xmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  if ( ( xyz - iw.coord_orth() ).lengthsq() < radius_*radius_ )
	    xmap[iw] = 1.0;
  }

  return true;
}


template<class T> bool EDcalc_mask<T>::operator() ( NXmap<T>& nxmap, const Atom_list& atoms ) const
{
  nxmap = 0.0;
  Coord_orth xyz;
  Coord_grid g0, g1;
  g0 = Coord_map( nxmap.operator_orth_grid().rot() *
		  Vec3<>(radius_,radius_,radius_) ).coord_grid();
  Grid_range gd( -g0, g0 );
  Grid_range box( Coord_grid(0,0,0), Coord_grid(nxmap.grid())-Coord_grid(1,1,1) );
  typename NXmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    xyz = atoms[i].coord_orth();
    g0 = nxmap.coord_map( xyz ).coord_grid() + gd.min();
    g1 = nxmap.coord_map( xyz ).coord_grid() + gd.max();
    i0 = typename NXmap<T>::Map_reference_coord( nxmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  if ( box.in_grid( iw.coord() ) )
	    if ( ( xyz - iw.coord_orth() ).lengthsq() < radius_*radius_ )
	      nxmap[iw] = 1.0;
  }

  return true;
}


template<class T> bool EDcalc_iso<T>::operator() ( Xmap<T>& xmap, const Atom_list& atoms ) const
{
  xmap = 0.0;
  const Cell& cell          = xmap.cell();
  const Grid_sampling& grid = xmap.grid_sampling();

  Coord_orth xyz;
  Coord_grid g0, g1;
  Grid_range gd( cell, grid, radius_ );
  typename Xmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    AtomShapeFn sf( atoms[i].coord_orth(), atoms[i].element(),
		    atoms[i].u_iso(), atoms[i].occupancy() );
    g0 = xmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.min();
    g1 = xmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.max();
    i0 = typename Xmap<T>::Map_reference_coord( xmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  xmap[iw] += sf.rho( iw.coord_orth() );
  }

  for ( typename Xmap<T>::Map_reference_index ix = xmap.first();
	!ix.last(); ix.next() )
    xmap[ix] *= xmap.multiplicity( ix.coord() );

  return true;
}


template<class T> bool EDcalc_iso<T>::operator() ( NXmap<T>& nxmap, const Atom_list& atoms ) const
{
  nxmap = 0.0;
  Coord_orth xyz;
  Coord_grid g0, g1;
  g0 = Coord_map( nxmap.operator_orth_grid().rot() *
		  Vec3<>(radius_,radius_,radius_) ).coord_grid();
  Grid_range gd( -g0, g0 );
  Grid_range box( Coord_grid(0,0,0), Coord_grid(nxmap.grid())-Coord_grid(1,1,1) );
  typename NXmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    AtomShapeFn sf( atoms[i].coord_orth(), atoms[i].element(),
		    atoms[i].u_iso(), atoms[i].occupancy() );
    g0 = nxmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.min();
    g1 = nxmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.max();
    i0 = typename NXmap<T>::Map_reference_coord( nxmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  if ( box.in_grid( iw.coord() ) )
	    nxmap[iw] += sf.rho( iw.coord_orth() );
  }

  return true;
}


template<class T> bool EDcalc_aniso<T>::operator() ( Xmap<T>& xmap, const Atom_list& atoms ) const
{
  xmap = 0.0;
  const Cell& cell          = xmap.cell();
  const Grid_sampling& grid = xmap.grid_sampling();

  Coord_orth xyz;
  Coord_grid g0, g1;
  Grid_range gd( cell, grid, radius_ );
  typename Xmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    U_aniso_orth u( atoms[i].u_aniso_orth() );
    if ( u.is_null() ) u = U_aniso_orth( atoms[i].u_iso() );
    AtomShapeFn sf( atoms[i].coord_orth(), atoms[i].element(),
		    u, atoms[i].occupancy() );
    g0 = xmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.min();
    g1 = xmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.max();
    i0 = typename Xmap<T>::Map_reference_coord( xmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  xmap[iw] += sf.rho( iw.coord_orth() );
  }

  for ( typename Xmap<T>::Map_reference_index ix = xmap.first();
	!ix.last(); ix.next() )
    xmap[ix] *= xmap.multiplicity( ix.coord() );

  return true;
}


template<class T> bool EDcalc_aniso<T>::operator() ( NXmap<T>& nxmap, const Atom_list& atoms ) const
{
  nxmap = 0.0;
  Coord_orth xyz;
  Coord_grid g0, g1;
  g0 = Coord_map( nxmap.operator_orth_grid().rot() *
		  Vec3<>(radius_,radius_,radius_) ).coord_grid();
  Grid_range gd( -g0, g0 );
  Grid_range box( Coord_grid(0,0,0), Coord_grid(nxmap.grid())-Coord_grid(1,1,1) );
  typename NXmap<T>::Map_reference_coord i0, iu, iv, iw;
  for ( int i = 0; i < atoms.size(); i++ ) if ( !atoms[i].is_null() ) {
    U_aniso_orth u( atoms[i].u_aniso_orth() );
    if ( u.is_null() ) u = U_aniso_orth( atoms[i].u_iso() );
    AtomShapeFn sf( atoms[i].coord_orth(), atoms[i].element(),
		    u, atoms[i].occupancy() );
    g0 = nxmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.min();
    g1 = nxmap.coord_map( atoms[i].coord_orth() ).coord_grid() + gd.max();
    i0 = typename NXmap<T>::Map_reference_coord( nxmap, g0 );
    for ( iu = i0; iu.coord().u() <= g1.u(); iu.next_u() )
      for ( iv = iu; iv.coord().v() <= g1.v(); iv.next_v() )
	for ( iw = iv; iw.coord().w() <= g1.w(); iw.next_w() )
	  if ( box.in_grid( iw.coord() ) )
	    nxmap[iw] += sf.rho( iw.coord_orth() );
  }

  return true;
}


// compile templates

template class EDcalc_mask<ftype32>;
template class EDcalc_mask<ftype64>;
template class EDcalc_iso<ftype32>;
template class EDcalc_iso<ftype64>;
template class EDcalc_aniso<ftype32>;
template class EDcalc_aniso<ftype64>;



} // namespace clipper
