lammps/lib/atc/Thermostat.cpp

2382 lines
97 KiB
C++

#include "Thermostat.h"
#include "ATC_Coupling.h"
#include "ATC_Error.h"
#include "PrescribedDataManager.h"
#include "ThermalTimeIntegrator.h"
#include "TransferOperator.h"
using namespace std;
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class Thermostat
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
Thermostat::Thermostat(ATC_Coupling * atc,
const string & regulatorPrefix) :
AtomicRegulator(atc,regulatorPrefix),
lambdaMaxIterations_(myLambdaMaxIterations)
{
// nothing to do
}
//--------------------------------------------------------
// modify:
// parses and adjusts thermostat state based on
// user input, in the style of LAMMPS user input
//--------------------------------------------------------
bool Thermostat::modify(int narg, char **arg)
{
bool foundMatch = false;
int argIndex = 0;
if (strcmp(arg[argIndex],"thermal")==0) {
argIndex++;
// thermostat type
/*! \page man_control_thermal fix_modify AtC control thermal
\section syntax
fix_modify AtC control thermal <control_type> <optional_args>
- control_type (string) = none | rescale | hoover | flux\n
fix_modify AtC control thermal rescale <frequency> \n
- frequency (int) = time step frequency for applying velocity rescaling \n
fix_modify AtC control thermal hoover \n
fix_modify AtC control thermal flux <boundary_integration_type(optional)> <face_set_id(optional)>\n
- boundary_integration_type (string) = faceset | interpolate\n
- face_set_id (string), optional = 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, does not work with interpolate\n
\section examples
<TT> fix_modify AtC control thermal none </TT> \n
<TT> fix_modify AtC control thermal rescale 10 </TT> \n
<TT> fix_modify AtC control thermal hoover </TT> \n
<TT> fix_modify AtC control thermal flux </TT> \n
<TT> fix_modify AtC control thermal flux faceset bndy_faces </TT> \n
\section description
Sets the energy exchange mechansim from the finite elements to the atoms, managed through a control algorithm. Rescale computes a scale factor for each atom to match the finite element temperature. Hoover is a Gaussian least-constraint isokinetic thermostat enforces that the nodal restricted atomic temperature matches the finite element temperature. Flux is a similar mode, but rather adds energy to the atoms based on conservation of energy. Hoover and flux allows the prescription of sources or fixed temperatures on the atoms.
\section restrictions
only for be used with specific transfers :
thermal (rescale, hoover, flux), two_temperature (flux) \n
rescale not valid with time filtering activated
\section related
\section default
none\n
rescale frequency is 1\n
flux boundary_integration_type is interpolate
*/
if (strcmp(arg[argIndex],"none")==0) { // restore defaults
regulatorTarget_ = NONE;
couplingMode_ = UNCOUPLED;
howOften_ = 1;
boundaryIntegrationType_ = NO_QUADRATURE;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"rescale")==0) {
argIndex++;
howOften_ = atoi(arg[argIndex]);
if (howOften_ < 1) {
throw ATC_Error("Bad rescaling thermostat frequency");
}
else {
regulatorTarget_ = FIELD;
couplingMode_ = UNCOUPLED;
boundaryIntegrationType_ = NO_QUADRATURE;
foundMatch = true;
}
}
else if (strcmp(arg[argIndex],"hoover")==0) {
regulatorTarget_ = DYNAMICS;
couplingMode_ = FIXED;
howOften_ = 1;
boundaryIntegrationType_ = NO_QUADRATURE;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"flux")==0) {
regulatorTarget_ = DYNAMICS;
couplingMode_ = FLUX;
howOften_ = 1;
argIndex++;
boundaryIntegrationType_ = atc_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_);
foundMatch = true;
}
// set parameters for numerical matrix solutions unique to this thermostat
/*! \page man_control_thermal_correction_max_iterations fix_modify AtC control thermal correction_max_iterations
\section syntax
fix_modify AtC control thermal correction_max_iterations <max_iterations>
- max_iterations (int) = maximum number of iterations that will be used by iterative matrix solvers\n
\section examples
<TT> fix_modify AtC control thermal correction_max_iterations 10 </TT> \n
\section description
Sets the maximum number of iterations to compute the 2nd order in time correction term for lambda with the fractional step method. The method uses the same tolerance as the controller's matrix solver.
\section restrictions
only for use with thermal physics using the fractional step method.
\section related
\section default
correction_max_iterations is 20
*/
else if (strcmp(arg[argIndex],"correction_max_iterations")==0) {
argIndex++;
lambdaMaxIterations_ = atoi(arg[argIndex]);
if (lambdaMaxIterations_ < 1) {
throw ATC_Error("Bad correction maximum iteration count");
}
foundMatch = true;
}
}
if (!foundMatch)
foundMatch = AtomicRegulator::modify(narg,arg);
if (foundMatch)
needReset_ = true;
return foundMatch;
}
//--------------------------------------------------------
// reset_lambda_contribution:
// resets the thermostat generated power to a
// prescribed value
//--------------------------------------------------------
void Thermostat::reset_lambda_contribution(const DENS_MAT & target)
{
DENS_MAN * lambdaPowerFiltered = regulator_data("LambdaPowerFiltered",1);
*lambdaPowerFiltered = target;
}
//--------------------------------------------------------
// construct_methods:
// instantiations desired regulator method(s)
// dependence, but in general there is also a
// time integrator dependence. In general the
// precedence order is:
// time filter -> time integrator -> thermostat
// In the future this may need to be added if
// different types of time integrators can be
// specified.
//--------------------------------------------------------
void Thermostat::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();
// update time filter
TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type();
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (timeFilterManager->need_reset() ) {
if (myIntegrationType == TimeIntegrator::GEAR)
timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT);
else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT);
}
if (timeFilterManager->filter_dynamics()) {
switch (regulatorTarget_) {
case NONE: {
regulatorMethod_ = new RegulatorMethod(this);
break;
}
case FIELD: { // error check, rescale and filtering not supported together
throw ATC_Error("Cannot use rescaling thermostat with time filtering");
break;
}
case DYNAMICS: {
switch (couplingMode_) {
case FIXED: {
if (use_lumped_lambda_solve()) {
throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats");
}
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
if (md_flux_nodes(TEMPERATURE)) {
if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fluxes but no fixed or coupled nodes
regulatorMethod_ = new ThermostatFluxFiltered(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new ThermostatFluxFixedFiltered(this);
}
}
else {
// there are only fixed nodes
regulatorMethod_ = new ThermostatFixedFiltered(this);
}
}
else {
regulatorMethod_ = new ThermostatHooverVerletFiltered(this);
}
break;
}
case FLUX: {
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
if (use_lumped_lambda_solve()) {
throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats");
}
if (md_fixed_nodes(TEMPERATURE)) {
if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fixed nodes but no fluxes
regulatorMethod_ = new ThermostatFixedFiltered(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new ThermostatFluxFixedFiltered(this);
}
}
else {
// there are only flux nodes
regulatorMethod_ = new ThermostatFluxFiltered(this);
}
}
else {
if (use_localized_lambda()) {
if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) &&
atc_->boundary_integration_type() != NO_QUADRATURE) {
throw ATC_Error("Cannot use flux coupling with localized lambda");
}
}
regulatorMethod_ = new ThermostatPowerVerletFiltered(this);
}
break;
}
default:
throw ATC_Error("Unknown coupling mode in Thermostat::initialize");
}
break;
}
default:
throw ATC_Error("Unknown thermostat type in Thermostat::initialize");
}
}
else {
switch (regulatorTarget_) {
case NONE: {
regulatorMethod_ = new RegulatorMethod(this);
break;
}
case FIELD: {
if (atc_->temperature_def()==KINETIC)
regulatorMethod_ = new ThermostatRescale(this);
else if (atc_->temperature_def()==TOTAL)
regulatorMethod_ = new ThermostatRescaleMixedKePe(this);
else
throw ATC_Error("Unknown temperature definition");
break;
}
case DYNAMICS: {
switch (couplingMode_) {
case FIXED: {
if (use_lumped_lambda_solve()) {
throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats");
}
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
if (md_flux_nodes(TEMPERATURE)) {
if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fluxes but no fixed or coupled nodes
regulatorMethod_ = new ThermostatFlux(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new ThermostatFluxFixed(this);
}
}
else {
// there are only fixed nodes
regulatorMethod_ = new ThermostatFixed(this);
}
}
else {
regulatorMethod_ = new ThermostatHooverVerlet(this);
}
break;
}
case FLUX: {
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) {
if (use_lumped_lambda_solve()) {
throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats");
}
if (md_fixed_nodes(TEMPERATURE)) {
if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) {
// there are fixed nodes but no fluxes
regulatorMethod_ = new ThermostatFixed(this);
}
else {
// there are both fixed and flux nodes
regulatorMethod_ = new ThermostatFluxFixed(this);
}
}
else {
// there are only flux nodes
regulatorMethod_ = new ThermostatFlux(this);
}
}
else {
if (use_localized_lambda()) {
if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) &&
atc_->boundary_integration_type() != NO_QUADRATURE) {
throw ATC_Error("Cannot use flux coupling with localized lambda");
}
}
regulatorMethod_ = new ThermostatPowerVerlet(this);
}
break;
}
default:
throw ATC_Error("Unknown coupling mode in Thermostat::initialize");
}
break;
}
default:
throw ATC_Error("Unknown thermostat target in Thermostat::initialize");
}
}
AtomicRegulator::reset_method();
}
else {
set_all_data_to_used();
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatShapeFunction
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatShapeFunction::ThermostatShapeFunction(Thermostat * thermostat,
const string & regulatorPrefix) :
RegulatorShapeFunction(thermostat,regulatorPrefix),
thermostat_(thermostat),
mdMassMatrix_(atc_->set_mass_mat_md(TEMPERATURE)),
atomVelocities_(NULL)
{
fieldMask_(TEMPERATURE,FLUX) = true;
lambda_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatShapeFunction::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
RegulatorShapeFunction::construct_transfers();
// get atom velocity data from manager
atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY);
// construct lambda evaluated at atom locations
atomLambdas_ = new FtaShapeFunctionProlongation(atc_,
lambda_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy");
}
//---------------------------------------------------------
// set_weights:
// set the diagonal weighting matrix to be the atomic
// temperatures
//---------------------------------------------------------
void ThermostatShapeFunction::set_weights()
{
if (this->use_local_shape_functions()) {
VelocitySquaredMapped * myWeights = new VelocitySquaredMapped(atc_,lambdaAtomMap_);
weights_ = myWeights;
(atc_->interscale_manager()).add_per_atom_quantity(myWeights,
regulatorPrefix_+"AtomVelocitySquaredMapped");
}
else {
VelocitySquared * myWeights = new VelocitySquared(atc_);
weights_ = myWeights;
(atc_->interscale_manager()).add_per_atom_quantity(myWeights,
regulatorPrefix_+"AtomVelocitySquared");
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatRescale
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatRescale::ThermostatRescale(Thermostat * thermostat) :
ThermostatShapeFunction(thermostat),
nodalTemperature_(atc_->field(TEMPERATURE)),
atomVelocityRescalings_(NULL)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatRescale::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_);
(atc_->interscale_manager()).add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixEnergy");
linearSolverType_ = AtomicRegulator::CG_SOLVE;
// base class transfers
ThermostatShapeFunction::construct_transfers();
// velocity rescaling factor
atomVelocityRescalings_ = new AtomicVelocityRescaleFactor(atc_,atomLambdas_);
interscaleManager.add_per_atom_quantity(atomVelocityRescalings_,
regulatorPrefix_+"AtomVelocityRescaling");
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the thermostat in the post corrector phase
//--------------------------------------------------------
void ThermostatRescale::apply_post_corrector(double dt)
{
// compute right-hand side
_rhs_ = mdMassMatrix_.quantity()*nodalTemperature_.quantity();
correct_rhs(_rhs_); // correct right-hand side for complex temperature definitions, e.g., when the potential energy is included
// solve equations
solve_for_lambda(_rhs_,lambda_->set_quantity());
// application of rescaling lambda due
apply_to_atoms(atomVelocities_);
}
//--------------------------------------------------------
// apply_lambda_to_atoms:
// applies the velocity rescale with an existing lambda
// note oldAtomicQuantity and dt are not used
//--------------------------------------------------------
void ThermostatRescale::apply_to_atoms(PerAtomQuantity<double> * atomVelocities)
{
*atomVelocities *= atomVelocityRescalings_->quantity();
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatRescale::output(OUTPUT_LIST & outputData)
{
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData["Lambda"] = &lambda;
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatRescaleMixedKePe
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatRescaleMixedKePe::ThermostatRescaleMixedKePe(Thermostat * thermostat) :
ThermostatRescale(thermostat),
nodalAtomicFluctuatingPotentialEnergy_(NULL)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatRescaleMixedKePe::construct_transfers()
{
ThermostatRescale::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// get fluctuating PE at nodes
nodalAtomicFluctuatingPotentialEnergy_ =
interscaleManager.dense_matrix("NodalAtomicFluctuatingPotentialEnergy");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatRescaleMixedKePe::initialize()
{
ThermostatRescale::initialize();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// multipliers for KE and PE
AtomicEnergyForTemperature * atomEnergyForTemperature =
static_cast<AtomicEnergyForTemperature * >(interscaleManager.per_atom_quantity("AtomicEnergyForTemperature"));
keMultiplier_ = atomEnergyForTemperature->kinetic_energy_multiplier();
peMultiplier_ = 2. - keMultiplier_;
keMultiplier_ /= 2.; // account for use of 2 X KE in matrix equation
}
//--------------------------------------------------------
// correct_rhs:
// accounts for potential energy contribution to
// definition of atomic temperature
//--------------------------------------------------------
void ThermostatRescaleMixedKePe::correct_rhs(DENS_MAT & rhs)
{
rhs -= peMultiplier_*(nodalAtomicFluctuatingPotentialEnergy_->quantity());
rhs /= keMultiplier_;
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatGlcFs
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatGlcFs::ThermostatGlcFs(Thermostat * thermostat,
const string & regulatorPrefix) :
ThermostatShapeFunction(thermostat,regulatorPrefix),
temperature_(atc_->field(TEMPERATURE)),
timeFilter_(atomicRegulator_->time_filter()),
nodalAtomicLambdaPower_(NULL),
lambdaPowerFiltered_(NULL),
atomThermostatForces_(NULL),
atomMasses_(NULL),
rhsLambdaSquared_(NULL),
isFirstTimestep_(true),
lambdaMaxIterations_(thermostat->lambda_max_iterations()),
nodalAtomicEnergy_(NULL),
atomPredictedVelocities_(NULL),
nodalAtomicPredictedEnergy_(NULL),
firstHalfAtomForces_(NULL),
dtFactor_(0.)
{
// construct/obtain data corresponding to stage 3 of ATC_Method::initialize
nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1);
lambdaPowerFiltered_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatGlcFs::construct_transfers()
{
ThermostatShapeFunction::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// get data from manager
atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS),
nodalAtomicEnergy_ = interscaleManager.dense_matrix("NodalAtomicEnergy");
// thermostat forces based on lambda and the atomic velocities
atomThermostatForces_ = new AtomicThermostatForce(atc_,atomLambdas_);
interscaleManager.add_per_atom_quantity(atomThermostatForces_,
regulatorPrefix_+"AtomThermostatForce");
// predicted temperature quantities: atom velocities, atom energies, and restricted atom energies
AtcAtomQuantity<double> * atomPredictedVelocities = new AtcAtomQuantity<double>(atc_,nsd_);
interscaleManager.add_per_atom_quantity(atomPredictedVelocities,
regulatorPrefix_+"AtomicPredictedVelocities");
atomPredictedVelocities_ = atomPredictedVelocities;
AtomicEnergyForTemperature * atomPredictedEnergyForTemperature = new TwiceKineticEnergy(atc_,
atomPredictedVelocities);
interscaleManager.add_per_atom_quantity(atomPredictedEnergyForTemperature,
regulatorPrefix_+"AtomicPredictedTwiceKineticEnergy");
nodalAtomicPredictedEnergy_ = new AtfShapeFunctionRestriction(atc_,
atomPredictedEnergyForTemperature,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicPredictedEnergy_,
regulatorPrefix_+"NodalAtomicPredictedEnergy");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatGlcFs::initialize()
{
ThermostatShapeFunction::initialize();
// reset data to zero
deltaEnergy1_.reset(nNodes_,1);
deltaEnergy2_.reset(nNodes_,1);
_lambdaPowerOutput_.reset(nNodes_,1);
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
*nodalAtomicLambdaPower_ = 0.; // energy change due to thermostats
*lambdaPowerFiltered_ = 0.; // filtered energy change due to thermostats
}
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(nodalAtomicLambdaPower_->quantity());
}
atomThermostatForces_->quantity(); // initialize
atomThermostatForces_->fix_quantity();
firstHalfAtomForces_ = atomThermostatForces_; // initialize
compute_rhs_map();
}
//--------------------------------------------------------
// compute_rhs_map
// creates mapping from all nodes to those to which
// the thermostat applies
//--------------------------------------------------------
void ThermostatGlcFs::compute_rhs_map()
{
rhsMap_.resize(overlapToNodeMap_->nRows(),1);
DENS_MAT rhsMapGlobal(nNodes_,1);
const set<int> & applicationNodes(applicationNodes_->quantity());
for (int i = 0; i < nNodes_; i++) {
if (applicationNodes.find(i) != applicationNodes.end()) {
rhsMapGlobal(i,0) = 1.;
}
else {
rhsMapGlobal(i,0) = 0.;
}
}
map_unique_to_overlap(rhsMapGlobal,rhsMap_);
}
//--------------------------------------------------------
// apply_to_atoms:
// determines what if any contributions to the
// atomic moition is needed for
// consistency with the thermostat
// and computes the instantaneous induced power
//--------------------------------------------------------
void ThermostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomicVelocity,
const DENS_MAN * nodalAtomicEnergy,
const DENS_MAT & lambdaForce,
DENS_MAT & nodalAtomicLambdaPower,
double dt)
{
// compute initial contributions to lambda power
nodalAtomicLambdaPower = nodalAtomicEnergy->quantity();
nodalAtomicLambdaPower *= -1.;
// apply lambda force to atoms
_velocityDelta_ = lambdaForce;
_velocityDelta_ /= atomMasses_->quantity();
_velocityDelta_ *= dt;
(*atomicVelocity) += _velocityDelta_;
// finalize lambda power
nodalAtomicLambdaPower += nodalAtomicEnergy->quantity();
}
//--------------------------------------------------------
// full_prediction:
// flag to perform a full prediction calcalation
// for lambda rather than using the old value
//--------------------------------------------------------
bool ThermostatGlcFs::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_predictor:
// apply the thermostat to the atoms in the first step
// of the Verlet algorithm
//--------------------------------------------------------
void ThermostatGlcFs::apply_pre_predictor(double dt)
{
DENS_MAT & myLambdaPowerFiltered(lambdaPowerFiltered_->set_quantity());
DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity());
// update filtered power
timeFilter_->apply_pre_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); // equivalent update to measure power as change in energy due to thermostat
// apply lambda force to atoms and compute instantaneous lambda power for first half of time step
this->apply_to_atoms(atomVelocities_,nodalAtomicEnergy_,
firstHalfAtomForces_->quantity(),
myNodalAtomicLambdaPower,0.5*dt);
// update nodal variables for first half of time step
this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy1_,0.5*dt);
// start update of filtered lambda power
myNodalAtomicLambdaPower = 0.; // temporary power for first part of update
timeFilter_->apply_post_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt);
}
//--------------------------------------------------------
// apply_pre_corrector:
// apply the thermostat to the atoms in the first part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void ThermostatGlcFs::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);
atomThermostatForces_->unfix_quantity(); // allow update of atomic force applied by lambda
}
// apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step
DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity());
apply_to_atoms(atomPredictedVelocities_,
nodalAtomicPredictedEnergy_,
firstHalfAtomForces_->quantity(),
myNodalAtomicLambdaPower,0.5*dt);
if (full_prediction())
atomThermostatForces_->fix_quantity();
// update predicted nodal variables for second half of time step
this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt);
deltaEnergy1_ += deltaEnergy2_;
atc_->apply_inverse_mass_matrix(deltaEnergy1_,TEMPERATURE);
temperature_ += deltaEnergy1_;
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the thermostat to the atoms in the second part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void ThermostatGlcFs::apply_post_corrector(double dt)
{
// remove predicted power effects
DENS_MAT & myTemperature(temperature_.set_quantity());
atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE);
myTemperature -= deltaEnergy2_;
// set up equation and update lambda
this->compute_lambda(dt);
// apply lambda force to atoms and compute instantaneous lambda power for second half of time step
DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity());
atomThermostatForces_->unfix_quantity(); // allow computation of force applied by lambda
apply_to_atoms(atomVelocities_,nodalAtomicEnergy_,
atomThermostatForces_->quantity(),
myNodalAtomicLambdaPower,0.5*dt);
atomThermostatForces_->fix_quantity();
// finalize filtered lambda power by adding latest contribution
timeFilter_->apply_post_step2(lambdaPowerFiltered_->set_quantity(),
myNodalAtomicLambdaPower,dt);
// update nodal variables for second half of time step
this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt);
atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE);
myTemperature += deltaEnergy2_;
isFirstTimestep_ = false;
}
//--------------------------------------------------------
// compute_lambda:
// sets up and solves linear system for lambda, if the
// bool is true it iterators to a non-linear solution
//--------------------------------------------------------
void ThermostatGlcFs::compute_lambda(double dt,
bool iterateSolution)
{
// set up rhs for lambda equation
rhs_.resize(nNodes_,1);
this->set_thermostat_rhs(rhs_,0.5*dt);
// solve linear system for lambda guess
DENS_MAT & myLambda(lambda_->set_quantity());
solve_for_lambda(rhs_,myLambda);
// iterate to solution
if (iterateSolution) {
iterate_lambda(rhs_);
}
}
//--------------------------------------------------------
// iterate_lambda:
// iteratively solves the equations for lambda
// for the higher order dt corrections, assuming
// an initial guess for lambda
//--------------------------------------------------------
void ThermostatGlcFs::iterate_lambda(const MATRIX & rhs)
{
int nNodeOverlap = overlapToNodeMap_->nRows();
DENS_VEC _lambdaOverlap_(nNodeOverlap);
DENS_MAT & lambda(lambda_->set_quantity());
map_unique_to_overlap(lambda,_lambdaOverlap_);
double factor = 0.5*dtFactor_*atc_->dt();
_lambdaOld_.resize(nNodes_,1);
_rhsOverlap_.resize(nNodeOverlap,1);
map_unique_to_overlap(rhs,_rhsOverlap_);
_rhsTotal_.resize(nNodeOverlap);
// solve assuming we get initial guess for lambda
double error(-1.);
for (int i = 0; i < lambdaMaxIterations_; ++i) {
_lambdaOld_ = lambda;
// solve the system with the new rhs
const DENS_MAT & rhsLambdaSquared(rhsLambdaSquared_->quantity());
for (int i = 0; i < nNodeOverlap; i++) {
if (rhsMap_(i,0) == 1.) {
_rhsTotal_(i) = _rhsOverlap_(i,0) + factor*rhsLambdaSquared(i,0);
}
else {
_rhsTotal_(i) = 0.;
}
}
matrixSolver_->execute(_rhsTotal_,_lambdaOverlap_);
// check convergence
map_overlap_to_unique(_lambdaOverlap_,lambda);
lambda_->force_reset();
DENS_MAT difference = lambda-_lambdaOld_;
error = difference.col_norm()/_lambdaOld_.col_norm();
if (error < tolerance_)
break;
}
if (error >= tolerance_) {
stringstream message;
message << " Iterative solve for lambda failed to converge after " << lambdaMaxIterations_ << " iterations, final tolerance was " << error << "\n";
ATC::LammpsInterface::instance()->print_msg(message.str());
}
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatGlcFs::output(OUTPUT_LIST & outputData)
{
_lambdaPowerOutput_ = nodalAtomicLambdaPower_->quantity();
// approximate value for lambda power
double dt = LammpsInterface::instance()->dt();
_lambdaPowerOutput_ *= (2./dt);
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFlux
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatFlux::ThermostatFlux(Thermostat * thermostat,
const string & regulatorPrefix) :
ThermostatGlcFs(thermostat,regulatorPrefix),
heatSource_(atc_->atomic_source(TEMPERATURE))
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatFlux::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_+"LambdaCouplingMatrixEnergy");
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
ThermostatGlcFs::construct_transfers();
// add transfers for computation of extra RHS term accounting of O(lambda^2)
// lambda squared followed by fractional step RHS contribution
PerAtomQuantity<double> * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"));
LambdaSquared * lambdaSquared = new LambdaSquared(atc_,
atomMasses_,
weights_,
lambdaAtom);
interscaleManager.add_per_atom_quantity(lambdaSquared,
regulatorPrefix_+"LambdaSquaredMapped");
rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_);
interscaleManager.add_dense_matrix(rhsLambdaSquared_,
regulatorPrefix_+"RhsLambdaSquared");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatFlux::initialize()
{
ThermostatGlcFs::initialize();
// timestep factor
dtFactor_ = 1.;
}
//--------------------------------------------------------
// construct_regulated_nodes:
// constructs the set of nodes being regulated
//--------------------------------------------------------
void ThermostatFlux::construct_regulated_nodes()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// matrix requires all entries even if localized for correct lumping
regulatedNodes_ = new RegulatedNodes(atc_);
interscaleManager.add_set_int(regulatedNodes_,
regulatorPrefix_+"ThermostatRegulatedNodes");
// if localized monitor nodes with applied fluxes
if (atomicRegulator_->use_localized_lambda()) {
if ((atomicRegulator_->coupling_mode() == Thermostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) {
// include boundary nodes
applicationNodes_ = new FluxBoundaryNodes(atc_);
boundaryNodes_ = new BoundaryNodes(atc_);
interscaleManager.add_set_int(boundaryNodes_,
regulatorPrefix_+"ThermostatBoundaryNodes");
}
else {
// fluxed nodes only
applicationNodes_ = new FluxNodes(atc_);
}
interscaleManager.add_set_int(applicationNodes_,
regulatorPrefix_+"ThermostatApplicationNodes");
}
else {
applicationNodes_ = regulatedNodes_;
}
// special set of boundary elements for boundary flux quadrature
if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION)
&& (atomicRegulator_->use_localized_lambda())) {
elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_);
interscaleManager.add_dense_matrix_bool(elementMask_,
regulatorPrefix_+"BoundaryElementMask");
}
}
//--------------------------------------------------------
// add_to_temperature
// add in contributions from lambda power and boundary
// flux to the FE temperature
//--------------------------------------------------------
void ThermostatFlux::add_to_energy(const DENS_MAT & nodalLambdaPower,
DENS_MAT & deltaEnergy,
double dt)
{
deltaEnergy.resize(nNodes_,1);
const DENS_MAT & myBoundaryFlux(boundaryFlux_[TEMPERATURE].quantity());
for (int i = 0; i < nNodes_; i++) {
deltaEnergy(i,0) = nodalLambdaPower(i,0) + dt*myBoundaryFlux(i,0);
}
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side including boundary
// fluxes (coupling & prescribed), heat sources, and
// fixed (uncoupled) nodes
//--------------------------------------------------------
void ThermostatFlux::set_thermostat_rhs(DENS_MAT & rhs,
double dt)
{
// only tested with flux != 0 + ess bc = 0
// (a) for flux based :
// form rhs : 2/3kB * W_I^-1 * \int N_I r dV
// vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I
// fluxes are set in ATC transfer
const DENS_MAT & heatSource(heatSource_.quantity());
const set<int> & applicationNodes(applicationNodes_->quantity());
for (int i = 0; i < nNodes_; i++) {
if (applicationNodes.find(i) != applicationNodes.end()) {
rhs(i,0) = heatSource(i,0);
}
else {
rhs(i,0) = 0.;
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFixed
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatFixed::ThermostatFixed(Thermostat * thermostat,
const string & regulatorPrefix) :
ThermostatGlcFs(thermostat,regulatorPrefix),
atomThermostatForcesPredVel_(NULL),
filterCoefficient_(1.)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatFixed::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_+"LambdaCouplingMatrixEnergy");
linearSolverType_ = AtomicRegulator::CG_SOLVE;
// base class transfers, e.g. weights
ThermostatGlcFs::construct_transfers();
// add transfers for computation of extra RHS term accounting of O(lambda^2)
// lambda squared followed by fractional step RHS contribution
PerAtomQuantity<double> * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"));
if (lambdaAtomMap_) {
LambdaSquaredMapped * lambdaSquared = new LambdaSquaredMapped(atc_,
lambdaAtomMap_,
atomMasses_,
weights_,
lambdaAtom);
interscaleManager.add_per_atom_quantity(lambdaSquared,
regulatorPrefix_+"LambdaSquared");
rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_);
}
else {
LambdaSquared * lambdaSquared = new LambdaSquared(atc_,
atomMasses_,
weights_,
lambdaAtom);
interscaleManager.add_per_atom_quantity(lambdaSquared,
regulatorPrefix_+"LambdaSquaredMapped");
rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_);
}
interscaleManager.add_dense_matrix(rhsLambdaSquared_,
regulatorPrefix_+"RhsLambdaSquared");
// predicted forces for halving update
atomThermostatForcesPredVel_ = new AtomicThermostatForce(atc_,atomLambdas_,atomPredictedVelocities_);
interscaleManager.add_per_atom_quantity(atomThermostatForcesPredVel_,
regulatorPrefix_+"AtomThermostatForcePredictedVelocity");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatFixed::initialize()
{
ThermostatGlcFs::initialize();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set KE multiplier
AtomicEnergyForTemperature * atomEnergyForTemperature =
static_cast<AtomicEnergyForTemperature * >(interscaleManager.per_atom_quantity("AtomicEnergyForTemperature"));
keMultiplier_ = atomEnergyForTemperature->kinetic_energy_multiplier();
// reset data to zero
deltaFeEnergy_.reset(nNodes_,1);
deltaNodalAtomicEnergy_.reset(nNodes_,1);
// initialize filtered energy
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
nodalAtomicEnergyFiltered_ = nodalAtomicEnergy_->quantity();
}
// timestep factor
dtFactor_ = 0.5;
}
//--------------------------------------------------------
// halve_force:
// flag to halve the lambda force for improved
// accuracy
//--------------------------------------------------------
bool ThermostatFixed::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 ThermostatFixed::construct_regulated_nodes()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
if (!atomicRegulator_->use_localized_lambda()) {
regulatedNodes_ = new RegulatedNodes(atc_);
}
else if (thermostat_->coupling_mode() == AtomicRegulator::FLUX) {
regulatedNodes_ = new FixedNodes(atc_);
}
else if (thermostat_->coupling_mode() == AtomicRegulator::FIXED) {
// include boundary nodes
regulatedNodes_ = new FixedBoundaryNodes(atc_);
}
else {
throw ATC_Error("ThermostatFixed::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_ = new ElementMaskNodeSet(atc_,applicationNodes_);
interscaleManager.add_dense_matrix_bool(elementMask_,
regulatorPrefix_+"BoundaryElementMask");
}
}
//--------------------------------------------------------
// initialize_delta_nodal_atomic_energy:
// initializes storage for the variable tracking
// the change in the nodal atomic energy
// that has occured over the past timestep
//--------------------------------------------------------
void ThermostatFixed::initialize_delta_nodal_atomic_energy(double dt)
{
// initialize delta energy
const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity());
initialNodalAtomicEnergy_ = myNodalAtomicEnergy;
initialNodalAtomicEnergy_ *= -1.; // initially stored as negative for efficiency
timeFilter_->apply_pre_step1(nodalAtomicEnergyFiltered_.set_quantity(),
myNodalAtomicEnergy,dt);
}
//--------------------------------------------------------
// compute_delta_nodal_atomic_energy:
// computes the change in the nodal atomic energy
// that has occured over the past timestep
//--------------------------------------------------------
void ThermostatFixed::compute_delta_nodal_atomic_energy(double dt)
{
// set delta energy based on predicted atomic velocities
const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity());
timeFilter_->apply_post_step1(nodalAtomicEnergyFiltered_.set_quantity(),
myNodalAtomicEnergy,dt);
deltaNodalAtomicEnergy_ = initialNodalAtomicEnergy_;
deltaNodalAtomicEnergy_ += myNodalAtomicEnergy;
}
//--------------------------------------------------------
// compute_lambda:
// sets up and solves linear system for lambda, if the
// bool is true it iterators to a non-linear solution
//--------------------------------------------------------
void ThermostatFixed::compute_lambda(double dt,
bool iterateSolution)
{
// compute predicted changes in nodal atomic energy
compute_delta_nodal_atomic_energy(dt);
// change in finite element energy
deltaFeEnergy_ = initialFeEnergy_;
deltaFeEnergy_ += (mdMassMatrix_.quantity())*(temperature_.quantity());
ThermostatGlcFs::compute_lambda(dt,iterateSolution);
}
//--------------------------------------------------------
// apply_predictor:
// apply the thermostat to the atoms in the first step
// of the Verlet algorithm
//--------------------------------------------------------
void ThermostatFixed::apply_pre_predictor(double dt)
{
// initialize values to be track change in finite element energy over the timestep
initialize_delta_nodal_atomic_energy(dt);
initialFeEnergy_ = -1.*((mdMassMatrix_.quantity())*(temperature_.quantity())); // initially stored as negative for efficiency
ThermostatGlcFs::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 ThermostatFixed::apply_pre_corrector(double dt)
{
// do full prediction if we just redid the shape functions
if (full_prediction()) {
firstHalfAtomForces_ = atomThermostatForces_; // reset in case this time step needed special treatment
_tempNodalAtomicEnergyFiltered_ = nodalAtomicEnergyFiltered_.quantity();
}
ThermostatGlcFs::apply_pre_corrector(dt);
if (full_prediction()) {
// reset temporary variables
nodalAtomicEnergyFiltered_ = _tempNodalAtomicEnergyFiltered_;
}
if (halve_force()) {
// save old velocities if we are doing halving calculation of lambda force
// copy velocities over into temporary storage
(*atomPredictedVelocities_) = atomVelocities_->quantity();
}
}
//--------------------------------------------------------
// add_to_temperature
// add in contributions from lambda power and boundary
// flux to the FE temperature
//--------------------------------------------------------
void ThermostatFixed::add_to_energy(const DENS_MAT & nodalLambdaPower,
DENS_MAT & deltaEnergy,
double dt)
{
deltaEnergy.resize(nNodes_,1);
const set<int> & regulatedNodes(regulatedNodes_->quantity());
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
deltaEnergy(i,0) = 0.;
}
else {
deltaEnergy(i,0) = nodalLambdaPower(i,0);
}
}
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the thermostat to the atoms in the second part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void ThermostatFixed::apply_post_corrector(double dt)
{
bool halveForce = halve_force();
ThermostatGlcFs::apply_post_corrector(dt);
// update filtered energy with lambda power
DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity());
timeFilter_->apply_post_step2(nodalAtomicEnergyFiltered_.set_quantity(),
myNodalAtomicLambdaPower,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;
firstHalfAtomForces_ = atomThermostatForcesPredVel_;
atomThermostatForcesPredVel_->unfix_quantity();
}
else {
firstHalfAtomForces_ = atomThermostatForces_;
}
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side including boundary
// fluxes (coupling & prescribed), heat sources, and
// fixed (uncoupled) nodes
//--------------------------------------------------------
void ThermostatFixed::set_thermostat_rhs(DENS_MAT & rhs,
double dt)
{
// for essential bcs (fixed nodes) :
// form rhs : (delThetaV - delTheta)/dt
const set<int> & regulatedNodes(regulatedNodes_->quantity());
double factor = (1./dt)/keMultiplier_;
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0));
}
else {
rhs(i,0) = 0.;
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFluxFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatFluxFiltered::ThermostatFluxFiltered(Thermostat * thermostat,
const string & regulatorPrefix) :
ThermostatFlux(thermostat,regulatorPrefix)
{
// do nothing
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatFluxFiltered::initialize()
{
ThermostatFlux::initialize();
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
// always must start as zero because of filtering scheme
heatSourceOld_.reset(nNodes_,1);
instantHeatSource_.reset(nNodes_,1);
timeStepSource_.reset(nNodes_,1);
}
}
//--------------------------------------------------------
// apply_post_corrector:
// apply the thermostat to the atoms in the second part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void ThermostatFluxFiltered::apply_post_corrector(double dt)
{
// compute lambda
ThermostatFlux::apply_post_corrector(dt);
// store data needed for filter inversion of heat flux for thermostat rhs
instantHeatSource_ = rhs_;
heatSourceOld_ = heatSource_.quantity();
}
//--------------------------------------------------------
// add_to_temperature
// add in contributions from lambda power and boundary
// flux to the FE temperature
//--------------------------------------------------------
void ThermostatFluxFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower,
DENS_MAT & deltaEnergy,
double dt)
{
deltaEnergy.reset(nNodes_,1);
double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt);
const DENS_MAT & myBoundaryFlux(boundaryFlux_[TEMPERATURE].quantity());
for (int i = 0; i < nNodes_; i++) {
deltaEnergy(i,0) = coef*nodalLambdaPower(i,0) + dt*myBoundaryFlux(i,0);
}
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side including boundary
// fluxes (coupling & prescribed), heat sources, and
// fixed (uncoupled) nodes
//--------------------------------------------------------
void ThermostatFluxFiltered::set_thermostat_rhs(DENS_MAT & rhs,
double dt)
{
// only tested with flux != 0 + ess bc = 0
// (a) for flux based :
// form rhs : 2/3kB * W_I^-1 * \int N_I r dV
// vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I
// fluxes are set in ATC transfer
// invert heatSource_ to get unfiltered source
// relevant coefficients from time filter
double coefF1 = timeFilter_->filtered_coefficient_pre_s1(2.*dt);
double coefF2 = timeFilter_->filtered_coefficient_post_s1(2.*dt);
double coefU1 = timeFilter_->unfiltered_coefficient_pre_s1(2.*dt);
double coefU2 = timeFilter_->unfiltered_coefficient_post_s1(2.*dt);
const DENS_MAT & heatSource(heatSource_.quantity());
const set<int> & applicationNodes(applicationNodes_->quantity());
for (int i = 0; i < nNodes_; i++) {
if (applicationNodes.find(i) != applicationNodes.end()) {
rhs(i,0) = heatSource(i,0) - coefF1*coefF2*heatSourceOld_(i,0) - coefU1*coefF2*instantHeatSource_(i,0);
rhs(i,0) /= coefU2;
}
else {
rhs(i,0) = 0.;
}
}
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatFluxFiltered::output(OUTPUT_LIST & outputData)
{
_lambdaPowerOutput_ = lambdaPowerFiltered_->quantity();
// approximate value for lambda power
double dt = LammpsInterface::instance()->dt();
_lambdaPowerOutput_ *= (2./dt);
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_);
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFixedFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatFixedFiltered::ThermostatFixedFiltered(Thermostat * thermostat,
const string & regulatorPrefix) :
ThermostatFixed(thermostat,regulatorPrefix)
{
// do nothing
}
//--------------------------------------------------------
// initialize_delta_nodal_atomic_energy:
// initializes storage for the variable tracking
// the change in the nodal atomic energy
// that has occured over the past timestep
//--------------------------------------------------------
void ThermostatFixedFiltered::initialize_delta_nodal_atomic_energy(double dt)
{
// initialize delta energy
DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity());
initialNodalAtomicEnergy_ = myNodalAtomicEnergyFiltered;
initialNodalAtomicEnergy_ *= -1.; // initially stored as negative for efficiency
timeFilter_->apply_pre_step1(myNodalAtomicEnergyFiltered,
nodalAtomicEnergy_->quantity(),dt);
}
//--------------------------------------------------------
// compute_delta_nodal_atomic_energy:
// computes the change in the nodal atomic energy
// that has occured over the past timestep
//--------------------------------------------------------
void ThermostatFixedFiltered::compute_delta_nodal_atomic_energy(double dt)
{
// set delta energy based on predicted atomic velocities
DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity());
timeFilter_->apply_post_step1(myNodalAtomicEnergyFiltered,
nodalAtomicEnergy_->quantity(),dt);
deltaNodalAtomicEnergy_ = initialNodalAtomicEnergy_;
deltaNodalAtomicEnergy_ += myNodalAtomicEnergyFiltered;
}
//--------------------------------------------------------
// add_to_temperature
// add in contributions from lambda power and boundary
// flux to the FE temperature
//--------------------------------------------------------
void ThermostatFixedFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower,
DENS_MAT & deltaEnergy,
double dt)
{
deltaEnergy.resize(nNodes_,1);
const set<int> & regulatedNodes(regulatedNodes_->quantity());
double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt);
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
deltaEnergy(i,0) = 0.;
}
else {
deltaEnergy(i,0) = coef*nodalLambdaPower(i,0);
}
}
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side for fixed
// (coupling & prescribed) temperature values
//--------------------------------------------------------
void ThermostatFixedFiltered::set_thermostat_rhs(DENS_MAT & rhs,
double dt)
{
// (b) for essential bcs (fixed nodes) :
// form rhs : (delThetaV - delTheta)/dt
const set<int> & regulatedNodes(regulatedNodes_->quantity());
double factor = (1./dt)/keMultiplier_;
factor /= timeFilter_->unfiltered_coefficient_post_s1(2.*dt);
for (int i = 0; i < nNodes_; i++) {
if (regulatedNodes.find(i) != regulatedNodes.end()) {
rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0));
}
else {
rhs(i,0) = 0.;
}
}
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatFixedFiltered::output(OUTPUT_LIST & outputData)
{
_lambdaPowerOutput_ = lambdaPowerFiltered_->quantity();
// approximate value for lambda power
double dt = LammpsInterface::instance()->dt();
_lambdaPowerOutput_ *= (2./dt);
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData[regulatorPrefix_+"Lambda"] = &lambda;
outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_);
}
}
// have one for combined and one for lumped (lumped has two sub-thermostats)
// use node sets to call out fixed and fluxed locations
// derived off lumped lambda class that only considers certain atoms and certain nodes based on a mask
// move matrix solver construction to thermostats that actually do something
// thermostats can instantiate the type of shape function object they want in the future, for now we'll need hacks,
// for now we'll have to construct them with appropriate shape function matrix
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFluxFixed
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatFluxFixed::ThermostatFluxFixed(Thermostat * thermostat,
bool constructThermostats) :
RegulatorMethod(thermostat),
thermostatFlux_(NULL),
thermostatFixed_(NULL),
thermostatBcs_(NULL)
{
if (constructThermostats) {
thermostatFlux_ = new ThermostatFlux(thermostat,regulatorPrefix_+"Flux");
thermostatFixed_ = new ThermostatFixed(thermostat,regulatorPrefix_+"Fixed");
// need to choose BC type based on coupling mode
if (thermostat->coupling_mode() == AtomicRegulator::FLUX) {
thermostatBcs_ = thermostatFlux_;
}
else if (thermostat->coupling_mode() == AtomicRegulator::FIXED) {
thermostatBcs_ = thermostatFixed_;
}
else {
throw ATC_Error("ThermostatFluxFixed:create_thermostats - invalid thermostat type provided");
}
}
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ThermostatFluxFixed::~ThermostatFluxFixed()
{
if (thermostatFlux_) delete thermostatFlux_;
if (thermostatFixed_) delete thermostatFixed_;
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatFluxFixed::construct_transfers()
{
thermostatFlux_->construct_transfers();
thermostatFixed_->construct_transfers();
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatFluxFixed::initialize()
{
thermostatFlux_->initialize();
thermostatFixed_->initialize();
}
//--------------------------------------------------------
// apply_predictor:
// apply the thermostat to the atoms in the first step
// of the Verlet algorithm
//--------------------------------------------------------
void ThermostatFluxFixed::apply_pre_predictor(double dt)
{
thermostatFixed_->apply_pre_predictor(dt);
thermostatFlux_->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 ThermostatFluxFixed::apply_pre_corrector(double dt)
{
thermostatFlux_->apply_pre_corrector(dt);
if (thermostatFixed_->full_prediction()) {
atc_->set_fixed_nodes();
}
thermostatFixed_->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 ThermostatFluxFixed::apply_post_corrector(double dt)
{
thermostatFlux_->apply_post_corrector(dt);
atc_->set_fixed_nodes();
thermostatFixed_->apply_post_corrector(dt);
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatFluxFixed::output(OUTPUT_LIST & outputData)
{
thermostatFlux_->output(outputData);
thermostatFixed_->output(outputData);
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatFluxFixedFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatFluxFixedFiltered::ThermostatFluxFixedFiltered(Thermostat * thermostat) :
ThermostatFluxFixed(thermostat,false)
{
thermostatFlux_ = new ThermostatFluxFiltered(thermostat,regulatorPrefix_+"Flux");
thermostatFixed_ = new ThermostatFixedFiltered(thermostat,regulatorPrefix_+"Fixed");
// need to choose BC type based on coupling mode
if (thermostat->coupling_mode() == AtomicRegulator::FLUX) {
thermostatBcs_ = thermostatFlux_;
}
else if (thermostat->coupling_mode() == AtomicRegulator::FIXED) {
thermostatBcs_ = thermostatFixed_;
}
else {
throw ATC_Error("ThermostatFluxFixed:create_thermostats - invalid thermostat type provided");
}
}
//--------------------------------------------------------
// Class ThermostatGlc
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ThermostatGlc::ThermostatGlc(Thermostat * thermostat) :
ThermostatShapeFunction(thermostat),
timeFilter_(atomicRegulator_->time_filter()),
lambdaPowerFiltered_(NULL),
atomThermostatForces_(NULL),
prescribedDataMgr_(atc_->prescribed_data_manager()),
atomMasses_(NULL)
{
// consistent with stage 3 of ATC_Method::initialize
lambdaPowerFiltered_= thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatGlc::construct_transfers()
{
ThermostatShapeFunction::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
// get data from manager
atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS);
// thermostat forces based on lambda and the atomic velocities
AtomicThermostatForce * atomThermostatForces = new AtomicThermostatForce(atc_);
interscaleManager.add_per_atom_quantity(atomThermostatForces,
regulatorPrefix_+"AtomThermostatForce");
atomThermostatForces_ = atomThermostatForces;
}
//--------------------------------------------------------
// apply_to_atoms:
// determines what if any contributions to the
// atomic moition is needed for
// consistency with the thermostat
//--------------------------------------------------------
void ThermostatGlc::apply_to_atoms(PerAtomQuantity<double> * atomVelocities,
const DENS_MAT & lambdaForce,
double dt)
{
_velocityDelta_ = lambdaForce;
_velocityDelta_ /= atomMasses_->quantity();
_velocityDelta_ *= dt;
(*atomVelocities) += _velocityDelta_;
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatPowerVerlet
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatPowerVerlet::ThermostatPowerVerlet(Thermostat * thermostat) :
ThermostatGlc(thermostat),
nodalTemperatureRoc_(atc_->field_roc(TEMPERATURE)),
heatSource_(atc_->atomic_source(TEMPERATURE)),
nodalAtomicPower_(NULL),
nodalAtomicLambdaPower_(NULL)
{
// do nothing
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatPowerVerlet::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
// set up node mappings
create_node_maps();
// determine if mapping is needed and set up if so
if (atomicRegulator_->use_localized_lambda()) {
lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_);
interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_,
regulatorPrefix_+"LambdaAtomMap");
}
// set up linear solver
if (atomicRegulator_->use_lumped_lambda_solve()) {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
linearSolverType_ = AtomicRegulator::RSL_SOLVE;
}
else {
if (lambdaAtomMap_) {
shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_,
lambdaAtomMap_,
nodeToOverlapMap_);
}
else {
shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_);
}
linearSolverType_ = AtomicRegulator::CG_SOLVE;
}
interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_,
regulatorPrefix_+"LambdaCouplingMatrixEnergy");
// base class transfers, e.g. weights
ThermostatGlc::construct_transfers();
// get managed data
nodalAtomicPower_ = interscaleManager.dense_matrix("NodalAtomicPower");
// power induced by lambda
DotTwiceKineticEnergy * atomicLambdaPower =
new DotTwiceKineticEnergy(atc_,atomThermostatForces_);
interscaleManager.add_per_atom_quantity(atomicLambdaPower,
regulatorPrefix_+"AtomicLambdaPower");
// restriction to nodes of power induced by lambda
nodalAtomicLambdaPower_ = new AtfShapeFunctionRestriction(atc_,
atomicLambdaPower,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicLambdaPower_,
regulatorPrefix_+"NodalAtomicLambdaPower");
}
//--------------------------------------------------------
// initialize
// initializes all method data
//--------------------------------------------------------
void ThermostatPowerVerlet::initialize()
{
ThermostatGlc::initialize();
// sets up time filter for cases where variables temporally filtered
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (!timeFilterManager->end_equilibrate()) {
_nodalAtomicLambdaPowerOut_ = 0.;
*lambdaPowerFiltered_ = 0.;
timeFilter_->initialize(lambdaPowerFiltered_->quantity());
}
}
//--------------------------------------------------------
// apply_predictor:
// apply the thermostat to the atoms in the first step
// of the Verlet algorithm
//--------------------------------------------------------
void ThermostatPowerVerlet::apply_pre_predictor(double dt)
{
atomThermostatForces_->unfix_quantity();
compute_thermostat(0.5*dt);
// apply lambda force to atoms
const DENS_MAT & thermostatForces(atomThermostatForces_->quantity());
atomThermostatForces_->fix_quantity();
apply_to_atoms(atomVelocities_,thermostatForces,0.5*dt);
}
//--------------------------------------------------------
// apply_pre_corrector:
// apply the thermostat to the atoms in the first part
// of the corrector step of the Verlet algorithm
//--------------------------------------------------------
void ThermostatPowerVerlet::apply_pre_corrector(double dt)
{
atomThermostatForces_->unfix_quantity();
compute_thermostat(0.5*dt);
// apply lambda force to atoms
const DENS_MAT & thermostatForces(atomThermostatForces_->quantity());
atomThermostatForces_->fix_quantity();
apply_to_atoms(atomVelocities_,thermostatForces,0.5*dt);
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the thermostat
//--------------------------------------------------------
void ThermostatPowerVerlet::add_to_rhs(FIELDS & rhs)
{
rhs[TEMPERATURE] += nodalAtomicLambdaPower_->quantity() + boundaryFlux_[TEMPERATURE].quantity();
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side including boundary
// fluxes (coupling & prescribed), heat sources, and
// fixed (uncoupled) nodes
//--------------------------------------------------------
void ThermostatPowerVerlet::set_thermostat_rhs(DENS_MAT & rhs_nodes)
{
// (a) for flux based :
// form rhs : \int N_I r dV
// vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I
// fluxes are set in ATC transfer
rhs_nodes = heatSource_.quantity();
// (b) for ess. bcs
// form rhs : {sum_a (2 * N_Ia * v_ia * f_ia) - (dtheta/dt)_I}
// replace rhs for prescribed nodes
const DENS_MAT & myNodalAtomicPower(nodalAtomicPower_->quantity());
const DIAG_MAT & myMdMassMatrix(mdMassMatrix_.quantity());
const DENS_MAT & myNodalTemperatureRoc(nodalTemperatureRoc_.quantity());
for (int i = 0; i < nNodes_; i++) {
if (prescribedDataMgr_->is_fixed(i,TEMPERATURE,0)) {
rhs_nodes(i,0) = 0.5*(myNodalAtomicPower(i,0) - myMdMassMatrix(i,i)*myNodalTemperatureRoc(i,0));
}
}
}
//--------------------------------------------------------
// compute_thermostat:
// sets up and solves the thermostat equations since
// they are the same at different parts of the time
// step
//--------------------------------------------------------
void ThermostatPowerVerlet::compute_thermostat(double dt)
{
// set up rhs
set_thermostat_rhs(_rhs_);
// solve linear system for lambda
DENS_MAT & myLambda(lambda_->set_quantity());
solve_for_lambda(_rhs_,myLambda);
nodalAtomicLambdaPower_->unfix_quantity(); // enable computation of force applied by lambda
timeFilter_->apply_pre_step1(lambdaPowerFiltered_->set_quantity(),
nodalAtomicLambdaPower_->quantity(),dt);
nodalAtomicLambdaPower_->fix_quantity();
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatPowerVerlet::output(OUTPUT_LIST & outputData)
{
_nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity();
DENS_MAT & lambda(lambda_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData["lambda"] = &lambda;
outputData["nodalLambdaPower"] = &(_nodalAtomicLambdaPowerOut_);
}
}
//--------------------------------------------------------
// finish:
// final tasks after a run
//--------------------------------------------------------
void ThermostatPowerVerlet::finish()
{
_nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity();
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatHooverVerlet
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatHooverVerlet::ThermostatHooverVerlet(Thermostat * thermostat) :
ThermostatPowerVerlet(thermostat),
lambdaHoover_(NULL),
nodalAtomicHooverLambdaPower_(NULL)
{
// set up data consistent with stage 3 of ATC_Method::initialize
lambdaHoover_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaHoover",1);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatHooverVerlet::construct_transfers()
{
ThermostatPowerVerlet::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
FtaShapeFunctionProlongation * atomHooverLambdas = new FtaShapeFunctionProlongation(atc_,
lambdaHoover_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_per_atom_quantity(atomHooverLambdas,
regulatorPrefix_+"AtomHooverLambda");
AtomicThermostatForce * atomHooverThermostatForces = new AtomicThermostatForce(atc_,atomHooverLambdas);
interscaleManager.add_per_atom_quantity(atomHooverThermostatForces,
regulatorPrefix_+"AtomHooverThermostatForce");
SummedAtomicQuantity<double> * atomTotalThermostatForces =
new SummedAtomicQuantity<double>(atc_,atomThermostatForces_,atomHooverThermostatForces);
interscaleManager.add_per_atom_quantity(atomTotalThermostatForces,
regulatorPrefix_+"AtomTotalThermostatForce");
atomThermostatForces_ = atomTotalThermostatForces;
// transfers dependent on time integration method
DotTwiceKineticEnergy * atomicHooverLambdaPower =
new DotTwiceKineticEnergy(atc_,atomHooverThermostatForces);
interscaleManager.add_per_atom_quantity(atomicHooverLambdaPower,
regulatorPrefix_+"AtomicHooverLambdaPower");
nodalAtomicHooverLambdaPower_ = new AtfShapeFunctionRestriction(atc_,
atomicHooverLambdaPower,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicHooverLambdaPower_,
regulatorPrefix_+"NodalAtomicHooverLambdaPower");
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the thermostat
//--------------------------------------------------------
void ThermostatHooverVerlet::add_to_rhs(FIELDS & rhs)
{
rhs[TEMPERATURE] += _nodalAtomicLambdaPowerOut_;
}
//--------------------------------------------------------
// compute_thermostat:
// sets up and solves the thermostat equations since
// they are the same at different parts of the time
// step
//--------------------------------------------------------
void ThermostatHooverVerlet::compute_thermostat(double dt)
{
// apply prescribed/extrinsic sources and fixed nodes
ThermostatPowerVerlet::compute_thermostat(0.5*dt);
_nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); // save power from lambda in power-based thermostat
// set up Hoover rhs
set_hoover_rhs(_rhs_);
// solve linear system for lambda
DENS_MAT & myLambda(lambdaHoover_->set_quantity());
solve_for_lambda(_rhs_,myLambda);
// compute force applied by lambda
// compute nodal atomic power from Hoover coupling
// only add in contribution to uncoupled nodes
if (atomicRegulator_->use_localized_lambda())
add_to_lambda_power(atomThermostatForces_->quantity(),0.5*dt);
}
//--------------------------------------------------------
// set_hoover_rhs:
// sets up the right-hand side for fixed value,
// i.e. Hoover coupling
//--------------------------------------------------------
void ThermostatHooverVerlet::set_hoover_rhs(DENS_MAT & rhs)
{
// form rhs : sum_a ( N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I
rhs = nodalAtomicPower_->quantity();
rhs -= mdMassMatrix_.quantity()*nodalTemperatureRoc_.quantity();
rhs /= 2.;
}
//--------------------------------------------------------
// add_to_nodal_lambda_power:
// determines the power exerted by the Hoover
// thermostat at each FE node
//--------------------------------------------------------
void ThermostatHooverVerlet::add_to_lambda_power(const DENS_MAT & myLambdaForce,
double dt)
{
_myNodalLambdaPower_ = nodalAtomicHooverLambdaPower_->quantity();
const INT_ARRAY & nodeToOverlapMap(nodeToOverlapMap_->quantity());
for (int i = 0; i < nNodes_; ++i) {
if (nodeToOverlapMap(i,0)==-1)
_nodalAtomicLambdaPowerOut_(i,0) += _myNodalLambdaPower_(i,0);
else
_myNodalLambdaPower_(i,0) = 0.;
}
timeFilter_->apply_post_step1(lambdaPowerFiltered_->set_quantity(),_myNodalLambdaPower_,dt);
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatPowerVerletFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(Thermostat * thermostat) :
ThermostatPowerVerlet(thermostat),
nodalTemperature2Roc_(atc_->field_2roc(TEMPERATURE)),
fieldsRoc_(atc_->fields_roc()),
filterScale_((atc_->time_filter_manager())->filter_scale())
{
heatSourceRoc_.reset(nNodes_,1);
fluxRoc_[TEMPERATURE].reset(nNodes_,1);
}
//--------------------------------------------------------
// compute_boundary_flux
// also sets time derivatives of boundary flux and
// heat sources
//--------------------------------------------------------
void ThermostatPowerVerletFiltered::compute_boundary_flux(FIELDS & fields)
{
ThermostatPowerVerlet::compute_boundary_flux(fields);
// compute boundary flux rate of change
fluxRoc_[TEMPERATURE] = 0.;
atc_->compute_boundary_flux(fieldMask_,
fieldsRoc_,
fluxRoc_,
atomMaterialGroups_,
shpFcnDerivs_);
// compute extrinsic model rate of change
(atc_->extrinsic_model_manager()).set_sources(fieldsRoc_,fluxRoc_);
heatSourceRoc_ = fluxRoc_[TEMPERATURE].quantity();
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the thermostat
//--------------------------------------------------------
void ThermostatPowerVerletFiltered::add_to_rhs(FIELDS & rhs)
{
rhs[TEMPERATURE] += lambdaPowerFiltered_->quantity() + boundaryFlux_[TEMPERATURE].quantity();
}
//--------------------------------------------------------
// set_thermostat_rhs:
// sets up the right-hand side including boundary
// fluxes (coupling & prescribed), heat sources, and
// fixed (uncoupled) nodes
//--------------------------------------------------------
void ThermostatPowerVerletFiltered::set_thermostat_rhs(DENS_MAT & rhs_nodes)
{
// (a) for flux based :
// form rhs : \int N_I r dV
// vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I
// fluxes are set in ATC transfer
rhs_nodes = heatSource_.quantity() + filterScale_*heatSourceRoc_.quantity();
// (b) for ess. bcs
// form rhs : {sum_a (N_Ia * v_ia * f_ia) - 0.5*(dtheta/dt)_I}
const DENS_MAT & myNodalAtomicPower(nodalAtomicPower_->quantity());
const DIAG_MAT & myMdMassMatrix(mdMassMatrix_.quantity());
const DENS_MAT & myNodalTemperatureRoc(nodalTemperatureRoc_.quantity());
const DENS_MAT & myNodalTemperature2Roc(nodalTemperature2Roc_.quantity());
for (int i = 0; i < nNodes_; i++) {
if (prescribedDataMgr_->is_fixed(i,TEMPERATURE,0)) {
rhs_nodes(i,0) = 0.5*(myNodalAtomicPower(i,0) - myMdMassMatrix(i,i)*(myNodalTemperatureRoc(i,0)+ filterScale_*myNodalTemperature2Roc(i,0)));
}
}
}
//--------------------------------------------------------
// output:
// adds all relevant output to outputData
//--------------------------------------------------------
void ThermostatPowerVerletFiltered::output(OUTPUT_LIST & outputData)
{
outputData["lambda"] = &(lambda_->set_quantity());
outputData["nodalLambdaPower"] = &(lambdaPowerFiltered_->set_quantity());
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ThermostatHooverVerletFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to ATC and thermostat data
//--------------------------------------------------------
ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(Thermostat * thermostat) :
ThermostatPowerVerletFiltered(thermostat),
lambdaHoover_(NULL),
nodalAtomicHooverLambdaPower_(NULL)
{
// consistent with stage 3 of ATC_Method::initialize
lambdaHoover_ = thermostat_->regulator_data("LambdaHoover",1);
}
//--------------------------------------------------------
// constructor_transfers
// instantiates or obtains all dependency managed data
//--------------------------------------------------------
void ThermostatHooverVerletFiltered::construct_transfers()
{
ThermostatPowerVerletFiltered::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
FtaShapeFunctionProlongation * atomHooverLambdas = new FtaShapeFunctionProlongation(atc_,
lambdaHoover_,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_per_atom_quantity(atomHooverLambdas,
regulatorPrefix_+"AtomHooverLambda");
AtomicThermostatForce * atomHooverThermostatForces = new AtomicThermostatForce(atc_,atomHooverLambdas);
interscaleManager.add_per_atom_quantity(atomHooverThermostatForces,
regulatorPrefix_+"AtomHooverThermostatForce");
SummedAtomicQuantity<double> * atomTotalThermostatForces =
new SummedAtomicQuantity<double>(atc_,atomThermostatForces_,atomHooverThermostatForces);
interscaleManager.add_per_atom_quantity(atomTotalThermostatForces,
regulatorPrefix_+"AtomTotalThermostatForce");
atomThermostatForces_ = atomTotalThermostatForces;
// transfers dependent on time integration method
DotTwiceKineticEnergy * atomicHooverLambdaPower =
new DotTwiceKineticEnergy(atc_,atomHooverThermostatForces);
interscaleManager.add_per_atom_quantity(atomicHooverLambdaPower,
regulatorPrefix_+"AtomicHooverLambdaPower");
nodalAtomicHooverLambdaPower_ = new AtfShapeFunctionRestriction(atc_,
atomicHooverLambdaPower,
interscaleManager.per_atom_sparse_matrix("Interpolant"));
interscaleManager.add_dense_matrix(nodalAtomicHooverLambdaPower_,
regulatorPrefix_+"NodalAtomicHooverLambdaPower");
}
//--------------------------------------------------------
// compute_thermostat:
// sets up and solves the thermostat equations since
// they are the same at different parts of the time
// step
//--------------------------------------------------------
void ThermostatHooverVerletFiltered::compute_thermostat(double dt)
{
// apply prescribed/extrinsic sources and fixed nodes
ThermostatPowerVerletFiltered::compute_thermostat(0.5*dt);
_nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); // save power from lambda in power-based thermostat
// set up Hoover rhs
set_hoover_rhs(_rhs_);
// solve linear system for lambda
DENS_MAT & myLambda(lambdaHoover_->set_quantity());
solve_for_lambda(_rhs_,myLambda);
// compute force applied by lambda
// compute nodal atomic power from Hoover coupling
// only add in contribution to uncoupled nodes
if (atomicRegulator_->use_localized_lambda())
add_to_lambda_power(atomThermostatForces_->quantity(),0.5*dt);
}
//--------------------------------------------------------
// set_hoover_rhs:
// sets up the right-hand side for fixed value,
// i.e. Hoover coupling
//--------------------------------------------------------
void ThermostatHooverVerletFiltered::set_hoover_rhs(DENS_MAT & rhs)
{
// form rhs : sum_a (N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I
rhs = nodalAtomicPower_->quantity();
rhs -= mdMassMatrix_.quantity()*(nodalTemperatureRoc_.quantity() + filterScale_*nodalTemperature2Roc_.quantity());
rhs /= 2.;
}
//--------------------------------------------------------
// add_to_nodal_lambda_power:
// determines the power exerted by the Hoover
// thermostat at each FE node
//--------------------------------------------------------
void ThermostatHooverVerletFiltered::add_to_lambda_power(const DENS_MAT & myLambdaForce,
double dt)
{
_myNodalLambdaPower_ = nodalAtomicHooverLambdaPower_->quantity();
const INT_ARRAY nodeToOverlapMap(nodeToOverlapMap_->quantity());
for (int i = 0; i < nNodes_; ++i) {
if (nodeToOverlapMap(i,0)==-1)
_nodalAtomicLambdaPowerOut_(i,0) += _myNodalLambdaPower_(i,0);
else
_myNodalLambdaPower_(i,0) = 0.;
}
timeFilter_->apply_post_step1(lambdaPowerFiltered_->set_quantity(),_myNodalLambdaPower_,dt);
}
//--------------------------------------------------------
// add_to_rhs:
// determines what if any contributions to the
// finite element equations are needed for
// consistency with the thermostat
//--------------------------------------------------------
void ThermostatHooverVerletFiltered::add_to_rhs(FIELDS & rhs)
{
rhs[TEMPERATURE] += lambdaPowerFiltered_->quantity();
}
};