lammps/lib/atc/ConcentrationRegulator.cpp

636 lines
22 KiB
C++

#include "ConcentrationRegulator.h"
#include "LammpsInterface.h"
#include "ATC_Coupling.h"
#include "ATC_Error.h"
using ATC_Utility::to_string;
using ATC_Utility::rnd;
using std::map;
using std::string;
using std::pair;
using std::min;
using std::max;
namespace ATC {
const double kMinScale_ = 10000.;
//========================================================
// Class ConcentrationRegulator
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ConcentrationRegulator::ConcentrationRegulator(ATC_Coupling * atc) :
AtomicRegulator(atc)
{
// do nothing
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ConcentrationRegulator::~ConcentrationRegulator()
{
if (regulators_.size()) {
map<string,ConcentrationRegulatorMethod *>::iterator it;
for (it = regulators_.begin(); it != regulators_.end(); ++it) {
delete it->second;
}
regulators_.clear();
}
if (parameters_.size()) parameters_.clear();
}
//--------------------------------------------------------
// modify:
// parses and adjusts charge regulator state based on
// user input, in the style of LAMMPS user input
//--------------------------------------------------------
bool ConcentrationRegulator::modify(int narg, char **arg)
{
bool foundMatch = false;
return foundMatch;
}
//--------------------------------------------------------
// construct_methods:
//--------------------------------------------------------
void ConcentrationRegulator::construct_methods()
{
AtomicRegulator::construct_methods();
if (atc_->reset_methods()) {
// eliminate existing methods
delete_method();
// consruct new ones
map<string, ConcentrationRegulatorParameters>::iterator itr;
for (itr = parameters_.begin();
itr != parameters_.end(); itr++) {
string tag = itr->first;
if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag];
ConcentrationRegulatorParameters & p = itr->second;
switch (p.method) {
case NONE: {
regulators_[tag] = new ConcentrationRegulatorMethod(this);
break;
}
case TRANSITION: {
p.type = atc_->tag_to_type(tag);
p.groupbit = LammpsInterface::instance()->type_to_groupbit(p.type);
p.transitionType = atc_->tag_to_type(p.transitionTag);
regulators_[tag] = new ConcentrationRegulatorMethodTransition(this,p);
break;
}
default:
throw ATC_Error("ConcentrationRegulator::initialize unknown concentration regulator type");
}
}
}
}
//--------------------------------------------------------
// initialize:
//--------------------------------------------------------
void ConcentrationRegulator::initialize()
{
map<string, ConcentrationRegulatorMethod *>::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;
}
//--------------------------------------------------------
// pre_exchange
//--------------------------------------------------------
void ConcentrationRegulator::pre_exchange()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->pre_exchange();}
}
//--------------------------------------------------------
// pre_force
//--------------------------------------------------------
void ConcentrationRegulator::pre_force()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->pre_force();}
}
//--------------------------------------------------------
// finish
//--------------------------------------------------------
void ConcentrationRegulator::finish()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->finish();}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ConcentrationRegulator::output(OUTPUT_LIST & outputData) const
{
map<string, ConcentrationRegulatorMethod *>::const_iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->output(outputData);}
}
//--------------------------------------------------------
// compute vector
//--------------------------------------------------------
double ConcentrationRegulator::compute_vector(int n) const
{
int s = regulators_.size();
if (s == 0) return 0;
int m = n / s;
n = n % s;
int c = 0;
map<string, ConcentrationRegulatorMethod *>::const_iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) {
if (c++ == n) { return itr->second->compute_vector(m); }
}
return 0.;
}
//--------------------------------------------------------
// size vector
//--------------------------------------------------------
int ConcentrationRegulator::size_vector(int i) const
{
int n = (regulators_.size())*5;
if (n==0) n = 20;
return n;
}
//========================================================
// Class ConcentrationRegulatorMethodTransition
//========================================================
//--------------------------------------------------------
// Constructor
// Grab references to ATC and ConcentrationRegulator
//--------------------------------------------------------
ConcentrationRegulatorMethodTransition::ConcentrationRegulatorMethodTransition
(ConcentrationRegulator *concReg,
ConcentrationRegulator::ConcentrationRegulatorParameters & p)
: ConcentrationRegulatorMethod(concReg),
concentrationRegulator_(concReg),
interscaleManager_(NULL),
lammpsInterface_(LammpsInterface::instance()),
list_(NULL),
targetConcentration_(p.value),
targetCount_(0),
elemset_(p.elemset),
p_(NULL),
randomNumberGenerator_(NULL),
q0_(0),
controlType_(p.type),
controlIndex_(0),
transitionType_(p.transitionType),
transitionInterval_(p.transitionInterval),
transitionCounter_(0),
nInTransition_(0),
transitionFactor_(0),
controlMask_(p.groupbit),
frequency_(p.frequency),
maxEnergy_(p.maxEnergy),
maxExchanges_(p.maxExchanges),
maxAttempts_(p.maxAttempts),
nexchanges_(0),
initialized_(false),
_rngUniformCounter_(0),
_rngNormalCounter_(0)
{
controlIndex_ = atc_->type_index(controlType_);
LammpsInterface * li = LammpsInterface::instance();
q0_ = li->type_to_charge(controlType_);
double kB = (li->boltz())/(li->mvv2e()); // E/T*m*v^2/E = m v^2/T
double m = li->atom_mass(controlType_);
sigma_ = sqrt(kB/m); // v / sqrt(T)
randomNumberGenerator_ = li->random_number_generator();
}
//--------------------------------------------------------
// Initialize
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::initialize(void)
{
#ifdef ATC_VERBOSE
lammpsInterface_->print_msg_once(
"\ncontrol type: "+to_string(controlType_)+
"\ntransistion type:"+to_string(transitionType_)+
"\ncontrol mask:"+to_string(controlMask_)+
"\nfrequency:"+to_string(frequency_)+
"\nmax exchanges:"+to_string(maxExchanges_)+
"\nmax attempts:"+to_string(maxAttempts_)+
"\nmax energy:"+to_string(maxEnergy_)
);
#endif
interscaleManager_ = &(atc_->interscale_manager());
PerAtomQuantity<int> * a2el = atc_->atom_to_element_map();
list_ = new AtomInElementSet(atc_,a2el,elemset_,controlType_);
nNodes_ = atc_->num_nodes();
DENS_MAT conc(nNodes_,1); conc = targetConcentration_;
DENS_VEC integral = atc_->fe_engine()->integrate(conc,elemset_);
targetCount_ = rnd(integral(0)) ;
volumes_.resize(elemset_.size());
ESET::const_iterator itr;
int i = 0;
DENS_MAT c(nNodes_,1); c = 1;
V_ = 0.;
for (itr = elemset_.begin(); itr != elemset_.end(); itr++, i++) {
ESET e; e.insert(*itr);
DENS_VEC v = atc_->fe_engine()->integrate(c,e);
volumes_(i) = v(0);
V_ += v(0);
}
volumes_ *= 1./V_;
for (int i = 1; i < volumes_.size(); i++) {
volumes_(i) += volumes_(i-1);
}
// record orginal energetic properties
int ntypes = lammpsInterface_->ntypes();
epsilon0_.reset(ntypes);
p_ = lammpsInterface_->potential();
lammpsInterface_->epsilons(controlType_,p_,epsilon0_.ptr());
#ifdef ATC_VERBOSE
string msg = "type "+to_string(controlType_)+" target count " + to_string(targetCount_);
msg += " volume " + to_string(V_);
msg += " current count " + to_string(count());
ATC::LammpsInterface::instance()->print_msg_once(msg);
msg = "WARNING: ensure neighboring happens at least every "+to_string(frequency_);
ATC::LammpsInterface::instance()->print_msg_once(msg);
#endif
}
double ConcentrationRegulatorMethodTransition::uniform() const {
_rngUniformCounter_++;
return lammpsInterface_->random_uniform(randomNumberGenerator_);
}
double ConcentrationRegulatorMethodTransition::normal() const {
_rngNormalCounter_++;
return lammpsInterface_->random_normal(randomNumberGenerator_);
}
//--------------------------------------------------------
// pre exchange
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pre_exchange(void)
{
// return if should not be called on this timestep
if ( ! lammpsInterface_->now(frequency_)) return;
nexchanges_ = excess();
int n = abs(nexchanges_);
bool success = false;
if (nexchanges_ > 0) { success = delete_atoms(n); }
else if (nexchanges_ < 0) { success = insert_atoms(n); }
else return;
if (!success) throw ATC_Error("insertions/deletions did not succeed");
if (nexchanges_ !=0) {
nInTransition_ = -nexchanges_;
lammpsInterface_->reset_ghosts(-nexchanges_);
atc_->reset_atoms();
}
transitionCounter_=0;
transition();
}
//--------------------------------------------------------
// pre force
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pre_force(void)
{
transition();
}
//--------------------------------------------------------
// accept
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::accept(double energy, double T) const
{
#ifdef ATC_VERBOSE2
if (energy < maxEnergy_) lammpsInterface_->print_msg(" energy "+to_string(energy)+" "+to_string(rngCounter_));
#endif
return (energy < maxEnergy_);
}
//--------------------------------------------------------
// energy
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::energy(int id) const
{
double e = lammpsInterface_->shortrange_energy(id,maxEnergy_);
#ifdef ATC_VERBOSE
{
int * tag = lammpsInterface_->atom_tag();
lammpsInterface_->print_msg(to_string(controlType_)+" deletion energy "+to_string(e)+" id "+to_string(tag[id])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_));
}
#endif
return e;
}
double ConcentrationRegulatorMethodTransition::energy(double * x) const
{
double e = lammpsInterface_->shortrange_energy(x,controlType_,maxEnergy_);
#ifdef ATC_VERBOSE
{
lammpsInterface_->print_msg(to_string(controlType_)+" insertion energy "+to_string(e)+" x "+to_string(x[0])+","+to_string(x[1])+","+to_string(x[2])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_));
}
#endif
return e;
}
//--------------------------------------------------------
// excess
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::excess(void) const
{
int nexcess = count()-targetCount_;
nexcess = max(min(nexcess,maxExchanges_),-maxExchanges_);
return nexcess;
}
//--------------------------------------------------------
// count
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::count(void) const
{
// integrate concentration over region
const DENS_MAT & c = (atc_->field(SPECIES_CONCENTRATION)).quantity();
DENS_VEC integral = atc_->fe_engine()->integrate(c,elemset_);
return rnd(integral(controlIndex_)) ;
}
//--------------------------------------------------------
// delete atoms
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::delete_atoms(int n)
{
ID_PAIR idPair;
deletionIds_.clear();
int deletions = 0;
int attempts = 0;
while(deletions < n && attempts < maxAttempts_){
if(accept(deletion_id(idPair))) {
deletionIds_.push_back(idPair);
deletions += 1;
}
deletions = lammpsInterface_->int_allmax(deletions);
attempts++;
}
ID_LIST::iterator itr;
for (itr = deletionIds_.begin(); itr != deletionIds_.end(); itr++) {
lammpsInterface_->delete_atom(itr->second);
}
#ifdef ATC_VERBOSE
string c = to_string(controlType_);
lammpsInterface_->all_print(attempts, c+"-attempts ");
lammpsInterface_->all_print(deletions,c+" deletions ");
lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal ");
// lammpsInterface_->all_print(uniform()," RANDOM ");
#endif
return (n == deletions); // success
}
//--------------------------------------------------------
// pick id
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::deletion_id(ID_PAIR & id) const
{
if (atc_->parallel_consistency()) return deletion_id_consistent(id);
else return deletion_id_free(id);
}
double ConcentrationRegulatorMethodTransition::deletion_id_consistent(ID_PAIR & id) const
{
id.first = -1;
id.second = -1;
int ntotal = lammpsInterface_->natoms();
double r = uniform();
r *= ntotal;
const ID_LIST & list = list_->quantity();
ID_LIST::const_iterator itr;
int i=0, idx = -1;
double min = ntotal;
int * tag = lammpsInterface_->atom_tag();
for (itr = list.begin(); itr != list.end(); itr++) {
int atag = tag[itr->second];
double d = abs(atag-r);
if (d < min) {
min = d;
idx = i;
}
i++;
}
int imin = kMinScale_*min;
if(imin == lammpsInterface_->int_allmin(imin)) {
if (idx < 0) throw ATC_Error("deletion_id failed to find a suitable atom");
id = list_->item(idx);
// avoid repeats
ID_LIST & l = list_->set_quantity();
l.erase(l.begin()+idx);
return energy(id.second);
}
else {
return maxEnergy_;
}
}
double ConcentrationRegulatorMethodTransition::deletion_id_free(ID_PAIR & id) const
{
id.first = -1;
id.second = -1;
int n = list_->size();
double nrank = lammpsInterface_->int_scansum(n);
int ntotal = lammpsInterface_->int_allsum(n);
if (ntotal == 0) throw ATC_Error("control type "+to_string(controlType_)+" is depleted");
double r = uniform();
r *= ntotal;
if ( (r >= nrank-n) && (r < nrank)) { // pick processor
r = uniform();
int idx = rnd(r*(n-1));
id = list_->item(idx);
// avoid repeats
ID_LIST & l = list_->set_quantity();
l.erase(l.begin()+idx);
return energy(id.second);
}
else {
return maxEnergy_;
}
}
//--------------------------------------------------------
// insert atoms
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::insert_atoms(int n)
{
insertionIds_.clear();
DENS_VEC x(3); x = 0;
DENS_VEC v(3); v = 0;
const DENS_MAN & T = atc_->field(TEMPERATURE);
int additions = 0;
int attempts = 0;
while(additions < n && attempts < maxAttempts_){
if(accept(insertion_location(x))) {
DENS_VEC Tv = atc_->fe_engine()->interpolate_field(x,T);
Tv(0) = 300.;
pick_velocity(v,Tv(0)); // 3 normal
int nlocal = lammpsInterface_->insert_atom(transitionType_,controlMask_,x.ptr(),v.ptr()); // no charge
insertionIds_.push_back(pair<int,int>(-1,nlocal)); // atc id unknown
additions += 1;
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg(">>> insert x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+" v:"+to_string(v(0))+" "+to_string(v(1))+" "+to_string(v(2))+" "+to_string(rngCounter_));
#endif
}
attempts++;
//lammpsInterface_->barrier();
additions = lammpsInterface_->int_allmax(additions);
#ifdef ATC_VERBOSE
{
string c = to_string(controlType_);
lammpsInterface_->all_print(_rngUniformCounter_,c+" rng-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" rng-normal ");
// lammpsInterface_->all_print(uniform()," random ");
}
#endif
if (atc_->parallel_consistency()) { sync_random_number_generators(); }
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg("attempts: "+to_string(attempts)+" additions "+to_string(additions)+" : "+to_string(rngCounter_));
#endif
#ifdef ATC_VERBOSE
{
string c = to_string(controlType_);
lammpsInterface_->all_print(attempts, c+"+attempts ");
lammpsInterface_->all_print(additions,c+" additions ");
lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal ");
// lammpsInterface_->all_print(uniform()," RANDOM ");
}
#endif
}
return (n == additions); // success
}
//--------------------------------------------------------
// sync random number generators
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::sync_random_number_generators() const
{
// normal
int n = lammpsInterface_->int_allmax(_rngNormalCounter_);
int dn = n - _rngNormalCounter_;
lammpsInterface_->advance_random_normal(randomNumberGenerator_,dn);
_rngNormalCounter_ = n;
// uniform
int u = lammpsInterface_->int_allmax(_rngUniformCounter_);
int du = u - _rngUniformCounter_;
lammpsInterface_->advance_random_uniform(randomNumberGenerator_,du);
_rngUniformCounter_ = u;
}
//--------------------------------------------------------
// pick location
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::insertion_location(DENS_VEC & x) const
{
// pick random element
int elem = pick_element(); // 1 uniform
// pick random local coordinate
DENS_VEC xi(3);
pick_coordinates(elem,xi,x); // 3 uniform
// if (! lammpsInterface_->in_box(x.ptr())) { throw ATC_Error("new atom is not in box");}
if (lammpsInterface_->in_my_processor_box(x.ptr())) {
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg(">>> insertion_location e:" +to_string(elem)+" xi:"+to_string(xi(0))+" "+to_string(xi(1))+" "+to_string(xi(2))+" x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+ " energy "+to_string(energy(x.ptr()))+" "+true_false(accept(energy(x.ptr())))+" "+to_string(rngUniformCounter_));
#endif
return energy(x.ptr());
}
else {
return maxEnergy_;
}
}
//--------------------------------------------------------
// pick element
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::pick_element() const
{
double r = uniform();
ESET::const_iterator itr = elemset_.begin(); // global?
for (int i = 0; i < volumes_.size() ; ++i) {
if (r < volumes_(i)) return *itr;
itr++;
}
return *itr;
}
//--------------------------------------------------------
// pick coordinates
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pick_coordinates(const int elem,
DENS_VEC & xi,
DENS_VEC & x) const
{
xi.reset(3);
xi(0) = 2.*uniform()-1.;
xi(1) = 2.*uniform()-1.;
xi(2) = 2.*uniform()-1.;
atc_->fe_engine()->fe_mesh()->position(elem,xi,x);
}
//--------------------------------------------------------
// pick velocity
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pick_velocity(DENS_VEC & v,double T) const
{
double s = sigma_*sqrt(T);
v(0) = s*normal();
v(1) = s*normal();
v(2) = s*normal();
//v = 0;
}
//--------------------------------------------------------
// transition
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::transition()
{
transitionCounter_++;
//if (insertionIds_.size() == 0) return; //
if (transitionCounter_> transitionInterval_) {
nInTransition_ = 0;
return;
}
else if (transitionCounter_==transitionInterval_) {
nInTransition_ -= lammpsInterface_->change_type(transitionType_,controlType_);
}
else {
transitionFactor_ = insertion_factor(transitionCounter_);
if (nInTransition_ < 0) transitionFactor_ = 1-transitionFactor_;
double q = 0;
lammpsInterface_->set_charge(transitionType_,q);
DENS_VEC eps = epsilon0_;
lammpsInterface_->set_epsilons(transitionType_,p_,eps.ptr());
lammpsInterface_->pair_reinit(); // epsilon
}
}
//--------------------------------------------------------
// diagnostics
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::compute_vector(int n) const
{
if (n==0) return count() - targetCount_;
else if (n==1) return count()/V_;
else if (n==2) return (1.-transitionFactor_)*nInTransition_;
else if (n==3) return _rngUniformCounter_;
else if (n==4) return _rngNormalCounter_;
else if (n==5) return lammpsInterface_->random_state(randomNumberGenerator_);
else return 0;
}
}; // end namespace