forked from lijiext/lammps
736 lines
28 KiB
C++
736 lines
28 KiB
C++
#include "ChargeRegulator.h"
|
|
#include "PoissonSolver.h"
|
|
#include "LammpsInterface.h"
|
|
#include "ATC_Coupling.h"
|
|
#include "ATC_Error.h"
|
|
|
|
#include "Function.h"
|
|
#include "PrescribedDataManager.h"
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <set>
|
|
|
|
using ATC_Utility::to_string;
|
|
using std::stringstream;
|
|
using std::map;
|
|
using std::vector;
|
|
using std::set;
|
|
using std::pair;
|
|
using std::string;
|
|
|
|
|
|
namespace ATC {
|
|
|
|
//========================================================
|
|
// Class ChargeRegulator
|
|
//========================================================
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ChargeRegulator::ChargeRegulator(ATC_Coupling * atc) :
|
|
AtomicRegulator(atc)
|
|
{
|
|
// do nothing
|
|
}
|
|
//--------------------------------------------------------
|
|
// Destructor
|
|
//--------------------------------------------------------
|
|
ChargeRegulator::~ChargeRegulator()
|
|
{
|
|
map<string,ChargeRegulatorMethod *>::iterator it;
|
|
for (it = regulators_.begin(); it != regulators_.end(); it++) {
|
|
if (it->second) delete it->second;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------
|
|
// modify:
|
|
// parses and adjusts charge regulator state based on
|
|
// user input, in the style of LAMMPS user input
|
|
//--------------------------------------------------------
|
|
bool ChargeRegulator::modify(int /* narg */, char ** /* arg */)
|
|
{
|
|
bool foundMatch = false;
|
|
return foundMatch;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// construct methods
|
|
//--------------------------------------------------------
|
|
void ChargeRegulator::construct_methods()
|
|
{
|
|
AtomicRegulator::construct_methods();
|
|
|
|
if (atc_->reset_methods()) {
|
|
// eliminate existing methods
|
|
delete_method();
|
|
// consruct new ones
|
|
map<string, ChargeRegulatorParameters>::iterator itr;
|
|
for (itr = parameters_.begin();
|
|
itr != parameters_.end(); itr++) {
|
|
string tag = itr->first;
|
|
if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag];
|
|
ChargeRegulatorParameters & p = itr->second;
|
|
LammpsInterface * lammpsInterface = LammpsInterface::instance();
|
|
p.groupBit = lammpsInterface->group_bit(tag);
|
|
if (! p.groupBit)
|
|
throw ATC_Error("ChargeRegulator::initialize group not found");
|
|
switch (p.method) {
|
|
case NONE: {
|
|
regulators_[tag] = new ChargeRegulatorMethod(this,p);
|
|
break;
|
|
}
|
|
case FEEDBACK: {
|
|
regulators_[tag] = new ChargeRegulatorMethodFeedback(this,p);
|
|
break;
|
|
}
|
|
case IMAGE_CHARGE: {
|
|
regulators_[tag] = new ChargeRegulatorMethodImageCharge(this,p);
|
|
break;
|
|
}
|
|
case EFFECTIVE_CHARGE: {
|
|
regulators_[tag] = new ChargeRegulatorMethodEffectiveCharge(this,p);
|
|
break;
|
|
}
|
|
default:
|
|
throw ATC_Error("ChargeRegulator::construct_method unknown charge regulator type");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// initialize:
|
|
//--------------------------------------------------------
|
|
void ChargeRegulator::initialize()
|
|
{
|
|
|
|
|
|
map<string, ChargeRegulatorMethod *>::iterator itr;
|
|
for (itr = regulators_.begin();
|
|
itr != regulators_.end(); itr++) { itr->second->initialize(); }
|
|
|
|
atc_->set_boundary_integration_type(boundaryIntegrationType_);
|
|
AtomicRegulator::reset_nlocal();
|
|
AtomicRegulator::delete_unused_data();
|
|
needReset_ = false;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply pre force
|
|
//--------------------------------------------------------
|
|
void ChargeRegulator::apply_pre_force(double dt)
|
|
{
|
|
map<string, ChargeRegulatorMethod *>::iterator itr;
|
|
for (itr = regulators_.begin();
|
|
itr != regulators_.end(); itr++) { itr->second->apply_pre_force(dt);}
|
|
}
|
|
//--------------------------------------------------------
|
|
// apply post force
|
|
//--------------------------------------------------------
|
|
void ChargeRegulator::apply_post_force(double dt)
|
|
{
|
|
map<string, ChargeRegulatorMethod *>::iterator itr;
|
|
for (itr = regulators_.begin();
|
|
itr != regulators_.end(); itr++) { itr->second->apply_post_force(dt);}
|
|
}
|
|
//--------------------------------------------------------
|
|
// output
|
|
//--------------------------------------------------------
|
|
void ChargeRegulator::output(OUTPUT_LIST & outputData) const
|
|
{
|
|
map<string, ChargeRegulatorMethod *>::const_iterator itr;
|
|
for (itr = regulators_.begin();
|
|
itr != regulators_.end(); itr++) { itr->second->output(outputData);}
|
|
}
|
|
|
|
//========================================================
|
|
// Class ChargeRegulatorMethod
|
|
//========================================================
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and ChargeRegulator
|
|
//--------------------------------------------------------
|
|
ChargeRegulatorMethod::ChargeRegulatorMethod
|
|
(ChargeRegulator *chargeRegulator,
|
|
ChargeRegulator::ChargeRegulatorParameters & p)
|
|
: RegulatorShapeFunction(chargeRegulator),
|
|
chargeRegulator_(chargeRegulator),
|
|
lammpsInterface_(LammpsInterface::instance()),
|
|
rC_(0), rCsq_(0),
|
|
targetValue_(NULL),
|
|
targetPhi_(p.value),
|
|
surface_(p.faceset),
|
|
atomGroupBit_(p.groupBit),
|
|
boundary_(false),
|
|
depth_(p.depth),
|
|
surfaceType_(p.surfaceType),
|
|
permittivity_(p.permittivity),
|
|
initialized_(false)
|
|
{
|
|
const FE_Mesh * feMesh = atc_->fe_engine()->fe_mesh();
|
|
feMesh->faceset_to_nodeset(surface_,nodes_);
|
|
// assume flat get normal and primary coord
|
|
PAIR face = *(surface_.begin());
|
|
normal_.reset(nsd_);
|
|
feMesh->face_normal(face,0,normal_);
|
|
DENS_MAT faceCoords;
|
|
feMesh->face_coordinates(face,faceCoords);
|
|
point_.reset(nsd_);
|
|
for (int i=0; i < nsd_; i++) { point_(i) = faceCoords(i,0); }
|
|
#ifdef ATC_VERBOSE
|
|
stringstream ss; ss << "point: (" << point_(0) << "," << point_(1) << "," << point_(2) << ") normal: (" << normal_(0) << "," << normal_(1) << "," << normal_(2) << ") depth: " << depth_;
|
|
lammpsInterface_->print_msg_once(ss.str());
|
|
#endif
|
|
sum_.reset(nsd_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// Initialize
|
|
|
|
//--------------------------------------------------------
|
|
|
|
|
|
// nomenclature might be a bit backwark: control --> nodes that exert the control, & influence --> atoms that feel the influence
|
|
void ChargeRegulatorMethod::initialize(void)
|
|
{
|
|
interscaleManager_ = &(atc_->interscale_manager());
|
|
|
|
poissonSolver_ =chargeRegulator_->poisson_solver();
|
|
if (! poissonSolver_) throw ATC_Error("need a poisson solver to initialize charge regulator");
|
|
|
|
// atomic vectors
|
|
// nodal information
|
|
nNodes_ = atc_->num_nodes();
|
|
// constants
|
|
rC_ = lammpsInterface_->pair_cutoff();
|
|
rCsq_ = rC_*rC_;
|
|
qV2e_ = lammpsInterface_->qv2e();
|
|
qqrd2e_ = lammpsInterface_->qqrd2e();
|
|
|
|
// note derived method set initialized to true
|
|
}
|
|
|
|
|
|
int ChargeRegulatorMethod::nlocal() { return atc_->nlocal(); }
|
|
|
|
void ChargeRegulatorMethod::set_greens_functions(void)
|
|
{
|
|
// set up Green's function per node
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
set<int> localNodes;
|
|
for (int j = 0; j < nNodes_; j++)
|
|
localNodes.insert(j);
|
|
// call Poisson solver to get Green's function for node i
|
|
DENS_VEC globalGreensFunction;
|
|
poissonSolver_->greens_function(i,globalGreensFunction);
|
|
// store green's functions as sparse vectors only on local nodes
|
|
set<int>::const_iterator thisNode;
|
|
SparseVector<double> sparseGreensFunction(nNodes_);
|
|
for (thisNode = localNodes.begin(); thisNode != localNodes.end(); thisNode++)
|
|
sparseGreensFunction(*thisNode) = globalGreensFunction(*thisNode);
|
|
greensFunctions_.push_back(sparseGreensFunction);
|
|
}
|
|
}
|
|
//--------------------------------------------------------
|
|
// output
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethod::output(OUTPUT_LIST & /* outputData */)
|
|
{
|
|
//vector<double> localSum(sum_.size());
|
|
//lammpsInteface_->allsum(localSum.pointer,sum_.pointer,sum_.size());
|
|
DENS_VEC localSum(sum_.size());
|
|
lammpsInterface_->allsum(localSum.ptr(),sum_.ptr(),sum_.size());
|
|
for (int i = 0; i < sum_.size(); i++) {
|
|
string name = "charge_regulator_influence_"+to_string(i);
|
|
// atc_->fe_engine()->add_global(name,sum_[i]);
|
|
}
|
|
}
|
|
|
|
//========================================================
|
|
// Class ChargeRegulatorMethodFeedback
|
|
//========================================================
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ChargeRegulatorMethodFeedback::ChargeRegulatorMethodFeedback
|
|
(ChargeRegulator *chargeRegulator,
|
|
ChargeRegulator::ChargeRegulatorParameters & p)
|
|
: ChargeRegulatorMethod (chargeRegulator, p),
|
|
controlNodes_(nodes_),
|
|
influenceGroupBit_(p.groupBit)
|
|
{
|
|
nControlNodes_ = controlNodes_.size();
|
|
sum_.resize(1);
|
|
}
|
|
//--------------------------------------------------------
|
|
// Initialize
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::initialize(void)
|
|
{
|
|
ChargeRegulatorMethod::initialize();
|
|
if (surfaceType_ != ChargeRegulator::CONDUCTOR)
|
|
throw ATC_Error("currently charge feedback can only mimic a conductor");
|
|
set_influence();
|
|
set_influence_matrix();
|
|
initialized_ = true;
|
|
}
|
|
//--------------------------------------------------------
|
|
// Initialize
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::construct_transfers(void)
|
|
{
|
|
ChargeRegulatorMethod::construct_transfers();
|
|
|
|
InterscaleManager & interscaleManager((atomicRegulator_->atc_transfer())->interscale_manager());
|
|
PerAtomSparseMatrix<double> * atomShapeFunctions = interscaleManager.per_atom_sparse_matrix("InterpolantGhost");
|
|
if (!atomShapeFunctions) {
|
|
atomShapeFunctions = new PerAtomShapeFunction(atomicRegulator_->atc_transfer(),
|
|
interscaleManager.per_atom_quantity("AtomicGhostCoarseGrainingPositions"),
|
|
interscaleManager.per_atom_int_quantity("AtomGhostElement"),
|
|
GHOST);
|
|
interscaleManager.add_per_atom_sparse_matrix(atomShapeFunctions,"InterpolantGhost");
|
|
}
|
|
}
|
|
//--------------------------------------------------------
|
|
// find measurement atoms and nodes
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::set_influence(void)
|
|
{
|
|
|
|
// get nodes that overlap influence atoms & compact list of influence atoms
|
|
boundary_ =
|
|
atc_->nodal_influence(influenceGroupBit_,influenceNodes_,influenceAtoms_);
|
|
nInfluenceAtoms_ = influenceAtoms_.size(); // local
|
|
nInfluenceNodes_ = influenceNodes_.size(); // global
|
|
stringstream ss; ss << "control nodes: " << nControlNodes_ << " influence nodes: " << nInfluenceNodes_ << " local influence atoms: " << nInfluenceAtoms_ ;
|
|
lammpsInterface_->print_msg(ss.str());
|
|
if (nInfluenceNodes_ == 0) throw ATC_Error("no influence nodes");
|
|
|
|
const Array<int> & map = (boundary_) ? atc_->ghost_to_atom_map() : atc_->internal_to_atom_map();
|
|
for (set<int>::const_iterator itr = influenceAtoms_.begin(); itr != influenceAtoms_.end(); itr++) {
|
|
influenceAtomsIds_.insert(map(*itr));
|
|
}
|
|
}
|
|
//--------------------------------------------------------
|
|
// constuct a Green's submatrix
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::set_influence_matrix(void)
|
|
{
|
|
// construct control-influence matrix bar{G}^-1: ds{p} = G{p,m}^-1 dphi{m}
|
|
|
|
|
|
//
|
|
if (nInfluenceNodes_ < nControlNodes_) throw ATC_Error(" least square not implemented ");
|
|
if (nInfluenceNodes_ > nControlNodes_) throw ATC_Error(" solve not possible ");
|
|
DENS_MAT G(nInfluenceNodes_,nControlNodes_);
|
|
DENS_VEC G_I;
|
|
set<int>::const_iterator itr,itr2,itr3;
|
|
const Array<int> & nmap = atc_->fe_engine()->fe_mesh()->global_to_unique_map();
|
|
int i = 0;
|
|
for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) {
|
|
poissonSolver_->greens_function(*itr, G_I);
|
|
int j = 0;
|
|
for (itr2 = controlNodes_.begin(); itr2 != controlNodes_.end(); itr2++) {
|
|
int jnode = nmap(*itr2);
|
|
G(i,j++) = G_I(jnode);
|
|
}
|
|
i++;
|
|
}
|
|
invG_ = inv(G);
|
|
|
|
// construct the prolong-restrict projector N N^T for influence nodes only
|
|
|
|
InterscaleManager & interscaleManager(atc_->interscale_manager());
|
|
const SPAR_MAT & N_Ia = (boundary_) ?
|
|
(interscaleManager.per_atom_sparse_matrix("InterpolantGhost"))->quantity():
|
|
(interscaleManager.per_atom_sparse_matrix("Interpolant"))->quantity();
|
|
NT_.reset(nInfluenceAtoms_,nInfluenceNodes_);
|
|
DENS_MAT NNT(nInfluenceNodes_,nInfluenceNodes_);
|
|
int k = 0;
|
|
for (itr3 = influenceAtoms_.begin(); itr3 != influenceAtoms_.end(); itr3++) {
|
|
int katom = *itr3;
|
|
int i = 0;
|
|
for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) {
|
|
int Inode = *itr;
|
|
int j = 0;
|
|
NT_(k,i) = N_Ia(katom,Inode);
|
|
for (itr2 = influenceNodes_.begin(); itr2 != influenceNodes_.end(); itr2++) {
|
|
int Jnode = *itr2;
|
|
NNT(i,j++) += N_Ia(katom,Inode)*N_Ia(katom,Jnode);
|
|
}
|
|
i++;
|
|
}
|
|
k++;
|
|
}
|
|
// swap contributions across processors
|
|
DENS_MAT localNNT = NNT;
|
|
int count = NNT.nRows()*NNT.nCols();
|
|
lammpsInterface_->allsum(localNNT.ptr(),NNT.ptr(),count);
|
|
invNNT_ = inv(NNT);
|
|
|
|
// total influence matrix
|
|
if (nInfluenceAtoms_ > 0) { NTinvNNTinvG_ = NT_*invNNT_*invG_; }
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// change potential/charge pre-force calculation
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::apply_pre_force(double /* dt */)
|
|
{
|
|
|
|
sum_ = 0;
|
|
if (nInfluenceAtoms_ == 0) return; // nothing to do
|
|
apply_feedback_charges();
|
|
}
|
|
//--------------------------------------------------------
|
|
// apply feedback charges to atoms
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodFeedback::apply_feedback_charges()
|
|
{
|
|
double * q = lammpsInterface_->atom_charge();
|
|
// calculate error in potential on the control nodes
|
|
|
|
const DENS_MAT & phiField = (atc_->field(ELECTRIC_POTENTIAL)).quantity();
|
|
DENS_MAT dphi(nControlNodes_,1);
|
|
int i = 0;
|
|
set<int>::const_iterator itr;
|
|
for (itr = controlNodes_.begin(); itr != controlNodes_.end(); itr++) {
|
|
dphi(i++,0) = targetPhi_ - phiField(*itr,0);
|
|
}
|
|
|
|
// construct the atomic charges consistent with the correction
|
|
DENS_MAT dq = NTinvNNTinvG_*dphi;
|
|
i = 0;
|
|
for (itr = influenceAtomsIds_.begin(); itr != influenceAtomsIds_.end(); itr++) {
|
|
sum_(0) += dq(i,0);
|
|
q[*itr] += dq(i++,0);
|
|
}
|
|
|
|
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE))->force_reset();
|
|
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE, GHOST))->force_reset();
|
|
}
|
|
|
|
//========================================================
|
|
// Class ChargeRegulatorMethodImageCharge
|
|
//========================================================
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ChargeRegulatorMethodImageCharge::ChargeRegulatorMethodImageCharge
|
|
(ChargeRegulator *chargeRegulator,
|
|
ChargeRegulator::ChargeRegulatorParameters & p)
|
|
: ChargeRegulatorMethod (chargeRegulator, p),
|
|
imageNodes_(nodes_)
|
|
{
|
|
}
|
|
//--------------------------------------------------------
|
|
// Initialize
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodImageCharge::initialize(void)
|
|
{
|
|
ChargeRegulatorMethod::initialize();
|
|
if (surfaceType_ != ChargeRegulator::DIELECTRIC) throw ATC_Error("currently image charge can only mimic a dielectric");
|
|
double eps1 = permittivity_;// dielectric
|
|
double eps2 = lammpsInterface_->dielectric();// ambient
|
|
permittivityRatio_ = (eps2-eps1)/(eps2+eps1);
|
|
#ifdef ATC_VERBOSE
|
|
stringstream ss; ss << "permittivity ratio: " << permittivityRatio_;
|
|
lammpsInterface_->print_msg_once(ss.str());
|
|
#endif
|
|
set_greens_functions();
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////
|
|
initialized_ = true;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// change potential/charge post-force calculation
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodImageCharge::apply_post_force(double /* dt */)
|
|
{
|
|
sum_ = 0;
|
|
apply_local_forces();
|
|
|
|
//correct_forces();
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply local coulomb forces
|
|
// -- due to image charges
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodImageCharge::apply_local_forces()
|
|
{
|
|
|
|
int inum = lammpsInterface_->neighbor_list_inum();
|
|
int * ilist = lammpsInterface_->neighbor_list_ilist();
|
|
int * numneigh = lammpsInterface_->neighbor_list_numneigh();
|
|
int ** firstneigh = lammpsInterface_->neighbor_list_firstneigh();
|
|
|
|
const int *mask = lammpsInterface_->atom_mask();
|
|
///..............................................
|
|
double ** x = lammpsInterface_->xatom();
|
|
double ** f = lammpsInterface_->fatom();
|
|
double * q = lammpsInterface_->atom_charge();
|
|
|
|
// loop over neighbor list
|
|
for (int ii = 0; ii < inum; ii++) {
|
|
int i = ilist[ii];
|
|
double qi = q[i];
|
|
if ((mask[i] & atomGroupBit_) && qi != 0.) {
|
|
double* fi = f[i];
|
|
DENS_VEC xi(x[i],nsd_);
|
|
// distance to surface
|
|
double dn = reflect(xi);
|
|
// all ions near the interface/wall
|
|
// (a) self image
|
|
if (dn < rC_) { // close enough to wall to have explicit image charges
|
|
double factor_coul = 1;
|
|
double dx = 2.*dn; // distance to image charge
|
|
double fn = factor_coul*qi*qi*permittivityRatio_/dx;
|
|
fi[0] += fn*normal_[0];
|
|
fi[1] += fn*normal_[1];
|
|
fi[2] += fn*normal_[2];
|
|
sum_ += fn*normal_;
|
|
// (b) neighbor images
|
|
int * jlist = firstneigh[i];
|
|
int jnum = numneigh[i];
|
|
for (int jj = 0; jj < jnum; jj++) {
|
|
int j = jlist[jj];
|
|
// this changes j
|
|
double factor_coul = lammpsInterface_->coulomb_factor(j);
|
|
double qj = q[j];
|
|
if (qj != 0.) { // all charged neighbors
|
|
DENS_VEC xj(x[j],nsd_);
|
|
dn = reflect(xj);
|
|
DENS_VEC dx = xi-xj;
|
|
double r2 = dx.norm_sq();
|
|
// neighbor image j' inside cutoff from i
|
|
if (r2 < rCsq_) {
|
|
double fm = factor_coul*qi*qj*permittivityRatio_/r2;
|
|
fi[0] += fm*dx(0);
|
|
fi[1] += fm*dx(1);
|
|
fi[2] += fm*dx(2);
|
|
sum_ += fm*dx;
|
|
}
|
|
}
|
|
}
|
|
} // end i < rC if
|
|
}
|
|
}
|
|
// update managed data
|
|
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_FORCE))->force_reset();
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// correct charge densities
|
|
// - to reflect image charges
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodImageCharge::correct_charge_densities()
|
|
{
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------
|
|
// correct_forces
|
|
// - due to image charge density used in short-range solution
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodImageCharge::correct_forces()
|
|
{
|
|
}
|
|
|
|
|
|
//========================================================
|
|
// Class ChargeRegulatorMethodEffectiveCharge
|
|
//========================================================
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
|
|
ChargeRegulatorMethodEffectiveCharge::ChargeRegulatorMethodEffectiveCharge(
|
|
ChargeRegulator *chargeRegulator,
|
|
ChargeRegulator::ChargeRegulatorParameters & p)
|
|
: ChargeRegulatorMethod (chargeRegulator, p),
|
|
chargeDensity_(p.value),
|
|
useSlab_(false)
|
|
{
|
|
}
|
|
//--------------------------------------------------------
|
|
// add_charged_surface
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodEffectiveCharge::initialize( )
|
|
{
|
|
ChargeRegulatorMethod::initialize();
|
|
boundary_ = atc_->is_ghost_group(atomGroupBit_);
|
|
// set face sources to all point at unit function for use in integration
|
|
SURFACE_SOURCE faceSources;
|
|
map<PAIR, Array<XT_Function*> > & fs(faceSources[ELECTRIC_POTENTIAL]);
|
|
XT_Function * f = XT_Function_Mgr::instance()->constant_function(1.);
|
|
set< PAIR >::const_iterator fsItr;
|
|
for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) {
|
|
Array < XT_Function * > & dof = fs[*fsItr];
|
|
dof.reset(1);
|
|
dof(0) = f;
|
|
}
|
|
|
|
// computed integrals of nodal shape functions on face
|
|
FIELDS nodalFaceWeights;
|
|
Array<bool> fieldMask(NUM_FIELDS); fieldMask(ELECTRIC_POTENTIAL) = true;
|
|
(atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,nodalFaceWeights);
|
|
const DENS_MAT & w = (nodalFaceWeights[ELECTRIC_POTENTIAL].quantity());
|
|
|
|
// Get coordinates of each node in face set
|
|
for (set<int>::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) {
|
|
DENS_VEC x = atc_->fe_engine()->fe_mesh()->nodal_coordinates(*n);
|
|
// compute effective charge at each node I
|
|
// multiply charge density by integral of N_I over face
|
|
double v = w(*n,0)*chargeDensity_;
|
|
pair<DENS_VEC,double> p(x,v);
|
|
nodeXFMap_[*n] = p;
|
|
}
|
|
|
|
// set up data structure holding charged faceset information
|
|
FIELDS sources;
|
|
double k = lammpsInterface_->coulomb_constant();
|
|
string fname = "radial_power";
|
|
double xtArgs[8];
|
|
xtArgs[0] = 0; xtArgs[1] = 0; xtArgs[2] = 0;
|
|
xtArgs[3] = 1; xtArgs[4] = 1; xtArgs[5] = 1;
|
|
xtArgs[6] = k*chargeDensity_;
|
|
xtArgs[7] = -1.;
|
|
const DENS_MAT & s(sources[ELECTRIC_POTENTIAL].quantity());
|
|
NODE_TO_XF_MAP::iterator XFitr;
|
|
for (XFitr = nodeXFMap_.begin(); XFitr != nodeXFMap_.end(); XFitr++) {
|
|
// evaluate voltage at each node I
|
|
// set up X_T function for integration: k*chargeDensity_/||x_I - x_s||
|
|
// integral is approximated in two parts:
|
|
// 1) near part with all faces within r < rcrit evaluated as 2 * pi * rcrit * k sigma A/A0, A is area of this region and A0 = pi * rcrit^2, so 2 k sigma A / rcrit
|
|
// 2) far part evaluated using Gaussian quadrature on faceset
|
|
DENS_VEC x((XFitr->second).first);
|
|
xtArgs[0] = x(0); xtArgs[1] = x(1); xtArgs[2] = x(2);
|
|
f = XT_Function_Mgr::instance()->function(fname,8,xtArgs);
|
|
for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) {
|
|
fs[*fsItr] = f;
|
|
}
|
|
|
|
// perform integration to get quantities at nodes on facesets
|
|
// V_J' = int_S N_J k*sigma/|x_I - x_s| dS
|
|
(atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,sources);
|
|
|
|
// sum over all nodes in faceset to get total potential:
|
|
// V_I = sum_J VJ'
|
|
int node = XFitr->first;
|
|
nodalChargePotential_[node] = s(node,0);
|
|
double totalPotential = 0.;
|
|
for (set<int>::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) {
|
|
totalPotential += s(*n,0); }
|
|
|
|
// assign an XT function per each node and
|
|
// then call the prescribed data manager and fix each node individually.
|
|
f = XT_Function_Mgr::instance()->constant_function(totalPotential);
|
|
(atc_->prescribed_data_manager())->fix_field(node,ELECTRIC_POTENTIAL,0,f);
|
|
}
|
|
initialized_ = true;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add effective forces post LAMMPS force call
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodEffectiveCharge::apply_post_force(double /* dt */)
|
|
{
|
|
apply_local_forces();
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_charged_surfaces
|
|
//--------------------------------------------------------
|
|
void ChargeRegulatorMethodEffectiveCharge::apply_local_forces()
|
|
{
|
|
double * q = lammpsInterface_->atom_charge();
|
|
_atomElectricalForce_.resize(nlocal(),nsd_);
|
|
|
|
double penalty = poissonSolver_->penalty_coefficient();
|
|
if (penalty <= 0.0) throw ATC_Error("ExtrinsicModelElectrostatic::apply_charged_surfaces expecting non zero penalty");
|
|
|
|
double dx[3];
|
|
const DENS_MAT & xa((interscaleManager_->per_atom_quantity("AtomicCoarseGrainingPositions"))->quantity());
|
|
|
|
// WORKSPACE - most are static
|
|
SparseVector<double> dv(nNodes_);
|
|
vector<SparseVector<double> > derivativeVectors;
|
|
derivativeVectors.reserve(nsd_);
|
|
const SPAR_MAT_VEC & shapeFunctionDerivatives((interscaleManager_->vector_sparse_matrix("InterpolateGradient"))->quantity());
|
|
|
|
DenseVector<INDEX> nodeIndices;
|
|
DENS_VEC nodeValues;
|
|
|
|
NODE_TO_XF_MAP::const_iterator inode;
|
|
for (inode = nodeXFMap_.begin(); inode != nodeXFMap_.end(); inode++) {
|
|
|
|
int node = inode->first;
|
|
DENS_VEC xI = (inode->second).first;
|
|
double qI = (inode->second).second;
|
|
double phiI = nodalChargePotential_[node];
|
|
for (int i = 0; i < nlocal(); i++) {
|
|
int atom = (atc_->internal_to_atom_map())(i);
|
|
double qa = q[atom];
|
|
if (qa != 0) {
|
|
double dxSq = 0.;
|
|
for (int j = 0; j < nsd_; j++) {
|
|
dx[j] = xa(i,j) - xI(j);
|
|
dxSq += dx[j]*dx[j];
|
|
}
|
|
if (dxSq < rCsq_) {
|
|
// first apply pairwise coulombic interaction
|
|
if (!useSlab_) {
|
|
double coulForce = qqrd2e_*qI*qa/(dxSq*sqrtf(dxSq));
|
|
for (int j = 0; j < nsd_; j++) {
|
|
_atomElectricalForce_(i,j) += dx[j]*coulForce; }
|
|
}
|
|
|
|
// second correct for FE potential induced by BCs
|
|
// determine shape function derivatives at atomic location
|
|
// and construct sparse vectors to store derivative data
|
|
|
|
|
|
for (int j = 0; j < nsd_; j++) {
|
|
shapeFunctionDerivatives[j]->row(i,nodeValues,nodeIndices);
|
|
derivativeVectors.push_back(dv);
|
|
for (int k = 0; k < nodeIndices.size(); k++) {
|
|
derivativeVectors[j](nodeIndices(k)) = nodeValues(k); }
|
|
}
|
|
|
|
// compute greens function from charge quadrature
|
|
|
|
SparseVector<double> shortFePotential(nNodes_);
|
|
shortFePotential.add_scaled(greensFunctions_[node],penalty*phiI);
|
|
|
|
// compute electric field induced by charge
|
|
DENS_VEC efield(nsd_);
|
|
for (int j = 0; j < nsd_; j++) {
|
|
efield(j) = -.1*dot(derivativeVectors[j],shortFePotential); }
|
|
|
|
// apply correction in atomic forces
|
|
double c = qV2e_*qa;
|
|
for (int j = 0; j < nsd_; j++) {
|
|
if ((!useSlab_) || (j==nsd_)) {
|
|
_atomElectricalForce_(i,j) -= c*efield(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}; // end namespace
|