lammps/lib/atc/Kinetostat.cpp

2509 lines
98 KiB
C++

#include "Kinetostat.h"
#include "ATC_Error.h"
#include "ATC_Coupling.h"
#include "LammpsInterface.h"
#include "PerAtomQuantityLibrary.h"
#include "PrescribedDataManager.h"
#include "ElasticTimeIntegrator.h"
#include "TransferOperator.h"
using std::set;
using std::pair;
using std::string;
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class Kinetostat
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
Kinetostat::Kinetostat(ATC_Coupling * atc,
const string & regulatorPrefix) :
AtomicRegulator(atc,regulatorPrefix)
{
// do nothing
}
//--------------------------------------------------------
// modify:
// parses and adjusts kinetostat state based on
// user input, in the style of LAMMPS user input
//--------------------------------------------------------
bool Kinetostat::modify(int narg, char **arg)
{
bool foundMatch = false;
int argIndex = 0;
if (strcmp(arg[argIndex],"momentum")==0) {
argIndex++;
// fluxstat type
/*! \page man_control_momentum fix_modify AtC control momentum
\section syntax
fix_modify AtC control momentum none \n
fix_modify AtC control momentum rescale <frequency>\n
- frequency (int) = time step frequency for applying displacement and velocity rescaling \n
fix_modify AtC control momentum glc_displacement \n
fix_modify AtC control momentum glc_velocity \n
fix_modify AtC control momentum hoover \n
fix_modify AtC control momentum flux [faceset face_set_id, interpolate]
- face_set_id (string) = id of boundary face set, if not specified
(or not possible when the atomic domain does not line up with
mesh boundaries) defaults to an atomic-quadrature approximate
evaulation\n
\section examples
fix_modify AtC control momentum glc_velocity \n
fix_modify AtC control momentum flux faceset bndy_faces \n
\section description
\section restrictions
only to be used with specific transfers :
elastic \n
rescale not valid with time filtering activated
\section related
\section default
none
*/
boundaryIntegrationType_ = NO_QUADRATURE;
howOften_ = 1;
if (strcmp(arg[argIndex],"none")==0) { // restore defaults
regulatorTarget_ = NONE;
couplingMode_ = UNCOUPLED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"glc_displacement")==0) {
regulatorTarget_ = FIELD;
couplingMode_ = FIXED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"glc_velocity")==0) {
regulatorTarget_ = DERIVATIVE;
couplingMode_ = FIXED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"hoover")==0) {
regulatorTarget_ = DYNAMICS;
couplingMode_ = FIXED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"flux")==0) {
regulatorTarget_ = DYNAMICS;
couplingMode_ = FLUX;
argIndex++;
boundaryIntegrationType_ = atc_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_);
foundMatch = true;
}
else if (strcmp(arg[argIndex],"ghost_flux")==0) {
regulatorTarget_ = DYNAMICS;
couplingMode_ = GHOST_FLUX;
foundMatch = true;
}
}
if (!foundMatch)
foundMatch = AtomicRegulator::modify(narg,arg);
if (foundMatch)
needReset_ = true;
return foundMatch;
}
//--------------------------------------------------------
// reset_lambda_contribution
// resets the kinetostat generated force to a
// prescribed value
//--------------------------------------------------------
void Kinetostat::reset_lambda_contribution(const DENS_MAT & target)
{
DENS_MAN * lambdaForceFiltered = regulator_data("LambdaForceFiltered",nsd_);
lambdaForceFiltered->set_quantity() = target;
}
//--------------------------------------------------------
// initialize:
// sets up methods before a run
// dependence, but in general there is also a
// time integrator dependence. In general the
// precedence order is:
// time filter -> time integrator -> kinetostat
// In the future this may need to be added if
// different types of time integrators can be
// specified.
//--------------------------------------------------------
void Kinetostat::construct_methods()
{
// get data associated with stages 1 & 2 of ATC_Method::initialize
AtomicRegulator::construct_methods();
if (atc_->reset_methods()) {
// eliminate existing methods
delete_method();
DENS_MAT nodalGhostForceFiltered;
TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(VELOCITY))->time_integration_type();
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (timeFilterManager->end_equilibrate() && regulatorTarget_==AtomicRegulator::DYNAMICS) {
StressFlux * myMethod;
myMethod = dynamic_cast<StressFlux *>(regulatorMethod_);
nodalGhostForceFiltered = (myMethod->filtered_ghost_force()).quantity();
}
// update time filter
if (timeFilterManager->need_reset()) {
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT);
}
else {
timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE);
}
}
if (timeFilterManager->filter_dynamics()) {
switch (regulatorTarget_) {
case NONE: {
regulatorMethod_ = new RegulatorMethod(this);
break;
}
case FIELD: {
regulatorMethod_ = new DisplacementGlcFiltered(this);
break;
}
case DERIVATIVE: {
regulatorMethod_ = new VelocityGlcFiltered(this);
break;
}
case DYNAMICS: {
throw ATC_Error("Kinetostat::initialize - force based kinetostats not yet implemented with time filtering");
regulatorMethod_ = new StressFluxFiltered(this);
if (timeFilterManager->end_equilibrate()) {
StressFlux * myMethod;
myMethod = dynamic_cast<StressFlux *>(regulatorMethod_);
myMethod->reset_filtered_ghost_force(nodalGhostForceFiltered);
}
break;
}
default:
throw ATC_Error("Kinetostat::construct_methods - Unknown filtered kinetostat type");
}
}
else {
switch (regulatorTarget_) {
case NONE: {
regulatorMethod_ = new RegulatorMethod(this);
break;
}
case FIELD: {
regulatorMethod_ = new DisplacementGlc(this);
break;
}
case DERIVATIVE: {
regulatorMethod_ = new VelocityGlc(this);
break;
}
case DYNAMICS: {
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
if (couplingMode_ == GHOST_FLUX) {
if (md_fixed_nodes(VELOCITY)) {
if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fixed nodes but no fluxes
regulatorMethod_ = new KinetostatFixed(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new KinetostatFluxFixed(this);
}
}
else {
// there are only flux nodes
regulatorMethod_ = new KinetostatFluxGhost(this);
}
}
else if (couplingMode_ == FIXED) {
if (md_flux_nodes(VELOCITY)) {
if (!md_fixed_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fluxes but no fixed or coupled nodes
regulatorMethod_ = new KinetostatFlux(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new KinetostatFluxFixed(this);
}
}
else {
// there are only fixed nodes
regulatorMethod_ = new KinetostatFixed(this);
}
}
else if (couplingMode_ == FLUX) {
if (md_fixed_nodes(VELOCITY)) {
if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fixed nodes but no fluxes
regulatorMethod_ = new KinetostatFixed(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new KinetostatFluxFixed(this);
}
}
else {
// there are only flux nodes
regulatorMethod_ = new KinetostatFlux(this);
}
}
break;
}
if (myIntegrationType == TimeIntegrator::GEAR) {
if (couplingMode_ == FIXED) {
regulatorMethod_ = new KinetostatFixed(this);
}
else if (couplingMode_ == FLUX) {
regulatorMethod_ = new KinetostatFlux(this);
}
break;
}
else {
if (couplingMode_ == GHOST_FLUX) {
regulatorMethod_ = new StressFluxGhost(this);
}
else {
regulatorMethod_ = new StressFlux(this);
}
break;
}
}
default:
throw ATC_Error("Kinetostat::construct_methods - Unknown kinetostat type");
}
AtomicRegulator::reset_method();
}
}
else {
set_all_data_to_used();
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatShapeFunction
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
KinetostatShapeFunction::KinetostatShapeFunction(AtomicRegulator *kinetostat,
const string & regulatorPrefix) :
RegulatorShapeFunction(kinetostat,regulatorPrefix),
mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)),
timeFilter_(atomicRegulator_->time_filter()),
nodalAtomicLambdaForce_(NULL),
lambdaForceFiltered_(NULL),
atomKinetostatForce_(NULL),
atomVelocities_(NULL),
atomMasses_(NULL)
{
// data associated with stage 3 in ATC_Method::initialize
lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaMomentum",nsd_);
lambdaForceFiltered_ = atomicRegulator_->regulator_data("LambdaForceFiltered",nsd_);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatShapeFunction::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// needed fundamental quantities
atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY);
atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS);
// base class transfers
RegulatorShapeFunction::construct_transfers();
// lambda interpolated to the atomic coordinates
atomLambdas_ = new FtaShapeFunctionProlongation(atc_,
lambda_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_per_atom_quantity(atomLambdas_,
regulatorPrefix_+"AtomLambdaMomentum");
}
//--------------------------------------------------------
// set_weights
// sets diagonal weighting matrix used in
// solve_for_lambda
//--------------------------------------------------------
void KinetostatShapeFunction::set_weights()
{
if (this->use_local_shape_functions()) {
ConstantQuantityMapped<double> * myWeights = new ConstantQuantityMapped<double>(atc_,1.,lambdaAtomMap_);
weights_ = myWeights;
(atc_->interscale_manager()).add_per_atom_quantity(myWeights,
"AtomOnesMapped");
}
else {
weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicOnes");
if (!weights_) {
weights_ = new ConstantQuantity<double>(atc_,1.);
(atc_->interscale_manager()).add_per_atom_quantity(weights_,
"AtomicOnes");
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class GlcKinetostat
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
GlcKinetostat::GlcKinetostat(AtomicRegulator *kinetostat) :
KinetostatShapeFunction(kinetostat),
atomPositions_(NULL)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void GlcKinetostat::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// needed fundamental quantities
atomPositions_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION);
// base class transfers
KinetostatShapeFunction::construct_transfers();
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void GlcKinetostat::initialize()
{
KinetostatShapeFunction::initialize();
// set up list of nodes using Hoover coupling
// (a) nodes with prescribed values
PrescribedDataManager * prescribedDataMgr(atc_->prescribed_data_manager());
for (int i = 0; i < nNodes_; ++i)
for (int j = 0; j < nsd_; ++j)
if (prescribedDataMgr->is_fixed(i,VELOCITY,j))
hooverNodes_.insert(pair<int,int>(i,j));
// (b) AtC coupling nodes
if (atomicRegulator_->coupling_mode()==AtomicRegulator::FIXED) {
InterscaleManager & interscaleManager(atc_->interscale_manager());
const INT_ARRAY & nodeType((interscaleManager.dense_matrix_int("NodalGeometryType"))->quantity());
if (atomicRegulator_->use_localized_lambda()) {
for (int i = 0; i < nNodes_; ++i) {
if (nodeType(i,0)==BOUNDARY) {
for (int j = 0; j < nsd_; ++j) {
hooverNodes_.insert(pair<int,int>(i,j));
}
}
}
}
else {
for (int i = 0; i < nNodes_; ++i) {
if (nodeType(i,0)==BOUNDARY || nodeType(i,0)==MD_ONLY) {
for (int j = 0; j < nsd_; ++j) {
hooverNodes_.insert(pair<int,int>(i,j));
}
}
}
}
}
}
//--------------------------------------------------------
// apply_lambda_to_atoms
// uses existing lambda to modify given
// atomic quantity
//--------------------------------------------------------
void GlcKinetostat::apply_to_atoms(PerAtomQuantity<double> * quantity,
const DENS_MAT & lambdaAtom,
double dt)
{
*quantity -= lambdaAtom;
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class DisplacementGlc
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
DisplacementGlc::DisplacementGlc(AtomicRegulator * kinetostat) :
GlcKinetostat(kinetostat),
nodalAtomicMassWeightedDisplacement_(NULL),
nodalDisplacements_(atc_->field(DISPLACEMENT))
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void DisplacementGlc::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// set up shape function matrix
if (this->use_local_shape_functions()) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_,
lambdaAtomMap_,
nodeToOverlapMap_);
}
else {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
}
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixMomentum");
// set linear solver strategy
if (atomicRegulator_->use_lumped_lambda_solve()) {
linearSolverType_ = AtomicRegulator::RSL_SOLVE;
}
else {
linearSolverType_ = AtomicRegulator::CG_SOLVE;
}
// base class transfers
GlcKinetostat::construct_transfers();
// atomic force induced by kinetostat
atomKinetostatForce_ = new AtomicKinetostatForceDisplacement(atc_);
interscaleManager.add_per_atom_quantity(atomKinetostatForce_,
regulatorPrefix_+"AtomKinetostatForce");
// restricted force due to kinetostat
nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_,
atomKinetostatForce_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_,
regulatorPrefix_+"NodalAtomicLambdaForce");
// nodal displacement restricted from atoms
nodalAtomicMassWeightedDisplacement_ = interscaleManager.dense_matrix("NodalAtomicMassWeightedDisplacement");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void DisplacementGlc::initialize()
{
GlcKinetostat::initialize();
// sets up time filter for cases where variables temporally filtered
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
*lambdaForceFiltered_ = 0.;
timeFilter_->initialize(lambdaForceFiltered_->quantity());
}
}
//--------------------------------------------------------
// apply:
// apply the kinetostat to the atoms
//--------------------------------------------------------
void DisplacementGlc::apply_post_predictor(double dt)
{
compute_kinetostat(dt);
}
//--------------------------------------------------------
// compute_kinetostat
// manages the solution and application of the
// kinetostat equations and variables
//--------------------------------------------------------
void DisplacementGlc::compute_kinetostat(double dt)
{
// initial filtering update
apply_pre_filtering(dt);
// set up rhs
DENS_MAT rhs(nNodes_,nsd_);
set_kinetostat_rhs(rhs,dt);
// solve linear system for lambda
solve_for_lambda(rhs,lambda_->set_quantity());
// compute nodal atomic power
compute_nodal_lambda_force(dt);
// apply kinetostat to atoms
apply_to_atoms(atomPositions_,atomLambdas_->quantity());
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the right-hand side of the
// kinetostat equations
//--------------------------------------------------------
void DisplacementGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii
rhs = nodalAtomicMassWeightedDisplacement_->quantity();
rhs -= (mdMassMatrix_.quantity())*(nodalDisplacements_.quantity());
}
//--------------------------------------------------------
// compute_nodal_lambda_force
// compute the effective FE force applied
// by the kinetostat
//--------------------------------------------------------
void DisplacementGlc::compute_nodal_lambda_force(double dt)
{
const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity());
timeFilter_->apply_post_step1(lambdaForceFiltered_->set_quantity(),
myNodalAtomicLambdaForce,dt);
// update FE displacements for localized thermostats
apply_localization_correction(myNodalAtomicLambdaForce,
nodalDisplacements_.set_quantity(),
dt*dt);
}
//--------------------------------------------------------
// apply_pre_filtering
// applies first step of filtering to
// relevant variables
//--------------------------------------------------------
void DisplacementGlc::apply_pre_filtering(double dt)
{
// apply time filtered lambda force
DENS_MAT lambdaZero(nNodes_,nsd_);
timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),(-1./dt/dt)*lambdaZero,dt);
}
//--------------------------------------------------------
// set_weights
// sets diagonal weighting matrix used in
// solve_for_lambda
//--------------------------------------------------------
void DisplacementGlc::set_weights()
{
if (lambdaAtomMap_) {
MappedAtomQuantity * myWeights = new MappedAtomQuantity(atc_,atomMasses_,lambdaAtomMap_);
weights_ = myWeights;
(atc_->interscale_manager()).add_per_atom_quantity(myWeights,
"AtomMassesMapped");
}
else {
weights_ = atomMasses_;
}
}
//--------------------------------------------------------
// apply_localization_correction
// corrects for localized kinetostats only
// solving kinetostat equations on a subset
// of the MD region
//--------------------------------------------------------
void DisplacementGlc::apply_localization_correction(const DENS_MAT & source,
DENS_MAT & nodalField,
double weight)
{
DENS_MAT nodalLambdaRoc(nNodes_,nsd_);
atc_->apply_inverse_mass_matrix(source,
nodalLambdaRoc,
VELOCITY);
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
nodalLambdaRoc(iter->first,iter->second) = 0.;
}
nodalField += weight*nodalLambdaRoc;
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void DisplacementGlc::output(OUTPUT_LIST & outputData)
{
_nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity();
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"LambdaMomentum"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class DisplacementGlcFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
DisplacementGlcFiltered::DisplacementGlcFiltered(AtomicRegulator * kinetostat) :
DisplacementGlc(kinetostat),
nodalAtomicDisplacements_(atc_->nodal_atomic_field(DISPLACEMENT))
{
// do nothing
}
//--------------------------------------------------------
// apply_pre_filtering
// applies first step of filtering to
// relevant variables
//--------------------------------------------------------
void DisplacementGlcFiltered::apply_pre_filtering(double dt)
{
// apply time filtered lambda to atomic fields
DisplacementGlc::apply_pre_filtering(dt);
DENS_MAT nodalAcceleration(nNodes_,nsd_);
atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->set_quantity(),
nodalAcceleration,
VELOCITY);
nodalAtomicDisplacements_ += dt*dt*nodalAcceleration;
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the right-hand side of the
// kinetostat equations
//--------------------------------------------------------
void DisplacementGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii
double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt));
rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicDisplacements_.quantity() - nodalDisplacements_.quantity());
}
//--------------------------------------------------------
// compute_nodal_lambda_force
// compute the effective FE force applied
// by the kinetostat
//--------------------------------------------------------
void DisplacementGlcFiltered::compute_nodal_lambda_force(double dt)
{
const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity());
DENS_MAT & myLambdaForceFiltered(lambdaForceFiltered_->set_quantity());
timeFilter_->apply_post_step1(myLambdaForceFiltered,
myNodalAtomicLambdaForce,dt);
// update filtered atomic displacements
DENS_MAT nodalLambdaRoc(myNodalAtomicLambdaForce.nRows(),myNodalAtomicLambdaForce.nCols());
atc_->apply_inverse_md_mass_matrix(myNodalAtomicLambdaForce,
nodalLambdaRoc,
VELOCITY);
timeFilter_->apply_post_step1(nodalAtomicDisplacements_.set_quantity(),dt*dt*nodalLambdaRoc,dt);
// update FE displacements for localized thermostats
apply_localization_correction(myLambdaForceFiltered,
nodalDisplacements_.set_quantity(),
dt*dt);
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void DisplacementGlcFiltered::output(OUTPUT_LIST & outputData)
{
DENS_MAT & lambda(lambda_->set_quantity());
DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"LambdaMomentum"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaForce"] = &lambdaForceFiltered;
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class VelocityGlc
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
VelocityGlc::VelocityGlc(AtomicRegulator * kinetostat) :
GlcKinetostat(kinetostat),
nodalAtomicMomentum_(NULL),
nodalVelocities_(atc_->field(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void VelocityGlc::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// set up shape function matrix
shapeFunctionMatrix_ = interscaleManager.per_atom_sparse_matrix(regulatorPrefix_+"LambdaCouplingMatrixMomentum");
if (!shapeFunctionMatrix_) {
if (this->use_local_shape_functions()) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_,
lambdaAtomMap_,
nodeToOverlapMap_);
}
else {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
}
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixMomentum");
}
// set linear solver strategy
if (atomicRegulator_->use_lumped_lambda_solve()) {
linearSolverType_ = AtomicRegulator::RSL_SOLVE;
}
else {
linearSolverType_ = AtomicRegulator::CG_SOLVE;
}
// base class transfers
GlcKinetostat::construct_transfers();
// atomic force induced by kinetostat
atomKinetostatForce_ = new AtomicKinetostatForceVelocity(atc_);
interscaleManager.add_per_atom_quantity(atomKinetostatForce_,
regulatorPrefix_+"AtomKinetostatForce");
// restricted force due to kinetostat
nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_,
atomKinetostatForce_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_,
regulatorPrefix_+"NodalAtomicLambdaForce");
// nodal momentum restricted from atoms
nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void VelocityGlc::initialize()
{
GlcKinetostat::initialize();
// sets up time filter for cases where variables temporally filtered
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
lambdaForceFiltered_->set_quantity() = 0.;
timeFilter_->initialize(lambdaForceFiltered_->quantity());
}
}
//--------------------------------------------------------
// apply_mid_corrector:
// apply the kinetostat during the middle of the
// predictor phase
//--------------------------------------------------------
void VelocityGlc::apply_mid_predictor(double dt)
{
double dtLambda = 0.5*dt;
compute_kinetostat(dtLambda);
apply_kinetostat(dtLambda);
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the kinetostat after the corrector phase
//--------------------------------------------------------
void VelocityGlc::apply_post_corrector(double dt)
{
double dtLambda = 0.5*dt;
compute_kinetostat(dtLambda);
apply_kinetostat(dtLambda);
}
//--------------------------------------------------------
// apply_pre_filtering
// applies first step of filtering to
// relevant variables
//--------------------------------------------------------
void VelocityGlc::apply_pre_filtering(double dt)
{
// apply time filtered lambda to atomic fields
DENS_MAT lambdaZero(nNodes_,nsd_);
timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),(-1./dt)*lambdaZero,dt);
}
//--------------------------------------------------------
// compute_kinetostat
// manages the solution and application of the
// kinetostat equations and variables
//--------------------------------------------------------
void VelocityGlc::compute_kinetostat(double dt)
{
// initial filtering update
apply_pre_filtering(dt);
// set up rhs
DENS_MAT rhs(nNodes_,nsd_);
this->set_kinetostat_rhs(rhs,dt);
// solve linear system for lambda
solve_for_lambda(rhs,lambda_->set_quantity());
#ifdef OBSOLETE
// compute nodal atomic power
compute_nodal_lambda_force(dt);
// apply kinetostat to atoms
apply_to_atoms(atomVelocities_,atomLambdas_->quantity());
#endif
}
//--------------------------------------------------------
// apply_kinetostat
// manages the application of the
// kinetostat equations and variables
//--------------------------------------------------------
void VelocityGlc::apply_kinetostat(double dt)
{
// compute nodal atomic power
compute_nodal_lambda_force(dt);
// apply kinetostat to atoms
apply_to_atoms(atomVelocities_,atomLambdas_->quantity());
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the right-hand side of the
// kinetostat equations
//--------------------------------------------------------
void VelocityGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// form rhs : sum_a (hatN_Ia * x_ai) - (\dot{Upsilon})_Ii
rhs = nodalAtomicMomentum_->quantity();
rhs -= (mdMassMatrix_.quantity())*(nodalVelocities_.quantity());
}
//--------------------------------------------------------
// compute_nodal_lambda_force
// compute the effective FE force applied
// by the kinetostat
//--------------------------------------------------------
void VelocityGlc::compute_nodal_lambda_force(double dt)
{
const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity());
timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),
myNodalAtomicLambdaForce,dt);
// update FE displacements for localized thermostats
apply_localization_correction(myNodalAtomicLambdaForce,
nodalVelocities_.set_quantity(),
dt);
}
//--------------------------------------------------------
// set_weights
// sets diagonal weighting matrix used in
// solve_for_lambda
//--------------------------------------------------------
void VelocityGlc::set_weights()
{
if (lambdaAtomMap_) {
MappedAtomQuantity * myWeights = new MappedAtomQuantity(atc_,atomMasses_,lambdaAtomMap_);
weights_ = myWeights;
(atc_->interscale_manager()).add_per_atom_quantity(myWeights,
"AtomMassesMapped");
}
else {
weights_ = atomMasses_;
}
}
//--------------------------------------------------------
// apply_localization_correction
// corrects for localized kinetostats only
// solving kinetostat equations on a subset
// of the MD region
//--------------------------------------------------------
void VelocityGlc::apply_localization_correction(const DENS_MAT & source,
DENS_MAT & nodalField,
double weight)
{
DENS_MAT nodalLambdaRoc(nNodes_,nsd_);
atc_->apply_inverse_mass_matrix(source,
nodalLambdaRoc,
VELOCITY);
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
nodalLambdaRoc(iter->first,iter->second) = 0.;
}
nodalField += weight*nodalLambdaRoc;
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void VelocityGlc::output(OUTPUT_LIST & outputData)
{
_nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity();
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"LambdaMomentum"] = &(lambda_->set_quantity());
outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class VelocityGlcFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
VelocityGlcFiltered::VelocityGlcFiltered(AtomicRegulator *kinetostat)
: VelocityGlc(kinetostat),
nodalAtomicVelocities_(atc_->nodal_atomic_field(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// apply_pre_filtering
// applies first step of filtering to
// relevant variables
//--------------------------------------------------------
void VelocityGlcFiltered::apply_pre_filtering(double dt)
{
// apply time filtered lambda to atomic fields
VelocityGlc::apply_pre_filtering(dt);
DENS_MAT nodalAcceleration(nNodes_,nsd_);
atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->quantity(),
nodalAcceleration,
VELOCITY);
nodalAtomicVelocities_ += dt*nodalAcceleration;
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the right-hand side of the
// kinetostat equations
//--------------------------------------------------------
void VelocityGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii
double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt));
rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicVelocities_.quantity() - nodalVelocities_.quantity());
}
//--------------------------------------------------------
// compute_nodal_lambda_force
// compute the effective FE force applied
// by the kinetostat
//--------------------------------------------------------
void VelocityGlcFiltered::compute_nodal_lambda_force(double dt)
{
const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity());
DENS_MAT & myLambdaForceFiltered(lambdaForceFiltered_->set_quantity());
timeFilter_->apply_post_step1(myLambdaForceFiltered,myNodalAtomicLambdaForce,dt);
// update filtered atomic displacements
DENS_MAT nodalLambdaRoc(myNodalAtomicLambdaForce.nRows(),myNodalAtomicLambdaForce.nCols());
atc_->apply_inverse_md_mass_matrix(myNodalAtomicLambdaForce,
nodalLambdaRoc,
VELOCITY);
timeFilter_->apply_post_step1(nodalAtomicVelocities_.set_quantity(),dt*nodalLambdaRoc,dt);
// update FE displacements for localized thermostats
apply_localization_correction(myLambdaForceFiltered,
nodalVelocities_.set_quantity(),
dt);
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void VelocityGlcFiltered::output(OUTPUT_LIST & outputData)
{
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &(lambda_->set_quantity());
outputData[regulatorPrefix_+"NodalLambdaForce"] = &(lambdaForceFiltered_->set_quantity());
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class StressFlux
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
StressFlux::StressFlux(AtomicRegulator * kinetostat) :
GlcKinetostat(kinetostat),
nodalForce_(atc_->field_rhs(VELOCITY)),
nodalAtomicForce_(NULL),
nodalGhostForce_(NULL),
momentumSource_(atc_->atomic_source(VELOCITY))
{
// flag for performing boundary flux calculation
fieldMask_(VELOCITY,FLUX) = true;
}
StressFlux::~StressFlux()
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void StressFlux::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// set up shape function matrix
if (this->use_local_shape_functions()) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_,
lambdaAtomMap_,
nodeToOverlapMap_);
}
else {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
}
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixMomentum");
// set linear solver strategy
if (atomicRegulator_->use_lumped_lambda_solve()) {
linearSolverType_ = AtomicRegulator::RSL_SOLVE;
}
else {
linearSolverType_ = AtomicRegulator::CG_SOLVE;
}
// base class transfers
GlcKinetostat::construct_transfers();
// force at nodes due to atoms
nodalAtomicForce_ = interscaleManager.dense_matrix("NodalAtomicForce");
// atomic force induced by kinetostat
atomKinetostatForce_ = new AtomicKinetostatForceStress(atc_,atomLambdas_);
interscaleManager.add_per_atom_quantity(atomKinetostatForce_,
regulatorPrefix_+"AtomKinetostatForce");
// restricted force due to kinetostat
nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_,
atomKinetostatForce_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_,
regulatorPrefix_+"NodalAtomicLambdaForce");
// sets up space for ghost force related variables
if (atc_->groupbit_ghost()) {
GhostCouplingMatrix * shapeFunctionGhost = new GhostCouplingMatrix(atc_,interscaleManager.per_atom_sparse_matrix("InterpolantGhost"),
regulatedNodes_,nodeToOverlapMap_);
interscaleManager.add_sparse_matrix(shapeFunctionGhost,
regulatorPrefix_+"GhostCouplingMatrix");
FundamentalAtomQuantity * atomGhostForce = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE,
GHOST);
nodalGhostForce_ = new AtfShapeFunctionRestriction(atc_,atomGhostForce,
shapeFunctionGhost);
interscaleManager.add_dense_matrix(nodalGhostForce_,
regulatorPrefix_+"NodalGhostForce");
nodalGhostForceFiltered_.reset(nNodes_,nsd_);
}
}
//--------------------------------------------------------
// compute_boundary_flux:
// computes the boundary flux to be consistent with
// the controller
//--------------------------------------------------------
void StressFlux::compute_boundary_flux(FIELDS & fields)
{
GlcKinetostat::compute_boundary_flux(fields);
}
//--------------------------------------------------------
// apply_pre_predictor:
// apply the kinetostat to the atoms in the
// mid-predictor integration phase
//--------------------------------------------------------
void StressFlux::apply_pre_predictor(double dt)
{
double dtLambda = 0.5*dt;
// apply lambda force to atoms
apply_to_atoms(atomVelocities_,atomKinetostatForce_->quantity(),dtLambda);
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the kinetostat to the atoms in the
// post-corrector integration phase
//--------------------------------------------------------
void StressFlux::apply_post_corrector(double dt)
{
double dtLambda = 0.5*dt;
// apply lambda force to atoms
apply_to_atoms(atomVelocities_,atomKinetostatForce_->quantity(),dtLambda);
}
//--------------------------------------------------------
// compute_kinetostat
// manages the solution and application of the
// kinetostat equations and variables
//--------------------------------------------------------
void StressFlux::compute_kinetostat(double dt)
{
// initial filtering update
apply_pre_filtering(dt);
// set up rhs
DENS_MAT rhs(nNodes_,nsd_);
set_kinetostat_rhs(rhs,dt);
// solve linear system for lambda
solve_for_lambda(rhs,lambda_->set_quantity());
// compute nodal atomic power
compute_nodal_lambda_force(dt);
}
//--------------------------------------------------------
// apply_pre_filtering
// applies first step of filtering to
// relevant variables
//--------------------------------------------------------
void StressFlux::apply_pre_filtering(double dt)
{
// apply time filtered lambda force
DENS_MAT lambdaZero(nNodes_,nsd_);
timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),lambdaZero,dt);
if (nodalGhostForce_) {
timeFilter_->apply_pre_step1(nodalGhostForceFiltered_.set_quantity(),
nodalGhostForce_->quantity(),dt);
}
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void StressFlux::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// (a) for flux based :
// form rhs : \int N_I r dV - \sum_g N_Ig^* f_g
// sources are set in ATC transfer
rhs.reset(nNodes_,nsd_);
rhs = momentumSource_.quantity();
if (nodalGhostForce_) {
rhs -= nodalGhostForce_->quantity();
}
// (b) for ess. bcs
// form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I}
DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity();
atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY);
rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed;
rhsPrescribed += nodalAtomicForce_->quantity();
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second);
}
}
//--------------------------------------------------------
// compute_nodal_lambda_force
// computes the force induced on the FE
// by applying lambdaForce on the atoms
//--------------------------------------------------------
void StressFlux::compute_nodal_lambda_force(double dt)
{
DENS_MAT myNodalAtomicLambdaForce = nodalAtomicLambdaForce_->quantity();
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
myNodalAtomicLambdaForce(iter->first,iter->second) = 0.;
}
timeFilter_->apply_post_step1(lambdaForceFiltered_->set_quantity(),
myNodalAtomicLambdaForce,dt);
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void StressFlux::add_to_rhs(FIELDS & rhs)
{
// compute the kinetostat force
compute_kinetostat(atc_->dt());
rhs[VELOCITY] += nodalAtomicLambdaForce_->quantity() + boundaryFlux_[VELOCITY].quantity();
}
//--------------------------------------------------------
// apply_lambda_to_atoms
// uses existing lambda to modify given
// atomic quantity
//--------------------------------------------------------
void StressFlux::apply_to_atoms(PerAtomQuantity<double> * atomVelocities,
const DENS_MAT & lambdaForce,
double dt)
{
_deltaVelocity_ = lambdaForce;
_deltaVelocity_ /= atomMasses_->quantity();
_deltaVelocity_ *= dt;
*atomVelocities += _deltaVelocity_;
}
//--------------------------------------------------------
// reset_filtered_ghost_force:
// resets the kinetostat generated ghost force to a
// prescribed value
//--------------------------------------------------------
void StressFlux::reset_filtered_ghost_force(DENS_MAT & target)
{
nodalGhostForceFiltered_ = target;
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void StressFlux::output(OUTPUT_LIST & outputData)
{
_nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity();
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class StressFluxGhost
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
StressFluxGhost::StressFluxGhost(AtomicRegulator * kinetostat) :
StressFlux(kinetostat)
{
// flag for performing boundary flux calculation
fieldMask_(VELOCITY,FLUX) = false;
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void StressFluxGhost::construct_transfers()
{
StressFlux::construct_transfers();
if (!nodalGhostForce_) {
throw ATC_Error("StressFluxGhost::StressFluxGhost - ghost atoms must be specified");
}
}
//--------------------------------------------------------
// compute_boundary_flux:
// computes the boundary flux to be consistent with
// the controller
//--------------------------------------------------------
void StressFluxGhost::compute_boundary_flux(FIELDS & fields)
{
// This is only used in computation of atomic sources
boundaryFlux_[VELOCITY] = 0.;
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void StressFluxGhost::add_to_rhs(FIELDS & rhs)
{
// compute the kinetostat force
compute_kinetostat(atc_->dt());
// uses ghost force as the boundary flux to add to the RHS
rhs[VELOCITY] += nodalAtomicLambdaForce_->quantity() + nodalGhostForce_->quantity();
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void StressFluxGhost::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// (a) for flux based :
// form rhs : \int N_I r dV - \sum_g N_Ig^* f_g
// sources are set in ATC transfer
rhs.reset(nNodes_,nsd_);
rhs = momentumSource_.quantity();
// (b) for ess. bcs
// form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I}
DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity();
atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY);
rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed;
rhsPrescribed += nodalAtomicForce_->quantity();
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class StressFluxFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
StressFluxFiltered::StressFluxFiltered(AtomicRegulator * kinetostat) :
StressFlux(kinetostat),
nodalAtomicVelocity_(atc_->nodal_atomic_field(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void StressFluxFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// set basic terms
// (a) for flux based :
// form rhs : \int N_I r dV - \sum_g N_Ig^* f_g
// sources are set in ATC transfer
rhs.reset(nNodes_,nsd_);
rhs = momentumSource_.quantity() - nodalGhostForceFiltered_.quantity();
// (b) for ess. bcs
// form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I}
DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity();
atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY);
rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed;
rhsPrescribed += nodalAtomicForce_->quantity();
set<pair<int,int> >::const_iterator iter;
for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) {
rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second);
}
// adjust for application of current lambda force
rhs += lambdaForceFiltered_->quantity();
// correct for time filtering
rhs *= 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt));
}
//--------------------------------------------------------
// apply_lambda_to_atoms
// uses existing lambda to modify given
// atomic quantity
//--------------------------------------------------------
void StressFluxFiltered::apply_to_atoms(PerAtomQuantity<double> * atomVelocities,
const DENS_MAT & lambdaForce,
double dt)
{
StressFlux::apply_to_atoms(atomVelocities,lambdaForce,dt);
// add in corrections to filtered nodal atomice velocity
DENS_MAT velocityRoc(nNodes_,nsd_);
atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->quantity(),
velocityRoc,
VELOCITY);
nodalAtomicVelocity_ += dt*velocityRoc;
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void StressFluxFiltered::add_to_rhs(FIELDS & rhs)
{
// compute kinetostat forces
compute_kinetostat(atc_->dt());
rhs[VELOCITY] += lambdaForceFiltered_->quantity() + boundaryFlux_[VELOCITY].quantity();
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void StressFluxFiltered::output(OUTPUT_LIST & outputData)
{
DENS_MAT & lambda(lambda_->set_quantity());
DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaForce"] = &lambdaForceFiltered;
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatGlcFs
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
KinetostatGlcFs::KinetostatGlcFs(AtomicRegulator * kinetostat,
const string & regulatorPrefix) :
KinetostatShapeFunction(kinetostat,regulatorPrefix),
velocity_(atc_->field(VELOCITY)),
//timeFilter_(atomicRegulator_->time_filter()),
//nodalAtomicLambdaForce_(NULL),
//lambdaPowerFiltered_(NULL),
//atomKinetostatForces_(NULL),
//atomMasses_(NULL),
nodalAtomicMomentum_(NULL),
isFirstTimestep_(true),
atomPredictedVelocities_(NULL),
nodalAtomicPredictedMomentum_(NULL),
dtFactor_(0.)
{
// constuct/obtain data corresponding to stage 3 of ATC_Method::initialize
nodalAtomicLambdaForce_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalAtomicLambdaForce",nsd_);
lambdaForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaForceFiltered",1);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatGlcFs::construct_transfers()
{
// base class transfers
KinetostatShapeFunction::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// get data from manager
nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum");
// atomic force induced by kinetostat
PerAtomQuantity<double> * atomLambdas = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaMomentum");
atomKinetostatForce_ = new AtomicKinetostatForceStress(atc_,atomLambdas);
interscaleManager.add_per_atom_quantity(atomKinetostatForce_,
regulatorPrefix_+"AtomKinetostatForce");
// predicted momentum quantities: atom velocities, atom momenta, and restricted atom momenta
// MAKE THINGS WORK WITH ONLY ONE PREDICTED VELOCITY AND DERIVED QUANTITIES, CHECK IT EXISTS
atomPredictedVelocities_ = new AtcAtomQuantity<double>(atc_,nsd_);
interscaleManager.add_per_atom_quantity(atomPredictedVelocities_,
regulatorPrefix_+"AtomicPredictedVelocities");
AtomicMomentum * atomPredictedMomentum = new AtomicMomentum(atc_,
atomPredictedVelocities_);
interscaleManager.add_per_atom_quantity(atomPredictedMomentum,
regulatorPrefix_+"AtomicPredictedMomentum");
nodalAtomicPredictedMomentum_ = new AtfShapeFunctionRestriction(atc_,
atomPredictedMomentum,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicPredictedMomentum_,
regulatorPrefix_+"NodalAtomicPredictedMomentum");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void KinetostatGlcFs::initialize()
{
KinetostatShapeFunction::initialize();
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
// we should reset lambda and lambdaForce to zero in this case
// implies an initial condition of 0 for the filtered nodal lambda power
// initial conditions will always be needed when using time filtering
// however, the fractional step scheme must assume the instantaneous
// nodal lambda power is 0 initially because all quantities are in delta form
*lambda_ = 0.; // ensures initial lambda force is zero
*nodalAtomicLambdaForce_ = 0.; // momentum change due to kinetostat
*lambdaForceFiltered_ = 0.; // filtered momentum change due to kinetostats
}
else {
// we can grab lambda power variables using time integrator and atc transfer in cases for equilibration
}
// sets up time filter for cases where variables temporally filtered
if (timeFilterManager->need_reset()) {
// the form of this integrator implies no time filters that require history data can be used
timeFilter_->initialize(nodalAtomicLambdaForce_->quantity());
}
atomKinetostatForce_->quantity(); // initialize
}
//--------------------------------------------------------
// apply_lambda_to_atoms
// uses existing lambda to modify given
// atomic quantity
//--------------------------------------------------------
void KinetostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomVelocity,
const DENS_MAN * nodalAtomicMomentum,
const DENS_MAT & lambdaForce,
DENS_MAT & nodalAtomicLambdaForce,
double dt)
{
// compute initial contributions to lambda force
nodalAtomicLambdaForce = nodalAtomicMomentum->quantity();
nodalAtomicLambdaForce *= -1.;
// apply lambda force to atoms
_velocityDelta_ = lambdaForce;
_velocityDelta_ /= atomMasses_->quantity();
_velocityDelta_ *= dt;
(*atomVelocity) += _velocityDelta_;
// finalize lambda force
nodalAtomicLambdaForce += nodalAtomicMomentum->quantity();
}
//--------------------------------------------------------
// full_prediction:
// flag to perform a full prediction calcalation
// for lambda rather than using the old value
//--------------------------------------------------------
bool KinetostatGlcFs::full_prediction()
{
if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN)
&& (atc_->atom_to_element_map_frequency() > 1)
&& (atc_->step() % atc_->atom_to_element_map_frequency() == 0 ))) {
return true;
}
return false;
}
//--------------------------------------------------------
// apply_pre_predictor:
// apply the kinetostat to the atoms in the
// pre-predictor integration phase
//--------------------------------------------------------
void KinetostatGlcFs::apply_pre_predictor(double dt)
{
DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity());
DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity());
// update filtered forces
timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt);
// apply lambda force to atoms and compute instantaneous lambda force
this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,
atomKinetostatForce_->quantity(),
nodalAtomicLambdaForce,0.5*dt);
// update nodal variables for first half of timestep
this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt);
atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY);
velocity_ += deltaMomentum_;
// start update of filtered lambda force
nodalAtomicLambdaForce = 0.;
timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt);
}
//--------------------------------------------------------
// apply_pre_corrector:
// apply the thermostat to the atoms in the first part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void KinetostatGlcFs::apply_pre_corrector(double dt)
{
(*atomPredictedVelocities_) = atomVelocities_->quantity();
// do full prediction if we just redid the shape functions
if (full_prediction()) {
this->compute_lambda(dt);
}
// apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step
DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity());
apply_to_atoms(atomPredictedVelocities_,
nodalAtomicPredictedMomentum_,
atomKinetostatForce_->quantity(),
myNodalAtomicLambdaForce,0.5*dt);
// update predicted nodal variables for second half of time step
this->add_to_momentum(myNodalAtomicLambdaForce,deltaMomentum_,0.5*dt);
atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY);
velocity_ += deltaMomentum_;
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the kinetostat to the atoms in the
// post-corrector integration phase
//--------------------------------------------------------
void KinetostatGlcFs::apply_post_corrector(double dt)
{
// remove predicted force effects
DENS_MAT & myVelocity(velocity_.set_quantity());
myVelocity -= deltaMomentum_;
// compute the kinetostat equation and update lambda
this->compute_lambda(dt);
// apply lambda force to atoms and compute instantaneous lambda force for second half of time step
DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity());
this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,
atomKinetostatForce_->quantity(),
nodalAtomicLambdaForce,0.5*dt);
// start update of filtered lambda force
timeFilter_->apply_post_step2(lambdaForceFiltered_->set_quantity(),
nodalAtomicLambdaForce,dt);
// update nodal variables for first half of timestep
this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt);
atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY);
velocity_ += deltaMomentum_;
isFirstTimestep_ = false;
}
//--------------------------------------------------------
// compute_kinetostat
// manages the solution and application of the
// kinetostat equations and variables
//--------------------------------------------------------
void KinetostatGlcFs::compute_lambda(double dt)
{
// set up rhs for lambda equation
this->set_kinetostat_rhs(rhs_,0.5*dt);
// solve linear system for lambda
DENS_MAT & lambda(lambda_->set_quantity());
solve_for_lambda(rhs_,lambda);
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void KinetostatGlcFs::output(OUTPUT_LIST & outputData)
{
_lambdaForceOutput_ = nodalAtomicLambdaForce_->quantity();
// approximate value for lambda force
double dt = LammpsInterface::instance()->dt();
_lambdaForceOutput_ *= (2./dt);
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"LambdaMomentum"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_lambdaForceOutput_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatFlux
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
KinetostatFlux::KinetostatFlux(AtomicRegulator * kinetostat,
const string & regulatorPrefix) :
KinetostatGlcFs(kinetostat,regulatorPrefix),
momentumSource_(atc_->atomic_source(VELOCITY)),
nodalGhostForce_(NULL),
nodalGhostForceFiltered_(NULL)
{
// flag for performing boundary flux calculation
fieldMask_(VELOCITY,FLUX) = true;
// constuct/obtain data corresponding to stage 3 of ATC_Method::initialize
nodalGhostForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalGhostForceFiltered",nsd_);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatFlux::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// set up data for linear solver
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixMomentum");
if (elementMask_) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
}
if (atomicRegulator_->use_localized_lambda()) {
linearSolverType_ = AtomicRegulator::RSL_SOLVE;
}
else {
linearSolverType_ = AtomicRegulator::CG_SOLVE;
}
// base class transfers
KinetostatGlcFs::construct_transfers();
// sets up space for ghost force related variables
if (atc_->groupbit_ghost()) {
MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap =
interscaleManager.dense_matrix_int(regulatorPrefix_+"NodeToOverlapMap");
GhostCouplingMatrix * shapeFunctionGhost = new GhostCouplingMatrix(atc_,interscaleManager.per_atom_sparse_matrix("InterpolantGhost"),
regulatedNodes_,
nodeToOverlapMap);
interscaleManager.add_sparse_matrix(shapeFunctionGhost,
regulatorPrefix_+"GhostCouplingMatrix");
FundamentalAtomQuantity * atomGhostForce = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE,
GHOST);
nodalGhostForce_ = new AtfShapeFunctionRestriction(atc_,atomGhostForce,
shapeFunctionGhost);
interscaleManager.add_dense_matrix(nodalGhostForce_,
regulatorPrefix_+"NodalGhostForce");
}
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void KinetostatFlux::initialize()
{
KinetostatGlcFs::initialize();
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
// we should reset lambda and lambdaForce to zero in this case
// implies an initial condition of 0 for the filtered nodal lambda power
// initial conditions will always be needed when using time filtering
// however, the fractional step scheme must assume the instantaneous
// nodal lambda power is 0 initially because all quantities are in delta form
*nodalGhostForceFiltered_ = 0.; // filtered force from ghost atoms
}
else {
// we can grab lambda power variables using time integrator and atc transfer in cases for equilibration
}
// timestep factor
dtFactor_ = 1.;
}
//--------------------------------------------------------
// construct_regulated_nodes:
// constructs the set of nodes being regulated
//--------------------------------------------------------
void KinetostatFlux::construct_regulated_nodes()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// matrix requires all entries even if localized for correct lumping
regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"KinetostatRegulatedNodes");
if (!regulatedNodes_) {
regulatedNodes_ = new RegulatedNodes(atc_);
interscaleManager.add_set_int(regulatedNodes_,
regulatorPrefix_+"KinetostatRegulatedNodes");
}
// if localized monitor nodes with applied fluxes
if (atomicRegulator_->use_localized_lambda()) {
if ((atomicRegulator_->coupling_mode() == Kinetostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) {
// include boundary nodes
applicationNodes_ = new FluxBoundaryNodes(atc_);
boundaryNodes_ = new BoundaryNodes(atc_);
interscaleManager.add_set_int(boundaryNodes_,
regulatorPrefix_+"KinetostatBoundaryNodes");
}
else {
// fluxed nodes only
applicationNodes_ = new FluxNodes(atc_);
}
interscaleManager.add_set_int(applicationNodes_,
regulatorPrefix_+"KinetostatApplicationNodes");
}
else {
applicationNodes_ = regulatedNodes_;
}
// special set of boundary elements for boundary flux quadrature
if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION)
&& (atomicRegulator_->use_localized_lambda())) {
elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask");
if (!elementMask_) {
elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_);
interscaleManager.add_dense_matrix_bool(elementMask_,
regulatorPrefix_+"BoundaryElementMask");
}
}
}
//--------------------------------------------------------
// apply_pre_predictor:
// apply the kinetostat to the atoms in the
// pre-predictor integration phase
//--------------------------------------------------------
void KinetostatFlux::apply_pre_predictor(double dt)
{
// update filtered forces
if (nodalGhostForce_) {
timeFilter_->apply_pre_step1(nodalGhostForceFiltered_->set_quantity(),
nodalGhostForce_->quantity(),dt);
}
KinetostatGlcFs::apply_pre_predictor(dt);
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the kinetostat to the atoms in the
// post-corrector integration phase
//--------------------------------------------------------
void KinetostatFlux::apply_post_corrector(double dt)
{
// update filtered ghost force
if (nodalGhostForce_) {
timeFilter_->apply_post_step1(nodalGhostForceFiltered_->set_quantity(),
nodalGhostForce_->quantity(),dt);
}
// compute the kinetostat equation and update lambda
KinetostatGlcFs::apply_post_corrector(dt);
}
//--------------------------------------------------------
// add_to_momentum:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void KinetostatFlux::add_to_momentum(const DENS_MAT & nodalLambdaForce,
DENS_MAT & deltaMomentum,
double dt)
{
deltaMomentum.resize(nNodes_,nsd_);
const DENS_MAT & boundaryFlux(boundaryFlux_[VELOCITY].quantity());
for (int i = 0; i < nNodes_; i++) {
for (int j = 0; j < nsd_; j++) {
deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j);
}
}
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void KinetostatFlux::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// (a) for flux based :
// form rhs : \int N_I r dV - \sum_g N_Ig^* f_g
// sources are set in ATC transfer
rhs.reset(nNodes_,nsd_);
const DENS_MAT & momentumSource(momentumSource_.quantity());
const set<int> & applicationNodes(applicationNodes_->quantity());
set<int>::const_iterator iNode;
for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) {
for (int j = 0; j < nsd_; j++) {
rhs(*iNode,j) = momentumSource(*iNode,j);
}
}
// add ghost forces, if needed
if (nodalGhostForce_) {
const DENS_MAT & nodalGhostForce(nodalGhostForce_->quantity());
for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) {
for (int j = 0; j < nsd_; j++) {
rhs(*iNode,j) -= nodalGhostForce(*iNode,j);
}
}
}
}
//--------------------------------------------------------
// reset_filtered_ghost_force:
// resets the kinetostat generated ghost force to a
// prescribed value
//--------------------------------------------------------
void KinetostatFlux::reset_filtered_ghost_force(DENS_MAT & target)
{
(*nodalGhostForceFiltered_) = target;
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatFluxGhost
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
KinetostatFluxGhost::KinetostatFluxGhost(AtomicRegulator * kinetostat,
const string & regulatorPrefix) :
KinetostatFlux(kinetostat,regulatorPrefix)
{
// flag for performing boundary flux calculation
fieldMask_(VELOCITY,FLUX) = false;
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatFluxGhost::construct_transfers()
{
KinetostatFlux::construct_transfers();
if (!nodalGhostForce_) {
throw ATC_Error("KinetostatFluxGhost::KinetostatFluxGhost - ghost atoms must be specified");
}
}
//--------------------------------------------------------
// compute_boundary_flux:
// computes the boundary flux to be consistent with
// the controller
//--------------------------------------------------------
void KinetostatFluxGhost::compute_boundary_flux(FIELDS & fields)
{
// This is only used in computation of atomic sources
boundaryFlux_[VELOCITY] = 0.;
}
//--------------------------------------------------------
// add_to_momentum:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void KinetostatFluxGhost::add_to_momentum(const DENS_MAT & nodalLambdaForce,
DENS_MAT & deltaMomentum,
double dt)
{
deltaMomentum.resize(nNodes_,nsd_);
const DENS_MAT & boundaryFlux(nodalGhostForce_->quantity());
for (int i = 0; i < nNodes_; i++) {
for (int j = 0; j < nsd_; j++) {
deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j);
}
}
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void KinetostatFluxGhost::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// (a) for flux based :
// form rhs : \int N_I r dV - \sum_g N_Ig^* f_g
// sources are set in ATC transfer
rhs.reset(nNodes_,nsd_);
const DENS_MAT & momentumSource(momentumSource_.quantity());
const set<int> & applicationNodes(applicationNodes_->quantity());
set<int>::const_iterator iNode;
for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) {
for (int j = 0; j < nsd_; j++) {
rhs(*iNode,j) = momentumSource(*iNode,j);
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatFixed
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and kinetostat data
//--------------------------------------------------------
KinetostatFixed::KinetostatFixed(AtomicRegulator * kinetostat,
const string & regulatorPrefix) :
KinetostatGlcFs(kinetostat,regulatorPrefix),
filterCoefficient_(1.)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatFixed::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// determine if map is needed and set up if so
if (this->use_local_shape_functions()) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_,
lambdaAtomMap_,
nodeToOverlapMap_);
}
else {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
}
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixMomentum");
linearSolverType_ = AtomicRegulator::CG_SOLVE;
// base class transfers
KinetostatGlcFs::construct_transfers();
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void KinetostatFixed::initialize()
{
KinetostatGlcFs::initialize();
// reset data to zero
deltaFeMomentum_.reset(nNodes_,nsd_);
deltaNodalAtomicMomentum_.reset(nNodes_,nsd_);
// initialize filtered energy
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
nodalAtomicMomentumFiltered_ = nodalAtomicMomentum_->quantity();
}
// timestep factor
dtFactor_ = 0.5;
}
//--------------------------------------------------------
// halve_force:
// flag to halve the lambda force for improved
// accuracy
//--------------------------------------------------------
bool KinetostatFixed::halve_force()
{
if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN)
&& (atc_->atom_to_element_map_frequency() > 1)
&& (atc_->step() % atc_->atom_to_element_map_frequency() == 1))) {
return true;
}
return false;
}
//--------------------------------------------------------
// construct_regulated_nodes:
// constructs the set of nodes being regulated
//--------------------------------------------------------
void KinetostatFixed::construct_regulated_nodes()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"RegulatedNodes");
if (!regulatedNodes_) {
if (!atomicRegulator_->use_localized_lambda()) {
regulatedNodes_ = new RegulatedNodes(atc_);
}
else if (atomicRegulator_->coupling_mode() == Kinetostat::FLUX) {
regulatedNodes_ = new FixedNodes(atc_);
}
else if (atomicRegulator_->coupling_mode() == Kinetostat::FIXED) {
// include boundary nodes
regulatedNodes_ = new FixedBoundaryNodes(atc_);
}
else {
throw ATC_Error("KinetostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes");
}
interscaleManager.add_set_int(regulatedNodes_,
regulatorPrefix_+"RegulatedNodes");
}
applicationNodes_ = regulatedNodes_;
// special set of boundary elements for defining regulated atoms
if (atomicRegulator_->use_localized_lambda()) {
elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask");
if (!elementMask_) {
elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_);
interscaleManager.add_dense_matrix_bool(elementMask_,
regulatorPrefix_+"BoundaryElementMask");
}
}
}
//--------------------------------------------------------
// initialize_delta_nodal_atomic_momentum:
// initializes storage for the variable tracking
// the change in the nodal atomic momentum
// that has occured over the past timestep
//--------------------------------------------------------
void KinetostatFixed::initialize_delta_nodal_atomic_momentum(double dt)
{
// initialize delta energy
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
initialNodalAtomicMomentum_ = myNodalAtomicMomentum;
initialNodalAtomicMomentum_ *= -1.; // initially stored as negative for efficiency
timeFilter_->apply_pre_step1(nodalAtomicMomentumFiltered_.set_quantity(),
myNodalAtomicMomentum,dt);
}
//--------------------------------------------------------
// compute_delta_nodal_atomic_momentum:
// computes the change in the nodal atomic momentum
// that has occured over the past timestep
//--------------------------------------------------------
void KinetostatFixed::compute_delta_nodal_atomic_momentum(double dt)
{
// set delta energy based on predicted atomic velocities
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
timeFilter_->apply_post_step1(nodalAtomicMomentumFiltered_.set_quantity(),
myNodalAtomicMomentum,dt);
deltaNodalAtomicMomentum_ = initialNodalAtomicMomentum_;
deltaNodalAtomicMomentum_ += myNodalAtomicMomentum;
}
//--------------------------------------------------------
// compute_lambda
// sets up and solves linear system for lambda
//--------------------------------------------------------
void KinetostatFixed::compute_lambda(double dt)
{
// compute predicted changes in nodal atomic momentum
compute_delta_nodal_atomic_momentum(dt);
// change in finite element momentum
deltaFeMomentum_ = initialFeMomentum_;
deltaFeMomentum_ += (mdMassMatrix_.quantity())*(velocity_.quantity());
// set up rhs for lambda equation
KinetostatGlcFs::compute_lambda(dt);
}
//--------------------------------------------------------
// apply_pre_predictor:
// apply the kinetostat to the atoms in the
// pre-predictor integration phase
//--------------------------------------------------------
void KinetostatFixed::apply_pre_predictor(double dt)
{
// initialize values to be track change in finite element energy over the timestep
initialize_delta_nodal_atomic_momentum(dt);
initialFeMomentum_ = -1.*((mdMassMatrix_.quantity())*(velocity_.quantity())); // initially stored as negative for efficiency
KinetostatGlcFs::apply_pre_predictor(dt);
}
//--------------------------------------------------------
// apply_pre_corrector:
// apply the kinetostat to the atoms in the first part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void KinetostatFixed::apply_pre_corrector(double dt)
{
// do full prediction if we just redid the shape functions
if (full_prediction()) {
_tempNodalAtomicMomentumFiltered_ = nodalAtomicMomentumFiltered_.quantity();
}
KinetostatGlcFs::apply_pre_corrector(dt);
if (full_prediction()) {
// reset temporary variables
nodalAtomicMomentumFiltered_ = _tempNodalAtomicMomentumFiltered_;
}
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the kinetostat to the atoms in the
// post-corrector integration phase
//--------------------------------------------------------
void KinetostatFixed::apply_post_corrector(double dt)
{
bool halveForce = halve_force();
KinetostatGlcFs::apply_post_corrector(dt);
// update filtered momentum with lambda force
DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity());
timeFilter_->apply_post_step2(nodalAtomicMomentumFiltered_.set_quantity(),
myNodalAtomicLambdaForce,dt);
if (halveForce) {
// Halve lambda force due to fixed temperature constraints
// 1) makes up for poor initial condition
// 2) accounts for possibly large value of lambda when atomic shape function values change
// from eulerian mapping after more than 1 timestep
// avoids unstable oscillations arising from
// thermostat having to correct for error introduced in lambda changing the
// shape function matrices
*lambda_ *= 0.5;
}
}
//--------------------------------------------------------
// add_to_momentum:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the kinetostat
//--------------------------------------------------------
void KinetostatFixed::add_to_momentum(const DENS_MAT & nodalLambdaForce,
DENS_MAT & deltaMomentum,
double dt)
{
deltaMomentum.resize(nNodes_,nsd_);
const set<int> & regulatedNodes(regulatedNodes_->quantity());
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
for (int j = 0; j < nsd_; j++) {
deltaMomentum(i,j) = 0.;
}
}
else {
for (int j = 0; j < nsd_; j++) {
deltaMomentum(i,j) = nodalLambdaForce(i,j);
}
}
}
}
//--------------------------------------------------------
// set_kinetostat_rhs
// sets up the RHS of the kinetostat equations
// for the coupling parameter lambda
//--------------------------------------------------------
void KinetostatFixed::set_kinetostat_rhs(DENS_MAT & rhs, double dt)
{
// for essential bcs (fixed nodes) :
// form rhs : (delUpsV - delUps)/dt
const set<int> & regulatedNodes(regulatedNodes_->quantity());
double factor = (1./dt);
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
for (int j = 0; j < nsd_; j++) {
rhs(i,j) = factor*(deltaNodalAtomicMomentum_(i,j) - deltaFeMomentum_(i,j));
}
}
else {
for (int j = 0; j < nsd_; j++) {
rhs(i,j) = 0.;
}
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class KinetostatFluxFixed
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
KinetostatFluxFixed::KinetostatFluxFixed(AtomicRegulator * kinetostat,
bool constructKinetostats) :
RegulatorMethod(kinetostat),
kinetostatFlux_(NULL),
kinetostatFixed_(NULL),
kinetostatBcs_(NULL)
{
if (constructKinetostats) {
if (kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) {
kinetostatFlux_ = new KinetostatFluxGhost(kinetostat,regulatorPrefix_+"Flux");
}
else {
kinetostatFlux_ = new KinetostatFlux(kinetostat,regulatorPrefix_+"Flux");
}
kinetostatFixed_ = new KinetostatFixed(kinetostat,regulatorPrefix_+"Fixed");
// need to choose BC type based on coupling mode
if (kinetostat->coupling_mode() == AtomicRegulator::FLUX || kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) {
kinetostatBcs_ = kinetostatFlux_;
}
else if (kinetostat->coupling_mode() == AtomicRegulator::FIXED) {
kinetostatBcs_ = kinetostatFixed_;
}
else {
throw ATC_Error("KinetostatFluxFixed::constructor - invalid kinetostat type provided");
}
}
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
KinetostatFluxFixed::~KinetostatFluxFixed()
{
if (kinetostatFlux_) delete kinetostatFlux_;
if (kinetostatFixed_) delete kinetostatFixed_;
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void KinetostatFluxFixed::construct_transfers()
{
kinetostatFlux_->construct_transfers();
kinetostatFixed_->construct_transfers();
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void KinetostatFluxFixed::initialize()
{
kinetostatFlux_->initialize();
kinetostatFixed_->initialize();
}
//--------------------------------------------------------
// apply_predictor:
// apply the thermostat to the atoms in the first step
// of the Verlet algorithm
//--------------------------------------------------------
void KinetostatFluxFixed::apply_pre_predictor(double dt)
{
kinetostatFlux_->apply_pre_predictor(dt);
kinetostatFixed_->apply_pre_predictor(dt);
}
//--------------------------------------------------------
// apply_pre_corrector:
// apply the thermostat to the atoms in the first part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void KinetostatFluxFixed::apply_pre_corrector(double dt)
{
kinetostatFlux_->apply_pre_corrector(dt);
if (kinetostatFixed_->full_prediction()) {
atc_->set_fixed_nodes();
}
kinetostatFixed_->apply_pre_corrector(dt);
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the thermostat to the atoms in the second part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void KinetostatFluxFixed::apply_post_corrector(double dt)
{
kinetostatFlux_->apply_post_corrector(dt);
atc_->set_fixed_nodes();
kinetostatFixed_->apply_post_corrector(dt);
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void KinetostatFluxFixed::output(OUTPUT_LIST & outputData)
{
kinetostatFlux_->output(outputData);
kinetostatFixed_->output(outputData);
}
};