#include "KernelFunction.h" #include #include #include "ATC_Error.h" #include "Quadrature.h" #include "Utility.h" using namespace std; using namespace ATC_Utility; static const double tol = 1.0e-8; static const int line_ngauss = 10; static double line_xg[line_ngauss], line_wg[line_ngauss]; namespace ATC { //======================================================================== // KernelFunctionMgr //======================================================================== KernelFunctionMgr * KernelFunctionMgr::myInstance_ = NULL; //------------------------------------------------------------------------ // instance //------------------------------------------------------------------------ KernelFunctionMgr * KernelFunctionMgr::instance() { if (myInstance_ == NULL) { myInstance_ = new KernelFunctionMgr(); } return myInstance_; } //------------------------------------------------------------------------ // get function from args //------------------------------------------------------------------------ KernelFunction* KernelFunctionMgr::function(char ** arg, int narg) { /*! \page man_kernel_function fix_modify AtC kernel \section syntax fix_modify AtC kernel - type (keyword) = step, cell, cubic_bar, cubic_cylinder, cubic_sphere, quartic_bar, quartic_cylinder, quartic_sphere \n - parameters :\n step = radius (double) \n cell = hx, hy, hz (double) or h (double) \n cubic_bar = half-width (double) \n cubic_cylinder = radius (double) \n cubic_sphere = radius (double) \n quartic_bar = half-width (double) \n quartic_cylinder = radius (double) \n quartic_sphere = radius (double) \n \section examples fix_modify AtC kernel cell 1.0 1.0 1.0 fix_modify AtC kernel quartic_sphere 10.0 \section description \section restrictions Must be used with the hardy AtC fix \n For bar kernel types, half-width oriented along x-direction \n For cylinder kernel types, cylindrical axis is assumed to be in z-direction \n ( see \ref man_fix_atc ) \section related \section default No default */ int argIdx = 0; KernelFunction * ptr = NULL; char* type = arg[argIdx++]; if (strcmp(type,"step")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionStep(1,parameters); } else if (strcmp(type,"cell")==0) { double parameters[3]; parameters[0] = parameters[1] = parameters[2] = atof(arg[argIdx++]); if (narg > argIdx) { // L_x, L_y, L_z for (int i = 1; i < 3; i++) { parameters[i] = atof(arg[argIdx++]); } } ptr = new KernelFunctionCell(2,parameters); } else if (strcmp(type,"cubic_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionCubicBar(1,parameters); } else if (strcmp(type,"linear_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionLinearBar(1,parameters); } else if (strcmp(type,"cubic_cylinder")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionCubicCyl(1,parameters); } else if (strcmp(type,"cubic_sphere")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionCubicSphere(1,parameters); } else if (strcmp(type,"quartic_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionQuarticBar(1,parameters); } else if (strcmp(type,"quartic_cylinder")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionQuarticCyl(1,parameters); } else if (strcmp(type,"quartic_sphere")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionQuarticSphere(1,parameters); } pointerSet_.insert(ptr); return ptr; } // Destructor KernelFunctionMgr::~KernelFunctionMgr() { set::iterator it; for (it = pointerSet_.begin(); it != pointerSet_.end(); it++) if (*it) delete *it; } //------------------------------------------------------------------------ // KernelFunction //------------------------------------------------------------------------ // constructor KernelFunction::KernelFunction(int nparameters, double* parameters): Rc_(0),invRc_(0),nsd_(3), lammpsInterface_(LammpsInterface::instance()) { Rc_ = parameters[0]; invRc_ = 1.0/Rc_; Rc_ = parameters[0]; invRc_ = 1.0/Rc_; invVol_ = 1.0/(4.0/3.0*Pi_*pow(Rc_,3)); ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg); // get periodicity and box bounds/lengths lammpsInterface_->box_periodicity(periodicity[0], periodicity[1],periodicity[2]); lammpsInterface_->box_bounds(box_bounds[0][0],box_bounds[1][0], box_bounds[0][1],box_bounds[1][1], box_bounds[0][2],box_bounds[1][2]); for (int k = 0; k < 3; k++) { box_length[k] = box_bounds[1][k] - box_bounds[0][k]; } } // does an input node's kernel intersect bonds on this processor bool KernelFunction::node_contributes(DENS_VEC node) const { DENS_VEC ghostNode = node; bool contributes = true; bool ghostContributes = lammpsInterface_->nperiodic(); double kernel_bounds[2][3]; lammpsInterface_->sub_bounds(kernel_bounds[0][0],kernel_bounds[1][0], kernel_bounds[0][1],kernel_bounds[1][1], kernel_bounds[0][2],kernel_bounds[1][2]); for (int i=0; i<3; ++i) { if (i < nsd_) { kernel_bounds[0][i] -= (Rc_+lammpsInterface_->pair_cutoff()); kernel_bounds[1][i] += (Rc_+lammpsInterface_->pair_cutoff()); contributes = contributes && (node(i) >= kernel_bounds[0][i]) && (node(i) < kernel_bounds[1][i]); if (periodicity[i]) { if (node[i] <= box_bounds[0][i] + box_length[i]/2) { ghostNode[i] += box_length[i]; } else { ghostNode[i] -= box_length[i]; } ghostContributes = ghostContributes && ((ghostNode(i) >= kernel_bounds[0][i]) || (node(i) >= kernel_bounds[0][i])) && ((ghostNode(i) < kernel_bounds[1][i]) || (node(i) < kernel_bounds[1][i])); } } if (!(contributes || ghostContributes)) break; } return true; } bool KernelFunction::in_support(DENS_VEC dx) const { if (dx.norm() > Rc_) return false; return true; } // bond function value via quadrature double KernelFunction::bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) const { DENS_VEC xab(nsd_), q(nsd_); double lamg; double bhsum=0.0; xab = xa - xb; for (int i = 0; i < line_ngauss; i++) { lamg=0.5*((lam2-lam1)*line_xg[i]+(lam2+lam1)); q = lamg*xab + xb; double locg_value=this->value(q); bhsum+=locg_value*line_wg[i]; } return 0.5*(lam2-lam1)*bhsum; } // localization-volume intercepts for bond calculation // bond intercept values assuming spherical support void KernelFunction::bond_intercepts(DENS_VEC& xa, DENS_VEC& xb, double &lam1, double &lam2) const { if (nsd_ == 2) {// for cylinders, axis is always z! const int iaxis = 2; xa[iaxis] = 0.0; xb[iaxis] = 0.0; } else if (nsd_ == 1) {// for bars, analysis is 1D in x xa[1] = 0.0; xa[2] = 0.0; xb[1] = 0.0; xb[2] = 0.0; } lam1 = lam2 = -1; double ra_n = invRc_*xa.norm(); // lambda = 1 double rb_n = invRc_*xb.norm(); // lambda = 0 bool a_in = (ra_n <= 1.0); bool b_in = (rb_n <= 1.0); if (a_in && b_in) { lam1 = 0.0; lam2 = 1.0; return; } DENS_VEC xab = xa - xb; double rab_n = invRc_*xab.norm(); double a = rab_n*rab_n; // always at least an interatomic distance double b = 2.0*invRc_*invRc_*xab.dot(xb); double c = rb_n*rb_n - 1.0; double discrim = b*b - 4.0*a*c; // discriminant if (discrim < 0) return; // no intersection // num recipes: double s1, s2; if (b < 0.0) { double aux = -0.5*(b-sqrt(discrim)); s1 = c/aux; s2 = aux/a; } else { double aux = -0.5*(b+sqrt(discrim)); s1 = aux/a; s2 = c/aux; } if (a_in && !b_in) { lam1 = s1; lam2 = 1.0; } else if (!a_in && b_in) { lam1 = 0.0; lam2 = s2; } else { if (s1 >= 0.0 && s2 <= 1.0) { lam1 = s1; lam2 = s2; } } } //------------------------------------------------------------------------ // constructor KernelFunctionStep::KernelFunctionStep (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); } } } } // function value double KernelFunctionStep::value(DENS_VEC& x_atom) const { double rn=invRc_*x_atom.norm(); if (rn <= 1.0) { return 1.0; } else { return 0.0; } } // function derivative value void KernelFunctionStep::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ /** a step with rectangular support suitable for a rectangular grid */ // constructor KernelFunctionCell::KernelFunctionCell (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { hx = parameters[0]; hy = parameters[1]; hz = parameters[2]; invVol_ = 1.0/8.0/(hx*hy*hz); cellBounds_.reset(6); cellBounds_(0) = -hx; cellBounds_(1) = hx; cellBounds_(2) = -hy; cellBounds_(3) = hy; cellBounds_(4) = -hz; cellBounds_(5) = hz; for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (parameters[k] > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); } } } } // does an input node's kernel intersect bonds on this processor bool KernelFunctionCell::node_contributes(DENS_VEC node) const { DENS_VEC ghostNode = node; bool contributes = true; bool ghostContributes = lammpsInterface_->nperiodic(); double kernel_bounds[2][3]; lammpsInterface_->sub_bounds(kernel_bounds[0][0],kernel_bounds[1][0], kernel_bounds[0][1],kernel_bounds[1][1], kernel_bounds[0][2],kernel_bounds[1][2]); for (int i=0; i<3; ++i) { kernel_bounds[0][i] -= (cellBounds_(i*2+1) + lammpsInterface_->pair_cutoff()); kernel_bounds[1][i] += (cellBounds_(i*2+1) + lammpsInterface_->pair_cutoff()); contributes = contributes && (node(i) >= kernel_bounds[0][i]) && (node(i) < kernel_bounds[1][i]); if (periodicity[i]) { if (node[i] <= box_bounds[0][i] + box_length[i]/2) { ghostNode[i] += box_length[i]; } else { ghostNode[i] -= box_length[i]; } ghostContributes = ghostContributes && ((ghostNode(i) >= kernel_bounds[0][i]) || (node(i) >= kernel_bounds[0][i])) && ((ghostNode(i) < kernel_bounds[1][i]) || (node(i) < kernel_bounds[1][i])); } if (!(contributes || ghostContributes)) break; } return true; } bool KernelFunctionCell::in_support(DENS_VEC dx) const { if (dx(0) < cellBounds_(0) || dx(0) > cellBounds_(1) || dx(1) < cellBounds_(2) || dx(1) > cellBounds_(3) || dx(2) < cellBounds_(4) || dx(2) > cellBounds_(5) ) return false; return true; } // function value double KernelFunctionCell::value(DENS_VEC& x_atom) const { if ((cellBounds_(0) <= x_atom(0)) && (x_atom(0) < cellBounds_(1)) && (cellBounds_(2) <= x_atom(1)) && (x_atom(1) < cellBounds_(3)) && (cellBounds_(4) <= x_atom(2)) && (x_atom(2) < cellBounds_(5))) { return 1.0; } else { return 0.0; } } // function derivative value void KernelFunctionCell::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } // bond intercept values for rectangular region : origin is the node position void KernelFunctionCell::bond_intercepts(DENS_VEC& xa, DENS_VEC& xb, double &lam1, double &lam2) const { lam1 = 0.0; // start lam2 = 1.0; // end bool a_in = (value(xa) > 0.0); bool b_in = (value(xb) > 0.0); // (1) both in, no intersection needed if (a_in && b_in) { return; } // (2) for one in & one out -> one plane intersection // determine the points of intersection between the line joining // atoms a and b and the bounding planes of the localization volume else if (a_in || b_in) { DENS_VEC xab = xa - xb; for (int i = 0; i < nsd_; i++) { // check if segment is parallel to face if (fabs(xab(i)) > tol) { for (int j = 0; j < 2; j++) { double s = (cellBounds_(2*i+j) - xb(i))/xab(i); // check if between a & b if (s >= 0 && s <= 1) { bool in_bounds = false; DENS_VEC x = xb + s*xab; if (i == 0) { if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { in_bounds = true; } } else if (i == 1) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { in_bounds = true; } } else if (i == 2) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { in_bounds = true; } } if (in_bounds) { if (a_in) { lam1 = s;} else { lam2 = s;} return; } } } } } throw ATC_Error("logic failure in HardyKernel Cell for single intersection\n"); } // (3) both out -> corner intersection else { lam2 = lam1; // default to no intersection DENS_VEC xab = xa - xb; double ss[6] = {-1,-1,-1,-1,-1,-1}; int is = 0; for (int i = 0; i < nsd_; i++) { // check if segment is parallel to face if (fabs(xab(i)) > tol) { for (int j = 0; j < 2; j++) { double s = (cellBounds_(2*i+j) - xb(i))/xab(i); // check if between a & b if (s >= 0 && s <= 1) { // check if in face DENS_VEC x = xb + s*xab; if (i == 0) { if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { ss[is++] = s; } } else if (i == 1) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { ss[is++] = s; } } else if (i == 2) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { ss[is++] = s; } } } } } } if (is == 1) { // intersection occurs at a box edge - leave lam1 = lam2 } else if (is == 2) { lam1 = min(ss[0],ss[1]); lam2 = max(ss[0],ss[1]); } else if (is == 3) { // intersection occurs at a box vertex - leave lam1 = lam2 } else { if (is != 0) throw ATC_Error("logic failure in HardyKernel Cell for corner intersection\n"); } } } //------------------------------------------------------------------------ // constructor KernelFunctionCubicSphere::KernelFunctionCubicSphere (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionCubicSphere::value(DENS_VEC& x_atom) const { double r=x_atom.norm(); double rn=r/Rc_; if (rn < 1.0) { return 5.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicSphere::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticSphere::KernelFunctionQuarticSphere (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionQuarticSphere::value(DENS_VEC& x_atom) const { double r=x_atom.norm(); double rn=r/Rc_; if (rn < 1.0) { return 35.0/8.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticSphere::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionCubicCyl::KernelFunctionCubicCyl (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { nsd_ = 2; double Lz = box_length[2]; invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionCubicCyl::value(DENS_VEC& x_atom) const { double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); double rn=r/Rc_; if (rn < 1.0) { return 10.0/3.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicCyl::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticCyl::KernelFunctionQuarticCyl (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { nsd_ = 2; double Lz = box_length[2]; invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionQuarticCyl::value(DENS_VEC& x_atom) const { double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); double rn=r/Rc_; if (rn < 1.0) { return 3.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticCyl::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionCubicBar::KernelFunctionCubicBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the x(0) direction nsd_ = 1; double Ly = box_length[1]; double Lz = box_length[2]; invVol_ = 1.0/(2*Rc_*Ly*Lz); if ((bool) periodicity[0]) { if (Rc_ > 0.5*box_length[0]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionCubicBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(0)); double rn=r/Rc_; if (rn < 1.0) { return 2.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionLinearBar::KernelFunctionLinearBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the z(0) direction double Lx = box_length[0]; double Ly = box_length[1]; invVol_ = 1.0/(Lx*Ly*Rc_); if ((bool) periodicity[2]) { if (Rc_ > 0.5*box_length[2]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionLinearBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(2)); double rn=r/Rc_; if (rn < 1.0) { return 1.0-rn; } else { return 0.0; } } // function derivative value void KernelFunctionLinearBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); deriv(0) = 0.0; deriv(1) = 0.0; double r=abs(x_atom(2)); double rn=r/Rc_; if (rn < 1.0 && x_atom(2) <= 0.0) { deriv(2) = 1.0/Rc_; } else if (rn < 1.0 && x_atom(2) > 0.0) { deriv(2) = -1.0/Rc_; } else { deriv(2) = 0.0; } } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticBar::KernelFunctionQuarticBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the x(0) direction nsd_ = 1; double Ly = box_length[1]; double Lz = box_length[2]; invVol_ = 1.0/(2*Rc_*Ly*Lz); if ((bool) periodicity[0]) { if (Rc_ > 0.5*box_length[0]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionQuarticBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(0)); double rn=r/Rc_; // if (rn < 1.0) { return 5.0/2.0*(1.0-6*rn*rn+8*rn*rn*rn-3*rn*rn*rn*rn); } - alternative quartic if (rn < 1.0) { return 15.0/8.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } };