// ATC transfer headers #include "ThermalTimeIntegrator.h" #include "TransferOperator.h" #include "ATC_Coupling.h" #include "TimeFilter.h" #include "ATC_Error.h" #include "PerAtomQuantityLibrary.h" namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalTimeIntegrator //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ThermalTimeIntegrator::ThermalTimeIntegrator(ATC_Coupling * atc, TimeIntegrationType timeIntegrationType) : TimeIntegrator(atc, timeIntegrationType) { // do nothing } //-------------------------------------------------------- // modify // parses inputs and modifies state of the integrator //-------------------------------------------------------- bool ThermalTimeIntegrator::modify(int narg, char **arg) { bool foundMatch = false; int argIndex = 0; // time integration scheme /*! \page man_thermal_time_integration fix_modify AtC time_integration (thermal) \section syntax fix_modify AtC time_integration \n - descriptor (string) = time integration type \n various time integration methods for the finite elements\n \section description gear - atomic velocity update with 2nd order Verlet, nodal temperature update with 3rd or 4th order Gear, thermostats based on controlling power \n fractional_step - atomic velocity update with 2nd order Verlet, mixed nodal temperature update, 3/4 Gear for continuum and 2 Verlet for atomic contributions, thermostats based on controlling discrete energy changes\n \section examples fix_modify atc time_integration gear \n fix_modify atc time_integration fractional_step \n \section description \section related see \ref man_fix_atc \section default none */ if (strcmp(arg[argIndex],"gear")==0) { timeIntegrationType_ = GEAR; needReset_ = true; foundMatch = true; } else if (strcmp(arg[argIndex],"fractional_step")==0) { timeIntegrationType_ = FRACTIONAL_STEP; needReset_ = true; foundMatch = true; } return foundMatch; } //-------------------------------------------------------- // construct_methods // creates algorithm objects //-------------------------------------------------------- void ThermalTimeIntegrator::construct_methods() { if (atc_->reset_methods()) { if (timeIntegrationMethod_) delete timeIntegrationMethod_; if (timeFilterManager_->need_reset()) { switch (timeIntegrationType_) { case GEAR: { timeFilter_ = timeFilterManager_->construct(TimeFilterManager::IMPLICIT); atc_->set_mass_mat_time_filter(TEMPERATURE,TimeFilterManager::EXPLICIT); break; } case FRACTIONAL_STEP: { timeFilter_ = timeFilterManager_->construct(TimeFilterManager::EXPLICIT_IMPLICIT); atc_->set_mass_mat_time_filter(TEMPERATURE,TimeFilterManager::EXPLICIT_IMPLICIT); break; } default: throw ATC_Error("Uknown time integration type in ThermalTimeIntegrator::Initialize()"); } } if (timeFilterManager_->filter_dynamics()) { switch (timeIntegrationType_) { case GEAR: { timeIntegrationMethod_ = new ThermalTimeIntegratorGearFiltered(this); break; } case FRACTIONAL_STEP: { timeIntegrationMethod_ = new ThermalTimeIntegratorFractionalStepFiltered(this); break; } default: throw ATC_Error("Uknown time integration type in ThermalTimeIntegrator::Initialize()"); } } else { switch (timeIntegrationType_) { case GEAR: { timeIntegrationMethod_ = new ThermalTimeIntegratorGear(this); break; } case FRACTIONAL_STEP: { timeIntegrationMethod_ = new ThermalTimeIntegratorFractionalStep(this); break; } default: throw ATC_Error("Uknown time integration type in ThermalTimeIntegrator::Initialize()"); } } } } //-------------------------------------------------------- // pack_fields // add persistent variables to data list //-------------------------------------------------------- void ThermalTimeIntegrator::pack_fields(RESTART_LIST & data) { data["NodalAtomicPowerFiltered"] = & nodalAtomicPowerFiltered_.set_quantity(); data["NodalAtomicEnergyFiltered"] = & nodalAtomicEnergyFiltered_.set_quantity(); TimeIntegrator::pack_fields(data); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalIntegrationMethod //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab data from ATC //-------------------------------------------------------- ThermalIntegrationMethod::ThermalIntegrationMethod(ThermalTimeIntegrator * thermalTimeIntegrator) : TimeIntegrationMethod(thermalTimeIntegrator), timeFilter_(thermalTimeIntegrator->time_filter()), temperature_(atc_->field(TEMPERATURE)), temperatureRoc_(atc_->field_roc(TEMPERATURE)), temperature2Roc_(atc_->field_2roc(TEMPERATURE)), nodalAtomicTemperatureOut_(atc_->nodal_atomic_field(TEMPERATURE)), nodalAtomicTemperature_(NULL), temperatureRhs_(atc_->field_rhs(TEMPERATURE)), nodalAtomicPowerOut_(atc_->nodal_atomic_field_roc(TEMPERATURE)) { // do nothing } //-------------------------------------------------------- // construct_transfers // Grab existing managed quantities, // create the rest //-------------------------------------------------------- void ThermalIntegrationMethod::construct_transfers() { nodalAtomicTemperature_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicTemperature"); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalIntegratorGear //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ThermalTimeIntegratorGear::ThermalTimeIntegratorGear(ThermalTimeIntegrator * thermalTimeIntegrator) : ThermalIntegrationMethod(thermalTimeIntegrator), nodalAtomicPowerFiltered_(thermalTimeIntegrator->nodal_atomic_power_filtered()) { // do nothing } //-------------------------------------------------------- // construct_transfers // Grab existing managed quantities, // create the rest //-------------------------------------------------------- void ThermalTimeIntegratorGear::construct_transfers() { ThermalIntegrationMethod::construct_transfers(); InterscaleManager & interscaleManager = atc_->interscale_manager(); // add in power computation DotTwiceKineticEnergy * dotTwiceKineticEnergy = new DotTwiceKineticEnergy(atc_); interscaleManager.add_per_atom_quantity(dotTwiceKineticEnergy,"DotTwiceKineticEnergy"); nodalAtomicPower_ = new AtfShapeFunctionRestriction(atc_, dotTwiceKineticEnergy, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicPower_,"NodalAtomicPower"); } //-------------------------------------------------------- // initialize // initialize all data //-------------------------------------------------------- void ThermalTimeIntegratorGear::initialize() { ThermalIntegrationMethod::initialize(); // sets up time filter for cases where variables temporally filtered // this time integrator should use an implicit filter TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (timeFilterManager->need_reset()) { // Some time filters need the old value for the power timeFilter_->initialize(nodalAtomicPower_->quantity()); } if (!timeFilterManager->end_equilibrate()) { nodalAtomicPowerFiltered_.reset(atc_->num_nodes(),1); } if (!timeFilterManager->filter_dynamics()) { temperatureRhs_ = nodalAtomicPower_->quantity(); } } //-------------------------------------------------------- // pre_initial_integrate2 /// time integration before Verlet step 1 //-------------------------------------------------------- void ThermalTimeIntegratorGear::pre_initial_integrate2(double dt) { // Predict nodal temperatures and time derivatives based on FE data // use 3rd order Gear gear1_3_predict(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.quantity(),dt); } //-------------------------------------------------------- // post_final_integrate1 // time integration after Verlet step 2 //-------------------------------------------------------- void ThermalTimeIntegratorGear::post_final_integrate1(double dt) { const DENS_MAT & myNodalAtomicPower(nodalAtomicPower_->quantity()); timeFilter_->apply_post_step2(nodalAtomicPowerFiltered_.set_quantity(), myNodalAtomicPower,dt); temperatureRhs_ += myNodalAtomicPower; // Finish updating temperature _temperatureResidual_.resize(atc_->num_nodes(),1); atc_->apply_inverse_mass_matrix(temperatureRhs_.quantity(), _temperatureResidual_, TEMPERATURE); _temperatureResidual_ -= temperatureRoc_.quantity(); _temperatureResidual_ *= dt; gear1_3_correct(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), _temperatureResidual_,dt); } //-------------------------------------------------------- // post_process // do any post-processing calculations required for // output phase //-------------------------------------------------------- void ThermalTimeIntegratorGear::post_process() { nodalAtomicPowerOut_ = nodalAtomicPower_->quantity(); nodalAtomicTemperatureOut_ = nodalAtomicTemperature_->quantity(); } //-------------------------------------------------------- // finish // finalize state of nodal atomic quantities //-------------------------------------------------------- void ThermalTimeIntegratorGear::finish() { post_process(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalTimeIntegratorGearFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab data from ATC //-------------------------------------------------------- ThermalTimeIntegratorGearFiltered::ThermalTimeIntegratorGearFiltered(ThermalTimeIntegrator * thermalTimeIntegrator) : ThermalTimeIntegratorGear(thermalTimeIntegrator), temperature3Roc_(atc_->field_3roc(TEMPERATURE)) { // do nothing // specifically if history data is required and we need another time filter object for the fields } //-------------------------------------------------------- // pre_initial_integrate2 // time integration before Verlet step 1 //-------------------------------------------------------- void ThermalTimeIntegratorGearFiltered::pre_initial_integrate2(double dt) { // Predict nodal temperatures and time derivatives based on FE data // use 3rd order Gear gear1_4_predict(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), temperature3Roc_.quantity(),dt); } //-------------------------------------------------------- // post_final_integrate1 // first time integration computations // after Verlet step 2 //-------------------------------------------------------- void ThermalTimeIntegratorGearFiltered::post_final_integrate1(double dt) { DENS_MAT & myNodalAtomicPowerFiltered(nodalAtomicPowerFiltered_.set_quantity()); timeFilter_->apply_post_step2(myNodalAtomicPowerFiltered,nodalAtomicPower_->quantity(),dt); temperatureRhs_ += myNodalAtomicPowerFiltered; // Finish updating temperature _temperatureResidual_.resize(atc_->num_nodes(),1); atc_->apply_inverse_mass_matrix(temperatureRhs_.quantity(), _temperatureResidual_, TEMPERATURE); _temperatureResidual_ -= temperatureRoc_.quantity(); _temperatureResidual_ *= dt; gear1_4_correct(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), temperature3Roc_.set_quantity(), _temperatureResidual_,dt); } //-------------------------------------------------------- // post_final_integrate3 // third time integration computations // after Verlet step 2 //-------------------------------------------------------- void ThermalTimeIntegratorGearFiltered::post_final_integrate3(double dt) { // update filtered atomic temperature timeFilter_->apply_post_step2(nodalAtomicTemperatureOut_.set_quantity(), nodalAtomicTemperature_->quantity(),dt); } //-------------------------------------------------------- // post_process // do any post-processing calculations required for // output phase //-------------------------------------------------------- void ThermalTimeIntegratorGearFiltered::post_process() { nodalAtomicPowerOut_ = nodalAtomicPowerFiltered_.quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalIntegratorFractionalStep //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab data from ATC //-------------------------------------------------------- ThermalTimeIntegratorFractionalStep::ThermalTimeIntegratorFractionalStep(ThermalTimeIntegrator * thermalTimeIntegrator) : ThermalIntegrationMethod(thermalTimeIntegrator), nodalAtomicEnergyFiltered_(thermalTimeIntegrator->nodal_atomic_energy_filtered()), nodalAtomicPowerFiltered_(thermalTimeIntegrator->nodal_atomic_power_filtered()), atomicTemperatureDelta_(atc_->num_nodes(),1), nodalAtomicEnergy_(NULL), nodalAtomicEnergyOld_(atc_->num_nodes(),1), nodalAtomicTemperatureOld_(atc_->num_nodes(),1) { // do nothing } //-------------------------------------------------------- // construct_transfers // Grab existing managed quantities, // create the rest //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::construct_transfers() { ThermalIntegrationMethod::construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); nodalAtomicEnergy_ = interscaleManager.dense_matrix("NodalAtomicEnergy"); } //-------------------------------------------------------- // initialize // initialize all data //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::initialize() { ThermalIntegrationMethod::initialize(); // initial power to zero nodalAtomicPower_.reset(atc_->num_nodes(),1); // sets up time filter for cases where variables temporally filtered // this time integrator should use Crank-Nicholson filter for 2nd order accuracy TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (timeFilterManager->need_reset()) { // the form of this integrator implies no time filters that require history data can be used timeFilter_->initialize(); } // sets up time filter for post-processing the filtered power // this time integrator should use an explicit-implicit filter // to mirror the 2nd order Verlet integration scheme // It requires no history information so initial value just sizes arrays if (!timeFilterManager->end_equilibrate()) { // implies an initial condition of the instantaneous atomic energy // for the corresponding filtered variable, consistent with the temperature nodalAtomicEnergyFiltered_ = nodalAtomicEnergy_->quantity(); nodalAtomicPowerFiltered_.reset(atc_->num_nodes(),1); } } //-------------------------------------------------------- // pre_initial_integrate1 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::pre_initial_integrate1(double dt) { const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); // updated filtered energy using explicit-implicit scheme timeFilter_->apply_pre_step1(nodalAtomicEnergyFiltered_.set_quantity(), myNodalAtomicEnergy,dt); } //-------------------------------------------------------- // pre_initial_integrate2 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::pre_initial_integrate2(double dt) { // used for updating change in temperature from mass matrix change this->compute_old_time_data(); // update FE contributions apply_gear_predictor(dt); // update filtered nodal atomic power // that way thermostat and integrator can be consistent timeFilter_->apply_pre_step1(nodalAtomicPowerFiltered_.set_quantity(), nodalAtomicPower_,dt); // store current energy for use later nodalAtomicPower_ = nodalAtomicEnergy_->quantity(); nodalAtomicPower_ *= -1.; } //-------------------------------------------------------- // pre_final_integrate1 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::pre_final_integrate1(double dt) { // before the new rhs is computed but after atomic velocity is updated // to allow for general notions of temperature beyond kinetic. // compute change in restricted atomic energy nodalAtomicPower_ += nodalAtomicEnergy_->quantity(); // update FE temperature with change in temperature from MD compute_temperature_delta(nodalAtomicPower_,dt); temperature_ += atomicTemperatureDelta_.quantity(); // approximation to power for output nodalAtomicPower_ /= dt; timeFilter_->apply_post_step1(nodalAtomicPowerFiltered_.set_quantity(), nodalAtomicPower_,dt); // make sure nodes are fixed atc_->set_fixed_nodes(); } //-------------------------------------------------------- // post_final_integrate1 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::post_final_integrate1(double dt) { // Finish updating temperature with FE contributions atc_->apply_inverse_mass_matrix(temperatureRhs_.quantity(), _temperatureResidual_,TEMPERATURE); _temperatureResidual_ -= temperatureRoc_.quantity(); _temperatureResidual_ *= dt; apply_gear_corrector(_temperatureResidual_,dt); } //-------------------------------------------------------- // post_final_integrate3 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::post_final_integrate3(double dt) { // update filtered atomic energy timeFilter_->apply_post_step1(nodalAtomicEnergyFiltered_.set_quantity(), nodalAtomicEnergy_->quantity(),dt); } //-------------------------------------------------------- // output // add variables to output list //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::post_process() { nodalAtomicPowerOut_ = nodalAtomicPower_; nodalAtomicTemperatureOut_ = nodalAtomicTemperature_->quantity(); } //-------------------------------------------------------- // finish // finalize state of nodal atomic quantities //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::finish() { post_process(); } //-------------------------------------------------------- // apply_gear_predictor //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::apply_gear_predictor(double dt) { gear1_3_predict(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.quantity(),dt); } //-------------------------------------------------------- // apply_gear_corrector //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::apply_gear_corrector(const DENS_MAT & R_theta, double dt) { gear1_3_correct(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), R_theta,dt); } //-------------------------------------------------------- // compute_old_time_data //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::compute_old_time_data() { const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); atc_->apply_inverse_mass_matrix(myNodalAtomicEnergy, nodalAtomicTemperatureOld_.set_quantity(), TEMPERATURE); nodalAtomicEnergyOld_ = myNodalAtomicEnergy; } //-------------------------------------------------------- // compute_temperature_delta //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStep::compute_temperature_delta(const DENS_MAT & atomicEnergyDelta, double dt) { DENS_MAT & myAtomicTemperatureDelta(atomicTemperatureDelta_.set_quantity()); myAtomicTemperatureDelta = nodalAtomicEnergyOld_.quantity() + atomicEnergyDelta; atc_->apply_inverse_mass_matrix(myAtomicTemperatureDelta, TEMPERATURE); myAtomicTemperatureDelta += -1.*(nodalAtomicTemperatureOld_.quantity()); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermalTimeIntegratorFracionalStepFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab data from ATC //-------------------------------------------------------- ThermalTimeIntegratorFractionalStepFiltered::ThermalTimeIntegratorFractionalStepFiltered(ThermalTimeIntegrator * thermalTimeIntegrator) : ThermalTimeIntegratorFractionalStep(thermalTimeIntegrator), temperature3Roc_(atc_->field_3roc(TEMPERATURE)) { // do nothing } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ThermalTimeIntegratorFractionalStepFiltered::~ThermalTimeIntegratorFractionalStepFiltered() { // do nothing } //-------------------------------------------------------- // pre_initial_integrate1 //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::pre_initial_integrate1(double dt) { // determine change in temperature if no forces were applied over this timestep // relevant coefficients from time filter double coefF1 = timeFilter_->filtered_coefficient_pre_s1(dt); double coefF2 = timeFilter_->filtered_coefficient_post_s1(dt); double coefU1 = timeFilter_->unfiltered_coefficient_pre_s1(dt); double coefU2 = timeFilter_->unfiltered_coefficient_post_s1(dt); DENS_MAT & myAtomicTemperatureDelta(atomicTemperatureDelta_.set_quantity()); DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity()); const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); // composite from change after two step update of current filtered energy myAtomicTemperatureDelta = (coefF1*coefF2-1.)*myNodalAtomicEnergyFiltered; // change in filtered temperature from current energy from this and next time levels myAtomicTemperatureDelta += (coefU1*coefF2+coefU2)*myNodalAtomicEnergy; // updated filtered energy using explicit-implicit scheme // nodalAtomicEnergy_ is either set from initialization or from the end of the last timestep timeFilter_->apply_pre_step1(myNodalAtomicEnergyFiltered,myNodalAtomicEnergy,dt); } //-------------------------------------------------------- // output // add variables to output list //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::output(OUTPUT_LIST & outputData) { atc_->apply_inverse_md_mass_matrix(nodalAtomicEnergyFiltered_.quantity(), nodalAtomicTemperatureOut_.set_quantity(), TEMPERATURE); DENS_MAT & nodalAtomicPower(nodalAtomicPowerFiltered_.set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData["NodalAtomicPower"] = &nodalAtomicPower; } } //-------------------------------------------------------- // apply_gear_predictor //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::apply_gear_predictor(double dt) { gear1_4_predict(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), temperature3Roc_.quantity(),dt); } //-------------------------------------------------------- // apply_gear_corrector //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::apply_gear_corrector(const DENS_MAT & R_theta, double dt) { gear1_4_correct(temperature_.set_quantity(), temperatureRoc_.set_quantity(), temperature2Roc_.set_quantity(), temperature3Roc_.set_quantity(), R_theta,dt); } //-------------------------------------------------------- // compute_temperature_delta //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::compute_old_time_data() { const DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.quantity()); atc_->apply_inverse_mass_matrix(myNodalAtomicEnergyFiltered, nodalAtomicTemperatureOld_.set_quantity(), TEMPERATURE); nodalAtomicEnergyOld_ = myNodalAtomicEnergyFiltered; } //-------------------------------------------------------- // compute_old_time_data //-------------------------------------------------------- void ThermalTimeIntegratorFractionalStepFiltered::compute_temperature_delta(const DENS_MAT & atomicEnergyDelta, double dt) { DENS_MAT & myAtomicTemperatureDelta(atomicTemperatureDelta_.set_quantity()); double coefU2 = timeFilter_->unfiltered_coefficient_post_s1(dt); myAtomicTemperatureDelta += nodalAtomicEnergyOld_.quantity() + coefU2*atomicEnergyDelta; atc_->apply_inverse_mass_matrix(myAtomicTemperatureDelta, TEMPERATURE); myAtomicTemperatureDelta += -1.*nodalAtomicTemperatureOld_.quantity(); } };