////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2008 The Regents of the University of California
//
// This file is part of Qbox
//
// Qbox is distributed 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.
// See the file COPYING in the root directory of this distribution
// or .
//
////////////////////////////////////////////////////////////////////////////////
//
// Species.cpp
//
////////////////////////////////////////////////////////////////////////////////
#include "Species.h"
#include "spline.h"
#include "sinft.h"
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
////////////////////////////////////////////////////////////////////////////////
static double simpsn ( int n, double *t )
{
const double c0 = 17.0/48.0, c1 = 59.0/48.0,
c2 = 43.0/48.0, c3 = 49.0/48.0;
double sum = c0 * ( t[0] + t[n-1] ) +
c1 * ( t[1] + t[n-2] ) +
c2 * ( t[2] + t[n-3] ) +
c3 * ( t[3] + t[n-4] );
for ( int i = 4; i < n-4; i++ )
{
sum += t[i];
}
return sum;
}
////////////////////////////////////////////////////////////////////////////////
Species::Species(string name) : name_(name),
zval_(-1), mass_(0.0), lmax_(-1), deltar_(0.0), atomic_number_(0),
llocal_(-1), nquad_(-1), rquad_(0.0),
rcps_(0.0), uri_(""), description_("undefined"), symbol_("Undef")
{}
////////////////////////////////////////////////////////////////////////////////
bool Species::initialize(double rcpsval)
{
rcps_ = rcpsval;
// select appropriate initialization method
if ( type_ == NCPP )
return initialize_ncpp();
else if ( type_ == SLPP )
return initialize_slpp();
else
throw SpeciesInitException("potential type unknown");
}
////////////////////////////////////////////////////////////////////////////////
bool Species::initialize_ncpp()
{
assert(description_ != "undefined");
const double fpi = 4.0 * M_PI;
const int np = vps_[0].size();
if (zval_ < 0) throw SpeciesInitException("zval_ < 0");
if (rcps_ < 0.0) throw SpeciesInitException("rcps_ < 0");
if (mass_ < 0.0) throw SpeciesInitException("mass_ < 0");
if (lmax_ < 0 || lmax_ > 3) throw SpeciesInitException("lmax_<0 or lmax_>3");
if (vps_.size() < lmax_+1) throw SpeciesInitException("vps_.size < lmax_+1");
if (llocal_ < 0 || llocal_ > lmax_)
throw SpeciesInitException("llocal_ < 0 || llocal_ > lmax_");
if ( nquad_ == 0 ) // KB potential
{
for ( int l = 0; l <= lmax_; l++ )
if ( l != llocal_ && phi_[l].size() == 0 )
throw SpeciesInitException("phi_[l] undefined for non-local projector");
}
if ( nquad_ < 0 ) throw SpeciesInitException("nquad < 0");
if ( nquad_ > 0 && rquad_ <= 0.0 )
throw SpeciesInitException("semilocal with rquad_ <= 0");
// compute number of non-local projectors nlm_
nlm_ = 0;
nop_ = lmax_ + 1;
for ( int l = 0; l <= lmax_; l++ )
{
if ( l != llocal_ )
{
nlm_ += 2 * l + 1;
}
lmap_.push_back(l);
}
// compute ndft_: size of radial FFT array
// ndft_ is a power of 2 larger than ( rdftmin / deltar_ )
// minimum outer bound in (a.u.)
const double rdftmin = 40.0;
assert(deltar_ > 0.0);
ndft_ = 1;
while ( ndft_ * deltar_ < rdftmin )
ndft_ *= 2;
rps_spl_.resize(ndft_);
for ( int i = 0; i < ndft_; i++ )
rps_spl_[i] = i * deltar_;
vps_spl_.resize(lmax_+1);
vps_spl2_.resize(lmax_+1);
phi_spl_.resize(lmax_+1);
phi_spl2_.resize(lmax_+1);
for ( int l = 0; l <= lmax_; l++ )
{
vps_spl_[l].resize(ndft_);
vps_spl2_[l].resize(ndft_);
if ( phi_[l].size() > 0 )
{
phi_spl_[l].resize(ndft_);
phi_spl2_[l].resize(ndft_);
}
}
// extend rps and vps_ to full mesh (up to i==ndft_-1)
vector fint(ndft_);
wsg_.resize(lmax_+1);
gspl_.resize(ndft_);
vlocg_spl_.resize(ndft_);
vlocg_spl2_.resize(ndft_);
vnlg_spl_.resize(lmax_+1);
vnlg_spl2_.resize(lmax_+1);
vector vlocr(ndft_);
vector > vnlr(lmax_+1);
for ( int l = 0; l <= lmax_; l++ )
{
vnlr[l].resize(ndft_);
vnlg_spl_[l].resize(ndft_+1);
vnlg_spl2_[l].resize(ndft_+1);
}
// Extend vps_[l][i] up to ndft_ using -zv/r
for ( int l = 0; l <= lmax_; l++ )
{
for ( int i = 0; i < np; i++ )
vps_spl_[l][i] = vps_[l][i];
for ( int i = np; i < ndft_; i++ )
vps_spl_[l][i] = - zval_ / rps_spl_[i];
if ( phi_[l].size() > 0 )
{
for ( int i = 0; i < np; i++ )
phi_spl_[l][i] = phi_[l][i];
for ( int i = np; i < ndft_; i++ )
phi_spl_[l][i] = 0.0;
}
}
// compute spline coefficients of vps_spl_ and phi_spl_
for ( int l = 0; l <= lmax_; l++ )
{
const double dvdr = zval_ / (rps_spl_[ndft_-1]*rps_spl_[ndft_-1]);
spline(ndft_,&rps_spl_[0],&vps_spl_[l][0],0.0,dvdr,0,0,&vps_spl2_[l][0]);
}
for ( int l = 0; l <= lmax_; l++ )
{
if ( l != llocal_ && phi_[l].size() > 0 )
{
spline(ndft_,&rps_spl_[0],&phi_spl_[l][0],0.0,0.0,0,1,&phi_spl2_[l][0]);
}
}
// nonlinear core correction
if ( has_nlcc() ) initialize_nlcc();
ztot_ = zval_;
// local potential: subtract the long range part due to the smeared charge
// Next line: constant is 2/sqrt(pi)
// math.h: # define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
vlocr[0] = vps_spl_[llocal_][0] + (ztot_/rcps_) * M_2_SQRTPI;
for ( int i = 1; i < ndft_; i++ )
{
vlocr[i] = vps_spl_[llocal_][i] + (ztot_/rps_spl_[i]) *
erf( rps_spl_[i]/rcps_ );
}
// Prepare the function vlocr to be used later in the Bessel transforms:
//
// local potential: v(G) = 4 pi \int r^2 vloc(r) sin(Gr)/(Gr) dr
// -> store 4 pi r dr vps_(lmax_) in vlocr(i,is)
//
// the Bessel transform is then:
// v(G) = 1/G \sum_r sin(Gr) vlocr
for ( int i = 0; i < ndft_; i++ )
{
vlocr[i] *= fpi * rps_spl_[i] * deltar_;
}
// Local potential
// Compute Fourier coefficients of the local potential
// vlocr[i] contains 4 pi r dr vpsr(lmax_)
// v(G) = 4 pi \int r^2 vpsr(r) sin(Gr)/(Gr) dr
// = 1/G \sum_r sin(Gr) vlocr
//
// v(G=0) by simpson integration
// v(G) = 4 pi \int r^2 vpsr(r) dr
// = \sum_r r vlocr
//
// N.B. vlocr[i] contains 4 pi r dr (vps_(lmax_)-v_pseudocharge(r))
// Simpson integration up to ndft_ (V is zero beyond that point)
// Use fint as temporary array for integration
for ( int i = 0; i < ndft_; i++ )
{
fint[i] = vlocr[i] * rps_spl_[i];
}
const double v0 = simpsn(ndft_,&fint[0]);
// next line: include correction for v(g=0) to be independent of rcps
// const double v0 = simpsn(ndft_,&fint[0]) - M_PI*zval_*rcps_*rcps_;
for ( int i = 0; i < ndft_; i++ )
{
vlocg_spl_[i] = vlocr[i];
}
sinft(ndft_,&vlocg_spl_[0]);
if ( has_nlcc() ) sinft(ndft_,&nlccg_spl_[0]);
// Divide by g's
gspl_[0] = 0.0;
vlocg_spl_[0] = v0;
if ( has_nlcc() ) nlccg_spl_[0] = zcore_;
double fac = M_PI/(ndft_*deltar_);
for ( int i = 1; i < ndft_; i++ )
{
gspl_[i] = i * fac;
vlocg_spl_[i] /= gspl_[i];
if ( has_nlcc() ) nlccg_spl_[i] /= gspl_[i];
}
// Initialize cubic spline interpolation for local potential Vloc(G)
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vlocg_spl_[0],0.0,0.0,0,1,&vlocg_spl2_[0]);
if ( has_nlcc() )
spline(ndft_,&gspl_[0],&nlccg_spl_[0],0.0,0.0,0,1,&nlccg_spl2_[0]);
// Non-local KB projectors
if ( nquad_ == 0 && lmax_ > 0)
{
for ( int l = 0; l <= lmax_; l++ )
{
wsg_[l] = 0.0;
if ( l != llocal_ )
{
// for KB potentials, compute weights wsg[l]
// Compute weight wsg_[l] by integration on the linear mesh
for ( int i = 0; i < ndft_; i++ )
{
double tmp = phi_spl_[l][i] * rps_spl_[i];
fint[i] = ( vps_spl_[l][i] - vps_spl_[llocal_][i] ) * tmp * tmp;
}
double tmp = simpsn(ndft_,&fint[0]);
assert(tmp != 0.0);
// Next lines: store 1/ in wsg[is][l]
wsg_[l] = 1.0 / ( deltar_ * tmp );
// compute non-local projectors:
// w(G) = Ylm(G) i^l 4 pi \int r^2 phi_l(r) j_l(Gr) v_l(r) dr
// -> store 4 pi v_l(r) phi_l(r) dr in vnlr[l][i]
// the Bessel transform is then done by
// l=0: j_0(Gr) = sin(Gr)/(Gr)
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
// l=1: j_1(Gr) = sin(Gr)/(Gr)^2 - cos(Gr)/Gr
// w_1(G) = Ylm(G) i^-1 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i^-1 1/G \sum_r cos(Gr) r vnlr
// l=2: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
for ( int i = 0; i < ndft_; i++ )
{
vnlr[l][i] = fpi * deltar_ *
( vps_spl_[l][i] - vps_spl_[llocal_][i] ) * phi_spl_[l][i];
}
}
}
// compute radial Fourier transforms of vnlr
// Next line: vnlg_spl_ dimensioned ndft_+1 since it is passed to cosft1
// Non-local potentials
//
// w(G) = Ylm(G) i^l 4 pi \int r^2 phi_l(r) j_l(Gr) v_l(r) dr
// -> store 4 pi v_l(r) phi_l(r) dr in vnlr[l][i]
// the Bessel transform is then done by
// s: j_0(Gr) = sin(Gr)/(Gr)
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
// p: j_1(Gr) = sin(Gr)/(Gr)^2 - cos(Gr)/Gr
// w_1(G) = Ylm(G) i^-1 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i^-1 1/G \sum_r cos(Gr) r vnlr
// d: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
// vnlr[l][i] contains 4 pi dr phi_l(r) v_l(r)
for ( int l = 0; l <= lmax_; l++ )
{
if ( l != llocal_ )
{
if ( l == 0 )
{
// s projector
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
//
// G=0: Simpson integration up to ndft_
// Use fint as temporary array for integration
for ( int i = 0; i < ndft_; i++ )
{
fint[i] = vnlr[l][i] * rps_spl_[i] * rps_spl_[i];
}
const double v0 = simpsn(ndft_, &fint[0]);
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i] * rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[l][0]);
vnlg_spl_[l][0] = v0;
// Divide by g
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[l][i] /= gspl_[i];
}
// Initialize cubic spline interpolation
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[l][0],0.0,0.0,0,1,
&vnlg_spl2_[l][0]);
}
else if ( l == 1 )
{
// p projectors
// w_1(G) = Ylm(G) i 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i 1/G \sum_r cos(Gr) r vnlr
// vnlr(i,is,l) contains 4 pi dr phi_l(r) v_l(r)
// Next line: v(G=0) is zero (j_1(Gr) -> 0 as G->0 )
const double v0 = 0.0;
// First part: 1/G^2 \sum_r sin(Gr) vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i];
}
sinft(ndft_,&vnlg_spl_[l][0]);
// Divide by g**2 and store in fint */
fint[0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
fint[i] = vnlg_spl_[l][i] / ( gspl_[i] * gspl_[i] );
}
// Second part: cosine transform: 1/G \sum_r cos(Gr) r vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i] * rps_spl_[i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[l][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[l][0]);
// Divide by g and add to fint to get vnlg_
vnlg_spl_[l][0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[l][i] = fint[i] - vnlg_spl_[l][i]/gspl_[i];
}
// Initialize spline interpolation
// Use natural bc (y"=0) at G=0 and natural bc (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[l][0],0.0,0.0,1,1,
&vnlg_spl2_[l][0]);
}
else if ( l == 2 )
{
// d projectors
// d: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
// vnlr[l][i] contains 4 pi dr phi_l(r) v_l(r)
// Next line: v(G=0) is zero (j_2(Gr) -> 0 as G->0 )
const double v0 = 0.0;
// First part: sine transform 3/G^3 \sum_r sin(Gr)/r vnlr
// Note: the integrand is linear near r=0 since vnlr(r) ~ r^2
vnlg_spl_[l][0] = 0.0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i] / rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[l][0]);
// multiply by 3/G^3 and store in fint */
fint[0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
fint[i] = 3.0 * vnlg_spl_[l][i] / ( gspl_[i]*gspl_[i]*gspl_[i] );
}
// Second part: sine transform -1/G \sum_r sin(Gr)*r vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i] * rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[l][0]);
// multiply by -1/G and accumulate in fint */
fint[0] += v0;
for ( int i = 1; i < ndft_; i++ )
{
fint[i] += - vnlg_spl_[l][i] / gspl_[i];
}
// Third part: cosine transform: -3/G^2 \sum_r cos(Gr) vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[l][i] = vnlr[l][i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[l][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[l][0]);
// Multiply by -3/G^2 and add to fint
fint[0] += v0;
for ( int i = 1; i < ndft_; i++ )
{
fint[i] += - 3.0 * vnlg_spl_[l][i] / (gspl_[i] * gspl_[i]);
}
vnlg_spl_[l][0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[l][i] = fint[i];
}
// Initialize spline interpolation
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[l][0],0.0,0.0,0,1,
&vnlg_spl2_[l][0]);
}
} // l != llocal_
} // l
} // nquad_ == 0 && lmax_ > 0 (KB projectors)
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool Species::initialize_slpp()
{
assert(description_ != "undefined");
const double fpi = 4.0 * M_PI;
const int np = vlocal_.size();
nquad_ = 0;
// number of projectors
nop_ = 0;
for ( int l = 0; l < proj_.size(); l++ )
for ( int n = 0; n < proj_[l].size(); n++ )
{
lmap_.push_back(l);
nop_++;
}
// check sanity of input
if (zval_ < 0) throw SpeciesInitException("zval_ < 0");
if (rcps_ < 0.0) throw SpeciesInitException("rcps_ < 0");
if (mass_ < 0.0) throw SpeciesInitException("mass_ < 0");
// check consistency of input
// a) for each angular momentum l there has to be a D matrix of
// the size n x n, where n are the number of projectors with
// this angular momentum l
if ( proj_.size() != d_.size() )
throw SpeciesInitException("projector and dmatrix inconsistent");
for ( int l = 0; l < proj_.size(); l++ )
{
// trivial case only one projector (n = 1)
// if D matrix is not present, create a trivial one D = 1
// otherwise leave D matrix unchanged
if ( proj_[l].size() == 1 )
{
if ( d_[l].size() > 1 )
throw SpeciesInitException("dmatrix has wrong dimension");
d_[l].resize(1);
if ( d_[l][0].size() > 1)
throw SpeciesInitException("dmatrix has wrong dimension");
d_[l][0].resize(1,1);
}
// general case dim( d[l] ) = n
else
{
const int nmax = proj_[l].size();
if ( d_[l].size() != nmax )
throw SpeciesInitException("dmatrix has wrong dimension");
for ( int n = 0; n < nmax; n++ )
{
if ( d_[l][n].size() != nmax )
throw SpeciesInitException("dmatrix has wrong dimension");
}
}
}
// compute number of non-local projectors nlm_
nlm_ = 0;
for ( int l = 0; l < proj_.size(); l++ )
{
nlm_ += (2 * l + 1) * proj_[l].size();
}
// compute ndft_: size of radial FFT array
// ndft_ is a power of 2 larger than ( rdftmin / deltar_ )
// minimum outer bound in (a.u.)
const double rdftmin = 40.0;
// next line: limit small mesh sizes
assert(deltar_ > 0.0001);
ndft_ = 1;
while ( ndft_ * deltar_ < rdftmin )
ndft_ *= 2;
rps_spl_.resize(ndft_);
for ( int i = 0; i < ndft_; i++ )
rps_spl_[i] = i * deltar_;
phi_spl_.resize(nop_);
phi_spl2_.resize(nop_);
for ( int iop = 0; iop < nop_; iop++ )
{
phi_spl_[iop].resize(ndft_);
phi_spl2_[iop].resize(ndft_);
}
// extend rps and vps_ to full mesh (up to i==ndft_-1)
vector fint(ndft_);
gspl_.resize(ndft_);
vlocg_spl_.resize(ndft_);
vlocg_spl2_.resize(ndft_);
vnlg_spl_.resize(nop_);
vnlg_spl2_.resize(nop_);
vector vlocr(ndft_);
vector > vnlr(nop_);
for ( int iop = 0; iop < nop_; iop++ )
{
vnlr[iop].resize(ndft_);
vnlg_spl_[iop].resize(ndft_ + 1);
vnlg_spl2_[iop].resize(ndft_ + 1);
}
// Extend vps_[l][i] up to ndft_ using -zv/r
// local potential
for ( int i = 0; i < np; i++ )
vlocr[i] = vlocal_[i];
for ( int i = np; i < ndft_; i++ )
vlocr[i] = -zval_ / rps_spl_[i];
for ( int iop = 0; iop < nop_; iop++ )
{
// counter for projectors of same l
int n = 0;
if ( iop == 0 || lmap_[iop] != lmap_[iop - 1] ) n = 0;
else n++;
for ( int i = 0; i < np; i++ )
phi_spl_[iop][i] = proj_[lmap_[iop]][n][i];
for ( int i = np; i < ndft_; i++ )
phi_spl_[iop][i] = 0.0;
}
for ( int iop = 0; iop < nop_; iop++ )
{
spline(ndft_,&rps_spl_[0],&phi_spl_[iop][0],0.0,0.0,0,1,&phi_spl2_[iop][0]);
}
// nonlinear core correction
if ( has_nlcc() ) initialize_nlcc();
ztot_ = zval_;
// local potential: subtract the long range part due to the smeared charge
// Next line: constant is 2/sqrt(pi)
// math.h: # define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
vlocr[0] += ( ztot_ / rcps_ ) * M_2_SQRTPI;
for ( int i = 1; i < ndft_; i++ )
{
vlocr[i] += ( ztot_ / rps_spl_[i] ) * erf(rps_spl_[i] / rcps_);
}
// Prepare the function vlocr to be used later in the Bessel transforms:
//
// local potential: v(G) = 4 pi \int r^2 vloc(r) sin(Gr)/(Gr) dr
// -> store 4 pi r dr vps_(lmax_) in vlocr(i,is)
//
// the Bessel transform is then:
// v(G) = 1/G \sum_r sin(Gr) vlocr
for ( int i = 0; i < ndft_; i++ )
{
vlocr[i] *= fpi * rps_spl_[i] * deltar_;
}
// Local potential
// Compute Fourier coefficients of the local potential
// vlocr[i] contains 4 pi r dr vpsr(lmax_)
// v(G) = 4 pi \int r^2 vpsr(r) sin(Gr)/(Gr) dr
// = 1/G \sum_r sin(Gr) vlocr
//
// v(G=0) by simpson integration
// v(G) = 4 pi \int r^2 vpsr(r) dr
// = \sum_r r vlocr
//
// N.B. vlocr[i] contains 4 pi r dr (vps_(lmax_)-v_pseudocharge(r))
// Simpson integration up to ndft_ (V is zero beyond that point)
// Use fint as temporary array for integration
for ( int i = 0; i < ndft_; i++ )
{
fint[i] = vlocr[i] * rps_spl_[i];
}
const double v0 = simpsn(ndft_,&fint[0]);
// next line: include correction for v(g=0) to be independent of rcps
// const double v0 = simpsn(ndft_,&fint[0]) - M_PI*zval_*rcps_*rcps_;
for ( int i = 0; i < ndft_; i++ )
{
vlocg_spl_[i] = vlocr[i];
}
sinft(ndft_,&vlocg_spl_[0]);
if ( has_nlcc() ) sinft(ndft_,&nlccg_spl_[0]);
// Divide by g's
gspl_[0] = 0.0;
vlocg_spl_[0] = v0;
if ( has_nlcc() ) nlccg_spl_[0] = zcore_;
double fac = M_PI/(ndft_*deltar_);
for ( int i = 1; i < ndft_; i++ )
{
gspl_[i] = i * fac;
vlocg_spl_[i] /= gspl_[i];
if ( has_nlcc() ) nlccg_spl_[i] /= gspl_[i];
}
// Initialize cubic spline interpolation for local potential Vloc(G)
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vlocg_spl_[0],0.0,0.0,0,1,&vlocg_spl2_[0]);
if ( has_nlcc() )
spline(ndft_,&gspl_[0],&nlccg_spl_[0],0.0,0.0,0,1,&nlccg_spl2_[0]);
// Non-local projectors
// inf
// / /
// | 3 i G r l | 2
// | d r e f (r) Y (r) = 4 pi i Y (G) | dr r j (Gr) f (r)
// | l lm lm | l l
// / /
// 0
// where f_l(r) = phi_l(r)
// multiply projectors with weight
// w_l = 1 / < phi_l | V_l | phi_l >
// as there is no V_l all weights are 1
wsg_.resize(nop_,1.0);
for ( int iop = 0; iop < nop_; iop++ )
{
// compute non-local projectors:
// w(G) = Ylm(G) i^l 4 pi \int r^2 j_l(Gr) f_l(r) dr
// -> store 4 pi f_l(r) dr in vnlr[l][i]
// the Bessel transform is then done by
// l=0: j_0(Gr) = sin(Gr)/(Gr)
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
// l=1: j_1(Gr) = sin(Gr)/(Gr)^2 - cos(Gr)/Gr
// w_1(G) = Ylm(G) i^-1 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i^-1 1/G \sum_r cos(Gr) r vnlr
// l=2: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
for ( int i = 0; i < ndft_; i++ )
{
vnlr[iop][i] = fpi * deltar_ * phi_spl_[iop][i];
}
} // loop over projector
// compute radial Fourier transforms of vnlr
// Next line: vnlg_spl_ dimensioned ndft_+1 since it is passed to cosft1
// Non-local potentials
//
// w(G) = Ylm(G) i^l 4 pi \int r^2 j_l(Gr) f_l(r) dr
// -> store 4 pi f_l(r) dr in vnlr[l][i]
// the Bessel transform is then done by
// s: j_0(Gr) = sin(Gr)/(Gr)
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
// p: j_1(Gr) = sin(Gr)/(Gr)^2 - cos(Gr)/Gr
// w_1(G) = Ylm(G) i^-1 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i^-1 1/G \sum_r cos(Gr) r vnlr
// d: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
// f: j_3(Gr) = sin(Gr)*(15/(Gr)^4-6/(Gr)^2)) +
// cos(Gr)*(1/(Gr)-15/(Gr)^3)
// w_3(G) = Ylm(G) i^-3 ( 15/G^4 \sum_r sin(Gr)/r^2 vnlr
// - 6/G^2 \sum_r sin(Gr) vnlr
// + 1/G \sum_r cos(Gr)*r vnlr
// - 15/G^3 \sum_r cos(Gr)/r vnlr )
for ( int iop = 0; iop < nop_; iop++ )
{
if ( lmap_[iop] == 0 )
{
// s projector
// w_0(G) = Ylm(G) 1/G \sum_r sin(Gr) r vnlr
//
// G=0: Simpson integration up to ndft_
// Use fint as temporary array for integration
for ( int i = 0; i < ndft_; i++ )
{
const double r = rps_spl_[i];
fint[i] = vnlr[iop][i] * r * r;
}
const double v0 = simpsn(ndft_,&fint[0]);
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] * rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[iop][0]);
vnlg_spl_[iop][0] = v0;
// Divide by g
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] /= gspl_[i];
}
// Initialize cubic spline interpolation
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[iop][0],
0.0,0.0,0,1,&vnlg_spl2_[iop][0]);
}
else if ( lmap_[iop] == 1 )
{
// p projectors
// w_1(G) = Ylm(G) i 1/G^2 \sum_r sin(Gr) vnlr -
// Ylm(G) i 1/G \sum_r cos(Gr) r vnlr
// Next line: v(G=0) is zero (j_1(Gr) -> 0 as G->0 )
const double v0 = 0.0;
// First part: 1/G^2 \sum_r sin(Gr) vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i];
}
sinft(ndft_,&vnlg_spl_[iop][0]);
// Divide by g**2 and store in fint */
fint[0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] = vnlg_spl_[iop][i] / ( g * g );
}
// Second part: cosine transform: 1/G \sum_r cos(Gr) r vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] * rps_spl_[i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[iop][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[iop][0]);
// Divide by g and add to fint to get vnlg_
vnlg_spl_[iop][0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = fint[i] - vnlg_spl_[iop][i] / gspl_[i];
}
// Initialize spline interpolation
// Use natural bc (y"=0) at G=0 and natural bc (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[iop][0],
0.0,0.0,1,1,&vnlg_spl2_[iop][0]);
}
else if ( lmap_[iop] == 2 )
{
// d projectors
// d: j_2(Gr) = sin(Gr)*(3/(Gr)^3 -1/(Gr)) - 3*cos(Gr)/(Gr)^2
// w_2(G) = Ylm(G) i^-2 ( 3/G^3 \sum_r sin(Gr)/r vnlr -
// 1/G \sum_r sin(Gr)*r vnlr -
// 3/G^2 \sum_r cos(Gr) vnlr )
// Next line: v(G=0) is zero (j_2(Gr) -> 0 as G->0 )
const double v0 = 0.0;
// First part: sine transform 3/G^3 \sum_r sin(Gr)/r vnlr
// Note: the integrand is linear near r=0 since vnlr(r) ~ r^2
vnlg_spl_[iop][0] = 0.0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] / rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[iop][0]);
// multiply by 3/G^3 and store in fint */
fint[0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] = 3.0 * vnlg_spl_[iop][i] / ( g * g * g );
}
// Second part: sine transform -1/G \sum_r sin(Gr)*r vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] * rps_spl_[i];
}
sinft(ndft_,&vnlg_spl_[iop][0]);
// multiply by -1/G and accumulate in fint */
for ( int i = 1; i < ndft_; i++ )
{
fint[i] += -vnlg_spl_[iop][i] / gspl_[i];
}
// Third part: cosine transform: -3/G^2 \sum_r cos(Gr) vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[iop][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[iop][0]);
// Multiply by -3/G^2 and add to fint
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] += -3.0 * vnlg_spl_[iop][i] / ( g * g );
}
vnlg_spl_[iop][0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = fint[i];
}
// Initialize spline interpolation
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[iop][0],
0.0,0.0,0,1,&vnlg_spl2_[iop][0]);
}
else if ( lmap_[iop] == 3 )
{
// f projectors
// f: j_3(Gr) = sin(Gr)*(15/(Gr)^4-6/(Gr)^2)) +
// cos(Gr)*(1/(Gr)-15/(Gr)^3)
// w_3(G) = Ylm(G) i^-3 ( 15/G^4 \sum_r sin(Gr)/r^2 vnlr
// - 6/G^2 \sum_r sin(Gr) vnlr
// + 1/G \sum_r cos(Gr)*r vnlr
// - 15/G^3 \sum_r cos(Gr)/r vnlr )
// Next line: v(G=0) is zero (j_3(Gr) -> 0 as G->0 )
const double v0 = 0.0;
// First part: sine transform 15/G^4 \sum_r sin(Gr)/r^2 vnlr
// Note: the integrand is linear near r=0 since vnlr(r) ~ r^3
vnlg_spl_[iop][0] = 0.0;
for ( int i = 1; i < ndft_; i++ )
{
const double r = rps_spl_[i];
vnlg_spl_[iop][i] = vnlr[iop][i] / (r*r);
}
sinft(ndft_,&vnlg_spl_[iop][0]);
// multiply by 15/G^4 and store in fint */
fint[0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] = 15.0 * vnlg_spl_[iop][i] / (g*g*g*g);
}
// Second part: sine transform -6/G^2 \sum_r sin(Gr) vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i];
}
sinft(ndft_,&vnlg_spl_[iop][0]);
// multiply by -6/G^2 and accumulate in fint */
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] += -6.0 * vnlg_spl_[iop][i] / (g*g);
}
// Third part: cosine transform: 1/G \sum_r cos(Gr)*r vnlr
for ( int i = 0; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] * rps_spl_[i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[iop][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[iop][0]);
// Multiply by 1/G and add to fint
for ( int i = 1; i < ndft_; i++ )
{
fint[i] += vnlg_spl_[iop][i] / gspl_[i];
}
// Fourth part: cosine transform: -15/G^3 \sum_r cos(Gr)/r vnlr
vnlg_spl_[iop][0] = 0.0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = vnlr[iop][i] / rps_spl_[i];
}
// N.B. Next line: Initialize also vnlg_[l][ndft_] to zero
// since it is used and modified by cosft1
// vnlg_ was dimensioned ndft_[is]+1
vnlg_spl_[iop][ndft_] = 0.0;
cosft1(ndft_,&vnlg_spl_[iop][0]);
// Multiply by -15/G^3 and add to fint
for ( int i = 1; i < ndft_; i++ )
{
const double g = gspl_[i];
fint[i] += -15.0 * vnlg_spl_[iop][i] / (g*g*g);
}
vnlg_spl_[iop][0] = v0;
for ( int i = 1; i < ndft_; i++ )
{
vnlg_spl_[iop][i] = fint[i];
}
// Initialize spline interpolation
// Use zero first derivative at G=0 and natural (y"=0) at Gmax
spline(ndft_,&gspl_[0],&vnlg_spl_[iop][0],
0.0,0.0,0,1,&vnlg_spl2_[iop][0]);
}
else
{
throw SpeciesInitException("Species::initialize_slpp: l < 0 or l > 3");
}
} // loop over projector
return true;
}
////////////////////////////////////////////////////////////////////////////////
void Species::initialize_nlcc()
{
const double fpi = 4.0 * M_PI;
// allocate array
nlcc_spl_.resize(ndft_);
nlcc_spl2_.resize(ndft_);
nlccg_spl_.resize(ndft_);
nlccg_spl2_.resize(ndft_);
fill( nlcc_spl_.begin(), nlcc_spl_.end(), 0.0 );
copy( nlcc_.begin(), nlcc_.end(), nlcc_spl_.begin() );
// compute spline coefficients
spline(ndft_,&rps_spl_[0],&nlcc_spl_[0],0.0,0.0,0,1,&nlcc_spl2_[0]);
// prepare the function nlccr to be used later in the Bessel transforms
//
// local density: nlcc(G) = 4 pi \int r^2 nlcc(r) sin(Gr)/(Gr) dr
// -> store 4 pi r dr nlcc_spl_ in nlccr(i)
//
// the Bessel transform is then:
// nlcc(G) = 1/G \sum_r sin(Gr) nlccr
for ( int i = 0; i < ndft_; i++ )
{
nlccg_spl_[i] = fpi * rps_spl_[i] * deltar_ * nlcc_spl_[i];
}
// integrate core correction density
// / 2
// | dr 4 pi r n(r)
// /
// nlccr already contains factor 4 pi r
// use fint for integration
vector fint(ndft_);
for ( int i = 0; i < ndft_; i++ )
{
fint[i] = nlccg_spl_[i] * rps_spl_[i];
}
zcore_ = simpsn(ndft_,&fint[0]);
}
void Species::phi(int l, double r, double &val)
{
val = 0.0;
if ( phi_[l].size() == 0 || l > lmax_ || r > rps_spl_[ndft_-1] )
return;
splint(ndft_,&rps_spl_[0],&phi_spl_[l][0],&phi_spl2_[l][0],r,&val);
}
void Species::vpsr(int l, double r, double &v)
{
if ( l > lmax_ || r > rps_spl_[ndft_-1] )
{
v = 0.0;
}
else
{
splint(ndft_,&rps_spl_[0],&vps_spl_[l][0],&vps_spl2_[l][0],r,&v);
}
}
void Species::dvpsr(int l, double r, double &v, double &dv)
{
if ( l > lmax_ || r > rps_spl_[ndft_-1] )
{
v = 0.0;
dv = 0.0;
}
else
{
splintd(ndft_,&rps_spl_[0],&vps_spl_[l][0],&vps_spl2_[l][0],r,&v,&dv);
}
}
void Species::vlocg(double g, double &v)
{
if ( g > gspl_[ndft_-1] )
{
v = 0.0;
}
else
{
splint(ndft_,&gspl_[0],&vlocg_spl_[0],&vlocg_spl2_[0],g,&v);
}
}
void Species::dvlocg(double g, double &v, double &dv)
{
if ( g > gspl_[ndft_-1] )
{
v = 0.0;
dv = 0.0;
}
else
{
splintd(ndft_,&gspl_[0],&vlocg_spl_[0],&vlocg_spl2_[0],g,&v,&dv);
}
}
void Species::vnlg(int iop, double g, double &v)
{
assert ( iop >= 0 && iop < nop_ );
int l = lmap_[iop];
if ( l == llocal_ || g > gspl_[ndft_-1] )
{
v = 0.0;
}
else
{
splint(ndft_,&gspl_[0],&vnlg_spl_[iop][0],&vnlg_spl2_[iop][0],g,&v);
}
}
void Species::dvnlg(int iop, double g, double &v, double &dv)
{
assert ( iop >= 0 && iop < nop_ );
int l = lmap_[iop];
if ( l == llocal_ || g > gspl_[ndft_-1] )
{
v = 0.0;
dv = 0.0;
}
else
{
splintd(ndft_,&gspl_[0],&vnlg_spl_[iop][0],&vnlg_spl2_[iop][0],g,&v,&dv);
}
}
double Species::rhopsg( double g )
{
double arg = 0.25 * rcps_ * rcps_ * g * g;
return -ztot_ * exp( -arg );
}
// core density for nonlinear core correction in reciprocal space
void Species::rhocoreg(double g, double &rho)
{
if ( has_nlcc() && g <= gspl_[ndft_-1] )
{
// spline interpolation
splint(ndft_,&gspl_[0],&nlccg_spl_[0],&nlccg_spl2_[0],g,&rho);
}
else
{
rho = 0.0;
}
}
// core density for nonlinear core correction in reciprocal space
void Species::drhocoreg(double g, double &rho, double &drho)
{
if ( has_nlcc() && g <= gspl_[ndft_ - 1] )
{
// spline interpolation
splintd(ndft_,&gspl_[0],&nlccg_spl_[0],&nlccg_spl2_[0],g,&rho,&drho);
}
else
{
rho = 0.0;
drho = 0.0;
}
}
ostream& operator << ( ostream &os, Species &s )
{
s.print(os, false);
return os;
}
void Species::print(ostream &os, bool expanded_form)
{
// XML output of species
// print in compact form if the uri is known
// and if expanded_form==false
if ( !expanded_form && uri() != "" )
{
os <<"" << endl;
}
else
{
os.setf(ios::scientific,ios::floatfield);
os << setprecision(12);
os <<"" << endl;
os << "" << description() << "" << endl;
os << "" << symbol() << "" << endl;
os << "" << atomic_number() << "" << endl;
os << "" << mass() << "" << endl;
if ( type_ == NCPP )
{
os << "" << endl;
os << "" << zval() << "" << endl;
os << "" << lmax() << "" << endl;
os << "" << llocal() << "" << endl;
os << "" << nquad() << "" << endl;
os << "" << rquad() << "" << endl;
os << "" << deltar() << "" << endl;
if ( nlcc_.size() > 0 ) print_nlcc(os);
for ( int l = 0; l <= lmax(); l++ )
{
const int size = vps_[l].size();
os << "" << endl;
os << "\n";
for ( int i = 0; i < size; i++ )
os << vps_[l][i] << "\n";
os << "\n";
os << "\n";
if ( l < phi_.size() && phi_[l].size() == size )
print_radial_function(os,phi_[l]);
os << "\n";
os << "" << endl;
}
os << "" << endl;
}
else if ( type_ == SLPP )
{
os << "" << endl;
os << "" << zval() << "" << endl;
os << "" << deltar() << "" << endl;
if ( nlcc_.size() > 0 ) print_nlcc(os);
os << "" << endl;
print_radial_function(os,vlocal_);
os << "" << endl;
// print projector
for ( int l = 0; l < proj_.size(); l++ )
{
for ( int n = 0; n < proj_[l].size(); n++ )
{
os << "" << endl;
// print radial function
print_radial_function(os,proj_[l][n]);
os << "" << endl;
}
}
// print D matrix
for ( int l = 0; l < d_.size(); l++ )
{
for ( int n = 0; n < d_[l].size(); n++ )
for ( int m = 0; m < d_[l][n].size(); m++ )
os << "" << d_[l][n][m] << "" << endl;
}
os << "" << endl;
}
os << "" << endl;
}
}
// print core density of nonlinear core correction
void Species::print_nlcc(std::ostream& os)
{
os << "" << endl;
print_radial_function(os,nlcc_);
os << "" << endl;
}
// print any radial function
void Species::print_radial_function(std::ostream& os,
const std::vector& rad_func)
{
for ( int i = 0; i < rad_func.size(); i++ )
os << rad_func[i] << endl;
}
void Species::info(ostream &os)
{
os.setf(ios::left,ios::adjustfield);
if ( uri() != "" )
{
os <<"" << endl;
}
else
{
os <<"" << endl;
}
os << " " << description() << " " << endl;
os << " " << symbol() << "" << endl;
os << " " << atomic_number() << "" << endl;
os << " " << mass() << "" << endl;
if ( type_ == NCPP )
{
os << " " << endl;
os << " " << zval() << "" << endl;
os << " " << lmax() << "" << endl;
os << " " << llocal() << "" << endl;
os << " " << nquad() << "" << endl;
os << " " << rquad() << "" << endl;
os << " " << deltar() << "" << endl;
os << " " << endl;
}
else
{
os << " " << endl;
os << " " << zval() << "" << endl;
os << " " << deltar() << "" << endl;
os << " " << endl;
}
os << "" << endl;
// describe type of potential
if ( type_ == NCPP )
{
if ( nquad() == 0 )
{
if ( lmax() == 0 )
os << " local potential" << endl;
else
os << " Kleinman-Bylander potential" << endl;
}
else if ( nquad() > 0 )
{
os << " NCPP potential with " << nquad()
<< " quadrature points in [0.0, " << rquad() << "]" << endl;
os << " local (within 1.e-6) beyond r = " << rcut_loc(1.e-6) << endl;
os << " local (within 1.e-5) beyond r = " << rcut_loc(1.e-5) << endl;
os << " local (within 1.e-4) beyond r = " << rcut_loc(1.e-4) << endl;
os << " local (within 1.e-3) beyond r = " << rcut_loc(1.e-3) << endl;
}
}
else if ( type_ == SLPP )
{
os << " SLPP semilocal potential" << endl;
}
os << " rcps_ = " << rcps() << endl;
os.setf(ios::right,ios::adjustfield);
}
////////////////////////////////////////////////////////////////////////////////
double Species::rcut_loc(double epsilon)
{
// find radius at which the largest deviation delta_vnl(r) is < epsilon
double delta = 0.0;
int i = ndft_-1;
while ( ( delta < epsilon ) && i > 0 )
{
i--;
for ( int l = 0; l <= lmax_; l++ )
{
if ( l != llocal_ )
{
// compute deviation vps_[l][i]
double dv = fabs( vps_spl_[l][i] - vps_spl_[llocal_][i] );
delta = dv > delta ? dv : delta;
}
}
}
// adjust i so that delta_v[i] < epsilon
if ( i < ndft_-1 ) i++;
return rps_spl_[i];
}
////////////////////////////////////////////////////////////////////////////////
// helper function that extract l and n from projector index
Species::ProjectorData Species::get_proj_data(int ipr)
{
ProjectorData res;
res.l = -1;
res.n = 0;
int jpr = 0;
for ( int iop = 0; iop < nop_; iop++ )
{
int l = lmap_[iop];
// n labels differentiates between projectors of same l
if ( res.l == l ) res.n++;
// reset n if new angular momentum is found
else
{
res.l = l;
res.n = 0;
}
// there is one projector Y_lm
for ( res.m = -l; res.m <= l; res.m++ )
{
if ( ipr == jpr ) return res;
jpr++;
}
}
// projector out of bounds
assert(false);
res.l = -1;
res.n = 0;
return res;
}
////////////////////////////////////////////////////////////////////////////////
// extract D matrix
double Species::dmatrix(int ipr, int jpr)
{
// extract angular momentum of projectors
const ProjectorData iem = get_proj_data(ipr);
const ProjectorData jem = get_proj_data(jpr);
// projectors have only nonzero overlap if diagonal in l and m
if ( iem.l != jem.l || iem.m != jem.m ) return 0;
// if dmatrix is not present identity matrix is assumed
if ( d_.size() <= iem.l || d_[iem.l].size() == 0 ) return 1;
// return dmatrix
return d_[iem.l][iem.n][jem.n];
}