diff --git a/lib/atc/ATC_Coupling.cpp b/lib/atc/ATC_Coupling.cpp index b600638839..b021584186 100644 --- a/lib/atc/ATC_Coupling.cpp +++ b/lib/atc/ATC_Coupling.cpp @@ -16,6 +16,10 @@ using std::string; using std::map; using std::pair; using std::set; +using std::ifstream; +using std::stringstream; +using ATC_Utility::is_numeric; +using ATC_Utility::to_string; namespace ATC { //-------------------------------------------------- @@ -138,11 +142,11 @@ namespace ATC { /*! \page man_fix_nodes fix_modify AtC fix \section syntax - fix_modify AtC fix + fix_modify AtC fix - = field name valid for type of physics - = name of set of nodes to apply boundary condition - - = value or name of function followed by its - parameters + - = value or name of function followed by its + parameters or nothing to fix the field at its current state \section examples fix_modify AtC fix temperature groupNAME 10. \n fix_modify AtC fix temperature groupNAME 0 0 0 10.0 0 0 1.0 \n @@ -174,10 +178,30 @@ namespace ATC { prescribedDataMgr_->fix_field(one,thisField,thisIndex,f); } } - // parse constant + // parse constant or file else if (narg == argIdx+1) { - f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); - prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + string a(arg[argIdx]); + if (is_numeric(a)) { // constant + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + } + else { + ATC::LammpsInterface::instance()->print_msg("reading "+field_to_string(thisField)+" on nodeset "+nsetName+" from file "+a); + string s = ATC::LammpsInterface::instance()->read_file(a); + stringstream ss; ss << s; + double v; + set nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set::const_iterator iset; + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + int i; + ss >> i >> v; + if (i != inode) ATC::LammpsInterface::instance()->print_msg_once("WARNING: node mismatch in file read"); + f = XT_Function_Mgr::instance()->constant_function(v); + set one; one.insert(inode); + prescribedDataMgr_->fix_field(one,thisField,thisIndex,f); + } + } } // parse function else { @@ -237,13 +261,38 @@ namespace ATC { XT_Function * f = NULL; // parse constant if (narg == argIdx+1) { - f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + string a(arg[argIdx]); + if (is_numeric(a)) { // constant + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); + } + else { + ATC::LammpsInterface::instance()->print_msg("reading "+field_to_string(thisField)+" source on node set "+esetName+" from file "+a); + string s = ATC::LammpsInterface::instance()->read_file(arg[argIdx]); + stringstream ss; ss << s; + double v; + set nset = (feEngine_->fe_mesh())->nodeset(esetName); + set< pair < int, double > > src; + set::const_iterator iset; + double sum = 0.; + for (iset = nset.begin(); iset != nset.end(); iset++) { + int inode = *iset; + int i; + ss >> i >> v; + if (i != inode) ATC::LammpsInterface::instance()->print_msg_once("WARNING: node mismatch in file read"); + src.insert(pair (inode,v)); + sum += v; + } + if (ss.gcount()) ATC::LammpsInterface::instance()->print_msg_once("WARNING: not all of file read"); + ATC::LammpsInterface::instance()->print_msg_once("total source: "+to_string(sum)); + prescribedDataMgr_->fix_source(thisField,thisIndex,src); + } } // parse function else { f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); } - prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); fieldMask_(thisField,PRESCRIBED_SOURCE) = true; match = true; } @@ -271,6 +320,30 @@ namespace ATC { fieldMask_(thisField,PRESCRIBED_SOURCE) = false; match = true; } + else if (strcmp(arg[argIdx],"write_source")==0) { + argIdx++; + FieldName thisField; + int thisIndex; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + string filename(arg[argIdx++]); + set_sources(); + FIELDS * s = & sources_; // PRESCRIBED_SOURCES + if (argIdx < narg && strcmp(arg[argIdx],"extrinsic")==0) s = & extrinsicSources_; +//s = & extrinsicSources_; + stringstream f; + set nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set::const_iterator iset; + const DENS_MAT & source =(s->find(thisField)->second).quantity(); + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + double v = source(inode,thisIndex); + f << inode << " " << std::setprecision(17) << v << "\n"; + } + LammpsInterface::instance()->write_file(filename,f.str()); + match = true; + } + /*! \page man_fix_flux fix_modify AtC fix_flux \section syntax @@ -478,6 +551,24 @@ namespace ATC { match = true; } + + else if (strcmp(arg[argIdx],"fix_open")==0) { + argIdx++; + parse_field(arg,argIdx,thisField); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->fix_open(fsetName,thisField); + fieldMask_(thisField,OPEN_SOURCE) = true; + match = true; + } + else if (strcmp(arg[argIdx],"unfix_open")==0) { + argIdx++; + parse_field(arg,argIdx,thisField); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_open(fsetName,thisField); + fieldMask_(thisField,OPEN_SOURCE) = false; + match = true; + } + /*! \page man_atomic_charge fix_modify AtC atomic_charge \section syntax fix_modify AtC atomic_charge @@ -525,6 +616,9 @@ namespace ATC { if (strcmp(arg[argIdx],"fe")==0) { sourceIntegration_ = FULL_DOMAIN; } + else { + sourceIntegration_ = FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE; + } match = true; } @@ -816,9 +910,8 @@ namespace ATC { //-------------------------------------------------------- void ATC_Coupling::set_sources() { - // set fields - prescribedDataMgr_->set_sources(time(),sources_); - + prescribedDataMgr_->set_sources(time(),sources_); // PRESCRIBED_SOURCE + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); // EXTRINSIC_SOURCE } //----------------------------------------------------------------- // this is w_a source_a @@ -902,10 +995,45 @@ namespace ATC { const PhysicsModel * physicsModel) { + if (integrationType == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE) { + RHS_MASK rhsMaskFE = rhsMask; + RHS_MASK rhsMaskMD = rhsMask; rhsMaskMD = false; + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if ( rhsMaskFE(thisFieldName,SOURCE) ) { + rhsMaskFE(thisFieldName,SOURCE) = false; + rhsMaskMD(thisFieldName,SOURCE) = true; + } + } + feEngine_->compute_tangent_matrix(rhsMaskFE, row_col, + fields , physicsModel, elementToMaterialMap_, stiffness); + masked_atom_domain_rhs_tangent(row_col, + rhsMaskMD, + fields, + stiffnessAtomDomain_, + physicsModel); + stiffness += stiffnessAtomDomain_; + + } + else { feEngine_->compute_tangent_matrix(rhsMask, row_col, fields , physicsModel, elementToMaterialMap_, stiffness); + } ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); feEngine_->add_robin_tangent(rhsMask, fields, time(), robinFcn, stiffness); + OPEN_SURFACE & openFaces = *(prescribedDataMgr_->open_faces()); + feEngine_->add_open_tangent(rhsMask, fields, openFaces, stiffness); + } + //----------------------------------------------------------------- + void ATC_Coupling::tangent_matrix( + const pair row_col, + const RHS_MASK & rhsMask, + const PhysicsModel * physicsModel, + SPAR_MAT & stiffness) + { + feEngine_->compute_tangent_matrix(rhsMask, row_col, + fields_ , physicsModel, elementToMaterialMap_, stiffness); } //----------------------------------------------------------------- void ATC_Coupling::compute_rhs_vector(const RHS_MASK & rhsMask, @@ -916,7 +1044,6 @@ namespace ATC { { if (!physicsModel) physicsModel = physicsModel_; - // compute FE contributions evaluate_rhs_integral(rhsMask,fields,rhs,domain,physicsModel); @@ -943,8 +1070,10 @@ namespace ATC { } } - ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); + ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); feEngine_->add_robin_fluxes(rhsMask, fields, time(), robinFcn, rhs); + OPEN_SURFACE & openFaces = *(prescribedDataMgr_->open_faces()); + feEngine_->add_open_fluxes(rhsMask, fields, openFaces, rhs); } //----------------------------------------------------------------- void ATC_Coupling::masked_atom_domain_rhs_integral( @@ -988,7 +1117,7 @@ namespace ATC { fields, physicsModel, elementToMaterialMap_, - rhs, + rhs, false, &(elementMask_->quantity())); masked_atom_domain_rhs_integral(rhsMask, fields, @@ -1007,6 +1136,42 @@ namespace ATC { rhs, physicsModel); } + else if (integrationType == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE) { + RHS_MASK rhsMaskFE = rhsMask; + RHS_MASK rhsMaskMD = rhsMask; rhsMaskMD = false; + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if ( rhsMaskFE(thisFieldName,SOURCE) ) { + rhsMaskFE(thisFieldName,SOURCE) = false; + rhsMaskMD(thisFieldName,SOURCE) = true; + } + } + feEngine_->compute_rhs_vector(rhsMaskFE, + fields, + physicsModel, + elementToMaterialMap_, + rhs); + masked_atom_domain_rhs_integral(rhsMaskMD, + fields, + rhsAtomDomain_, + physicsModel); + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + + if ( ((rhs[thisFieldName].quantity()).size() > 0) + && ((rhsAtomDomain_[thisFieldName].quantity()).size() > 0) ) + rhs[thisFieldName] += rhsAtomDomain_[thisFieldName].quantity(); + } + } + else if (integrationType == FULL_DOMAIN_FREE_ONLY) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs, true); + } else { // domain == FULL_DOMAIN feEngine_->compute_rhs_vector(rhsMask, fields, @@ -1336,7 +1501,7 @@ namespace ATC { if (internalElementSet_.size()) { // when geometry is based on elements, there are no mixed elements elementMask_ = new MatrixDependencyManager; - (elementMask_->set_quantity()).reset(feEngine_->num_elements(),1,false); + (elementMask_->set_quantity()).reset(feEngine_->num_elements(),1); } else { elementMask_ = new ElementMask(this); @@ -1375,7 +1540,7 @@ namespace ATC { } //-------------------------------------------------------- // construct_interpolant - // constructs: interpolatn, accumulant, weights, and spatial derivatives + // constructs: interpolant, accumulant, weights, and spatial derivatives //-------------------------------------------------------- void ATC_Coupling::construct_interpolant() { @@ -1593,6 +1758,23 @@ namespace ATC { // brute force computation of inverse consistentMassMatsInv_[thisField] = inv((consistentMassMats_[thisField].quantity()).dense_copy()); } + else if (! is_intrinsic(thisField)) { + Array massMask(1); + massMask(0) = thisField; + + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel, + elementToMaterialMap_,massMats_, + &(elementMask_->quantity())); + const DIAG_MAT & myMassMat(massMats_[thisField].quantity()); + DIAG_MAT & myMassMatInv(massMatsInv_[thisField].set_quantity()); + for (int iNode = 0; iNode < nNodes_; iNode++) { + + if (fabs(myMassMat(iNode,iNode))>0) + myMassMatInv(iNode,iNode) = 1./myMassMat(iNode,iNode); + else + myMassMatInv(iNode,iNode) = 0.; + } + } else { // lumped mass matrix // compute FE mass matrix in full domain Array massMask(1); @@ -1631,6 +1813,7 @@ namespace ATC { // fully remove contributions from internal nodes DIAG_MAT & myMassMatFE(massMatsFE_[thisField].set_quantity()); + //myMassMatFE.print("MMFE"); if (!atomQuadForInternal_) { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); for (int iNode = 0; iNode < nNodes_; iNode++) @@ -1939,7 +2122,7 @@ namespace ATC { // Set sources prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); extrinsicModelManager_.pre_final_integrate(); - + bool needsSources = false; for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { if ((_tiIt_->second)->has_final_predictor()) { @@ -1953,7 +2136,7 @@ namespace ATC { compute_atomic_sources(intrinsicMask_,fields_,atomicSources_); } atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); - + // Compute atom-integrated rhs // parallel communication happens within FE_Engine compute_rhs_vector(intrinsicMask_,fields_,rhs_,FE_DOMAIN); @@ -1966,7 +2149,7 @@ namespace ATC { for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { (_tiIt_->second)->post_final_integrate1(dt); } - + // fix nodes, non-group bcs applied through FE set_fixed_nodes(); @@ -1993,6 +2176,7 @@ namespace ATC { } // apply corrector phase of thermostat + set_fixed_nodes(); atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep()); // final phase of time integration @@ -2037,7 +2221,7 @@ namespace ATC { const DIAG_MAN * atomicWeights, const MatrixDependencyManager * elementMask, - const RegulatedNodes * nodeSet) + const SetDependencyManager * nodeSet) { if (bndyIntType_ == FE_QUADRATURE) { feEngine_->compute_boundary_flux(rhsMask, @@ -2094,7 +2278,8 @@ if (thisFieldName >= rhsMask.nRows()) break; void ATC_Coupling::compute_flux(const Array2D & rhsMask, const FIELDS & fields, GRAD_FIELD_MATS & flux, - const PhysicsModel * physicsModel) + const PhysicsModel * physicsModel, + bool project) { if (! physicsModel) { physicsModel = physicsModel_; } feEngine_->compute_flux(rhsMask, @@ -2102,9 +2287,22 @@ if (thisFieldName >= rhsMask.nRows()) break; physicsModel, elementToMaterialMap_, flux); + if (project) { + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName name = field->first; + if ( rhsMask(name,FLUX) ) { + for(int i=0; i < nsd_ ; ++i) { + DENS_MAT & f = flux[name][i]; +if (i==0) f.print("pre flux_"+field_to_string(name)+"_"+ATC_Utility::to_string(i)); + apply_inverse_mass_matrix(f); +if (i==0) f.print("flux_"+field_to_string(name)+"_"+ATC_Utility::to_string(i)); + } + } + } + } } - //-------------------------------------------------------- void ATC_Coupling::nodal_projection(const FieldName & fieldName, diff --git a/lib/atc/ATC_Coupling.h b/lib/atc/ATC_Coupling.h index 7a51ba3b80..002041b536 100644 --- a/lib/atc/ATC_Coupling.h +++ b/lib/atc/ATC_Coupling.h @@ -35,6 +35,7 @@ namespace ATC { friend class ExtrinsicModelElectrostaticMomentum; friend class SchrodingerPoissonSolver; friend class SliceSchrodingerPoissonSolver; + friend class GlobalSliceSchrodingerPoissonSolver; /** constructor */ ATC_Coupling(std::string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix); @@ -116,9 +117,14 @@ namespace ATC { const SPAR_MAN * shpFcn = NULL, const DIAG_MAN * atomicWeights = NULL, const MatrixDependencyManager * elementMask = NULL, - const RegulatedNodes * nodeSet = NULL); + const SetDependencyManager * nodeSet = NULL); /** access to full right hand side / forcing vector */ FIELDS &rhs() {return rhs_;}; + Array2D rhs_mask() const { + Array2D mask(NUM_FIELDS,NUM_FLUX); + mask = false; + return mask; + } DENS_MAN &field_rhs(FieldName thisField) { return rhs_[thisField]; }; /** allow FE_Engine to construct ATC structures after mesh is constructed */ @@ -126,7 +132,7 @@ namespace ATC { // public for FieldIntegrator bool source_atomic_quadrature(FieldName field) - { return false; } + { return (sourceIntegration_ == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE); } ATC::IntegrationDomainType source_integration() { return sourceIntegration_; } @@ -154,6 +160,10 @@ namespace ATC { SPAR_MAT & stiffness, const IntegrationDomainType integrationType, const PhysicsModel * physicsModel=NULL); + void tangent_matrix(const std::pair row_col, + const RHS_MASK & rhsMask, + const PhysicsModel * physicsModel, + SPAR_MAT & stiffness); /** PDE type */ WeakEquation::PDE_Type pde_type(const FieldName fieldName) const; @@ -165,8 +175,41 @@ namespace ATC { PrescribedDataManager * prescribed_data_manager() { return prescribedDataMgr_; } // public for Kinetostat + // TODO rename to "mass_matrix" DIAG_MAT &get_mass_mat(FieldName thisField) { return massMats_[thisField].set_quantity();}; + /** access to underlying mass matrices */ + MATRIX * mass_matrix(FieldName thisField) + { + if (!useConsistentMassMatrix_(thisField)) { + return & massMats_[thisField].set_quantity(); + } + else { + return & consistentMassMats_[thisField].set_quantity(); + } + } + /** const access to underlying mass matrices */ + const MATRIX * mass_matrix(FieldName thisField) const + { + if (!useConsistentMassMatrix_(thisField)) { + MASS_MATS::const_iterator it = massMats_.find(thisField); + if (it != massMats_.end()) { + return & (it->second).quantity(); + } + else { + return NULL; + } + } + else { + CON_MASS_MATS::const_iterator it = consistentMassMats_.find(thisField); + if (it != consistentMassMats_.end()) { + return & (it->second).quantity(); + } + else { + return NULL; + } + } + } /** */ DENS_MAN &atomic_source(FieldName thisField){return atomicSources_[thisField];}; @@ -279,7 +322,8 @@ namespace ATC { void compute_flux(const Array2D & rhs_mask, const FIELDS &fields, GRAD_FIELD_MATS &flux, - const PhysicsModel * physicsModel=NULL); + const PhysicsModel * physicsModel=NULL, + const bool normalize = false); /** evaluate rhs on the atomic domain which is near the FE region */ void masked_atom_domain_rhs_integral(const Array2D & rhs_mask, const FIELDS &fields, diff --git a/lib/atc/ATC_CouplingEnergy.cpp b/lib/atc/ATC_CouplingEnergy.cpp index 8ba10c4f8a..0338054902 100644 --- a/lib/atc/ATC_CouplingEnergy.cpp +++ b/lib/atc/ATC_CouplingEnergy.cpp @@ -155,10 +155,10 @@ namespace ATC { // kinetic temperature measure for post-processing // nodal restriction of the atomic energy quantity for the temperature definition AtfShapeFunctionRestriction * nodalAtomicTwiceKineticEnergy = new AtfShapeFunctionRestriction(this, - atomicTwiceKineticEnergy, - shpFcn_); + atomicTwiceKineticEnergy, + shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicTwiceKineticEnergy, - "NodalAtomicTwiceKineticEnergy"); + "NodalAtomicTwiceKineticEnergy"); nodalAtomicKineticTemperature_ = new AtfShapeFunctionMdProjection(this, nodalAtomicTwiceKineticEnergy, TEMPERATURE); @@ -171,12 +171,12 @@ namespace ATC { atomicFluctuatingPotentialEnergy, shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicFluctuatingPotentialEnergy, - "NodalAtomicFluctuatingPotentialEnergy"); + "NodalAtomicFluctuatingPotentialEnergy"); nodalAtomicConfigurationalTemperature_ = new AtfShapeFunctionMdProjection(this, nodalAtomicFluctuatingPotentialEnergy, TEMPERATURE); interscaleManager_.add_dense_matrix(nodalAtomicConfigurationalTemperature_, - "NodalAtomicConfigurationalTemperature"); + "NodalAtomicConfigurationalTemperature"); } // register the per-atom quantity for the temperature definition @@ -185,10 +185,10 @@ namespace ATC { // nodal restriction of the atomic energy quantity for the temperature definition AtfShapeFunctionRestriction * nodalAtomicEnergy = new AtfShapeFunctionRestriction(this, - atomEnergyForTemperature, - shpFcn_); + atomEnergyForTemperature, + shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicEnergy, - "NodalAtomicEnergy"); + "NodalAtomicEnergy"); // nodal atomic temperature field diff --git a/lib/atc/ATC_CouplingMomentumEnergy.cpp b/lib/atc/ATC_CouplingMomentumEnergy.cpp index 7c1de1bbcb..a29603a618 100644 --- a/lib/atc/ATC_CouplingMomentumEnergy.cpp +++ b/lib/atc/ATC_CouplingMomentumEnergy.cpp @@ -3,6 +3,7 @@ #include "KinetoThermostat.h" #include "ATC_Error.h" #include "PrescribedDataManager.h" +#include "FieldManager.h" // Other Headers #include @@ -181,11 +182,14 @@ namespace ATC { "NodalAtomicDisplacement"); } - // always need kinetic energy + // always need fluctuating velocity and kinetic energy + FtaShapeFunctionProlongation * atomicMeanVelocity = new FtaShapeFunctionProlongation(this,&fields_[VELOCITY],shpFcn_); interscaleManager_.add_per_atom_quantity(atomicMeanVelocity, - "AtomicMeanVelocity"); - AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceFluctuatingKineticEnergy(this); + field_to_prolongation_name(VELOCITY)); + FieldManager fieldManager(this); + PerAtomQuantity * fluctuatingAtomicVelocity = fieldManager.per_atom_quantity("AtomicFluctuatingVelocity"); // also creates ProlongedVelocity + AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceKineticEnergy(this,fluctuatingAtomicVelocity); AtomicEnergyForTemperature * atomEnergyForTemperature = NULL; // Appropriate per-atom quantity based on desired temperature definition diff --git a/lib/atc/ATC_Error.h b/lib/atc/ATC_Error.h index 91d3b4dcff..fc356dbec8 100644 --- a/lib/atc/ATC_Error.h +++ b/lib/atc/ATC_Error.h @@ -17,7 +17,6 @@ #define HACK(l,m) - namespace ATC { /** * @class ATC_Error diff --git a/lib/atc/ATC_Method.cpp b/lib/atc/ATC_Method.cpp index 6acf25efd2..b1f3fa2545 100644 --- a/lib/atc/ATC_Method.cpp +++ b/lib/atc/ATC_Method.cpp @@ -52,6 +52,7 @@ namespace ATC { atomGhostCoarseGrainingPositions_(NULL), atomProcGhostCoarseGrainingPositions_(NULL), atomReferencePositions_(NULL), + nNodes_(0), nsd_(lammpsInterface_->dimension()), xref_(NULL), readXref_(false), @@ -355,7 +356,6 @@ pecified } } - /*! \page man_boundary_integral fix_modify AtC output boundary_integral \section syntax fix_modify AtC output boundary_integral [field] faceset [name] @@ -412,7 +412,7 @@ pecified /*! \page man_output_elementset fix_modify AtC output elementset \section syntax - fix_modify AtC output volume_integral {` + fix_modify AtC output volume_integral - set_name (string) = name of elementset to be integrated over - fieldname (string) = name of field to integrate csum = creates nodal sum over nodes in specified nodeset \n @@ -508,6 +508,25 @@ pecified match = true; } } + else if (strcmp(arg[argIdx],"write")==0) { + argIdx++; + FieldName thisField; + int thisIndex; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + string filename(arg[argIdx++]); + stringstream f; + set nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set::const_iterator iset; + const DENS_MAT & field =(fields_.find(thisField)->second).quantity(); + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + double v = field(inode,thisIndex); + f << inode << " " << std::setprecision(17) << v << "\n"; + } + LammpsInterface::instance()->write_file(filename,f.str()); + match = true; + } // add a species for tracking /*! \page man_add_species fix_modify AtC add_species \section syntax @@ -907,7 +926,7 @@ pecified \section syntax fix_modify AtC kernel_bandwidth \section examples - fix_modify atc reset_time 8 \n + fix_modify atc kernel_bandwidth 8 \n \section description Sets a maximum parallel bandwidth for the kernel functions during parallel communication. If the command is not issued, the default will be to assume the bandwidth of the kernel matrix corresponds to the number of sampling locations. \section restrictions @@ -1078,6 +1097,22 @@ pecified } + //-------------------------------------------------- + // helper function for parser + // handles : "displacement x" and "temperature" by indexing argIdx + // for fluxes : only normal fluxes can be prescribed + //-------------------------------------------------- + void ATC_Method::parse_field(/*const*/ char ** args, int & argIdx, + FieldName & thisField) + { + string thisName = args[argIdx++]; + thisField = string_to_field(thisName); + map::const_iterator iter = fieldSizes_.find(thisField); + if (iter == fieldSizes_.end()) { + throw ATC_Error("Bad field name: "+thisName); + } + } + //-------------------------------------------------- // helper function for parser // handles : "displacement x" and "temperature" by indexing argIdx @@ -1266,6 +1301,33 @@ pecified readXref_ = false; } + // ensure initial configuration is consistent with element set + + if (internalElementSet_.size() && groupbitGhost_) { + int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + const FE_Mesh * feMesh = feEngine_->fe_mesh(); + const set & elementSet(feMesh->elementset(internalElementSet_)); + int element; + DENS_VEC coords(nsd_); + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit_ || mask[i] & groupbitGhost_) { + for (int j = 0; j < nsd_; j++) { + coords(j) = xref_[i][j]; + } + element = feMesh->map_to_element(coords); + if (elementSet.find(element) == elementSet.end()) { + mask[i] |= groupbitGhost_; + mask[i] &= ~groupbit_; + } + else { + mask[i] &= ~groupbitGhost_; + mask[i] |= groupbit_; + } + } + } + } + // set up maps from lammps to atc indexing reset_nlocal(); } @@ -1321,6 +1383,12 @@ pecified // 6e) atomic output for 0th step update_peratom_output(); + massMatInv_.reset(nNodes_,nNodes_); + feEngine_->compute_lumped_mass_matrix(massMatInv_); + for (int i = 0; i < nNodes_; ++i) { + massMatInv_(i,i) = 1./massMatInv_(i,i); + } + // clear need for resets needReset_ = false; @@ -1792,12 +1860,7 @@ pecified } } - - return m; - //int mySize = 3; - //if (num_bond) - // mySize += 1 + lammpsInterface_->bond_per_atom(); - //return mySize; + return m; // total amount of data sent } //----------------------------------------------------------------- @@ -1827,6 +1890,17 @@ pecified } } + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + int ATC_Method::comm_forward() + { + int size = 3; + if (lammpsInterface_->num_bond()) + { size += lammpsInterface_->bond_per_atom()+1; } + return size; + } + //----------------------------------------------------------------- // //----------------------------------------------------------------- @@ -2195,6 +2269,8 @@ pecified // get number of atoms int natoms = 0; + int ncols = 0; + int style = LammpsInterface::CHARGE_STYLE; if (LammpsInterface::instance()->rank_zero()) { in.open(filename); string msg; @@ -2204,10 +2280,45 @@ pecified in.getline(line,lineSize); // header in.getline(line,lineSize); // blank line in >> natoms; - in.close(); stringstream ss; - ss << "found " << natoms << " atoms in reference file"; + ss << "found " << natoms << " atoms in reference " << filename ; + while(in.good()) { + in.getline(line,lineSize); + string str(line); + int pos = str.find("Atoms"); + if (pos > -1) { + in.getline(line,lineSize); // blank line + break; + } + } + in.getline(line,lineSize); + std::vector tokens; + ATC_Utility::command_strings(line, tokens); + ncols = tokens.size(); + switch (ncols) { + // atomic: id type x y z + case 5: + case 8: + ss << " style:atomic"; + style = LammpsInterface::ATOMIC_STYLE; + break; + // charge: id type q x y z + // molecule : id molecule-ID type x y z + case 6: + ss << " style:charge"; + style = LammpsInterface::CHARGE_STYLE; + break; + // full : id molecule-ID type q x y z + case 7: + ss << " style:full"; + style = LammpsInterface::FULL_STYLE; + break; + default: + throw ATC_Error("cannot determine atom style, columns:"+to_string(ncols)); + break; + } LammpsInterface::instance()->print_msg(ss.str()); + in.close(); } LammpsInterface::instance()->int_broadcast(&natoms); @@ -2224,12 +2335,17 @@ pecified } } } - int nread = 0,type = -1, tag = -1, count = 0; - double x[3]={0,0,0}; + int nread = 0,type = -1, tag = -1, count = 0, mId = -1; + double x[3]={0,0,0}, q =0; while (nread < natoms) { if (LammpsInterface::instance()->rank_zero()) { in.getline(line,lineSize); stringstream ss (line,stringstream::in | stringstream::out); + if (style == LammpsInterface::CHARGE_STYLE) + ss >> tag >> type >> q >> x[0] >> x[1] >> x[2]; + else if (style == LammpsInterface::FULL_STYLE) + ss >> tag >> mId >> type >> q >> x[0] >> x[1] >> x[2]; + else ss >> tag >> type >> x[0] >> x[1] >> x[2]; nread++; } diff --git a/lib/atc/ATC_Method.h b/lib/atc/ATC_Method.h index d7708d2988..aa8f238721 100644 --- a/lib/atc/ATC_Method.h +++ b/lib/atc/ATC_Method.h @@ -51,6 +51,8 @@ namespace ATC { /** parser/modifier */ virtual bool modify(int narg, char **arg); + void parse_field(/*const*/ char ** args, int &argIndex, + FieldName &thisField); void parse_field(/*const*/ char ** args, int &argIndex, FieldName &thisField, int &thisIndex); @@ -111,7 +113,7 @@ namespace ATC { void copy_arrays(int, int); int pack_exchange(int, double *); int unpack_exchange(int, double *); - int comm_forward(void) {return sizeComm_;} + int comm_forward(void); int pack_comm(int , int *, double *, int, int *); void unpack_comm(int, int, double *); /*@}*/ @@ -331,6 +333,9 @@ namespace ATC { //---------------------------------------------------------------- /** \name mass matrix operations */ //---------------------------------------------------------------- + void apply_inverse_mass_matrix(MATRIX & data) { + data = massMatInv_*data; + } // inverted using GMRES void apply_inverse_mass_matrix(MATRIX & data, FieldName thisField) { @@ -360,6 +365,7 @@ namespace ATC { DIAG_MAN &mass_mat(FieldName thisField) { return massMats_[thisField];}; + //--------------------------------------------------------------- /** \name mass matrices */ @@ -503,6 +509,7 @@ namespace ATC { void pack_fields(RESTART_LIST & data); /** mass matrices */ + DIAG_MAT massMatInv_; MASS_MATS massMats_; MASS_MATS massMatsInv_; MASS_MATS massMatsMd_; diff --git a/lib/atc/ATC_TypeDefs.h b/lib/atc/ATC_TypeDefs.h index 8906d5c63a..5567af13c2 100644 --- a/lib/atc/ATC_TypeDefs.h +++ b/lib/atc/ATC_TypeDefs.h @@ -52,7 +52,9 @@ namespace ATC enum IntegrationDomainType { FULL_DOMAIN=0, ATOM_DOMAIN, - FE_DOMAIN + FE_DOMAIN, + FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE, + FULL_DOMAIN_FREE_ONLY }; /** domain decomposition */ enum DomainDecompositionType { @@ -479,6 +481,7 @@ namespace ATC SOURCE, // has a source term weighted by the shape function PRESCRIBED_SOURCE, // has a prescribed source term ROBIN_SOURCE, // has a Robin source term + OPEN_SOURCE, // has a open boundary source term EXTRINSIC_SOURCE, // has an extrinsic source term NUM_FLUX }; @@ -549,6 +552,7 @@ namespace ATC class UXT_Function; typedef std::map > > SURFACE_SOURCE; typedef std::map > > ROBIN_SURFACE_SOURCE; + typedef std::map > OPEN_SURFACE; typedef std::map > VOLUME_SOURCE; typedef std::map > ATOMIC_DATA; @@ -609,12 +613,14 @@ namespace ATC || rhsMask(field,SOURCE) || rhsMask(field,PRESCRIBED_SOURCE) || rhsMask(field,ROBIN_SOURCE) + || rhsMask(field,OPEN_SOURCE) || rhsMask(field,EXTRINSIC_SOURCE)) { msg = "RHS_MASK: " + name; if (rhsMask(field,FLUX)) msg += " flux"; if (rhsMask(field,SOURCE)) msg += " source"; if (rhsMask(field,PRESCRIBED_SOURCE)) msg += " prescribed_src"; if (rhsMask(field,ROBIN_SOURCE)) msg += " robin_src"; + if (rhsMask(field,OPEN_SOURCE)) msg += " open_src"; if (rhsMask(field,EXTRINSIC_SOURCE)) msg += " extrinsic_src"; } } diff --git a/lib/atc/AtomicRegulator.cpp b/lib/atc/AtomicRegulator.cpp index ec04b8556b..9d329852af 100644 --- a/lib/atc/AtomicRegulator.cpp +++ b/lib/atc/AtomicRegulator.cpp @@ -592,17 +592,17 @@ namespace ATC { this->construct_regulated_nodes(); InterscaleManager & interscaleManager(atc_->interscale_manager()); - nodeToOverlapMap_ = static_cast(interscaleManager.dense_matrix_int("NodeToOverlapMap")); + nodeToOverlapMap_ = static_cast(interscaleManager.dense_matrix_int(regulatorPrefix_+"NodeToOverlapMap")); if (!nodeToOverlapMap_) { nodeToOverlapMap_ = new NodeToSubset(atc_,regulatedNodes_); interscaleManager.add_dense_matrix_int(nodeToOverlapMap_, - regulatorPrefix_+"NodeToOverlapMap"); + regulatorPrefix_+"NodeToOverlapMap"); } - overlapToNodeMap_ = static_cast(interscaleManager.dense_matrix_int("OverlapToNodeMap")); + overlapToNodeMap_ = static_cast(interscaleManager.dense_matrix_int(regulatorPrefix_+"OverlapToNodeMap")); if (!overlapToNodeMap_) { overlapToNodeMap_ = new SubsetToNode(nodeToOverlapMap_); interscaleManager.add_dense_matrix_int(overlapToNodeMap_, - regulatorPrefix_+"OverlapToNodeMap"); + regulatorPrefix_+"OverlapToNodeMap"); } } @@ -681,7 +681,7 @@ namespace ATC { throw ATC_Error("RegulatorShapeFunction::initialize - unsupported solver type"); } - compute_sparsity(); + compute_sparsity(); } //-------------------------------------------------------- @@ -828,7 +828,7 @@ namespace ATC { void RegulatorShapeFunction::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); - regulatedNodes_ = static_cast(interscaleManager.set_int("RegulatedNodes")); + regulatedNodes_ = interscaleManager.set_int("RegulatedNodes"); if (!regulatedNodes_) { if (!(atomicRegulator_->use_localized_lambda())) { @@ -848,9 +848,12 @@ namespace ATC { // special set of boundary elements if (atomicRegulator_->use_localized_lambda()) { - elementMask_ = new ElementMaskNodeSet(atc_,boundaryNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,boundaryNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } @@ -921,7 +924,7 @@ namespace ATC { // Constructor // Grab references to necessary data //-------------------------------------------------------- - LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const RegulatedNodes * applicationNodes, const NodeToSubset * nodeToOverlapMap) : + LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const SetDependencyManager * applicationNodes, const NodeToSubset * nodeToOverlapMap) : LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance), applicationNodes_(applicationNodes), nodeToOverlapMap_(nodeToOverlapMap) diff --git a/lib/atc/AtomicRegulator.h b/lib/atc/AtomicRegulator.h index eadfce66dc..5d1e59f607 100644 --- a/lib/atc/AtomicRegulator.h +++ b/lib/atc/AtomicRegulator.h @@ -403,6 +403,9 @@ namespace ATC { /** pre-"run" initialization */ virtual void initialize(); + /** sets lambda to an initial value */ + virtual void set_lambda_to_value(double value) {*lambda_ = value;} + /** reset number of local atoms, as well as atomic data */ virtual void reset_nlocal(); @@ -413,6 +416,9 @@ namespace ATC { /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields); + /** access to nodes the thermostat is applied to */ + const SetDependencyManager * application_nodes() const {return applicationNodes_;}; + /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return false;}; @@ -448,7 +454,7 @@ namespace ATC { DENS_MAN * lambda_; /** lambda at atomic locations */ - ProtectedAtomQuantity * atomLambdas_; + PerAtomQuantity * atomLambdas_; /** shape function matrix for use in GLC solve */ PerAtomSparseMatrix * shapeFunctionMatrix_; @@ -469,13 +475,13 @@ namespace ATC { LambdaMatrixSolver * matrixSolver_; /** set of nodes used to construct matrix */ - RegulatedNodes * regulatedNodes_; + SetDependencyManager * regulatedNodes_; /** set of nodes on which lambda is non-zero */ - RegulatedNodes * applicationNodes_; + SetDependencyManager * applicationNodes_; /** set of nodes needed for localized boundary quadrature */ - RegulatedNodes * boundaryNodes_; + SetDependencyManager * boundaryNodes_; /** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */ NodeToSubset * nodeToOverlapMap_; @@ -490,7 +496,7 @@ namespace ATC { DIAG_MAN * atomicWeights_; /** element mask for boundary elements corresponding to nodeToOverlapMap_ */ - ElementMaskNodeSet * elementMask_; + MatrixDependencyManager * elementMask_; /** maps atoms from atc indexing to regulator indexing */ LargeToSmallAtomMap * lambdaAtomMap_; @@ -565,7 +571,7 @@ namespace ATC { public: - LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const RegulatedNodes * applicationNodes, const NodeToSubset * nodeToOverlapMap); + LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const SetDependencyManager * applicationNodes, const NodeToSubset * nodeToOverlapMap); virtual ~LambdaMatrixSolverLumped(){}; @@ -581,7 +587,7 @@ namespace ATC { DIAG_MAT lumpedMatrix_; /** set of regulated nodes */ - const RegulatedNodes * applicationNodes_; + const SetDependencyManager * applicationNodes_; /** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */ const NodeToSubset * nodeToOverlapMap_; diff --git a/lib/atc/DenseMatrix.h b/lib/atc/DenseMatrix.h index 39e2670814..e77e2ac854 100644 --- a/lib/atc/DenseMatrix.h +++ b/lib/atc/DenseMatrix.h @@ -16,7 +16,7 @@ template class DenseMatrix : public Matrix { public: - DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1) { _create(rows, cols, z); } + DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1): _data(NULL){ _create(rows, cols, z); } DenseMatrix(const DenseMatrix& c) : _data(NULL){ _copy(c); } DenseMatrix(const SparseMatrix& c): _data(NULL){ c.dense_copy(*this);} DenseMatrix(const Matrix& c) : _data(NULL){ _copy(c); } @@ -25,6 +25,9 @@ public: ~DenseMatrix() { _delete();} void reset (INDEX rows, INDEX cols, bool zero=true); + void reset (const DenseMatrix& c) {_delete(); _copy(c); }; + void reset (const SparseMatrix & c) {_delete(); c.dense_copy(*this);}; + void reset (const Matrix& c) {_delete(); _copy(c); } void resize(INDEX rows, INDEX cols, bool copy=false); void copy (const T * ptr, INDEX rows, INDEX cols); /** returns transpose(this) * B */ @@ -57,6 +60,7 @@ public: virtual bool check_range(T min, T max) const; protected: + void _set_equal(const Matrix &r); void _delete(); void _create(INDEX rows, INDEX cols, bool zero=false); void _copy(const Matrix &c); @@ -257,6 +261,7 @@ void DenseMatrix::_delete() _nRows = _nCols = 0; if (_data){ delete [] _data; + _data = NULL; } } //---------------------------------------------------------------------------- @@ -333,7 +338,22 @@ DenseMatrix& DenseMatrix::operator=(const SparseMatrix &c) } return *this; } - +//---------------------------------------------------------------------------- +// general matrix assignment (for densely packed matrices) +//---------------------------------------------------------------------------- +template +void DenseMatrix::_set_equal(const Matrix &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix *pr = &r; + const DenseMatrix *pdd = dynamic_cast*> (pr); + if (pdd) this->reset(*pdd); + else + { + std::cout <<"Error in general dense matrix assignment\n"; + exit(1); + } +} //* Returns the transpose of the cofactor matrix of A. //* see http://en.wikipedia.org/wiki/Adjugate_matrix //* symmetric flag only affects cases N>3 diff --git a/lib/atc/DiagonalMatrix.h b/lib/atc/DiagonalMatrix.h index 2ec096cb50..ce51c3f365 100644 --- a/lib/atc/DiagonalMatrix.h +++ b/lib/atc/DiagonalMatrix.h @@ -49,7 +49,7 @@ class DiagonalMatrix : public Matrix void write_restart(FILE *f) const; // Dump matrix contents to screen (not defined for all datatypes) - std::string to_string() const { return _data->to_string(); } + std::string to_string(int p=myPrecision) const { return _data->to_string(); } using Matrix::matlab; void matlab(std::ostream &o, const std::string &s="D") const; @@ -488,7 +488,7 @@ void DiagonalMatrix::_set_equal(const Matrix &r) else if (pv) this->reset(*pv); else { - std::cout <<"Error in general sparse matrix assignment\n"; + std::cout <<"Error in general diagonal matrix assignment\n"; exit(1); } } diff --git a/lib/atc/ElectronDragPower.cpp b/lib/atc/ElectronDragPower.cpp index b6bb452248..aff6be6106 100644 --- a/lib/atc/ElectronDragPower.cpp +++ b/lib/atc/ElectronDragPower.cpp @@ -63,12 +63,14 @@ void ElectronDragPowerLinear::electron_drag_velocity_coefficient(const FIELD_MAT { FIELD_MATS::const_iterator enField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = enField->second; - + //n.print("DENS"); // -1/tau (m_e * n) material_->inv_effective_mass(fields,invEffMassWorkspace_); + //invEffMassWorkspace_.print("INV MASS"); dragCoef = n; dragCoef /= invEffMassWorkspace_; dragCoef *= -electronDragInvTau_; + //dragCoef.print("DRAG COEF"); } } diff --git a/lib/atc/ElectronFlux.h b/lib/atc/ElectronFlux.h index c40ea475bc..013f9aac42 100644 --- a/lib/atc/ElectronFlux.h +++ b/lib/atc/ElectronFlux.h @@ -93,7 +93,8 @@ namespace ATC { FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY); GRAD_FIELD_MATS::const_iterator dEdField = gradFields.find(ELECTRON_DENSITY); GRAD_FIELD_MATS::const_iterator dPhiField = gradFields.find(ELECTRIC_POTENTIAL); - // J_n = - \mu n grad \phi - D grad n + // J_n = - \mu n E - D grad n + // note electrons move counter to electric field grad E = - grad \phi const DENS_MAT & n = edField->second; const DENS_MAT_VEC & dn = dEdField->second; const DENS_MAT_VEC & dphi = dPhiField->second; @@ -112,21 +113,21 @@ namespace ATC { if (maskX_) flux[0] = 0.; else { - flux[0] *= -electronMobility_*dphi[0]; // scale by n to get : -n \mu grad(\phi) + flux[0] *= electronMobility_*dphi[0]; // scale by n to get : n \mu grad(\phi) flux[0] += -electronDiffusivity_* dn[0]; } if (maskY_) flux[1] = 0.; else { - flux[1] *= -electronMobility_* dphi[1] ; + flux[1] *= electronMobility_* dphi[1] ; flux[1] += -electronDiffusivity_* dn[1]; } if (maskZ_) flux[2] = 0.; else { - flux[2] *= -electronMobility_*dphi[2]; + flux[2] *= electronMobility_*dphi[2]; flux[2] += -electronDiffusivity_* dn[2]; } diff --git a/lib/atc/ExtrinsicModelDriftDiffusion.cpp b/lib/atc/ExtrinsicModelDriftDiffusion.cpp index f98467703f..73beca3aae 100644 --- a/lib/atc/ExtrinsicModelDriftDiffusion.cpp +++ b/lib/atc/ExtrinsicModelDriftDiffusion.cpp @@ -16,6 +16,7 @@ #include using ATC_Utility::to_string; +using std::cout; using std::string; using std::set; using std::pair; @@ -27,8 +28,6 @@ const double f_tol = 1.e-8; namespace ATC { -enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; - //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusion @@ -56,7 +55,7 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; schrodingerPoissonSolver_(NULL), maxConsistencyIter_(0), maxConstraintIter_(1), safe_dEf_(0.1), Ef_shift_(0.0), - oneD_(false), oneDcoor_(0), oneDconserve_(ONED_DENSITY) + oneD_(false), oneDcoor_(0), oneDconserve_(0) { // delete base class's version of the physics model if (physicsModel_) delete physicsModel_; @@ -78,6 +77,8 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; physicsModel_ = new PhysicsModelDriftDiffusion(matFileName); } atc_->useConsistentMassMatrix_(ELECTRON_DENSITY) = true; + rhsMaskIntrinsic_(ELECTRON_TEMPERATURE,SOURCE) = true; + //atc_->fieldMask_(ELECTRON_TEMPERATURE,EXTRINSIC_SOURCE) = true; } //-------------------------------------------------------- @@ -157,21 +158,16 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; // set up schrodinger solver if ( electronDensityEqn_ == ELECTRON_SCHRODINGER ) { - int type = ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC; - if (schrodingerSolverType_ == DIRECT) { - type = ATC::LinearSolver::DIRECT_SOLVE; - } if ( schrodingerSolver_ ) delete schrodingerSolver_; if ( oneD_ ) { EfHistory_.reset(oneDslices_.size(),2); schrodingerSolver_ = new SliceSchrodingerSolver(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, - oneDslices_, type, true); + oneDslices_,oneDdxs_); } else { schrodingerSolver_ = new SchrodingerSolver(ELECTRON_DENSITY, - physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, - type, true); + physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_); } schrodingerSolver_->initialize(); @@ -187,6 +183,10 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; ((atc_->fields())[ELECTRON_WAVEFUNCTION_ENERGIES].set_quantity()).reset(nNodes_,1); } +#if 0 + cout << " RHS MASK\n"; + print_mask(rhsMask); +#endif } //-------------------------------------------------------- @@ -219,27 +219,53 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; poissonSolver_->solve(atc_->fields(),rhs_); } else if (electronDensityEqn_ == ELECTRON_SCHRODINGER) { - schrodingerPoissonSolver_->solve(rhs_,fluxes_); + if ( (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) + || (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) ) + schrodingerPoissonSolver_->solve(rhs_,fluxes_); } // update electron temperature - if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) + && temperatureIntegrator_ ) { +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->stream_msg_once("start temperature integration...",true,false); +#endif temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->stream_msg_once(" done",false,true); +#endif + } atc_->set_fixed_nodes(); } + } + //-------------------------------------------------------- + // set coupling source terms + //------------------------------------------------------- + void ExtrinsicModelDriftDiffusion::set_sources(FIELDS & fields, FIELDS & sources) + { + atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, + sources, + atc_->source_integration(), physicsModel_); + } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::output(OUTPUT_LIST & outputData) - { + { +#ifdef ATC_VERBOSE +// ATC::LammpsInterface::instance()->print_msg_once("start output",true,false); +#endif ExtrinsicModelTwoTemperature::output(outputData); // fields outputData["dot_electron_density"] = & (atc_->dot_field(ELECTRON_DENSITY)).set_quantity(); - outputData["joule_heating"] = & rhs_[ELECTRON_TEMPERATURE].set_quantity(); + DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); + double totalJouleHeating =JE.sum(); + outputData["joule_heating"] = & JE; + atc_->feEngine_->add_global("total_joule_heating",totalJouleHeating); Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = true; rhsMask(ELECTRIC_POTENTIAL,FLUX) = true; @@ -275,6 +301,9 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; // globals double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); atc_->feEngine_->add_global("total_electron_density",nSum); +#ifdef ATC_VERBOSE +// ATC::LammpsInterface::instance()->print_msg_once("... done",false,true); +#endif } //-------------------------------------------------------- @@ -284,7 +313,7 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; { int xSize = ExtrinsicModelTwoTemperature::size_vector(intrinsicSize); baseSize_ = intrinsicSize + xSize; - xSize += 1; + xSize += 2; return xSize; } @@ -294,6 +323,7 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; bool ExtrinsicModelDriftDiffusion::compute_vector(int n, double & value) { // output[1] = total electron density + // output[2] = total joule heating bool match = ExtrinsicModelTwoTemperature::compute_vector(n,value); if (match) return match; @@ -303,6 +333,12 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; value = nSum; return true; } + if (n == baseSize_+1) { + DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); + double totalJouleHeating =JE.sum(); + value = totalJouleHeating; + return true; + } return false; } @@ -332,8 +368,8 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; else { physicsModel_ = new PhysicsModelDriftDiffusionConvection(matFileName); } - atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY) = true; - atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE) = true; + atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY) = false; + atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE) = false; } //-------------------------------------------------------- @@ -353,21 +389,6 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; void ExtrinsicModelDriftDiffusionConvection::initialize() { ExtrinsicModelDriftDiffusion::initialize(); - - // change temperature integrator to be Crank-Nicolson - if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { - if (temperatureIntegrator_) delete temperatureIntegrator_; - Array2D rhsMask(NUM_FIELDS,NUM_FLUX); - rhsMask = false; - for (int i = 0; i < NUM_FLUX; i++) { - rhsMask(ELECTRON_TEMPERATURE,i) = atc_->fieldMask_(ELECTRON_TEMPERATURE,i); - } - temperatureIntegrator_ = new FieldImplicitEulerIntegrator(ELECTRON_TEMPERATURE, - physicsModel_, - atc_->feEngine_, atc_, - rhsMask); - } - nNodes_ = atc_->num_nodes(); nsd_ = atc_->nsd(); rhs_[ELECTRON_VELOCITY].reset(nNodes_,nsd_); @@ -433,17 +454,31 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; rhsMask = false; rhsMask(ELECTRON_VELOCITY,SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,SOURCE); rhsMask(ELECTRON_VELOCITY,FLUX) = atc_->fieldMask_(ELECTRON_VELOCITY,FLUX); + rhsMask(ELECTRON_VELOCITY,OPEN_SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,OPEN_SOURCE); FIELDS rhs; rhs[ELECTRON_VELOCITY].reset(nNodes_,nsd_); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); const DENS_MAT & velocityRhs = rhs[ELECTRON_VELOCITY].quantity(); + // add a solver for electron momentum DENS_MAT & velocity = (atc_->field(ELECTRON_VELOCITY)).set_quantity(); for (int j = 0; j < nsd_; ++j) { if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_VELOCITY,j) ) { - CLON_VEC v = column(velocity,j); - const CLON_VEC r = column(velocityRhs,j); - (velocitySolvers_[j])->solve(v,r); + if (atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY)) { + //#ifdef ATC_PRINT_DEBUGGING + const SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).quantity(); + velocityMassMat.print("VMASS"); + //#endif + CLON_VEC v = column(velocity,j); + const CLON_VEC r = column(velocityRhs,j); + (velocitySolvers_[j])->solve(v,r); + } + else { + //velocityRhs.print("VRHS"); + //const DIAG_MAT & velocityMassMat = (atc_->massMats_[ELECTRON_VELOCITY]).quantity(); + //velocityMassMat.print("MASS"); + atc_->apply_inverse_mass_matrix(velocityRhs,velocity,ELECTRON_VELOCITY); + } } } } @@ -452,18 +487,25 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; if (electronDensityEqn_ == ELECTRON_CONTINUITY) { // update continuity eqn + Array2D rhsMask(NUM_FIELDS,NUM_FLUX); + rhsMask = false; + rhsMask(ELECTRON_DENSITY,FLUX) = atc_->fieldMask_(ELECTRON_DENSITY,FLUX); + rhsMask(ELECTRON_DENSITY,SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,SOURCE); + rhsMask(ELECTRON_DENSITY,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,PRESCRIBED_SOURCE); + FIELDS rhs; + rhs[ELECTRON_DENSITY].reset(nNodes_,1); + atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) - continuityIntegrator_->update(idt,time,atc_->fields_,rhs_); + continuityIntegrator_->update(idt,time,atc_->fields_,rhs); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) { //poissonSolver_->solve(atc_->fields_,rhs_); - Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRIC_POTENTIAL,SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,SOURCE); rhsMask(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE); - FIELDS rhs; + // FIELDS rhs; rhs[ELECTRIC_POTENTIAL].reset(nNodes_,1); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); CLON_VEC x =column((atc_->field(ELECTRIC_POTENTIAL)).set_quantity(),0); @@ -479,8 +521,14 @@ enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; // update electron temperature mass matrix atc_->compute_mass_matrix(ELECTRON_TEMPERATURE,physicsModel_); // update electron temperature - if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) - temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) { + //if (atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE)) { + temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + //} + //else { // lumped mass matrix + + //} + } atc_->set_fixed_nodes(); } diff --git a/lib/atc/ExtrinsicModelDriftDiffusion.h b/lib/atc/ExtrinsicModelDriftDiffusion.h index 92e2365dc1..abc40e84d2 100644 --- a/lib/atc/ExtrinsicModelDriftDiffusion.h +++ b/lib/atc/ExtrinsicModelDriftDiffusion.h @@ -52,6 +52,9 @@ namespace ATC { /** Predictor phase, executed before Verlet */ virtual void pre_init_integrate(); + /** Set sources to AtC equation */ + virtual void set_sources(FIELDS & fields, FIELDS & sources); + /** Add model-specific output data */ virtual void output(OUTPUT_LIST & outputData); @@ -117,6 +120,7 @@ namespace ATC { std::string oneDnodesetName_; std::set oneDnodeset_; Array< std::set > oneDslices_; + Array< double > oneDdxs_; int oneDconserve_; DENS_MAT JE_; diff --git a/lib/atc/ExtrinsicModelElectrostatic.cpp b/lib/atc/ExtrinsicModelElectrostatic.cpp index d07af21c03..5f5321f65d 100644 --- a/lib/atc/ExtrinsicModelElectrostatic.cpp +++ b/lib/atc/ExtrinsicModelElectrostatic.cpp @@ -836,10 +836,12 @@ namespace ATC { double qV2e = LammpsInterface::instance()->qv2e(); double qqrd2e = LammpsInterface::instance()->qqrd2e(); //double ** fatom = LammpsInterface::instance()->fatom(); - double * qatom = LammpsInterface::instance()->atom_charge(); + //double * qatom = LammpsInterface::instance()->atom_charge(); + InterscaleManager & interscaleManager(atc_->interscale_manager()); + const DENS_MAT & qatom((interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE))->quantity()); double cutoffRadius = LammpsInterface::instance()->pair_cutoff(); double cutoffSq = cutoffRadius*cutoffRadius; - int nLocal = atc_->nlocal(); + int nLocal = qatom.nRows(); int nsd = atc_->nsd(); int nNodes = atc_->num_nodes(); double penalty = poissonSolver_->penalty_coefficient(); @@ -859,8 +861,7 @@ namespace ATC { PerAtomQuantity * atomicCoords = (atc_->interscale_manager()).per_atom_quantity("AtomicCoarseGrainingPositions"); const DENS_MAT & myAtomicCoords(atomicCoords->quantity()); for (int i = 0; i < nLocal; i++) { - int atomIdx = atc_->internalToAtom_(i); - if (abs(qatom[atomIdx]) > 0) { + if (abs(qatom(i,0)) > 0) { double distanceSq = 0.; double deltaX[3]; for (int j = 0; j < nsd; j++) { @@ -870,7 +871,7 @@ namespace ATC { if (distanceSq < cutoffSq) { // first apply pairwise coulombic interaction if (!useSlab_) { - double coulForce = qqrd2e*nodalCharge*qatom[atomIdx]/(distanceSq*sqrtf(distanceSq)); + double coulForce = qqrd2e*nodalCharge*qatom(i,0)/(distanceSq*sqrtf(distanceSq)); for (int j = 0; j < nsd; j++) //fatom[atomIdx][j] += deltaX[j]*coulForce; _atomElectricalForce_(i,j) += deltaX[j]*coulForce; @@ -882,7 +883,6 @@ namespace ATC { vector > derivativeVectors; derivativeVectors.reserve(nsd); - InterscaleManager & interscaleManager(atc_->interscale_manager()); const SPAR_MAT_VEC & shapeFunctionDerivatives((interscaleManager.vector_sparse_matrix("InterpolantGradient"))->quantity()); for (int j = 0; j < nsd; j++) { DenseVector nodeIndices; @@ -906,7 +906,7 @@ namespace ATC { // apply correction in atomic forces //double c = qE2f*qatom[atomIdx]; - double c = qV2e*qatom[atomIdx]; + double c = qV2e*qatom(i,0); for (int j = 0; j < nsd; j++) if ((!useSlab_) || (j==nsd)) { //fatom[atomIdx][j] -= c*efield(j); diff --git a/lib/atc/ExtrinsicModelTwoTemperature.cpp b/lib/atc/ExtrinsicModelTwoTemperature.cpp index ddacbf2294..2be6cfeb3c 100644 --- a/lib/atc/ExtrinsicModelTwoTemperature.cpp +++ b/lib/atc/ExtrinsicModelTwoTemperature.cpp @@ -116,7 +116,7 @@ namespace ATC { else if (strcmp(arg[argIndx],"electron_integration")==0) { argIndx++; nsubcycle_ = 1; - if (strcmp(arg[argIndx],"explicit")==0) { + if (strcmp(arg[argIndx],"explicit")==0) { electronTimeIntegration_ = TimeIntegrator::EXPLICIT; match = true; } @@ -124,10 +124,18 @@ namespace ATC { electronTimeIntegration_ = TimeIntegrator::IMPLICIT; match = true; } + else if (strcmp(arg[argIndx],"direct")==0) { + electronTimeIntegration_ = TimeIntegrator::DIRECT; + match = true; + } else if (strcmp(arg[argIndx],"steady")==0) { electronTimeIntegration_ = TimeIntegrator::STEADY; match = true; } + else if (strcmp(arg[argIndx],"off")==0) { + electronTimeIntegration_ = TimeIntegrator::NONE; + match = true; + } if (narg > ++argIndx) nsubcycle_ = atoi(arg[argIndx]); } // end "electron_integration" @@ -155,6 +163,10 @@ namespace ATC { for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRON_TEMPERATURE,i) = atc_->fieldMask_(ELECTRON_TEMPERATURE,i); } + if (electronTimeIntegration_ == TimeIntegrator::NONE) { + temperatureIntegrator_ = NULL; + return; + } if (temperatureIntegrator_) delete temperatureIntegrator_; if (electronTimeIntegration_ == TimeIntegrator::STEADY) { throw ATC_Error("not implemented"); @@ -165,11 +177,14 @@ namespace ATC { ELECTRON_TEMPERATURE, physicsModel_, atc_->feEngine_, atc_, rhsMask, alpha); } - else { + else if (electronTimeIntegration_ == TimeIntegrator::EXPLICIT) { temperatureIntegrator_ = new FieldExplicitEulerIntegrator( ELECTRON_TEMPERATURE, physicsModel_, atc_->feEngine_, atc_, rhsMask); } + double dt = atc_->lammpsInterface_->dt(); + double time = atc_->time(); + temperatureIntegrator_->initialize(dt,time,atc_->fields_); // set up mass matrix @@ -184,17 +199,27 @@ namespace ATC { //-------------------------------------------------------- void ExtrinsicModelTwoTemperature::pre_init_integrate() { +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->print_msg_once("start ttm evolution",true,false); +#endif + double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); // integrate fast electron variable/s // note: atc calls set_sources in pre_final_integrate atc_->set_fixed_nodes(); - double idt = dt/nsubcycle_; - for (int i = 0; i < nsubcycle_ ; ++i) { - temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) + && temperatureIntegrator_ ) { + double idt = dt/nsubcycle_; + for (int i = 0; i < nsubcycle_ ; ++i) { + temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + } } +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->print_msg_once("...done",false,true); +#endif } //-------------------------------------------------------- diff --git a/lib/atc/ExtrinsicModelTwoTemperature.h b/lib/atc/ExtrinsicModelTwoTemperature.h index 4c3038bba1..bf1979f400 100644 --- a/lib/atc/ExtrinsicModelTwoTemperature.h +++ b/lib/atc/ExtrinsicModelTwoTemperature.h @@ -42,12 +42,6 @@ namespace ATC { /** pre time integration */ virtual void initialize(); - /** set up LAMMPS display variables */ - virtual int size_vector(int externalSize); - - /** get LAMMPS display variables */ - virtual bool compute_vector(int n, double & value); - /** Predictor phase, executed before Verlet */ virtual void pre_init_integrate(); @@ -57,6 +51,12 @@ namespace ATC { /** Add model-specific output data */ virtual void output(OUTPUT_LIST & outputData); + /** set up LAMMPS display variables */ + virtual int size_vector(int externalSize); + + /** get LAMMPS display variables */ + virtual bool compute_vector(int n, double & value); + protected: /** electron time integration flag */ TimeIntegrator::TimeIntegrationType electronTimeIntegration_; diff --git a/lib/atc/FE_Element.cpp b/lib/atc/FE_Element.cpp index 6bc9d390aa..cfc59aa601 100644 --- a/lib/atc/FE_Element.cpp +++ b/lib/atc/FE_Element.cpp @@ -178,6 +178,26 @@ static const double localCoordinatesTolerance = 1.e-09; xi(1) = J2/J; xi(2) = J3/J; } + else if (projectionGuess_ == TWOD_ANALYTIC) { + // assume x-y planar and HEX8 + double x0 = x(0), y0 = x(1); + double X[4] = {eltCoords(0,0),eltCoords(0,1),eltCoords(0,2),eltCoords(0,3)}; + double Y[4] = {eltCoords(1,0),eltCoords(1,1),eltCoords(1,2),eltCoords(1,3)}; + double c[3]={0,0,0}; + c[0] = y0*X[0] - y0*X[1] - y0*X[2] + y0*X[3] - x0*Y[0] + (X[1]*Y[0])*0.5 + (X[2]*Y[0])*0.5 + x0*Y[1] - (X[0]*Y[1])*0.5 - (X[3]*Y[1])*0.5 + x0*Y[2] - (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - x0*Y[3] + (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; + c[1] = -(y0*X[0]) + y0*X[1] - y0*X[2] + y0*X[3] + x0*Y[0] - X[1]*Y[0] - x0*Y[1] + X[0]*Y[1] + x0*Y[2] - X[3]*Y[2] - x0*Y[3] + X[2]*Y[3]; + c[1] = (X[1]*Y[0])*0.5 - (X[2]*Y[0])*0.5 - (X[0]*Y[1])*0.5 + (X[3]*Y[1])*0.5 + (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; + double xi2[2]={0,0}; + int nroots = solve_quadratic(c,xi2); + if (nroots == 0) throw ATC_Error("no real roots in 2D analytic projection guess"); + double xi1[2]={0,0}; + xi1[0] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); + xi1[1] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); + // choose which one gives back x + xi(0) = xi1[0]; + xi(1) = xi2[0]; + xi(2) = 0; + } } diff --git a/lib/atc/FE_Engine.cpp b/lib/atc/FE_Engine.cpp index a87a03032c..4b519e336f 100644 --- a/lib/atc/FE_Engine.cpp +++ b/lib/atc/FE_Engine.cpp @@ -89,7 +89,7 @@ namespace ATC{ // now do all FE_Engine data structure partitioning // partition nullElements_ - /*for (vector::const_iterator elemsIter = feMesh_->processor_elts().begin(); + /*for (vector::iterator elemsIter = feMesh_->processor_elts().begin(); elemsIter != feMesh_->processor_elts().end(); ++elemsIter) { @@ -182,33 +182,15 @@ namespace ATC{ dx = 0; dy = 0; dz = 0; - double x[3] = {0,0,0}; while (argIdx < narg) { - if (strcmp(arg[argIdx++],"dx")==0) { - // parse relative values for each element - if (is_numeric(arg[argIdx])) { - for (int i = 0; i < nx; ++i) { - if (is_numeric(arg[argIdx])) { dx(i) = atof(arg[argIdx++]); } - else throw ATC_Error("not enough element partitions"); - } - } - // construct relative values from a density function - // evaluate for a domain (0,1) - else { - XT_Function * f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); - for (int i = 0; i < nx; ++i) { - x[0] = (i+0.5)/nx; dx(i) = f->f(x,0.); - } - } - - - break ; + if (strcmp(arg[argIdx],"dx")==0) { + parse_partitions(++argIdx, narg, arg, 0, dx); } - else if (strcmp(arg[argIdx++],"dy")==0) { - for (int i = 0; i < ny; ++i) { dy(i) = atof(arg[argIdx++]); } + else if (strcmp(arg[argIdx],"dy")==0) { + parse_partitions(++argIdx, narg, arg, 1, dy); } - else if (strcmp(arg[argIdx++],"dz")==0) { - for (int i = 0; i < nz; ++i) { dz(i) = atof(arg[argIdx++]); } + else if (strcmp(arg[argIdx],"dz")==0) { + parse_partitions(++argIdx, narg, arg, 2, dz); } } @@ -354,6 +336,65 @@ namespace ATC{ } return match; } + //----------------------------------------------------------------- + void FE_Engine::parse_partitions(int & argIdx, int narg, char ** arg, + int idof, Array & dx ) const + { + double x[3] = {0,0,0}; + int nx = dx.size(); + // parse relative values for each element + if (is_numeric(arg[argIdx])) { + for (int i = 0; i < nx; ++i) { + if (is_numeric(arg[argIdx])) { dx(i) = atof(arg[argIdx++]); } + else throw ATC_Error("not enough element partitions"); + } + } + // each segment of the piecewise funcion is length-normalized separately + else if (strcmp(arg[argIdx],"position-number-density")==0) { + argIdx++; + double y[nx],w[nx]; + int n[nx]; + int nn = 0; + while (argIdx < narg) { + if (! is_numeric(arg[argIdx])) break; + y[nn] = atof(arg[argIdx++]); + n[nn] = atoi(arg[argIdx++]); + w[nn++] = atof(arg[argIdx++]); + } + if (n[nn-1] != nx) throw ATC_Error("total element partitions do not match"); + int k = 0; + for (int i = 1; i < nn; ++i) { + int dn = n[i]-n[i-1]; + double dy = y[i]-y[i-1]; + double w0 = w[i-1]; + double dw = w[i]-w0; + double lx = 0; + double l[dn]; + for (int j = 0; j < dn; ++j) { + double x = (j+0.5)/dn; + double dl = w0+x*dw; + lx += dl; + l[j]= dl; + } + double scale = dy/lx; + for (int j = 0; j < dn; ++j) { + dx(k++) = scale*l[j]; + } + } + } + // construct relative values from a density function + // evaluate for a domain (0,1) + else { + XT_Function * f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + argIdx++; + while (argIdx < narg) { + if (! is_numeric(arg[argIdx++])) break; + } + for (int i = 0; i < nx; ++i) { + x[idof] = (i+0.5)/nx; dx(i) = f->f(x,0.); + } + } + } //----------------------------------------------------------------- void FE_Engine::finish() @@ -550,7 +591,7 @@ namespace ATC{ DENS_MAT elementMassMatrix(nNodesPerElement_,nNodesPerElement_); vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -604,7 +645,7 @@ namespace ATC{ DENS_MAT coefsAtIPs; vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -782,7 +823,7 @@ namespace ATC{ // element wise assembly vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -849,7 +890,7 @@ namespace ATC{ massMatrix.reset(nNodesUnique_,nNodesUnique_);// zero since partial fill DENS_MAT elementMassMatrix(nNodesPerElement_,nNodesPerElement_); vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -905,7 +946,7 @@ namespace ATC{ // assemble lumped diagonal mass DENS_VEC Nvec(nNodesPerElement_); vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -944,7 +985,7 @@ namespace ATC{ } // assemble diagonal matrix vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -1028,9 +1069,13 @@ namespace ATC{ // treat single material point sets specially int nMatls = pointMaterialGroups.size(); int atomMatls = 0; + int iatomMat = 0; for (int imat = 0; imat < nMatls; imat++) { const set & indices = pointMaterialGroups(imat); - if ( indices.size() > 0) atomMatls++; + if ( indices.size() > 0) { + iatomMat = imat; + atomMatls++; + } } if (atomMatls == 0) { throw ATC_Error("no materials in atom region - atoms may have migrated to FE-only region"); @@ -1045,12 +1090,11 @@ namespace ATC{ FIELD_MATS capacities; // evaluate physics model & compute capacity|density for requested fields if (singleMaterial) { - int imat = 0; - const Material * mat = physicsModel->material(imat); + const Material * mat = physicsModel->material(iatomMat); for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); // mass matrix needs to be invertible so null matls have cap=1 - if (physicsModel->null(_fieldName_,imat)) { + if (physicsModel->null(_fieldName_,iatomMat)) { throw ATC_Error("null material not supported for atomic region (single material)"); const FIELD & f = (fields.find(_fieldName_))->second; capacities[_fieldName_].reset(f.nRows(),f.nCols()); @@ -1143,7 +1187,7 @@ namespace ATC{ Array count(nNodesUnique_); count = 0; set_quadrature(NODAL); vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -1204,7 +1248,7 @@ namespace ATC{ DENS_MAT elementEnergy(nNodesPerElement_,1); // workspace vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -1249,6 +1293,7 @@ namespace ATC{ const PhysicsModel * physicsModel, const Array & elementMaterials, FIELDS &rhs, + bool freeOnly, const DenseMatrix *elementMask) const { vector usedFields; @@ -1269,7 +1314,7 @@ namespace ATC{ // Iterate over elements partitioned onto current processor. vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -1337,7 +1382,7 @@ namespace ATC{ } } - vector::const_iterator _fieldIter_; + vector::iterator _fieldIter_; for (_fieldIter_ = usedFields.begin(); _fieldIter_ != usedFields.end(); ++_fieldIter_) { // myRhs is where we put the global result for this field. @@ -1660,7 +1705,7 @@ namespace ATC{ // element wise assembly vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -1740,7 +1785,7 @@ namespace ATC{ } // element wise assembly - set< PAIR >::const_iterator iter; + set< PAIR >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { // get connectivity int ielem = iter->first; @@ -1841,10 +1886,11 @@ namespace ATC{ // R_I = - int_Omega Delta _N_I .q dV compute_rhs_vector(rhsMask, fields, physicsModel, elementMaterials, rhs, elementMask); + // R_I^md = - int_Omega^md Delta _N_I .q dV compute_rhs_vector(rhsMask, fields, physicsModel, pointMaterialGroups, _weights_, N, dN, rhsSubset); - + // flux_I = 1/Delta V_I V_I^md R_I + R_I^md // where : Delta V_I = int_Omega _N_I dV for (int n = 0; n < rhsMask.nRows(); n++) { @@ -1920,21 +1966,16 @@ namespace ATC{ DENS_MAT localField; DENS_MAT xAtIPs(nSD_,nIPsPerFace_); DENS_MAT uAtIPs(nIPsPerFace_,1); - + FIELDS myNodalSources; // element wise assembly ROBIN_SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) - { - _fieldName_ = src_iter->first; - if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } + for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) + { + _fieldName_ = src_iter->first; + if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { @@ -1982,12 +2023,12 @@ namespace ATC{ } // assemble - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); _Nmat_ = _fN_.transMat(_fweights_*faceSource); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } } } @@ -1996,8 +2037,10 @@ namespace ATC{ for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!rhsMask((int) _fieldName_,ROBIN_SOURCE)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; } } @@ -2021,11 +2064,7 @@ namespace ATC{ // element wise assembly ROBIN_SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out result (tangent) matrix on all non-main processors - // to avoid multiple-counting of the values already in tangent - tangent.reset(tangent.nRows(), tangent.nCols()); - } + SPAR_MAT K(tangent.nRows(), tangent.nCols()); for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; @@ -2068,24 +2107,221 @@ namespace ATC{ // assemble DIAG_MAT D(column(coefsAtIPs,0)); D *= -1; - D *= _fweights_; _Nmat_ = _fN_.transMat(D*_fN_); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); - tangent.add(inode, jnode, _Nmat_(i,j)); + K.add(inode, jnode, _Nmat_(i,j)); } } } } // assemble partial result matrices #ifdef ISOLATE_FE - sparse_allsum(communicator_,tangent); + sparse_allsum(communicator_,K); #else - LammpsInterface::instance()->sparse_allsum(tangent); + LammpsInterface::instance()->sparse_allsum(K); #endif + tangent += K; + } + + //----------------------------------------------------------------- + // Robin boundary flux using native quadrature + // integrate \int_delV _N_I s(x,t).n dA + //----------------------------------------------------------------- + void FE_Engine::add_open_fluxes(const Array2D &rhsMask, + const FIELDS & fields, + const OPEN_SURFACE & openFaces, + FIELDS &nodalSources, + const FieldName Velocity) const + { + + // sizing working arrays + DENS_MAT xCoords(nSD_,nNodesPerElement_); + DENS_MAT faceSource; + DENS_MAT localField; + DENS_MAT xAtIPs(nSD_,nIPsPerFace_); + DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols); + DENS_MAT aAtIPs(nIPsPerFace_,1); + FIELDS myNodalSources; + + // throw error if electron velocity is not defined + + // element wise assembly + OPEN_SURFACE::const_iterator face_iter; + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); + } + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + + typedef set FSET; + const FSET *fset = (const FSET *)&(face_iter->second); + FSET::const_iterator fset_iter; + for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) + { + const PAIR &face = *fset_iter; + const int elem = face.first; + // if this is not our element, do not do calculations + if (!feMesh_->is_owned_elt(elem)) continue; + // get velocity, multiply with field (vector gives tensor) + // dot with face normal + _conn_ = feMesh_->element_connectivity_unique(elem); + // evaluate location at ips + feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); + // collect field at ips + _fieldItr_ = fields.find(_fieldName_); + const DENS_MAT & field = (_fieldItr_->second).quantity(); + feMesh_->element_field(elem, field, localField); + int nFieldDOF = field.nCols(); + // "u" is the quantity being advected + uAtIPs.reset(nIPsPerFace_,nFieldDOF); + uAtIPs = _fN_*localField; + // collect velocity at ips for "advection" = v.n + _fieldItr_ = fields.find(Velocity); + const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity + feMesh_->element_field(elem, advection, localField); + for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int I = 0; I < nNodesPerElement_; ++I) { + aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j); + } + } + } + // construct open flux at ips of this element + // flux = field \otimes advection_vector \dot n + FSET::const_iterator face_iter = fset->find(face); + faceSource.reset(nIPsPerFace_,nFieldDOF); + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int idof = 0; idof facesource is nips X ndofs + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + _Nmat_ = _fN_.transMat(_fweights_*faceSource); + // s_Ii = \sum_ip N_I (u_i v.n)_ip wg_ip + for (int i = 0; i < nNodesPerElement_; ++i) { + int inode = _conn_(i); + for (int idof = 0; idof < nFieldDOF; ++idof) { + s(inode,idof) += _Nmat_(i,idof); + } + } + } + } + // assemble partial result matrices + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) { + _fieldName_ = face_iter->first; + if (!rhsMask((int) _fieldName_,OPEN_SOURCE)) continue; + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; + } + } + + //----------------------------------------------------------------- + // Open boundary flux stiffness using native quadrature + // integrate \int_delV _N_I ds/du(x,t).n dA + //----------------------------------------------------------------- + void FE_Engine::add_open_tangent(const Array2D &rhsMask, + const FIELDS & fields, + const OPEN_SURFACE & openFaces, + SPAR_MAT &tangent, + const FieldName Velocity) const + { + + // sizing working arrays + DENS_MAT xCoords(nSD_,nNodesPerElement_); + DENS_MAT faceSource; + DENS_MAT localField; + DENS_MAT xAtIPs(nSD_,nIPsPerFace_); + DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols); + DENS_MAT aAtIPs(nIPsPerFace_,nSD_); + SPAR_MAT K(tangent.nRows(), tangent.nCols()); + // element wise assembly + OPEN_SURFACE::const_iterator face_iter; + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + bool convective = false; + if (_fieldName_ == Velocity) convective = true; + + typedef set FSET; + const FSET *fset = (const FSET *)&(face_iter->second); + FSET::const_iterator fset_iter; + for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) + { + const PAIR &face = *fset_iter; + const int elem = face.first; + // if this is not our element, do not do calculations + if (!feMesh_->is_owned_elt(elem)) continue; + _conn_ = feMesh_->element_connectivity_unique(elem); + // evaluate location at ips + feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); + // collect field at ips + _fieldItr_ = fields.find(_fieldName_); + const DENS_MAT & field = (_fieldItr_->second).quantity(); + feMesh_->element_field(elem, field, localField); + int nFieldDOF = field.nCols(); + // "u" is the quantity being advected + uAtIPs.reset(nIPsPerFace_,nFieldDOF); + uAtIPs = _fN_*localField; + // collect velocity at ips for "advection" = v.n + _fieldItr_ = fields.find(Velocity); + const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity + feMesh_->element_field(elem, advection, localField); + for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int I = 0; I < nNodesPerElement_; ++I) { + aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j); + } + } + } + // K_IJ = \sum_ip N_I ( v.n I + u (x) n ) N_J wg_ip + DENS_MAT D(nFieldDOF,nFieldDOF); + DENS_VEC n(nSD_); + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int idof = 0; idofface_normal(face,ip,n); + for (int jdof = 0; jdofsparse_allsum(K); +#endif + tangent += K; } //----------------------------------------------------------------- @@ -2102,20 +2338,16 @@ namespace ATC{ DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT xAtIPs(nSD_,nIPsPerFace_); DENS_MAT faceSource; + FIELDS myNodalSources; // element wise assembly SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) - { - _fieldName_ = src_iter->first; - if (!fieldMask((int)_fieldName_)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } + for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) + { + _fieldName_ = src_iter->first; + if (!fieldMask((int)_fieldName_)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { @@ -2162,14 +2394,14 @@ namespace ATC{ } // assemble - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); _Nmat_ = _fN_.transMat(_fweights_*faceSource); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } // end assemble nFieldDOF } // end assemble nNodesPerElement_ } // end fset loop @@ -2178,8 +2410,10 @@ namespace ATC{ for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!fieldMask((int)_fieldName_)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; } } @@ -2197,24 +2431,18 @@ namespace ATC{ DENS_MAT elemSource; DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT xAtIPs(nSD_,nIPsPerElement_); + FIELDS myNodalSources; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); - src_iter != sourceFunctions.end(); src_iter++) { - _fieldName_ = src_iter->first; - int index = (int) _fieldName_; - if (fieldMask(index)) { - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } - } + for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); + src_iter != sourceFunctions.end(); src_iter++) { + _fieldName_ = src_iter->first; + int index = (int) _fieldName_; + if (! fieldMask(index)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } - vector myElems = feMesh_->owned_elts(); - for (vector::const_iterator elemsIter = myElems.begin(); + for (vector::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { @@ -2244,12 +2472,12 @@ namespace ATC{ } // assemble _Nmat_ = _N_.transMat(_weights_*elemSource); - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } } } @@ -2260,9 +2488,31 @@ namespace ATC{ src_iter != sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; int index = (int) _fieldName_; - if (fieldMask(index)) { - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + if (!fieldMask(index)) continue; + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; + } + } + //----------------------------------------------------------------- + // previously computed nodal sources + //----------------------------------------------------------------- + void FE_Engine::add_sources(const Array &fieldMask, + const double time, + const FIELDS &sources, + FIELDS &nodalSources) const + { + FIELDS::const_iterator itr; + for (itr=sources.begin(); itr!=sources.end(); itr++) { + _fieldName_ = itr->first; + if (!fieldMask((int)_fieldName_)) continue; + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + const DENS_MAT & s((sources.find(_fieldName_)->second).quantity()); + for (int inode = 0; inode < nNodesUnique_; ++inode) { + for (int j = 0; j < src.nCols(); ++j) { + src(inode,j) += s(inode,j); + } } } } @@ -2291,7 +2541,7 @@ namespace ATC{ // the data member? // element wise assembly - set< pair >::const_iterator iter; + set< pair >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { int ielem = iter->first; // if this is not our element, do not do calculations @@ -2644,31 +2894,43 @@ namespace ATC{ << feMesh_->num_elements() << " elements"; print_msg_once(communicator_,ss.str()); #ifdef ATC_VERBOSE - string msg = "element sizes in x:\n"; + print_partitions(xmin,xmax,dx); + print_partitions(ymin,ymax,dy); + print_partitions(zmin,zmax,dz); +#endif + } + //----------------------------------------------------------------- + void FE_Engine::print_partitions(double xmin, double xmax, Array & dx) const { + stringstream msg; + msg.precision(3); + msg << std::fixed; + msg << "\nindex weight fraction location size[A] size[uc]:\n"; double sum = 0; for (int i = 0; i < dx.size(); ++i) { sum += dx(i); } double xn= 1.0/sum; double xs= xn*(xmax-xmin); double xl= xs/(ATC::LammpsInterface::instance()->xlattice()); double sumn = 0, sums = 0, suml = 0; + double x = xmin; for (int i = 0; i < dx.size(); ++i) { double dxn = dx(i)*xn; sumn += dxn; double dxs = dx(i)*xs; sums += dxs; double dxl = dx(i)*xl; suml += dxl; - msg += " "+to_string(i+1) - +" "+to_string(dx(i)) - +" "+to_string(dxn) - +" "+to_string(dxs) - +" "+to_string(dxl)+"\n"; + msg << std::setw(5) << i+1 + << std::setw(7) << dx(i) + << std::setw(9) << dxn + << std::setw(9) << x + << std::setw(8) << dxs + << std::setw(9) << dxl << "\n"; + x += dxs; } - msg += "sum "+to_string(sum)+" " - +to_string(sumn)+" " - +to_string(sums)+" " - +to_string(suml)+"\n"; - print_msg_once(communicator_,msg); -#endif + msg << "sum " << setw(7) << sum + << setw(9) << sumn + << setw(9) << x + << setw(8) << sums + << setw(9) << suml << "\n"; + print_msg_once(communicator_,msg.str()); } - //----------------------------------------------------------------- // create a uniform rectangular mesh on a rectangular region //----------------------------------------------------------------- diff --git a/lib/atc/FE_Engine.h b/lib/atc/FE_Engine.h index ab902fe605..bc3fb0e4a5 100644 --- a/lib/atc/FE_Engine.h +++ b/lib/atc/FE_Engine.h @@ -222,6 +222,7 @@ namespace ATC { const PhysicsModel *physicsModel, const Array &elementMaterials, FIELDS &rhs, + bool freeOnly=false, const DenseMatrix *elementMask=NULL) const; /** compute RHS for given quadrature */ @@ -295,7 +296,7 @@ namespace ATC { add_fluxes(fieldMask, time, sourceFunctions, nodalSources); } - /** compute prescribed flux given an array of functions of u, x & t */ + /** compute robin flux given an array of functions of u, x & t */ void add_robin_fluxes(const Array2D &rhsMask, const FIELDS &fields, const double time, @@ -308,11 +309,29 @@ namespace ATC { const ROBIN_SURFACE_SOURCE &sourceFunctions, SPAR_MAT &tangent) const; + /** compute open flux given a face set */ + + void add_open_fluxes(const Array2D &rhsMask, + const FIELDS &fields, + const OPEN_SURFACE &openFaces, + FIELDS &nodalSources, + const FieldName velocity = ELECTRON_VELOCITY) const; + + void add_open_tangent(const Array2D &rhsMask, + const FIELDS &fields, + const OPEN_SURFACE &openFaces, + SPAR_MAT &tangent, + const FieldName velocity = ELECTRON_VELOCITY) const; + /** compute nodal vector of volume based sources */ void add_sources(const Array &fieldMask, const double time, const VOLUME_SOURCE &sourceFunctions, FIELDS &nodalSources) const; + void add_sources(const Array &fieldMask, + const double time, + const FIELDS &sources, + FIELDS &nodalSources) const; /** compute surface flux of a nodal field */ void field_surface_flux(const DENS_MAT &field, @@ -496,6 +515,10 @@ namespace ATC { /** initialized flag */ bool initialized_; + void parse_partitions(int & argIdx, int narg, char ** arg, + int idof, Array & dx ) const; + void print_partitions(double xmin, double xmax, Array &dx) const; + /** create a uniform, structured mesh */ void create_mesh(Array &dx, Array &dy, diff --git a/lib/atc/FE_Mesh.cpp b/lib/atc/FE_Mesh.cpp index 6e7036a0c9..6225a1b192 100644 --- a/lib/atc/FE_Mesh.cpp +++ b/lib/atc/FE_Mesh.cpp @@ -24,6 +24,7 @@ using ATC_Utility::split_values; using ATC_Utility::plane_coords; using ATC_Utility::norm3; using ATC_Utility::to_string; +using ATC_Utility::tolerance; @@ -113,55 +114,24 @@ namespace ATC { if (strcmp(arg[argIdx],"plane")==0) { argIdx++; - string n(arg[argIdx++]); - int idir = -1, isgn = 0; - int i2dir = -1, i3dir = -1; - double x2min = 0.0, x2max = 0.0, x3min = 0.0, x3max = 0.0; - string_to_index(n, idir, isgn); - double x = parse_minmax(arg[argIdx++]); - if (narg > argIdx ) { - if (string_to_index(arg[argIdx],i2dir)) { - argIdx++; - x2min = parse_min(arg[argIdx++]); - x2max = parse_max(arg[argIdx++]); - } - } - if (narg > argIdx ) { - if (string_to_index(arg[argIdx],i3dir)) { - argIdx++; - x3min = parse_min(arg[argIdx++]); - x3max = parse_max(arg[argIdx++]); - } - } - if (i2dir >= 0 && - ((i2dir == idir) || (i2dir == idir) || (i2dir == idir) ) ) - { - throw ATC_Error( "inconsistent directions in create_faceset plane"); - } - if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) - {} - else - { - if (idir == 0) { x *= xscale_; } - else if (idir == 1) { x *= yscale_; } - else if (idir == 2) { x *= zscale_; } - if (i2dir >=0) { - if (i2dir == 0) { x2min *= xscale_; x2max *= xscale_; } - else if (i2dir == 1) { x2min *= yscale_; x2max *= yscale_; } - else if (i2dir == 2) { x2min *= zscale_; x2max *= zscale_; } - } - if (i3dir >=0) { - if (i3dir == 0) { x3min *= xscale_; x2max *= xscale_; } - else if (i3dir == 1) { x3min *= yscale_; x3max *= yscale_; } - else if (i3dir == 2) { x3min *= zscale_; x3max *= zscale_; } - } - } - if (i2dir >=0) { - create_faceset(tag, x, idir, isgn, i2dir, x2min, x2max, - i3dir, x3min, x3max); + int ndir, idir[3], isgn; + double xlimits[3][2]; + parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); + if (xlimits[idir[1]][0] == xlimits[idir[1]][1]) + split_values(xlimits[idir[1]][0],xlimits[idir[1]][1]); + if (xlimits[idir[2]][0] == xlimits[idir[2]][1]) + split_values(xlimits[idir[2]][0],xlimits[idir[2]][1]); + parse_units(argIdx,narg,arg, + xlimits[0][0],xlimits[0][1], + xlimits[1][0],xlimits[1][1], + xlimits[2][0],xlimits[2][1]); + if (ndir > 1) { + create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn, + idir[1], xlimits[idir[1]][0], xlimits[idir[1]][1], + idir[2], xlimits[idir[2]][0], xlimits[idir[2]][1]); } else { - create_faceset(tag, x, idir, isgn); + create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn); } match = true; } @@ -178,18 +148,7 @@ namespace ATC { bool outward = true; if (narg > argIdx && (strcmp(arg[argIdx++],"in") == 0)) outward = false; - - if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) {} - else - { // scale from lattice units to physical units - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - } - + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_faceset(tag, xmin, xmax, ymin, ymax, zmin, zmax, outward); match = true; } @@ -212,13 +171,33 @@ namespace ATC { Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_nodeset")==0) { - string tag = arg[2]; - double xmin = parse_min(arg[3]); - double xmax = parse_max(arg[4]); - double ymin = parse_min(arg[5]); - double ymax = parse_max(arg[6]); - double zmin = parse_min(arg[7]); - double zmax = parse_max(arg[8]); + int argIdx = 2; + string tag = arg[argIdx++]; + double xmin, xmax, ymin, ymax, zmin, zmax; + if (strcmp(arg[argIdx],"plane")==0) { + argIdx++; + int ndir, idir[3], isgn; + double xlimits[3][2]; + parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); + xmin = xlimits[0][0]; + xmax = xlimits[0][1]; + ymin = xlimits[1][0]; + ymax = xlimits[1][1]; + zmin = xlimits[2][0]; + zmax = xlimits[2][1]; + } + else { + xmin = parse_min(arg[argIdx++]); + xmax = parse_max(arg[argIdx++]); + ymin = parse_min(arg[argIdx++]); + ymax = parse_max(arg[argIdx++]); + zmin = parse_min(arg[argIdx++]); + zmax = parse_max(arg[argIdx++]); + } + if (xmin == xmax) split_values(xmin,xmax); + if (ymin == ymax) split_values(ymin,ymax); + if (zmin == zmax) split_values(zmin,zmax); + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } @@ -268,7 +247,8 @@ namespace ATC { Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_elementset")==0) { - string tag = arg[2]; + int argIdx = 2; + string tag = arg[argIdx++]; double xmin = 0; double xmax = 0; double ymin = 0; @@ -276,15 +256,19 @@ namespace ATC { double zmin = 0; double zmax = 0; if (narg > 4) { - xmin = parse_min(arg[3]); - xmax = parse_max(arg[4]); - ymin = parse_min(arg[5]); - ymax = parse_max(arg[6]); - zmin = parse_min(arg[7]); - zmax = parse_max(arg[8]); + xmin = parse_min(arg[argIdx++]); + xmax = parse_max(arg[argIdx++]); + ymin = parse_min(arg[argIdx++]); + ymax = parse_max(arg[argIdx++]); + zmin = parse_min(arg[argIdx++]); + zmax = parse_max(arg[argIdx++]); + if (xmin == xmax) split_values(xmin,xmax); + if (ymin == ymax) split_values(ymin,ymax); + if (zmin == zmax) split_values(zmin,zmax); + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); } else { - string regionName = arg[3]; + string regionName = arg[argIdx++]; LammpsInterface::instance()-> region_bounds(regionName.c_str(),xmin,xmax,ymin,ymax,zmin,zmax); } @@ -355,16 +339,86 @@ namespace ATC { return match; } // ------------------------------------------------------------- + void FE_Mesh::parse_units(int & argIdx, int narg, char ** arg, + double & xmin, double & xmax, double & ymin, double & ymax, + double & zmin, double & zmax) + { + if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) {} + else + { // scale from lattice units to physical units + xmin *= xscale_; xmax *= xscale_; + ymin *= yscale_; ymax *= yscale_; + zmin *= zscale_; zmax *= zscale_; + } + } + // ------------------------------------------------------------- + // parse plane + // ------------------------------------------------------------- + void FE_Mesh::parse_plane(int & argIdx, int narg, char ** arg, + int & ndir, int * idir, int & isgn, double xlimits[][2]) + { + ndir = 0; + xlimits[0][0] = parse_min("-INF"); + xlimits[0][1] = parse_max("INF"); + xlimits[1][0] = parse_min("-INF"); + xlimits[1][1] = parse_max("INF"); + xlimits[2][0] = parse_min("-INF"); + xlimits[2][1] = parse_max("INF"); + string n(arg[argIdx++]); + int i1dir = -1, i2dir = -1, i3dir = -1; + string_to_index(n, i1dir, isgn); + idir[ndir++] = i1dir; + idir[ndir] = (i1dir+1) % 3; + idir[ndir+1] = (i1dir+2) % 3; + xlimits[i1dir][0] = parse_minmax(arg[argIdx++]); + xlimits[i1dir][1] = xlimits[i1dir][0]; + if (narg > argIdx ) { + if (string_to_index(arg[argIdx],i2dir)) { + argIdx++; + xlimits[i2dir][0] = parse_min(arg[argIdx++]); + xlimits[i2dir][1] = parse_max(arg[argIdx++]); + idir[ndir++] = i2dir; + } + if (narg > argIdx ) { + if (string_to_index(arg[argIdx],i3dir)) { + argIdx++; + xlimits[i3dir][0] = parse_min(arg[argIdx++]); + xlimits[i3dir][1] = parse_max(arg[argIdx++]); + } + } + else { + i3dir = 0; + if ((i1dir == 0 && i2dir == 1) || (i1dir == 1 && i2dir == 0) ) { + i3dir = 2; + } + else if ((i1dir == 0 && i2dir == 2) || (i1dir == 2 && i2dir == 0) ) { + i3dir = 1; + } + } + idir[ndir++] = i3dir; + } + if ((idir[0]==idir[1]) || (idir[0]==idir[2]) || (idir[1]==idir[2]) ) { + throw ATC_Error( "inconsistent directions in plane:"+to_string(idir[0]+1)+" "+to_string(idir[1]+1)+" "+to_string(idir[2]+1)); + } + } + // ------------------------------------------------------------- // initialize // ------------------------------------------------------------- void FE_Mesh::initialize(void) { + bool aligned = is_aligned(); if (!aligned) { feElement_->set_projection_guess(CENTROID_LINEARIZED); ATC::LammpsInterface::instance()->print_msg_once("WARNING: mesh is not aligned with the coordinate directions atom-to-element mapping will be expensive"); // if HEX8 -> orient(); } + bool twoD = is_two_dimensional(); + if (twoD) { + feElement_->set_projection_guess(TWOD_ANALYTIC); + if (feElement_->order()< 3) hasPlanarFaces_ = true; + ATC::LammpsInterface::instance()->print_msg_once(" mesh is two dimensional"); + } } //----------------------------------------------------------------- @@ -702,7 +756,7 @@ namespace ATC { // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset - (const set & elemSet, set & nodeSet) const + (const set & elemSet, set nodeSet) const { int npe = num_nodes_per_element(); set::const_iterator itr; @@ -721,7 +775,7 @@ namespace ATC { // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset - (const string & name, set & nodeSet) const + (const string & name, set nodeSet) const { if (name == "all") for (int ielem = 0; ielem < nElts_; ielem++) @@ -748,6 +802,13 @@ namespace ATC { } } } + set FE_Mesh::elementset_to_nodeset + (const string & name) const + { + set nset; + elementset_to_nodeset(name,nset); + return nset; + } // ------------------------------------------------------------- // elementset_to_minimal_nodeset @@ -944,17 +1005,6 @@ namespace ATC { throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set nodeSet; // Loop over nodes and add their unique id's to the set if they're @@ -1003,17 +1053,6 @@ namespace ATC { throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set nodeSet; // Loop over nodes and add their unique id's to the set if they're @@ -1060,10 +1099,6 @@ namespace ATC { if (iter != faceSetMap_.end()) throw ATC_Error( "A faceset with name " + name + " is already defined."); - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - set faceSet; // Loop over face and add their unique id's to the set if they concide // with region @@ -1147,7 +1182,7 @@ namespace ATC { int nIdx2, double x2lo, double x2hi, int nIdx3, double x3lo, double x3hi) { - // note scaling done by caller and split_values is tricky with orientation + double xtol = tolerance(xRef); // Make sure we don't already have a faceset with this name FACE_SET_MAP::iterator iter = faceSetMap_.find(name); if (iter != faceSetMap_.end()) @@ -1170,7 +1205,7 @@ namespace ATC { for (int inode = 0; inode < npf; inode++) { int node = connectivity_(face_conn(iface,inode),ielem); double x = nodalCoords_(nIdx,node); - if ( fabs(x-xRef) > tol){ in = false; break;} + if ( fabs(x-xRef) > xtol){ in = false; break;} if (finite2) { double y = nodalCoords_(nIdx2,node); if ( y < x2lo || y > x2hi){ in = false; break;} @@ -1227,17 +1262,6 @@ namespace ATC { throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set nodeSet; // Loop over nodes and add their unique id's to the set if they're @@ -1758,9 +1782,7 @@ namespace ATC { } FE_3DMesh::~FE_3DMesh() { - //if (tree_) delete tree_; - - // not sure why, commenting out for now - JZ 2/28/13 + if (tree_) delete tree_; } // ------------------------------------------------------------- @@ -2498,6 +2520,7 @@ namespace ATC { const double zscale) : hx_(hx), hy_(hy), hz_(hz) { + tree_ = NULL; hasPlanarFaces_ = true; xscale_ = xscale; yscale_ = yscale; diff --git a/lib/atc/FE_Mesh.h b/lib/atc/FE_Mesh.h index b832a5b704..c7709168d5 100644 --- a/lib/atc/FE_Mesh.h +++ b/lib/atc/FE_Mesh.h @@ -154,7 +154,7 @@ namespace ATC { AliasArray element_connectivity_unique(const int eltID) const; void face_connectivity(const PAIR & faceID, - Array & nodes) const + Array & nodes) const { int nNodesPerFace = num_nodes_per_face(); nodes.reset(nNodesPerFace); int eltID = faceID.first; @@ -271,9 +271,10 @@ namespace ATC { std::set &nodeSet) const; void elementset_to_nodeset(const std::string &name, - std::set &nodeSet) const; + std::set nodeSet) const; void elementset_to_nodeset(const std::set &elemSet, - std::set &nodeSet) const; + std::set nodeSet) const; + std::set elementset_to_nodeset(const std::string &name) const; /** convert faceset to nodeset in _unique_ node numbering */ void faceset_to_nodeset(const std::string &name, @@ -401,6 +402,12 @@ namespace ATC { protected: + void parse_plane(int & argIdx, int narg, char ** arg, + int & ndir, int * idir, int & isgn, double xlimits[][2]); + + void parse_units(int & argIdx, int narg, char ** arg, + double & xmin, double & xmax, double & ymin, double & ymax, double & zmin, double & zmax); + /** will this mesh use data decomposition? */ bool decomposition_; diff --git a/lib/atc/FieldEulerIntegrator.cpp b/lib/atc/FieldEulerIntegrator.cpp index 2375a33ff5..eeea0cd4eb 100644 --- a/lib/atc/FieldEulerIntegrator.cpp +++ b/lib/atc/FieldEulerIntegrator.cpp @@ -2,9 +2,12 @@ #include "ATC_Coupling.h" #include "FE_Engine.h" #include "PhysicsModel.h" -#include "GMRES.h" -#include "CG.h" +#include "PrescribedDataManager.h" +//#include "GMRES.h" +//#include "CG.h" #include "ImplicitSolveOperator.h" +#include "MatrixDef.h" +#include "LinearSolver.h" namespace ATC { @@ -80,23 +83,71 @@ FieldImplicitEulerIntegrator::FieldImplicitEulerIntegrator( void FieldImplicitEulerIntegrator::update(const double dt, double time, FIELDS & fields, FIELDS & rhs) { // solver handles bcs - FieldImplicitSolveOperator solver(atc_, feEngine_, + FieldImplicitSolveOperator solver(atc_, fields, fieldName_, rhsMask_, physicsModel_, time, dt, alpha_); - DiagonalMatrix preconditioner = solver.preconditioner(fields); - DENS_VEC myRhs = solver.rhs(); + DiagonalMatrix preconditioner = solver.preconditioner(); + DENS_VEC rT = solver.r(); DENS_VEC dT(nNodes_); dT = dT_; DENS_MAT H(maxRestarts_+1, maxRestarts_); double tol = tol_; // tol returns the residual int iterations = maxIterations_; // iterations returns number of iterations int restarts = maxRestarts_; int convergence = GMRES(solver, - dT, myRhs, preconditioner, H, restarts, iterations, tol); + dT, rT, preconditioner, H, restarts, iterations, tol); if (convergence != 0) { throw ATC_Error(field_to_string(fieldName_) + " evolution did not converge"); } - fields[fieldName_] += dT; - rhs[fieldName_] = myRhs; + solver.solution(dT,fields[fieldName_].set_quantity()); } +// ==================================================================== +// FieldImplicitDirectEulerIntegrator +// ==================================================================== +FieldImplicitDirectEulerIntegrator::FieldImplicitDirectEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Coupling * atc, + const Array2D< bool > & rhsMask, // copy + const double alpha +) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atc,rhsMask), + alpha_(alpha),solver_(NULL) +{ + rhsMask_(fieldName_,FLUX) = false; // handle laplacian term with stiffness + const BC_SET & bcs = (atc_->prescribed_data_manager()->bcs(fieldName_))[0]; + solver_ = new LinearSolver(_lhsMK_,bcs); + solver_->allow_reinitialization(); +} +FieldImplicitDirectEulerIntegrator::~FieldImplicitDirectEulerIntegrator() +{ + if (solver_) delete solver_; +} + +// -------------------------------------------------------------------- +// initialize +// -------------------------------------------------------------------- +void FieldImplicitDirectEulerIntegrator::initialize(const double dt, double time, + FIELDS & fields) +{ + std::pair p(fieldName_,fieldName_); + Array2D rmask = atc_->rhs_mask(); + rmask(fieldName_,FLUX) = true; + atc_->tangent_matrix(p,rmask,physicsModel_,_K_); + _lhsMK_ = (1./dt)*(_M_)- alpha_*(_K_); + _rhsMK_ = (1./dt)*(_M_)+(1.+alpha_)*(_K_); +} +// -------------------------------------------------------------------- +// update +// -------------------------------------------------------------------- +void FieldImplicitDirectEulerIntegrator::update(const double dt, double time, + FIELDS & fields, FIELDS & rhs) +{ + atc_->compute_rhs_vector(rhsMask_, fields, rhs, + FULL_DOMAIN, physicsModel_); + CLON_VEC myRhs = column( rhs[fieldName_].set_quantity(),0); + CLON_VEC myField = column(fields[fieldName_].set_quantity(),0); + myRhs += _rhsMK_*myField; // f = (1/dt M + (1+alpha) K) T + f + solver_->solve(myField,myRhs); // (1/dt M -alpha K)^-1 f +} } // namespace ATC diff --git a/lib/atc/FieldEulerIntegrator.h b/lib/atc/FieldEulerIntegrator.h index 174f37435a..f349ada5d4 100644 --- a/lib/atc/FieldEulerIntegrator.h +++ b/lib/atc/FieldEulerIntegrator.h @@ -40,6 +40,10 @@ class FieldEulerIntegrator { /** Destructor */ virtual ~FieldEulerIntegrator() {}; + /** initialize */ + virtual void initialize(const double dt, const double time, + FIELDS & fields) {}; + /** update */ virtual void update(const double dt, const double time, FIELDS & fields, FIELDS & rhs) = 0; @@ -133,6 +137,47 @@ class FieldImplicitEulerIntegrator : public FieldEulerIntegrator { double tol_; }; +/** + * @class FieldImplicitDirectEulerIntegrator + * @brief implicit Euler method with direct solve + */ +class FieldImplicitDirectEulerIntegrator : public FieldEulerIntegrator { + + public: + + /** Constructor */ + FieldImplicitDirectEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Coupling * atc, + const Array2D< bool > & rhsMask, // copy + const double alpha = 0.5 // default to trap/midpt + ); + + /** Destructor */ + virtual ~FieldImplicitDirectEulerIntegrator(); + + /** initalize - init the matrices and inverses */ + void initialize(const double dt, const double time, + FIELDS & fields); + + /** update */ + void update(const double dt, const double time, + FIELDS & fields, FIELDS & rhs); + + protected: + /** euler update factor */ + double alpha_; + + /** matrices */ + SPAR_MAT _M_; + SPAR_MAT _K_; + SPAR_MAT _lhsMK_; + SPAR_MAT _rhsMK_; + class LinearSolver * solver_; +}; + } // namespace ATC #endif diff --git a/lib/atc/FieldManager.cpp b/lib/atc/FieldManager.cpp index 9a74fc22cd..ce9f859533 100644 --- a/lib/atc/FieldManager.cpp +++ b/lib/atc/FieldManager.cpp @@ -530,6 +530,7 @@ typedef PerAtomQuantity PAQ; PAQ * quantity = interscaleManager_.per_atom_quantity(field_to_prolongation_name(field)); if (!quantity) { + DENS_MAN * coarseQuantity = interscaleManager_.dense_matrix(field_to_string(field)); if (!coarseQuantity) coarseQuantity = nodal_atomic_field(field); if (!coarseQuantity) throw ATC_Error("can not prolong quantity: " + field_to_string(field) + " no field registered"); diff --git a/lib/atc/Function.cpp b/lib/atc/Function.cpp index 36d3e9a090..0bd759695c 100644 --- a/lib/atc/Function.cpp +++ b/lib/atc/Function.cpp @@ -294,6 +294,12 @@ XT_Function_Mgr * XT_Function_Mgr::myInstance_ = NULL; fi(i++) = args[idx++]; } tag_ = "piecewise_linear"; + stringstream ss; + ss << "created piecewise function : " << n << " \n"; + for (i = 0; i < n; i++) { + ss << xi(i) << " " << fi(i) << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } double PiecewiseLinearFunction::f(double * x, double t) { diff --git a/lib/atc/GhostManager.cpp b/lib/atc/GhostManager.cpp index 1f7fc8c838..3c9e54b4f5 100644 --- a/lib/atc/GhostManager.cpp +++ b/lib/atc/GhostManager.cpp @@ -4,6 +4,13 @@ #include "LammpsInterface.h" #include "ATC_Error.h" +using std::vector; +using std::set; +using std::pair; +using std::map; +using std::stringstream; +using ATC_Utility::to_string; + namespace ATC { //-------------------------------------------------------- @@ -19,10 +26,7 @@ namespace ATC { ghostModifier_(NULL), atc_(atc), boundaryDynamics_(NO_BOUNDARY_DYNAMICS), - needReset_(true), - kappa_(0.), - gamma_(0.), - mu_(0.) + needReset_(true) { // do nothing } @@ -73,18 +77,30 @@ namespace ATC { } else if (strcmp(arg[argIndex],"damped_harmonic")==0) { argIndex++; - kappa_ = atof(arg[argIndex++]); - gamma_ = atof(arg[argIndex++]); - mu_ = atof(arg[argIndex]); + kappa_.push_back(atof(arg[argIndex++])); + gamma_.push_back(atof(arg[argIndex++])); + mu_.push_back(atof(arg[argIndex])); boundaryDynamics_ = DAMPED_HARMONIC; needReset_ = true; return true; } + else if (strcmp(arg[argIndex],"damped_layers")==0) { + argIndex++; + while (argIndex < narg) { + kappa_.push_back(atof(arg[argIndex++])); + gamma_.push_back(atof(arg[argIndex++])); + mu_.push_back(atof(arg[argIndex++])); + } + + boundaryDynamics_ = DAMPED_LAYERS; + needReset_ = true; + return true; + } else if (strcmp(arg[argIndex],"coupled")==0) { boundaryDynamics_ = COUPLED; - kappa_ = 0.; - gamma_ = 0.; - mu_ = 0.; + kappa_.push_back(0.); + gamma_.push_back(0.); + mu_.push_back(0.); needReset_ = true; return true; } @@ -124,6 +140,11 @@ namespace ATC { ghostModifier_->set_integrate_atoms(true); break; } + case DAMPED_LAYERS: { + ghostModifier_ = new GhostModifierDampedHarmonicLayers(this,kappa_,gamma_,mu_); + ghostModifier_->set_integrate_atoms(true); + break; + } case SWAP: { // if regions based on element sets, use verlet on ghosts and swap ghosts and internal when they move between regions const std::string & internalElementSet(atc_->internal_element_set()); @@ -357,8 +378,9 @@ namespace ATC { // Constructor //-------------------------------------------------------- GhostModifierDampedHarmonic::GhostModifierDampedHarmonic(GhostManager * ghostManager, - double kappa, - double gamma, double mu) : + const vector & kappa, + const vector & gamma, + const vector & mu) : GhostModifierPrescribed(ghostManager), atomVelocities_(NULL), atomFeVelocity_(NULL), @@ -396,13 +418,18 @@ namespace ATC { k0_ = LammpsInterface::instance()->bond_stiffness(i,j,rsq); } +#if true //-------------------------------------------------------- // initial_integrate_velocity // velocity update in first part of velocity-verlet //-------------------------------------------------------- void GhostModifierDampedHarmonic::init_integrate_velocity(double dt) { - atomTimeIntegrator_->init_integrate_velocity(mu_*dt); +#if true + atomTimeIntegrator_->init_integrate_velocity(mu_[0]*dt); +#else + atomTimeIntegrator_->init_integrate_velocity(dt); +#endif } //-------------------------------------------------------- @@ -413,7 +440,7 @@ namespace ATC { { atomTimeIntegrator_->init_integrate_position(dt); } - +#endif //-------------------------------------------------------- // final_integrate // velocity update in second part of velocity-verlet @@ -430,11 +457,265 @@ namespace ATC { _forces_ = atomFeDisplacement; _forces_ += atomRefPositions; _forces_ -= atomPositions; - _forces_ *= kappa_; - _forces_ += gamma_*(atomFeVelocity - atomVelocities); + _forces_ *= kappa_[0]; + _forces_ += gamma_[0]*(atomFeVelocity - atomVelocities); +#if true +#else + _forces_ *= 1./mu_[0]; +#endif *atomForces_ = _forces_; - atomTimeIntegrator_->final_integrate(mu_*dt); +#if true + atomTimeIntegrator_->final_integrate(mu_[0]*dt); +#else + atomTimeIntegrator_->final_integrate(dt); +#endif + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class GhostModifierDampedHarmonicLayers + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + GhostModifierDampedHarmonicLayers::GhostModifierDampedHarmonicLayers(GhostManager * ghostManager, + const vector & kappa, + const vector & gamma, + const vector & mu) : + GhostModifierDampedHarmonic(ghostManager,kappa,gamma,mu), + ghostToBoundaryDistance_(NULL), + layerId_(NULL) + { + + // do nothing + } + + //-------------------------------------------------------- + // construct_transfers + // sets/constructs all required dependency managed data + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::construct_transfers() + { + GhostModifierDampedHarmonic::construct_transfers(); + + InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); + + // transfer for distance to boundary + ghostToBoundaryDistance_ = new AtcAtomQuantity(ghostManager_->atc(), + 1,GHOST); + interscaleManager.add_per_atom_quantity(ghostToBoundaryDistance_, + "GhostToBoundaryDistance"); + + // transfer from ghost atom to its layer id + layerId_ = new AtcAtomQuantity(ghostManager_->atc(), + 1,GHOST); + interscaleManager.add_per_atom_int_quantity(layerId_,"GhostLayerId"); + } + + //-------------------------------------------------------- + // initialize + // initialize all data and variables before a run + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::initialize() + { + compute_distances(); + int nlayers = find_layers(); + if (nlayers > ((int)gamma_.size())) throw ATC_Error("GhostModifierDampedHarmonicLayers::initialize not enough damping factors specfied " + to_string(gamma_.size())); + } + + //-------------------------------------------------------- + // find atomic mononlayers + //-------------------------------------------------------- + bool compare( pair a, pair b) { + return (a.second < b.second); + } + int GhostModifierDampedHarmonicLayers::find_layers() + { + DENS_MAT & d(ghostToBoundaryDistance_->set_quantity()); + DenseMatrix & ids = layerId_->set_quantity(); + + // get distances for every ghost atom for sorting + // size arrays of length number of processors + int commSize = LammpsInterface::instance()->comm_size(); + int * procCounts = new int[commSize]; + int * procOffsets = new int[commSize]; + + // get size information from all processors + int localGhosts = d.nRows(); + LammpsInterface::instance()->int_allgather(localGhosts,procCounts); + procOffsets[0] = 0; + int totalGhosts = 0; + for (int i = 0; i < commSize-1; ++i) { + totalGhosts += procCounts[i]; + procOffsets[i+1] = totalGhosts; + } + totalGhosts += procCounts[commSize-1]; + double * globalDistances = new double[totalGhosts]; + + // allgather distances + LammpsInterface::instance()->allgatherv(d.ptr(), localGhosts, + globalDistances, procCounts, procOffsets); + + // add to distances vector with -1 ids + // convert to STL for sort + vector< pair > distances; + distances.resize(totalGhosts); + int myRank = LammpsInterface::instance()->comm_rank(); + int j = 0; + for (int i = 0; i < totalGhosts; ++i) { + if (i >= procOffsets[myRank] && i < procOffsets[myRank] + procCounts[myRank]) { + distances[i] = pair (j++,globalDistances[i]); + } + else { + distances[i] = pair (-1,globalDistances[i]); + } + } + delete [] globalDistances; + delete [] procCounts; + delete [] procOffsets; + + std::sort(distances.begin(),distances.end(),compare); + double min = (distances[0]).second; + double a = LammpsInterface::instance()->max_lattice_constant(); + double tol = a/4; + double xlayer = min; // nominal position of layer + int ilayer =0; + vector counts(1); + counts[0] = 0; + for (vector >::const_iterator itr = distances.begin(); + itr != distances.end(); itr++) { + int id = (*itr).first; + double d = (*itr).second; + if (fabs(d-xlayer) > tol) { + counts.push_back(0); + ilayer++; + xlayer = d; + } + if (id > -1) { + ids(id,0) = ilayer; + } + counts[ilayer]++; + } + int nlayers = ilayer; + stringstream msg; + msg << nlayers << " boundary layers:\n"; + for (int i = 0; i < nlayers; ++i) { + msg << i+1 << ": " << counts[i] << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(msg.str()); + return nlayers; + } + + //-------------------------------------------------------- + // compute distances to boundary faces + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::compute_distances() + { + // get fe mesh + const FE_Mesh * feMesh = ((ghostManager_->atc())->fe_engine())->fe_mesh(); + InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); + + // get elements in which ghosts reside + const DenseMatrix & elementHasGhost((interscaleManager.dense_matrix_int("ElementHasGhost"))->quantity()); + // get type of each node + const DenseMatrix & nodeType((interscaleManager.dense_matrix_int("NodalGeometryType"))->quantity()); + + // create map from those elements to pair (centroid, normal) + map > elementToFace; + int nsd = (ghostManager_->atc())->nsd(); + int nfe = feMesh->num_faces_per_element(); + DENS_MAT nodalCoords(nsd,nfe); + DENS_VEC centroid(nsd), normal(nsd); + Array faceNodes; + bool isBoundaryFace; + for (int elt = 0; elt < elementHasGhost.nRows(); ++elt) { + if (elementHasGhost(elt,0)) { + // loop over all faces + int face; + for (face = 0; face < nfe; ++face) { + // get the nodes in a face + feMesh->face_connectivity_unique(PAIR(elt,face),faceNodes); + + // identify the boundary face by the face which contains only boundary nodes + isBoundaryFace = true; + for (int i = 0; i < faceNodes.size(); ++i) { + if (nodeType(faceNodes(i),0) != BOUNDARY) { + isBoundaryFace = false; + break; + } + } + + if (isBoundaryFace) { + break; + } + } + if (face == nfe) { + throw ATC_Error("GhostModifierDampedHarmonicLayers::initialize - Could not find boundary face for element " + to_string(elt)); + } + + // for each boundary face get the centroid by the average position of the nodes comprising it + feMesh->face_coordinates(PAIR(elt,face),nodalCoords); + centroid = 0.; + for (int i = 0; i < nodalCoords.nRows(); ++i) { + for (int j = 0; j < nodalCoords.nCols(); ++j) { + centroid(i) += nodalCoords(i,j); + } + } + centroid *= -1./double(nfe); // -1 gets outward normal from ATC region => all distances should be > 0 + + // for each boundary face get the normal + // ASSUMES all faces are planar + feMesh->face_normal(PAIR(elt,face),0,normal); + + elementToFace[elt] = pair(centroid,normal); + } + } + + // for each atom compute (atom_pos - element->face_centroid) dot element_->face_normal + // get atom to element map for ghosts + PerAtomQuantity * ghostToElementMap = interscaleManager.per_atom_int_quantity("AtomGhostElement"); + const DenseMatrix & ghostToElement(ghostToElementMap->quantity()); + DENS_MAT & distance(ghostToBoundaryDistance_->set_quantity()); + const DENS_MAT & atomPositions(atomPositions_->quantity()); + DENS_VEC diff(nsd); + for (int i = 0; i < distance.nRows(); ++i) { + int elt = ghostToElement(i,0); + const DENS_VEC & c(elementToFace[elt].first); + const DENS_VEC & n(elementToFace[elt].second); + for (int j = 0; j < nsd; ++j) { + diff(j) = atomPositions(i,j) - c(j); + } + distance(i,0) = diff.dot(n); // should always be positive + } + } + + + //-------------------------------------------------------- + // final_integrate + // velocity update in second part of velocity-verlet + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::final_integrate(double dt) + { + + const DENS_MAT & atomPositions(atomPositions_->quantity()); + const DENS_MAT & atomVelocities(atomVelocities_->quantity()); + const DENS_MAT & atomFeDisplacement(atomFeDisplacement_->quantity()); + const DENS_MAT & atomFeVelocity(atomFeVelocity_->quantity()); + const DENS_MAT & atomRefPositions(atomRefPositions_->quantity()); + + _forces_ = atomFeDisplacement; + _forces_ += atomRefPositions; + _forces_ -= atomPositions; + + _forces_ *= kappa_[0]; + _forces_ += gamma_[0]*(atomFeVelocity - atomVelocities); + _forces_ *= 1./mu_[0]; + *atomForces_ = _forces_; + + atomTimeIntegrator_->final_integrate(dt); } //-------------------------------------------------------- diff --git a/lib/atc/GhostManager.h b/lib/atc/GhostManager.h index c146644db2..9c3bbbe939 100644 --- a/lib/atc/GhostManager.h +++ b/lib/atc/GhostManager.h @@ -26,12 +26,13 @@ namespace ATC { /** types of ghost boundary conditions in momentum */ enum BoundaryDynamicsType { NO_BOUNDARY_DYNAMICS=0, - VERLET, - PRESCRIBED, - DAMPED_HARMONIC, - COUPLED, - SWAP, - SWAP_VERLET + VERLET, // performs velocity-verlet + PRESCRIBED, // forces ghost locations to conform to interpolated finite element locations + DAMPED_HARMONIC, // turns ghost atoms into spring-mass-dashpot systems + DAMPED_LAYERS, // oer layer DAMPED_HARMONIC + COUPLED, // applies a spring-dashpot force to the ghosts + SWAP, // exchanges ghost and real atoms when they cross AtC boundaries + SWAP_VERLET // like above, but integrates the ghosts using velocity verlet }; // constructor @@ -91,13 +92,13 @@ namespace ATC { bool needReset_; /** spring constant for some models */ - double kappa_; + std::vector kappa_; /** damping constant for some models */ - double gamma_; + std::vector gamma_; /** ratio between mass of ghost types and desired mass for some models */ - double mu_; + std::vector mu_; private: @@ -214,20 +215,22 @@ namespace ATC { // constructor GhostModifierDampedHarmonic(GhostManager * ghostManager, - double kappa_, double gamma, double mu); + const std::vector & kappa, + const std::vector & gamma, + const std::vector & mu); // destructor virtual ~GhostModifierDampedHarmonic(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(); - +#if true /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt); /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt); - +#endif /** set positions after integration */ virtual void post_init_integrate(){}; @@ -245,14 +248,17 @@ namespace ATC { /** atom forces */ PerAtomQuantity * atomForces_; + /** effective spring constant for potential */ + double k0_; + /** spring constant */ - double kappa_, k0_; + const std::vector & kappa_; /** damping constant */ - double gamma_; + const std::vector & gamma_; /** ratio between mass of ghost types and desired mass */ - double mu_; + const std::vector & mu_; // workspace DENS_MAT _forces_; @@ -264,6 +270,56 @@ namespace ATC { }; + /** + * @class GhostModifierDampedHarmonicLayers + * @brief Integrates ghost atoms using velocity-verlet with a damped harmonic force based on which layer the atom resides in + */ + + class GhostModifierDampedHarmonicLayers : public GhostModifierDampedHarmonic { + + public: + + // constructor + GhostModifierDampedHarmonicLayers(GhostManager * ghostManager, + const std::vector & kappa, + const std::vector & gamma, + const std::vector & mu); + + // destructor + virtual ~GhostModifierDampedHarmonicLayers(){}; + + /** create and get necessary transfer operators */ + virtual void construct_transfers(); + + /** pre time integration initialization of data */ + virtual void initialize(); + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(double dt); + + protected: + + // methods + /** compute distance of ghost atom to boundary */ + void compute_distances(); + /** sorting heuristics to identify layers */ + int find_layers(); + + // data + /** distance from all ghost atoms to boundary, i.e. boundary face of containing element */ + PerAtomQuantity * ghostToBoundaryDistance_; + + /** layer id for ghost atoms */ + PerAtomQuantity * layerId_; + + private: + + // DO NOT define this + GhostModifierDampedHarmonicLayers(); + + }; + + /** * @class GhostIntegratorSwap * @brief Integrates ghost atoms using velocity-verlet, and swaps atoms between ghost diff --git a/lib/atc/ImplicitSolveOperator.cpp b/lib/atc/ImplicitSolveOperator.cpp index 93ee362e5c..d66da8a0e3 100644 --- a/lib/atc/ImplicitSolveOperator.cpp +++ b/lib/atc/ImplicitSolveOperator.cpp @@ -7,6 +7,10 @@ #include "PhysicsModel.h" #include "PrescribedDataManager.h" + +using std::set; +using std::vector; + namespace ATC { // -------------------------------------------------------------------- @@ -15,16 +19,77 @@ namespace ATC { // -------------------------------------------------------------------- // -------------------------------------------------------------------- ImplicitSolveOperator:: -ImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, - const PhysicsModel * physicsModel) - : atc_(atc), - feEngine_(feEngine), - physicsModel_(physicsModel) +ImplicitSolveOperator(double alpha, double dt) + : n_(0), + dof_(0), + dt_(dt), + alpha_(alpha), + epsilon0_(1.0e-8) { // Nothing else to do here } +// -------------------------------------------------------------------- +// operator * +// -------------------------------------------------------------------- +DENS_VEC +ImplicitSolveOperator::operator * (const DENS_VEC & x) const +{ + // This method uses a matrix-free approach to approximate the + // multiplication by matrix A in the matrix equation Ax=b, where the + // matrix equation results from an implicit treatment of the + // fast field. In brief, if the ODE for the fast field can be written: + // + // dx/dt = R(x) + // + // A generalized discretization can be written: + // + // 1/dt * (x^n+1 - x^n) = alpha * R(x^n+1) + (1-alpha) * R(x^n) + // + // Taylor expanding the R(x^n+1) term and rearranging gives the + // equation to be solved for dx at each timestep: + // + // [1 - dt * alpha * dR/dx] * dx = dt * R(x^n) + // + // The operator defined in this method computes the left-hand side, + // given a vector dx. It uses a finite difference, matrix-free + // approximation of dR/dx * dx, giving: + // + // [1 - dt * alpha * dR/dx] * dx = dt * R(x^n) + // ~= dx - dt*alpha/epsilon * ( R(x^n + epsilon*dx) - R(x^n) ) + // + // Compute epsilon + double epsilon = (x.norm()>0.0) ? epsilon0_*x0_.norm()/x.norm():epsilon0_; + // Compute incremented vector x^n+1 = x^n + epsilon*dx + x_ = x0_ + epsilon * x; + // Evaluate R(x) + this->R(x_,R_); + // Compute full left hand side and return it + DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (R_ - R0_); + return Ax; +} + +// -------------------------------------------------------------------- +// rhs of Ax = r +// -------------------------------------------------------------------- +DENS_VEC +ImplicitSolveOperator::r() const +{ + return dt_ * R0_; // dt * R(T^n) +} + +// -------------------------------------------------------------------- +// preconditioner +// -------------------------------------------------------------------- +DIAG_MAT +ImplicitSolveOperator::preconditioner() const +{ + DENS_VEC diag(n_); + diag = 1.0; + DIAG_MAT preconditioner(diag); + return preconditioner; +} + // -------------------------------------------------------------------- // -------------------------------------------------------------------- // FieldImplicitSolveOperator @@ -32,7 +97,6 @@ ImplicitSolveOperator(ATC_Coupling * atc, // -------------------------------------------------------------------- FieldImplicitSolveOperator:: FieldImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, FIELDS & fields, const FieldName fieldName, const Array2D< bool > & rhsMask, @@ -40,121 +104,97 @@ FieldImplicitSolveOperator(ATC_Coupling * atc, double simTime, double dt, double alpha) - : ImplicitSolveOperator(atc, feEngine, physicsModel), + : ImplicitSolveOperator(alpha, dt), fieldName_(fieldName), - fields_(fields), // ref to fields - time_(simTime), - dt_(dt), - alpha_(alpha), - epsilon0_(1.0e-8) + atc_(atc), + physicsModel_(physicsModel), + fields0_(fields), // ref to fields + fields_ (fields), // copy of fields + rhsMask_(rhsMask), + time_(simTime) { - // find field associated with ODE + const DENS_MAT & f = fields0_[fieldName_].quantity(); + dof_ = f.nCols(); + if (dof_ > 1) throw ATC_Error("Implicit solver operator can only handle scalar fields"); + // create all to free map + int nNodes = f.nRows(); + set fixedNodes_ = atc_->prescribed_data_manager()->fixed_nodes(fieldName_); + n_ = nNodes; + vector tag(nNodes); + set::const_iterator it; int i = 0; + for (i = 0; i < nNodes; ++i) { tag[i] = true; } + for (it=fixedNodes_.begin();it!=fixedNodes_.end();++it) {tag[*it]=false;} + int m = 0; + for (i = 0; i < nNodes; ++i) { if (tag[i]) freeNodes_[i]= m++; } +//std::cout << " nodes " << n_ << " " << nNodes << "\n"; + + // Save current field + x0_.reset(n_); + to_free(f,x0_); + x_ = x0_; // initialize + + // righthand side/forcing vector rhsMask_.reset(NUM_FIELDS,NUM_FLUX); rhsMask_ = false; for (int i = 0; i < rhsMask.nCols(); i++) { rhsMask_(fieldName_,i) = rhsMask(fieldName_,i); } +//std::cout << print_mask(rhsMask_) << "\n"; massMask_.reset(1); massMask_(0) = fieldName_; - - // Save off current field - TnVect_ = column(fields_[fieldName_].quantity(),0); - - // Allocate vectors for fields and rhs - int nNodes = atc_->num_nodes(); - // copy fields - fieldsNp1_ = fields_; - // size rhs - int dof = fields_[fieldName_].nCols(); - RnMap_ [fieldName_].reset(nNodes,dof); - RnpMap_[fieldName_].reset(nNodes,dof); - + rhs_[fieldName_].reset(nNodes,dof_); // Compute the RHS vector R(T^n) - // Set BCs on Rn, multiply by inverse mass and then extract its vector - atc_->compute_rhs_vector(rhsMask_, fields_, RnMap_, - FULL_DOMAIN, physicsModel_); - DENS_MAT & Rn = RnMap_[fieldName_].set_quantity(); - atc_->prescribed_data_manager() - ->set_fixed_dfield(time_, fieldName_, Rn); - atc_->apply_inverse_mass_matrix(Rn,fieldName_); - RnVect_ = column(Rn,0); + R0_.reset(n_); + R_ .reset(n_); + R(x0_, R0_); } - -// -------------------------------------------------------------------- -// operator *(Vector) -// -------------------------------------------------------------------- -DENS_VEC -FieldImplicitSolveOperator::operator * (DENS_VEC x) const +void FieldImplicitSolveOperator::to_all(const VECTOR &x, MATRIX &f) const +{ + f.reset(x.nRows(),1); + for (int i = 0; i < x.nRows(); ++i) { + f(i,0) = x(i); + } +} +void FieldImplicitSolveOperator::to_free(const MATRIX &r, VECTOR &v) const +{ + v.reset(r.nRows()); + for (int i = 0; i < r.nRows(); ++i) { + v(i) = r(i,0); + } +} +void +FieldImplicitSolveOperator::R(const DENS_VEC &x, DENS_VEC &v ) const +{ + DENS_MAT & f = fields_[fieldName_].set_quantity(); + atc_->prescribed_data_manager()->set_fixed_field(time_, fieldName_, f); + to_all(x,f); + atc_->compute_rhs_vector(rhsMask_,fields_,rhs_,FULL_DOMAIN,physicsModel_); + DENS_MAT & r = rhs_[fieldName_].set_quantity(); + atc_->prescribed_data_manager()->set_fixed_dfield(time_, fieldName_, r); + atc_->apply_inverse_mass_matrix(r,fieldName_); + to_free(r,v); +#if 0 +int n = 6; +//std::cout << "# x "; for (int i = 0; i < n_; ++i) std::cout << x(i) << " "; std::cout << "\n"; +//std::cout << "# f "; for (int i = 0; i < n; ++i) std::cout << f(i,0) << " "; std::cout << "\n"; +std::cout << "# r "; for (int i = 0; i < n; ++i) std::cout << r(i,0) << " "; std::cout << "\n"; +//std::cout << "# v "; for (int i = 0; i < n; ++i) std::cout << v(i) << " "; std::cout << "\n"; +#endif +} + +void FieldImplicitSolveOperator::solution(const DENS_MAT & dx, DENS_MAT &f) const +{ + DENS_MAT & df = fields_[fieldName_].set_quantity(); + to_all(column(dx,0),df); + atc_->prescribed_data_manager()->set_fixed_dfield(time_, fieldName_, df); + f += df; +} +void FieldImplicitSolveOperator::rhs(const DENS_MAT & r, DENS_MAT &rhs) const { - // This method uses a matrix-free approach to approximate the - // multiplication by matrix A in the matrix equation Ax=b, where the - // matrix equation results from an implicit treatment of the - // fast field solve for the Two Temperature Model. In - // brief, if the ODE for the fast field can be written: - // - // dT/dt = R(T) - // - // A generalized discretization can be written: - // - // 1/dt * (T^n+1 - T^n) = alpha * R(T^n+1) + (1-alpha) * R(T^n) - // - // Taylor expanding the R(T^n+1) term and rearranging gives the - // equation to be solved for dT at each timestep: - // - // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) - // - // The operator defined in this method computes the left-hand side, - // given a vector dT. It uses a finite difference, matrix-free - // approximation of dR/dT * dT, giving: - // - // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) - // ~= dT - dt*alpha/epsilon * ( R(T^n + epsilon*dT) - R(T^n) ) - // - - // Compute epsilon - double epsilon = (x.norm() > 0.0) ? epsilon0_ * TnVect_.norm()/x.norm() : epsilon0_; - - // Compute incremented vector = T + epsilon*dT - fieldsNp1_[fieldName_] = TnVect_ + epsilon * x; - - // Evaluate R(b) - atc_->compute_rhs_vector(rhsMask_, fieldsNp1_, RnpMap_, - FULL_DOMAIN, physicsModel_); - DENS_MAT & Rnp = RnpMap_[fieldName_].set_quantity(); - atc_->prescribed_data_manager() - ->set_fixed_dfield(time_, fieldName_, Rnp); - atc_->apply_inverse_mass_matrix(Rnp,fieldName_); - RnpVect_ = column(Rnp,0); - - // Compute full left hand side and return it - DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (RnpVect_ - RnVect_); - return Ax; + to_all(column(r,0),rhs); } -// -------------------------------------------------------------------- -// rhs -// -------------------------------------------------------------------- -DENS_VEC -FieldImplicitSolveOperator::rhs() -{ - // Return dt * R(T^n) - return dt_ * RnVect_; -} - -// -------------------------------------------------------------------- -// preconditioner -// -------------------------------------------------------------------- -DIAG_MAT -FieldImplicitSolveOperator::preconditioner(FIELDS & fields) -{ - // Just create and return identity matrix - int nNodes = atc_->num_nodes(); - DENS_VEC ones(nNodes); - ones = 1.0; - DIAG_MAT identity(ones); - return identity; -} } // namespace ATC diff --git a/lib/atc/ImplicitSolveOperator.h b/lib/atc/ImplicitSolveOperator.h index 670f20d2ce..8b9f28dc5b 100644 --- a/lib/atc/ImplicitSolveOperator.h +++ b/lib/atc/ImplicitSolveOperator.h @@ -21,37 +21,31 @@ class FE_Engine; * @brief Helper class to compute matrix-free product for use with IML++ solvers */ class ImplicitSolveOperator { - public: - /** Constructor */ - ImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, - const PhysicsModel * physicsModel); - + ImplicitSolveOperator(double alpha, double dt); /** Destructor */ virtual ~ImplicitSolveOperator() {}; - /** pure virtual operator to compute Ax, for equation Ax=b */ - virtual DENS_VEC operator * (DENS_VEC x) const = 0; - + virtual DENS_VEC operator * (const DENS_VEC &x) const; /** pure virtual method to return the rhs vector b */ - virtual DENS_VEC rhs() = 0; - + virtual void R(const DENS_VEC &f, DENS_VEC &v) const = 0; + /** pure virtual method to return the rhs vector b */ + virtual DENS_VEC r() const; /** pure virtual method to return preconditioner */ - virtual DiagonalMatrix preconditioner(FIELDS & fields) = 0; - + virtual DiagonalMatrix preconditioner() const; + /** finalize */ + virtual void solution(const DENS_MAT & dx, DENS_MAT &x) const = 0; + virtual void rhs(const DENS_MAT & r, DENS_MAT &rhs) const = 0; protected: - - /** Pointer to atc */ - ATC_Coupling * atc_; - - /** Pointer to FE_Engine */ - /*const*/ FE_Engine * feEngine_; - - /** Pointer to PhysicsModel */ - const PhysicsModel * physicsModel_; - + int n_,dof_; + DENS_VEC x0_; // condensed previous + mutable DENS_VEC x_; // condensed current + DENS_VEC R0_; // condensed previous + mutable DENS_VEC R_; // condensed current + double dt_; // timestep + double alpha_; // implicit/explicit parameter (0 -> explicit, else implicit) + double epsilon0_; // small parameter to compute increment }; /** @@ -59,69 +53,37 @@ class ImplicitSolveOperator { * @brief Class to perform A*x operation for electron temperature solution */ class FieldImplicitSolveOperator : public ImplicitSolveOperator { - public: - /** Constructor */ FieldImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * fe_Engine, FIELDS & fields, - const FieldName electronField, + const FieldName f, const Array2D< bool > & rhsMask, const PhysicsModel * physicsModel, - double simTime, - double dt, - double alpha); - + double simTime, double dt, double alpha = 0.5); /** Destructor */ virtual ~FieldImplicitSolveOperator() {}; - - /** operator to compute A*x for the electron temperature equation */ - virtual DENS_VEC operator * (DENS_VEC x) const; - - /** method to return the rhs vector b */ - virtual DENS_VEC rhs(); - - /** method to return preconditioner (identity matrix) */ - virtual DIAG_MAT preconditioner(FIELDS & fields); - + virtual void R(const DENS_VEC &f, DENS_VEC &v) const; + virtual void solution(const DENS_MAT & dx, DENS_MAT &x) const; + virtual void rhs(const DENS_MAT & r, DENS_MAT &rhs) const; protected: - // field name of ODE to solve - FieldName fieldName_; - - // Reference to current fields (passed in constructor) - FIELDS & fields_; - + void to_all(const VECTOR& free, MATRIX& all) const; + void to_free(const MATRIX& all, VECTOR& free) const; + FieldName fieldName_; // field name of ODE to solve + ATC_Coupling * atc_; /** Pointer to atc */ + const PhysicsModel * physicsModel_; /** Pointer to PhysicsModel */ + FIELDS & fields0_; // Reference to current fields (passed in constructor) // Local fields - mutable FIELDS fieldsNp1_; - - // Vector to hold current temperature - DENS_VEC TnVect_; - - // Old and new RHS maps (not including inverse mass) - FIELDS RnMap_; - mutable FIELDS RnpMap_; - - // Matrices/vectors to hold electron temperature components of RHS - // vectors (including inverse mass) - DENS_VEC RnVect_; - mutable DENS_VEC RnpVect_; - + mutable FIELDS fields_; // full + // set of fixed nodes + std::map freeNodes_; + // masks Array2D rhsMask_; Array massMask_; - + // right hand side of dot x = R(x) + mutable FIELDS rhs_; // full // simulation time double time_; - - // timestep - double dt_; - - // implicit/explicit parameter (0 -> explicit, else implicit) - double alpha_; - - // small parameter to compute increment - double epsilon0_; - }; } // namespace ATC diff --git a/lib/atc/KD_Tree.cpp b/lib/atc/KD_Tree.cpp index a28e54b7a4..362bd70d34 100644 --- a/lib/atc/KD_Tree.cpp +++ b/lib/atc/KD_Tree.cpp @@ -25,6 +25,7 @@ KD_Tree *KD_Tree::create_KD_tree(const int nNodesPerElem, const int nNodes, KD_Tree::~KD_Tree() { delete sortedPts_; + delete candElems_; delete leftChild_; delete rightChild_; } @@ -85,12 +86,16 @@ KD_Tree::KD_Tree(vector *points, vector *elements, // Create child tree, or NULL if there's nothing to create if (candElems_->size() - leftElems->size() < 4 || leftElems->size() == 0) { leftChild_ = NULL; + delete leftPts; + delete leftElems; } else { leftChild_ = new KD_Tree(leftPts, leftElems, (dimension+1) % 3); } // Create child tree, or NULL if there's nothing to create if (candElems_->size() - rightElems->size() < 4 || rightElems->size() == 0) { rightChild_ = NULL; + delete rightPts; + delete rightElems; } else { rightChild_ = new KD_Tree(rightPts, rightElems, (dimension+1) % 3); } diff --git a/lib/atc/KernelFunction.cpp b/lib/atc/KernelFunction.cpp index 214d81f5dc..91f99a6149 100644 --- a/lib/atc/KernelFunction.cpp +++ b/lib/atc/KernelFunction.cpp @@ -131,7 +131,7 @@ namespace ATC { invRc_ = 1.0/Rc_; Rc_ = parameters[0]; invRc_ = 1.0/Rc_; - invVol_ = 1.0/(4.0/3.0*Pi*pow(Rc_,3)); + invVol_ = 1.0/(4.0/3.0*Pi_*pow(Rc_,3)); ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg); @@ -561,7 +561,7 @@ namespace ATC { { nsd_ = 2; double Lz = box_length[2]; - invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { @@ -594,7 +594,7 @@ namespace ATC { { nsd_ = 2; double Lz = box_length[2]; - invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { diff --git a/lib/atc/KinetoThermostat.cpp b/lib/atc/KinetoThermostat.cpp index cf3b9c29c7..bdc11238ad 100644 --- a/lib/atc/KinetoThermostat.cpp +++ b/lib/atc/KinetoThermostat.cpp @@ -22,8 +22,7 @@ namespace ATC { KinetoThermostat::KinetoThermostat(ATC_Coupling * atc, const string & regulatorPrefix) : AtomicRegulator(atc,regulatorPrefix), - kinetostat_(atc,"Momentum"), - thermostat_(atc,"Energy") + couplingMaxIterations_(myCouplingMaxIterations) { // nothing to do } @@ -47,12 +46,13 @@ namespace ATC { void KinetoThermostat::reset_lambda_contribution(const DENS_MAT & target, const FieldName field) { - //TEMP_JAT if (field==VELOCITY) { - kinetostat_.reset_lambda_contribution(target); + DENS_MAN * lambdaForceFiltered = regulator_data("LambdaForceFiltered",atc_->nsd()); + *lambdaForceFiltered = target; } else if (field == TEMPERATURE) { - thermostat_.reset_lambda_contribution(target); + DENS_MAN * lambdaPowerFiltered = regulator_data("LambdaPowerFiltered",1); + *lambdaPowerFiltered = target; } else { throw ATC_Error("KinetoThermostat::reset_lambda_contribution - invalid field given"); @@ -73,364 +73,631 @@ namespace ATC { //-------------------------------------------------------- void KinetoThermostat::construct_methods() { -// #ifdef WIP_JAT -// // get data associated with stages 1 & 2 of ATC_Method::initialize -// AtomicRegulator::construct_methods(); + // get data associated with stages 1 & 2 of ATC_Method::initialize + AtomicRegulator::construct_methods(); -// if (atc_->reset_methods()) { -// // eliminate existing methods -// delete_method(); + if (atc_->reset_methods()) { + // eliminate existing methods + delete_method(); -// // update time filter -// TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); -// TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); -// if (timeFilterManager->need_reset() ) { -// if (myIntegrationType == TimeIntegrator::GEAR) -// timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); -// else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) -// timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); -// } + // error check time integration methods + TimeIntegrator::TimeIntegrationType myEnergyIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); + TimeIntegrator::TimeIntegrationType myMomentumIntegrationType = (atc_->time_integrator(VELOCITY))->time_integration_type(); + if (myEnergyIntegrationType != TimeIntegrator::FRACTIONAL_STEP || myMomentumIntegrationType != TimeIntegrator::FRACTIONAL_STEP) { + throw ATC_Error("KinetoThermostat::construct_methods - this scheme only valid with fractional step integration"); + } + + // update time filter + TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); + if (timeFilterManager->need_reset() ) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } -// if (timeFilterManager->filter_dynamics()) { -// switch (regulatorTarget_) { -// case NONE: { -// regulatorMethod_ = new RegulatorMethod(this); -// break; -// } -// case FIELD: { // error check, rescale and filtering not supported together -// throw ATC_Error("Cannot use rescaling thermostat with time filtering"); -// break; -// } -// case DYNAMICS: { -// switch (couplingMode_) { -// case FIXED: { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); -// } -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (md_flux_nodes(TEMPERATURE)) { -// if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fluxes but no fixed or coupled nodes -// regulatorMethod_ = new ThermostatFluxFiltered(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixedFiltered(this); -// } -// } -// else { -// // there are only fixed nodes -// regulatorMethod_ = new ThermostatFixedFiltered(this); -// } -// } -// else { -// regulatorMethod_ = new ThermostatHooverVerletFiltered(this); -// } -// break; -// } -// case FLUX: { -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); -// } -// if (md_fixed_nodes(TEMPERATURE)) { -// if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fixed nodes but no fluxes -// regulatorMethod_ = new ThermostatFixedFiltered(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixedFiltered(this); -// } -// } -// else { -// // there are only flux nodes -// regulatorMethod_ = new ThermostatFluxFiltered(this); -// } -// } -// else { -// if (use_localized_lambda()) { -// if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && -// atc_->boundary_integration_type() != NO_QUADRATURE) { -// throw ATC_Error("Cannot use flux coupling with localized lambda"); -// } -// } -// regulatorMethod_ = new ThermostatPowerVerletFiltered(this); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown thermostat type in Thermostat::initialize"); -// } -// } -// else { -// switch (regulatorTarget_) { -// case NONE: { -// regulatorMethod_ = new RegulatorMethod(this); -// break; -// } -// case FIELD: { -// if (atc_->temperature_def()==KINETIC) -// regulatorMethod_ = new ThermostatRescale(this); -// else if (atc_->temperature_def()==TOTAL) -// regulatorMethod_ = new ThermostatRescaleMixedKePe(this); -// else -// throw ATC_Error("Unknown temperature definition"); -// break; -// } -// case DYNAMICS: { -// switch (couplingMode_) { -// case FIXED: { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); -// } -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (md_flux_nodes(TEMPERATURE)) { -// if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fluxes but no fixed or coupled nodes -// regulatorMethod_ = new ThermostatFlux(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixed(this); -// } -// } -// else { -// // there are only fixed nodes -// regulatorMethod_ = new ThermostatFixed(this); -// } -// } -// else { -// regulatorMethod_ = new ThermostatHooverVerlet(this); -// } -// break; -// } -// case FLUX: { -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); -// } -// if (md_fixed_nodes(TEMPERATURE)) { -// if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fixed nodes but no fluxes -// regulatorMethod_ = new ThermostatFixed(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixed(this); -// } -// } -// else { -// // there are only flux nodes -// regulatorMethod_ = new ThermostatFlux(this); -// } -// } -// else { -// if (use_localized_lambda()) { -// if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && -// atc_->boundary_integration_type() != NO_QUADRATURE) { -// throw ATC_Error("Cannot use flux coupling with localized lambda"); -// } -// } -// regulatorMethod_ = new ThermostatPowerVerlet(this); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown thermostat target in Thermostat::initialize"); -// } -// } + if (timeFilterManager->filter_dynamics()) { + switch (regulatorTarget_) { + case NONE: { + regulatorMethod_ = new RegulatorMethod(this); + break; + } + case FIELD: { // error check, rescale and filtering not supported together + throw ATC_Error("KinetoThermostat::construct_methods - Cannot use rescaling thermostat with time filtering"); + break; + } + case DYNAMICS: { + } + default: + throw ATC_Error("Unknown thermostat type in Thermostat::initialize"); + } + } + else { + switch (regulatorTarget_) { + case NONE: { + regulatorMethod_ = new RegulatorMethod(this); + break; + } + case FIELD: { + if (atc_->temperature_def()==KINETIC) { + regulatorMethod_ = new KinetoThermostatRescale(this,couplingMaxIterations_); + } + else if (atc_->temperature_def()==TOTAL) { + regulatorMethod_ = new KinetoThermostatRescaleMixedKePe(this,couplingMaxIterations_); + } + else + throw ATC_Error("Unknown temperature definition"); + break; + } + default: + throw ATC_Error("Unknown thermostat target in Thermostat::initialize"); + } + } -// AtomicRegulator::reset_method(); -// } -// else { -// set_all_data_to_used(); -// } -// #endif - //TEMP_JAT - kinetostat_.construct_methods(); - thermostat_.construct_methods(); - } - - //TEMP_JAT all functions which have explicit thermo and kinetostats need to be removed - //-------------------------------------------------------- - // reset_nlocal: - // resizes lambda force if necessary - //-------------------------------------------------------- - void KinetoThermostat::reset_nlocal() - { - kinetostat_.reset_nlocal(); - thermostat_.reset_nlocal(); - } - //-------------------------------------------------------- - // reset_atom_materials: - // resets the localized atom to material map - //-------------------------------------------------------- - void KinetoThermostat::reset_atom_materials(const Array & elementToMaterialMap, - const MatrixDependencyManager * atomElement) - { - kinetostat_.reset_atom_materials(elementToMaterialMap, - atomElement); - thermostat_.reset_atom_materials(elementToMaterialMap, - atomElement); - } - //-------------------------------------------------------- - // construct_transfers: - // pass through to appropriate transfer constuctors - //-------------------------------------------------------- - void KinetoThermostat::construct_transfers() - { - kinetostat_.construct_transfers(); - thermostat_.construct_transfers(); - } - //-------------------------------------------------------- - // initialize: - // sets up methods before a run - //-------------------------------------------------------- - void KinetoThermostat::initialize() - { - kinetostat_.initialize(); - thermostat_.initialize(); - needReset_ = false; - } - //-------------------------------------------------------- - // output: - // pass through to appropriate output methods - //-------------------------------------------------------- - void KinetoThermostat::output(OUTPUT_LIST & outputData) const - { - kinetostat_.output(outputData); - thermostat_.output(outputData); - } - //-------------------------------------------------------- - // finish: - // pass through to appropriate end-of-run methods - //-------------------------------------------------------- - void KinetoThermostat::finish() - { - kinetostat_.finish(); - thermostat_.finish(); - } - //-------------------------------------------------- - // pack_fields - // bundle all allocated field matrices into a list - // for output needs - //-------------------------------------------------- - void KinetoThermostat::pack_fields(RESTART_LIST & data) - { - kinetostat_.pack_fields(data); - thermostat_.pack_fields(data); - } - //-------------------------------------------------------- - // compute_boundary_flux: - // computes the boundary flux to be consistent with - // the controller - //-------------------------------------------------------- - void KinetoThermostat::compute_boundary_flux(FIELDS & fields) - { - kinetostat_.compute_boundary_flux(fields); - thermostat_.compute_boundary_flux(fields); - } - //-------------------------------------------------------- - // add_to_rhs: - // adds any controller contributions to the FE rhs - //-------------------------------------------------------- - void KinetoThermostat::add_to_rhs(FIELDS & rhs) - { - thermostat_.add_to_rhs(rhs); - kinetostat_.add_to_rhs(rhs); - } - //-------------------------------------------------------- - // apply_pre_predictor: - // applies the controller in the pre-predictor - // phase of the time integrator - //-------------------------------------------------------- - void KinetoThermostat::apply_pre_predictor(double dt, int timeStep) - { - thermostat_.apply_pre_predictor(dt,timeStep); - kinetostat_.apply_pre_predictor(dt,timeStep); - } - //-------------------------------------------------------- - // apply_mid_predictor: - // applies the controller in the mid-predictor - // phase of the time integrator - //-------------------------------------------------------- - void KinetoThermostat::apply_mid_predictor(double dt, int timeStep) - { - thermostat_.apply_mid_predictor(dt,timeStep); - kinetostat_.apply_mid_predictor(dt,timeStep); - } - //-------------------------------------------------------- - // apply_post_predictor: - // applies the controller in the post-predictor - // phase of the time integrator - //-------------------------------------------------------- - void KinetoThermostat::apply_post_predictor(double dt, int timeStep) - { - thermostat_.apply_post_predictor(dt,timeStep); - kinetostat_.apply_post_predictor(dt,timeStep); - } - //-------------------------------------------------------- - // apply_pre_corrector: - // applies the controller in the pre-corrector phase - // of the time integrator - //-------------------------------------------------------- - void KinetoThermostat::apply_pre_corrector(double dt, int timeStep) - { - thermostat_.apply_pre_corrector(dt,timeStep); - kinetostat_.apply_pre_corrector(dt,timeStep); - } - //-------------------------------------------------------- - // apply_post_corrector: - // applies the controller in the post-corrector phase - // of the time integrator - //-------------------------------------------------------- - void KinetoThermostat::apply_post_corrector(double dt, int timeStep) - { - thermostat_.apply_post_corrector(dt,timeStep); - kinetostat_.apply_post_corrector(dt,timeStep); - } - //-------------------------------------------------------- - // pre_exchange - //-------------------------------------------------------- - void KinetoThermostat::pre_exchange() - { - thermostat_.pre_exchange(); - kinetostat_.pre_exchange(); - } - //-------------------------------------------------------- - // pre_force - //-------------------------------------------------------- - void KinetoThermostat::pre_force() - { - thermostat_.pre_force(); - kinetostat_.pre_force(); - } - //-------------------------------------------------------- - // coupling_mode - //------------------------------------------------------- - AtomicRegulator::RegulatorCouplingType KinetoThermostat::coupling_mode(const FieldName field) const - { - //TEMP_JAT - if (field==VELOCITY) { - return kinetostat_.coupling_mode(); - } - else if (field == TEMPERATURE) { - return thermostat_.coupling_mode(); + AtomicRegulator::reset_method(); } else { - throw ATC_Error("KinetoThermostat::coupling_mode - invalid field given"); + set_all_data_to_used(); } } + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class VelocityRescaleCombined + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + VelocityRescaleCombined::VelocityRescaleCombined(AtomicRegulator * kinetostat) : + VelocityGlc(kinetostat), + velocity_(atc_->field(VELOCITY)), + thermostatCorrection_(NULL) + { + // do nothing + } + + //-------------------------------------------------------- + // initialize + // initializes all data + //-------------------------------------------------------- + void VelocityRescaleCombined::initialize() + { + VelocityGlc::initialize(); + thermostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicFluctuatingMomentumRescaled"); + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations + //-------------------------------------------------------- + void VelocityRescaleCombined::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + rhs = ((atc_->mass_mat_md(VELOCITY)).quantity())*(velocity_.quantity()); + rhs -= thermostatCorrection_->quantity(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatRescaleCombined + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatRescaleCombined::ThermostatRescaleCombined(AtomicRegulator * thermostat) : + ThermostatRescale(thermostat), + kinetostatCorrection_(NULL) + { + // do nothing + } + + //-------------------------------------------------------- + // initialize + // initializes all data + //-------------------------------------------------------- + void ThermostatRescaleCombined::initialize() + { + ThermostatRescale::initialize(); + kinetostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicCombinedRescaleThermostatError"); + } + + //-------------------------------------------------------- + // set_rhs: + // constructs the RHS vector with the target + // temperature + //-------------------------------------------------------- + void ThermostatRescaleCombined::set_rhs(DENS_MAT & rhs) + { + ThermostatRescale::set_rhs(rhs); + rhs -= kinetostatCorrection_->quantity(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatRescale + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatRescale::KinetoThermostatRescale(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations) : + KinetoThermostatShapeFunction(kinetoThermostat,couplingMaxIterations), + atomVelocities_(NULL), + nodalVelocities_(atc_->field(VELOCITY)), + lambdaMomentum_(NULL), + lambdaEnergy_(NULL), + atomicFluctuatingVelocityRescaled_(NULL), + atomicStreamingVelocity_(NULL), + thermostat_(NULL), + kinetostat_(NULL) + { + thermostat_ = this->construct_rescale_thermostat(); + kinetostat_ = new VelocityRescaleCombined(kinetoThermostat); + // data associated with stage 3 in ATC_Method::initialize + lambdaMomentum_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaMomentum",atc_->nsd()); + lambdaEnergy_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + KinetoThermostatRescale::~KinetoThermostatRescale() + { + if (thermostat_) delete thermostat_; + if (kinetostat_) delete kinetostat_; + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void KinetoThermostatRescale::construct_transfers() + { + // construct independent transfers first + thermostat_->construct_transfers(); + kinetostat_->construct_transfers(); + + InterscaleManager & interscaleManager(atc_->interscale_manager()); + + // get atom velocity data from manager + atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); + + // transfers requiring terms from both regulators + // rescaled velocity fluctuations + atomicFluctuatingVelocityRescaled_ = new AtomicFluctuatingVelocityRescaled(atc_); + interscaleManager.add_per_atom_quantity(atomicFluctuatingVelocityRescaled_, + "AtomFluctuatingVelocityRescaled"); + + // streaming velocity component + atomicStreamingVelocity_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); + + // rescaled momentum fluctuations, error term for kinetostat rhs + PerAtomQuantity * tempAtom = new AtomicMomentum(atc_, + atomicFluctuatingVelocityRescaled_); + interscaleManager.add_per_atom_quantity(tempAtom,"AtomFluctuatingMomentumRescaled"); + DENS_MAN * tempNodes = new AtfShapeFunctionRestriction(atc_, + tempAtom, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(tempNodes, + "NodalAtomicFluctuatingMomentumRescaled"); + + // error term for thermostat rhs + tempAtom = new AtomicCombinedRescaleThermostatError(atc_); + interscaleManager.add_per_atom_quantity(tempAtom,"AtomCombinedRescaleThermostatError"); + tempNodes = new AtfShapeFunctionRestriction(atc_, + tempAtom, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(tempNodes, + "NodalAtomicCombinedRescaleThermostatError"); + } + + //-------------------------------------------------------- + // construct_rescale_thermostat + // constructs the appropriate rescaling thermostat + // varied through inheritance + //-------------------------------------------------------- + ThermostatRescale * KinetoThermostatRescale::construct_rescale_thermostat() + { + return new ThermostatRescaleCombined(atomicRegulator_); + } + + //-------------------------------------------------------- + // initialize + // initializes all data + //-------------------------------------------------------- + void KinetoThermostatRescale::initialize() + { + KinetoThermostatShapeFunction::initialize(); + thermostat_->initialize(); + kinetostat_->initialize(); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat in the post corrector phase + //-------------------------------------------------------- + void KinetoThermostatRescale::apply_post_corrector(double dt) + { + // initial guesses + lambdaMomentum_->set_quantity() = nodalVelocities_.quantity(); + lambdaEnergy_->set_quantity() = 1.; + + int iteration = 0; + double eErr, pErr; + while (iteration < couplingMaxIterations_) { + _lambdaMomentumOld_ = lambdaMomentum_->quantity(); + _lambdaEnergyOld_ = lambdaEnergy_->quantity(); + + // update thermostat + thermostat_->compute_thermostat(dt); + + // update kinetostat + kinetostat_->compute_kinetostat(dt); + + // check convergence + _diff_ = lambdaEnergy_->quantity() - _lambdaEnergyOld_; + eErr = _diff_.col_norm()/_lambdaEnergyOld_.col_norm(); + _diff_ = lambdaMomentum_->quantity() - _lambdaMomentumOld_; + pErr = _diff_.col_norm()/_lambdaMomentumOld_.col_norm(); + if (eErr < tolerance_ && pErr < tolerance_) { + break; + } + iteration++; + } + if (iteration == couplingMaxIterations_) { + stringstream message; + message << "WARNING: Iterative solve for lambda failed to converge after " << couplingMaxIterations_ << " iterations, final tolerance was " << std::max(eErr,pErr) << "\n"; + ATC::LammpsInterface::instance()->print_msg(message.str()); + } + + // application of rescaling lambda due + apply_to_atoms(atomVelocities_); + } + + //-------------------------------------------------------- + // apply_lambda_to_atoms: + // applies the velocity rescale with an existing lambda + // note oldAtomicQuantity and dt are not used + //-------------------------------------------------------- + void KinetoThermostatRescale::apply_to_atoms(PerAtomQuantity * atomVelocities) + { + *atomVelocities = atomicFluctuatingVelocityRescaled_->quantity() + atomicStreamingVelocity_->quantity(); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void KinetoThermostatRescale::output(OUTPUT_LIST & outputData) + { + thermostat_->output(outputData); + kinetostat_->output(outputData); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatRescaleMixedKePeCombined + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatRescaleMixedKePeCombined::ThermostatRescaleMixedKePeCombined(AtomicRegulator * thermostat) : + ThermostatRescaleMixedKePe(thermostat), + kinetostatCorrection_(NULL) + { + // do nothing + } + + //-------------------------------------------------------- + // initialize + // initializes all data + //-------------------------------------------------------- + void ThermostatRescaleMixedKePeCombined::initialize() + { + ThermostatRescaleMixedKePe::initialize(); + kinetostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicCombinedRescaleThermostatError"); + } + + //-------------------------------------------------------- + // set_rhs: + // constructs the RHS vector with the target + // temperature + //-------------------------------------------------------- + void ThermostatRescaleMixedKePeCombined::set_rhs(DENS_MAT & rhs) + { + ThermostatRescaleMixedKePe::set_rhs(rhs); + rhs -= kinetostatCorrection_->quantity(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatRescaleMixedKePe + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatRescaleMixedKePe::KinetoThermostatRescaleMixedKePe(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations) : + KinetoThermostatRescale(kinetoThermostat,couplingMaxIterations) + { + // do nothing + } + + //-------------------------------------------------------- + // construct_rescale_thermostat + // constructs the appropriate rescaling thermostat + // varied through inheritance + //-------------------------------------------------------- + ThermostatRescale * KinetoThermostatRescaleMixedKePe::construct_rescale_thermostat() + { + return new ThermostatRescaleMixedKePeCombined(atomicRegulator_); + } + + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatGlcFs + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatGlcFs::KinetoThermostatGlcFs(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const string & regulatorPrefix) : + KinetoThermostatShapeFunction(kinetoThermostat,couplingMaxIterations,regulatorPrefix), + velocity_(atc_->field(VELOCITY)), + temperature_(atc_->field(TEMPERATURE)), + timeFilter_(atomicRegulator_->time_filter()), + nodalAtomicLambdaForce_(NULL), + lambdaForceFiltered_(NULL), + nodalAtomicLambdaPower_(NULL), + lambdaPowerFiltered_(NULL), + atomRegulatorForces_(NULL), + atomThermostatForces_(NULL), + atomMasses_(NULL), + atomVelocities_(NULL), + isFirstTimestep_(true), + nodalAtomicMomentum_(NULL), + nodalAtomicEnergy_(NULL), + atomPredictedVelocities_(NULL), + nodalAtomicPredictedMomentum_(NULL), + nodalAtomicPredictedEnergy_(NULL), + firstHalfAtomForces_(NULL), + dtFactor_(0.) + { + // construct/obtain data corresponding to stage 3 of ATC_Method::initialize + //nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1); + //lambdaPowerFiltered_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void KinetoThermostatGlcFs::construct_transfers() + { + // BASES INITIALIZE + // GRAB ANY COPIES FROM BASES + + // TOTAL REGULATOR FORCE + // TOTAL FIRST HALF FORCE, IF NECESSARY + } + + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void KinetoThermostatGlcFs::initialize() + { + RegulatorMethod::initialize(); + + // INITIALIZE BASES + + // MAKE SURE ANY NEEDED POINTERS FROM BASES ARE COPIED BY HERE + } + + //-------------------------------------------------------- + // apply_to_atoms: + // determines what if any contributions to the + // atomic moition is needed for + // consistency with the thermostat + // and computes the instantaneous induced power + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_to_atoms(PerAtomQuantity * atomicVelocity, + const DENS_MAN * nodalAtomicMomentum, + const DENS_MAN * nodalAtomicEnergy, + const DENS_MAT & lambdaForce, + DENS_MAT & nodalAtomicLambdaForce, + DENS_MAT & nodalAtomicLambdaPower, + double dt) + { + // compute initial contributions to lambda force and power + nodalAtomicLambdaPower = nodalAtomicEnergy->quantity(); + nodalAtomicLambdaPower *= -1.; + nodalAtomicLambdaForce = nodalAtomicMomentum->quantity(); + nodalAtomicLambdaForce *= -1.; + + // apply lambda force to atoms + _velocityDelta_ = lambdaForce; + _velocityDelta_ /= atomMasses_->quantity(); + _velocityDelta_ *= dt; + (*atomicVelocity) += _velocityDelta_; + + // finalize lambda force and power + nodalAtomicLambdaForce += nodalAtomicMomentum->quantity(); + nodalAtomicLambdaPower += nodalAtomicEnergy->quantity(); + } + + //-------------------------------------------------------- + // full_prediction: + // flag to perform a full prediction calcalation + // for lambda rather than using the old value + //-------------------------------------------------------- + bool KinetoThermostatGlcFs::full_prediction() + { + // CHECK BOTH BASES + return false; + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_pre_predictor(double dt) + { + DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myLambdaPowerFiltered(lambdaPowerFiltered_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + + // update filtered forces power, equivalent to measuring changes in momentum and energy + timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + timeFilter_->apply_pre_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); + + // apply lambda force to atoms and compute instantaneous lambda power for first half of time step + this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,nodalAtomicEnergy_, + firstHalfAtomForces_->quantity(), + nodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + // update nodal variables for first half of time step + // velocity + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy1_,0.5*dt); + + // start update of filtered lambda force and power using temporary (i.e., 0 valued) quantities for first part of update + nodalAtomicLambdaForce = 0.; + timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + myNodalAtomicLambdaPower = 0.; + timeFilter_->apply_post_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_pre_corrector(double dt) + { + // CHECK WHEN CREATING PREDICTED VELOCITIES IN BASE REGULATORS, ONLY NEED ONE + (*atomPredictedVelocities_) = atomVelocities_->quantity(); + + // do full prediction if we just redid the shape functions + if (full_prediction()) { + this->compute_lambda(dt); + + atomThermostatForces_->unfix_quantity(); // allow update of atomic force applied by lambda + } + + // apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step + DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + apply_to_atoms(atomPredictedVelocities_, + nodalAtomicPredictedMomentum_,nodalAtomicPredictedEnergy_, + firstHalfAtomForces_->quantity(), + myNodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + if (full_prediction()) + atomThermostatForces_->fix_quantity(); + + // SPLIT OUT FUNCTION TO CREATE DELTA VARIABLES IN BASES, ONLY NEED THESE + // update predicted nodal variables for second half of time step + // velocity + this->add_to_momentum(myNodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); + // following manipulations performed this way for efficiency + deltaEnergy1_ += deltaEnergy2_; + atc_->apply_inverse_mass_matrix(deltaEnergy1_,TEMPERATURE); + temperature_ += deltaEnergy1_; + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_post_corrector(double dt) + { + // remove predicted power effects + // velocity + DENS_MAT & myVelocity(velocity_.set_quantity()); + myVelocity -= deltaMomentum_; + // temperature + DENS_MAT & myTemperature(temperature_.set_quantity()); + atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); + myTemperature -= deltaEnergy2_; + + // set up equation and update lambda + this->compute_lambda(dt); + + // apply lambda force to atoms and compute instantaneous lambda power for second half of time step + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + // allow computation of force applied by lambda using current velocities + atomThermostatForces_->unfix_quantity(); + atomThermostatForces_->quantity(); + atomThermostatForces_->fix_quantity(); + apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,nodalAtomicEnergy_, + atomRegulatorForces_->quantity(), + nodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + // finalize filtered lambda force and power by adding latest contribution + timeFilter_->apply_post_step2(lambdaForceFiltered_->set_quantity(), + nodalAtomicLambdaForce,dt); + timeFilter_->apply_post_step2(lambdaPowerFiltered_->set_quantity(), + myNodalAtomicLambdaPower,dt); + + // update nodal variables for second half of time step + // velocity + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); + myTemperature += deltaEnergy2_; + + + isFirstTimestep_ = false; + } + + //-------------------------------------------------------- + // compute_lambda: + // sets up and solves linear system for lambda, if the + // bool is true it iterators to a non-linear solution + //-------------------------------------------------------- + void KinetoThermostatGlcFs::compute_lambda(double dt, + bool iterateSolution) + { + // ITERATIVE SOLUTION + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void KinetoThermostatGlcFs::output(OUTPUT_LIST & outputData) + { + // DO NOT CALL INDIVIDUAL REGULATORS + // OUTPUT TOTAL FORCE AND TOTAL POWER + // OUTPUT EACH LAMBDA + } + + }; diff --git a/lib/atc/KinetoThermostat.h b/lib/atc/KinetoThermostat.h index 00c43f666d..465da6b49a 100644 --- a/lib/atc/KinetoThermostat.h +++ b/lib/atc/KinetoThermostat.h @@ -3,7 +3,6 @@ #include "AtomicRegulator.h" #include "PerAtomQuantityLibrary.h" -//TEMP_JAT - transitional headers until we have a new method #include "Kinetostat.h" #include "Thermostat.h" #include @@ -11,9 +10,9 @@ #include namespace ATC { -/* #ifdef WIP_JAT */ -/* static const int myLambdaMaxIterations = 50; */ -/* #endif */ + + static const int myCouplingMaxIterations = 50; + // forward declarations class MomentumTimeIntegrator; class ThermalTimeIntegrator; @@ -42,338 +41,448 @@ namespace ATC { /** instantiate up the desired method(s) */ virtual void construct_methods(); - //TEMP_JAT - required temporarily while we have two regulators - - /** initialization of method data */ - virtual void initialize(); - /** method(s) create all necessary transfer operators */ - virtual void construct_transfers(); - /** reset number of local atoms, as well as atomic data */ - virtual void reset_nlocal(); - /** set up atom to material identification */ - virtual void reset_atom_materials(const Array & elementToMaterialMap, - const MatrixDependencyManager * atomElement); - /** apply the regulator in the pre-predictor phase */ - virtual void apply_pre_predictor(double dt, int timeStep); - /** apply the regulator in the mid-predictor phase */ - virtual void apply_mid_predictor(double dt, int timeStep); - /** apply the regulator in the post-predictor phase */ - virtual void apply_post_predictor(double dt, int timeStep); - /** apply the regulator in the pre-correction phase */ - virtual void apply_pre_corrector(double dt, int timeStep); - /** apply the regulator in the post-correction phase */ - virtual void apply_post_corrector(double dt, int timeStep); - /** prior to exchanges */ - virtual void pre_exchange(); - /** prior to force calculation */ - virtual void pre_force(); - /** force a reset to occur */ - void force_reset() {kinetostat_.force_reset();thermostat_.force_reset();}; - /** compute the thermal boundary flux, must be consistent with regulator */ - virtual void compute_boundary_flux(FIELDS & fields); - /** type of boundary coupling */ - virtual RegulatorCouplingType coupling_mode(const FieldName) const; - virtual void output(OUTPUT_LIST & outputData) const; - /** final work at the end of a run */ - virtual void finish(); - /** add contributions (if any) to the finite element right-hand side */ - virtual void add_to_rhs(FIELDS & rhs); - - /** pack fields for restart */ - virtual void pack_fields(RESTART_LIST & data); // data access, intended for method objects /** reset the nodal power to a prescribed value */ virtual void reset_lambda_contribution(const DENS_MAT & target, const FieldName field); - //TEMP_JAT - will use real accessor - /** return value for the correction maximum number of iterations */ - int lambda_max_iterators() {return thermostat_.lambda_max_iterations();}; + + /** return value for the max number of mechanical/thermal coupling iterations */ + int coupling_max_iterations() const {return couplingMaxIterations_;}; protected: - //TEMP_JAT - individual controllers for now - Kinetostat kinetostat_; - Thermostat thermostat_; + /** maximum number of iterations used to solved coupled thermo/mechanical problem */ + int couplingMaxIterations_; + private: // DO NOT define this KinetoThermostat(); }; -/* #ifdef WIP_JAT */ -/* /\** */ -/* * @class ThermostatShapeFunction */ -/* * @brief Class for thermostat algorithms using the shape function matrices */ -/* * (thermostats have general for of N^T w N lambda = rhs) */ -/* *\/ */ + + /** + * @class KinetoThermostatShapeFunction + * @brief Class for kinetostat/thermostat algorithms using the shape function matrices + * (thermostats have general for of N^T w N lambda = rhs) + */ -/* class ThermostatShapeFunction : public RegulatorShapeFunction { */ + class KinetoThermostatShapeFunction : public RegulatorMethod { -/* public: */ + public: -/* ThermostatShapeFunction(Thermostat * thermostat, */ -/* const std::string & regulatorPrefix = ""); */ + KinetoThermostatShapeFunction(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const std::string & regulatorPrefix = "") : RegulatorMethod(kinetoThermostat), + couplingMaxIterations_(couplingMaxIterations) {}; -/* virtual ~ThermostatShapeFunction() {}; */ + virtual ~KinetoThermostatShapeFunction() {}; -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ + /** instantiate all needed data */ + virtual void construct_transfers() = 0; -/* protected: */ + /** initialize all data */ + virtual void initialize() {tolerance_ = atomicRegulator_->tolerance();}; -/* // methods */ -/* /\** set weighting factor for in matrix Nhat^T * weights * Nhat *\/ */ -/* virtual void set_weights(); */ + protected: -/* // member data */ -/* /\** pointer to thermostat object for data *\/ */ -/* Thermostat * thermostat_; */ + /** maximum number of iterations between energy and momentum regulators */ + int couplingMaxIterations_; -/* -/* /\** MD mass matrix *\/ */ -/* DIAG_MAN & mdMassMatrix_; + /** tolerance */ + double tolerance_; -/* /\** pointer to atom velocities *\/ */ -/* FundamentalAtomQuantity * atomVelocities_; */ + private: -/* /\** workspace variables *\/ */ -/* DENS_VEC _weightVector_, _maskedWeightVector_; */ + // DO NOT define this + KinetoThermostatShapeFunction(); -/* private: */ + }; -/* // DO NOT define this */ -/* ThermostatShapeFunction(); */ - -/* }; */ - -/* /\** */ -/* * @class ThermostatRescale */ -/* * @brief Enforces constraint on atomic kinetic energy based on FE temperature */ -/* *\/ */ + /** + * @class VelocityRescaleCombined + * @brief Enforces constraints on atomic velocity based on FE temperature and velocity + */ -/* class ThermostatRescale : public ThermostatShapeFunction { */ + class VelocityRescaleCombined : public VelocityGlc { -/* public: */ + public: + + friend class KinetoThermostatRescale; // since this is basically a set of member functions for friend -/* ThermostatRescale(Thermostat * thermostat); */ + VelocityRescaleCombined(AtomicRegulator * kinetostat); -/* virtual ~ThermostatRescale() {}; */ + virtual ~VelocityRescaleCombined(){}; -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ - -/* /\** applies thermostat to atoms in the post-corrector phase *\/ */ -/* virtual void apply_post_corrector(double dt); */ -/* #ifdef WIP_JAT */ -/* /\** compute boundary flux, requires thermostat input since it is part of the coupling scheme *\/ */ -/* virtual void compute_boundary_flux(FIELDS & fields) */ -/* {boundaryFlux_[TEMPERATURE] = 0.;}; */ -/* #endif */ - -/* /\** get data for output *\/ */ -/* virtual void output(OUTPUT_LIST & outputData); */ - -/* protected: */ - -/* /\** apply solution to atomic quantities *\/ */ -/* void apply_to_atoms(PerAtomQuantity * atomVelocities); */ - -/* /\** correct the RHS for complex temperature definitions *\/ */ -/* virtual void correct_rhs(DENS_MAT & rhs) {}; // base class does no correction, assuming kinetic definition */ - -/* /\** FE temperature field *\/ */ -/* DENS_MAN & nodalTemperature_; */ - -/* /\** construction for prolongation of lambda to atoms *\/ */ -/* AtomicVelocityRescaleFactor * atomVelocityRescalings_; */ - -/* /\** workspace variables *\/ */ -/* DENS_MAT _rhs_; */ - -/* private: */ - -/* // DO NOT define this */ -/* ThermostatRescale(); */ - -/* }; */ - -/* /\** */ -/* * @class ThermostatRescaleMixedKePe */ -/* * @brief Enforces constraint on atomic kinetic energy based on FE temperature */ -/* * when the temperature is a mix of the KE and PE */ -/* *\/ */ - -/* class ThermostatRescaleMixedKePe : public ThermostatRescale { */ - -/* public: */ - -/* ThermostatRescaleMixedKePe(Thermostat * thermostat); */ - -/* virtual ~ThermostatRescaleMixedKePe() {}; */ - -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ - -/* /\** pre-run initialization of method data *\/ */ -/* virtual void initialize(); */ - -/* protected: */ - -/* /\** correct the RHS for inclusion of the PE *\/ */ -/* virtual void correct_rhs(DENS_MAT & rhs); */ - -/* /\** nodal fluctuating potential energy *\/ */ -/* DENS_MAN * nodalAtomicFluctuatingPotentialEnergy_; */ - -/* /\** fraction of temperature from KE *\/ */ -/* double keMultiplier_; */ - -/* /\** fraction of temperature from PE *\/ */ -/* double peMultiplier_; */ - -/* private: */ - -/* // DO NOT define this */ -/* ThermostatRescaleMixedKePe(); */ - -/* }; */ - -/* /\** */ -/* * @class ThermostatGlcFs */ -/* * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm */ -/* *\/ */ - -/* class ThermostatGlcFs : public ThermostatShapeFunction { */ - -/* public: */ - -/* ThermostatGlcFs(Thermostat * thermostat, */ -/* const std::string & regulatorPrefix = ""); */ - -/* virtual ~ThermostatGlcFs() {}; */ - -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ - -/* /\** pre-run initialization of method data *\/ */ -/* virtual void initialize(); */ - -/* /\** applies thermostat to atoms in the predictor phase *\/ */ -/* virtual void apply_pre_predictor(double dt); */ - -/* /\** applies thermostat to atoms in the pre-corrector phase *\/ */ -/* virtual void apply_pre_corrector(double dt); */ - -/* /\** applies thermostat to atoms in the post-corrector phase *\/ */ -/* virtual void apply_post_corrector(double dt); */ + /** pre-run initialization of method data */ + virtual void initialize(); -/* /\** get data for output *\/ */ -/* virtual void output(OUTPUT_LIST & outputData); */ + /** applies kinetostat to atoms */ + virtual void apply_mid_predictor(double dt){}; + /** applies kinetostat to atoms */ + virtual void apply_post_corrector(double dt){}; + + /** local shape function matrices are incompatible with this mode */ + virtual bool use_local_shape_functions() const {return false;}; -/* /\* flag for performing the full lambda prediction calculation *\/ */ -/* bool full_prediction(); */ + protected: -/* protected: */ + // data + /** reference to AtC FE velocity */ + DENS_MAN & velocity_; + + /** RHS correct based on thermostat */ + DENS_MAN * thermostatCorrection_; + + // methods + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); -/* // methods */ -/* /\** determine mapping from all nodes to those to which the thermostat applies *\/ */ -/* void compute_rhs_map(); */ + // disable un-needed functionality + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt){}; + /** applies kinetostat correction to atoms */ + virtual void apply_kinetostat(double dt) {}; + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt){}; + /** apply any required corrections for localized kinetostats */ + virtual void apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight = 1.){}; -/* /\** sets up appropriate rhs for thermostat equations *\/ */ -/* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ -/* double dt) = 0; */ + private: -/* /\** apply forces to atoms *\/ */ -/* virtual void apply_to_atoms(PerAtomQuantity * atomicVelocity, */ -/* const DENS_MAN * nodalAtomicEnergy, */ -/* const DENS_MAT & lambdaForce, */ -/* DENS_MAT & nodalAtomicLambdaPower, */ -/* double dt); */ + // DO NOT define this + VelocityRescaleCombined(); + + }; -/* /\** add contributions from thermostat to FE energy *\/ */ -/* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ -/* DENS_MAT & deltaEnergy, */ -/* double dt) = 0; */ + /** + * @class ThermostatRescaleCombined + * @brief Enforces constraint on atomic kinetic energy based on FE temperature and velocity + */ + + class ThermostatRescaleCombined : public ThermostatRescale { + + public: + + ThermostatRescaleCombined(AtomicRegulator * thermostat); + + virtual ~ThermostatRescaleCombined() {}; -/* /\* sets up and solves the linear system for lambda *\/ */ -/* virtual void compute_lambda(double dt, */ -/* bool iterateSolution = true); */ + /** pre-run initialization of method data */ + virtual void initialize(); + + // deactivate un-needed methods + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + protected: -/* /\** solves the non-linear equation for lambda iteratively *\/ */ -/* void iterate_lambda(const MATRIX & rhs); */ + // data + /** RHS correct based on kinetostat */ + DENS_MAN * kinetostatCorrection_; -/* // member data */ -/* /\** reference to AtC FE temperature *\/ */ -/* DENS_MAN & temperature_; */ + // deactivate un-needed methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity * atomVelocities){}; -/* /\** pointer to a time filtering object *\/ */ -/* TimeFilter * timeFilter_; */ + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); -/* /\** power induced by lambda *\/ */ -/* DENS_MAN * nodalAtomicLambdaPower_; */ + private: -/* /\** filtered lambda power *\/ */ -/* DENS_MAN * lambdaPowerFiltered_; */ + // DO NOT define this + ThermostatRescaleCombined(); + + }; -/* /\** atomic force induced by lambda *\/ */ -/* AtomicThermostatForce * atomThermostatForces_; */ + /** + * @class KinetoThermostatRescale + * @brief Enforces constraints on atomic kinetic energy and velocity based on FE temperature and velocity + */ + + class KinetoThermostatRescale : public KinetoThermostatShapeFunction { + + public: + + KinetoThermostatRescale(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations); + + virtual ~KinetoThermostatRescale(); -/* /\** pointer to atom masses *\/ */ -/* FundamentalAtomQuantity * atomMasses_; */ + /** instantiate all needed data */ + virtual void construct_transfers(); -/* /\** pointer to the values of lambda interpolated to atoms *\/ */ -/* DENS_MAN * rhsLambdaSquared_; */ + /** pre-run initialization of method data */ + virtual void initialize(); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); -/* /\** hack to determine if first timestep has been passed *\/ */ -/* bool isFirstTimestep_; */ + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[TEMPERATURE] = 0.; boundaryFlux_[VELOCITY] = 0.;}; -/* /\** maximum number of iterations used in iterative solve for lambda *\/ */ -/* int lambdaMaxIterations_; */ + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); + + protected: -/* /\** nodal atomic energy *\/ */ -/* DENS_MAN * nodalAtomicEnergy_; */ + // methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity * atomVelocities); -/* /\** local version of velocity used as predicted final veloctiy *\/ */ -/* PerAtomQuantity * atomPredictedVelocities_; */ + /** creates the appropriate rescaling thermostat */ + virtual ThermostatRescale * construct_rescale_thermostat(); -/* /\** predicted nodal atomic energy *\/ */ -/* AtfShapeFunctionRestriction * nodalAtomicPredictedEnergy_; */ + // data + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; -/* /\** pointer for force applied in first time step *\/ */ -/* DENS_MAN * firstHalfAtomForces_; */ + /** clone of FE velocity field */ + DENS_MAN & nodalVelocities_; -/* /\** FE temperature change from thermostat during predictor phase in second half of timestep *\/ */ -/* DENS_MAT deltaEnergy1_; */ + /** lambda coupling parameter for momentum */ + DENS_MAN * lambdaMomentum_; -/* /\** FE temperature change from thermostat during corrector phase in second half of timestep *\/ */ -/* DENS_MAT deltaEnergy2_; */ + /** lambda coupling parameter for energy */ + DENS_MAN * lambdaEnergy_; -/* /\** right-hand side data for thermostat equation *\/ */ -/* DENS_MAT rhs_; */ + /** pointer to rescaled velocity fluctuations */ + PerAtomQuantity * atomicFluctuatingVelocityRescaled_; -/* /\** mapping from all to regulated nodes *\/ */ -/* DENS_MAT rhsMap_; */ + /** pointer to streaming velocity */ + PerAtomQuantity * atomicStreamingVelocity_; -/* /\** fraction of timestep over which constraint is exactly enforced *\/ */ -/* double dtFactor_; */ + /** rescaling thermostat */ + ThermostatRescale * thermostat_; -/* // workspace */ -/* DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format */ -/* DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied */ -/* DENS_VEC _lambdaOverlap_; // lambda in MD overlapping FE nodes */ -/* DENS_MAT _lambdaOld_; // lambda from previous iteration */ -/* DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes */ -/* DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop */ + /** velocity regulating kinetostat */ + VelocityRescaleCombined * kinetostat_; -/* private: */ + // workspace + DENS_MAT _lambdaEnergyOld_, _lambdaMomentumOld_, _diff_; -/* // DO NOT define this */ -/* ThermostatGlcFs(); */ + private: -/* }; */ + // DO NOT define this + KinetoThermostatRescale(); + + }; + /** + * @class ThermostatRescaleMixedKePeCombined + * @brief Enforces constraint on atomic kinetic energy based on FE temperature and velocity when the temperature is comprised of both KE and PE contributions + */ + + class ThermostatRescaleMixedKePeCombined : public ThermostatRescaleMixedKePe { + + public: + + ThermostatRescaleMixedKePeCombined(AtomicRegulator * thermostat); + + virtual ~ThermostatRescaleMixedKePeCombined() {}; + + /** pre-run initialization of method data */ + virtual void initialize(); + + // deactivate un-needed methods + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + protected: + + // data + /** RHS correct based on kinetostat */ + DENS_MAN * kinetostatCorrection_; + + // deactivate un-needed methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity * atomVelocities){}; + + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); + + private: + + // DO NOT define this + ThermostatRescaleMixedKePeCombined(); + + }; + + /** + * @class KinetoThermostatRescaleMixedKePe + * @brief Enforces constraint on atomic kinetic energy based on FE temperature + * when the temperature is a mix of the KE and PE + */ + + class KinetoThermostatRescaleMixedKePe : public KinetoThermostatRescale { + + public: + + KinetoThermostatRescaleMixedKePe(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations); + + virtual ~KinetoThermostatRescaleMixedKePe() {}; + + protected: + + /** creates the appropriate rescaling thermostat */ + virtual ThermostatRescale * construct_rescale_thermostat(); + + private: + + // DO NOT define this + KinetoThermostatRescaleMixedKePe(); + + }; + + /** + * @class KinetoThermostatGlcFs + * @brief Class for regulation algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm + */ + + class KinetoThermostatGlcFs : public KinetoThermostatShapeFunction { + + public: + + KinetoThermostatGlcFs(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~KinetoThermostatGlcFs() {}; + + /** instantiate all needed data */ + virtual void construct_transfers(); + + /** pre-run initialization of method data */ + virtual void initialize(); + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); + + /* flag for performing the full lambda prediction calculation */ + bool full_prediction(); + + protected: + + // methods + + /** apply forces to atoms */ + virtual void apply_to_atoms(PerAtomQuantity * atomicVelocity, + const DENS_MAN * nodalAtomicMomentum, + const DENS_MAN * nodalAtomicEnergy, + const DENS_MAT & lambdaForce, + DENS_MAT & nodalAtomicLambdaForce, + DENS_MAT & nodalAtomicLambdaPower, + double dt); + + // USE BASE CLASSES FOR THESE + /** add contributions from regulator to FE momentum */ + virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, + DENS_MAT & deltaForce, + double dt) = 0; + + /** add contributions from regulator to FE energy */ + virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) = 0; + + /* sets up and solves the linear system for lambda */ + virtual void compute_lambda(double dt, + bool iterateSolution = true); + + // member data + /** reference to AtC FE velocity */ + DENS_MAN & velocity_; + + /** reference to AtC FE temperature */ + DENS_MAN & temperature_; + + /** pointer to a time filtering object */ + TimeFilter * timeFilter_; + + /** force induced by lambda */ + DENS_MAN * nodalAtomicLambdaForce_; + + /** filtered lambda force */ + DENS_MAN * lambdaForceFiltered_; + + /** power induced by lambda */ + DENS_MAN * nodalAtomicLambdaPower_; + + /** filtered lambda power */ + DENS_MAN * lambdaPowerFiltered_; + + /** atomic force induced by lambda */ + AtomicThermostatForce * atomRegulatorForces_; + + /** atomic force induced by thermostat lambda */ + AtomicThermostatForce * atomThermostatForces_; + + /** pointer to atom masses */ + FundamentalAtomQuantity * atomMasses_; + + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; + + /** hack to determine if first timestep has been passed */ + bool isFirstTimestep_; + + /** nodal atomic momentum */ + DENS_MAN * nodalAtomicMomentum_; + + /** nodal atomic energy */ + DENS_MAN * nodalAtomicEnergy_; + + /** local version of velocity used as predicted final veloctiy */ + PerAtomQuantity * atomPredictedVelocities_; + + /** predicted nodal atomic momentum */ + AtfShapeFunctionRestriction * nodalAtomicPredictedMomentum_; + + /** predicted nodal atomic energy */ + AtfShapeFunctionRestriction * nodalAtomicPredictedEnergy_; + + /** pointer for force applied in first time step */ + DENS_MAN * firstHalfAtomForces_; + + /** FE momentum change from regulator during predictor phase in second half of timestep */ + DENS_MAT deltaMomentum_; + + /** FE temperature change from regulator during predictor phase in second half of timestep */ + DENS_MAT deltaEnergy1_; + + /** FE temperature change from regulator during corrector phase in second half of timestep */ + DENS_MAT deltaEnergy2_; + + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; + + // workspace + DENS_MAT _lambdaForceOutput_; // force applied by lambda in output format + DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format + DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied + + private: + + // DO NOT define this + KinetoThermostatGlcFs(); + + }; +/* #ifdef WIP_JAT */ /* /\** */ /* * @class ThermostatFlux */ /* * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ diff --git a/lib/atc/Kinetostat.cpp b/lib/atc/Kinetostat.cpp index c104efc720..17c5b6caf7 100644 --- a/lib/atc/Kinetostat.cpp +++ b/lib/atc/Kinetostat.cpp @@ -160,8 +160,12 @@ namespace ATC { // update time filter if (timeFilterManager->need_reset()) { - - timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE); + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } + else { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE); + } } if (timeFilterManager->filter_dynamics()) { @@ -189,7 +193,7 @@ namespace ATC { break; } default: - throw ATC_Error("Unknown kinetostat type in Kinetostat::initialize"); + throw ATC_Error("Kinetostat::construct_methods - Unknown filtered kinetostat type"); } } else { @@ -209,13 +213,52 @@ namespace ATC { case DYNAMICS: { if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (couplingMode_ == GHOST_FLUX) { - regulatorMethod_ = new KinetostatFluxGhost(this); + if (md_fixed_nodes(VELOCITY)) { + if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fixed nodes but no fluxes + regulatorMethod_ = new KinetostatFixed(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only flux nodes + regulatorMethod_ = new KinetostatFluxGhost(this); + } } else if (couplingMode_ == FIXED) { - regulatorMethod_ = new KinetostatFixed(this); + if (md_flux_nodes(VELOCITY)) { + if (!md_fixed_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fluxes but no fixed or coupled nodes + regulatorMethod_ = new KinetostatFlux(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only fixed nodes + regulatorMethod_ = new KinetostatFixed(this); + } } else if (couplingMode_ == FLUX) { - regulatorMethod_ = new KinetostatFlux(this); + if (md_fixed_nodes(VELOCITY)) { + if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fixed nodes but no fluxes + regulatorMethod_ = new KinetostatFixed(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only flux nodes + regulatorMethod_ = new KinetostatFlux(this); + } } break; } @@ -239,7 +282,7 @@ namespace ATC { } } default: - throw ATC_Error("Unknown kinetostat type in Kinetostat::initialize"); + throw ATC_Error("Kinetostat::construct_methods - Unknown kinetostat type"); } AtomicRegulator::reset_method(); } @@ -259,10 +302,10 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatShapeFunction::KinetostatShapeFunction(Kinetostat *kinetostat, + KinetostatShapeFunction::KinetostatShapeFunction(AtomicRegulator *kinetostat, const string & regulatorPrefix) : RegulatorShapeFunction(kinetostat,regulatorPrefix), - kinetostat_(kinetostat), + mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), timeFilter_(atomicRegulator_->time_filter()), nodalAtomicLambdaForce_(NULL), lambdaForceFiltered_(NULL), @@ -271,8 +314,8 @@ namespace ATC { atomMasses_(NULL) { // data associated with stage 3 in ATC_Method::initialize - lambda_ = kinetostat->regulator_data(regulatorPrefix_+"LambdaMomentum",nsd_); - lambdaForceFiltered_ = kinetostat_->regulator_data("LambdaForceFiltered",nsd_); + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaMomentum",nsd_); + lambdaForceFiltered_ = atomicRegulator_->regulator_data("LambdaForceFiltered",nsd_); } //-------------------------------------------------------- @@ -331,9 +374,8 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - GlcKinetostat::GlcKinetostat(Kinetostat *kinetostat) : + GlcKinetostat::GlcKinetostat(AtomicRegulator *kinetostat) : KinetostatShapeFunction(kinetostat), - mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), atomPositions_(NULL) { // do nothing @@ -418,7 +460,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - DisplacementGlc::DisplacementGlc(Kinetostat * kinetostat) : + DisplacementGlc::DisplacementGlc(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalAtomicMassWeightedDisplacement_(NULL), nodalDisplacements_(atc_->field(DISPLACEMENT)) @@ -538,7 +580,7 @@ namespace ATC { { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii rhs = nodalAtomicMassWeightedDisplacement_->quantity(); - rhs -= ((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalDisplacements_.quantity()); + rhs -= (mdMassMatrix_.quantity())*(nodalDisplacements_.quantity()); } //-------------------------------------------------------- @@ -620,7 +662,7 @@ namespace ATC { _nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity(); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_); } } @@ -635,7 +677,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - DisplacementGlcFiltered::DisplacementGlcFiltered(Kinetostat * kinetostat) : + DisplacementGlcFiltered::DisplacementGlcFiltered(AtomicRegulator * kinetostat) : DisplacementGlc(kinetostat), nodalAtomicDisplacements_(atc_->nodal_atomic_field(DISPLACEMENT)) { @@ -667,7 +709,7 @@ namespace ATC { { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt)); - rhs = coef*((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalAtomicDisplacements_.quantity() - nodalDisplacements_.quantity()); + rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicDisplacements_.quantity() - nodalDisplacements_.quantity()); } //-------------------------------------------------------- @@ -704,7 +746,7 @@ namespace ATC { DENS_MAT & lambda(lambda_->set_quantity()); DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &lambdaForceFiltered; } } @@ -719,7 +761,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - VelocityGlc::VelocityGlc(Kinetostat * kinetostat) : + VelocityGlc::VelocityGlc(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalAtomicMomentum_(NULL), nodalVelocities_(atc_->field(VELOCITY)) @@ -739,19 +781,22 @@ namespace ATC { create_node_maps(); // set up shape function matrix - if (this->use_local_shape_functions()) { - lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); - interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, - regulatorPrefix_+"LambdaAtomMap"); - shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, - lambdaAtomMap_, - nodeToOverlapMap_); + shapeFunctionMatrix_ = interscaleManager.per_atom_sparse_matrix(regulatorPrefix_+"LambdaCouplingMatrixMomentum"); + if (!shapeFunctionMatrix_) { + if (this->use_local_shape_functions()) { + lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); + interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, + regulatorPrefix_+"LambdaAtomMap"); + shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, + lambdaAtomMap_, + nodeToOverlapMap_); + } + else { + shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); + } + interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, + regulatorPrefix_+"LambdaCouplingMatrixMomentum"); } - else { - shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); - } - interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, - regulatorPrefix_+"LambdaCouplingMatrixMomentum"); // set linear solver strategy if (atomicRegulator_->use_lumped_lambda_solve()) { @@ -774,7 +819,7 @@ namespace ATC { atomKinetostatForce_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_, - regulatorPrefix_+"NodalAtomicLambdaForce"); + regulatorPrefix_+"NodalAtomicLambdaForce"); // nodal momentum restricted from atoms nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum"); @@ -807,6 +852,7 @@ namespace ATC { { double dtLambda = 0.5*dt; compute_kinetostat(dtLambda); + apply_kinetostat(dtLambda); } //-------------------------------------------------------- @@ -817,6 +863,7 @@ namespace ATC { { double dtLambda = 0.5*dt; compute_kinetostat(dtLambda); + apply_kinetostat(dtLambda); } //-------------------------------------------------------- @@ -843,11 +890,26 @@ namespace ATC { // set up rhs DENS_MAT rhs(nNodes_,nsd_); - set_kinetostat_rhs(rhs,dt); + this->set_kinetostat_rhs(rhs,dt); // solve linear system for lambda solve_for_lambda(rhs,lambda_->set_quantity()); +#ifdef OBSOLETE + // compute nodal atomic power + compute_nodal_lambda_force(dt); + // apply kinetostat to atoms + apply_to_atoms(atomVelocities_,atomLambdas_->quantity()); +#endif + } + + //-------------------------------------------------------- + // apply_kinetostat + // manages the application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void VelocityGlc::apply_kinetostat(double dt) + { // compute nodal atomic power compute_nodal_lambda_force(dt); @@ -864,7 +926,7 @@ namespace ATC { { // form rhs : sum_a (hatN_Ia * x_ai) - (\dot{Upsilon})_Ii rhs = nodalAtomicMomentum_->quantity(); - rhs -= ((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalVelocities_.quantity()); + rhs -= (mdMassMatrix_.quantity())*(nodalVelocities_.quantity()); } //-------------------------------------------------------- @@ -934,7 +996,7 @@ namespace ATC { { _nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity(); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = &(lambda_->set_quantity()); + outputData[regulatorPrefix_+"LambdaMomentum"] = &(lambda_->set_quantity()); outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_); } } @@ -949,7 +1011,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - VelocityGlcFiltered::VelocityGlcFiltered(Kinetostat *kinetostat) + VelocityGlcFiltered::VelocityGlcFiltered(AtomicRegulator *kinetostat) : VelocityGlc(kinetostat), nodalAtomicVelocities_(atc_->nodal_atomic_field(VELOCITY)) { @@ -981,7 +1043,7 @@ namespace ATC { { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt)); - rhs = coef*((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalAtomicVelocities_.quantity() - nodalVelocities_.quantity()); + rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicVelocities_.quantity() - nodalVelocities_.quantity()); } //-------------------------------------------------------- @@ -1030,7 +1092,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - StressFlux::StressFlux(Kinetostat * kinetostat) : + StressFlux::StressFlux(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalForce_(atc_->field_rhs(VELOCITY)), nodalAtomicForce_(NULL), @@ -1295,7 +1357,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - StressFluxGhost::StressFluxGhost(Kinetostat * kinetostat) : + StressFluxGhost::StressFluxGhost(AtomicRegulator * kinetostat) : StressFlux(kinetostat) { // flag for performing boundary flux calculation @@ -1376,7 +1438,7 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - StressFluxFiltered::StressFluxFiltered(Kinetostat * kinetostat) : + StressFluxFiltered::StressFluxFiltered(AtomicRegulator * kinetostat) : StressFlux(kinetostat), nodalAtomicVelocity_(atc_->nodal_atomic_field(VELOCITY)) { @@ -1473,13 +1535,24 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatGlcFs::KinetostatGlcFs(Kinetostat * kinetostat, + KinetostatGlcFs::KinetostatGlcFs(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatShapeFunction(kinetostat,regulatorPrefix), - velocity_(atc_->field(VELOCITY)) + velocity_(atc_->field(VELOCITY)), + //timeFilter_(atomicRegulator_->time_filter()), + //nodalAtomicLambdaForce_(NULL), + //lambdaPowerFiltered_(NULL), + //atomKinetostatForces_(NULL), + //atomMasses_(NULL), + nodalAtomicMomentum_(NULL), + isFirstTimestep_(true), + atomPredictedVelocities_(NULL), + nodalAtomicPredictedMomentum_(NULL), + dtFactor_(0.) { // constuct/obtain data corresponding to stage 3 of ATC_Method::initialize - nodalAtomicLambdaForce_ = kinetostat_->regulator_data(regulatorPrefix_+"NodalAtomicLambdaForce",nsd_); + nodalAtomicLambdaForce_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalAtomicLambdaForce",nsd_); + lambdaForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaForceFiltered",1); } //-------------------------------------------------------- @@ -1488,10 +1561,9 @@ namespace ATC { //-------------------------------------------------------- void KinetostatGlcFs::construct_transfers() { - InterscaleManager & interscaleManager(atc_->interscale_manager()); - // base class transfers KinetostatShapeFunction::construct_transfers(); + InterscaleManager & interscaleManager(atc_->interscale_manager()); // get data from manager nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum"); @@ -1501,6 +1573,21 @@ namespace ATC { atomKinetostatForce_ = new AtomicKinetostatForceStress(atc_,atomLambdas); interscaleManager.add_per_atom_quantity(atomKinetostatForce_, regulatorPrefix_+"AtomKinetostatForce"); + + // predicted momentum quantities: atom velocities, atom momenta, and restricted atom momenta + // MAKE THINGS WORK WITH ONLY ONE PREDICTED VELOCITY AND DERIVED QUANTITIES, CHECK IT EXISTS + atomPredictedVelocities_ = new AtcAtomQuantity(atc_,nsd_); + interscaleManager.add_per_atom_quantity(atomPredictedVelocities_, + regulatorPrefix_+"AtomicPredictedVelocities"); + AtomicMomentum * atomPredictedMomentum = new AtomicMomentum(atc_, + atomPredictedVelocities_); + interscaleManager.add_per_atom_quantity(atomPredictedMomentum, + regulatorPrefix_+"AtomicPredictedMomentum"); + nodalAtomicPredictedMomentum_ = new AtfShapeFunctionRestriction(atc_, + atomPredictedMomentum, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(nodalAtomicPredictedMomentum_, + regulatorPrefix_+"NodalAtomicPredictedMomentum"); } //-------------------------------------------------------- @@ -1510,10 +1597,6 @@ namespace ATC { void KinetostatGlcFs::initialize() { KinetostatShapeFunction::initialize(); - - // set up workspaces - _deltaMomentum_.reset(nNodes_,nsd_); - _lambdaForceOutput_.reset(nNodes_,nsd_); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { @@ -1536,102 +1619,7 @@ namespace ATC { timeFilter_->initialize(nodalAtomicLambdaForce_->quantity()); } - compute_rhs_map(); - } - - //-------------------------------------------------------- - // compute_rhs_map - // creates mapping from all nodes to those to which - // the kinetostat applies - //-------------------------------------------------------- - void KinetostatGlcFs::compute_rhs_map() - { - rhsMap_.resize(overlapToNodeMap_->nRows(),1); - DENS_MAT rhsMapGlobal(nNodes_,1); - const set & applicationNodes(applicationNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (applicationNodes.find(i) != applicationNodes.end()) { - rhsMapGlobal(i,0) = 1.; - } - else { - rhsMapGlobal(i,0) = 0.; - } - } - map_unique_to_overlap(rhsMapGlobal,rhsMap_); - } - - //-------------------------------------------------------- - // apply_pre_predictor: - // apply the kinetostat to the atoms in the - // pre-predictor integration phase - //-------------------------------------------------------- - void KinetostatGlcFs::apply_pre_predictor(double dt) - { - DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); - DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); - - // update filtered forces - timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); - - // apply lambda force to atoms and compute instantaneous lambda force - this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, - atomKinetostatForce_->quantity(), - nodalAtomicLambdaForce,0.5*dt); - - // update nodal variables for first half of timestep - this->add_to_momentum(nodalAtomicLambdaForce,_deltaMomentum_,0.5*dt); - atc_->apply_inverse_mass_matrix(_deltaMomentum_,VELOCITY); - velocity_ += _deltaMomentum_; - - // start update of filtered lambda force - nodalAtomicLambdaForce = 0.; - timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); - } - - //-------------------------------------------------------- - // apply_post_corrector: - // apply the kinetostat to the atoms in the - // post-corrector integration phase - //-------------------------------------------------------- - void KinetostatGlcFs::apply_post_corrector(double dt) - { - // compute the kinetostat equation and update lambda - this->compute_lambda(dt); - - DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); - DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); - - // update filtered force - timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); - - // apply lambda force to atoms and compute instantaneous lambda force - this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, - atomKinetostatForce_->quantity(), - nodalAtomicLambdaForce,0.5*dt); - - // update nodal variables for first half of timestep - this->add_to_momentum(nodalAtomicLambdaForce,_deltaMomentum_,0.5*dt); - nodalAtomicLambdaForce *= 2./dt; - atc_->apply_inverse_mass_matrix(_deltaMomentum_,VELOCITY); - velocity_ += _deltaMomentum_; - - // start update of filtered lambda force - timeFilter_->apply_post_step2(lambdaForceFiltered,nodalAtomicLambdaForce,dt); - } - - //-------------------------------------------------------- - // compute_kinetostat - // manages the solution and application of the - // kinetostat equations and variables - //-------------------------------------------------------- - void KinetostatGlcFs::compute_lambda(double dt) - { - // set up rhs for lambda equation - this->set_kinetostat_rhs(rhs_,0.5*dt); - - // solve linear system for lambda - DENS_MAT & lambda(lambda_->set_quantity()); - solve_for_lambda(rhs_,lambda); + atomKinetostatForce_->quantity(); // initialize } //-------------------------------------------------------- @@ -1659,6 +1647,124 @@ namespace ATC { nodalAtomicLambdaForce += nodalAtomicMomentum->quantity(); } + //-------------------------------------------------------- + // full_prediction: + // flag to perform a full prediction calcalation + // for lambda rather than using the old value + //-------------------------------------------------------- + bool KinetostatGlcFs::full_prediction() + { + if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) + && (atc_->atom_to_element_map_frequency() > 1) + && (atc_->step() % atc_->atom_to_element_map_frequency() == 0 ))) { + return true; + } + return false; + } + + //-------------------------------------------------------- + // apply_pre_predictor: + // apply the kinetostat to the atoms in the + // pre-predictor integration phase + //-------------------------------------------------------- + void KinetostatGlcFs::apply_pre_predictor(double dt) + { + DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + + // update filtered forces + timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + + // apply lambda force to atoms and compute instantaneous lambda force + this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, + atomKinetostatForce_->quantity(), + nodalAtomicLambdaForce,0.5*dt); + + // update nodal variables for first half of timestep + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + + // start update of filtered lambda force + nodalAtomicLambdaForce = 0.; + timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatGlcFs::apply_pre_corrector(double dt) + { + (*atomPredictedVelocities_) = atomVelocities_->quantity(); + + // do full prediction if we just redid the shape functions + if (full_prediction()) { + this->compute_lambda(dt); + } + + // apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step + DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + apply_to_atoms(atomPredictedVelocities_, + nodalAtomicPredictedMomentum_, + atomKinetostatForce_->quantity(), + myNodalAtomicLambdaForce,0.5*dt); + + // update predicted nodal variables for second half of time step + this->add_to_momentum(myNodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the kinetostat to the atoms in the + // post-corrector integration phase + //-------------------------------------------------------- + void KinetostatGlcFs::apply_post_corrector(double dt) + { + // remove predicted force effects + DENS_MAT & myVelocity(velocity_.set_quantity()); + myVelocity -= deltaMomentum_; + + // compute the kinetostat equation and update lambda + this->compute_lambda(dt); + + // apply lambda force to atoms and compute instantaneous lambda force for second half of time step + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, + atomKinetostatForce_->quantity(), + nodalAtomicLambdaForce,0.5*dt); + + // start update of filtered lambda force + timeFilter_->apply_post_step2(lambdaForceFiltered_->set_quantity(), + nodalAtomicLambdaForce,dt); + + // update nodal variables for first half of timestep + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + + + isFirstTimestep_ = false; + } + + //-------------------------------------------------------- + // compute_kinetostat + // manages the solution and application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void KinetostatGlcFs::compute_lambda(double dt) + { + // set up rhs for lambda equation + this->set_kinetostat_rhs(rhs_,0.5*dt); + + // solve linear system for lambda + DENS_MAT & lambda(lambda_->set_quantity()); + solve_for_lambda(rhs_,lambda); + } + //-------------------------------------------------------- // output: // adds all relevant output to outputData @@ -1671,7 +1777,7 @@ namespace ATC { _lambdaForceOutput_ *= (2./dt); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_lambdaForceOutput_); } } @@ -1686,7 +1792,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFlux::KinetostatFlux(Kinetostat * kinetostat, + KinetostatFlux::KinetostatFlux(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatGlcFs(kinetostat,regulatorPrefix), momentumSource_(atc_->atomic_source(VELOCITY)), @@ -1697,7 +1803,7 @@ namespace ATC { fieldMask_(VELOCITY,FLUX) = true; // constuct/obtain data corresponding to stage 3 of ATC_Method::initialize - nodalGhostForceFiltered_ = kinetostat_->regulator_data(regulatorPrefix_+"NodalGhostForceFiltered",nsd_); + nodalGhostForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalGhostForceFiltered",nsd_); } //-------------------------------------------------------- @@ -1711,8 +1817,6 @@ namespace ATC { // set up node mappings create_node_maps(); - - // set up linear solver // set up data for linear solver shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, @@ -1770,6 +1874,9 @@ namespace ATC { else { // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration } + + // timestep factor + dtFactor_ = 1.; } //-------------------------------------------------------- @@ -1781,13 +1888,16 @@ namespace ATC { InterscaleManager & interscaleManager(atc_->interscale_manager()); // matrix requires all entries even if localized for correct lumping - regulatedNodes_ = new RegulatedNodes(atc_); - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"KinetostatRegulatedNodes"); - + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"KinetostatRegulatedNodes"); + if (!regulatedNodes_) { + regulatedNodes_ = new RegulatedNodes(atc_); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"KinetostatRegulatedNodes"); + } + // if localized monitor nodes with applied fluxes if (atomicRegulator_->use_localized_lambda()) { - if ((kinetostat_->coupling_mode() == Kinetostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { + if ((atomicRegulator_->coupling_mode() == Kinetostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { // include boundary nodes applicationNodes_ = new FluxBoundaryNodes(atc_); @@ -1809,9 +1919,12 @@ namespace ATC { // special set of boundary elements for boundary flux quadrature if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION) && (atomicRegulator_->use_localized_lambda())) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } @@ -1848,6 +1961,25 @@ namespace ATC { KinetostatGlcFs::apply_post_corrector(dt); } + //-------------------------------------------------------- + // add_to_momentum: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the kinetostat + //-------------------------------------------------------- + void KinetostatFlux::add_to_momentum(const DENS_MAT & nodalLambdaForce, + DENS_MAT & deltaMomentum, + double dt) + { + deltaMomentum.resize(nNodes_,nsd_); + const DENS_MAT & boundaryFlux(boundaryFlux_[VELOCITY].quantity()); + for (int i = 0; i < nNodes_; i++) { + for (int j = 0; j < nsd_; j++) { + deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j); + } + } + } + //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations @@ -1879,25 +2011,6 @@ namespace ATC { } } - //-------------------------------------------------------- - // add_to_momentum: - // determines what if any contributions to the - // finite element equations are needed for - // consistency with the kinetostat - //-------------------------------------------------------- - void KinetostatFlux::add_to_momentum(const DENS_MAT & nodalLambdaForce, - DENS_MAT & deltaMomentum, - double dt) - { - deltaMomentum.resize(nNodes_,nsd_); - const DENS_MAT & boundaryFlux(boundaryFlux_[VELOCITY].quantity()); - for (int i = 0; i < nNodes_; i++) { - for (int j = 0; j < nsd_; j++) { - deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j); - } - } - } - //-------------------------------------------------------- // reset_filtered_ghost_force: // resets the kinetostat generated ghost force to a @@ -1918,7 +2031,7 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFluxGhost::KinetostatFluxGhost(Kinetostat * kinetostat, + KinetostatFluxGhost::KinetostatFluxGhost(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatFlux(kinetostat,regulatorPrefix) { @@ -1934,7 +2047,7 @@ namespace ATC { { KinetostatFlux::construct_transfers(); if (!nodalGhostForce_) { - throw ATC_Error("StressFluxGhost::StressFluxGhost - ghost atoms must be specified"); + throw ATC_Error("KinetostatFluxGhost::KinetostatFluxGhost - ghost atoms must be specified"); } } @@ -1999,11 +2112,10 @@ namespace ATC { // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFixed::KinetostatFixed(Kinetostat * kinetostat, + KinetostatFixed::KinetostatFixed(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatGlcFs(kinetostat,regulatorPrefix), - mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), - isFirstTimestep_(true) + filterCoefficient_(1.) { // do nothing } @@ -2050,6 +2162,15 @@ namespace ATC { // reset data to zero deltaFeMomentum_.reset(nNodes_,nsd_); deltaNodalAtomicMomentum_.reset(nNodes_,nsd_); + + // initialize filtered energy + TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + nodalAtomicMomentumFiltered_ = nodalAtomicMomentum_->quantity(); + } + + // timestep factor + dtFactor_ = 0.5; } //-------------------------------------------------------- @@ -2074,31 +2195,37 @@ namespace ATC { void KinetostatFixed::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"RegulatedNodes"); - if (!atomicRegulator_->use_localized_lambda()) { - regulatedNodes_ = new RegulatedNodes(atc_); - } - else if (kinetostat_->coupling_mode() == Kinetostat::FLUX) { - regulatedNodes_ = new FixedNodes(atc_); - } - else if (kinetostat_->coupling_mode() == Kinetostat::FIXED) { - // include boundary nodes - regulatedNodes_ = new FixedBoundaryNodes(atc_); - } - else { - throw ATC_Error("ThermostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); - } + if (!regulatedNodes_) { + if (!atomicRegulator_->use_localized_lambda()) { + regulatedNodes_ = new RegulatedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == Kinetostat::FLUX) { + regulatedNodes_ = new FixedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == Kinetostat::FIXED) { + // include boundary nodes + regulatedNodes_ = new FixedBoundaryNodes(atc_); + } + else { + throw ATC_Error("KinetostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); + } - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"RegulatedNodes"); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"RegulatedNodes"); + } applicationNodes_ = regulatedNodes_; // special set of boundary elements for defining regulated atoms if (atomicRegulator_->use_localized_lambda()) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } @@ -2134,51 +2261,8 @@ namespace ATC { } //-------------------------------------------------------- - // apply_pre_predictor: - // apply the kinetostat to the atoms in the - // pre-predictor integration phase - //-------------------------------------------------------- - void KinetostatFixed::apply_pre_predictor(double dt) - { - // initialize values to be track change in finite element energy over the timestep - initialize_delta_nodal_atomic_momentum(dt); - initialFeMomentum_ = -1.*((mdMassMatrix_.quantity())*(velocity_.quantity())); // initially stored as negative for efficiency - - KinetostatGlcFs::apply_pre_predictor(dt); - } - - //-------------------------------------------------------- - // apply_post_corrector: - // apply the kinetostat to the atoms in the - // post-corrector integration phase - //-------------------------------------------------------- - void KinetostatFixed::apply_post_corrector(double dt) - { - KinetostatGlcFs::apply_post_corrector(dt); - - // update filtered momentum with lambda force - DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); - timeFilter_->apply_post_step2(nodalAtomicMomentumFiltered_.set_quantity(), - myNodalAtomicLambdaForce,dt); - - if (halve_force()) { - // Halve lambda force due to fixed temperature constraints - // 1) makes up for poor initial condition - // 2) accounts for possibly large value of lambda when atomic shape function values change - // from eulerian mapping after more than 1 timestep - // avoids unstable oscillations arising from - // thermostat having to correct for error introduced in lambda changing the - // shape function matrices - *lambda_ *= 0.5; - } - - isFirstTimestep_ = false; - } - - //-------------------------------------------------------- - // compute_kinetostat - // manages the solution and application of the - // kinetostat equations and variables + // compute_lambda + // sets up and solves linear system for lambda //-------------------------------------------------------- void KinetostatFixed::compute_lambda(double dt) { @@ -2194,27 +2278,65 @@ namespace ATC { } //-------------------------------------------------------- - // set_kinetostat_rhs - // sets up the RHS of the kinetostat equations - // for the coupling parameter lambda + // apply_pre_predictor: + // apply the kinetostat to the atoms in the + // pre-predictor integration phase //-------------------------------------------------------- - void KinetostatFixed::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + void KinetostatFixed::apply_pre_predictor(double dt) { - // for essential bcs (fixed nodes) : - // form rhs : (delUpsV - delUps)/dt - const set & regulatedNodes(regulatedNodes_->quantity()); - double factor = (1./dt); - for (int i = 0; i < nNodes_; i++) { - if (regulatedNodes.find(i) != regulatedNodes.end()) { - for (int j = 0; j < nsd_; j++) { - rhs(i,j) = factor*(deltaNodalAtomicMomentum_(i,j) - deltaFeMomentum_(i,j)); - } - } - else { - for (int j = 0; j < nsd_; j++) { - rhs(i,j) = 0.; - } - } + // initialize values to be track change in finite element energy over the timestep + initialize_delta_nodal_atomic_momentum(dt); + initialFeMomentum_ = -1.*((mdMassMatrix_.quantity())*(velocity_.quantity())); // initially stored as negative for efficiency + + KinetostatGlcFs::apply_pre_predictor(dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the kinetostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFixed::apply_pre_corrector(double dt) + { + // do full prediction if we just redid the shape functions + if (full_prediction()) { + _tempNodalAtomicMomentumFiltered_ = nodalAtomicMomentumFiltered_.quantity(); + } + + KinetostatGlcFs::apply_pre_corrector(dt); + + if (full_prediction()) { + // reset temporary variables + nodalAtomicMomentumFiltered_ = _tempNodalAtomicMomentumFiltered_; + } + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the kinetostat to the atoms in the + // post-corrector integration phase + //-------------------------------------------------------- + void KinetostatFixed::apply_post_corrector(double dt) + { + + bool halveForce = halve_force(); + + KinetostatGlcFs::apply_post_corrector(dt); + + // update filtered momentum with lambda force + DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + timeFilter_->apply_post_step2(nodalAtomicMomentumFiltered_.set_quantity(), + myNodalAtomicLambdaForce,dt); + + if (halveForce) { + // Halve lambda force due to fixed temperature constraints + // 1) makes up for poor initial condition + // 2) accounts for possibly large value of lambda when atomic shape function values change + // from eulerian mapping after more than 1 timestep + // avoids unstable oscillations arising from + // thermostat having to correct for error introduced in lambda changing the + // shape function matrices + *lambda_ *= 0.5; } } @@ -2243,4 +2365,144 @@ namespace ATC { } } } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the RHS of the kinetostat equations + // for the coupling parameter lambda + //-------------------------------------------------------- + void KinetostatFixed::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // for essential bcs (fixed nodes) : + // form rhs : (delUpsV - delUps)/dt + const set & regulatedNodes(regulatedNodes_->quantity()); + double factor = (1./dt); + for (int i = 0; i < nNodes_; i++) { + if (regulatedNodes.find(i) != regulatedNodes.end()) { + for (int j = 0; j < nsd_; j++) { + rhs(i,j) = factor*(deltaNodalAtomicMomentum_(i,j) - deltaFeMomentum_(i,j)); + } + } + else { + for (int j = 0; j < nsd_; j++) { + rhs(i,j) = 0.; + } + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetostatFluxFixed + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetostatFluxFixed::KinetostatFluxFixed(AtomicRegulator * kinetostat, + bool constructKinetostats) : + RegulatorMethod(kinetostat), + kinetostatFlux_(NULL), + kinetostatFixed_(NULL), + kinetostatBcs_(NULL) + { + if (constructKinetostats) { + if (kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) { + kinetostatFlux_ = new KinetostatFluxGhost(kinetostat,regulatorPrefix_+"Flux"); + } + else { + kinetostatFlux_ = new KinetostatFlux(kinetostat,regulatorPrefix_+"Flux"); + } + kinetostatFixed_ = new KinetostatFixed(kinetostat,regulatorPrefix_+"Fixed"); + + // need to choose BC type based on coupling mode + if (kinetostat->coupling_mode() == AtomicRegulator::FLUX || kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) { + kinetostatBcs_ = kinetostatFlux_; + } + else if (kinetostat->coupling_mode() == AtomicRegulator::FIXED) { + kinetostatBcs_ = kinetostatFixed_; + } + else { + throw ATC_Error("KinetostatFluxFixed::constructor - invalid kinetostat type provided"); + } + } + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + KinetostatFluxFixed::~KinetostatFluxFixed() + { + if (kinetostatFlux_) delete kinetostatFlux_; + if (kinetostatFixed_) delete kinetostatFixed_; + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void KinetostatFluxFixed::construct_transfers() + { + kinetostatFlux_->construct_transfers(); + kinetostatFixed_->construct_transfers(); + } + + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void KinetostatFluxFixed::initialize() + { + kinetostatFlux_->initialize(); + kinetostatFixed_->initialize(); + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_pre_predictor(double dt) + { + kinetostatFlux_->apply_pre_predictor(dt); + kinetostatFixed_->apply_pre_predictor(dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_pre_corrector(double dt) + { + kinetostatFlux_->apply_pre_corrector(dt); + if (kinetostatFixed_->full_prediction()) { + atc_->set_fixed_nodes(); + } + kinetostatFixed_->apply_pre_corrector(dt); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_post_corrector(double dt) + { + kinetostatFlux_->apply_post_corrector(dt); + atc_->set_fixed_nodes(); + kinetostatFixed_->apply_post_corrector(dt); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void KinetostatFluxFixed::output(OUTPUT_LIST & outputData) + { + kinetostatFlux_->output(outputData); + kinetostatFixed_->output(outputData); + } + }; diff --git a/lib/atc/Kinetostat.h b/lib/atc/Kinetostat.h index 1840034296..3d9b8cd5d2 100644 --- a/lib/atc/Kinetostat.h +++ b/lib/atc/Kinetostat.h @@ -58,7 +58,7 @@ namespace ATC { public: - KinetostatShapeFunction(Kinetostat *kinetostat, + KinetostatShapeFunction(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatShapeFunction(){}; @@ -73,8 +73,8 @@ namespace ATC { virtual void set_weights(); // member data - /** pointer to thermostat object for data */ - Kinetostat * kinetostat_; + /** MD mass matrix */ + DIAG_MAN & mdMassMatrix_; /** pointer to a time filtering object */ TimeFilter * timeFilter_; /** stress induced by lambda */ @@ -110,7 +110,7 @@ namespace ATC { public: - GlcKinetostat(Kinetostat *kinetostat); + GlcKinetostat(AtomicRegulator *kinetostat); virtual ~GlcKinetostat(){}; @@ -134,10 +134,6 @@ namespace ATC { double weight = 1.){}; // member data - - /** MD mass matrix */ - DIAG_MAN & mdMassMatrix_; - /** nodeset corresponding to Hoover coupling */ std::set > hooverNodes_; @@ -161,7 +157,7 @@ namespace ATC { public: - DisplacementGlc(Kinetostat * kinetostat); + DisplacementGlc(AtomicRegulator * kinetostat); virtual ~DisplacementGlc(){}; @@ -227,7 +223,7 @@ namespace ATC { public: - DisplacementGlcFiltered(Kinetostat * kinetostat); + DisplacementGlcFiltered(AtomicRegulator * kinetostat); virtual ~DisplacementGlcFiltered(){}; @@ -270,7 +266,7 @@ namespace ATC { public: - VelocityGlc(Kinetostat * kinetostat); + VelocityGlc(AtomicRegulator * kinetostat); virtual ~VelocityGlc(){}; @@ -302,6 +298,8 @@ namespace ATC { virtual void apply_pre_filtering(double dt); /** sets up and solves kinetostat equations */ virtual void compute_kinetostat(double dt); + /** applies kinetostat correction to atoms */ + virtual void apply_kinetostat(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ @@ -339,7 +337,7 @@ namespace ATC { public: - VelocityGlcFiltered(Kinetostat * kinetostat); + VelocityGlcFiltered(AtomicRegulator * kinetostat); virtual ~VelocityGlcFiltered(){}; @@ -382,7 +380,7 @@ namespace ATC { public: - StressFlux(Kinetostat * kinetostat); + StressFlux(AtomicRegulator * kinetostat); virtual ~StressFlux(); @@ -464,7 +462,7 @@ namespace ATC { public: - StressFluxGhost(Kinetostat * kinetostat); + StressFluxGhost(AtomicRegulator * kinetostat); virtual ~StressFluxGhost() {}; @@ -504,7 +502,7 @@ namespace ATC { public: - StressFluxFiltered(Kinetostat * kinetostat); + StressFluxFiltered(AtomicRegulator * kinetostat); virtual ~StressFluxFiltered(){}; @@ -545,7 +543,7 @@ namespace ATC { public: - KinetostatGlcFs(Kinetostat *kinetostat, + KinetostatGlcFs(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatGlcFs(){}; @@ -559,12 +557,18 @@ namespace ATC { /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); + /* flag for performing the full lambda prediction calculation */ + bool full_prediction(); + protected: // methods @@ -590,9 +594,6 @@ namespace ATC { /* sets up and solves the linear system for lambda */ virtual void compute_lambda(double dt); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes() = 0; - // member data /** reference to AtC FE velocity */ DENS_MAN & velocity_; @@ -600,16 +601,27 @@ namespace ATC { /** nodal atomic momentum */ DENS_MAN * nodalAtomicMomentum_; + /** hack to determine if first timestep has been passed */ + bool isFirstTimestep_; + + /** local version of velocity used as predicted final veloctiy */ + PerAtomQuantity * atomPredictedVelocities_; + + /** predicted nodal atomic momentum */ + AtfShapeFunctionRestriction * nodalAtomicPredictedMomentum_; + + /** FE momentum change from kinetostat forces */ + DENS_MAT deltaMomentum_; + /** right-hand side data for thermostat equation */ DENS_MAT rhs_; - /** mapping from all to regulated nodes */ - DENS_MAT rhsMap_; + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; // workspace DENS_MAT _lambdaForceOutput_; // force applied by lambda in output format DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied - DENS_MAT _deltaMomentum_; // FE velocity change from kinetostat private: @@ -628,7 +640,7 @@ namespace ATC { public: - KinetostatFlux(Kinetostat *kinetostat, + KinetostatFlux(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFlux(){}; @@ -689,7 +701,7 @@ namespace ATC { public: - KinetostatFluxGhost(Kinetostat *kinetostat, + KinetostatFluxGhost(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFluxGhost(){}; @@ -728,7 +740,7 @@ namespace ATC { public: - KinetostatFixed(Kinetostat *kinetostat, + KinetostatFixed(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFixed(){}; @@ -742,9 +754,16 @@ namespace ATC { /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[VELOCITY] = 0.;}; + /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return atomicRegulator_->use_localized_lambda();}; @@ -776,9 +795,6 @@ namespace ATC { virtual void construct_regulated_nodes(); // member data - /** MD mass matrix */ - DIAG_MAN & mdMassMatrix_; - /** change in FE momentum over a timestep */ DENS_MAT deltaFeMomentum_; @@ -794,8 +810,11 @@ namespace ATC { /** filtered nodal atomic momentum */ DENS_MAN nodalAtomicMomentumFiltered_; - /** hack to determine if first timestep has been passed */ - bool isFirstTimestep_; + /** coefficient to account for effect of time filtering on rhs terms */ + double filterCoefficient_; + + // workspace + DENS_MAT _tempNodalAtomicMomentumFiltered_; // stores filtered momentum change in atoms for persistence during predictor private: @@ -804,6 +823,60 @@ namespace ATC { }; + /** + * @class KinetostatFluxFixed + * @brief Class for kinetostatting using the velocity matching constraint one one set of nodes and the flux matching constraint on another + */ + + class KinetostatFluxFixed : public RegulatorMethod { + + public: + + KinetostatFluxFixed(AtomicRegulator * kinetostat, + bool constructThermostats = true); + + virtual ~KinetostatFluxFixed(); + + /** instantiate all needed data */ + virtual void construct_transfers(); + + /** pre-run initialization of method data */ + virtual void initialize(); + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); + + /** compute boundary flux, requires kinetostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {kinetostatBcs_->compute_boundary_flux(fields);}; + + protected: + + // data + /** kinetostat for imposing the fluxes */ + KinetostatFlux * kinetostatFlux_; + + /** kinetostat for imposing fixed nodes */ + KinetostatFixed * kinetostatFixed_; + + /** pointer to whichever kinetostat should compute the flux, based on coupling method */ + KinetostatGlcFs * kinetostatBcs_; + + private: + + // DO NOT define this + KinetostatFluxFixed(); + }; + } #endif diff --git a/lib/atc/LammpsInterface.cpp b/lib/atc/LammpsInterface.cpp index 565a3c9872..78859aec6f 100644 --- a/lib/atc/LammpsInterface.cpp +++ b/lib/atc/LammpsInterface.cpp @@ -219,6 +219,27 @@ void LammpsInterface::sparse_allsum(SparseMatrix &toShare) const } #endif +std::string LammpsInterface::read_file(std::string filename) const +{ + FILE *fp = NULL; + if (! comm_rank()) { + fp = fopen(filename.c_str(),"r"); + if (!fp) throw ATC_Error("can't open file: "+filename); + } + const int MAXLINE = 256; + const int CHUNK = 1024; + char * buffer = new char[CHUNK*MAXLINE]; + std::stringstream s; + bool eof = false; + while ( ! eof) { + eof = lammps_->comm->read_lines_from_file(fp,1,MAXLINE,buffer); + s << buffer; + } + delete [] buffer; + return s.str(); // can't copy the stream itself +} + + // ----------------------------------------------------------------- // atom interface methods // ----------------------------------------------------------------- diff --git a/lib/atc/LammpsInterface.h b/lib/atc/LammpsInterface.h index e306a37aa1..238e6cff34 100644 --- a/lib/atc/LammpsInterface.h +++ b/lib/atc/LammpsInterface.h @@ -7,9 +7,11 @@ #include #include #include +#include #include #include "mpi.h" #include "lammps.h" +#include "comm.h" #include "modify.h" #include "memory.h" #include "random_park.h" @@ -91,6 +93,14 @@ class LammpsInterface { SI }; + // Enumeration for atom data style + enum AtomStyle { + UNKNOWN_STYLE=0, + ATOMIC_STYLE, + CHARGE_STYLE, + FULL_STYLE + }; + // Provides a struct for easily passing/recovering data about SparseMats struct SparseMatInfo { INDEX rows; @@ -212,11 +222,15 @@ class LammpsInterface { MPI_Wrappers::send(lammps_->world, send_buf, send_size); } void allgatherv(double * send_buf, int send_count, - double * rec_buf, int * rec_counts, int * displacements) const + double * rec_buf, int * rec_counts, int * displacements) const { MPI_Wrappers::allgatherv(lammps_->world, send_buf, send_count, rec_buf, rec_counts, displacements); } + void int_allgather(int send, int* recv) + { + MPI_Wrappers::int_allgather(lammps_->world,send,recv); + } void int_scatter(int * send_buf, int * rec_buf, int count = 1) { MPI_Wrappers::int_scatter(lammps_->world, send_buf, rec_buf, count); @@ -233,6 +247,16 @@ class LammpsInterface { { MPI_Wrappers::stop(lammps_->world, msg); } + std::string read_file(std::string filename) const; + void write_file(std::string filename, std::string contents, + std::ofstream::openmode mode = std::ofstream::out) const { + if (! comm_rank()) { + std::ofstream f(filename.c_str(),mode); + f << contents; + f.close(); + } + // ignore other ranks and assume they are consistent + } // end MPI -------------------------------------------------------------------- void print_debug(std::string msg="") const diff --git a/lib/atc/LinearSolver.cpp b/lib/atc/LinearSolver.cpp index c6e1e9cf75..ea31a64a74 100644 --- a/lib/atc/LinearSolver.cpp +++ b/lib/atc/LinearSolver.cpp @@ -41,7 +41,8 @@ LinearSolver::LinearSolver( matrix_(A), matrixDense_(), matrixFreeFree_(), matrixFreeFixed_(),matrixInverse_(), - penalty_(0),maxIterations_(0), maxRestarts_(0), tol_(0), + penalty_(1), + maxIterations_(0), maxRestarts_(0), tol_(0), parallel_(parallel) { // deep copy @@ -70,7 +71,8 @@ LinearSolver::LinearSolver( matrix_(A), matrixDense_(), matrixFreeFree_(), matrixFreeFixed_(),matrixInverse_(), - penalty_(0),maxIterations_(0), maxRestarts_(0), tol_(0), + penalty_(1), + maxIterations_(0), maxRestarts_(0), tol_(0), parallel_(parallel) { // shallow copy @@ -84,7 +86,6 @@ LinearSolver::LinearSolver( // -------------------------------------------------------------------- void LinearSolver::setup(void) { - penalty_ = kPenalty; // relative to matrix diagonal tol_ = kTol; nVariables_ = matrix_.nRows(); maxIterations_=2*nVariables_; @@ -106,8 +107,7 @@ void LinearSolver::setup(void) if (solverType_ == DIRECT_SOLVE) constraintHandlerType_ = CONDENSE_CONSTRAINTS; } if ( solverType_ == DIRECT_SOLVE && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) allowReinitialization_ = true; - if ( solverType_ == ITERATIVE_SOLVE_SYMMETRIC && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) - throw ATC_Error("LinearSolver::unimplemented method"); + if ( solverType_ == ITERATIVE_SOLVE_SYMMETRIC && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) { throw ATC_Error("LinearSolver::unimplemented method"); } } // -------------------------------------------------------------------- @@ -217,6 +217,7 @@ void LinearSolver::initialize_rhs(void) // -------------------------------------------------------------------- void LinearSolver::add_matrix_penalty(void) { + penalty_ = kPenalty; // relative to matrix diagonal SPAR_MAT & A = matrixCopy_; penalty_ *= (A.diag()).maxabs(); BC_SET::const_iterator itr; diff --git a/lib/atc/LinearSolver.h b/lib/atc/LinearSolver.h index eb84976c36..5f456b9a1d 100644 --- a/lib/atc/LinearSolver.h +++ b/lib/atc/LinearSolver.h @@ -34,6 +34,7 @@ class LinearSolver { }; enum LinearSolveConstraintHandlingType { + AUTO_HANDLE_CONSTRAINTS=-1, NO_CONSTRAINTS=0, CONDENSE_CONSTRAINTS, PENALIZE_CONSTRAINTS diff --git a/lib/atc/MPI_Wrappers.cpp b/lib/atc/MPI_Wrappers.cpp index d69620ecb9..9468644c53 100644 --- a/lib/atc/MPI_Wrappers.cpp +++ b/lib/atc/MPI_Wrappers.cpp @@ -183,6 +183,15 @@ namespace MPI_Wrappers { if (error != MPI_SUCCESS) throw ATC_Error("error in allgatherv "+to_string(error)); } + void int_allgather(MPI_Comm comm, int send, int* recv) + { + int send_count = 1; + int recv_count = 1; + int error = MPI_Allgather(&send, send_count, MPI_INT, + recv, recv_count, MPI_INT, comm); + if (error != MPI_SUCCESS) throw ATC_Error("error in allgatherv "+to_string(error)); + } + void logical_or(MPI_Comm comm, void *send_buf, int *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_LOR, diff --git a/lib/atc/MPI_Wrappers.h b/lib/atc/MPI_Wrappers.h index 691c9d5250..37f484c6a9 100644 --- a/lib/atc/MPI_Wrappers.h +++ b/lib/atc/MPI_Wrappers.h @@ -30,6 +30,7 @@ namespace MPI_Wrappers { void allgatherv(MPI_Comm comm, double *send_buf, int send_count, double *rec_buf, int *rec_counts, int *displacements); void gather(MPI_Comm comm, double send, double * recv); + void int_allgather(MPI_Comm comm, int send, int* recv); void logical_or(MPI_Comm comm, void *send_buf, int *rec_buf, int count = 1); void barrier(MPI_Comm comm); void stop(MPI_Comm comm, std::string msg=""); diff --git a/lib/atc/Material.cpp b/lib/atc/Material.cpp index eaeb7c9754..22273de2ef 100644 --- a/lib/atc/Material.cpp +++ b/lib/atc/Material.cpp @@ -86,6 +86,25 @@ namespace ATC { electronRecombinationInvTau_(0), electronChargeDensity_(NULL) { + /*! \page man_material material + \section syntax + material \n + \n + end \n + tag - a unique identifier for the material type which can be referenced in input decks. Multiple materials are specified using different tag regions, terminated with an 'end', in the material file. + units - the LAMMPS units system the material is based on, used as a check against the actual LAMMPS units. AtC units are consistent units using the LAMMPS length, mass, time, charge, and volts. The only units conversion occuring within AtC are LAMMPS to AtC units and charge to volts units. + \section examples + material Argon real + ------- + end + \section description + Starts a section in which material properties can be specified. Materials are organized by material, identified by a tag, and all associated material models are specified within its scope. Unspecified material properties use defaults as indicated or are considered as null. Null material properties contribute no value to integrals using them. Material properties defined which are not part of the physics model are ignored. Functions which are specified correspond to those implemented in the code and there is no mechanism for user-specified material models unless they are added to the main code.\n + \section restrictions + Material models are only used for evaluating finite element integrals with for physics models they are associated with. + \section related + \section default + Default for all material properties is null. The null material using the tag 'null' is the only material defined by default. \n + */ linearFlux_.reset(NUM_FIELDS); linearFlux_ = false; linearSource_.reset(NUM_FIELDS); @@ -114,6 +133,20 @@ namespace ATC { return; } } + /*! \page man_mat_heat_capacity material heat_capcity + \section syntax + heat_capacity constant\n + capacity \n + end \n + \section description + Overrides use of lattice heat capacity using Dulong-Petit law for continuum regions. \n + \section restrictions + Only valid with AtC models incorporating a phonon temperature: thermal, two-temperature, drift-diffusion + \section related + material + \section default + If no value is given, the Dulong-Petit value for the lattice is used. \n + */ if (line[0] == "heat_capacity") { // over-ride default registry_. insert("heat_capacity"); registry_. insert("thermal_energy"); @@ -130,6 +163,20 @@ namespace ATC { } } } + /*! \page man_mat_heat_flux material heat_flux + \section syntax + heat_flux linear\n + conductivity \n + end \n + \section description + Specifies a heat flux proportional to the temperature gradient. \n + \section restrictions + Only valid with AtC models incorporating a phonon temperature: thermal, two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "heat_flux") { registry_. insert("heat_flux"); if (line[1] == "linear") { @@ -145,6 +192,24 @@ namespace ATC { } } } + /*! \page man_mat_electron_heat_flux material electron_heat_flux + \section syntax + electron_heat_flux \n + \n + end \n + null - no electron heat flux contributions \n + linear - a heat flux proportional to the temperature gradient, parameter is 'conductivity'\n + power_law - a heat flux proportional to the temperature gradient and ratio of electron to phonon temperatures, parameter is 'conductivity'\n + thermopower - same as power_law but with an addition proportional to the electron current, parameters are 'conductivity' but it also uses the Seebeck coefficient defined elsewhere + \section description + Specifies the form for the electron heat flux. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_heat_flux") { registry_. insert("electron_heat_flux"); if (line[1] == "null") { @@ -183,6 +248,23 @@ namespace ATC { parameters_, electronFlux_); } } + /*! \page man_mat_electron_heat_capacity material electron_heat_capacity + \section syntax + electron_heat_capacity \n + capacity \n + end \n + no_density - if this keyword is present, the electron density does not multiply the capacity\n + constant - a constant electron heat flux \n + linear - a heat flux proportional to the electron temperature\n + \section description + Specifies the form for the electron heat capacity. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_heat_capacity") { registry_. insert("electron_heat_capacity"); registry_. insert("electron_thermal_energy"); @@ -214,6 +296,24 @@ namespace ATC { } } } + /*! \page man_mat_electron_phonon_exchange material electron_phonon_exchange + \section syntax + electron_phonon_exchange \n + \n + end \n + null - no electron heat flux contributions \n + linear - an energy exchange proportional to the temperature difference between the electron and phonon temperatures, parameter is 'coefficient'\n + power_law - same as linear, but the temperature difference is raised to a specified power, parameters are 'coefficient' and 'exponent'\n + hertel - exchange proportional to temperature difference to the 5th divided by the electron temperature, the coefficient is a function of the mass enhancement and Debeye temperature, parameters are 'debeye_temperature' and 'mass_enhancement' + \section description + Specifies the form for the electron/phonon heat exchange. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_phonon_exchange") { registry_. insert("electron_phonon_exchange"); if (line[1] == "null") { @@ -239,6 +339,23 @@ namespace ATC { electronPhononExchange_ = new ElectronPhononExchangeHertel(fileId,parameters_,this); } } + /*! \page man_mass_density material mass_density + \section syntax + mass_density \n + \n + end \n + no entry - compute mass density from the lattice using the mass of the first type, no keyword or values\n + basis - compute mass density for the given number of atoms of each type in the lattice, no keyword, values are one integer per type specifying the number of atoms of that type in the lattice\n + constant - prescribed mass density, keyword = density, value = desired mass density + \section description + Specifies the mass density of the system. \n + \section restrictions + Valid for all AtC physics models. + \section related + material + \section default + Compute from the basis. \n + */ else if (line[0] == "mass_density") { // over-ride default registry_. insert("mass_density"); registry_. insert("kinetic_energy"); @@ -280,6 +397,25 @@ namespace ATC { } } } + /*! \page man_mat_stress material stress + \section syntax + stress \n + \n + end \n + null - no electron heat flux contributions \n + linear - a stress tensor proportional to the displacements, keywords are 'modulus' and 'poissons_ratio'\n + cubic - an anisotropic linear stress tensor, keywords are 'c11', 'c12', and 'c44'\n + damped_cubic - same as cubic, with a damping term proportional to the velocity vector, keywords are 'c11', 'c12', 'c44', and the damping parameter 'gamma'\n + cauchy_born - stress tensor is computed using the Cauchy-Born formalism from the lattice and given potential, keywords are 'pairstyle', 'linear' (linearizes the Cauchy-Born relationship), or 'temperature' (the temperature used to determine the Cauchy-Born stress). The 'pairstyle' lines are followed by values of 'lj/cut', 'lj/smooth/linear', and 'eam', the latter two of which are followed on the line by the value for the cut-off radius. The 'lj/cut' and 'lj/smooth/linear' pairstyles are followed on the next line using the keyword 'pair_coeff' followed by value of the pair-coefficients \sigma and \epsilon. + \section description + Specifies the form for the mechanical stress tensor. \n + \section restrictions + Only valid with AtC models incorporating a mechanical stress: elastic + \section related + material + \section default + Null. \n + */ else if (line[0] == "stress") { registry_. insert("stress"); registry_. insert("elastic_energy"); @@ -321,6 +457,22 @@ namespace ATC { viscousStress_ = new ViscousStressConstant(fileId); } } + /*! \page man_body_force material body_force + \section syntax + body_force \n + \n + end \n + electric_field - adds body force proportional to the electric field and charge density, no keywords or values\n + viscous - adds a body force proportional to the velocity vector, keyword = gamma (damping parameter) followed by its value\n + \section description + Specifies body forces acting on the system. \n + \section restrictions + Valid for all AtC mechanical models: elastic + \section related + material + \section default + Null. \n + */ else if (line[0] == "body_force") { registry_. insert("body_force"); if (line.size() > 1) { @@ -363,6 +515,21 @@ namespace ATC { electronFlux_ = new ElectronFluxConvection(fileId, parameters_); } } + /*! \page man_electric_field material electric_field + \section syntax + electric_field linear\n + permittivity \n + end \n + Provide a value for the permittivity or use LAMMPS' value if no value is given.\n + \section description + Specifies the electric displacement vector to be proportional to the electric field. \n + \section restrictions + Valid for AtC physics models using electric fields: fem_efield, drift-diffusion + \section related + material + \section default + Use LAMMPS' permittivity. \n + */ else if (line[0] == "electric_field") { registry_. insert("electric_field"); registry_. insert("electric_displacement"); @@ -383,9 +550,11 @@ namespace ATC { } // convert relative permitivity (dielectric) to abs internal units stringstream ss; - ss << "permittivity : " << permittivity_ ; + ss << "permittivity: relative= " << permittivity_ ; permittivity_ *= LammpsInterface::instance()->epsilon0(); - ss << " -> " << permittivity_ ; + ss << ", absolute= " << permittivity_ ; + ss << ", lattice constant= " << LammpsInterface::instance()->max_lattice_constant() ; + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); LammpsInterface::UnitsType utype = LammpsInterface::instance()->units_style(); if ( utype != LammpsInterface::REAL && utype != LammpsInterface::METAL) { @@ -454,6 +623,25 @@ namespace ATC { } } } + /*! \page man_mat_electron_density material electron_density + \section syntax + electron_density \n + \n + end \n + null - no electron density constitutive model, uses the state variable \n + linear - density is proportional to the electric field, keyword is 'coefficient' followed by its value\n + interpolation - interpolates in a look-up table contained in a file provided after the 'interpolation' word, keywords are 'scale' and 'number_of_points', followed by their values \n + exponential - density is based on Boltzmann statistics for the electric potential above an activation energy, keywords are 'intrinsic_concentration', 'intrinsic_energy', and reference_temperature', followed by their values\n + fermi_dirac - density is based on Fermi-Dirac statistics for the electric potential relative to an activation energy, keywords are 'fermi_energy', 'reference_temperature', 'band_edge', 'donor_ionization_energy', and 'donor_concentration' + \section description + Specifies the form for the electron density. \n + \section restrictions + Only valid with AtC models incorporating an electrons: electrostatic, two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_density") { // density is converted to charge registry_. insert("electron_charge_density"); if (line[1] == "null") { diff --git a/lib/atc/Matrix.h b/lib/atc/Matrix.h index 179bea52b9..076ed18676 100644 --- a/lib/atc/Matrix.h +++ b/lib/atc/Matrix.h @@ -21,12 +21,12 @@ public: virtual ~Matrix() {} //* stream output functions - void print(std::ostream &o) const { o << to_string(); } - void print(std::ostream &o, const std::string &name) const; + void print(std::ostream &o, int p=myPrecision) const { o << this->to_string(p); } + void print(std::ostream &o, const std::string &name, int p=myPrecision) const; friend std::ostream& operator<<(std::ostream &o, const Matrix &m){m.print(o); return o;} void print() const; - virtual void print(const std::string &name) const; - virtual std::string to_string() const; + virtual void print(const std::string &name, int p = myPrecision) const; + virtual std::string to_string(int p = myPrecision) const; // element by element operations DenseMatrix operator/(const Matrix& B) const; @@ -144,7 +144,7 @@ public: virtual bool check_range(T min, T max) const; protected: - virtual void _set_equal(const Matrix &r); + virtual void _set_equal(const Matrix &r) = 0; }; //* Matrix operations @@ -286,16 +286,14 @@ void MultAB(const Matrix &A, const Matrix &B, DenseMatrix &C, //----------------------------------------------------------------------------- //* output operator template -std::string Matrix::to_string() const +std::string Matrix::to_string(int p) const { std::string s; - for (INDEX i=0; i::to_string() const //----------------------------------------------------------------------------- //* output operator that wraps the matrix in a nice labeled box template -void Matrix::print(std::ostream &o, const std::string &name) const +void Matrix::print(std::ostream &o, const std::string &name, int p) const { o << "------- Begin "<print(o); + this->print(o,p); o << "\n------- End "<::print() const //----------------------------------------------------------------------------- //* named print operator, use cout by default template -void Matrix::print(const std::string &name) const +void Matrix::print(const std::string &name, int p) const { - print(std::cout, name); + print(std::cout, name, p); } //----------------------------------------------------------------------------- //* element by element division @@ -648,24 +646,6 @@ Matrix& Matrix::operator=(const Matrix &r) this->_set_equal(r); return *this; } -//---------------------------------------------------------------------------- -// general matrix assignment (for densely packed matrices) -//---------------------------------------------------------------------------- -template -void Matrix::_set_equal(const Matrix &r) -{ - this->resize(r.nRows(), r.nCols()); - const Matrix *pr = &r; - if (const SparseMatrix *ps = sparse_cast(pr)) - copy_sparse_to_matrix(ps, *this); - - else if (diag_cast(pr)) // r is Diagonal? - { - this->zero(); - for (INDEX i=0; iptr(), r.ptr(), r.size()*sizeof(T)); -} //----------------------------------------------------------------------------- //* sets all elements to a constant template diff --git a/lib/atc/MeshReader.cpp b/lib/atc/MeshReader.cpp index 0ecb8f6254..a50a195847 100644 --- a/lib/atc/MeshReader.cpp +++ b/lib/atc/MeshReader.cpp @@ -45,6 +45,9 @@ namespace ATC { /** destructor */ MeshReader::~MeshReader() { + if (conn_) delete conn_; + if (nodeCoords_) delete nodeCoords_; + if (nodeSets_) delete nodeSets_; } /** creates handle to new mesh object */ diff --git a/lib/atc/OutputManager.cpp b/lib/atc/OutputManager.cpp index 586b4294f0..414c5173e7 100644 --- a/lib/atc/OutputManager.cpp +++ b/lib/atc/OutputManager.cpp @@ -57,8 +57,8 @@ OutputManager::OutputManager(string outputPrefix, set & otypes) fullTextOutput_(otypes.count(FULL_GNUPLOT)), vtkOutput_(otypes.count(VTK)), tensorToComponents_(false), // paraview does not support tensors - vectorToComponents_(false) - + vectorToComponents_(false), + warnTooManyCols_(true) {} //----------------------------------------------------------------------------- //* @@ -461,9 +461,10 @@ void OutputManager::write_data_ensight(string field_name, const MATRIX *field_da if (use_component_names(type)){ nfiles = ndof; if (nfiles > kFileNameSize) { - if (ATC::LammpsInterface::instance()->rank_zero()) { + if (warnTooManyCols_ && ATC::LammpsInterface::instance()->rank_zero()) { + warnTooManyCols_ = false; stringstream ss; - ss << " only writing " << kFileNameSize + ss << "WARNING: only writing " << kFileNameSize << " components of " << field_name << " which has " << ndof; ATC::LammpsInterface::instance()->print_msg(ss.str()); } diff --git a/lib/atc/OutputManager.h b/lib/atc/OutputManager.h index f63780b77c..c5a901e9f4 100644 --- a/lib/atc/OutputManager.h +++ b/lib/atc/OutputManager.h @@ -138,6 +138,8 @@ namespace ATC { bool tensorToComponents_; /** output vector as its components */ bool vectorToComponents_; + /** warn once flags */ + bool warnTooManyCols_; /** global variables */ std::map globalData_; }; diff --git a/lib/atc/POEMSChain.h b/lib/atc/POEMSChain.h new file mode 100644 index 0000000000..7dc143a9d9 --- /dev/null +++ b/lib/atc/POEMSChain.h @@ -0,0 +1,73 @@ +/* + *_________________________________________________________________________* + * POEMS: PARALLELIZABLE OPEN SOURCE EFFICIENT MULTIBODY SOFTWARE * + * DESCRIPTION: SEE READ-ME * + * FILE NAME: PoemsChain.h * + * AUTHORS: See Author List * + * GRANTS: See Grants List * + * COPYRIGHT: (C) 2005 by Authors as listed in Author's List * + * LICENSE: Please see License Agreement * + * DOWNLOAD: Free at www.rpi.edu/~anderk5 * + * ADMINISTRATOR: Prof. Kurt Anderson * + * Computational Dynamics Lab * + * Rensselaer Polytechnic Institute * + * 110 8th St. Troy NY 12180 * + * CONTACT: anderk5@rpi.edu * + *_________________________________________________________________________*/ + +#ifndef POEMSCHAIN_H_ +#define POEMSCHAIN_H_ + +#include "poemslist.h" + +struct ChildRingData { + List * childRing; + int entranceNodeId; +}; + +struct POEMSChain{ + ~POEMSChain(){ + for(int i = 0; i < childChains.GetNumElements(); i++) + { + delete childChains(i); + } + } + //void printTreeStructure(int tabs); + //void getTreeAsList(List * temp); + List listOfNodes; + List childChains; + POEMSChain * parentChain; + List childRings; + + + void printTreeStructure(int tabs){ + for(int i = 0; i < tabs; i++) + { + cout << "\t"; + } + cout << "Chain: "; + for(int i = 0; i < listOfNodes.GetNumElements(); i++) + { + cout << *(listOfNodes(i)) << " "; + } + cout << endl; + for(int i = 0; i < childChains.GetNumElements(); i++) + { + childChains(i)->printTreeStructure(tabs + 1); + } + } + void getTreeAsList(List * temp) + { + for(int i = 0; i < listOfNodes.GetNumElements(); i++) + { + int * integer = new int; + *integer = *(listOfNodes(i)); + temp->Append(integer); + } + for(int i = 0; i < childChains.GetNumElements(); i++) + { + childChains(i)->getTreeAsList(temp); + } + } +}; +#endif diff --git a/lib/atc/PerAtomQuantityLibrary.cpp b/lib/atc/PerAtomQuantityLibrary.cpp index c7f57fd723..36f9252451 100644 --- a/lib/atc/PerAtomQuantityLibrary.cpp +++ b/lib/atc/PerAtomQuantityLibrary.cpp @@ -719,7 +719,7 @@ namespace ATC { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, - atomType); + atomType); if (!atomMasses_) atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); @@ -835,7 +835,7 @@ namespace ATC { LammpsInterface::ATOM_VELOCITY, atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); @@ -980,6 +980,7 @@ namespace ATC { } } } + //-------------------------------------------------------- //-------------------------------------------------------- // Class TwiceFluctuatingKineticEnergy @@ -1009,7 +1010,7 @@ namespace ATC { atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); @@ -1144,7 +1145,7 @@ namespace ATC { atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); @@ -1428,7 +1429,7 @@ namespace ATC { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, - atomType); + atomType); atomVelocities_->register_dependence(this); } @@ -1923,17 +1924,12 @@ namespace ATC { PerAtomQuantity * atomLambdas, AtomType atomType) : ProtectedAtomQuantity(atc,1,atomType), - atomMasses_(NULL), atomLambdas_(atomLambdas) { InterscaleManager & interscaleManager(atc->interscale_manager()); - atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, - atomType); if (!atomLambdas) { atomLambdas_ = interscaleManager.per_atom_quantity("AtomLambdaEnergy"); } - - atomMasses_->register_dependence(this); atomLambdas_->register_dependence(this); } @@ -1942,7 +1938,6 @@ namespace ATC { //-------------------------------------------------------- AtomicVelocityRescaleFactor::~AtomicVelocityRescaleFactor() { - atomMasses_->remove_dependence(this); atomLambdas_->remove_dependence(this); } @@ -1953,11 +1948,143 @@ namespace ATC { { if (need_reset()) { PerAtomQuantity::reset(); - const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & lambda(atomLambdas_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { - quantity_(i,0) = sqrt(lambda(i,0)/mass(i,0)); + quantity_(i,0) = sqrt(lambda(i,0)); + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicFluctuatingVelocityRescaled + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // constructor + //-------------------------------------------------------- + AtomicFluctuatingVelocityRescaled::AtomicFluctuatingVelocityRescaled(ATC_Method * atc, + PerAtomQuantity * atomRescaleFactor, + PerAtomQuantity * atomFluctuatingVelocity, + AtomType atomType) : + ProtectedAtomQuantity(atc,atc->nsd(),atomType), + atomRescaleFactor_(atomRescaleFactor), + atomFluctuatingVelocity_(atomFluctuatingVelocity) + { + InterscaleManager & interscaleManager(atc->interscale_manager()); + if (!atomRescaleFactor_) { + atomRescaleFactor_ = interscaleManager.per_atom_quantity("AtomVelocityRescaling"); + } + if (!atomFluctuatingVelocity_) { + atomFluctuatingVelocity_ = interscaleManager.per_atom_quantity("AtomicFluctuatingVelocity"); + } + + atomRescaleFactor_->register_dependence(this); + atomFluctuatingVelocity_->register_dependence(this); + } + + //-------------------------------------------------------- + // destructor + //-------------------------------------------------------- + AtomicFluctuatingVelocityRescaled::~AtomicFluctuatingVelocityRescaled() + { + atomRescaleFactor_->remove_dependence(this); + atomFluctuatingVelocity_->remove_dependence(this); + } + + //-------------------------------------------------------- + // reset + //-------------------------------------------------------- + void AtomicFluctuatingVelocityRescaled::reset() const + { + if (need_reset()) { + PerAtomQuantity::reset(); + const DENS_MAT & factor(atomRescaleFactor_->quantity()); + const DENS_MAT & v(atomFluctuatingVelocity_->quantity()); + + for (int i = 0; i < quantity_.nRows(); i++) { + for (int j = 0; j < quantity_.nCols(); j++) { + quantity_(i,j) = factor(i,0)*v(i,j); + } + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicCombinedRescaleThermostatError + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // constructor + //-------------------------------------------------------- + AtomicCombinedRescaleThermostatError::AtomicCombinedRescaleThermostatError(ATC_Method * atc, + PerAtomQuantity * atomFluctuatingMomentumRescaled, + PerAtomQuantity * atomMeanVelocity, + PerAtomQuantity * atomStreamingVelocity, + PerAtomQuantity * atomMass, + AtomType atomType) : + ProtectedAtomQuantity(atc,1,atomType), + atomFluctuatingMomentumRescaled_(atomFluctuatingMomentumRescaled), + atomMeanVelocity_(atomMeanVelocity), + atomStreamingVelocity_(atomStreamingVelocity), + atomMass_(atomMass) + { + InterscaleManager & interscaleManager(atc->interscale_manager()); + if (!atomFluctuatingMomentumRescaled_) { + atomFluctuatingMomentumRescaled_ = interscaleManager.per_atom_quantity("AtomFluctuatingMomentumRescaled"); + } + if (!atomMeanVelocity_) { + atomMeanVelocity_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); + } + if (!atomStreamingVelocity_) { + atomStreamingVelocity_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); + } + if (!atomMass_) { + atomMass_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, + atomType); + } + + atomFluctuatingMomentumRescaled_->register_dependence(this); + atomMeanVelocity_->register_dependence(this); + atomStreamingVelocity_->register_dependence(this); + atomMass_->register_dependence(this); + } + + //-------------------------------------------------------- + // destructor + //-------------------------------------------------------- + AtomicCombinedRescaleThermostatError::~AtomicCombinedRescaleThermostatError() + { + atomFluctuatingMomentumRescaled_->remove_dependence(this); + atomMeanVelocity_->remove_dependence(this); + atomStreamingVelocity_->remove_dependence(this); + atomMass_->remove_dependence(this); + } + + //-------------------------------------------------------- + // reset + //-------------------------------------------------------- + void AtomicCombinedRescaleThermostatError::reset() const + { + if (need_reset()) { + PerAtomQuantity::reset(); + const DENS_MAT & m(atomMass_->quantity()); + const DENS_MAT & p(atomFluctuatingMomentumRescaled_->quantity()); + const DENS_MAT & v(atomMeanVelocity_->quantity()); + const DENS_MAT & s(atomStreamingVelocity_->quantity()); + + double diff; + for (int i = 0; i < quantity_.nRows(); i++) { + diff = s(i,0)-v(i,0); + quantity_(i,0) = 2.*p(i,0)*diff + m(i,0)*diff*diff; + for (int j = 1; j < p.nCols(); j++) { + diff = s(i,j)-v(i,j); + quantity_(i,0) += 2.*p(i,j)*diff + m(i,0)*diff*diff; + } } } } diff --git a/lib/atc/PerAtomQuantityLibrary.h b/lib/atc/PerAtomQuantityLibrary.h index e6847ef6af..dbc1f16e29 100644 --- a/lib/atc/PerAtomQuantityLibrary.h +++ b/lib/atc/PerAtomQuantityLibrary.h @@ -1382,9 +1382,6 @@ namespace ATC { /** handles resetting of data */ virtual void reset() const; - /** atomic masses */ - FundamentalAtomQuantity * atomMasses_; - /** atomic lambdas */ PerAtomQuantity * atomLambdas_; @@ -1395,6 +1392,86 @@ namespace ATC { }; + /** + * @class AtomicFluctuatingVelocityRescaled + * @brief Class for computing the atomic rescaling of the velocity fluctuations by the rescaling thermostat + */ + + class AtomicFluctuatingVelocityRescaled : public ProtectedAtomQuantity { + + public: + + // constructor + AtomicFluctuatingVelocityRescaled(ATC_Method * atc, + PerAtomQuantity * atomRescaleFactor = NULL, + PerAtomQuantity * atomFluctuatingVelocity = NULL, + AtomType atomType = INTERNAL); + + // destructor + virtual ~AtomicFluctuatingVelocityRescaled(); + + protected: + + /** handles resetting of data */ + virtual void reset() const; + + /** atomic rescaling factor */ + PerAtomQuantity * atomRescaleFactor_; + + /** atomic fluctuating velocity */ + PerAtomQuantity * atomFluctuatingVelocity_; + + private: + + // do not define + AtomicFluctuatingVelocityRescaled(); + + }; + + /** + * @class AtomicCombinedRescaleThermostatError + * @brief Class for computing the atomic error in the rescaling thermostat when used in combination with a specified streaming velocity + */ + + class AtomicCombinedRescaleThermostatError : public ProtectedAtomQuantity { + + public: + + // constructor + AtomicCombinedRescaleThermostatError(ATC_Method * atc, + PerAtomQuantity * atomFluctuatingMomentumRescaled = NULL, + PerAtomQuantity * atomMeanVelocity = NULL, + PerAtomQuantity * atomStreamingVelocity = NULL, + PerAtomQuantity * atomMass = NULL, + AtomType atomType = INTERNAL); + + // destructor + virtual ~AtomicCombinedRescaleThermostatError(); + + protected: + + /** handles resetting of data */ + virtual void reset() const; + + /** atomic rescaled fluctuating momentum */ + PerAtomQuantity * atomFluctuatingMomentumRescaled_; + + /** atomic mean (prolonged FE) velocity */ + PerAtomQuantity * atomMeanVelocity_; + + /** atomic streaming velocity, as computed by rescaling kinetothermostat */ + PerAtomQuantity * atomStreamingVelocity_; + + /** atomic masses */ + PerAtomQuantity * atomMass_; + + private: + + // do not define + AtomicCombinedRescaleThermostatError(); + + }; + /** * @class AtomicThermostatForce * @brief Class for computing the atomic force induced by the GLC-based thermostats diff --git a/lib/atc/PoissonSolver.cpp b/lib/atc/PoissonSolver.cpp index a5b4668e58..b3c077b593 100644 --- a/lib/atc/PoissonSolver.cpp +++ b/lib/atc/PoissonSolver.cpp @@ -51,6 +51,7 @@ PoissonSolver::PoissonSolver( rhsMask_(fieldName,FLUX) = true; rhsMask_(fieldName,SOURCE) = true; } + if (prescribedDataMgr_->has_robin_source(fieldName)) { @@ -114,6 +115,9 @@ void PoissonSolver::initialize(void) { nNodes_ = feEngine_->num_nodes(); + if (atc_->source_atomic_quadrature(fieldName_)) + integrationType_ = FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE; + // compute penalty for Dirichlet boundaries if (prescribedDataMgr_->none_fixed(fieldName_)) throw ATC_Error("Poisson solver needs Dirichlet data"); @@ -125,6 +129,7 @@ void PoissonSolver::initialize(void) pair row_col(fieldName_,fieldName_); Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(fieldName_,FLUX) = true; + if (prescribedDataMgr_->has_robin_source(fieldName_)) { rhsMask(fieldName_,ROBIN_SOURCE) = true; } @@ -132,7 +137,7 @@ void PoissonSolver::initialize(void) atc_->compute_rhs_tangent(row_col, rhsMask, atc_->fields(), stiffness_, FULL_DOMAIN, physicsModel_); // create solver - solver_ = new LinearSolver(stiffness_,bcs,solverType_,-1,parallel_); + solver_ = new LinearSolver(stiffness_,bcs,solverType_,LinearSolver::AUTO_HANDLE_CONSTRAINTS,parallel_); } else { diff --git a/lib/atc/PrescribedDataManager.cpp b/lib/atc/PrescribedDataManager.cpp index 8d27f7c211..bd649c8ace 100644 --- a/lib/atc/PrescribedDataManager.cpp +++ b/lib/atc/PrescribedDataManager.cpp @@ -47,6 +47,8 @@ namespace ATC { elementSources_[thisField](ielem,idof) = NULL; } } + // node based sources + nodalSources_[thisField].reset(nNodes_,thisSize); } } @@ -113,7 +115,6 @@ namespace ATC { const int thisIndex, const XT_Function * f) { - using std::set; set nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); set::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { @@ -130,7 +131,6 @@ namespace ATC { const int thisIndex, const XT_Function * f) { - using std::set; // fix fields set::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { @@ -144,7 +144,6 @@ namespace ATC { const int thisIndex, const XT_Function * f) { - using std::set; set nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); fix_field(nodeSet,thisField,thisIndex,f); } @@ -156,7 +155,6 @@ namespace ATC { const FieldName thisField, const int thisIndex) { - using std::set; set nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); set::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { @@ -269,6 +267,36 @@ namespace ATC { } } //------------------------------------------------------------------------- +// fix_open +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_open + (const string facesetName, + const FieldName thisField) + { + const set< pair > * fset + = & ( (feEngine_->fe_mesh())->faceset(facesetName)); + set< pair >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair face = *iset; + facesOpen_[thisField].insert(face); // uniquely insert this face + } + } +//------------------------------------------------------------------------- +// unfix_open +//------------------------------------------------------------------------- + void PrescribedDataManager::unfix_open + (const string facesetName, + const FieldName thisField) + { + const set< pair > * fset + = & ( (feEngine_->fe_mesh())->faceset(facesetName)); + set< pair >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair face = *iset; + facesOpen_[thisField].erase(face); + } + } +//------------------------------------------------------------------------- // fix_source //------------------------------------------------------------------------- void PrescribedDataManager::fix_source @@ -277,7 +305,6 @@ namespace ATC { const int thisIndex, const XT_Function *f) { - using std::set; set elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); set::const_iterator iset; for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { @@ -287,6 +314,23 @@ namespace ATC { } } //------------------------------------------------------------------------- +// fix_source +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_source + (const FieldName thisField, + const int thisIndex, + const set > & s) + { + set >::const_iterator iset; + DENS_MAT & n(nodalSources_[thisField].set_quantity()); + for (iset = s.begin(); iset != s.end(); iset++) { + pair p = *iset; + int inode = p.first; + double v = p.second; + n(inode,thisIndex) = v; + } + } +//------------------------------------------------------------------------- // unfix_source //------------------------------------------------------------------------- void PrescribedDataManager::unfix_source @@ -294,7 +338,6 @@ namespace ATC { const FieldName thisField, const int thisIndex) { - using std::set; set elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); set::const_iterator iset; for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { @@ -457,6 +500,7 @@ namespace ATC { // compute internal sources feEngine_->add_sources(fieldMask,t,elementSources_,sources); + feEngine_->add_sources(fieldMask,t,nodalSources_,sources); // mask out nodes with essential bcs for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { diff --git a/lib/atc/PrescribedDataManager.h b/lib/atc/PrescribedDataManager.h index 1801360a8c..05f553ab2d 100644 --- a/lib/atc/PrescribedDataManager.h +++ b/lib/atc/PrescribedDataManager.h @@ -71,6 +71,14 @@ namespace ATC { /** */ const std::map > * robin_functions(FieldName fieldName) { return & faceSourcesRobin_[fieldName]; } + /** */ + OPEN_SURFACE * open_faces(void) { return & facesOpen_; } + bool has_open_face(FieldName fieldName) const { + return ((facesOpen_.find(fieldName)->second).size() > 0) ; + } + /** */ + const std::set * + open_faces(FieldName fieldName) { return & facesOpen_[fieldName]; } /** query initial state */ bool is_initially_fixed(const int node, @@ -134,7 +142,8 @@ namespace ATC { const int thisIndex=0) const { return (fixed_nodes(thisField,thisIndex).size() == 0 ) - && (faceSourcesRobin_.size() == 0); + && (faceSourcesRobin_.size() == 0) + && (facesOpen_.size() == 0); } /** */ @@ -319,12 +328,19 @@ namespace ATC { void unfix_robin(const std::string facesetName, const FieldName thisField, const int thisIndex); + void fix_open (const std::string facesetName, + const FieldName thisField); + void unfix_open(const std::string facesetName, + const FieldName thisField); /** un/set sources */ - void fix_source(const std::string nodesetName, + void fix_source(const std::string elemsetName, const FieldName thisField, const int thisIndex, const XT_Function * f); - void unfix_source(const std::string nodesetName, + void fix_source( const FieldName thisField, + const int thisIndex, + const std::set > & source); + void unfix_source(const std::string elemsetName, const FieldName thisField, const int thisIndex); /** get initial conditions */ @@ -381,8 +397,12 @@ namespace ATC { /** sources : UXT_Function * f = faceSourcesRobin_[field][face](idof) */ std::map < FieldName, std::map < std::pair , Array < UXT_Function * > > > faceSourcesRobin_; + /** sources : facesOpen_[field][face] */ + std::map < FieldName, std::set < std::pair > > + facesOpen_; /** sources : XT_Function * f = elementSources_[field](ielem,idof) */ std::map < FieldName, Array2D < XT_Function * > > elementSources_; + FIELDS nodalSources_; /** values of bcs in a compact set */ std::map < FieldName, BCS > bcValues_; diff --git a/lib/atc/SchrodingerSolver.cpp b/lib/atc/SchrodingerSolver.cpp index 191ddbb466..0bbfde8e2b 100644 --- a/lib/atc/SchrodingerSolver.cpp +++ b/lib/atc/SchrodingerSolver.cpp @@ -8,11 +8,15 @@ #include "LinearSolver.h" #include "PoissonSolver.h" +#include "Utility.h" #include - using std::pair; using std::set; +using std::stringstream; +using std::min; +using ATC_Utility::to_string; +using ATC_Utility::sgn; const double tol = 1.e-8; const double zero_tol = 1.e-12; @@ -20,27 +24,30 @@ const double f_tol = 1.e-8; namespace ATC { -enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; +enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX, ONED_GLOBAL_FLUX}; + + + + double fermi_dirac(const double E, const double T) { double f = 1.0; - if (T > 0) f = 1.0 / ( exp(E/kBeV_/T)+1.0 ); + if (T > 0) f = 1.0 / ( exp(E/(kBeV_*T))+1.0 ); else if (E > 0) f = 0; return f; }; - //-------------------------------------------------------- + //======================================================== // Schrodinger solve - //-------------------------------------------------------- + //======================================================== SchrodingerSolver::SchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, - const int solverType, bool parallel ) : atc_(atc), @@ -48,24 +55,18 @@ double fermi_dirac(const double E, const double T) prescribedDataMgr_(prescribedDataMgr), physicsModel_(physicsModel), fieldName_(fieldName), - solver_(NULL), - solverType_(solverType), nNodes_(atc->num_nodes()), parallel_(parallel) { } - SchrodingerSolver::~SchrodingerSolver() - { - if (solver_) delete solver_; - } - + //----------------------------------------------------- void SchrodingerSolver::initialize() { SPAR_MAT sparseM; atc_->fe_engine()->compute_mass_matrix(sparseM); M_ = sparseM.dense_copy(); } - + //----------------------------------------------------- bool SchrodingerSolver::solve(FIELDS & fields) { @@ -106,9 +107,9 @@ double fermi_dirac(const double E, const double T) return true; } - //-------------------------------------------------------- + //======================================================== // Schrodinger solve on slices - //-------------------------------------------------------- + //======================================================== SliceSchrodingerSolver::SliceSchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, @@ -116,22 +117,22 @@ double fermi_dirac(const double E, const double T) const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, const Array< set > & oneDslices, - const int solverType, + const Array< double > & oneDdxs, bool parallel ) : SchrodingerSolver(fieldName, physicsModel, feEngine, prescribedDataMgr, - atc, solverType, parallel), - oneDslices_(oneDslices) - { - } - SliceSchrodingerSolver::~SliceSchrodingerSolver() - { - } + atc, parallel), + oneDslices_(oneDslices), + oneDdxs_(oneDdxs) + {} + //-------------------------------------------------------- void SliceSchrodingerSolver::initialize() { SchrodingerSolver::initialize(); } - + //-------------------------------------------------------- + // compute charge density per slice + //-------------------------------------------------------- bool SliceSchrodingerSolver::solve(FIELDS & fields) { // fields @@ -144,7 +145,6 @@ double fermi_dirac(const double E, const double T) DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); DENS_MAT & n = (atc_->field(ELECTRON_DENSITY)).set_quantity(); DENS_MAT & T = (atc_->field(ELECTRON_TEMPERATURE)).set_quantity(); - // stiffness = K + V M SPAR_MAT stiffness_; @@ -177,9 +177,10 @@ double fermi_dirac(const double E, const double T) const BC_SET & bc = bcs[0]; int nfixed = bc.size(); if (nfixed != snodes) { + // A: solve for e-values and wavefunctions K.map(slice,slice,K1); M_.map(slice,slice,M1); - LinearSolver eigensolver(K1,bc,LinearSolver::AUTO_SOLVE,-1,parallel_); + LinearSolver eigensolver(K1,bc,LinearSolver::AUTO_SOLVE,-1); // wave functions evals1.reset(snodes,1); evecs1.reset(snodes,snodes); @@ -190,7 +191,7 @@ double fermi_dirac(const double E, const double T) eindex.clear(); for (int j = 0; j < snodes; j++) eindex.insert(j); eVecs.insert(slice,eindex,evecs1); - // electron density + // slice charge density n1.reset(snodes,1); set::const_iterator iset; @@ -200,44 +201,56 @@ double fermi_dirac(const double E, const double T) aveE_f += Ef(gnode,0); } aveE_f /= snodes; - +//#define VERBOSE +#ifdef VERBOSE + stringstream ss; + ss << " slice "+to_string(islice+1)+" E_f "+to_string(aveE_f) << "\n" + << "#-----------------------------------------------\n" + << "# E-Ef f psi n\n" + << "#-----------------------------------------------\n"; +#endif + // B: compute charge density on slice int node = 0; for (iset = slice.begin(); iset != slice.end(); iset++) { // node int gnode = *iset; double temp = T(gnode,0); - //double E_f = Ef(gnode,0); for (int mode = 0; mode < snodes-nfixed; mode++) { double Ei = evals1(mode,0); double E = Ei-aveE_f; double f = fermi_dirac(E,temp); - if (f < f_tol) break; // take advantage of E ordering double psi1 = evecs1(node,mode); // 2nd index corresp to evals order +#ifdef VERBOSE + ss << node<<":"<print_msg_once(ss.str()); +#endif n.insert(slice,one, n1); // note not "assemble" } } return true; } - //-------------------------------------------------------- + //======================================================== // Schrodinger-Poisson Manager - //-------------------------------------------------------- + //======================================================== SchrodingerPoissonManager::SchrodingerPoissonManager() : maxConsistencyIter_(0), maxConstraintIter_(0), oneD_(false), oneDconserve_(ONED_FLUX), Ef_shift_(0.), - safe_dEf_(0.) + safe_dEf_(0.), + tol_(1.e-10), + mu_(1.),D_(0.) { } - SchrodingerPoissonManager::~SchrodingerPoissonManager() - { - } - + //---------------------------------------------------------- bool SchrodingerPoissonManager::modify(int narg, char **arg) { bool match = false; @@ -250,8 +263,9 @@ double fermi_dirac(const double E, const double T) else if (strcmp(arg[argIndx],"conserve")==0) { oneD_ = true; argIndx++; - if (strcmp(arg[argIndx],"density")==0) oneDconserve_ = ONED_DENSITY; - else oneDconserve_ = ONED_FLUX; + if (strcmp(arg[argIndx],"density")==0) oneDconserve_ = ONED_DENSITY; + else if (strcmp(arg[argIndx],"flux")==0) oneDconserve_ = ONED_FLUX; + else oneDconserve_ = ONED_GLOBAL_FLUX; argIndx++; maxConstraintIter_ = atoi(arg[argIndx]); match = true; @@ -266,9 +280,29 @@ double fermi_dirac(const double E, const double T) safe_dEf_ = atof(arg[argIndx]); match = true; } + else if (strcmp(arg[argIndx],"relaxation")==0) { + argIndx++; + alpha_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"tolerance")==0) { + argIndx++; + tol_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"mobility")==0) { + argIndx++; + mu_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"diffusivity")==0) { + argIndx++; + D_ = atof(arg[argIndx]); + match = true; + } return match; } - + //---------------------------------------------------------------- SchrodingerPoissonSolver * SchrodingerPoissonManager::initialize( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, @@ -278,9 +312,17 @@ double fermi_dirac(const double E, const double T) { SchrodingerPoissonSolver * ptr; if (oneD_) { + if (oneDconserve_ == ONED_GLOBAL_FLUX) { + ptr = new GlobalSliceSchrodingerPoissonSolver(atc, + schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter_, + maxConstraintIter_, oneDconserve_, Ef_shift_, alpha_, safe_dEf_, tol_, + mu_,D_); + } + else { ptr = new SliceSchrodingerPoissonSolver(atc, schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter_, maxConstraintIter_, oneDconserve_, Ef_shift_, safe_dEf_); + } } else { ptr = new SchrodingerPoissonSolver(atc, @@ -289,9 +331,9 @@ double fermi_dirac(const double E, const double T) return ptr; } - //------------------------------------------------------------------- + //=================================================================== // SchrodingerPoissonSolver - //------------------------------------------------------------------- + //=================================================================== SchrodingerPoissonSolver::SchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, @@ -307,9 +349,7 @@ double fermi_dirac(const double E, const double T) nNodes_(atc_->num_nodes()) { } - SchrodingerPoissonSolver::~SchrodingerPoissonSolver(void) - { - } + //---------------------------------------------------------------------- void SchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) { @@ -328,7 +368,6 @@ double fermi_dirac(const double E, const double T) DENS_MAT Te0 = Te; // save const double tol = 1.e-4; -// double Tmax = Te.max(); int k = 0; double logRatio = 3; @@ -399,9 +438,9 @@ double fermi_dirac(const double E, const double T) } } - //---------------------------------------------------------------------------- - // SchrodingerPoissonSolver - //------------------------------------------------------------------- + //=================================================================== + // SliceSchrodingerPoissonSolver + //=================================================================== SliceSchrodingerPoissonSolver::SliceSchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, @@ -414,18 +453,17 @@ double fermi_dirac(const double E, const double T) double safe_dEf ) : SchrodingerPoissonSolver(atc,schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter), - maxConstraintIter_(maxConstraintIter), oneDconserve_(oneDconserve), oneDcoor_(0), - Ef_shift_(Ef_shift), - safe_dEf_(safe_dEf), - oneDslices_(((SliceSchrodingerSolver *) schrodingerSolver_)->slices()) + oneDslices_(((SliceSchrodingerSolver *) schrodingerSolver_)->slices()), + oneDdxs_(((SliceSchrodingerSolver *) schrodingerSolver_)->dxs()) { + Ef_shift_=Ef_shift; + safe_dEf_=safe_dEf; + maxConstraintIter_=maxConstraintIter; EfHistory_.reset(oneDslices_.size(),2); } - SliceSchrodingerPoissonSolver::~SliceSchrodingerPoissonSolver(void) - { - } + //-------------------------------------------------------------------------- void SliceSchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) { const double tol = 1.e-4; // tolerance on consistency & constraint @@ -443,6 +481,7 @@ double fermi_dirac(const double E, const double T) // target for constraint double target = 0.0; + set & slice = oneDslices_(0); // note assume first slice is fixed if (oneDconserve_ == ONED_FLUX) atc_->set_sources(); DENS_MAT & nSource = (atc_->source(ELECTRON_DENSITY)).set_quantity(); @@ -451,8 +490,14 @@ double fermi_dirac(const double E, const double T) else target += n(*iset,0); } target /= slice.size(); +#ifdef VERBOSE + if (oneDconserve_ == ONED_FLUX) { + if (target > 0) ATC::LammpsInterface::instance()->print_msg_once(" influx target "+ to_string(target)); + else ATC::LammpsInterface::instance()->print_msg_once(" efflux target "+ to_string(target)); + } +#endif - // self consistency loop between Phi and n(psi_i) + // A: self consistency loop between Phi and n(psi_i) double error = 1.0; for (int i = 0; i < maxConsistencyIter_ ; ++i) { atc_->set_fixed_nodes(); @@ -461,16 +506,19 @@ double fermi_dirac(const double E, const double T) if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) { // iterate on Ef //if (i==0) Ef = -1.0*phi;// E ~ -|e| \Phi, charge of electron e = 1 - Ef = -1.0*phi; // E ~ -|e| \Phi, charge of electron e = 1 in eV + Ef = -1.0*phi; + Ef +=Ef_shift_; + // B: conservation constraint for (int j = 0; j < maxConstraintIter_ ; ++j) { - - schrodingerSolver_->solve(atc_->fields()); - + schrodingerSolver_->solve(atc_->fields()); // n(E_f) atc_->set_fixed_nodes(); - error = update_fermi_energy(target,(j==0),fluxes); + error = update_fermi_energy(target,(j==0),fluxes);// root finder +#ifdef VERBOSE + ATC::LammpsInterface::instance()->print_msg_once(to_string(i)+":"+to_string(j)+" constraint_error "+to_string(error)+" / "+to_string(tol*target)+"\n"); +#endif // exit condition based on constraint satisfaction - if (error < tol*target) break; + if (error < tol*fabs(target)) break; } // loop j : flux constraint // error based on change in field (Cauchy convergence) if (i == 0) { @@ -482,6 +530,12 @@ double fermi_dirac(const double E, const double T) norm = dn.norm(); } nPrev = n; +#ifdef VERBOSE +#if 0 + if (i > 0) ATC::LammpsInterface::instance()->print_msg_once(to_string(i)+" density_change: "+to_string(norm)+" / "+to_string(norm0)); + else ATC::LammpsInterface::instance()->print_msg_once("initial norm "+to_string(norm)); +#endif +#endif if (i > 0 && norm <= tol*norm0 && error < tol) break; } } // loop i : self consistency @@ -495,65 +549,407 @@ double fermi_dirac(const double E, const double T) (double target, bool first, GRAD_FIELD_MATS & fluxes) { DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); - double safe_dEf = safe_dEf_; - DENS_MAT & n = (atc_->field(ELECTRON_DENSITY)).set_quantity(); + DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); const DENS_MAT * y = &n; if (oneDconserve_ == ONED_FLUX) { // compute J_x Array2D rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = true; +//#define WIP_REJ atc_->compute_flux(rhsMask,atc_->fields_,fluxes,physicsModel_); y = & ( fluxes[ELECTRON_DENSITY][oneDcoor_] ); } - BCS bcs; double error = 0; // slice for (int islice = 0; islice < oneDslices_.size(); islice++) { +#ifdef VERBOSE + std::string cStr(" conserved "); + std::string Estr(" Ef"); +#endif set & slice = oneDslices_(islice); int nSlice = slice.size(); - //atc_->prescribedDataMgr_->bcs(ELECTRON_DENSITY,slice,bcs,true); atc_->prescribedDataMgr_->bcs(ELECTRON_WAVEFUNCTION,slice,bcs,true); const BC_SET & bc = bcs[0]; int nFixed = bc.size(); - if (nFixed == nSlice) continue; + if (nFixed == nSlice) continue; // skip if all fixed double Y = 0.0, X = 0.0; -double nave = 0.0; + double nAve = 0., phiAve = 0.; for (set::const_iterator iset = slice.begin(); iset != slice.end(); iset++) { int gnode = *iset; X += Ef(gnode,0); Y += (*y)(gnode,0); -nave += n(gnode,0); + nAve += n(gnode,0); + phiAve += phi(gnode,0); } X /= nSlice; Y /= nSlice; -nave /= nSlice; - + nAve /= nSlice; + phiAve /= nSlice; + // now adjust Ef for each slice double dY = Y - EfHistory_(islice,0); double dX = X - EfHistory_(islice,1); - if (fabs(dY) < zero_tol*dX) throw ATC_Error("zero increment in conserved field on slice"); double err = target - Y; if (target*Y < -zero_tol*target) { - //throw ATC_Error("target and quantity opposite signs"); - ATC::LammpsInterface::instance()->print_msg_once("WARNING: target and quantity opposite signs"); +#ifdef VERBOSE + cStr = " opp. SIGNS"; +#else + ATC::LammpsInterface::instance()->print_msg_once("WARNING: slice "+to_string(islice)+" target and quantity opposite signs "+to_string(Y)); +#endif } error += fabs(err); - //error = max(error,err); - double dEf = err / dY * dX; + double dEf = 0.; if (first) { - dEf = (err < 0) ? -safe_dEf : safe_dEf; + dEf = (err < 0) ? -safe_dEf_ : safe_dEf_; } - else if (fabs(dEf) > safe_dEf) { - dEf = safe_dEf * dEf / fabs(dEf); + else { + if (fabs(dY) < zero_tol*dX) throw ATC_Error("zero increment in conserved field on slice:"+to_string(islice)); + dEf = err / dY * dX; + if (fabs(dEf) > safe_dEf_) { + dEf = safe_dEf_* dEf / fabs(dEf); +#ifdef VERBOSE + Estr = " !!"; +#else + ATC::LammpsInterface::instance()->print_msg_once("WARNING: slice "+to_string(islice)+ " large Delta E_f "+to_string(dEf)); +#endif + } } for (set::const_iterator iset = slice.begin(); iset != slice.end(); iset++) { int gnode = *iset; - Ef(gnode,0) += dEf; + Ef(gnode,0) += dEf; } EfHistory_(islice,0) = Y; EfHistory_(islice,1) = X; + if ( std::isnan(Y) ) throw ATC_Error("target on slice is not a number"); +#ifdef VERBOSE + ATC::LammpsInterface::instance()->print_msg_once(" slice"+to_string(islice,2) +cStr+to_string(4,Y/target) +Estr+to_string(4,X)+" n"+to_string(5,nAve)+" phi"+to_string(4,phiAve)); + //ATC::LammpsInterface::instance()->print_msg_once(" slice "+to_string(islice) +cStr+to_string(4,Y/target) +" E_f"+to_string(4,X)+dEstr+to_string(4,X-EfHistory_(std::max(0,islice-1),1))+" n"+to_string(4,nAve)+" phi"+to_string(4,phiAve)+" "+to_string(nFixed)+" dn "+to_string(4,dnAve)+" dphi "+to_string(4,dphiAve)); +#endif } // loop slice return error; } + //=================================================================== + // GlobalSliceSchrodingerPoissonSolver + //=================================================================== + GlobalSliceSchrodingerPoissonSolver::GlobalSliceSchrodingerPoissonSolver( + /*const*/ ATC_Coupling * atc, + SchrodingerSolver * schrodingerSolver, + PoissonSolver * poissonSolver, + const PhysicsModel * physicsModel, + int maxConsistencyIter, + int maxConstraintIter, + int oneDconserve, + double Ef0, + double alpha, + double safe_dEf, + double tol, + double mu, double D + ) : + SliceSchrodingerPoissonSolver(atc,schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter,maxConstraintIter,oneDconserve,0,0), + solver_(NULL), + mobility_(mu),diffusivity_(D) + { + Ef0_ = Ef0; + alpha_ = alpha; + safe_dEf_ = safe_dEf; + if (safe_dEf_ < 1.e-20) throw ATC_Error("safe dE_f must be positive"); + ATC::LammpsInterface::instance()->print_msg("mobility:"+to_string(mobility_)+" diffusivity:"+to_string(diffusivity_)); + tol_ = tol; + nslices_ = oneDslices_.size(); + sliceSize_ = (oneDslices_(0)).size(); + nNodes_ = nslices_*sliceSize_; + flux_.reset(nNodes_); + J_.reset(nslices_); + //nfixed_ = 2; + nfixed_ = 1; + nfreeSlices_ = nslices_-nfixed_; + nLambda_ = nslices_-1; + lambda_.reset(nLambda_); + dJ_.reset(nLambda_); + F_.reset(nslices_); + Phi_.reset(nslices_); + n_.reset(nslices_); + // form stiffness, lhs dirichlet bc, rhs homogeneous neumann bc + //int m = nfreeSlices_; + int m = nLambda_; + DENS_MAT A(m,m); + for (int i = 1; i < m; ++i) { + A(i,i) = -2; + if (i>0) A(i,i-1) = 1; + if (i gnode = *iset + int k = 0; + set::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + double v = 0.5/dx; + if ( k < sliceSize_ || k+1 > (nslices_-1)*sliceSize_ ) v *=2.0; + if (islice > 0) { C(k,k-sliceSize_) += v; } + else { C(k,k) += v; } + if (islice < nslices_-1) { C(k,k+sliceSize_) -= v; } + else { C(k,k) -= v; } + k++; + } + } + //C.print("2D gradient",4); + SPAR_MAT G2(C); + G2_ = G2; + solver_ = new LinearSolver(K_); // for lambda + rhsMask_.reset(NUM_FIELDS,NUM_FLUX); rhsMask_ = false; + rhsMask_(ELECTRON_DENSITY,FLUX) = true; + + // report + if (nfixed_ ==2) + ATC::LammpsInterface::instance()->print_msg_once("schrodinger-poisson solver: Dirichlet INLET, Dirichlet; OUTLET"); + else if (nfixed_ ==1) + ATC::LammpsInterface::instance()->print_msg_once("schrodinger-poisson solver: Dirichlet INLET, Neumann; OUTLET"); + else + ATC_Error("schrodinger-poisson solver:too many fixed"); + } + GlobalSliceSchrodingerPoissonSolver::~GlobalSliceSchrodingerPoissonSolver(void) { + if (solver_) delete solver_; + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) + { + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + const DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).quantity(); + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); + Ef.reset(phi.nRows(),1); + norm_ = norm0_ = 1.0; + for (int i = 0; i < maxConstraintIter_ ; ++i) { + atc_->set_fixed_nodes(); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) { + poissonSolver_->solve(atc_->fields(),rhs); + } + else { + ATC::LammpsInterface::instance()->print_msg_once("WARNING: phi is fixed"); + } + if (i == 0) { report(0); } + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) { + update_fermi_level(); // update Ef = Ef0 +lambda + schrodingerSolver_->solve(atc_->fields()); // updates n(E_f) + //exponential_electron_density(); // surrogate + compute_flux(n,phi); // compute J(n,phi) & dJ_ + solver_->solve(lambda_,dJ_); // conservation constraint + //lambda_.print("lambda"); + //lambda_.print("[[J}}"); + } + else { + ATC::LammpsInterface::instance()->print_msg_once("WARNING: rho is fixed"); + } + norm_ = dJ_.norm(); + report(i+1); + if (i == 0 && norm_ > tol_) norm0_ = norm_; + else { if (norm_ < tol_*norm0_) break; } + } + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::exponential_electron_density() + { + std::cout << "******************HACK******************\n"; + + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).set_quantity(); + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); + double T = 300; + double n0 = 1.e-2; + set::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + double aveE_f = 0.0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + aveE_f += Ef(gnode,0); + } + aveE_f /= slice.size(); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + //std::cout << phi(gnode,0)+aveE_f << "\n"; + //n(gnode,0) = -n0*exp(-(phi(gnode,0)+aveE_f)/(kBeV_*T)); + //n(gnode,0) = -n0*exp((-phi(gnode,0))/(kBeV_*T)); + //n(gnode,0) = -n0*exp(aveE_f/(kBeV_*T)); + //n(gnode,0) = aveE_f+0.01; + //n(gnode,0) = aveE_f; + //n(gnode,0) = phi(gnode,0); + //n(gnode,0) = -n0*(phi(gnode,0)+aveE_f)/(kBeV_*T); + n(gnode,0) = -n0*(aveE_f)/(kBeV_*T); + } + } + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::report(int i) + { + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + const DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).quantity(); + const DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).quantity(); + set::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + double Phi = 0.0; + double N = 0.0; + double EF = 0.0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Phi += phi(gnode,0); + N += n(gnode,0); + EF += Ef(gnode,0); + } + Phi /= slice.size(); + Phi_(islice) = Phi; // average potential + N /= slice.size(); + n_(islice) = N; // average electron density + EF /= slice.size(); + F_(islice) = EF; // average Fermi level + } + stringstream header; + header << "\n"; + header << "#----------------------------------------------------------------------\n"; + header << "# [[J]] lambda E_f phi n J\n"; + header << "#----------------------------------------------------------------------\n"; + if (i == 0) { + ATC::LammpsInterface::instance()->write_file("slice.dat",header.str()); + } + stringstream ss; + ss << "\n"; + // first slice (fixed E_F) + double dJ0 = J_(1)-J_(0); + ss << to_string(1,2) << "*" << to_string(6,dJ0) << " " << to_string(6,0.) << " " << to_string(6,F_(0)) << " " << to_string(6,Phi_(0)) << " " << to_string(6,n_(0)) << " " << to_string(6,J_(0)) << "\n"; + // interior + for (int j = 1; j < nslices_-1; ++j) { + ss << to_string(j+1,2) << " " << to_string(6,dJ_(j-1)) << " " << to_string(6,lambda_(j-1)) << " " << to_string(6,F_(j)) << " " << to_string(6,Phi_(j)) << " " << to_string(6,n_(j)) << " " << to_string(6,J_(j)) << "\n"; + } + // last slice (fixed E_F) + double dJn = J_(nslices_-1)-J_(nslices_-2); + int j = nslices_-1; + double lambdaN = 0.; + std::string space = "*"; + if (nfixed_ == 1) { + lambdaN = lambda_(nslices_-2); + space = " "; + } + ss << to_string(nslices_,2) << space << to_string(6,dJn) << " " << to_string(6,lambdaN) << " " << to_string(6,F_(j)) << " " << to_string(6,Phi_(j)) << " " << to_string(6,n_(j)) << " " << to_string(6,J_(j)) << "\n"; + stringstream is; + is << "\n# iteration: " << to_string(i)+"/ "+to_string(maxConstraintIter_)+" constraint norm:"+to_string(6,norm_/norm0_) << " " << nslices_ << " slices"; + ATC::LammpsInterface::instance()->print_msg(is.str()+header.str()+ss.str()); + ATC::LammpsInterface::instance()->write_file("slice.dat",ss.str()+is.str()+"\n",std::ofstream::app); + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::compute_flux( + const DENS_MAT & n, const DENS_MAT & phi) + { + DENS_VEC f(nNodes_); + DENS_VEC gradphi(nNodes_); + DENS_VEC gradn(nNodes_); + int k = 0; + set::const_iterator iset; + // grad phi + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + f(k) = phi(gnode,0); + k++; + } + } + //f.print("phi"); + gradphi = G2_*f; + //gradphi.print("grad phi"); + k = 0; + // grad n + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + f(k) = n(gnode,0); + k++; + } + } + //f.print("n"); + gradn = G2_*f; + ////gradn.print("grad n"); + flux_.reset(nNodes_); + for (k = 0; k < nNodes_; k++) { + flux_(k) = -mobility_*f(k)*gradphi(k)-diffusivity_*gradn(k); + } + //flux_.print("flux"); + // per slice flux and diference + k = 0; + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + J_(islice) = 0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + J_(islice) += flux_(k); + k++; + } + J_(islice) /= slice.size(); + //std::cout << islice << " J " << J_(islice) << "\n"; + } + //J_.print("J"); + dJ_ = G_*J_; + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::update_fermi_level() + { + + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY) ).set_quantity(); + DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); + DENS_MAT & n = (atc_->field(ELECTRON_DENSITY) ).set_quantity(); + set::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set & slice = oneDslices_(islice); + double Phi = 0.; + double N = 0.; + //F_(islice) = Ef0_; + if (islice > 0 && islice < nslices_-1) { + F_(islice) += alpha_*lambda_(islice-1); + } + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Phi += phi(gnode,0); + N += n(gnode,0); + } + Phi /= slice.size(); + Phi_(islice) = Phi; // average potential + N /= slice.size(); + n_(islice) = N; // average electron density + + //F_(j) += min(fabs(alpha_*lambda),safe_dEf_)*sgn(lambda); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Ef(gnode,0) = F_(islice); + } + } + //Ef.print("Ef"); + } }; diff --git a/lib/atc/SchrodingerSolver.h b/lib/atc/SchrodingerSolver.h index e190c8eae6..9b8616b458 100644 --- a/lib/atc/SchrodingerSolver.h +++ b/lib/atc/SchrodingerSolver.h @@ -34,12 +34,11 @@ class SchrodingerSolver { const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, - const int solverType = ATC::LinearSolver::DIRECT_SOLVE, - bool parallel = false + bool parallel = true ); /** Destructor */ - virtual ~SchrodingerSolver(); + virtual ~SchrodingerSolver(){}; /** parser */ bool modify(int narg, char **arg){ return false;} @@ -70,24 +69,20 @@ class SchrodingerSolver { /** field to solve for */ FieldName fieldName_; - /** linear solver */ - LinearSolver * solver_; - int solverType_; - /** number of nodes */ int nNodes_; - /** stiffness matrix */ - - //SPAR_MAT stiffness_; - - //SPAR_MAT massMatrix_; + /** mass matrix */ DENS_MAT M_; bool parallel_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger equation on slices + */ class SliceSchrodingerSolver : public SchrodingerSolver { public: @@ -100,12 +95,12 @@ class SliceSchrodingerSolver : public SchrodingerSolver { const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, const Array< std::set > & oneDslices, - const int solverType = ATC::LinearSolver::DIRECT_SOLVE, - bool parallel = false + const Array< double > & oneDdxs, + bool parallel = true ); /** Destructor */ - virtual ~SliceSchrodingerSolver(); + virtual ~SliceSchrodingerSolver(){}; /** parser */ bool modify(int narg, char **arg){return false;} @@ -117,13 +112,19 @@ class SliceSchrodingerSolver : public SchrodingerSolver { virtual bool solve(FIELDS & fields); Array< std::set > & slices(void){ return oneDslices_;} + Array< double > & dxs(void){ return oneDdxs_;} protected: Array< std::set > oneDslices_; + Array< double > oneDdxs_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system + */ class SchrodingerPoissonSolver { public: SchrodingerPoissonSolver( @@ -133,7 +134,7 @@ class SchrodingerPoissonSolver { const PhysicsModel * physicsModel, int maxConsistencyIter ); - virtual ~SchrodingerPoissonSolver(void); + virtual ~SchrodingerPoissonSolver(void){}; virtual void solve( FIELDS & rhs, GRAD_FIELD_MATS & fluxes @@ -144,9 +145,18 @@ class SchrodingerPoissonSolver { PoissonSolver * poissonSolver_; const PhysicsModel * physicsModel_; int maxConsistencyIter_; + int maxConstraintIter_; int nNodes_; + double Ef0_; + double Ef_shift_; + double safe_dEf_; + double tol_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system on slices + */ class SliceSchrodingerPoissonSolver : public SchrodingerPoissonSolver { public: SliceSchrodingerPoissonSolver( @@ -160,27 +170,78 @@ class SliceSchrodingerPoissonSolver : public SchrodingerPoissonSolver { double Ef_shift, double safe_dEf ); - virtual ~SliceSchrodingerPoissonSolver(void); + virtual ~SliceSchrodingerPoissonSolver(void){}; virtual void solve( FIELDS & rhs, GRAD_FIELD_MATS & fluxes ); protected: + int nslices_; double update_fermi_energy(double target, bool first, GRAD_FIELD_MATS & fluxes); - int maxConstraintIter_; int oneDconserve_; int oneDcoor_; - double Ef_shift_; - double safe_dEf_; Array< std::set > & oneDslices_; + Array< double > & oneDdxs_; Array2D EfHistory_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system on slices + */ +class GlobalSliceSchrodingerPoissonSolver : public SliceSchrodingerPoissonSolver { + public: + GlobalSliceSchrodingerPoissonSolver( + /*const*/ ATC_Coupling * atc, + SchrodingerSolver * schrodingerSolver, + PoissonSolver * poissonSolver, + const PhysicsModel * physicsModel, + int maxConsistencyIter, + int maxConstraintIter, + int oneDconserve, + double Ef0, + double alpha, + double safe_dEf, + double tol, + double mu, double D + ); + virtual ~GlobalSliceSchrodingerPoissonSolver(void); + virtual void solve( + FIELDS & rhs, + GRAD_FIELD_MATS & fluxes + ); + protected: + void compute_flux(const DENS_MAT & n, const DENS_MAT & phi); + void update_fermi_level(); + void report(int i); + void exponential_electron_density(); + class LinearSolver * solver_; + double alpha_; + int sliceSize_, nNodes_, nfreeSlices_, nfixed_, nLambda_; + SPAR_MAT K_; + SPAR_MAT G_,G2_; // 1D & 2D grad mats = int N gradN dv + DENS_VEC J_; + DENS_VEC flux_; + DENS_VEC dJ_; + DENS_VEC lambda_; + DENS_VEC F_; + DENS_VEC Phi_; + DENS_VEC n_; + Array2D rhsMask_; + double mobility_; + double diffusivity_; + double norm_, norm0_; +}; + +/** + * @class SchrodingerSolver + * @brief a manager class + */ class SchrodingerPoissonManager { public: SchrodingerPoissonManager(); - ~SchrodingerPoissonManager(); + ~SchrodingerPoissonManager(){}; /** parser */ bool modify(int narg, char **arg); @@ -199,6 +260,9 @@ class SchrodingerPoissonManager { int oneDconserve_; double Ef_shift_; double safe_dEf_; + double alpha_; + double tol_; + double mu_, D_; }; } // namespace ATC #endif diff --git a/lib/atc/SystemProcessor.h b/lib/atc/SystemProcessor.h new file mode 100644 index 0000000000..9ef511f997 --- /dev/null +++ b/lib/atc/SystemProcessor.h @@ -0,0 +1,283 @@ +/* + *_________________________________________________________________________* + * POEMS: PARALLELIZABLE OPEN SOURCE EFFICIENT MULTIBODY SOFTWARE * + * DESCRIPTION: SEE READ-ME * + * FILE NAME: SystemProcessor.h * + * AUTHORS: See Author List * + * GRANTS: See Grants List * + * COPYRIGHT: (C) 2005 by Authors as listed in Author's List * + * LICENSE: Please see License Agreement * + * DOWNLOAD: Free at www.rpi.edu/~anderk5 * + * ADMINISTRATOR: Prof. Kurt Anderson * + * Computational Dynamics Lab * + * Rensselaer Polytechnic Institute * + * 110 8th St. Troy NY 12180 * + * CONTACT: anderk5@rpi.edu * + *_________________________________________________________________________*/ + +#ifndef _SYS_PROCESSOR_H_ +#define _SYS_PROCESSOR_H_ +#include "poemslist.h" +#include "poemstree.h" +#include "POEMSChain.h" + + +struct POEMSNode { + List links; + List taken; + int idNumber; + bool visited; + + ~POEMSNode(){ + for(int i = 0; i < taken.GetNumElements(); i++) + { + delete taken(i); + } + }; +}; + + +class SystemProcessor{ +private: + Tree nodes; +// List forDeletion; + List headsOfSystems; + List > ringsInSystem; + POEMSNode * findSingleLink(TreeNode * aNode); + POEMSChain * AddNewChain(POEMSNode * currentNode); + bool setLinkVisited(POEMSNode * firstNode, POEMSNode * secondNode); +public: + SystemProcessor(void); + + ~SystemProcessor(void) { + headsOfSystems.DeleteValues(); + for(int i = 0; i < ringsInSystem.GetNumElements(); i++) + { + for(int k = 0; k < ringsInSystem(i)->GetNumElements(); i++) + { + delete (*ringsInSystem(i))(k); + } + } + }; + void processArray(int** links, int numLinks); + List * getSystemData(); + int getNumberOfHeadChains(); +}; + +SystemProcessor::SystemProcessor(void){ +} + +void SystemProcessor::processArray(int** links, int numLinks) +{ + bool * false_var; //holds the value false; needed because a constant cannot be put into a list; the list requires a + //reference. + for(int i = 0; i < numLinks; i++) //go through all the links in the input array + { + if(!nodes.Find(links[i][0])) //if the first node in the pair is not found in the storage tree + { + POEMSNode * newNode = new POEMSNode; //make a new node +// forDeletion.Append(newNode); + newNode->idNumber = links[i][0]; //set its ID to the value + newNode->visited = false; //set it to be unvisited + nodes.Insert(links[i][0], links[i][0], (void *) newNode); //and add it to the tree storage structure + } + if(!nodes.Find(links[i][1])) //repeat process for the other half of each link + { + POEMSNode * newNode = new POEMSNode; +// forDeletion.Append(newNode); + newNode->idNumber = links[i][1]; + newNode->visited = false; + nodes.Insert(links[i][1], links[i][1], (void *) newNode); + } + POEMSNode * firstNode = (POEMSNode *)nodes.Find(links[i][0]); //now that we are sure both nodes exist, + POEMSNode * secondNode = (POEMSNode *)nodes.Find(links[i][1]); //we can get both of them out of the tree + firstNode->links.Append(secondNode); //and add the link from the first to the second... + false_var = new bool; + *false_var = false; //make a new false boolean to note that the link between these two + firstNode->taken.Append(false_var); //has not already been taken, and append it to the taken list + secondNode->links.Append(firstNode); //repeat process for link from second node to first + false_var = new bool; + *false_var = false; + secondNode->taken.Append(false_var); + } + + TreeNode * temp = nodes.GetRoot(); //get the root node of the node storage tree + POEMSNode * currentNode; + do + { + currentNode = findSingleLink(temp); //find the start of the next available chain + if(currentNode != NULL) + { + headsOfSystems.Append(AddNewChain(currentNode)); //and add it to the headsOfSystems list of chains + } + } + while(currentNode != NULL); //repeat this until all chains have been added +} + +POEMSChain * SystemProcessor::AddNewChain(POEMSNode * currentNode){ + if(currentNode == NULL) //Termination condition; if the currentNode is null, then return null + { + return NULL; + } + int * tmp; + POEMSNode * nextNode = NULL; //nextNode stores the proposed next node to add to the chain. this will be checked to make sure no backtracking is occuring before being assigned as the current node. + POEMSChain * newChain = new POEMSChain; //make a new POEMSChain object. This will be the object returned + + if(currentNode->links.GetNumElements() == 0) //if we have no links from this node, then the whole chain is only one node. Add this node to the chain and return it; mark node as visited for future reference + { + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); + return newChain; + } + while(currentNode->links.GetNumElements() <= 2) //we go until we get to a node that branches, or both branches have already been taken both branches can already be taken if a loop with no spurs is found in the input data + { + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); //append the current node to the chain & mark as visited + //cout << "Appending node " << currentNode->idNumber << " to chain" << endl; + nextNode = currentNode->links.GetHeadElement()->value; //the next node is the first or second value stored in the links array + //of the current node. We get the first value... + if(!setLinkVisited(currentNode, nextNode)) //...and see if it points back to where we came from. If it does... + { //either way, we set this link as visited + if(currentNode->links.GetNumElements() == 1) //if it does, then if that is the only link to this node, we're done with the chain, so append the chain to the list and return the newly created chain + { +// headsOfSystems.Append(newChain); + return newChain; + } + nextNode = currentNode->links.GetHeadElement()->next->value;//follow the other link if there is one, so we go down the chain + if(!setLinkVisited(currentNode, nextNode)) //mark link as followed, so we know not to backtrack + { + // headsOfSystems.Append(newChain); + return newChain; //This condition, where no branches have occurred but both links have already + //been taken can only occur in a loop with no spurs; add this loop to the + //system (currently added as a chain for consistency), and return. + } + } + currentNode = nextNode; //set the current node to be the next node in the chain + } + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); //append the last node before branch (node shared jointly with branch chains) + //re-mark as visited, just to make sure + ListElement * tempNode = currentNode->links.GetHeadElement(); //go through all of the links, one at a time that branch + POEMSChain * tempChain = NULL; //temporary variable to hold data + while(tempNode != NULL) //when we have followed all links, stop + { + if(setLinkVisited(tempNode->value, currentNode)) //dont backtrack, or create closed loops + { + tempChain = AddNewChain(tempNode->value); //Add a new chain created out of the next node down that link + tempChain->parentChain = newChain; //set the parent to be this chain + newChain->childChains.Append(tempChain); //append the chain to this chain's list of child chains + } + tempNode = tempNode->next; //go to process the next chain + } + //headsOfSystems.Append(newChain); //append this chain to the system list + return newChain; +} + +POEMSNode * SystemProcessor::findSingleLink(TreeNode * aNode) +//This function takes the root of a search tree containing POEMSNodes and returns a POEMSNode corresponding to the start of a chain in the +//system. It finds a node that has not been visited before, and only has one link; this node will be used as the head of the chain. +{ + if(aNode == NULL) + { + return NULL; + } + POEMSNode * returnVal = (POEMSNode *)aNode->GetAuxData(); //get the poemsnode data out of the treenode + POEMSNode * detectLoneLoops = NULL; //is used to handle a loop that has no protruding chains + if(returnVal->visited == false) + { + detectLoneLoops = returnVal; //if we find any node that has not been visited yet, save it + } + if(returnVal->links.GetNumElements() == 1 && returnVal->visited == false) //see if it has one element and hasnt been visited already + { + return returnVal; //return the node is it meets this criteria + } + returnVal = findSingleLink(aNode->Left()); //otherwise, check the left subtree + if(returnVal == NULL) //and if we find nothing... + { + returnVal = findSingleLink(aNode->Right()); //check the right subtree + } + if(returnVal == NULL) //if we could not find any chains + { + returnVal = detectLoneLoops; //see if we found any nodes at all that havent been processed + } + return returnVal; //return what we find (will be NULL if no new chains are + //found) +} + +bool SystemProcessor::setLinkVisited(POEMSNode * firstNode, POEMSNode * secondNode) +//setLinkVisited sets the links between these two nodes as visited. If they are already visited, it returns false. Otherwise, it sets +//them as visited and returns true. This function is used to see whether a certain path has been taken already in the graph structure. +//If it has been, then we need to know so we dont follow it again; this prevents infinite recursion when there is a loop, and prevents +//backtracking up a chain that has already been made. The list of booleans denoting if a link has been visited is called 'taken' and is +//part of the POEMSNode struct. The list is the same size as the list of pointers to other nodes, and stores the boolean visited/unvisited +//value for that particular link. Because each link is represented twice, (once at each node in the link), both of the boolean values need +//to be set in the event that the link has to be set as visited. +{ + //cout << "Checking link between nodes " << firstNode->idNumber << " and " << secondNode->idNumber << "... "; + ListElement * tmp = firstNode->links.GetHeadElement(); //get the head element of the list of pointers for node 1 + ListElement * tmp2 = firstNode->taken.GetHeadElement(); //get the head element of the list of bool isVisited flags for node 1 + while(tmp->value != NULL || tmp2->value != NULL) //go through untill we reach the end of the lists + { + if(tmp->value == secondNode) //if we find the link to the other node + { + if(*(tmp2->value) == true) //if the link has already been visited + { + //cout << "visited already" << endl; + return false; //return false to indicate that the link has been visited before this attempt + } + else //otherwise, visit it + { + *tmp2->value = true; + } + break; + } + tmp = tmp->next; //go check next link + tmp2 = tmp2->next; + } + + tmp = secondNode->links.GetHeadElement(); //now, if the link was unvisited, we need to go set the other node's list such that + //it also knows this link is being visited + tmp2 = secondNode->taken.GetHeadElement(); + while(tmp->value != NULL || tmp2->value != NULL) //go through the list + { + if(tmp->value == firstNode) //if we find the link + { + if(*(tmp2->value) == true) //and it has already been visited, then signal an error; this shouldnt ever happen + { + cout << "Error in parsing structure! Should never reach this condition! \n" << + "Record of visited links out of synch between two adjacent nodes.\n"; + return false; + } + else + { + *tmp2->value = true; //set the appropriate value to true to indicate this link has been visited + } + break; + } + tmp = tmp->next; + tmp2 = tmp2->next; + } + //cout << "not visited" << endl; + return true; //return true to indicate that this is the first time the link has been visited +} + +List * SystemProcessor::getSystemData(void) //Gets the list of POEMSChains that comprise the system. Might eventually only + //return chains linked to the reference plane, but currently returns every chain + //in the system. +{ + return &headsOfSystems; +} + +int SystemProcessor::getNumberOfHeadChains(void) //This function isnt implemented yet, and might be taken out entirely; this was a holdover + //from when I intended to return an array of chain pointers, rather than a list of chains + //It will probably be deleted once I finish figuring out exactly what needs to be returned +{ + return 0; +} +#endif diff --git a/lib/atc/Thermostat.cpp b/lib/atc/Thermostat.cpp index 110d633ac7..41312bd851 100644 --- a/lib/atc/Thermostat.cpp +++ b/lib/atc/Thermostat.cpp @@ -179,10 +179,12 @@ namespace ATC { TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (timeFilterManager->need_reset() ) { - if (myIntegrationType == TimeIntegrator::GEAR) - timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); - else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) - timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + if (myIntegrationType == TimeIntegrator::GEAR) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); + } + else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } } if (timeFilterManager->filter_dynamics()) { @@ -205,16 +207,16 @@ namespace ATC { if (md_flux_nodes(TEMPERATURE)) { if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fluxes but no fixed or coupled nodes - regulatorMethod_ = new ThermostatFluxFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFluxFiltered(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixedFiltered(this); + regulatorMethod_ = new ThermostatFluxFixedFiltered(this,lambdaMaxIterations_); } } else { // there are only fixed nodes - regulatorMethod_ = new ThermostatFixedFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFixedFiltered(this,lambdaMaxIterations_); } } else { @@ -230,16 +232,16 @@ namespace ATC { if (md_fixed_nodes(TEMPERATURE)) { if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fixed nodes but no fluxes - regulatorMethod_ = new ThermostatFixedFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFixedFiltered(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixedFiltered(this); + regulatorMethod_ = new ThermostatFluxFixedFiltered(this,lambdaMaxIterations_); } } else { // there are only flux nodes - regulatorMethod_ = new ThermostatFluxFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFluxFiltered(this,lambdaMaxIterations_); } } else { @@ -287,16 +289,16 @@ namespace ATC { if (md_flux_nodes(TEMPERATURE)) { if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fluxes but no fixed or coupled nodes - regulatorMethod_ = new ThermostatFlux(this); + regulatorMethod_ = new ThermostatIntegratorFlux(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixed(this); + regulatorMethod_ = new ThermostatFluxFixed(this,lambdaMaxIterations_); } } else { // there are only fixed nodes - regulatorMethod_ = new ThermostatFixed(this); + regulatorMethod_ = new ThermostatIntegratorFixed(this,lambdaMaxIterations_); } } else { @@ -312,16 +314,16 @@ namespace ATC { if (md_fixed_nodes(TEMPERATURE)) { if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fixed nodes but no fluxes - regulatorMethod_ = new ThermostatFixed(this); + regulatorMethod_ = new ThermostatIntegratorFixed(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixed(this); + regulatorMethod_ = new ThermostatFluxFixed(this,lambdaMaxIterations_); } } else { // there are only flux nodes - regulatorMethod_ = new ThermostatFlux(this); + regulatorMethod_ = new ThermostatIntegratorFlux(this,lambdaMaxIterations_); } } else { @@ -361,15 +363,14 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatShapeFunction::ThermostatShapeFunction(Thermostat * thermostat, + ThermostatShapeFunction::ThermostatShapeFunction(AtomicRegulator * thermostat, const string & regulatorPrefix) : RegulatorShapeFunction(thermostat,regulatorPrefix), - thermostat_(thermostat), mdMassMatrix_(atc_->set_mass_mat_md(TEMPERATURE)), atomVelocities_(NULL) { fieldMask_(TEMPERATURE,FLUX) = true; - lambda_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize } //-------------------------------------------------------- @@ -411,8 +412,7 @@ namespace ATC { regulatorPrefix_+"AtomVelocitySquared"); } } - - + //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatRescale @@ -422,7 +422,7 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatRescale::ThermostatRescale(Thermostat * thermostat) : + ThermostatRescale::ThermostatRescale(AtomicRegulator * thermostat) : ThermostatShapeFunction(thermostat), nodalTemperature_(atc_->field(TEMPERATURE)), atomVelocityRescalings_(NULL) @@ -456,23 +456,52 @@ namespace ATC { regulatorPrefix_+"AtomVelocityRescaling"); } + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatRescale::set_weights() + { + weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicEnergyForTemperature"); + } + //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat in the post corrector phase //-------------------------------------------------------- void ThermostatRescale::apply_post_corrector(double dt) { - // compute right-hand side - _rhs_ = mdMassMatrix_.quantity()*nodalTemperature_.quantity(); - correct_rhs(_rhs_); // correct right-hand side for complex temperature definitions, e.g., when the potential energy is included - - // solve equations - solve_for_lambda(_rhs_,lambda_->set_quantity()); + compute_thermostat(dt); // application of rescaling lambda due apply_to_atoms(atomVelocities_); } + //-------------------------------------------------------- + // compute_thermostat + // manages the solution of the + // thermostat equations and variables + //-------------------------------------------------------- + void ThermostatRescale::compute_thermostat(double dt) + { + // compute right-hand side + this->set_rhs(_rhs_); + + // solve equations + solve_for_lambda(_rhs_,lambda_->set_quantity()); + } + + //-------------------------------------------------------- + // set_rhs: + // constructs the RHS vector with the target + // temperature + //-------------------------------------------------------- + void ThermostatRescale::set_rhs(DENS_MAT & rhs) + { + rhs = mdMassMatrix_.quantity()*nodalTemperature_.quantity(); + } + //-------------------------------------------------------- // apply_lambda_to_atoms: // applies the velocity rescale with an existing lambda @@ -491,7 +520,7 @@ namespace ATC { { DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData["Lambda"] = λ + outputData["LambdaEnergy"] = λ } } @@ -504,7 +533,7 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatRescaleMixedKePe::ThermostatRescaleMixedKePe(Thermostat * thermostat) : + ThermostatRescaleMixedKePe::ThermostatRescaleMixedKePe(AtomicRegulator * thermostat) : ThermostatRescale(thermostat), nodalAtomicFluctuatingPotentialEnergy_(NULL) { @@ -525,6 +554,16 @@ namespace ATC { interscaleManager.dense_matrix("NodalAtomicFluctuatingPotentialEnergy"); } + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatRescaleMixedKePe::set_weights() + { + weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicTwiceKineticEnergy"); + } + //-------------------------------------------------------- // initialize // initializes all method data @@ -543,130 +582,47 @@ namespace ATC { } //-------------------------------------------------------- - // correct_rhs: + // set_rhs: // accounts for potential energy contribution to // definition of atomic temperature //-------------------------------------------------------- - void ThermostatRescaleMixedKePe::correct_rhs(DENS_MAT & rhs) + void ThermostatRescaleMixedKePe::set_rhs(DENS_MAT & rhs) { + ThermostatRescale::set_rhs(rhs); rhs -= peMultiplier_*(nodalAtomicFluctuatingPotentialEnergy_->quantity()); rhs /= keMultiplier_; } - + //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatGlcFs + // Class ThermostatFsSolver //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatGlcFs::ThermostatGlcFs(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatShapeFunction(thermostat,regulatorPrefix), - temperature_(atc_->field(TEMPERATURE)), - timeFilter_(atomicRegulator_->time_filter()), - nodalAtomicLambdaPower_(NULL), - lambdaPowerFiltered_(NULL), - atomThermostatForces_(NULL), - atomMasses_(NULL), + ThermostatFsSolver::ThermostatFsSolver(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + RegulatorShapeFunction(thermostat,regulatorPrefix), + lambdaMaxIterations_(lambdaMaxIterations), rhsLambdaSquared_(NULL), - isFirstTimestep_(true), - lambdaMaxIterations_(thermostat->lambda_max_iterations()), - nodalAtomicEnergy_(NULL), - atomPredictedVelocities_(NULL), - nodalAtomicPredictedEnergy_(NULL), - firstHalfAtomForces_(NULL), - dtFactor_(0.) + dtFactor_(1.) { - // construct/obtain data corresponding to stage 3 of ATC_Method::initialize - nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1); - lambdaPowerFiltered_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); - } - - //-------------------------------------------------------- - // constructor_transfers - // instantiates or obtains all dependency managed data - //-------------------------------------------------------- - void ThermostatGlcFs::construct_transfers() - { - ThermostatShapeFunction::construct_transfers(); - InterscaleManager & interscaleManager(atc_->interscale_manager()); - - // get data from manager - atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), - nodalAtomicEnergy_ = interscaleManager.dense_matrix("NodalAtomicEnergy"); - - // thermostat forces based on lambda and the atomic velocities - atomThermostatForces_ = new AtomicThermostatForce(atc_,atomLambdas_); - interscaleManager.add_per_atom_quantity(atomThermostatForces_, - regulatorPrefix_+"AtomThermostatForce"); - - // predicted temperature quantities: atom velocities, atom energies, and restricted atom energies - AtcAtomQuantity * atomPredictedVelocities = new AtcAtomQuantity(atc_,nsd_); - interscaleManager.add_per_atom_quantity(atomPredictedVelocities, - regulatorPrefix_+"AtomicPredictedVelocities"); - atomPredictedVelocities_ = atomPredictedVelocities; - AtomicEnergyForTemperature * atomPredictedEnergyForTemperature = new TwiceKineticEnergy(atc_, - atomPredictedVelocities); - interscaleManager.add_per_atom_quantity(atomPredictedEnergyForTemperature, - regulatorPrefix_+"AtomicPredictedTwiceKineticEnergy"); - nodalAtomicPredictedEnergy_ = new AtfShapeFunctionRestriction(atc_, - atomPredictedEnergyForTemperature, - interscaleManager.per_atom_sparse_matrix("Interpolant")); - interscaleManager.add_dense_matrix(nodalAtomicPredictedEnergy_, - regulatorPrefix_+"NodalAtomicPredictedEnergy"); + fieldMask_(TEMPERATURE,FLUX) = true; + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize } //-------------------------------------------------------- // initialize - // initializes all method data - //-------------------------------------------------------- - void ThermostatGlcFs::initialize() - { - ThermostatShapeFunction::initialize(); - - // reset data to zero - deltaEnergy1_.reset(nNodes_,1); - deltaEnergy2_.reset(nNodes_,1); - _lambdaPowerOutput_.reset(nNodes_,1); - - TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); - if (!timeFilterManager->end_equilibrate()) { - // we should reset lambda and lambdaForce to zero in this case - // implies an initial condition of 0 for the filtered nodal lambda power - // initial conditions will always be needed when using time filtering - // however, the fractional step scheme must assume the instantaneous - // nodal lambda power is 0 initially because all quantities are in delta form - *lambda_ = 0.; // ensures initial lambda force is zero - *nodalAtomicLambdaPower_ = 0.; // energy change due to thermostats - *lambdaPowerFiltered_ = 0.; // filtered energy change due to thermostats - } - else { - // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration - } - - // sets up time filter for cases where variables temporally filtered - if (timeFilterManager->need_reset()) { - // the form of this integrator implies no time filters that require history data can be used - timeFilter_->initialize(nodalAtomicLambdaPower_->quantity()); - } - - atomThermostatForces_->quantity(); // initialize - atomThermostatForces_->fix_quantity(); - firstHalfAtomForces_ = atomThermostatForces_; // initialize - - compute_rhs_map(); - } - - //-------------------------------------------------------- - // compute_rhs_map // creates mapping from all nodes to those to which // the thermostat applies //-------------------------------------------------------- - void ThermostatGlcFs::compute_rhs_map() + void ThermostatFsSolver::initialize() { + RegulatorShapeFunction::initialize(); + rhsMap_.resize(overlapToNodeMap_->nRows(),1); DENS_MAT rhsMapGlobal(nNodes_,1); const set & applicationNodes(applicationNodes_->quantity()); @@ -681,6 +637,228 @@ namespace ATC { map_unique_to_overlap(rhsMapGlobal,rhsMap_); } + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatFsSolver::set_weights() + { + if (this->use_local_shape_functions()) { + VelocitySquaredMapped * myWeights = new VelocitySquaredMapped(atc_,lambdaAtomMap_); + weights_ = myWeights; + (atc_->interscale_manager()).add_per_atom_quantity(myWeights, + regulatorPrefix_+"AtomVelocitySquaredMapped"); + } + else { + VelocitySquared * myWeights = new VelocitySquared(atc_); + weights_ = myWeights; + (atc_->interscale_manager()).add_per_atom_quantity(myWeights, + regulatorPrefix_+"AtomVelocitySquared"); + } + } + + //-------------------------------------------------------- + // compute_lambda: + // solves linear system for lambda, if the + // bool is true it iterators to a non-linear solution + //-------------------------------------------------------- + void ThermostatFsSolver::compute_lambda(const DENS_MAT & rhs, + bool iterateSolution) + { + // solve linear system for lambda guess + DENS_MAT & lambda(lambda_->set_quantity()); + solve_for_lambda(rhs,lambda); + + // iterate to solution + if (iterateSolution) { + iterate_lambda(rhs); + } + } + + //-------------------------------------------------------- + // iterate_lambda: + // iteratively solves the equations for lambda + // for the higher order dt corrections, assuming + // an initial guess for lambda + //-------------------------------------------------------- + void ThermostatFsSolver::iterate_lambda(const MATRIX & rhs) + { + int nNodeOverlap = overlapToNodeMap_->nRows(); + DENS_VEC _lambdaOverlap_(nNodeOverlap); + DENS_MAT & lambda(lambda_->set_quantity()); + map_unique_to_overlap(lambda,_lambdaOverlap_); + double factor = 0.5*dtFactor_*atc_->dt(); + + _lambdaOld_.resize(nNodes_,1); + _rhsOverlap_.resize(nNodeOverlap,1); + map_unique_to_overlap(rhs,_rhsOverlap_); + _rhsTotal_.resize(nNodeOverlap); + + // solve assuming we get initial guess for lambda + double error(-1.); + for (int i = 0; i < lambdaMaxIterations_; ++i) { + _lambdaOld_ = lambda; + + // solve the system with the new rhs + const DENS_MAT & rhsLambdaSquared(rhsLambdaSquared_->quantity()); + for (int i = 0; i < nNodeOverlap; i++) { + if (rhsMap_(i,0) == 1.) { + _rhsTotal_(i) = _rhsOverlap_(i,0) + factor*rhsLambdaSquared(i,0); + } + else { + _rhsTotal_(i) = 0.; + } + } + matrixSolver_->execute(_rhsTotal_,_lambdaOverlap_); + + // check convergence + map_overlap_to_unique(_lambdaOverlap_,lambda); + lambda_->force_reset(); + DENS_MAT difference = lambda-_lambdaOld_; + error = difference.col_norm()/_lambdaOld_.col_norm(); + if (error < tolerance_) + break; + } + + if (error >= tolerance_) { + stringstream message; + message << "WARNING: Iterative solve for lambda failed to converge after " << lambdaMaxIterations_ << " iterations, final tolerance was " << error << "\n"; + ATC::LammpsInterface::instance()->print_msg(message.str()); + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatGlcFs + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatGlcFs::ThermostatGlcFs(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + RegulatorMethod(thermostat,regulatorPrefix), + lambdaSolver_(NULL), + mdMassMatrix_(atc_->set_mass_mat_md(TEMPERATURE)), + atomVelocities_(NULL), + temperature_(atc_->field(TEMPERATURE)), + timeFilter_(atomicRegulator_->time_filter()), + nodalAtomicLambdaPower_(NULL), + lambdaPowerFiltered_(NULL), + atomLambdas_(NULL), + atomThermostatForces_(NULL), + atomMasses_(NULL), + isFirstTimestep_(true), + nodalAtomicEnergy_(NULL), + atomPredictedVelocities_(NULL), + nodalAtomicPredictedEnergy_(NULL), + firstHalfAtomForces_(NULL) + { + // construct/obtain data corresponding to stage 3 of ATC_Method::initialize + nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1); + lambdaPowerFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void ThermostatGlcFs::construct_transfers() + { + lambdaSolver_->construct_transfers(); + InterscaleManager & interscaleManager(atc_->interscale_manager()); + + // get atom velocity data from manager + atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); + + // construct lambda evaluated at atom locations + atomLambdas_ = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"); + if (!atomLambdas_) { + DENS_MAN * lambda = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } + + // get data from manager + atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), + nodalAtomicEnergy_ = interscaleManager.dense_matrix("NodalAtomicEnergy"); + + // thermostat forces based on lambda and the atomic velocities + atomThermostatForces_ = new AtomicThermostatForce(atc_,atomLambdas_); + interscaleManager.add_per_atom_quantity(atomThermostatForces_, + regulatorPrefix_+"AtomThermostatForce"); + + // predicted temperature quantities: atom velocities, atom energies, and restricted atom energies + atomPredictedVelocities_ = new AtcAtomQuantity(atc_,atc_->nsd()); + // MAKE THINGS WORK WITH ONLY ONE PREDICTED VELOCITY, CHECK IT EXISTS + interscaleManager.add_per_atom_quantity(atomPredictedVelocities_, + regulatorPrefix_+"AtomicPredictedVelocities"); + AtomicEnergyForTemperature * atomPredictedEnergyForTemperature = new TwiceKineticEnergy(atc_, + atomPredictedVelocities_); + interscaleManager.add_per_atom_quantity(atomPredictedEnergyForTemperature, + regulatorPrefix_+"AtomicPredictedTwiceKineticEnergy"); + nodalAtomicPredictedEnergy_ = new AtfShapeFunctionRestriction(atc_, + atomPredictedEnergyForTemperature, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(nodalAtomicPredictedEnergy_, + regulatorPrefix_+"NodalAtomicPredictedEnergy"); + } + + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void ThermostatGlcFs::initialize() + { + RegulatorMethod::initialize(); + + TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + // we should reset lambda and lambdaForce to zero in this case + // implies an initial condition of 0 for the filtered nodal lambda power + // initial conditions will always be needed when using time filtering + // however, the fractional step scheme must assume the instantaneous + // nodal lambda power is 0 initially because all quantities are in delta form + lambdaSolver_->initialize(); // ensures initial lambda force is zero + lambdaSolver_->set_lambda_to_value(0.); + *nodalAtomicLambdaPower_ = 0.; // energy change due to thermostats + *lambdaPowerFiltered_ = 0.; // filtered energy change due to thermostats + } + else { + lambdaSolver_->initialize(); + // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration + } + + // sets up time filter for cases where variables temporally filtered + if (timeFilterManager->need_reset()) { + // the form of this integrator implies no time filters that require history data can be used + timeFilter_->initialize(nodalAtomicLambdaPower_->quantity()); + } + + atomThermostatForces_->quantity(); // initialize + atomThermostatForces_->fix_quantity(); + firstHalfAtomForces_ = atomThermostatForces_; // initialize +#ifdef OBSOLETE + compute_rhs_map(); +#endif + } + + //-------------------------------------------------------- + // reset_atom_materials: + // resets the localized atom to material map + //-------------------------------------------------------- + void ThermostatGlcFs::reset_atom_materials(const Array & elementToMaterialMap, + const MatrixDependencyManager * atomElement) + { + lambdaSolver_->reset_atom_materials(elementToMaterialMap, + atomElement); + } + //-------------------------------------------------------- // apply_to_atoms: // determines what if any contributions to the @@ -777,7 +955,8 @@ namespace ATC { // update predicted nodal variables for second half of time step this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); - deltaEnergy1_ += deltaEnergy2_; + // following manipulations performed this way for efficiency + deltaEnergy1_ += deltaEnergy2_; atc_->apply_inverse_mass_matrix(deltaEnergy1_,TEMPERATURE); temperature_ += deltaEnergy1_; } @@ -799,11 +978,13 @@ namespace ATC { // apply lambda force to atoms and compute instantaneous lambda power for second half of time step DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); - atomThermostatForces_->unfix_quantity(); // allow computation of force applied by lambda + // allow computation of force applied by lambda using current velocities + atomThermostatForces_->unfix_quantity(); + atomThermostatForces_->quantity(); + atomThermostatForces_->fix_quantity(); apply_to_atoms(atomVelocities_,nodalAtomicEnergy_, atomThermostatForces_->quantity(), myNodalAtomicLambdaPower,0.5*dt); - atomThermostatForces_->fix_quantity(); // finalize filtered lambda power by adding latest contribution timeFilter_->apply_post_step2(lambdaPowerFiltered_->set_quantity(), @@ -827,69 +1008,31 @@ namespace ATC { bool iterateSolution) { // set up rhs for lambda equation - rhs_.resize(nNodes_,1); this->set_thermostat_rhs(rhs_,0.5*dt); - + + // solve system + lambdaSolver_->compute_lambda(rhs_,iterateSolution); +#ifdef OBSOLETE // solve linear system for lambda guess - DENS_MAT & myLambda(lambda_->set_quantity()); - solve_for_lambda(rhs_,myLambda); + DENS_MAT & lambda(lambda_->set_quantity()); + solve_for_lambda(rhs_,lambda); // iterate to solution if (iterateSolution) { iterate_lambda(rhs_); } +#endif } //-------------------------------------------------------- - // iterate_lambda: - // iteratively solves the equations for lambda - // for the higher order dt corrections, assuming - // an initial guess for lambda + // compute_boundary_flux + // default computation of boundary flux based on + // finite //-------------------------------------------------------- - void ThermostatGlcFs::iterate_lambda(const MATRIX & rhs) + void ThermostatGlcFs::compute_boundary_flux(FIELDS & fields) { - int nNodeOverlap = overlapToNodeMap_->nRows(); - DENS_VEC _lambdaOverlap_(nNodeOverlap); - DENS_MAT & lambda(lambda_->set_quantity()); - map_unique_to_overlap(lambda,_lambdaOverlap_); - double factor = 0.5*dtFactor_*atc_->dt(); - - _lambdaOld_.resize(nNodes_,1); - _rhsOverlap_.resize(nNodeOverlap,1); - map_unique_to_overlap(rhs,_rhsOverlap_); - _rhsTotal_.resize(nNodeOverlap); - - // solve assuming we get initial guess for lambda - double error(-1.); - for (int i = 0; i < lambdaMaxIterations_; ++i) { - _lambdaOld_ = lambda; - - // solve the system with the new rhs - const DENS_MAT & rhsLambdaSquared(rhsLambdaSquared_->quantity()); - for (int i = 0; i < nNodeOverlap; i++) { - if (rhsMap_(i,0) == 1.) { - _rhsTotal_(i) = _rhsOverlap_(i,0) + factor*rhsLambdaSquared(i,0); - } - else { - _rhsTotal_(i) = 0.; - } - } - matrixSolver_->execute(_rhsTotal_,_lambdaOverlap_); - - // check convergence - map_overlap_to_unique(_lambdaOverlap_,lambda); - lambda_->force_reset(); - DENS_MAT difference = lambda-_lambdaOld_; - error = difference.col_norm()/_lambdaOld_.col_norm(); - if (error < tolerance_) - break; - } - if (error >= tolerance_) { - stringstream message; - message << " Iterative solve for lambda failed to converge after " << lambdaMaxIterations_ << " iterations, final tolerance was " << error << "\n"; - ATC::LammpsInterface::instance()->print_msg(message.str()); - } + lambdaSolver_->compute_boundary_flux(fields); } //-------------------------------------------------------- @@ -902,16 +1045,16 @@ namespace ATC { // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaEnergy"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); } } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFlux + // Class ThermostatSolverFlux //-------------------------------------------------------- //-------------------------------------------------------- @@ -919,10 +1062,10 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFlux::ThermostatFlux(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatGlcFs(thermostat,regulatorPrefix), - heatSource_(atc_->atomic_source(TEMPERATURE)) + ThermostatSolverFlux::ThermostatSolverFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatFsSolver(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } @@ -931,7 +1074,7 @@ namespace ATC { // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- - void ThermostatFlux::construct_transfers() + void ThermostatSolverFlux::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); @@ -955,15 +1098,21 @@ namespace ATC { } // base class transfers - ThermostatGlcFs::construct_transfers(); + ThermostatFsSolver::construct_transfers(); // add transfers for computation of extra RHS term accounting of O(lambda^2) // lambda squared followed by fractional step RHS contribution - PerAtomQuantity * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + atomLambdas_ = (interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + if (!atomLambdas_) { + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda_, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } LambdaSquared * lambdaSquared = new LambdaSquared(atc_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquaredMapped"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); @@ -971,31 +1120,22 @@ namespace ATC { regulatorPrefix_+"RhsLambdaSquared"); } - //-------------------------------------------------------- - // initialize - // initializes all method data - //-------------------------------------------------------- - void ThermostatFlux::initialize() - { - ThermostatGlcFs::initialize(); - - // timestep factor - dtFactor_ = 1.; - } - //-------------------------------------------------------- // construct_regulated_nodes: // constructs the set of nodes being regulated //-------------------------------------------------------- - void ThermostatFlux::construct_regulated_nodes() + void ThermostatSolverFlux::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // matrix requires all entries even if localized for correct lumping - regulatedNodes_ = new RegulatedNodes(atc_); - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"ThermostatRegulatedNodes"); - + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + if (!regulatedNodes_) { + regulatedNodes_ = new RegulatedNodes(atc_); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"ThermostatRegulatedNodes"); + } + // if localized monitor nodes with applied fluxes if (atomicRegulator_->use_localized_lambda()) { if ((atomicRegulator_->coupling_mode() == Thermostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { @@ -1020,20 +1160,44 @@ namespace ATC { // special set of boundary elements for boundary flux quadrature if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION) && (atomicRegulator_->use_localized_lambda())) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatIntegratorFlux + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatIntegratorFlux::ThermostatIntegratorFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatGlcFs(thermostat,lambdaMaxIterations,regulatorPrefix), + heatSource_(atc_->atomic_source(TEMPERATURE)) + { + lambdaSolver_ = new ThermostatSolverFlux(thermostat, + lambdaMaxIterations, + regulatorPrefix); + } + //-------------------------------------------------------- // add_to_temperature // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFlux::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFlux::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.resize(nNodes_,1); const DENS_MAT & myBoundaryFlux(boundaryFlux_[TEMPERATURE].quantity()); @@ -1042,14 +1206,26 @@ namespace ATC { } } + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void ThermostatIntegratorFlux::initialize() + { + ThermostatGlcFs::initialize(); + + // timestep factor + lambdaSolver_->set_timestep_factor(1.); + } + //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFlux::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFlux::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // only tested with flux != 0 + ess bc = 0 @@ -1059,7 +1235,9 @@ namespace ATC { // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I // fluxes are set in ATC transfer const DENS_MAT & heatSource(heatSource_.quantity()); - const set & applicationNodes(applicationNodes_->quantity()); +#if true + const set & applicationNodes((lambdaSolver_->application_nodes())->quantity()); + rhs.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (applicationNodes.find(i) != applicationNodes.end()) { rhs(i,0) = heatSource(i,0); @@ -1068,11 +1246,17 @@ namespace ATC { rhs(i,0) = 0.; } } +#else + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { + rhs(i,0) = heatSource(i,0); + } +#endif } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFixed + // Class ThermostatSolverFixed //-------------------------------------------------------- //-------------------------------------------------------- @@ -1080,11 +1264,10 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFixed::ThermostatFixed(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatGlcFs(thermostat,regulatorPrefix), - atomThermostatForcesPredVel_(NULL), - filterCoefficient_(1.) + ThermostatSolverFixed::ThermostatSolverFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatFsSolver(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } @@ -1093,7 +1276,7 @@ namespace ATC { // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- - void ThermostatFixed::construct_transfers() + void ThermostatSolverFixed::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); @@ -1117,32 +1300,112 @@ namespace ATC { linearSolverType_ = AtomicRegulator::CG_SOLVE; // base class transfers, e.g. weights - ThermostatGlcFs::construct_transfers(); + ThermostatFsSolver::construct_transfers(); // add transfers for computation of extra RHS term accounting of O(lambda^2) // lambda squared followed by fractional step RHS contribution - PerAtomQuantity * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + atomLambdas_ = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"); + if (!atomLambdas_) { + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda_, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } if (lambdaAtomMap_) { LambdaSquaredMapped * lambdaSquared = new LambdaSquaredMapped(atc_, lambdaAtomMap_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquared"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); } else { LambdaSquared * lambdaSquared = new LambdaSquared(atc_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquaredMapped"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); } interscaleManager.add_dense_matrix(rhsLambdaSquared_, - regulatorPrefix_+"RhsLambdaSquared"); + regulatorPrefix_+"RhsLambdaSquared"); + } + + //-------------------------------------------------------- + // construct_regulated_nodes: + // constructs the set of nodes being regulated + //-------------------------------------------------------- + void ThermostatSolverFixed::construct_regulated_nodes() + { + InterscaleManager & interscaleManager(atc_->interscale_manager()); + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + + if (!regulatedNodes_) { + if (!atomicRegulator_->use_localized_lambda()) { + regulatedNodes_ = new RegulatedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == AtomicRegulator::FLUX) { + regulatedNodes_ = new FixedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == AtomicRegulator::FIXED) { + // include boundary nodes + regulatedNodes_ = new FixedBoundaryNodes(atc_); + } + else { + throw ATC_Error("ThermostatSolverFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); + } + + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"ThermostatRegulatedNodes"); + } + + applicationNodes_ = regulatedNodes_; + + // special set of boundary elements for defining regulated atoms + if (atomicRegulator_->use_localized_lambda()) { + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatIntegratorFixed + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatIntegratorFixed::ThermostatIntegratorFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatGlcFs(thermostat,lambdaMaxIterations,regulatorPrefix), + atomThermostatForcesPredVel_(NULL), + filterCoefficient_(1.) + { + lambdaSolver_ = new ThermostatSolverFixed(thermostat, + lambdaMaxIterations, + regulatorPrefix); + } + + //-------------------------------------------------------- + // construct_regulated_nodes: + // constructs the set of nodes being regulated + //-------------------------------------------------------- + void ThermostatIntegratorFixed::construct_transfers() + { + ThermostatGlcFs::construct_transfers(); + + InterscaleManager & interscaleManager(atc_->interscale_manager()); // predicted forces for halving update atomThermostatForcesPredVel_ = new AtomicThermostatForce(atc_,atomLambdas_,atomPredictedVelocities_); @@ -1154,7 +1417,7 @@ namespace ATC { // initialize // initializes all method data //-------------------------------------------------------- - void ThermostatFixed::initialize() + void ThermostatIntegratorFixed::initialize() { ThermostatGlcFs::initialize(); InterscaleManager & interscaleManager(atc_->interscale_manager()); @@ -1175,7 +1438,7 @@ namespace ATC { } // timestep factor - dtFactor_ = 0.5; + lambdaSolver_->set_timestep_factor(0.5); } //-------------------------------------------------------- @@ -1183,7 +1446,7 @@ namespace ATC { // flag to halve the lambda force for improved // accuracy //-------------------------------------------------------- - bool ThermostatFixed::halve_force() + bool ThermostatIntegratorFixed::halve_force() { if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) && (atc_->atom_to_element_map_frequency() > 1) @@ -1193,48 +1456,13 @@ namespace ATC { return false; } - //-------------------------------------------------------- - // construct_regulated_nodes: - // constructs the set of nodes being regulated - //-------------------------------------------------------- - void ThermostatFixed::construct_regulated_nodes() - { - InterscaleManager & interscaleManager(atc_->interscale_manager()); - - if (!atomicRegulator_->use_localized_lambda()) { - regulatedNodes_ = new RegulatedNodes(atc_); - } - else if (thermostat_->coupling_mode() == AtomicRegulator::FLUX) { - regulatedNodes_ = new FixedNodes(atc_); - } - else if (thermostat_->coupling_mode() == AtomicRegulator::FIXED) { - // include boundary nodes - regulatedNodes_ = new FixedBoundaryNodes(atc_); - } - else { - throw ATC_Error("ThermostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); - } - - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"RegulatedNodes"); - - applicationNodes_ = regulatedNodes_; - - // special set of boundary elements for defining regulated atoms - if (atomicRegulator_->use_localized_lambda()) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); - } - } - //-------------------------------------------------------- // initialize_delta_nodal_atomic_energy: // initializes storage for the variable tracking // the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixed::initialize_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixed::initialize_delta_nodal_atomic_energy(double dt) { // initialize delta energy const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); @@ -1249,7 +1477,7 @@ namespace ATC { // computes the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixed::compute_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixed::compute_delta_nodal_atomic_energy(double dt) { // set delta energy based on predicted atomic velocities const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); @@ -1261,11 +1489,10 @@ namespace ATC { //-------------------------------------------------------- // compute_lambda: - // sets up and solves linear system for lambda, if the - // bool is true it iterators to a non-linear solution + // sets up and solves linear system for lambda //-------------------------------------------------------- - void ThermostatFixed::compute_lambda(double dt, - bool iterateSolution) + void ThermostatIntegratorFixed::compute_lambda(double dt, + bool iterateSolution) { // compute predicted changes in nodal atomic energy compute_delta_nodal_atomic_energy(dt); @@ -1282,7 +1509,7 @@ namespace ATC { // apply the thermostat to the atoms in the first step // of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_pre_predictor(double dt) + void ThermostatIntegratorFixed::apply_pre_predictor(double dt) { // initialize values to be track change in finite element energy over the timestep initialize_delta_nodal_atomic_energy(dt); @@ -1296,7 +1523,7 @@ namespace ATC { // apply the thermostat to the atoms in the first part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_pre_corrector(double dt) + void ThermostatIntegratorFixed::apply_pre_corrector(double dt) { // do full prediction if we just redid the shape functions if (full_prediction()) { @@ -1318,33 +1545,12 @@ namespace ATC { } } - //-------------------------------------------------------- - // add_to_temperature - // add in contributions from lambda power and boundary - // flux to the FE temperature - //-------------------------------------------------------- - void ThermostatFixed::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) - { - deltaEnergy.resize(nNodes_,1); - const set & regulatedNodes(regulatedNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (regulatedNodes.find(i) != regulatedNodes.end()) { - deltaEnergy(i,0) = 0.; - } - else { - deltaEnergy(i,0) = nodalLambdaPower(i,0); - } - } - } - //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_post_corrector(double dt) + void ThermostatIntegratorFixed::apply_post_corrector(double dt) { bool halveForce = halve_force(); @@ -1364,7 +1570,7 @@ namespace ATC { // avoids unstable oscillations arising from // thermostat having to correct for error introduced in lambda changing the // shape function matrices - *lambda_ *= 0.5; + lambdaSolver_->scale_lambda(0.5); firstHalfAtomForces_ = atomThermostatForcesPredVel_; atomThermostatForcesPredVel_->unfix_quantity(); } @@ -1373,19 +1579,48 @@ namespace ATC { } } + //-------------------------------------------------------- + // add_to_temperature + // add in contributions from lambda power and boundary + // flux to the FE temperature + //-------------------------------------------------------- + void ThermostatIntegratorFixed::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) + { + deltaEnergy.resize(nNodes_,1); + + SetDependencyManager * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set & regulatedNodes(myRegulatedNodes->quantity()); + + for (int i = 0; i < nNodes_; i++) { + if (regulatedNodes.find(i) != regulatedNodes.end()) { + deltaEnergy(i,0) = 0.; + } + else { + deltaEnergy(i,0) = nodalLambdaPower(i,0); + } + } + } + //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFixed::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFixed::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // for essential bcs (fixed nodes) : // form rhs : (delThetaV - delTheta)/dt - const set & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set & regulatedNodes(myRegulatedNodes->quantity()); double factor = (1./dt)/keMultiplier_; + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0)); @@ -1398,7 +1633,7 @@ namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFluxFiltered + // Class ThermostatIntegratorFluxFiltered //-------------------------------------------------------- //-------------------------------------------------------- @@ -1406,9 +1641,10 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFluxFiltered::ThermostatFluxFiltered(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatFlux(thermostat,regulatorPrefix) + ThermostatIntegratorFluxFiltered::ThermostatIntegratorFluxFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatIntegratorFlux(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } @@ -1417,9 +1653,9 @@ namespace ATC { // initialize // initializes all method data //-------------------------------------------------------- - void ThermostatFluxFiltered::initialize() + void ThermostatIntegratorFluxFiltered::initialize() { - ThermostatFlux::initialize(); + ThermostatIntegratorFlux::initialize(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { @@ -1435,10 +1671,10 @@ namespace ATC { // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFluxFiltered::apply_post_corrector(double dt) + void ThermostatIntegratorFluxFiltered::apply_post_corrector(double dt) { // compute lambda - ThermostatFlux::apply_post_corrector(dt); + ThermostatIntegratorFlux::apply_post_corrector(dt); // store data needed for filter inversion of heat flux for thermostat rhs instantHeatSource_ = rhs_; @@ -1450,9 +1686,9 @@ namespace ATC { // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFluxFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFluxFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.reset(nNodes_,1); double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); @@ -1468,8 +1704,8 @@ namespace ATC { // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFluxFiltered::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFluxFiltered::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // only tested with flux != 0 + ess bc = 0 @@ -1488,7 +1724,10 @@ namespace ATC { double coefU2 = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); const DENS_MAT & heatSource(heatSource_.quantity()); - const set & applicationNodes(applicationNodes_->quantity()); + SetDependencyManager * myApplicationNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatApplicationNodes"); + const set & applicationNodes(myApplicationNodes->quantity()); + rhs.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (applicationNodes.find(i) != applicationNodes.end()) { rhs(i,0) = heatSource(i,0) - coefF1*coefF2*heatSourceOld_(i,0) - coefU1*coefF2*instantHeatSource_(i,0); @@ -1504,13 +1743,13 @@ namespace ATC { // output: // adds all relevant output to outputData //-------------------------------------------------------- - void ThermostatFluxFiltered::output(OUTPUT_LIST & outputData) + void ThermostatIntegratorFluxFiltered::output(OUTPUT_LIST & outputData) { _lambdaPowerOutput_ = lambdaPowerFiltered_->quantity(); // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); @@ -1519,7 +1758,7 @@ namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFixedFiltered + // Class ThermostatIntegratorFixedFiltered //-------------------------------------------------------- //-------------------------------------------------------- @@ -1527,9 +1766,10 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFixedFiltered::ThermostatFixedFiltered(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatFixed(thermostat,regulatorPrefix) + ThermostatIntegratorFixedFiltered::ThermostatIntegratorFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatIntegratorFixed(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } @@ -1542,7 +1782,7 @@ namespace ATC { //-------------------------------------------------------- - void ThermostatFixedFiltered::initialize_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixedFiltered::initialize_delta_nodal_atomic_energy(double dt) { // initialize delta energy DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity()); @@ -1557,7 +1797,7 @@ namespace ATC { // computes the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixedFiltered::compute_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixedFiltered::compute_delta_nodal_atomic_energy(double dt) { // set delta energy based on predicted atomic velocities DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity()); @@ -1572,12 +1812,14 @@ namespace ATC { // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFixedFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFixedFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.resize(nNodes_,1); - const set & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set & regulatedNodes(myRegulatedNodes->quantity()); double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { @@ -1593,14 +1835,18 @@ namespace ATC { // sets up the right-hand side for fixed // (coupling & prescribed) temperature values //-------------------------------------------------------- - void ThermostatFixedFiltered::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFixedFiltered::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { - // (b) for essential bcs (fixed nodes) : + // (b) for essential bcs (fixed nodes): // form rhs : (delThetaV - delTheta)/dt - const set & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set & regulatedNodes(myRegulatedNodes->quantity()); double factor = (1./dt)/keMultiplier_; factor /= timeFilter_->unfiltered_coefficient_post_s1(2.*dt); + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0)); @@ -1615,27 +1861,19 @@ namespace ATC { // output: // adds all relevant output to outputData //-------------------------------------------------------- - void ThermostatFixedFiltered::output(OUTPUT_LIST & outputData) + void ThermostatIntegratorFixedFiltered::output(OUTPUT_LIST & outputData) { _lambdaPowerOutput_ = lambdaPowerFiltered_->quantity(); // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); } } - - // have one for combined and one for lumped (lumped has two sub-thermostats) - // use node sets to call out fixed and fluxed locations - // derived off lumped lambda class that only considers certain atoms and certain nodes based on a mask - // move matrix solver construction to thermostats that actually do something - // thermostats can instantiate the type of shape function object they want in the future, for now we'll need hacks, - // for now we'll have to construct them with appropriate shape function matrix - //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatFluxFixed @@ -1645,7 +1883,8 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatFluxFixed::ThermostatFluxFixed(Thermostat * thermostat, + ThermostatFluxFixed::ThermostatFluxFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, bool constructThermostats) : RegulatorMethod(thermostat), thermostatFlux_(NULL), @@ -1653,8 +1892,8 @@ namespace ATC { thermostatBcs_(NULL) { if (constructThermostats) { - thermostatFlux_ = new ThermostatFlux(thermostat,regulatorPrefix_+"Flux"); - thermostatFixed_ = new ThermostatFixed(thermostat,regulatorPrefix_+"Fixed"); + thermostatFlux_ = new ThermostatIntegratorFlux(thermostat,lambdaMaxIterations,regulatorPrefix_+"Flux"); + thermostatFixed_ = new ThermostatIntegratorFixed(thermostat,lambdaMaxIterations,regulatorPrefix_+"Fixed"); // need to choose BC type based on coupling mode if (thermostat->coupling_mode() == AtomicRegulator::FLUX) { @@ -1694,8 +1933,8 @@ namespace ATC { //-------------------------------------------------------- void ThermostatFluxFixed::initialize() { - thermostatFlux_->initialize(); thermostatFixed_->initialize(); + thermostatFlux_->initialize(); } //-------------------------------------------------------- @@ -1754,11 +1993,12 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatFluxFixedFiltered::ThermostatFluxFixedFiltered(Thermostat * thermostat) : - ThermostatFluxFixed(thermostat,false) + ThermostatFluxFixedFiltered::ThermostatFluxFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations) : + ThermostatFluxFixed(thermostat,lambdaMaxIterations,false) { - thermostatFlux_ = new ThermostatFluxFiltered(thermostat,regulatorPrefix_+"Flux"); - thermostatFixed_ = new ThermostatFixedFiltered(thermostat,regulatorPrefix_+"Fixed"); + thermostatFlux_ = new ThermostatIntegratorFluxFiltered(thermostat,lambdaMaxIterations,regulatorPrefix_+"Flux"); + thermostatFixed_ = new ThermostatIntegratorFixedFiltered(thermostat,lambdaMaxIterations,regulatorPrefix_+"Fixed"); // need to choose BC type based on coupling mode if (thermostat->coupling_mode() == AtomicRegulator::FLUX) { @@ -1779,7 +2019,7 @@ namespace ATC { //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatGlc::ThermostatGlc(Thermostat * thermostat) : + ThermostatGlc::ThermostatGlc(AtomicRegulator * thermostat) : ThermostatShapeFunction(thermostat), timeFilter_(atomicRegulator_->time_filter()), lambdaPowerFiltered_(NULL), @@ -1788,7 +2028,7 @@ namespace ATC { atomMasses_(NULL) { // consistent with stage 3 of ATC_Method::initialize - lambdaPowerFiltered_= thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + lambdaPowerFiltered_= atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); } //-------------------------------------------------------- @@ -1836,7 +2076,7 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatPowerVerlet::ThermostatPowerVerlet(Thermostat * thermostat) : + ThermostatPowerVerlet::ThermostatPowerVerlet(AtomicRegulator * thermostat) : ThermostatGlc(thermostat), nodalTemperatureRoc_(atc_->field_roc(TEMPERATURE)), heatSource_(atc_->atomic_source(TEMPERATURE)), @@ -2045,13 +2285,13 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatHooverVerlet::ThermostatHooverVerlet(Thermostat * thermostat) : + ThermostatHooverVerlet::ThermostatHooverVerlet(AtomicRegulator * thermostat) : ThermostatPowerVerlet(thermostat), lambdaHoover_(NULL), nodalAtomicHooverLambdaPower_(NULL) { // set up data consistent with stage 3 of ATC_Method::initialize - lambdaHoover_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaHoover",1); + lambdaHoover_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaHoover",1); } @@ -2171,7 +2411,7 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(Thermostat * thermostat) : + ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(AtomicRegulator * thermostat) : ThermostatPowerVerlet(thermostat), nodalTemperature2Roc_(atc_->field_2roc(TEMPERATURE)), fieldsRoc_(atc_->fields_roc()), @@ -2263,13 +2503,13 @@ namespace ATC { // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(Thermostat * thermostat) : + ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(AtomicRegulator * thermostat) : ThermostatPowerVerletFiltered(thermostat), lambdaHoover_(NULL), nodalAtomicHooverLambdaPower_(NULL) { // consistent with stage 3 of ATC_Method::initialize - lambdaHoover_ = thermostat_->regulator_data("LambdaHoover",1); + lambdaHoover_ = atomicRegulator_->regulator_data("LambdaHoover",1); } //-------------------------------------------------------- diff --git a/lib/atc/Thermostat.h b/lib/atc/Thermostat.h index aa26581565..65dbba7e15 100644 --- a/lib/atc/Thermostat.h +++ b/lib/atc/Thermostat.h @@ -74,7 +74,7 @@ namespace ATC { public: - ThermostatShapeFunction(Thermostat * thermostat, + ThermostatShapeFunction(AtomicRegulator * thermostat, const std::string & regulatorPrefix = ""); virtual ~ThermostatShapeFunction() {}; @@ -89,9 +89,6 @@ namespace ATC { virtual void set_weights(); // member data - /** pointer to thermostat object for data */ - Thermostat * thermostat_; - /** MD mass matrix */ DIAG_MAN & mdMassMatrix_; @@ -117,8 +114,10 @@ namespace ATC { class ThermostatRescale : public ThermostatShapeFunction { public: + + friend class KinetoThermostatRescale; // since this is sometimes used as a set of member functions for friend - ThermostatRescale(Thermostat * thermostat); + ThermostatRescale(AtomicRegulator * thermostat); virtual ~ThermostatRescale() {}; @@ -128,16 +127,26 @@ namespace ATC { /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[TEMPERATURE] = 0.;}; + /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + /** sets up and solves thermostat equations */ + virtual void compute_thermostat(double dt); + /** apply solution to atomic quantities */ void apply_to_atoms(PerAtomQuantity * atomVelocities); - /** correct the RHS for complex temperature definitions */ - virtual void correct_rhs(DENS_MAT & rhs) {}; // base class does no correction, assuming kinetic definition + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); /** FE temperature field */ DENS_MAN & nodalTemperature_; @@ -165,7 +174,7 @@ namespace ATC { public: - ThermostatRescaleMixedKePe(Thermostat * thermostat); + ThermostatRescaleMixedKePe(AtomicRegulator * thermostat); virtual ~ThermostatRescaleMixedKePe() {}; @@ -177,8 +186,11 @@ namespace ATC { protected: - /** correct the RHS for inclusion of the PE */ - virtual void correct_rhs(DENS_MAT & rhs); + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); /** nodal fluctuating potential energy */ DENS_MAN * nodalAtomicFluctuatingPotentialEnergy_; @@ -196,16 +208,82 @@ namespace ATC { }; - /** - * @class ThermostatGlcFs - * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm + /** + * @class ThermostatFsSolver + * @brief Class for solving the linear system for lambda + * (thermostats have general for of N^T w N lambda = rhs) */ - class ThermostatGlcFs : public ThermostatShapeFunction { + class ThermostatFsSolver : public RegulatorShapeFunction { + public: - ThermostatGlcFs(Thermostat * thermostat, + ThermostatFsSolver(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatFsSolver() {}; + + /** pre-run initialization of method data */ + virtual void initialize(); + + /* sets up and solves the linear system for lambda */ + virtual void compute_lambda(const DENS_MAT & rhs, + bool iterateSolution = true); + + /* scales lambda */ + virtual void scale_lambda(double factor) {*lambda_ *= factor;}; + + /** change the time step factor */ + virtual void set_timestep_factor(double dtFactor) {dtFactor_ = dtFactor;}; + + protected: + + // methods + /** solves the non-linear equation for lambda iteratively */ + void iterate_lambda(const MATRIX & rhs); + + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + // data + /** mapping from all to regulated nodes */ + DENS_MAT rhsMap_; + + /** maximum number of iterations used in iterative solve for lambda */ + int lambdaMaxIterations_; + + /** pointer to the values of lambda interpolated to atoms */ + DENS_MAN * rhsLambdaSquared_; + + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; + + // workspace + DENS_MAT _lambdaOld_; // lambda from previous iteration + DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes + DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop + DENS_VEC _weightVector_, _maskedWeightVector_; + + private: + + // DO NOT define this + ThermostatFsSolver(); + + }; + + /** + * @class ThermostatGlcFs + * @brief Class for thermostat algorithms which perform the time-integration component of the fractional step method + */ + + class ThermostatGlcFs : public RegulatorMethod { + + public: + + ThermostatGlcFs(AtomicRegulator * thermostat, + int lambdaMaxIterations, const std::string & regulatorPrefix = ""); virtual ~ThermostatGlcFs() {}; @@ -225,18 +303,22 @@ namespace ATC { /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /* flag for performing the full lambda prediction calculation */ bool full_prediction(); + /** set up atom to material identification */ + virtual void reset_atom_materials(const Array & elementToMaterialMap, + const MatrixDependencyManager * atomElement); + protected: // methods - /** determine mapping from all nodes to those to which the thermostat applies */ - void compute_rhs_map(); - /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt) = 0; @@ -257,10 +339,17 @@ namespace ATC { virtual void compute_lambda(double dt, bool iterateSolution = true); - /** solves the non-linear equation for lambda iteratively */ - void iterate_lambda(const MATRIX & rhs); - // member data + /** solver for lambda linear system */ + ThermostatFsSolver * lambdaSolver_; + + + /** MD mass matrix */ + DIAG_MAN & mdMassMatrix_; + + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; + /** reference to AtC FE temperature */ DENS_MAN & temperature_; @@ -273,21 +362,18 @@ namespace ATC { /** filtered lambda power */ DENS_MAN * lambdaPowerFiltered_; + /** lambda at atomic locations */ + PerAtomQuantity * atomLambdas_; + /** atomic force induced by lambda */ AtomicThermostatForce * atomThermostatForces_; /** pointer to atom masses */ FundamentalAtomQuantity * atomMasses_; - /** pointer to the values of lambda interpolated to atoms */ - DENS_MAN * rhsLambdaSquared_; - /** hack to determine if first timestep has been passed */ bool isFirstTimestep_; - /** maximum number of iterations used in iterative solve for lambda */ - int lambdaMaxIterations_; - /** nodal atomic energy */ DENS_MAN * nodalAtomicEnergy_; @@ -309,19 +395,10 @@ namespace ATC { /** right-hand side data for thermostat equation */ DENS_MAT rhs_; - /** mapping from all to regulated nodes */ - DENS_MAT rhsMap_; - - /** fraction of timestep over which constraint is exactly enforced */ - double dtFactor_; - // workspace DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied DENS_VEC _lambdaOverlap_; // lambda in MD overlapping FE nodes - DENS_MAT _lambdaOld_; // lambda from previous iteration - DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes - DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop private: @@ -331,21 +408,50 @@ namespace ATC { }; /** - * @class ThermostatFlux + * @class ThermostatSolverFlux * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ - class ThermostatFlux : public ThermostatGlcFs { + class ThermostatSolverFlux : public ThermostatFsSolver { public: - ThermostatFlux(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatSolverFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFlux() {}; + virtual ~ThermostatSolverFlux() {}; /** instantiate all needed data */ virtual void construct_transfers(); + + protected: + + // methods + /** sets up the transfer which is the set of nodes being regulated */ + virtual void construct_regulated_nodes(); + + private: + + // DO NOT define this + ThermostatSolverFlux(); + + }; + + /** + * @class ThermostatIntegratorFlux + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration + */ + + class ThermostatIntegratorFlux : public ThermostatGlcFs { + + public: + + ThermostatIntegratorFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatIntegratorFlux() {}; /** pre-run initialization of method data */ virtual void initialize(); @@ -361,9 +467,6 @@ namespace ATC { DENS_MAT & deltaEnergy, double dt); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes(); - // data /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN & heatSource_; @@ -371,23 +474,55 @@ namespace ATC { private: // DO NOT define this - ThermostatFlux(); + ThermostatIntegratorFlux(); }; /** - * @class ThermostatFixed + * @class ThermostatSolverFixed * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ - class ThermostatFixed : public ThermostatGlcFs { + class ThermostatSolverFixed : public ThermostatFsSolver { public: - ThermostatFixed(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatSolverFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFixed() {}; + virtual ~ThermostatSolverFixed() {}; + + /** instantiate all needed data */ + virtual void construct_transfers(); + + protected: + + // methods + /** sets up the transfer which is the set of nodes being regulated */ + virtual void construct_regulated_nodes(); + + private: + + // DO NOT define this + ThermostatSolverFixed(); + + }; + + /** + * @class ThermostatIntegratorFixed + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration + */ + + class ThermostatIntegratorFixed : public ThermostatGlcFs { + + public: + + ThermostatIntegratorFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatIntegratorFixed() {}; /** instantiate all needed data */ virtual void construct_transfers(); @@ -436,9 +571,6 @@ namespace ATC { /** flag for halving the applied force to mitigate numerical errors */ bool halve_force(); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes(); - // data /** change in FE energy over a timestep */ DENS_MAT deltaFeEnergy_; @@ -470,24 +602,25 @@ namespace ATC { private: // DO NOT define this - ThermostatFixed(); + ThermostatIntegratorFixed(); }; /** - * @class ThermostatFluxFiltered - * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration + * @class ThermostatIntegratorFluxFiltered + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration * in conjunction with time filtering */ - class ThermostatFluxFiltered : public ThermostatFlux { + class ThermostatIntegratorFluxFiltered : public ThermostatIntegratorFlux { public: - ThermostatFluxFiltered(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatIntegratorFluxFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFluxFiltered() {}; + virtual ~ThermostatIntegratorFluxFiltered() {}; /** pre-run initialization of method data */ virtual void initialize(); @@ -518,24 +651,25 @@ namespace ATC { private: // DO NOT define this - ThermostatFluxFiltered(); + ThermostatIntegratorFluxFiltered(); }; /** - * @class ThermostatFixedFiltered + * @class ThermostatIntegratorFixedFiltered * @brief Class for thermostatting using the temperature matching constraint and is compatible with the fractional step time-integration with time filtering */ - class ThermostatFixedFiltered : public ThermostatFixed { + class ThermostatIntegratorFixedFiltered : public ThermostatIntegratorFixed { public: - ThermostatFixedFiltered(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatIntegratorFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFixedFiltered() {}; + virtual ~ThermostatIntegratorFixedFiltered() {}; /** get data for output */ virtual void output(OUTPUT_LIST & outputData); @@ -561,7 +695,7 @@ namespace ATC { private: // DO NOT define this - ThermostatFixedFiltered(); + ThermostatIntegratorFixedFiltered(); }; @@ -574,7 +708,8 @@ namespace ATC { public: - ThermostatFluxFixed(Thermostat * thermostat, + ThermostatFluxFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, bool constructThermostats = true); virtual ~ThermostatFluxFixed(); @@ -605,10 +740,10 @@ namespace ATC { // data /** thermostat for imposing the fluxes */ - ThermostatFlux * thermostatFlux_; + ThermostatIntegratorFlux * thermostatFlux_; /** thermostat for imposing fixed nodes */ - ThermostatFixed * thermostatFixed_; + ThermostatIntegratorFixed * thermostatFixed_; /** pointer to whichever thermostat should compute the flux, based on coupling method */ ThermostatGlcFs * thermostatBcs_; @@ -628,7 +763,8 @@ namespace ATC { public: - ThermostatFluxFixedFiltered(Thermostat * thermostat); + ThermostatFluxFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations); virtual ~ThermostatFluxFixedFiltered(){}; @@ -648,7 +784,7 @@ namespace ATC { public: - ThermostatGlc(Thermostat * thermostat); + ThermostatGlc(AtomicRegulator * thermostat); virtual ~ThermostatGlc() {}; @@ -699,7 +835,7 @@ namespace ATC { public: - ThermostatPowerVerlet(Thermostat * thermostat); + ThermostatPowerVerlet(AtomicRegulator * thermostat); virtual ~ThermostatPowerVerlet() {}; @@ -770,7 +906,7 @@ namespace ATC { public: - ThermostatHooverVerlet(Thermostat * thermostat); + ThermostatHooverVerlet(AtomicRegulator * thermostat); virtual ~ThermostatHooverVerlet() {}; @@ -825,7 +961,7 @@ namespace ATC { public: - ThermostatPowerVerletFiltered(Thermostat * thermostat); + ThermostatPowerVerletFiltered(AtomicRegulator * thermostat); virtual ~ThermostatPowerVerletFiltered(){}; @@ -875,7 +1011,7 @@ namespace ATC { public: - ThermostatHooverVerletFiltered(Thermostat * thermostat); + ThermostatHooverVerletFiltered(AtomicRegulator * thermostat); virtual ~ThermostatHooverVerletFiltered() {}; diff --git a/lib/atc/TimeIntegrator.h b/lib/atc/TimeIntegrator.h index 22e4b716fd..2753386d06 100644 --- a/lib/atc/TimeIntegrator.h +++ b/lib/atc/TimeIntegrator.h @@ -109,13 +109,15 @@ namespace ATC { /** types of time integration */ enum TimeIntegrationType { + NONE=0, STEADY, VERLET, GEAR, FRACTIONAL_STEP, EXPLICIT, IMPLICIT, - CRANK_NICOLSON + CRANK_NICOLSON, + DIRECT }; // constructor diff --git a/lib/atc/TransferLibrary.cpp b/lib/atc/TransferLibrary.cpp index f4cdb172aa..a6bba5ca23 100644 --- a/lib/atc/TransferLibrary.cpp +++ b/lib/atc/TransferLibrary.cpp @@ -418,7 +418,7 @@ namespace ATC { // Constructor //-------------------------------------------------------- ElementMaskNodeSet::ElementMaskNodeSet(ATC_Coupling * atc, - RegulatedNodes * nodeSet) : + SetDependencyManager * nodeSet) : nodeSet_(nodeSet), feMesh_((atc->fe_engine())->fe_mesh()) { diff --git a/lib/atc/TransferLibrary.h b/lib/atc/TransferLibrary.h index 380cb6dcf3..3a0f420f37 100644 --- a/lib/atc/TransferLibrary.h +++ b/lib/atc/TransferLibrary.h @@ -899,7 +899,7 @@ namespace ATC { // constructor ElementMaskNodeSet(ATC_Coupling * atc, - RegulatedNodes * nodeSet); + SetDependencyManager * nodeSet); // destructor virtual ~ElementMaskNodeSet() { @@ -912,7 +912,7 @@ namespace ATC { protected: /** transfer determining used nodes */ - RegulatedNodes * nodeSet_; + SetDependencyManager * nodeSet_; /** finite element mesh */ const FE_Mesh * feMesh_; diff --git a/lib/atc/Utility.h b/lib/atc/Utility.h index aa5104293f..f3bbd7d1fb 100644 --- a/lib/atc/Utility.h +++ b/lib/atc/Utility.h @@ -20,10 +20,11 @@ namespace ATC_Utility { /** constants */ - static const double Pi = 4.0*atan(1.0); - static const double Big = 1.e20; - const static double parsetol = 1.0e-10; - const static double parsebig = 1.0e10; + static const double Pi_ = 4.0*atan(1.0); + static const double Big_ = 1.e20; + const static double parsetol_ = 1.0e-8; + //const static double parsetol_ = 1.0e-10; + const static double parsebig_ = 1.0e10; /** scalar triple product */ inline double det3(double * v1, double * v2, double * v3) { @@ -49,6 +50,7 @@ namespace ATC_Utility inline double norm3(double * v) {return sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); } inline int sgn(double x) { return (int) ((x>0) - (x<0)); } +// template int sgn(T val) { return (T(0) < val) - (val < T(0)); } inline int rnd(double x) { return (int) (x+sgn(x)*0.5); } /** Compares doubles acceptably */ @@ -63,22 +65,28 @@ namespace ATC_Utility tolMult * std::max(abs(dblL), abs(dblR)))))); } - + inline double tolerance(double x, double tol = parsetol_) { + return std::max(tol,tol*fabs(x)); + } + inline double nudge_up(double x) { return x+tolerance(x); } + inline double nudge_down(double x) { return x-tolerance(x); } inline double parse_min(const char * arg) { - if (std::strcmp(arg,"INF") == 0) return -parsebig; - else return atof(arg); } + if (std::strcmp(arg,"INF") == 0) return -parsebig_; + else if (std::strcmp(arg,"-INF") == 0) return -parsebig_; + else return (atof(arg)); + } inline double parse_max(const char * arg) { - if (std::strcmp(arg,"INF") == 0) return parsebig; - else return atof(arg); + if (std::strcmp(arg,"INF") == 0) return parsebig_; + else return (atof(arg)); } inline double parse_minmax(const char * arg) { - if (std::strcmp(arg,"-INF") == 0) return -parsebig; - else if (std::strcmp(arg,"INF") == 0) return parsebig; - else return atof(arg); } + if (std::strcmp(arg,"-INF") == 0) return -parsebig_; + else if (std::strcmp(arg,"INF") == 0) return parsebig_; + else return atof(arg); + } inline void split_values(double & min, double & max) { - double eps = std::max(parsetol,parsetol*fabs(min)); - min -= eps; - max += eps; + min = nudge_down(min); + max = nudge_up(max); } @@ -165,11 +173,25 @@ namespace ATC_Utility inline std::string to_string(const T &v, int precision=0) { std::ostringstream out; - if (precision) out << std::setprecision(precision); + if (precision) { + out << std::setprecision(precision); + out << std::setw(precision+3); + out << std::showpoint; + } out << v; + out << std::noshowpoint; return out.str(); } + /** formatted double to a string */ + inline std::string to_string(int precision, const double v) + { + char b[50]; + sprintf(b, "%*.*f",4+precision,precision, v); + std::string s(b); + return s; + } + /** conversion to string */ inline std::string true_false(const double &v) { @@ -205,6 +227,13 @@ namespace ATC_Utility if(endptr != NULL && *endptr == '\0') return true; return false; } + + inline bool is_number(const std::string& s) + { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); + } /** convert a string to an int */ inline int str2int(const std::string &s) { return str2T(s, int(4)); } diff --git a/lib/atc/Vector.h b/lib/atc/Vector.h index 5630b0ee13..338183d00b 100644 --- a/lib/atc/Vector.h +++ b/lib/atc/Vector.h @@ -52,6 +52,7 @@ public: static bool same_size(const Vector &a, const Vector &b); protected: + void _set_equal(const Matrix &r); //* don't allow this Vector& operator=(const Vector &r); }; @@ -199,7 +200,34 @@ inline bool Vector::same_size(const Vector &a, const Vector &b) { return a.same_size(b); } - +//---------------------------------------------------------------------------- +// general matrix assignment (for densely packed matrices) +//---------------------------------------------------------------------------- +template +void Vector::_set_equal(const Matrix &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix *pr = &r; +#ifdef OBSOLETE + if (const SparseMatrix *ps = dynamic_cast*>(pr))//sparse_cast(pr)) + copy_sparse_to_matrix(ps, *this); + + else if (dynamic_cast*>(pr))//diag_cast(pr)) // r is Diagonal? + { + this->zero(); + for (INDEX i=0; iptr(), r.ptr(), r.size()*sizeof(T)); +#else + const Vector *pv = dynamic_cast*> (pr); + if (pv) this->copy(pv->ptr(),pv->nRows()); + else + { + std::cout <<"Error in general vector assignment\n"; + exit(1); + } +#endif +} } // end namespace diff --git a/lib/atc/WeakEquationElectronContinuity.cpp b/lib/atc/WeakEquationElectronContinuity.cpp index 67ffc581d8..3b2a18b0f2 100644 --- a/lib/atc/WeakEquationElectronContinuity.cpp +++ b/lib/atc/WeakEquationElectronContinuity.cpp @@ -82,6 +82,7 @@ void WeakEquationElectronEquilibrium::M_integrand( } //--------------------------------------------------------------------- + bool WeakEquationElectronEquilibrium::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, diff --git a/lib/atc/WeakEquationElectronMomentum.cpp b/lib/atc/WeakEquationElectronMomentum.cpp index 4ae48ed709..37484f61d5 100644 --- a/lib/atc/WeakEquationElectronMomentum.cpp +++ b/lib/atc/WeakEquationElectronMomentum.cpp @@ -172,6 +172,7 @@ bool WeakEquationElectronMomentumDDM::N_integrand( FIELD_MATS::const_iterator nField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = nField->second; + CLON_VEC tsx(flux,CLONE_COL,0); CLON_VEC tsy(flux,CLONE_COL,1); diff --git a/lib/atc/WeakEquationElectronTemperature.cpp b/lib/atc/WeakEquationElectronTemperature.cpp index 3841f619ad..97a21628d8 100644 --- a/lib/atc/WeakEquationElectronTemperature.cpp +++ b/lib/atc/WeakEquationElectronTemperature.cpp @@ -130,16 +130,18 @@ bool WeakEquationElectronTemperatureJouleHeating::N_integrand( const Material * material, DENS_MAT &flux) const { + // call base class to get electron_temperature terms WeakEquationElectronTemperature::N_integrand(fields, grad_fields, material, flux); + // Joule heating = -I.grad Psi = J.grad Psi \approx J.E DENS_MAT jouleHeating; - material->electron_flux (fields, grad_fields, _J_); + material->electron_flux (fields, grad_fields, _J_); material->electric_field(fields, grad_fields, _E_); jouleHeating = _J_[0].mult_by_element(_E_[0]); for (DENS_MAT_VEC::size_type i=1; i < _J_.size(); i++) jouleHeating += _J_[i].mult_by_element(_E_[i]); jouleHeating *= eV2E_; - flux += jouleHeating; + flux -= jouleHeating; return true; } @@ -151,7 +153,7 @@ bool WeakEquationElectronTemperatureJouleHeating::N_integrand( // Constructor //-------------------------------------------------------------- WeakEquationElectronTemperatureConvection::WeakEquationElectronTemperatureConvection() - : WeakEquationElectronTemperature() + : WeakEquationElectronTemperatureJouleHeating() { int nSD = 3; _convectiveFlux_.assign(nSD, DENS_MAT()); @@ -174,7 +176,7 @@ void WeakEquationElectronTemperatureConvection::B_integrand( { // add diffusion term - WeakEquationElectronTemperature::B_integrand(fields, grad_fields, material, flux); + WeakEquationElectronTemperatureJouleHeating::B_integrand(fields, grad_fields, material, flux); //flux[0] = 0.; //flux[1] = 0.; //flux[2] = 0.; @@ -195,7 +197,8 @@ bool WeakEquationElectronTemperatureConvection::N_integrand( DENS_MAT &flux) const { // call base class to get electron_temperature terms - WeakEquationElectronTemperature::N_integrand(fields, grad_fields, material, flux); + WeakEquationElectronTemperatureJouleHeating::N_integrand(fields, grad_fields, material, flux); +#ifdef TEST // add exchange with kinetic energy DENS_MAT keExchange; DENS_MAT capacity; @@ -220,7 +223,7 @@ bool WeakEquationElectronTemperatureConvection::N_integrand( keExchange *= capacity; flux -= keExchange; - +#endif return true; } diff --git a/lib/atc/WeakEquationElectronTemperature.h b/lib/atc/WeakEquationElectronTemperature.h index 8680de31a5..1a5ecbab00 100644 --- a/lib/atc/WeakEquationElectronTemperature.h +++ b/lib/atc/WeakEquationElectronTemperature.h @@ -136,7 +136,7 @@ class WeakEquationElectronTemperatureJouleHeating : */ class WeakEquationElectronTemperatureConvection : - public WeakEquationElectronTemperature + public WeakEquationElectronTemperatureJouleHeating { public: