forked from lijiext/lammps
1580 lines
60 KiB
C++
1580 lines
60 KiB
C++
// ATC_Transfer Headers
|
|
#include "Thermostat.h"
|
|
#include "CG.h"
|
|
#include "ATC_Error.h"
|
|
#include "PrescribedDataManager.h"
|
|
#include "TimeIntegrator.h"
|
|
|
|
namespace ATC {
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class Thermostat
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
Thermostat::Thermostat(ATC_Transfer * atcTransfer) :
|
|
AtomicRegulator(atcTransfer),
|
|
thermostatType_(NONE)
|
|
{
|
|
// 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;
|
|
|
|
// thermostat type
|
|
/*! \page man_thermal_control fix_modify AtC transfer thermal control
|
|
\section syntax
|
|
fix_modify AtC transfer thermal control <control_type> <optional args>\n
|
|
- control_type (string) = none | rescale | hoover | flux\n
|
|
|
|
fix_modify AtC transfer thermal control rescale <frequency>\n
|
|
- frequency (int) = time step frequency for applying velocity rescaling \n
|
|
|
|
fix_modify AtC transfer thermal control hoover \n
|
|
|
|
fix_modify AtC transfer thermal control flux <boundary_integration_type> <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 transfer thermal control none </TT> \n
|
|
<TT> fix_modify AtC transfer thermal control rescale 10 </TT> \n
|
|
<TT> fix_modify AtC transfer thermal control hoover </TT> \n
|
|
<TT> fix_modify AtC transfer thermal control flux 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
|
|
*/
|
|
int argIndex = 0;
|
|
if (strcmp(arg[argIndex],"none")==0) { // restore defaults
|
|
thermostatType_ = NONE;
|
|
howOften_ = 1;
|
|
boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE;
|
|
foundMatch = true;
|
|
}
|
|
else if (strcmp(arg[argIndex],"rescale")==0) {
|
|
argIndex++;
|
|
howOften_ = atoi(arg[argIndex]);
|
|
if (howOften_ < 1) {
|
|
throw ATC_Error(0,"Bad rescaling thermostat frequency");
|
|
}
|
|
else {
|
|
thermostatType_ = RESCALE;
|
|
boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE;
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
else if (strcmp(arg[argIndex],"hoover")==0) {
|
|
thermostatType_ = HOOVER;
|
|
howOften_ = 1;
|
|
boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE;
|
|
foundMatch = true;
|
|
}
|
|
else if (strcmp(arg[argIndex],"flux")==0) {
|
|
thermostatType_ = FLUX;
|
|
howOften_ = 1;
|
|
argIndex++;
|
|
boundaryIntegrationType_ = atcTransfer_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_);
|
|
foundMatch = true;
|
|
}
|
|
|
|
if (!foundMatch)
|
|
foundMatch = AtomicRegulator::modify(narg,arg);
|
|
if (foundMatch)
|
|
needReset_ = true;
|
|
return foundMatch;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// reset_nodal_lambda_power:
|
|
// resets the thermostat generated power to a
|
|
// prescribed value
|
|
//--------------------------------------------------------
|
|
void Thermostat::reset_lambda_power(DENS_MAT & target)
|
|
{
|
|
for (int i = 0; i < nNodes_; ++i)
|
|
lambdaPowerFiltered_(i) = target(i,0);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// initialize:
|
|
// sets up methods before a run
|
|
// NOTE we currently only include time filter
|
|
// 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 Thermostat::initialize()
|
|
{
|
|
//TimeIntegrationType myIntegrationType = (atcTransfer_->get_time_integrator())->get_time_integration_type();
|
|
// HACK until thermal time integrator is implemented
|
|
TimeIntegrator::TimeIntegrationType myIntegrationType = TimeIntegrator::VERLET;
|
|
TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager();
|
|
|
|
// reset data if needed and perform any error/conflict checking
|
|
if (resetData_) {
|
|
AtomicRegulator::reset_data();
|
|
|
|
// set up storage
|
|
lambda_.reset(nNodes_,1);
|
|
nodalAtomicLambdaPower_.reset(nNodes_);
|
|
lambdaPowerFiltered_.reset(nNodes_);
|
|
}
|
|
|
|
if (needReset_ || timeFilterManager->need_reset() || timeFilterManager->end_equilibrate()) {
|
|
// eliminate existing methods
|
|
destroy();
|
|
if (timeFilterManager->need_reset()) {
|
|
if (timeFilter_)
|
|
delete timeFilter_;
|
|
if (myIntegrationType == TimeIntegrator::VERLET)
|
|
timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT);
|
|
else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
|
|
timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT);
|
|
}
|
|
|
|
if (timeFilterManager->filter_dynamics()) {
|
|
switch (thermostatType_) {
|
|
case NONE: {
|
|
break;
|
|
}
|
|
case RESCALE: { // error check, rescale and filtering not supported together
|
|
throw ATC_Error(0,"Cannot use rescaling thermostat with time filtering");
|
|
break;
|
|
}
|
|
case HOOVER: {
|
|
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
|
|
//regulatorMethod_ = new ThermostatHooverFiltered(this);
|
|
cout << "temporary line\n";
|
|
else
|
|
regulatorMethod_ = new ThermostatHooverVerletFiltered(this);
|
|
break;
|
|
}
|
|
case FLUX: {
|
|
if (atcTransfer_->use_localized_lambda())
|
|
throw ATC_Error(0,"Cannot use flux thermostating with localized lambda");
|
|
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
|
|
regulatorMethod_ = new ThermostatPowerFiltered(this);
|
|
else
|
|
regulatorMethod_ = new ThermostatPowerVerletFiltered(this);
|
|
break;
|
|
}
|
|
default:
|
|
throw ATC_Error(0,"Unknown thermostat type in Thermostat::initialize");
|
|
}
|
|
}
|
|
else {
|
|
switch (thermostatType_) {
|
|
case NONE: {
|
|
break;
|
|
}
|
|
case RESCALE: {
|
|
regulatorMethod_ = new ThermostatRescale(this);
|
|
break;
|
|
}
|
|
case HOOVER: {
|
|
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
|
|
regulatorMethod_ = new ThermostatHoover(this);
|
|
else
|
|
regulatorMethod_ = new ThermostatHooverVerlet(this);
|
|
break;
|
|
}
|
|
case FLUX: {
|
|
if (atcTransfer_->use_localized_lambda())
|
|
throw ATC_Error(0,"Cannot use flux thermostating with localized lambda");
|
|
if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP)
|
|
regulatorMethod_ = new ThermostatPower(this);
|
|
else
|
|
regulatorMethod_ = new ThermostatPowerVerlet(this);
|
|
break;
|
|
}
|
|
default:
|
|
throw ATC_Error(0,"Unknown thermostat type in Thermostat::initialize");
|
|
}
|
|
}
|
|
|
|
AtomicRegulator::reset_method();
|
|
}
|
|
|
|
AtomicRegulator::initialize();
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatShapeFunction
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ThermostatShapeFunction::ThermostatShapeFunction(Thermostat * thermostat) :
|
|
RegulatorShapeFunction(thermostat),
|
|
thermostat_(thermostat),
|
|
v_(atcTransfer_->get_v()),
|
|
mdMassMatrix_(atcTransfer_->get_mass_mat_md(TEMPERATURE))
|
|
{
|
|
fieldMask_(TEMPERATURE,FLUX) = true;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// set_weights:
|
|
// set the diagonal weighting matrix to be the atomic
|
|
// temperatures
|
|
//---------------------------------------------------------
|
|
void ThermostatShapeFunction::set_weights(DIAG_MAT & weights)
|
|
{
|
|
if (nLocalLambda_>0) {
|
|
DENS_VEC weightVector(nLocal_);
|
|
for (int i = 0; i < nLocal_; i++)
|
|
for (int j = 0; j < nsd_; j++)
|
|
weightVector(i) += v_[internalToAtom_(i)][j]*v_[internalToAtom_(i)][j];
|
|
DENS_VEC maskedWeightVector(nLocalLambda_);
|
|
maskedWeightVector = internalToOverlapMap_*weightVector;
|
|
weights.reset(maskedWeightVector);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// reset_nlocal:
|
|
// resets data dependent on local atom count
|
|
//--------------------------------------------------------
|
|
void ThermostatShapeFunction::reset_nlocal()
|
|
{
|
|
RegulatorShapeFunction::reset_nlocal();
|
|
if (nLocal_ > 0) {
|
|
atcTransfer_->compute_atomic_mass(atomicMass_);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatRescale
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ThermostatRescale::ThermostatRescale(Thermostat * thermostat) :
|
|
ThermostatShapeFunction(thermostat),
|
|
nodalTemperature_(atcTransfer_->get_field(TEMPERATURE))
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_post_corrector:
|
|
// apply the thermostat in the post corrector phase
|
|
//--------------------------------------------------------
|
|
void ThermostatRescale::apply_post_corrector(double dt)
|
|
{
|
|
// compute right-hand side
|
|
DENS_MAT rhs(nNodes_,1);
|
|
rhs = mdMassMatrix_*nodalTemperature_;
|
|
|
|
// solve equations
|
|
solve_for_lambda(rhs);
|
|
|
|
// application of rescaling lambda due
|
|
apply_to_atoms(v_,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_lambda_to_atoms:
|
|
// applies the velocity rescale with an existing lambda
|
|
// note oldAtomicQuantity and dt are not used
|
|
//--------------------------------------------------------
|
|
void ThermostatRescale::apply_to_atoms(double ** atomicQuantity,
|
|
const double dt)
|
|
{
|
|
if (nLocal_>0) {
|
|
DENS_MAT lambdaAtom(nLocal_,1);
|
|
atcTransfer_->prolong(lambda_,lambdaAtom);
|
|
|
|
double xi;
|
|
for (int i = 0; i < nLocal_; i++) {
|
|
xi = sqrt(lambdaAtom(i,0)/atomicMass_(i));
|
|
for (int j = 0; j < nsd_; j++) {
|
|
atomicQuantity[internalToAtom_(i)][j] *= xi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// output:
|
|
// adds all relevant output to outputData
|
|
//--------------------------------------------------------
|
|
void ThermostatRescale::output(double dt, OUTPUT_LIST & outputData)
|
|
{
|
|
outputData["lambda"] = &(lambda_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatGlc
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------
|
|
ThermostatGlc::ThermostatGlc(Thermostat * thermostat) :
|
|
ThermostatShapeFunction(thermostat),
|
|
timeFilter_(atomicRegulator_->get_time_filter()),
|
|
nodalAtomicLambdaPower_(thermostat_->get_nodal_atomic_lambda_power()),
|
|
lambdaPowerFiltered_(thermostat_->get_filtered_lambda_power()),
|
|
lambdaForce_(atomicRegulator_->get_lambda_force()),
|
|
isFixedNode_(atcTransfer_->get_fixed_node_flags(TEMPERATURE))
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_to_atoms:
|
|
// determines what if any contributions to the
|
|
// atomic moition is needed for
|
|
// consistency with the thermostat
|
|
//--------------------------------------------------------
|
|
void ThermostatGlc::apply_to_atoms(double ** atomicQuantity,
|
|
const DENS_MAT & lambdaForce,
|
|
double dt)
|
|
{
|
|
if (nLocal_>0) {
|
|
// explicit update
|
|
for (int i = 0; i < nLocal_; i++) {
|
|
for (int j = 0; j < nsd_; j++) {
|
|
atomicQuantity[internalToAtom_(i)][j] += dt*lambdaForce(i,j)/atomicMass_(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// compute_lambda_force:
|
|
// computes the atomic force applied by lambda
|
|
//---------------------------------------------------------
|
|
void ThermostatGlc::compute_lambda_force(double * const * atomicQuantity,
|
|
DENS_MAT & lambdaForce)
|
|
{
|
|
if (nLocal_>0) {
|
|
// scaled prolongation to (unique) nodes
|
|
DENS_MAT lambdaAtom(nLocal_,1);
|
|
atcTransfer_->prolong(lambda_,lambdaAtom);
|
|
|
|
// explicit update
|
|
for (int i = 0; i < nLocal_; i++) {
|
|
for (int j = 0; j < nsd_; j++) {
|
|
lambdaForce(i,j) = -lambdaAtom(i,0)*atomicQuantity[internalToAtom_(i)][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatPower
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatPower::ThermostatPower(Thermostat * thermostat) :
|
|
ThermostatGlc(thermostat),
|
|
nodalTemperature_(atcTransfer_->get_field(TEMPERATURE)),
|
|
nodalAtomicTemperature_(atcTransfer_->get_atomic_field(TEMPERATURE)),
|
|
heatSource_(atcTransfer_->get_atomic_source(TEMPERATURE)),
|
|
nodalWeights_(atcTransfer_->get_shape_function_weights()),
|
|
myAtomicVelocity_(NULL),
|
|
f_(atcTransfer_->get_f()),
|
|
nLocalTotal_(0),
|
|
lambdaMaxIterations_(3),
|
|
lambdaTolerance_(1.e-14),
|
|
filterCoefficient_(1.),
|
|
Nstar_(atcTransfer_->get_shape_function_ghost_overlap()),
|
|
ghostToAtom_(atcTransfer_->get_ghost_to_atom_map())
|
|
{
|
|
deltaTemperature_.reset(nNodes_,1);
|
|
deltaNodalAtomicTemperature_.reset(nNodes_,1);
|
|
nodalTemperatureRoc_.reset(nNodes_,1);
|
|
|
|
// sets up time filter for cases where variables temporally filtered
|
|
TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager();
|
|
if (!timeFilterManager->end_equilibrate()) {
|
|
// NOTE do we need a good initial guess for lambdaForce_?
|
|
nodalAtomicLambdaPower_ = 0.;
|
|
lambdaPowerFiltered_ = 0.;
|
|
timeFilter_->initialize(lambdaPowerFiltered_);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// Destructor
|
|
// delete thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatPower::~ThermostatPower()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// destroy
|
|
// deletes allocated memory
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::destroy()
|
|
{
|
|
if (myAtomicVelocity_) {
|
|
for (int i = 0; i < nLocalTotal_; i++)
|
|
delete [] myAtomicVelocity_[i];
|
|
delete [] myAtomicVelocity_;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// reset_nlocal:
|
|
// resizes myAtomicVelocity force if necessary
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::reset_nlocal()
|
|
{
|
|
destroy();
|
|
ThermostatGlc::reset_nlocal();
|
|
nLocalTotal_ = atcTransfer_->get_nlocal_total();
|
|
if (nLocal_ > 0) {
|
|
myAtomicVelocity_ = new double*[nLocalTotal_];
|
|
for (int i = 0; i < nLocalTotal_; i++)
|
|
myAtomicVelocity_[i] = new double[nsd_];
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_predictor:
|
|
// apply the thermostat to the atoms in the first step
|
|
// of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::apply_pre_predictor(double dt)
|
|
{
|
|
// initialize delta temperatures
|
|
deltaTemperature_ = -1.*nodalTemperature_;
|
|
deltaNodalAtomicTemperature_ = -1.*nodalAtomicTemperature_;
|
|
|
|
// apply lambda force to atoms and compute instantaneous lambda power
|
|
apply_to_atoms(v_,lambdaForce_,dt);
|
|
|
|
// update nodal variables
|
|
update_nodal_quantities_predictor(dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_predictor:
|
|
// updates all needed nodal quantities after the
|
|
// predictor step
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::update_nodal_quantities_predictor(double dt)
|
|
{
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
boundaryFluxRoc,
|
|
TEMPERATURE);
|
|
nodalTemperatureRoc_ += boundaryFluxRoc;
|
|
|
|
// update FE temperature
|
|
nodalTemperature_ += dt*nodalTemperatureRoc_;
|
|
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_pre_corrector:
|
|
// apply the thermostat to the atoms in the first part
|
|
// of the corrector step of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::apply_pre_corrector(double dt)
|
|
{
|
|
// copy velocities over into temporary storage
|
|
if (nLocal_ > 0) {
|
|
for (int i = 0; i < nLocalTotal_; i++)
|
|
for (int j = 0; j < nsd_; j++)
|
|
myAtomicVelocity_[i][j] = v_[i][j];
|
|
}
|
|
|
|
// compute predicted changes in FE and nodal atomic temperatures
|
|
compute_delta_nodal_atomic_temperature(dt);
|
|
DENS_MAT myDeltaTemperature(deltaTemperature_);
|
|
deltaTemperature_ += nodalTemperature_;
|
|
|
|
// set up rhs for lambda equation
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_thermostat_rhs(rhs,dt);
|
|
|
|
// solve linear system for lambda guess
|
|
solve_for_lambda(rhs);
|
|
|
|
// iterate to solution
|
|
iterate_lambda(rhs,v_,dt);
|
|
|
|
// compute force applied by lambda
|
|
compute_lambda_force(v_,lambdaForce_);
|
|
|
|
// apply lambda force to atoms and compute instantaneous lambda power
|
|
apply_to_atoms(myAtomicVelocity_,lambdaForce_,dt);
|
|
|
|
// update nodal variables
|
|
update_nodal_quantities_pre_corrector(dt);
|
|
|
|
// reset temporary variables
|
|
deltaTemperature_ = myDeltaTemperature;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// compute_delta_nodal_atomic_temperature:
|
|
// computes the change in the nodal atomic temperature
|
|
// that has occured over the past timestep
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::compute_delta_nodal_atomic_temperature(double dt)
|
|
{
|
|
// set delta temperatures based on predicted atomic velocities
|
|
DENS_MAT atomicTemperature(nLocal_,1);
|
|
atcTransfer_->compute_atomic_temperature(atomicTemperature, v_);
|
|
DENS_MAT nodalAtomicTemperature(nNodes_,1);
|
|
// NOTE change this when we use physical mass matrices
|
|
atcTransfer_->restrict(atomicTemperature,nodalAtomicTemperature);
|
|
deltaNodalAtomicTemperature_ += nodalAtomicTemperature;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_pre_corrector:
|
|
// updates all needed nodal quantities after the
|
|
// pre-corrector step
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::update_nodal_quantities_pre_corrector(double dt)
|
|
{
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
boundaryFluxRoc,
|
|
TEMPERATURE);
|
|
nodalTemperatureRoc_ += boundaryFluxRoc;
|
|
|
|
// update FE temperature
|
|
nodalTemperature_ += dt*nodalTemperatureRoc_;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// undo_update_nodal_quantities_pre_corrector:
|
|
// undoes the nodal quantities update from after the
|
|
// pre-corrector step
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::undo_update_nodal_quantities_pre_corrector(double dt)
|
|
{
|
|
// remove predicted power effects
|
|
nodalTemperature_ -= dt*nodalTemperatureRoc_;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_post_corrector:
|
|
// apply the thermostat to the atoms in the second part
|
|
// of the corrector step of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::apply_post_corrector(double dt)
|
|
{
|
|
// remove predicted power effects
|
|
undo_update_nodal_quantities_pre_corrector(dt);
|
|
|
|
// set delta FE temperature
|
|
deltaTemperature_ += nodalTemperature_;
|
|
|
|
// set up rhs for lambda equation
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_thermostat_rhs(rhs,dt);
|
|
|
|
// solve linear system for lambda guess
|
|
solve_for_lambda(rhs);
|
|
|
|
// iterate to solution
|
|
iterate_lambda(rhs,myAtomicVelocity_,dt);
|
|
|
|
// compute force applied by lambda
|
|
compute_lambda_force(myAtomicVelocity_,lambdaForce_);
|
|
|
|
// apply lambda force to atoms and compute instantaneous lambda power
|
|
apply_to_atoms(v_,lambdaForce_,dt);
|
|
|
|
// update nodal variables
|
|
update_nodal_quantities_post_corrector(dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_post_corrector:
|
|
// updates all needed nodal quantities after the
|
|
// post-corrector step
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::update_nodal_quantities_post_corrector(double dt)
|
|
{
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_post_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
boundaryFluxRoc,
|
|
TEMPERATURE);
|
|
nodalTemperatureRoc_ += boundaryFluxRoc;
|
|
|
|
// update FE temperature
|
|
nodalTemperature_ += dt*nodalTemperatureRoc_;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_to_atoms:
|
|
// add the thermostat force to the atoms and compute
|
|
// the instantaneous induced power
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::apply_to_atoms(double ** atomicVelocity,
|
|
const DENS_MAT & lambdaForce,
|
|
double dt)
|
|
{
|
|
// compute initial contributions to lambda power
|
|
DENS_VEC atomicTemperature(nLocal_);
|
|
DENS_VEC nodalAtomicTemperature(nNodes_);
|
|
atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity);
|
|
atcTransfer_->restrict_unscaled(atomicTemperature,nodalAtomicTemperature);
|
|
nodalAtomicLambdaPower_ = -1.*nodalAtomicTemperature;
|
|
|
|
ThermostatGlc::apply_to_atoms(atomicVelocity,lambdaForce,dt);
|
|
|
|
// finalize lambda power
|
|
atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity);
|
|
atcTransfer_->restrict_unscaled(atomicTemperature,nodalAtomicTemperature);
|
|
nodalAtomicLambdaPower_ += nodalAtomicTemperature;
|
|
nodalAtomicLambdaPower_ = (1./dt)*nodalAtomicLambdaPower_;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// set_weights:
|
|
// set the diagonal weighting matrix to be the atomic
|
|
// temperatures
|
|
//---------------------------------------------------------
|
|
void ThermostatPower::set_weights(DIAG_MAT & weights)
|
|
{
|
|
if (nLocalLambda_>0) {
|
|
DENS_VEC weightVector(nLocal_);
|
|
DENS_VEC maskedWeightVector(nLocalLambda_);
|
|
atcTransfer_->compute_atomic_temperature(weightVector,v_,myAtomicVelocity_);
|
|
maskedWeightVector = internalToOverlapMap_*weightVector;
|
|
weights.reset(maskedWeightVector);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// set_thermostat_rhs:
|
|
// sets up the right-hand side including boundary
|
|
// fluxes (coupling & prescribed), heat sources, and
|
|
// fixed (uncoupled) nodes
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::set_thermostat_rhs(DENS_MAT & rhs_nodes,
|
|
double dt)
|
|
{
|
|
// NOTE is the scaling right? (a) E/t/kB & (b) T/t/kB
|
|
// 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
|
|
// NOTE change coef and nodalWeights when physical mass matrices are used
|
|
// and remove rhoCp
|
|
double rhoCp = LammpsInterface::instance()->heat_capacity();
|
|
double k_boltzmann = LammpsInterface::instance()->kBoltzmann();
|
|
double coef = filterCoefficient_*rhoCp*2./(nsd_*k_boltzmann);
|
|
rhs_nodes = coef*heatSource_*nodalWeights_;
|
|
|
|
// (b) for essential bcs (fixed nodes) :
|
|
// form rhs : (delThetaV - delTheta)/dt
|
|
DENS_MAT rhs_nodes_prescribed(nNodes_,1);
|
|
rhs_nodes_prescribed = (1./dt)*filterCoefficient_*(deltaNodalAtomicTemperature_-deltaTemperature_);
|
|
|
|
// replace rhs for prescribed nodes
|
|
// NOTE can we make is_fixed member data?
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
if (isFixedNode_(i,0)) {
|
|
rhs_nodes(i,0) = rhs_nodes_prescribed(i,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// iterate_lambda:
|
|
// iteratively solves the equations for lambda
|
|
// for the higher order dt corrections, assuming
|
|
// an initial guess for lambda
|
|
//--------------------------------------------------------
|
|
void ThermostatPower::iterate_lambda(const MATRIX & rhs,
|
|
double * const * atomicVelocity,
|
|
double dt)
|
|
{
|
|
DENS_VEC lambdaOverlap(nNodeOverlap_);
|
|
atcTransfer_->map_unique_to_overlap(lambda_, lambdaOverlap);
|
|
|
|
DENS_VEC atomicTemperature;
|
|
DENS_MAT lambdaAtom;
|
|
DENS_MAT lambdaSquaredT;
|
|
if (nLocalLambda_>0) {
|
|
lambdaAtom.reset(nLocal_,1);
|
|
lambdaSquaredT.reset(nLocal_,1);
|
|
atomicTemperature.reset(nLocal_);
|
|
atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity);
|
|
}
|
|
|
|
DENS_MAT lambdaOld(nNodes_,1);
|
|
DENS_MAT rhsOverlap(nNodeOverlap_,1);
|
|
DENS_MAT rhsTotalLocal(nNodes_,1);
|
|
DENS_MAT rhsTotal(nNodes_,1);
|
|
// solve assuming we get initial guess for lambda
|
|
for (int i = 0; i < lambdaMaxIterations_; ++i) {
|
|
lambdaOld = lambda_;
|
|
|
|
// assemble quadratic term
|
|
if (nLocalLambda_ > 0) {
|
|
atcTransfer_->prolong_scaled(lambda_, lambdaAtom);
|
|
for (int i = 0; i < nLocal_; i++) {
|
|
lambdaSquaredT(i,0) = lambdaAtom(i,0)*lambdaAtom(i,0)*atomicTemperature(i);
|
|
}
|
|
rhsOverlap = (dt/4.)*shapeFunctionMatrix_.transMat(internalToOverlapMap_*lambdaSquaredT);
|
|
atcTransfer_->map_overlap_to_unique(rhsOverlap,rhsTotalLocal);
|
|
}
|
|
LammpsInterface::instance()->allsum(rhsTotalLocal.get_ptr(),rhsTotal.get_ptr(),rhsTotal.size());
|
|
|
|
// the system with the new rhs
|
|
rhsTotal += rhs;
|
|
solve_for_lambda(rhsTotal);
|
|
|
|
// check convergence
|
|
DENS_MAT difference = lambda_-lambdaOld;
|
|
if (difference.col_norm()/lambdaOld.col_norm() < lambdaTolerance_) break;
|
|
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatHoover
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatHoover::ThermostatHoover(Thermostat * thermostat) :
|
|
ThermostatPower(thermostat),
|
|
nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map())
|
|
{
|
|
lambdaForceHoover_.reset(nLocal_,nsd_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// reset_nlocal:
|
|
// resizes Hoover lambda force if necessary
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::reset_nlocal()
|
|
{
|
|
ThermostatPower::reset_nlocal();
|
|
if (nLocal_ > 0)
|
|
lambdaForceHoover_.reset(nLocal_,nsd_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_predictor:
|
|
// apply the thermostat to the atoms in the first step
|
|
// of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::apply_pre_predictor(double dt)
|
|
{
|
|
ThermostatPower::apply_pre_predictor(dt);
|
|
|
|
// apply lambda force to atoms and compute instantaneous lambda power
|
|
DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_);
|
|
apply_to_atoms(v_,lambdaForceHoover_,dt);
|
|
|
|
// compute nodal atomic power from Hoover coupling
|
|
// only add in contribution to uncoupled nodes
|
|
if (atcTransfer_->use_localized_lambda())
|
|
add_to_lambda_power_predictor(dt);
|
|
else
|
|
nodalAtomicLambdaPower_ = 0.;
|
|
|
|
// finish updating lambda power
|
|
nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add_to_lambda_power_predictor:
|
|
// adds Hoover contributions to the lambda power term
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::add_to_lambda_power_predictor(double dt)
|
|
{
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
|
|
// update FE temperature
|
|
for (int i = 0; i < nNodes_; ++i) {
|
|
if (nodeToOverlapMap_(i)==-1)
|
|
nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0);
|
|
else
|
|
nodalAtomicLambdaPower_(i) = 0.;
|
|
}
|
|
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_pre_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_pre_corrector:
|
|
// apply the thermostat to the atoms in the first part
|
|
// of the corrector step of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::apply_pre_corrector(double dt)
|
|
{
|
|
ThermostatPower::apply_pre_corrector(dt);
|
|
|
|
// set up Hoover rhs
|
|
DENS_MAT myDeltaTemperature(deltaTemperature_);
|
|
deltaTemperature_ += nodalTemperature_;
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_hoover_rhs(rhs,dt);
|
|
|
|
// solve linear system for lambda
|
|
solve_for_lambda(rhs);
|
|
|
|
// iterate to solution
|
|
iterate_lambda(rhs,v_,dt);
|
|
|
|
// compute force applied by lambda
|
|
compute_lambda_force(v_,lambdaForceHoover_);
|
|
|
|
// apply the force to the atoms
|
|
DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_);
|
|
apply_to_atoms(myAtomicVelocity_,lambdaForceHoover_,dt);
|
|
|
|
// compute nodal atomic power from Hoover coupling
|
|
// only add in contribution to uncoupled nodes
|
|
if (atcTransfer_->use_localized_lambda())
|
|
add_to_lambda_power_pre_corrector(dt);
|
|
else
|
|
nodalAtomicLambdaPower_ = 0.;
|
|
|
|
// finish updating lambda power
|
|
nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower;
|
|
|
|
// reset temporary variables
|
|
deltaTemperature_ = myDeltaTemperature;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add_to_lambda_power_pre_corrector:
|
|
// adds Hoover contributions to the lambda power term
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::add_to_lambda_power_pre_corrector(double dt)
|
|
{
|
|
DENS_MAT myNodalTemperatureRoc(nodalTemperatureRoc_);
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
|
|
// update FE temperature
|
|
for (int i = 0; i < nNodes_; ++i) {
|
|
if (nodeToOverlapMap_(i)==-1)
|
|
nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0);
|
|
else {
|
|
nodalTemperatureRoc_(i,0) = 0.;
|
|
nodalAtomicLambdaPower_(i) = 0.;
|
|
}
|
|
}
|
|
|
|
// correct nodal temperature rate of change
|
|
nodalTemperatureRoc_ += myNodalTemperatureRoc;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_post_corrector:
|
|
// apply the thermostat to the atoms in the second part
|
|
// of the corrector step of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::apply_post_corrector(double dt)
|
|
{
|
|
ThermostatPower::apply_post_corrector(dt);
|
|
|
|
// set up Hoover rhs
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_hoover_rhs(rhs,dt);
|
|
|
|
// solve linear system for lambda
|
|
solve_for_lambda(rhs);
|
|
|
|
// iterate to solution
|
|
iterate_lambda(rhs,myAtomicVelocity_,dt);
|
|
|
|
// compute force applied by lambda
|
|
compute_lambda_force(myAtomicVelocity_,lambdaForceHoover_);
|
|
|
|
// apply the force to the atoms
|
|
DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_);
|
|
apply_to_atoms(v_,lambdaForceHoover_,dt);
|
|
|
|
// compute nodal atomic power from Hoover coupling
|
|
// only add in contribution to uncoupled nodes
|
|
if (atcTransfer_->use_localized_lambda())
|
|
add_to_lambda_power_post_corrector(dt);
|
|
else
|
|
nodalAtomicLambdaPower_ = 0.;
|
|
|
|
// finish updating lambda power
|
|
nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add_to_lambda_power_post_corrector:
|
|
// adds Hoover contributions to the lambda power term
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::add_to_lambda_power_post_corrector(double dt)
|
|
{
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_post_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
|
|
// update FE temperature
|
|
for (int i = 0; i < nNodes_; ++i) {
|
|
if (nodeToOverlapMap_(i)==-1)
|
|
nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0);
|
|
else
|
|
nodalAtomicLambdaPower_(i) = 0.;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// set_hoover_rhs:
|
|
// sets up the right-hand side for Hoover coupling with
|
|
// boundary nodes
|
|
//--------------------------------------------------------
|
|
void ThermostatHoover::set_hoover_rhs(DENS_MAT & rhs_nodes,
|
|
double dt)
|
|
{
|
|
// form rhs : (delThetaV - delTheta)/dt
|
|
rhs_nodes = (1./dt)*filterCoefficient_*(deltaNodalAtomicTemperature_-deltaTemperature_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatPowerFiltered
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatPowerFiltered::ThermostatPowerFiltered(Thermostat * thermostat) :
|
|
ThermostatPower(thermostat)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_predictor:
|
|
// updates all needed nodal quantities after the
|
|
// predictor step
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerFiltered::update_nodal_quantities_predictor(double dt)
|
|
{
|
|
// update nodal atomic temperature
|
|
//atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_,
|
|
// nodalTemperatureRoc_,
|
|
// TEMPERATURE);
|
|
// NOTE temporary until new mass matrices are used
|
|
DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights());
|
|
nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*lambdaPowerFiltered_;
|
|
nodalAtomicTemperature_ += dt*nodalTemperatureRoc_;
|
|
|
|
// update FE temperature
|
|
atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
//DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
//atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
// boundaryFluxRoc,
|
|
// TEMPERATURE);
|
|
//nodalTemperatureRoc_ += boundaryFluxRoc;
|
|
nodalTemperature_ += 0.5*dt*nodalTemperatureRoc_;
|
|
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// compute_delta_nodal_atomic_temperature:
|
|
// computes the change in the nodal atomic temperature
|
|
// that has occured over the past timestep
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerFiltered::compute_delta_nodal_atomic_temperature(double dt)
|
|
{
|
|
// compute next lambda power assuming zero new force
|
|
//DENS_VEC myLambdaPowerFiltered(lambdaPowerFiltered_);
|
|
DENS_VEC zeroNodalLambdaPower(nNodes_);
|
|
timeFilter_->apply_post_step1(lambdaPowerFiltered_,zeroNodalLambdaPower,dt);
|
|
|
|
// update nodal atomic temperature
|
|
//atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_,
|
|
// nodalTemperatureRoc_,
|
|
// TEMPERATURE);
|
|
// NOTE temporary until new mass matrices are used
|
|
DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights());
|
|
nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*lambdaPowerFiltered_;
|
|
nodalAtomicTemperature_ += dt*nodalTemperatureRoc_;
|
|
|
|
// update FE temperature
|
|
atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
nodalTemperature_ += .5*dt*nodalTemperatureRoc_;
|
|
|
|
deltaNodalAtomicTemperature_ += nodalAtomicTemperature_;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_pre_corrector:
|
|
// updates all needed nodal quantities after the
|
|
// pre-corrector step
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerFiltered::update_nodal_quantities_pre_corrector(double dt)
|
|
{
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
|
|
double filterCoefficient = timeFilter_->get_unfiltered_coefficient_post_s1(dt);
|
|
|
|
//DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
//atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
// boundaryFluxRoc,
|
|
// TEMPERATURE);
|
|
//nodalTemperatureRoc_ = boundaryFluxRoc + filterCoefficient*nodalTemperatureRoc_;
|
|
nodalTemperatureRoc_ = 0.5*filterCoefficient*nodalTemperatureRoc_;
|
|
// update FE temperature
|
|
nodalTemperature_ += dt*nodalTemperatureRoc_;
|
|
}
|
|
|
|
// //--------------------------------------------------------
|
|
// // undo_update_nodal_quantities_pre_corrector:
|
|
// // undoes the update of all nodal quantities from after
|
|
// // the pre-corrector step
|
|
// //--------------------------------------------------------
|
|
// void ThermostatPowerFiltered::undo_update_nodal_quantities_pre_corrector(double dt)
|
|
// {
|
|
// double filterCoefficient = timeFilter_->get_unfiltered_coefficient_pre_s2(dt);
|
|
|
|
// // update FE temperature
|
|
// nodalTemperature_ -= dt*filterCoefficient*nodalTemperatureRoc_;
|
|
// }
|
|
|
|
//--------------------------------------------------------
|
|
// update_nodal_quantities_post_corrector:
|
|
// updates all needed nodal quantities after the
|
|
// post-corrector step
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerFiltered::update_nodal_quantities_post_corrector(double dt)
|
|
{
|
|
// finalize filtered lambda power
|
|
timeFilter_->apply_post_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
double filterCoefficient = timeFilter_->get_unfiltered_coefficient_post_s1(dt);
|
|
|
|
// update nodal atomic temperature
|
|
//atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
// nodalTemperatureRoc_,
|
|
// TEMPERATURE);
|
|
// NOTE temporary until new mass matrices are used
|
|
DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights());
|
|
nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*nodalAtomicLambdaPower_;
|
|
//nodalTemperatureRoc_.print("NTR");
|
|
nodalAtomicTemperature_ += filterCoefficient*dt*nodalTemperatureRoc_;
|
|
|
|
// update FE temperature
|
|
atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_,
|
|
nodalTemperatureRoc_,
|
|
TEMPERATURE);
|
|
//DENS_MAT boundaryFluxRoc(nNodes_,1);
|
|
//atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE],
|
|
// boundaryFluxRoc,
|
|
// TEMPERATURE);
|
|
//nodalTemperatureRoc_ = boundaryFluxRoc + filterCoefficient*nodalTemperatureRoc_;
|
|
nodalTemperatureRoc_ = .5*filterCoefficient*nodalTemperatureRoc_;
|
|
nodalTemperature_ += dt*nodalTemperatureRoc_;
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// set_thermostat_rhs:
|
|
// sets up the right-hand side including boundary
|
|
// fluxes (coupling & prescribed), heat sources, and
|
|
// fixed (uncoupled) nodes
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerFiltered::set_thermostat_rhs(DENS_MAT & rhs_nodes,
|
|
double dt)
|
|
{
|
|
filterCoefficient_ = 1./(timeFilter_->get_unfiltered_coefficient_post_s1(dt));
|
|
// NOTE change the 0.5 when we change how the lambda power is accounted
|
|
// NOTE change rhoCp^-1 when physical mass matrices are used
|
|
double rhoCp = LammpsInterface::instance()->heat_capacity();
|
|
heatSource_ += (0.5)*lambdaPowerFiltered_;
|
|
ThermostatPower::set_thermostat_rhs(rhs_nodes,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatPowerVerlet
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatPowerVerlet::ThermostatPowerVerlet(Thermostat * thermostat) :
|
|
ThermostatGlc(thermostat),
|
|
nodalTemperatureRoc_(atcTransfer_->get_field_roc(TEMPERATURE)),
|
|
heatSource_(atcTransfer_->get_atomic_source(TEMPERATURE)),
|
|
f_(atcTransfer_->get_f())
|
|
{
|
|
// sets up time filter for cases where variables temporally filtered
|
|
TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager();
|
|
if (!timeFilterManager->end_equilibrate()) {
|
|
nodalAtomicLambdaPower_ = 0.;
|
|
lambdaPowerFiltered_ = 0.;
|
|
timeFilter_->initialize(lambdaPowerFiltered_);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// apply_predictor:
|
|
// apply the thermostat to the atoms in the first step
|
|
// of the Verlet algorithm
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerVerlet::apply_pre_predictor(double dt)
|
|
{
|
|
compute_thermostat(dt);
|
|
|
|
// apply lambda force to atoms
|
|
apply_to_atoms(v_,lambdaForce_,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)
|
|
{
|
|
compute_thermostat(dt);
|
|
|
|
// apply lambda force to atoms
|
|
apply_to_atoms(v_,lambdaForce_,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_ + boundaryFlux_[TEMPERATURE];
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 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_;
|
|
|
|
// (b) for ess. bcs
|
|
// form rhs : {sum_a (2 * N_Ia * v_ia * f_ia) - (dtheta/dt)_I}
|
|
DENS_MAT atomicPower;
|
|
atcTransfer_->compute_atomic_power(atomicPower, v_, f_);
|
|
DENS_MAT rhs_nodes_prescribed(nNodes_,1);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes_prescribed);
|
|
rhs_nodes_prescribed -= 0.5*(mdMassMatrix_*nodalTemperatureRoc_);
|
|
|
|
// replace rhs for prescribed nodes
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
if (isFixedNode_(i,0)) {
|
|
rhs_nodes(i,0) = rhs_nodes_prescribed(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
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_thermostat_rhs(rhs);
|
|
|
|
// solve linear system for lambda
|
|
solve_for_lambda(rhs);
|
|
|
|
// compute force applied by lambda
|
|
compute_lambda_force(v_,lambdaForce_);
|
|
|
|
// compute nodal atomic power
|
|
compute_nodal_lambda_power(dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// compute_nodal_lambda_power:
|
|
// determines the power exerted by the thermostat at
|
|
// each FE node
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerVerlet::compute_nodal_lambda_power(double dt)
|
|
{
|
|
DENS_VEC atomicLambdaPower;
|
|
atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,lambdaForce_);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,nodalAtomicLambdaPower_);
|
|
nodalAtomicLambdaPower_ *= 2.; // accounts for kinetic definition of temperature
|
|
timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// output:
|
|
// adds all relevant output to outputData
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerVerlet::output(double dt, OUTPUT_LIST & outputData)
|
|
{
|
|
outputData["lambda"] = &(lambda_);
|
|
outputData["nodalLambdaPower"] = &(nodalAtomicLambdaPower_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatHooverVerlet
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatHooverVerlet::ThermostatHooverVerlet(Thermostat * thermostat) :
|
|
ThermostatPowerVerlet(thermostat),
|
|
nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map())
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 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(dt);
|
|
|
|
// set up Hoover rhs
|
|
DENS_MAT rhs(nNodes_);
|
|
set_hoover_rhs(rhs);
|
|
|
|
// solve linear system for lambda
|
|
solve_for_lambda(rhs);
|
|
|
|
// compute force applied by lambda
|
|
DENS_MAT lambdaForceHoover(nLocal_,nsd_);
|
|
compute_lambda_force(v_,lambdaForceHoover);
|
|
lambdaForce_ += lambdaForceHoover;
|
|
|
|
// compute nodal atomic power from Hoover coupling
|
|
// only add in contribution to uncoupled nodes
|
|
if (atcTransfer_->use_localized_lambda())
|
|
add_to_lambda_power(lambdaForceHoover,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_nodes)
|
|
{
|
|
// form rhs : sum_a ( N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I
|
|
DENS_MAT atomicPower(atcTransfer_->get_nlocal(),1);
|
|
atcTransfer_->compute_atomic_power(atomicPower, v_, f_);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes);
|
|
rhs_nodes -= 0.5*(mdMassMatrix_*nodalTemperatureRoc_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add_to_nodal_lambda_power:
|
|
// determines the power exerted by the Hoover
|
|
// thermostat at each FE node
|
|
//--------------------------------------------------------
|
|
void ThermostatHooverVerlet::add_to_lambda_power(DENS_MAT & myLambdaForce,
|
|
double dt)
|
|
{
|
|
DENS_VEC atomicLambdaPower;
|
|
atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,myLambdaForce);
|
|
DENS_VEC myNodalLambdaPower(nNodes_);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,myNodalLambdaPower);
|
|
myNodalLambdaPower *= 2.;
|
|
for (int i = 0; i < nNodes_; ++i) {
|
|
if (nodeToOverlapMap_(i)==-1)
|
|
nodalAtomicLambdaPower_(i) += myNodalLambdaPower(i);
|
|
else
|
|
myNodalLambdaPower(i) = 0.;
|
|
}
|
|
timeFilter_->apply_post_step1(lambdaPowerFiltered_,myNodalLambdaPower,dt);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class ThermostatPowerVerletFiltered
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(Thermostat * thermostat) :
|
|
ThermostatPowerVerlet(thermostat),
|
|
nodalTemperature2Roc_(atcTransfer_->get_field_2roc(TEMPERATURE)),
|
|
fieldsRoc_(atcTransfer_->get_fields_roc()),
|
|
filterScale_((atcTransfer_->get_time_filter_manager())->get_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.;
|
|
atcTransfer_->compute_boundary_flux(fieldMask_,
|
|
fieldsRoc_,
|
|
fluxRoc_);
|
|
|
|
// NOTE add time derivatives from sources, currently not implemented
|
|
|
|
// compute extrinsic model rate of change
|
|
(atcTransfer_->get_extrinsic_model_manager()).set_sources(fieldsRoc_,fluxRoc_);
|
|
heatSourceRoc_ = fluxRoc_[TEMPERATURE];
|
|
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 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(std::map<FieldName,DenseMatrix<double> > & rhs)
|
|
{
|
|
rhs[TEMPERATURE] += lambdaPowerFiltered_ + boundaryFlux_[TEMPERATURE];
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 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
|
|
// for (int i = 0; i < nNodes_; i++) {
|
|
// rhs_nodes(i) = heatSource_(i,0) + filterScale_*heatSourceRoc_(i,0);
|
|
// }
|
|
rhs_nodes = heatSource_ + filterScale_*heatSourceRoc_;
|
|
|
|
// (b) for ess. bcs
|
|
// form rhs : {sum_a (N_Ia * v_ia * f_ia) - 0.5*(dtheta/dt)_I}
|
|
DENS_MAT atomicPower;
|
|
atcTransfer_->compute_atomic_power(atomicPower, v_, f_);
|
|
DENS_MAT rhs_nodes_prescribed(nNodes_,1);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes_prescribed);
|
|
// DENS_VEC myTemperatureRoc(nNodes_);
|
|
// for (int i = 0; i < nNodes_; i++) {
|
|
// myTemperatureRoc(i) = nodalTemperatureRoc_(i,0) + filterScale_*nodalTemperature2Roc_(i,0);
|
|
// }
|
|
rhs_nodes_prescribed -= 0.5*(mdMassMatrix_*(nodalTemperatureRoc_ + filterScale_*nodalTemperature2Roc_));
|
|
|
|
// replace rhs for prescribed nodes
|
|
for (int i = 0; i < nNodes_; i++) {
|
|
if (isFixedNode_(i,0)) {
|
|
rhs_nodes(i,0) = rhs_nodes_prescribed(i,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// output:
|
|
// adds all relevant output to outputData
|
|
//--------------------------------------------------------
|
|
void ThermostatPowerVerletFiltered::output(double dt, OUTPUT_LIST & outputData)
|
|
{
|
|
outputData["lambda"] = &(lambda_);
|
|
outputData["nodalLambdaPower"] = &(lambdaPowerFiltered_);
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
// Class HooverVerletFiltered
|
|
//--------------------------------------------------------
|
|
//--------------------------------------------------------
|
|
|
|
//--------------------------------------------------------
|
|
// Constructor
|
|
// Grab references to ATC and thermostat data
|
|
//--------------------------------------------------------
|
|
ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(Thermostat * thermostat) :
|
|
ThermostatPowerVerletFiltered(thermostat),
|
|
nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map())
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// 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(dt);
|
|
|
|
// set up Hoover rhs
|
|
DENS_MAT rhs(nNodes_,1);
|
|
set_hoover_rhs(rhs);
|
|
|
|
// solve linear system for lambda
|
|
solve_for_lambda(rhs);
|
|
|
|
// NOTE modify these sections to be for hoover
|
|
// compute force applied by lambda
|
|
DENS_MAT lambdaForceHoover(nLocal_,nsd_);
|
|
compute_lambda_force(v_,lambdaForceHoover);
|
|
lambdaForce_ += lambdaForceHoover;
|
|
|
|
// compute nodal atomic power from Hoover coupling
|
|
// only add in contribution to uncoupled nodes
|
|
if (atcTransfer_->use_localized_lambda())
|
|
add_to_lambda_power(lambdaForceHoover,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_nodes)
|
|
{
|
|
// form rhs : sum_a (N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I
|
|
DENS_MAT atomicPower(atcTransfer_->get_nlocal(),1);
|
|
atcTransfer_->compute_atomic_power(atomicPower, v_, f_);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes);
|
|
// DENS_VEC myTemperatureRoc(nNodes_);
|
|
// for (int i = 0; i < nNodes_; i++) {
|
|
// myTemperatureRoc(i) = nodalTemperatureRoc_(i,0) + filterScale_*nodalTemperature2Roc_(i,0);
|
|
// }
|
|
rhs_nodes -= 0.5*(mdMassMatrix_*(nodalTemperatureRoc_ + filterScale_*nodalTemperature2Roc_));
|
|
}
|
|
|
|
//--------------------------------------------------------
|
|
// add_to_nodal_lambda_power:
|
|
// determines the power exerted by the Hoover
|
|
// thermostat at each FE node
|
|
//--------------------------------------------------------
|
|
void ThermostatHooverVerletFiltered::add_to_lambda_power(DENS_MAT & myLambdaForce,
|
|
double dt)
|
|
{
|
|
DENS_VEC atomicLambdaPower;
|
|
atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,myLambdaForce);
|
|
DENS_VEC myNodalLambdaPower(nNodes_);
|
|
atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,myNodalLambdaPower);
|
|
myNodalLambdaPower *= 2.;
|
|
for (int i = 0; i < nNodes_; ++i) {
|
|
if (nodeToOverlapMap_(i)==-1)
|
|
nodalAtomicLambdaPower_(i) += myNodalLambdaPower(i);
|
|
else
|
|
myNodalLambdaPower(i) = 0.;
|
|
}
|
|
timeFilter_->apply_post_step1(lambdaPowerFiltered_,myNodalLambdaPower,dt);
|
|
}
|
|
|
|
};
|