// 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 \n - control_type (string) = none | rescale | hoover | flux\n fix_modify AtC transfer thermal control rescale \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 \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 fix_modify AtC transfer thermal control none \n fix_modify AtC transfer thermal control rescale 10 \n fix_modify AtC transfer thermal control hoover \n fix_modify AtC transfer thermal control flux bndy_faces \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 > & 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); } };