diff --git a/lib/atc/ATC_Coupling.cpp b/lib/atc/ATC_Coupling.cpp new file mode 100644 index 0000000000..d9ca2be878 --- /dev/null +++ b/lib/atc/ATC_Coupling.cpp @@ -0,0 +1,1711 @@ +// ATC headers +#include "ATC_Coupling.h" +#include "FE_Engine.h" +#include "Array.h" +#include "Array2D.h" +#include "ATC_Error.h" +#include "PrescribedDataManager.h" +#include "AtomicRegulator.h" +#include "TimeIntegrator.h" +#include "PhysicsModel.h" +#include "AtomToMoleculeTransfer.h" +#include "MoleculeSet.h" + +namespace ATC { + //-------------------------------------------------- + ATC_Coupling::ATC_Coupling(string groupName, double ** & perAtomArray, LAMMPS_NS::Fix * thisFix) : + ATC_Method(groupName, perAtomArray, thisFix), + consistentInitialization_(false), + equilibriumStart_(false), + useFeMdMassMatrix_(false), + trackCharge_(false), + temperatureDef_(NONE), + prescribedDataMgr_(NULL), + physicsModel_(NULL), + extrinsicModelManager_(this), + atomicRegulator_(NULL), + atomQuadForInternal_(true), + elementMask_(NULL), + internalToMask_(NULL), + internalElement_(NULL), + ghostElement_(NULL), + nodalGeometryType_(NULL), + bndyIntType_(NO_QUADRATURE), + bndyFaceSet_(NULL), + atomicWeightsMask_(NULL), + shpFcnMask_(NULL), + shpFcnDerivsMask_(NULL), + sourceIntegration_(FULL_DOMAIN) + { + // size the field mask + fieldMask_.reset(NUM_FIELDS,NUM_FLUX); + fieldMask_ = false; + // default: no consistent mass matrices + useConsistentMassMatrix_.reset(NUM_FIELDS); + useConsistentMassMatrix_ = false; + mdMassNormalization_ = true; + // check to see if lammps has any charges + if (lammpsInterface_->atom_charge()) trackCharge_ = true; + } + //-------------------------------------------------- + ATC_Coupling::~ATC_Coupling() + { + interscaleManager_.clear(); + if (feEngine_) { delete feEngine_; feEngine_ = NULL; } + if (physicsModel_) delete physicsModel_; + if (atomicRegulator_) delete atomicRegulator_; + if (prescribedDataMgr_) delete prescribedDataMgr_; + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + delete _tiIt_->second; + } + } + //-------------------------------------------------- + // Interactions with LAMMPS fix commands + // parse input command and pass on to finite element engine + // or physics specific transfers if necessary + // revert to physics-specific transfer if no command matches input + // first keyword is unique to particular class + // base class keyword matching must apply to ALL physics + // order: derived, base, owned objects + //-------------------------------------------------- + bool ATC_Coupling::modify(int narg, char **arg) + { + FieldName thisField; + int thisIndex; + int argIdx=0; + + bool match = false; + + // gateways to other modules e.g. extrinsic, control, mesh + // pass off to extrinsic + if (strcmp(arg[argIdx],"extrinsic")==0) { + argIdx++; + match = extrinsicModelManager_.modify(narg-argIdx,&arg[argIdx]); + } + // catch special case + if ((strcmp(arg[argIdx],"control")==0) + &&(strcmp(arg[argIdx+1],"charge")==0)) { + match = extrinsicModelManager_.modify(narg-argIdx,&arg[argIdx]); + } + // parsing handled here + else { + /*! \page man_initial fix_modify AtC initial + \section syntax + fix_modify AtC initial + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of nodes to apply boundary condition + - = value or name of function followed by its + parameters + \section examples + fix_modify atc initial temperature groupNAME 10. + \section description + Sets the initial values for the specified field at the specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_internal + \section default + none + */ + // set initial conditions + if (strcmp(arg[argIdx],"initial")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_initial_field(nsetName,thisField,thisIndex,f); + match = true; + } + + /*! \page man_fix_nodes fix_modify AtC fix + \section syntax + 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 + \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 + \section description + Creates a constraint on the values of the specified field at specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_unfix_nodes + \section default + none + */ + // fix and unfix nodes + else if (strcmp(arg[argIdx],"fix")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + match = true; + } + + /*! \page man_unfix_nodes fix_modify AtC unfix + \section syntax + fix_modify AtC unfix + - = field name valid for type of physics + - = name of set of nodes + \section examples + fix_modify AtC unfix temperature groupNAME + \section description + Removes constraint on field values for specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_fix_nodes + \section default + none + */ + else if (strcmp(arg[argIdx],"unfix")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_field(nsetName,thisField,thisIndex); + match = true; + } + + /*! \page man_source fix_modify AtC source + \section syntax + fix_modify AtC source + - = field name valid for type of physics + - = name of set of elements + \section examples + fix_modify atc source temperature middle temporal_ramp 10. 0. + \section description + Add domain sources to the mesh. The units are consistent with LAMMPS's + units for mass, length and time and are defined by the PDE being solved, + e.g. for thermal transfer the balance equation is for energy and source + is energy per time. + \section restrictions + keyword 'all' reserved in element_set name + \section related + see \ref man_remove_source + \section default + none + */ + else if (strcmp(arg[argIdx],"source")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string esetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); + fieldMask_(thisField,PRESCRIBED_SOURCE) = true; + match = true; + } + + /*! \page man_remove_source fix_modify AtC remove_source + \section syntax + fix_modify AtC remove_source + - = field name valid for type of physics + - = name of set of elements + \section examples + fix_modify atc remove_source temperature groupNAME + \section description + Remove a domain source. + \section restrictions + keyword 'all' reserved in element_set name + \section related + see \ref man_source + \section default + */ + else if (strcmp(arg[argIdx],"remove_source")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string esetName(arg[argIdx++]); + prescribedDataMgr_->unfix_source(esetName,thisField,thisIndex); + fieldMask_(thisField,PRESCRIBED_SOURCE) = false; + match = true; + } + + /*! \page man_fix_flux fix_modify AtC fix_flux + \section syntax + fix_modify AtC fix_flux + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of element faces + \section examples + fix_modify atc fix_flux temperature faceSet 10.0 \n + + \section description + Command for fixing normal fluxes e.g. heat_flux. + This command only prescribes the normal component of the physical flux, e.g. heat (energy) flux. + The units are in AtC units, i.e. derived from the LAMMPS length, time, and mass scales. + \section restrictions + Only normal fluxes (Neumann data) can be prescribed. + \section related + see \ref man_unfix_flux + \section default + */ + else if (strcmp(arg[argIdx],"fix_flux")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_flux(fsetName,thisField,thisIndex,f); + fieldMask_(thisField,PRESCRIBED_SOURCE) = true; + match = true; + } + + /*! \page man_unfix_flux fix_modify AtC unfix_flux + \section syntax + fix_modify AtC fix_flux + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of element faces + \section examples + fix_modify atc unfix_flux temperature faceSet \n + + \section description + Command for removing prescribed normal fluxes e.g. heat_flux, stress. + \section restrictions + \section related + see \ref man_unfix_flux + \section default + */ + else if (strcmp(arg[argIdx],"unfix_flux")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_flux(fsetName,thisField,thisIndex); + fieldMask_(thisField,PRESCRIBED_SOURCE) = false; + match = true; + } + + + /*! \page man_fe_md_boundary fix_modify AtC fe_md_boundary + \section syntax + fix_modify AtC fe_md_boundary [args] + \section examples + fix_modify atc fe_md_boundary interpolate \n + \section description + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIdx],"fe_md_boundary")==0) { + bndyIntType_ = FE_INTERPOLATION;// default + if(strcmp(arg[argIdx],"faceset")==0) { + argIdx++; + bndyIntType_ = FE_QUADRATURE; + string name(arg[argIdx++]); + bndyFaceSet_ = & ( (feEngine_->fe_mesh())->faceset(name)); + } + else if (strcmp(arg[argIdx],"interpolate")==0) { + argIdx++; + bndyIntType_ = FE_INTERPOLATION; + } + else if (strcmp(arg[argIdx],"no_boundary")==0) { + bndyIntType_ = NO_QUADRATURE; + } + else { + throw ATC_Error("Bad boundary integration type"); + } + } + + + + /*! \page man_boundary_faceset fix_modify AtC boundary_faceset + \section syntax + fix_modify AtC boundary_faceset [args] + \section examples + fix_modify AtC boundary_faceset is obndy + \section description + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIdx],"boundary_faceset")==0) { + argIdx++; + if (strcmp(arg[argIdx],"is")==0) { // replace existing faceset + argIdx++; + boundaryFaceNames_.clear(); + string name(arg[argIdx++]); + boundaryFaceNames_.insert(name); + match = true; + } + else if (strcmp(arg[argIdx],"add")==0) { // add this faceset to list + argIdx++; + string name(arg[argIdx]); + boundaryFaceNames_.insert(name); + match = true; + } + } + + /*! \page man_internal_quadrature fix_modify AtC internal_quadrature + \section syntax + fix_modify atc internal_quadrature [region] + \section examples + fix_modify atc internal_quadrature off + \section description + Command use or not use atomic quadrature on internal elements + fully filled with atoms. By turning the internal quadrature off + these elements do not contribute to the governing PDE and the fields + at the internal nodes follow the weighted averages of the atomic data. + \section optional + Optional region tag specifies which finite element nodes will be treated + as being within the MD region. This option is only valid with + internal_quadrature off. + \section restrictions + \section related + \section default + on + */ + else if (strcmp(arg[argIdx],"internal_quadrature")==0) { + if (initialized_) { + throw ATC_Error("Cannot change internal_quadrature method after first run"); + } + argIdx++; + if (strcmp(arg[argIdx],"on")==0) { + argIdx++; + atomQuadForInternal_ = true; + match = true; + } + else if (strcmp(arg[argIdx],"off")==0) { + argIdx++; + if (argIdx == narg) { + atomQuadForInternal_ = false; + regionID_ = -1; + match = true; + } + else { + for (regionID_ = 0; regionID_ < lammpsInterface_->nregion(); regionID_++) + if (strcmp(arg[argIdx],lammpsInterface_->region_name(regionID_)) == 0) break; + if (regionID_ < lammpsInterface_->nregion()) { + atomQuadForInternal_ = false; + match = true; + } + else { + throw ATC_Error("Region " + string(arg[argIdx]) + " does not exist"); + } + } + } + if (match) { + needReset_ = true; + } + } + + else if (strcmp(arg[argIdx],"fix_robin")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + UXT_Function * f = NULL; + // parse linear + if (narg == argIdx+2) { + f = UXT_Function_Mgr::instance()->linear_function(atof(arg[argIdx]),atof(arg[argIdx+1])); + } + // parse function + else { + throw ATC_Error("unimplemented function"); + } + prescribedDataMgr_->fix_robin(fsetName,thisField,thisIndex,f); + fieldMask_(thisField,ROBIN_SOURCE) = true; + match = true; + } + else if (strcmp(arg[argIdx],"unfix_robin")==0) { + argIdx++; + parse_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_robin(fsetName,thisField,thisIndex); + fieldMask_(thisField,ROBIN_SOURCE) = false; + match = true; + } + + /*! \page man_atomic_charge fix_modify AtC atomic_charge + - = switch to activiate/deactiviate inclusion of intrinsic atomic charge in ATC + \section examples + fix_modify atc compute include atomic_charge + \section description + Determines whether AtC tracks the total charge as a finite element field + \section restrictions + Required for: electrostatics + \section related + \section default + if the atom charge is defined, default is on, otherwise default is off + */ + else if (strcmp(arg[argIdx],"include")==0) { + argIdx++; + if (strcmp(arg[argIdx],"atomic_charge")==0) { + trackCharge_ = true; + match = true; + needReset_ = true; + } + } + else if (strcmp(arg[argIdx],"omit")==0) { + argIdx++; + if (strcmp(arg[argIdx],"atomic_charge")==0) { + trackCharge_ = false; + match = true; + needReset_ = true; + } + } + + /*! \page man_source_integration fix_modify AtC source_integration < fe | atom> + \section examples + fix_modify atc source_integration atom + \section description + \section restrictions + \section related + \section default + Default is fe + */ + else if (strcmp(arg[argIdx],"source_integration")==0) { + argIdx++; + if (strcmp(arg[argIdx],"fe")==0) { + sourceIntegration_ = FULL_DOMAIN; + } + else { + sourceIntegration_ = FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE; + } + match = true; + } + + /*! \page man_consistent_fe_initialization fix_modify AtC consistent_fe_initialization + - = switch to activiate/deactiviate the intial setting of FE intrinsic field to match the projected MD field + \section examples + fix_modify atc compute include atomic_charge + \section description + Determines whether AtC initializes FE intrinsic fields (e.g., temperature) to match the projected MD values. This is particularly useful for fully overlapping simulations. + \section restrictions + Can be used with: thermal, two_temperature + Cannot be used with time filtering on + Does not include boundary nodes + \section related + \section default + Default is off + */ + else if (strcmp(arg[argIdx],"consistent_fe_initialization")==0) { + argIdx++; + if (strcmp(arg[argIdx],"on")==0) { + if (timeFilterManager_.filter_dynamics()) + throw ATC_Error("Consistent FE initialization cannot be used with time filtering"); + consistentInitialization_ = true; + match = true; + } + else if (strcmp(arg[argIdx],"off")==0) { + consistentInitialization_ = false; + match = true; + } + } + + // switch for equilibrium filtering start + /*! \page man_equilibrium_start fix_modify AtC equilibrium_start + \section syntax + fix_modify AtC equilibrium_start + + \section examples + fix_modify atc equilibrium_start on \n + + \section description + Starts filtered calculations assuming they start in equilibrium, i.e. perfect finite element force balance. + + \section restrictions + only needed before filtering is begun + + \section related + see \ref man_time_filter + + \section default + on + */ + else if (strcmp(arg[argIdx],"equilibrium_start")==0) { + argIdx++; + if (strcmp(arg[argIdx],"on")==0) { + equilibriumStart_ = true; + match = true; + } + else if (strcmp(arg[argIdx],"off")==0) { + equilibriumStart_ = false; + match = true; + } + } + + /*! \page man_mass_matrix fix_modify AtC mass_matrix + - = activiate/deactiviate using the FE mass matrix in the MD region + \section examples + fix_modify atc mass_matrix fe + \section description + Determines whether AtC uses the FE mass matrix based on Gaussian quadrature or based on atomic quadrature in the MD region. This is useful for fully overlapping simulations to improve efficiency. + \section restrictions + Should not be used unless the FE region is contained within the MD region, + otherwise the method will be unstable and inaccurate + \section related + \section default + Default is off + */ + + else if (strcmp(arg[argIdx],"mass_matrix")==0) { + argIdx++; + if (strcmp(arg[argIdx],"fe")==0) { + useFeMdMassMatrix_ = true; + match = true; + } + else { + useFeMdMassMatrix_ = false; + match = true; + } + if (match) { + needReset_ = true; + } + } + + /*! \page man_material fix_modify AtC material + \section syntax + fix_modify AtC material [elementset_name] [material_id] \n + \section examples + fix_modify AtC material gap_region 2 + \section description + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIdx],"material")==0) { + argIdx++; + string elemsetName(arg[argIdx++]); + int matId = physicsModel_->material_index(arg[argIdx++]); + using std::set; + set elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); + if(elementToMaterialMap_.size() == 0) { + throw ATC_Error("need mesh before material command"); + } + // set elementToMaterialMap + set::const_iterator iset; + for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { + int ielem = *iset; + + // and the tag a string + elementToMaterialMap_(ielem) = matId; + } + match = true; + needReset_ = true; + } + + } // end else + // no match, call base class parser + if (!match) { + match = ATC_Method::modify(narg, arg); + } + return match; // return to FixATC + } + + //-------------------------------------------------- + /** PDE type */ + WeakEquation::PDE_Type ATC_Coupling::pde_type(const FieldName fieldName) const + { + const WeakEquation * weakEq = physicsModel_->weak_equation(fieldName); + if (weakEq == NULL) return WeakEquation::PROJECTION_PDE; + return weakEq->type(); + } + //-------------------------------------------------- + /** is dynamic PDE */ + bool ATC_Coupling::is_dynamic(const FieldName fieldName) const + { + const WeakEquation * weakEq = physicsModel_->weak_equation(fieldName); + if (weakEq == NULL) return false; + return (physicsModel_->weak_equation(fieldName)->type() == WeakEquation::DYNAMIC_PDE); + } + + + //-------------------------------------------------- + /** allow FE_Engine to construct data manager after mesh is constructed */ + void ATC_Coupling::construct_prescribed_data_manager (void) { + prescribedDataMgr_ = new PrescribedDataManager(feEngine_,fieldSizes_); + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_Coupling::pack_fields(RESTART_LIST & data) + { + ATC_Method::pack_fields(data); + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pack_fields(data); + } + } + + //-------------------------------------------------------------- + // create_physics_model + // - method to create physics model + //-------------------------------------------------------------- + void ATC_Coupling::create_physics_model(const PhysicsType & physicsType, + string matFileName) + { + if (physicsModel_) { + throw ATC_Error("Attempted to create PhysicsModel multiple times in ATC_Coupling"); + } + // Create PhysicsModel based on physicsType + switch (physicsType) { + case NO_PHYSICS : + break; + case THERMAL : + physicsModel_ = new PhysicsModelThermal(matFileName); + break; + case ELASTIC : + physicsModel_ = new PhysicsModelElastic(matFileName); + break; + case SHEAR: + physicsModel_ = new PhysicsModelShear(matFileName); + break; + case SPECIES : + physicsModel_ = new PhysicsModelSpecies(matFileName); + break; + case THERMO_ELASTIC : + physicsModel_ = new PhysicsModelThermoElastic(matFileName); + break; + default: + throw ATC_Error("Unknown physics type in ATC_Coupling::create_physics_model"); + } + } + //-------------------------------------------------------- + void ATC_Coupling::init_integrate_velocity() + { + const DENS_MAT & m(atomMasses_->quantity()); + double dt = 0.5 * lammpsInterface_->dt(); + + _deltaQuantity_ = atomForces_->quantity(); + for (int i = 0; i < nLocal_; i++) + for (int j = 0; j < nsd_; j ++) + _deltaQuantity_(i,j) *= dt/m(i,0); + + (*atomVelocities_) += _deltaQuantity_; + } + //-------------------------------------------------------- + void ATC_Coupling::init_integrate_position() + { + double dt = lammpsInterface_->dt(); + _deltaQuantity_ = atomVelocities_->quantity(); + _deltaQuantity_ *= dt; + (*atomPositions_) += _deltaQuantity_; + } + //-------------------------------------------------------- + void ATC_Coupling::final_integrate() + { + const DENS_MAT & m(atomMasses_->quantity()); + double dt = 0.5 * lammpsInterface_->dt(); + + _deltaQuantity_ = atomForces_->quantity(); + for (int i = 0; i < nLocal_; i++) + for (int j = 0; j < nsd_; j ++) + _deltaQuantity_(i,j) *= dt/m(i,0); + + (*atomVelocities_) += _deltaQuantity_; + } + //-------------------------------------------------------- + void ATC_Coupling::construct_methods() + { + // construct needed time filters for mass matrices + if (timeFilterManager_.need_reset()) { + init_filter(); + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + // fill in mass matrix time filters if needed + if (!massMatTimeFilters_[thisField]) + massMatTimeFilters_[thisField] = timeFilterManager_.construct(TimeFilterManager::INSTANTANEOUS); + } + } + } + //------------------------------------------------------------------- + void ATC_Coupling::init_filter() + { + if (timeFilterManager_.need_reset()) { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + (nodalAtomicFieldsRoc_[thisField].set_quantity()).reset(nNodes_,thisSize); + } + } + } + //-------------------------------------------------------- + void ATC_Coupling::set_fixed_nodes() + { + // set fields + prescribedDataMgr_->set_fixed_fields(time(), + fields_,dot_fields_,ddot_fields_,dddot_fields_); + + + // set related data + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + DENS_MAT & rhs(rhs_[thisField].set_quantity()); + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + if (prescribedDataMgr_->is_fixed(inode,thisField,thisIndex)) { + rhs(inode,thisIndex) = 0.; + } + } + } + } + } + //-------------------------------------------------------- + void ATC_Coupling::set_initial_conditions() + { + // set fields + prescribedDataMgr_->set_initial_conditions(time(), + fields_,dot_fields_,ddot_fields_,dddot_fields_); + + // set (all) related data + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + DENS_MAT & rhs(rhs_[thisField].set_quantity()); + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + rhs(inode,thisIndex) = 0.; + } + } + } + } + //-------------------------------------------------------- + void ATC_Coupling::set_sources() + { + // set fields + prescribedDataMgr_->set_sources(time(),sources_); + + } + //----------------------------------------------------------------- + // this is w_a source_a + void ATC_Coupling::compute_sources_at_atoms(const RHS_MASK & rhsMask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + FIELD_MATS & atomicSources) + { + feEngine_->compute_source(rhsMask, + fields, + physicsModel, + atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(), + shpFcnMask_->quantity(), + shpFcnDerivsMask_->quantity(), + atomicSources); + + } + //----------------------------------------------------------------- + + void ATC_Coupling::compute_atomic_sources(const RHS_MASK & fieldMask, + const FIELDS & fields, + FIELDS & atomicSources) + { + + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if (is_intrinsic(thisFieldName)) { + atomicSources[thisFieldName] = 0.; + if (fieldMask(thisFieldName,FLUX)) { + atomicSources[thisFieldName] = boundaryFlux_[thisFieldName]; + } + if (fieldMask(thisFieldName,PRESCRIBED_SOURCE)) { + atomicSources[thisFieldName] -= fluxMask_*(sources_[thisFieldName].quantity()); + } + + + // add in sources from extrinsic models + if (fieldMask(thisFieldName,EXTRINSIC_SOURCE)) + atomicSources[thisFieldName] -= fluxMask_*(extrinsicSources_[thisFieldName].quantity()); + + } + } + } + //----------------------------------------------------------------- + void ATC_Coupling::compute_rhs_tangent( + const pair row_col, + const RHS_MASK & rhsMask, + const FIELDS & fields, + SPAR_MAT & stiffness, + const IntegrationDomainType integrationType, + 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); + feEngine_->compute_tangent_matrix(rhsMaskMD, row_col, + fields, physicsModel, atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(), shpFcnMask_->quantity(), + shpFcnDerivsMask_->quantity(),stiffnessAtomDomain_); + 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); + } + //----------------------------------------------------------------- + void ATC_Coupling::compute_rhs_vector(const RHS_MASK & rhsMask, + const FIELDS & fields, + FIELDS & rhs, + const IntegrationDomainType domain, + const PhysicsModel * physicsModel) + { + if (!physicsModel) physicsModel = physicsModel_; + + + // compute FE contributions + + evaluate_rhs_integral(rhsMask,fields,rhs,domain,physicsModel); + + for (int n = 0; n < rhsMask.nRows(); n++) { + FieldName thisFieldName = FieldName(n); + if (rhsMask(thisFieldName,PRESCRIBED_SOURCE)) { + if (is_intrinsic(thisFieldName)) { + rhs[thisFieldName] += fluxMaskComplement_*(sources_[thisFieldName].quantity()); + } + else { + rhs[thisFieldName] += sources_[thisFieldName].quantity(); + } + } + + // add in sources from extrinsic models + if (rhsMask(thisFieldName,EXTRINSIC_SOURCE)) { + if (is_intrinsic(thisFieldName)) { + rhs[thisFieldName] += fluxMaskComplement_*(extrinsicSources_[thisFieldName].quantity()); + } + else { + rhs[thisFieldName] += extrinsicSources_[thisFieldName].quantity(); + } + } + + } + ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); + feEngine_->add_robin_fluxes(rhsMask, fields, time(), robinFcn, rhs); + } + //----------------------------------------------------------------- + void ATC_Coupling::evaluate_rhs_integral( + const Array2D & rhsMask, + const FIELDS & fields, FIELDS & rhs, + const IntegrationDomainType integrationType, + const PhysicsModel * physicsModel) + { + + if (!physicsModel) physicsModel = physicsModel_; + + + if (integrationType == FE_DOMAIN ) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs, + &(elementMask_->quantity())); + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(), + shpFcnMask_->quantity(), + shpFcnDerivsMask_->quantity(), + rhsAtomDomain_); + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + rhs[thisFieldName] -= rhsAtomDomain_[thisFieldName].quantity(); + } + } + else if (integrationType == ATOM_DOMAIN) { + + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(), + shpFcnMask_->quantity(), + shpFcnDerivsMask_->quantity(), + rhs); + } + 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); + feEngine_->compute_rhs_vector(rhsMaskMD, + fields, + physicsModel, + atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(), + shpFcnMask_->quantity(), + shpFcnDerivsMask_->quantity(), + rhsAtomDomain_); + 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 { // domain == FULL_DOMAIN + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs); + } + } + //-------------------------------------------------- + void ATC_Coupling::initialize() + { + // initialize physics model + if (physicsModel_) physicsModel_->initialize(); + + ATC_Method::initialize(); + + // initialized_ is set to true by derived class initialize() + // STEP 6 - data initialization continued: set initial conditions + if (!initialized_) { + // Apply integration masking and new ICs + // initialize schedule derivatives + try { + set_initial_conditions(); + } + catch (ATC::ATC_Error& atcError) { + if (!useRestart_) + throw; + } + } + + // initialize and fix computational geometry, this can be changed in the future for Eulerian calculations that fill and empty elements which is why it is outside a !initialized_ guard + internalElement_->unfix_quantity(); + if (ghostElement_) ghostElement_->unfix_quantity(); + internalElement_->quantity(); + if (ghostElement_) ghostElement_->quantity(); + nodalGeometryType_->quantity(); + internalElement_->fix_quantity(); + if (ghostElement_) ghostElement_->fix_quantity(); + reset_flux_mask(); + + // setup grouping of atoms by material + reset_atom_materials(); + + // reset time filters if needed + if (timeFilterManager_.need_reset()) { + if ((!initialized_) || (atomToElementMapType_ == EULERIAN)) { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + if (is_intrinsic(thisField) && is_dynamic(thisField)) { + compute_mass_matrix(thisField); + if (!useConsistentMassMatrix_(thisField) && !useFeMdMassMatrix_) { + massMatsMd_[thisField] = massMatsMdInstantaneous_[thisField].quantity(); + massMatsAq_[thisField] = massMatsAqInstantaneous_[thisField].quantity(); + update_mass_matrix(thisField); + } + } + } + } + } + + + // prepare computes for first timestep + lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+1); + } + //------------------------------------------------------------------- + void ATC_Coupling::construct_time_integration_data() + { + if (!initialized_) { + + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + + // Allocate fields, initialize to default values, set up initial schedule + + fields_[thisField].reset(nNodes_,thisSize); + dot_fields_[thisField].reset(nNodes_,thisSize); + ddot_fields_[thisField].reset(nNodes_,thisSize); + dddot_fields_[thisField].reset(nNodes_,thisSize); + + // Allocate restricted fields + if (is_intrinsic(thisField)) { + nodalAtomicFields_[thisField].reset(nNodes_,thisSize); + nodalAtomicFieldsRoc_[thisField].reset(nNodes_,thisSize); + } + + // Dimension finite element rhs matrix + rhs_[thisField].reset(nNodes_,thisSize); + rhsAtomDomain_[thisField].reset(nNodes_,thisSize); + + sources_[thisField].reset(nNodes_,thisSize); + extrinsicSources_[thisField].reset(nNodes_,thisSize); + boundaryFlux_[thisField].reset(nNodes_,thisSize); + + if (is_intrinsic(thisField) && is_dynamic(thisField)) { + massMats_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + massMatsFE_[thisField].reset(nNodes_,nNodes_); + massMatsAq_[thisField].reset(nNodes_,nNodes_); + massMatsMd_[thisField].reset(nNodes_,nNodes_); + massMatsMdInstantaneous_[thisField].reset(nNodes_,nNodes_); + massMatsAqInstantaneous_[thisField].reset(nNodes_,nNodes_); + massMatsInv_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + massMatsMdInv_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + } + else { + // no MD mass matrices needed, regular matrices computed in extrinsic model + if (useConsistentMassMatrix_(thisField)) { + // compute FE mass matrix in full domain + + consistentMassMats_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + consistentMassMatsInv_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + } + else { + massMats_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + massMatsInv_[thisField].reset(nNodes_,nNodes_); // PARALLELIZE + } + } + } + } + } + //-------------------------------------------------------- + // set_computational_geometry + // constructs needed transfer operators which define + // hybrid atom/FE computational geometry + //-------------------------------------------------------- + void ATC_Coupling::set_computational_geometry() + { + ATC_Method::set_computational_geometry(); + + if (groupbitGhost_) { + atomGhostElement_ = new AtomToElementMap(this, + atomGhostCoarseGrainingPositions_, + GHOST); + interscaleManager_.add_per_atom_int_quantity(atomGhostElement_, + "AtomGhostElement"); + } + + // does element contain internal atoms + internalElement_ = new AtomTypeElement(this,atomElement_); + interscaleManager_.add_dense_matrix_int(internalElement_, + "ElementHasInternal"); + // does element contain ghost atoms + if (atomGhostElement_) { + ghostElement_ = new AtomTypeElement(this,atomGhostElement_); + interscaleManager_.add_dense_matrix_int(ghostElement_, + "ElementHasGhost"); + } + // element masking for FE quadrature + if (atomQuadForInternal_) { + elementMask_ = new MatrixDependencyManager(feEngine_->num_elements(),1); + DenseMatrix & elementMask(elementMask_->set_quantity()); + elementMask = true; + + const set & nullElements = feEngine_->null_elements(); + set::const_iterator iset; + for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { + int ielem = *iset; + elementMask(ielem,0) = false; + } + } + else { + elementMask_ = new ElementMask(this); + internalToMask_ = new AtomToElementset(this,elementMask_); + interscaleManager_.add_per_atom_int_quantity(internalToMask_, + "InternalToMaskMap"); + } + interscaleManager_.add_dense_matrix_bool(elementMask_, + "ElementMask"); + // node type + nodalGeometryType_ = new NodalGeometryType(this); + interscaleManager_.add_dense_matrix_int(nodalGeometryType_, + "NodalGeometryType"); + } + //-------------------------------------------------------- + // construct_transfers + // constructs needed transfer operators + //-------------------------------------------------------- + void ATC_Coupling::construct_transfers() + { + ATC_Method::construct_transfers(); + + // finite element shape functions for interpolants + PerAtomShapeFunction * atomShapeFunctions = new PerAtomShapeFunction(this); + interscaleManager_.add_per_atom_sparse_matrix(atomShapeFunctions,"Interpolant"); + shpFcn_ = atomShapeFunctions; + if (groupbitGhost_) { + atomShapeFunctions = new PerAtomShapeFunction(this, + atomGhostCoarseGrainingPositions_, + atomGhostElement_, + GHOST); + interscaleManager_.add_per_atom_sparse_matrix(atomShapeFunctions,"InterpolantGhost"); + shpFcnGhost_ = atomShapeFunctions; + } + + // use shape functions for accumulants if no kernel function is provided + if (!kernelFunction_) { + accumulant_ = shpFcn_; + } + else { + if (kernelOnTheFly_) throw ATC_Error("ATC_Coupling::construct_transfers - on the fly kernel evaluations not supported"); + PerAtomKernelFunction * atomKernelFunctions = new PerAtomKernelFunction(this); + interscaleManager_.add_per_atom_sparse_matrix(atomKernelFunctions, + "Accumulant"); + accumulant_ = atomKernelFunctions; + accumulantWeights_ = new AccumulantWeights(accumulant_); + mdMassNormalization_ = false; + } + + // add species transfer operators + + map >::const_iterator specid; + for (specid = speciesIds_.begin(); specid != speciesIds_.end(); specid++) { + const string specie = specid->first; + LargeToSmallAtomMap * specieMap; + if ((specid->second).first == ATOM_TYPE) { + specieMap = new AtomToType(this,(specid->second).second); + interscaleManager_.add_per_atom_int_quantity(specieMap, + "AtomMap"+specie); + } + else { // if ((specie->second).first == ATOM_GROUP) + specieMap = new AtomToGroup(this,(specid->second).second); + interscaleManager_.add_per_atom_int_quantity(specieMap, + "AtomMap"+specie); + } + ReducedSparseMatrix * accumulantSpecie = new ReducedSparseMatrix(this, + accumulant_, + specieMap); + interscaleManager_.add_sparse_matrix(accumulantSpecie,"Accumulant"+specie); + } + + // add molecule transfer operators + + map >::const_iterator molecule; + PerAtomQuantity * atomProcGhostCoarseGrainingPositions = interscaleManager_.per_atom_quantity("AtomicProcGhostCoarseGrainingPositions"); + FundamentalAtomQuantity * mass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, + PROC_GHOST); + for (molecule = moleculeIds_.begin(); molecule != moleculeIds_.end(); molecule++) { + const string moleculeName = molecule->first; + int groupbit = (molecule->second).second; + SmallMoleculeSet * smallMoleculeSet = new SmallMoleculeSet(this,groupbit); + smallMoleculeSet->initialize(); + interscaleManager_.add_small_molecule_set(smallMoleculeSet,moleculeName); + SmallMoleculeCentroid * moleculeCentroid = + new SmallMoleculeCentroid(this,mass,smallMoleculeSet,atomProcGhostCoarseGrainingPositions); + interscaleManager_.add_dense_matrix(moleculeCentroid,"MoleculeCentroid"+moleculeName); + + // shape function at molecular coordinates + PointToElementMap * elementMapMol = + new PointToElementMap(this,moleculeCentroid); + interscaleManager_.add_dense_matrix_int(elementMapMol, + "ElementMap"+moleculeName); + InterpolantSmallMolecule * shpFcnMol = new InterpolantSmallMolecule(this, + elementMapMol, moleculeCentroid, smallMoleculeSet); + interscaleManager_.add_sparse_matrix(shpFcnMol, + "ShapeFunction"+moleculeName); + } + + + this->create_atom_volume(); + + // masked atom weights + if (atomQuadForInternal_) { + atomicWeightsMask_ = atomVolume_; + } + else { + atomicWeightsMask_ = new MappedDiagonalMatrix(this, + atomVolume_, + internalToMask_); + interscaleManager_.add_diagonal_matrix(atomicWeightsMask_, + "AtomWeightsMask"); + } + + // shape function derivatives + PerAtomShapeFunctionGradient * atomShapeFunctionGradients = new PerAtomShapeFunctionGradient(this); + interscaleManager_.add_vector_sparse_matrix(atomShapeFunctionGradients, + "InterpolantGradient"); + shpFcnDerivs_ = atomShapeFunctionGradients; + if (groupbitGhost_) { + atomShapeFunctionGradients = new PerAtomShapeFunctionGradient(this, + atomGhostElement_, + atomGhostCoarseGrainingPositions_, + "InterpolantGradientGhost", + GHOST); + interscaleManager_.add_vector_sparse_matrix(atomShapeFunctionGradients, + "InterpolantGradientGhost"); + shpFcnDerivsGhost_ = atomShapeFunctionGradients; + } + + // masked shape function and derivatives + if (atomQuadForInternal_) { + shpFcnMask_ = shpFcn_; + shpFcnDerivsMask_ = shpFcnDerivs_; + } + else { + shpFcnMask_ = new RowMappedSparseMatrix(this, + shpFcn_, + internalToMask_); + interscaleManager_.add_sparse_matrix(shpFcnMask_, + "ShapeFunctionMask"); + shpFcnDerivsMask_ = new RowMappedSparseMatrixVector(this, + shpFcnDerivs_, + internalToMask_); + interscaleManager_.add_vector_sparse_matrix(shpFcnDerivsMask_,"ShapeFunctionGradientMask"); + } + + // nodal volumes for mass matrix, relies on atomVolumes constructed in base class construct_transfers + nodalAtomicVolume_ = new AdmtfShapeFunctionRestriction(this,atomVolume_,shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicVolume_,"NodalAtomicVolume"); + + extrinsicModelManager_.construct_transfers(); + } + //-------------------------------------------------- + void ATC_Coupling::delete_mass_mat_time_filter(FieldName thisField) + { + } + //-------------------------------------------------- + void ATC_Coupling::set_mass_mat_time_filter(FieldName thisField,TimeFilterManager::FilterIntegrationType filterIntegrationType) + { + massMatTimeFilters_[thisField] = timeFilterManager_.construct(filterIntegrationType); + } + //-------------------------------------------------------------- + /** method to trigger construction of mesh data after mesh construction */ + //-------------------------------------------------------------- + void ATC_Coupling::initialize_mesh_data(void) + { + int nelts = feEngine_->fe_mesh()->num_elements(); + elementToMaterialMap_.reset(nelts); + elementToMaterialMap_ = 0; + + construct_prescribed_data_manager(); + meshDataInitialized_ = true; + } + //-------------------------------------------------------- + + void ATC_Coupling::reset_flux_mask(void) + { + int i; + // this is exact only for uniform meshes and certain types of atomic weights + // \int_{\Omega_MD} N_I dV = \sum_\alpha N_I\alpha V_\alpha + fluxMask_.reset((invNodeVolumes_.quantity()) + * (nodalAtomicVolume_->quantity())); + + DIAG_MAT id(fluxMask_.nRows(),fluxMask_.nCols()); + id = 1.0; + fluxMaskComplement_ = id + -1.0*fluxMask_; + + // set flux masks for nodes we can tell by geometry + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (i = 0; i < nNodes_; ++i) { + if (nodeType(i,0)==MD_ONLY) { + fluxMask_(i,i) = 1.; + fluxMaskComplement_(i,i) = 0.; + } + else if (nodeType(i,0)==FE_ONLY) { + fluxMask_(i,i) = 0.; + fluxMaskComplement_(i,i) = 1.; + } + } + } + + //-------------------------------------------------------- + void ATC_Coupling::compute_mass_matrix(FieldName thisField, PhysicsModel * physicsModel) + { + + if (!physicsModel) physicsModel = physicsModel_; + if (useConsistentMassMatrix_(thisField)) { + // compute FE mass matrix in full domain + + Array massMask(1); + massMask(0) = thisField; + + feEngine_->compute_mass_matrix(massMask,fields_,physicsModel, + elementToMaterialMap_,consistentMassMats_, + &(elementMask_->quantity())); + // brute force computation of inverse + consistentMassMatsInv_[thisField] = inv((consistentMassMats_[thisField].quantity()).dense_copy()); + } + else { // lumped mass matrix + // compute FE mass matrix in full domain + Array massMask(1); + massMask(0) = thisField; + + if (useFeMdMassMatrix_) { + 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()); + DIAG_MAT & myMassMatMDInv(massMatsMdInv_[thisField].set_quantity()); + (massMatsMd_[thisField].set_quantity()) = myMassMat; + // compute inverse mass matrices since we're using lumped masses + 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.; + myMassMatMDInv = myMassMatInv; + } + } + else { + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel, + elementToMaterialMap_,massMatsFE_, + &(elementMask_->quantity())); + // fully remove contributions from internal nodes + + DIAG_MAT & myMassMatFE(massMatsFE_[thisField].set_quantity()); + if (!atomQuadForInternal_) { + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (int iNode = 0; iNode < nNodes_; iNode++) + if (nodeType(iNode,0)==MD_ONLY) { + myMassMatFE(iNode,iNode) = 0.; + } + } + + // atomic quadrature for FE mass matrix in atomic domain + + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel,atomMaterialGroupsMask_, + atomicWeightsMask_->quantity(),shpFcnMask_->quantity(), + massMatsAqInstantaneous_); + + // set up mass MD matrices + compute_md_mass_matrix(thisField,massMatsMdInstantaneous_[thisField].set_quantity()); + } + } + } + //-------------------------------------------------------- + void ATC_Coupling::update_mass_matrix(FieldName thisField) + { + DIAG_MAT & myMassMat(massMats_[thisField].set_quantity()); + DIAG_MAT & myMassMatInv(massMatsInv_[thisField].set_quantity()); + DIAG_MAT & myMassMatMDInv(massMatsMdInv_[thisField].set_quantity()); + const DIAG_MAT & myMassMatMD(massMatsMd_[thisField].quantity()); + + myMassMat = massMatsFE_[thisField].quantity(); + // remove contributions from overlap by approximate quadrature + myMassMat -= massMatsAq_[thisField].quantity(); + // add contributions from atomic region + myMassMat += myMassMatMD; + + // compute inverse mass matrices since we're using lumped masses + for (int iNode = 0; iNode < nNodes_; iNode++) { + + if (fabs(myMassMatMD(iNode,iNode))>0) { + myMassMatMDInv(iNode,iNode) = 1./myMassMatMD(iNode,iNode); + } + else + myMassMatMDInv(iNode,iNode) = 0.; + + if (fabs(myMassMat(iNode,iNode))>0) { + myMassMatInv(iNode,iNode) = 1./myMassMat(iNode,iNode); + } + else + myMassMatInv(iNode,iNode) = 0.; + } + } + //-------------------------------------------------------- + void ATC_Coupling::reset_atom_materials() + { + int nMaterials = physicsModel_->nMaterials(); + atomMaterialGroups_.reset(nMaterials); + atomMaterialGroupsMask_.reset(nMaterials); + + for (int i = 0; i < nMaterials; i++) { + atomMaterialGroups_(i).clear(); + atomMaterialGroupsMask_(i).clear(); + } + + const INT_ARRAY & atomToElementMap(atomElement_->quantity()); + for (int i = 0; i < nLocal_; i++) { + atomMaterialGroups_(elementToMaterialMap_(atomToElementMap(i,0))).insert(i); + } + if (atomQuadForInternal_) { + for (int i = 0; i < nLocal_; i++) { + atomMaterialGroupsMask_(elementToMaterialMap_(atomToElementMap(i,0))).insert(i); + } + } + else { + const INT_ARRAY & map(internalToMask_->quantity()); + for (int i = 0; i < nLocal_; i++) { + int idx = map(i,0); + if (idx > -1) { + atomMaterialGroupsMask_(elementToMaterialMap_(atomToElementMap(i,0))).insert(idx); + } + } + } + } +//-------------------------------------------------------- + void ATC_Coupling::post_init_integrate() + { + // Apply time filtering to mass matrices, if needed + if (timeFilterManager_.filter_dynamics() && !useFeMdMassMatrix_) { + double dt = lammpsInterface_->dt(); + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + if (!useConsistentMassMatrix_(thisField) && is_intrinsic(thisField)) { + massMatTimeFilters_[thisField]->apply_pre_step1(massMatsAq_[thisField].set_quantity(), + massMatsAqInstantaneous_[thisField].quantity(),dt); + massMatTimeFilters_[thisField]->apply_pre_step1(massMatsMd_[thisField].set_quantity(), + massMatsMdInstantaneous_[thisField].quantity(),dt); + } + } + } + } + + + //-------------------------------------------------------- + void ATC_Coupling::pre_neighbor() + { + ATC_Method::pre_neighbor(); + reset_atom_materials(); + } + + //-------------------------------------------------------- + void ATC_Coupling::pre_exchange() + { + ATC_Method::pre_exchange(); + } + + //-------------------------------------------------------- + void ATC_Coupling::post_force() + { + ATC_Method::post_force(); + + if ( (atomToElementMapType_ == EULERIAN) && (step() % atomToElementMapFrequency_ == 0) ) { + reset_atom_materials(); + + if (!useFeMdMassMatrix_) { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + if (is_intrinsic(thisField) && is_dynamic(thisField)) { + compute_mass_matrix(thisField); + } + } + } + } + + if (atomToElementMapType_ == EULERIAN && !useFeMdMassMatrix_) { + if (timeFilterManager_.filter_dynamics() || (step() % atomToElementMapFrequency_ == 0)) { + double dt = lammpsInterface_->dt(); + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + if (is_intrinsic(thisField) && is_dynamic(thisField)) { + massMatTimeFilters_[thisField]->apply_post_step1(massMatsAq_[thisField].set_quantity(), + massMatsAqInstantaneous_[thisField].quantity(),dt); + massMatTimeFilters_[thisField]->apply_post_step1(massMatsMd_[thisField].set_quantity(), + massMatsMdInstantaneous_[thisField].quantity(),dt); + update_mass_matrix(thisField); + } + } + } + } + + // apply extrinsic model + extrinsicModelManager_.post_force(); + } + + //================================================================= + // + //================================================================= + void ATC_Coupling::finish() + { + ATC_Method::finish(); + // Time integrator + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->finish(); + } + } + + + + //================================================================= + // + //================================================================= + void ATC_Coupling::compute_boundary_flux(const Array2D & rhsMask, + const FIELDS & fields, + FIELDS & rhs, + const Array< set > atomMaterialGroups, + const VectorDependencyManager * shpFcnDerivs, + const SPAR_MAN * shpFcn, + const DIAG_MAN * atomicWeights, + + const MatrixDependencyManager * elementMask, + const RegulatedNodes * nodeSet) + { + if (bndyIntType_ == FE_QUADRATURE) { + feEngine_->compute_boundary_flux(rhsMask, + fields, + physicsModel_, + elementToMaterialMap_, + (* bndyFaceSet_), + rhs); + } + else if (bndyIntType_ == FE_INTERPOLATION) { + if (elementMask) { + feEngine_->compute_boundary_flux(rhsMask, + fields, + physicsModel_, + elementToMaterialMap_, + atomMaterialGroups, + atomicWeights->quantity(), + shpFcn->quantity(), + shpFcnDerivs->quantity(), + fluxMask_, + rhs, + &elementMask->quantity(), + &nodeSet->quantity()); + } + else { + feEngine_->compute_boundary_flux(rhsMask, + fields, + physicsModel_, + elementToMaterialMap_, + atomMaterialGroups_, + atomVolume_->quantity(), + shpFcn_->quantity(), + shpFcnDerivs_->quantity(), + fluxMask_, + rhs); + } + } + else if (bndyIntType_ == NO_QUADRATURE) { + FIELDS::const_iterator field; + for (field = fields.begin(); field != fields.end(); field++) { + FieldName thisFieldName = field->first; + +if (thisFieldName >= rhsMask.nRows()) break; + if (rhsMask(thisFieldName,FLUX)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs[thisFieldName].reset(nrows,ncols); + } + } + } + } + + //----------------------------------------------------------------- + void ATC_Coupling::compute_flux(const Array2D & rhsMask, + const FIELDS & fields, + GRAD_FIELD_MATS & flux, + const PhysicsModel * physicsModel) + { + if (! physicsModel) { physicsModel = physicsModel_; } + feEngine_->compute_flux(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + flux); + } + + + //-------------------------------------------------------- + + void ATC_Coupling::nodal_projection(const FieldName & fieldName, + const PhysicsModel * physicsModel, + FIELD & field) + { + FIELDS rhs; + rhs[fieldName].reset(nNodes_,field.nCols()); + Array2D rhsMask(NUM_FIELDS,NUM_FLUX); + rhsMask = false; + rhsMask(fieldName,SOURCE) = true; + compute_rhs_vector(rhsMask, fields_, rhs, sourceIntegration_, physicsModel); + const DENS_MAT & B(rhs[fieldName].quantity()); + + field = (invNodeVolumes_.quantity())*B; + } + + // parse_boundary_integration + // parses the boundary integration to determine + // the type of boundary integration being used + //-------------------------------------------------- + + + BoundaryIntegrationType ATC_Coupling::parse_boundary_integration(int narg, + char **arg, + const set< pair > * boundaryFaceSet) + { + + int argIndex = 0; + BoundaryIntegrationType myBoundaryIntegrationType = FE_INTERPOLATION;// default + if (narg > 0) { + if(strcmp(arg[argIndex],"faceset")==0) { + argIndex++; + myBoundaryIntegrationType = FE_QUADRATURE; + string name(arg[argIndex]); + boundaryFaceSet = & ( (feEngine_->fe_mesh())->faceset(name)); + set_boundary_face_set(boundaryFaceSet); + } + else if (strcmp(arg[argIndex],"interpolate")==0) { + myBoundaryIntegrationType = FE_INTERPOLATION; + } + else if (strcmp(arg[argIndex],"no_boundary")==0) { + myBoundaryIntegrationType = NO_QUADRATURE; + } + else { + throw ATC_Error("Bad boundary integration type"); + } + } + set_boundary_integration_type(myBoundaryIntegrationType); + return myBoundaryIntegrationType; + } + +}; // namespace ATC diff --git a/lib/atc/ATC_Coupling.h b/lib/atc/ATC_Coupling.h new file mode 100644 index 0000000000..a890d9d838 --- /dev/null +++ b/lib/atc/ATC_Coupling.h @@ -0,0 +1,424 @@ +#ifndef ATC_COUPLING_H +#define ATC_COUPLING_H + +// ATC headers +#include "ATC_Method.h" +#include "ExtrinsicModel.h" + +using namespace std; + +namespace ATC { + + // Forward declarations + class PrescribedDataManager; + class AtomicRegulator; + class TimeIntegrator; + class ReferencePositions; + + /** + * @class ATC_Coupling + * @brief Base class for atom-continuum coupling + */ + + class ATC_Coupling : public ATC_Method { + + public: /** methods */ + + friend class ExtrinsicModel; // friend is not inherited + friend class ExtrinsicModelTwoTemperature; + friend class ExtrinsicModelDriftDiffusion; + friend class ExtrinsicModelDriftDiffusionConvection; + friend class ExtrinsicModelElectrostatic; + friend class ExtrinsicModelElectrostaticMomentum; + friend class SchrodingerPoissonSolver; + friend class SliceSchrodingerPoissonSolver; + + /** constructor */ + ATC_Coupling(string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix); + + /** destructor */ + virtual ~ATC_Coupling(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre neighbor */ + virtual void pre_neighbor(); + + /** pre exchange */ + virtual void pre_exchange(); + virtual void reset_atoms(){}; + + /** pre force */ + virtual void pre_force() {}; + + /** post force */ + virtual void post_force(); + + /** pre integration run */ + virtual void initialize(); + + /** post integration run : called at end of run or simulation */ + virtual void finish(); + + /** Predictor phase, Verlet first step for velocity */ + virtual void init_integrate_velocity(); + + /** Predictor phase, executed between velocity and position Verlet */ + virtual void mid_init_integrate(){}; + + /** Predictor phase, Verlet first step for position */ + virtual void init_integrate_position(); + + /** Predictor phase, executed after Verlet */ + virtual void post_init_integrate(); + + /** Corrector phase, executed before Verlet */ + virtual void pre_final_integrate(){}; + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(); + + /** Corrector phase, executed after Verlet*/ + + virtual void post_final_integrate() {lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+1);}; + + /** pre/post atomic force calculation in minimize */ + virtual void min_pre_force(){}; + virtual void min_post_force(){}; + + // data access + /** get map general atomic shape function matrix to overlap region */ + SPAR_MAT &get_atom_to_overlap_mat() {return atomToOverlapMat_.set_quantity();}; + /** get map general atomic shape function matrix to overlap region */ + SPAR_MAN &atom_to_overlap_mat() {return atomToOverlapMat_;}; + /** check if atomic quadrature is being used for MD_ONLY nodes */ + bool atom_quadrature_on(){return atomQuadForInternal_;}; + const set & boundary_face_names() {return boundaryFaceNames_;}; + /** access to boundary integration method */ + int boundary_integration_type() {return bndyIntType_;}; + void set_boundary_integration_type(int boundaryIntegrationType) + {bndyIntType_ = boundaryIntegrationType;}; + void set_boundary_face_set(const set< pair > * boundaryFaceSet) + {bndyFaceSet_ = boundaryFaceSet;}; + BoundaryIntegrationType parse_boundary_integration + (int narg, char **arg, const set< pair > * boundaryFaceSet); + TemperatureDefType temperature_def() const {return temperatureDef_;}; + void set_temperature_def(TemperatureDefType tdef) {temperatureDef_ = tdef;}; + +//-------------------------------------------------------- + + /** access to all boundary fluxes */ + FIELDS &boundary_fluxes() {return boundaryFlux_;}; + /** wrapper for FE_Engine's compute_boundary_flux functions */ + void compute_boundary_flux(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &rhs, + const Array< set > atomMaterialGroups, + const VectorDependencyManager * shpFcnDerivs, + const SPAR_MAN * shpFcn = NULL, + const DIAG_MAN * atomicWeights = NULL, + const MatrixDependencyManager * elementMask = NULL, + const RegulatedNodes * nodeSet = NULL); + /** access to full right hand side / forcing vector */ + FIELDS &rhs() {return rhs_;}; + + DENS_MAN &field_rhs(FieldName thisField) { return rhs_[thisField]; }; + /** allow FE_Engine to construct ATC structures after mesh is constructed */ + virtual void initialize_mesh_data(void); + +// public for FieldIntegrator + bool source_atomic_quadrature(FieldName field) + { return (sourceIntegration_ == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE); } + ATC::IntegrationDomainType source_integration() + { return sourceIntegration_; } + + /** wrapper for FE_Engine's compute_sources */ + void compute_sources_at_atoms(const RHS_MASK & rhsMask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + FIELD_MATS & atomicSources); + + /** wrapper for FE_Engine's compute_rhs_vector functions */ + void compute_rhs_vector(const RHS_MASK & rhs_mask, + const FIELDS &fields, + FIELDS &rhs, + const IntegrationDomainType domain, // = FULL_DOMAIN + const PhysicsModel * physicsModel=NULL); + /** wrapper for FE_Engine's compute_tangent_matrix */ + void compute_rhs_tangent(const pair row_col, + const RHS_MASK & rhsMask, + const FIELDS & fields, + SPAR_MAT & stiffness, + const IntegrationDomainType integrationType, + const PhysicsModel * physicsModel=NULL); + + /** PDE type */ + WeakEquation::PDE_Type pde_type(const FieldName fieldName) const; + /** is dynamic PDE */ + bool is_dynamic(const FieldName fieldName) const; + +// public for ImplicitSolveOperator + /** return pointer to PrescribedDataManager */ + PrescribedDataManager * prescribed_data_manager() + { return prescribedDataMgr_; } +// public for Kinetostat + DIAG_MAT &get_mass_mat(FieldName thisField) + { return massMats_[thisField].set_quantity();}; + /** */ + DENS_MAN &atomic_source(FieldName thisField){return atomicSources_[thisField];}; + + + //--------------------------------------------------------------- + /** \name materials */ + //--------------------------------------------------------------- + /*@{*/ + /** access to element to material map */ + Array &element_to_material_map(void){return elementToMaterialMap_;} + /*@}*/ + + /** check if method is tracking charge */ + bool track_charge() {return trackCharge_;}; + + void set_mass_mat_time_filter(FieldName thisField,TimeFilterManager::FilterIntegrationType filterIntegrationType); + + /** return referece to ExtrinsicModelManager */ + ExtrinsicModelManager & extrinsic_model_manager() + { return extrinsicModelManager_; } + /** access to time integrator */ + const TimeIntegrator * time_integrator(const FieldName & field) const { + _ctiIt_ = timeIntegrators_.find(field); + if (_ctiIt_ == timeIntegrators_.end()) return NULL; + return _ctiIt_->second; + }; + + //--------------------------------------------------------------- + /** \name managers */ + //--------------------------------------------------------------- + /*@{*/ + /** allow FE_Engine to construct data manager after mesh is constructed */ + void construct_prescribed_data_manager (void); + /** method to create physics model */ + void create_physics_model(const PhysicsType & physicsType, + string matFileName); + /** access to physics model */ + PhysicsModel * physics_model() {return physicsModel_; }; + /*@}*/ + + //--------------------------------------------------------------- + /** \name creation */ + //--------------------------------------------------------------- + /*@{*/ + /** set up atom to material identification */ + virtual void reset_atom_materials(); + /** */ + void reset_node_mask(); + /** */ + void reset_overlap_map(); + /*@}*/ + + //--------------------------------------------------------------- + /** \name output/restart */ + //--------------------------------------------------------------- + /*@{*/ + void pack_fields(RESTART_LIST & data); + void output() { ATC_Method::output(); } + /*@}*/ + + //--------------------------------------------------------------- + /** \name initial & boundary conditions */ + //--------------------------------------------------------------- + /*@{*/ + /** mask for computation of fluxes */ + void set_fixed_nodes(); + /** set initial conditions by changing fields */ + void set_initial_conditions(); + /*@}*/ + + //--------------------------------------------------------------- + /** \name sources */ + //--------------------------------------------------------------- + /** calculate and set matrix of sources_ */ + void set_sources(); + /** assemble various contributions to the heat flux in the atomic region */ + void compute_atomic_sources(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &atomicSources); + + DENS_MAT &get_source(FieldName thisField){return sources_[thisField].set_quantity();}; + DENS_MAN &source(FieldName thisField){return sources_[thisField];}; + FIELDS & sources(){return sources_;}; + /** access to name atomic source terms */ + DENS_MAT &get_atomic_source(FieldName thisField){return atomicSources_[thisField].set_quantity();}; + /** access to name extrinsic source terms */ + DENS_MAT &get_extrinsic_source(FieldName thisField){return extrinsicSources_[thisField].set_quantity();}; + DENS_MAN &extrinsic_source(FieldName thisField){return extrinsicSources_[thisField];}; + + /** nodal projection of a field through the physics model */ + + void nodal_projection(const FieldName & fieldName, + const PhysicsModel * physicsModel, + FIELD & field); + /*@}*/ + + //--------------------------------------------------------------- + /** \name fluxes */ + //--------------------------------------------------------------- + /*@{*/ + /** access for field mask */ + Array2D &field_mask() {return fieldMask_;}; + /** create field mask */ + void reset_flux_mask(); + /** wrapper for FE_Engine's compute_flux functions */ + void compute_flux(const Array2D & rhs_mask, + const FIELDS &fields, + GRAD_FIELD_MATS &flux, + const PhysicsModel * physicsModel=NULL); + /** evaluate rhs on a specified domain defined by mask and physics model */ + void evaluate_rhs_integral(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &rhs, + const IntegrationDomainType domain, + const PhysicsModel * physicsModel=NULL); + /** access to boundary fluxes */ + DENS_MAT &get_boundary_flux(FieldName thisField){return boundaryFlux_[thisField].set_quantity();}; + DENS_MAN &boundary_flux(FieldName thisField){return boundaryFlux_[thisField];}; + /** access to finite element right-hand side data */ + DENS_MAT &get_field_rhs(FieldName thisField) + { return rhs_[thisField].set_quantity(); }; + /*@}*/ + + //--------------------------------------------------------------- + /** \name mass matrices */ + //--------------------------------------------------------------- + /*@{*/ + // atomic field time derivative filtering + virtual void init_filter(void); + // mass matrix filtering + void delete_mass_mat_time_filter(FieldName thisField); + /** compute mass matrix for requested field */ + void compute_mass_matrix(FieldName thisField, PhysicsModel * physicsModel = NULL); + /** updates filtering of MD contributions */ + void update_mass_matrix(FieldName thisField); + + private: /** methods */ + ATC_Coupling(); // do not define + + protected: /** data */ + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** sets up all data necessary to define the computational geometry */ + virtual void set_computational_geometry(); + /** constructs all data which is updated with time integration, i.e. fields */ + virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + //--------------------------------------------------------------- + /** status */ + //--------------------------------------------------------------- + /*@{*/ + /** flag on if FE nodes in MD region should be initialized to projected MD values */ + bool consistentInitialization_; + bool equilibriumStart_; + bool useFeMdMassMatrix_; + /** flag to determine if charge is tracked */ + bool trackCharge_; + /** temperature definition model */ + TemperatureDefType temperatureDef_; + /*@}*/ + + //--------------------------------------------------------------- + /** \name managers */ + //--------------------------------------------------------------- + /*@{*/ + /** prescribed data handler */ + PrescribedDataManager * prescribedDataMgr_; + /** pointer to physics model */ + PhysicsModel * physicsModel_; + /** manager for extrinsic models */ + ExtrinsicModelManager extrinsicModelManager_; + /** manager for regulator */ + AtomicRegulator * atomicRegulator_; + /** managers for time integrators per field */ + map timeIntegrators_; + /** time integrator iterator */ + mutable map::iterator _tiIt_; + /** time integrator const iterator */ + mutable map::const_iterator _ctiIt_; + /*@}*/ + + //--------------------------------------------------------------- + /** materials */ + //--------------------------------------------------------------- + /*@{*/ + Array elementToMaterialMap_; // ATOMIC_TAG * elementToMaterialMap_; + /** atomic ATC material tag */ + + + Array< set > atomMaterialGroups_; // ATOMIC_TAG*atomMaterialGroups_; + Array< set > atomMaterialGroupsMask_; // ATOMIC_TAG*atomMaterialGroupsMask_; + /*@}*/ + + //--------------------------------------------------------------- + /** computational geometry */ + //--------------------------------------------------------------- + /*@{*/ + bool atomQuadForInternal_; + MatrixDependencyManager * elementMask_; + LargeToSmallAtomMap * internalToMask_; + AtomTypeElement * internalElement_; + AtomTypeElement * ghostElement_; + NodalGeometryType * nodalGeometryType_; + /*@}*/ + + /** \name boundary integration */ + /*@{*/ + /** boundary flux quadrature */ + int bndyIntType_; + const set< pair > * bndyFaceSet_; + set boundaryFaceNames_; + /*@}*/ + + //---------------------------------------------------------------- + /** \name shape function matrices */ + //---------------------------------------------------------------- + /*@{*/ + DIAG_MAN * atomicWeightsMask_; + SPAR_MAN * shpFcnMask_; + VectorDependencyManager * shpFcnDerivsMask_; + Array atomMask_; + SPAR_MAN atomToOverlapMat_; + DIAG_MAN nodalMaskMat_; + /*@}*/ + + //--------------------------------------------------------------- + /** \name PDE data */ + //--------------------------------------------------------------- + /*@{*/ + /** mask for computation of fluxes */ + Array2D fieldMask_; + DIAG_MAT fluxMask_; + DIAG_MAT fluxMaskComplement_; + /** sources */ + FIELDS sources_; + FIELDS atomicSources_; + FIELDS extrinsicSources_; + ATC::IntegrationDomainType sourceIntegration_; + SPAR_MAT stiffnessAtomDomain_; + /** rhs/forcing terms */ + FIELDS rhs_; // for pde + FIELDS rhsAtomDomain_; // for thermostat + FIELDS boundaryFlux_; // for thermostat & rhs pde + /*@}*/ + + // workspace variables + mutable DENS_MAT _deltaQuantity_; + }; +}; + +#endif diff --git a/lib/atc/ATC_CouplingEnergy.cpp b/lib/atc/ATC_CouplingEnergy.cpp new file mode 100644 index 0000000000..5a80620b0f --- /dev/null +++ b/lib/atc/ATC_CouplingEnergy.cpp @@ -0,0 +1,728 @@ +// ATC_Transfer headers +#include "ATC_CouplingEnergy.h" +#include "Thermostat.h" +#include "ATC_Error.h" +#include "PrescribedDataManager.h" +#include "FieldManager.h" + +// Other Headers +#include +#include +#include +#include + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ATC_CouplingEnergy + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ATC_CouplingEnergy::ATC_CouplingEnergy(string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile, + ExtrinsicModelType extrinsicModel) + : ATC_Coupling(groupName,perAtomArray,thisFix), + nodalAtomicHeatCapacity_(NULL), + nodalAtomicKineticTemperature_(NULL), + nodalAtomicConfigurationalTemperature_(NULL) + { + // Allocate PhysicsModel + create_physics_model(THERMAL, matParamFile); + + // create extrinsic physics model + if (extrinsicModel != NO_MODEL) { + extrinsicModelManager_.create_model(extrinsicModel,matParamFile); + } + + // Defaults + set_time(); + bndyIntType_ = FE_INTERPOLATION; + + // set up field data based on physicsModel + physicsModel_->num_fields(fieldSizes_,fieldMask_); + + // set up atomic regulator + atomicRegulator_ = new Thermostat(this); + + // set up physics specific time integrator and thermostat + timeIntegrators_[TEMPERATURE] = new ThermalTimeIntegrator(this,TimeIntegrator::GEAR); + + // default physics + temperatureDef_ = KINETIC; + + // output variable vector info: + // output[1] = total coarse scale thermal energy + // output[2] = average temperature + vectorFlag_ = 1; + sizeVector_ = 2; + scalarVectorFreq_ = 1; + extVector_ = 1; + if (extrinsicModel != NO_MODEL) + sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_); + + // create PE per atom ccompute + //lammpsInterface_->create_compute_pe_peratom(); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ATC_CouplingEnergy::~ATC_CouplingEnergy() + { + // clear out all managed memory to avoid conflicts with dependencies on class member data + interscaleManager_.clear(); + } + + //-------------------------------------------------------- + // initialize + // sets up all the necessary data + //-------------------------------------------------------- + void ATC_CouplingEnergy::initialize() + { + // Base class initalizations + ATC_Coupling::initialize(); + + // resetting precedence: + // time integrator -> thermostat -> time filter + // init_filter uses fieldRateNdFiltered which comes from the time integrator, + // which is why the time integrator is initialized first + + // set the reference potential, if necessary, because the nodal energy is needed to initialize the time integrator + if (!initialized_) { + if (temperatureDef_==TOTAL) { + PerAtomQuantity * atomicReferencePotential = interscaleManager_.per_atom_quantity("AtomicReferencePotential"); + PerAtomQuantity * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + atomicReferencePotential->set_quantity() = atomicPotentialEnergy->quantity(); + } + } + + // other initializations + if (reset_methods()) { + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->initialize(); + } + atomicRegulator_->initialize(); + } + extrinsicModelManager_.initialize(); + // reset thermostat power for time filter initial conditions for special cases + if (timeFilterManager_.need_reset()) { + init_filter(); + } + timeFilterManager_.initialize(); // clears need for reset + + if (!initialized_) { + // initialize sources based on initial FE temperature + double dt = lammpsInterface_->dt(); + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(fieldMask_,fields_,atomicSources_); + + // read in field data if necessary + if (useRestart_) { + RESTART_LIST data; + read_restart_data(restartFileName_,data); + useRestart_ = false; + } + + // set consistent initial conditions, if requested + if (!timeFilterManager_.filter_dynamics()) { + if (consistentInitialization_) { + + DENS_MAT & temperature(fields_[TEMPERATURE].set_quantity()); + DENS_MAN * nodalAtomicTemperature(interscaleManager_.dense_matrix("NodalAtomicTemperature")); + const DENS_MAT & atomicTemperature(nodalAtomicTemperature->quantity()); + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (int i = 0; isecond)->construct_methods(); + } + atomicRegulator_->construct_methods(); + } + + //-------------------------------------------------------- + // construct_transfers + // constructs needed transfer operators + //-------------------------------------------------------- + void ATC_CouplingEnergy::construct_transfers() + { + ATC_Coupling::construct_transfers(); + + // always need kinetic energy + AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceKineticEnergy(this); + AtomicEnergyForTemperature * atomEnergyForTemperature = NULL; + + // Appropriate per-atom quantity based on desired temperature definition + if (temperatureDef_==KINETIC) { + atomEnergyForTemperature = atomicTwiceKineticEnergy; + } + else if (temperatureDef_==TOTAL) { + if (timeIntegrators_[TEMPERATURE]->time_integration_type() != TimeIntegrator::FRACTIONAL_STEP) + throw ATC_Error("ATC_CouplingEnergy:construct_transfers() on the fractional step time integrator can be used with non-kinetic defitions of the temperature"); + + // kinetic energy + interscaleManager_.add_per_atom_quantity(atomicTwiceKineticEnergy, + "AtomicTwiceKineticEnergy"); + + // atomic potential energy + //ComputedAtomQuantity * atomicPotentialEnergy = new ComputedAtomQuantity(this,lammpsInterface_->compute_pe_name(), 1./(lammpsInterface_->mvv2e())); + //interscaleManager_.add_per_atom_quantity(atomicPotentialEnergy, + // "AtomicPotentialEnergy"); + PerAtomQuantity * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + +///////////////////////////////////// + FieldManager fmgr(this); + fmgr.nodal_atomic_field(REFERENCE_POTENTIAL_ENERGY); + PerAtomQuantity * atomicReferencePotential = + interscaleManager_.per_atom_quantity("AtomicReferencePotential"); +///////////////////////////////////// + // fluctuating potential energy + AtomicEnergyForTemperature * atomicFluctuatingPotentialEnergy = new FluctuatingPotentialEnergy(this, + atomicPotentialEnergy, + atomicReferencePotential); + interscaleManager_.add_per_atom_quantity(atomicFluctuatingPotentialEnergy, + "AtomicFluctuatingPotentialEnergy"); + + // atomic total energy + atomEnergyForTemperature = new MixedKePeEnergy(this,1,1); + + // kinetic temperature measure for post-processing + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicTwiceKineticEnergy = new AtfShapeFunctionRestriction(this, + atomicTwiceKineticEnergy, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicTwiceKineticEnergy, + "NodalAtomicTwiceKineticEnergy"); + nodalAtomicKineticTemperature_ = new AtfShapeFunctionMdProjection(this, + nodalAtomicTwiceKineticEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicKineticTemperature_, + "NodalAtomicKineticTemperature"); + + // potential temperature measure for post-processing (must multiply by 2 for configurational temperature + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicFluctuatingPotentialEnergy = new AtfShapeFunctionRestriction(this, + atomicFluctuatingPotentialEnergy, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicFluctuatingPotentialEnergy, + "NodalAtomicFluctuatingPotentialEnergy"); + nodalAtomicConfigurationalTemperature_ = new AtfShapeFunctionMdProjection(this, + nodalAtomicFluctuatingPotentialEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicConfigurationalTemperature_, + "NodalAtomicConfigurationalTemperature"); + } + + // register the per-atom quantity for the temperature definition + interscaleManager_.add_per_atom_quantity(atomEnergyForTemperature, + "AtomicEnergyForTemperature"); + + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicEnergy = new AtfShapeFunctionRestriction(this, + atomEnergyForTemperature, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicEnergy, + "NodalAtomicEnergy"); + + // nodal atomic temperature field + + AtfShapeFunctionMdProjection * nodalAtomicTemperature = new AtfShapeFunctionMdProjection(this, + nodalAtomicEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicTemperature, + "NodalAtomicTemperature"); + + if (!useFeMdMassMatrix_) { + // classical thermodynamic heat capacity of the atoms + HeatCapacity * heatCapacity = new HeatCapacity(this); + interscaleManager_.add_per_atom_quantity(heatCapacity, + "AtomicHeatCapacity"); + + // atomic thermal mass matrix + nodalAtomicHeatCapacity_ = new AtfShapeFunctionRestriction(this, + heatCapacity, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicHeatCapacity_, + "NodalAtomicHeatCapacity"); + } + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->construct_transfers(); + } + atomicRegulator_->construct_transfers(); + } + + //--------------------------------------------------------- + // init_filter + // sets up the time filtering operations in all objects + //--------------------------------------------------------- + void ATC_CouplingEnergy::init_filter() + { + + TimeIntegrator::TimeIntegrationType timeIntegrationType = timeIntegrators_[TEMPERATURE]->time_integration_type(); + + + + + if (timeFilterManager_.end_equilibrate()) { + if (timeIntegrationType==TimeIntegrator::GEAR) { + if (equilibriumStart_) { + + + + if (atomicRegulator_->regulator_target()==AtomicRegulator::DYNAMICS) { // based on FE equation + DENS_MAT vdotflamMat(-2.*(nodalAtomicFields_[TEMPERATURE].quantity())); // note 2 is for 1/2 vdotflam addition + atomicRegulator_->reset_lambda_contribution(vdotflamMat); + } + else { // based on MD temperature equation + DENS_MAT vdotflamMat(-1.*(nodalAtomicFields_[TEMPERATURE].quantity())); + atomicRegulator_->reset_lambda_contribution(vdotflamMat); + } + } + } + else if (timeIntegrationType==TimeIntegrator::FRACTIONAL_STEP) { + if (equilibriumStart_) { + DENS_MAT powerMat(-1.*(nodalAtomicFields_[TEMPERATURE].quantity())); + atomicRegulator_->reset_lambda_contribution(powerMat); + } + } + } + } + + //--------------------------------------------------------- + // compute_md_mass_matrix + // compute the mass matrix arising from only atomistic + // quadrature and contributions as a summation + //--------------------------------------------------------- + void ATC_CouplingEnergy::compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMat) + { + + if (thisField == TEMPERATURE) + massMat.reset(nodalAtomicHeatCapacity_->quantity()); + } + + //-------------------------------------------------------- + // finish + // final clean up after a run + //-------------------------------------------------------- + void ATC_CouplingEnergy::finish() + { + // base class + ATC_Coupling::finish(); + + atomicRegulator_->finish(); + } + + //-------------------------------------------------------- + // modify + // parses inputs and modifies state of the filter + //-------------------------------------------------------- + bool ATC_CouplingEnergy::modify(int narg, char **arg) + { + bool foundMatch = false; + int argIndx = 0; + + // check to see if input is a transfer class command + // check derived class before base class + + // pass-through to thermostat + if (strcmp(arg[argIndx],"control")==0) { + argIndx++; + foundMatch = atomicRegulator_->modify(narg-argIndx,&arg[argIndx]); + } + + // pass-through to timeIntegrator class + else if (strcmp(arg[argIndx],"time_integration")==0) { + argIndx++; + foundMatch = timeIntegrators_[TEMPERATURE]->modify(narg-argIndx,&arg[argIndx]); + } + + // switch for the kind of temperature being used + /*! \page man_temperature_definition fix_modify AtC temperature_definition + \section syntax + fix_modify AtC temperature_definition + + \section examples + fix_modify atc temperature_definition kinetic \n + + \section description + Change the definition for the atomic temperature used to create the finite element temperature. The kinetic option is based only on the kinetic energy of the atoms while the total option uses the total energy (kinetic + potential) of an atom. + + \section restrictions + This command is only valid when using thermal coupling. Also, while not a formal restriction, the user should ensure that associating a potential energy with each atom makes physical sense for the total option to be meaningful. + + \section default + kinetic + */ + else if (strcmp(arg[argIndx],"temperature_definition")==0) { + argIndx++; + string_to_temperature_def(arg[argIndx],temperatureDef_); + foundMatch = true; + needReset_ = true; + } + + // no match, call base class parser + if (!foundMatch) { + foundMatch = ATC_Coupling::modify(narg, arg); + } + + return foundMatch; + + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_CouplingEnergy::pack_thermal_fields(RESTART_LIST & data) + { + atomicRegulator_->pack_fields(data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingEnergy::write_restart_data(string fileName, RESTART_LIST & data) + { + pack_thermal_fields(data); + ATC_Method::write_restart_data(fileName,data); + } + + //-------------------------------------------------- + // read_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingEnergy::read_restart_data(string fileName, RESTART_LIST & data) + { + pack_thermal_fields(data); + ATC_Method::read_restart_data(fileName,data); + } + + //-------------------------------------------------- + void ATC_CouplingEnergy::reset_nlocal() + { + ATC_Coupling::reset_nlocal(); + atomicRegulator_->reset_nlocal(); + } + + //-------------------------------------------------- + // reset_atom_materials + // update the atom materials map + //-------------------------------------------------- + void ATC_CouplingEnergy::reset_atom_materials() + { + ATC_Coupling::reset_atom_materials(); + atomicRegulator_->reset_atom_materials(elementToMaterialMap_, + atomElement_); + } + + //-------------------------------------------------- + // pre_init_integrate + // time integration before the lammps atomic + // integration of the Verlet step 1 + //-------------------------------------------------- + void ATC_CouplingEnergy::pre_init_integrate() + { + ATC_Coupling::pre_init_integrate(); + double dt = lammpsInterface_->dt(); + + // Perform any initialization, no actual integration + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate1(dt); + } + + // Apply thermostat force to atom velocities + atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep()); + + // Predict nodal temperatures and time derivatives based on FE data + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate2(dt); + } + extrinsicModelManager_.pre_init_integrate(); + } + + //-------------------------------------------------------- + // mid_init_integrate + // time integration between the velocity update and + // the position lammps update of Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingEnergy::mid_init_integrate() + { + // CONTINUOUS VELOCITY UPDATE + + ATC_Coupling::mid_init_integrate(); + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1/2 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->mid_initial_integrate1(dt); + } + + atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep()); + + extrinsicModelManager_.mid_init_integrate(); + } + + //-------------------------------------------------------- + // post_init_integrate + // time integration after the lammps atomic updates of + // Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingEnergy::post_init_integrate() + { + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_initial_integrate1(dt); + } + + // Update kinetostat quantities if displacement is being regulated + atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep()); + + // Update extrisic model + extrinsicModelManager_.post_init_integrate(); + + // fixed values, non-group bcs handled through FE + set_fixed_nodes(); + + update_time(0.5); + + ATC_Coupling::post_init_integrate(); + } + + //-------------------------------------------------------- + // post_final_integrate + // integration after the second stage lammps atomic + // update of Verlet step 2 + //-------------------------------------------------------- + void ATC_CouplingEnergy::post_final_integrate() + { + double dt = lammpsInterface_->dt(); + + // update changes in atomic energy or from atomic work, if needed + // this is here to simplify computing changes in total atomic energy + // even though all the data needed is available by pre_final_integrate + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_final_integrate1(dt); + } + + // Set prescribed sources for current time + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + + // predictor step in extrinsic model + extrinsicModelManager_.pre_final_integrate(); + + // predict thermostat contributions + // compute sources based on predicted FE temperature + + if (timeIntegrators_[TEMPERATURE]->has_final_predictor()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(temperatureMask_,fields_,atomicSources_); + } + + // Compute thermostat forces + atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); + + // Determine FE contributions to d theta/dt + // Compute atom-integrated rhs + // parallel communication happens within FE_Engine + + + + // Determine FE contributions to dT/dt----------------------- + compute_rhs_vector(temperatureMask_,fields_,rhs_,FE_DOMAIN); + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->add_to_rhs(); + } + // For flux matching, add appropriate fraction of "drag" power + + atomicRegulator_->add_to_rhs(rhs_); + + // final phase predictor step + 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(); + + // corrector step extrinsic model + extrinsicModelManager_.post_final_integrate(); + + // correct thermostat and finish + if (timeIntegrators_[TEMPERATURE]->has_final_corrector()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(temperatureMask_,fields_,atomicSources_); + } + + // finish FE temperature update + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate2(dt); + } + + // apply corrector phase of thermostat + atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep()); + + // finalalize time filtering + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate3(dt); + } + + // Fix nodes, non-group bcs applied through FE + set_fixed_nodes(); + + update_time(0.5); + + output(); + ATC_Coupling::post_final_integrate(); // adds next step to computes + } + + //-------------------------------------------------------------------- + // compute_vector + //-------------------------------------------------------------------- + // this is for direct output to lammps thermo + double ATC_CouplingEnergy::compute_vector(int n) + { + // output[1] = total coarse scale thermal energy + // output[2] = average temperature + + double mvv2e = lammpsInterface_->mvv2e(); // convert to lammps energy units + + if (n == 0) { + Array mask(1); + FIELD_MATS energy; + mask(0) = TEMPERATURE; + + feEngine_->compute_energy(mask, + fields_, + physicsModel_, + elementToMaterialMap_, + energy, + &(elementMask_->quantity())); + + double phononEnergy = mvv2e * energy[TEMPERATURE].col_sum(); + return phononEnergy; + } + else if (n == 1) { + double aveT = (fields_[TEMPERATURE].quantity()).col_sum()/nNodes_; + return aveT; + } + else if (n > 1) { + double extrinsicValue = extrinsicModelManager_.compute_vector(n); + return extrinsicValue; + } + + return 0.; + + } + + //-------------------------------------------------------------------- + // output + //-------------------------------------------------------------------- + void ATC_CouplingEnergy::output() + { + if (output_now()) { + feEngine_->departition_mesh(); + + // avoid possible mpi calls + if (nodalAtomicKineticTemperature_) + _keTemp_ = nodalAtomicKineticTemperature_->quantity(); + if (nodalAtomicConfigurationalTemperature_) + _peTemp_ = nodalAtomicConfigurationalTemperature_->quantity(); + + OUTPUT_LIST outputData; + + // base class output + ATC_Method::output(); + + // push atc fields time integrator modifies into output arrays + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_process(); + } + + // auxilliary data + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->output(outputData); + } + atomicRegulator_->output(outputData); + extrinsicModelManager_.output(outputData); + + DENS_MAT & temperature(nodalAtomicFields_[TEMPERATURE].set_quantity()); + DENS_MAT & dotTemperature(dot_fields_[TEMPERATURE].set_quantity()); + DENS_MAT & ddotTemperature(ddot_fields_[TEMPERATURE].set_quantity()); + DENS_MAT & rocTemperature(nodalAtomicFieldsRoc_[TEMPERATURE].set_quantity()); + DENS_MAT & fePower(rhs_[TEMPERATURE].set_quantity()); + if (lammpsInterface_->rank_zero()) { + // global data + double T_mean = (fields_[TEMPERATURE].quantity()).col_sum(0)/nNodes_; + feEngine_->add_global("temperature_mean", T_mean); + double T_stddev = (fields_[TEMPERATURE].quantity()).col_stdev(0); + feEngine_->add_global("temperature_std_dev", T_stddev); + double Ta_mean = (nodalAtomicFields_[TEMPERATURE].quantity()).col_sum(0)/nNodes_; + feEngine_->add_global("atomic_temperature_mean", Ta_mean); + double Ta_stddev = (nodalAtomicFields_[TEMPERATURE].quantity()).col_stdev(0); + feEngine_->add_global("atomic_temperature_std_dev", Ta_stddev); + + // different temperature measures, if appropriate + if (nodalAtomicKineticTemperature_) + outputData["kinetic_temperature"] = & _keTemp_; + + if (nodalAtomicConfigurationalTemperature_) { + _peTemp_ *= 2; // account for full temperature + outputData["configurational_temperature"] = & _peTemp_; + } + + // mesh data + outputData["NodalAtomicTemperature"] = &temperature; + outputData["dot_temperature"] = &dotTemperature; + outputData["ddot_temperature"] = &ddotTemperature; + outputData["NodalAtomicPower"] = &rocTemperature; + outputData["fePower"] = &fePower; + + // write data + feEngine_->write_data(output_index(), fields_, & outputData); + } + feEngine_->partition_mesh(); + } + } +}; diff --git a/lib/atc/ATC_CouplingEnergy.h b/lib/atc/ATC_CouplingEnergy.h new file mode 100644 index 0000000000..6866cd0510 --- /dev/null +++ b/lib/atc/ATC_CouplingEnergy.h @@ -0,0 +1,127 @@ +#ifndef ATC_COUPLING_ENERGY_H +#define ATC_COUPLING_ENERGY_H + +// ATC headers +#include "ATC_Coupling.h" +#include "ThermalTimeIntegrator.h" +//TEMP_JAT - remove when needs_reset is moved to base class +#include "AtomicRegulator.h" + +// Other headers +#include + +namespace ATC { + + class AtfShapeFunctionRestriction; + class AtfShapeFunctionMdProjection; + + /** + * @class ATC_CouplingEnergy + * @brief A class for atom-continuum transfers & control involving heat transport + * (owned field/s: TEMPERATURE) + */ + + class ATC_CouplingEnergy : public ATC_Coupling { + + public: + + // constructor + ATC_CouplingEnergy(std::string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile, + ExtrinsicModelType extrinsic = NO_MODEL); + + // destructor + virtual ~ATC_CouplingEnergy(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** flags whether a methods reset is required */ + + virtual bool reset_methods() const { + bool resetMethods = ATC_Method::reset_methods() || atomicRegulator_->need_reset(); + _ctiIt_ = timeIntegrators_.find(TEMPERATURE); + if (_ctiIt_ == timeIntegrators_.end()) return resetMethods; + return resetMethods || (_ctiIt_->second)->need_reset(); + }; + + /** post time integration */ + virtual void finish(); + + /** first time substep routines */ + virtual void pre_init_integrate(); + /** first time, after atomic velocity but before position integration */ + virtual void mid_init_integrate(); + /** first time, after atomic integration */ + virtual void post_init_integrate(); + + /** second time substep routine */ + virtual void post_final_integrate(); + + /** compute vector for output */ + virtual double compute_vector(int n); + + /** output */ + virtual void output(); + + + /** set up atom to material identification */ + virtual void reset_atom_materials(); + + protected: + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** constructs all data which is updated with time integration, i.e. fields */ + //virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + /** sets the position/velocity of the ghost atoms */ + virtual void set_ghost_atoms(){}; + + /** adds resetting of any thermostat arrays associated with local atom count */ + virtual void reset_nlocal(); + + /** compute the mass matrix components coming from MD integration */ + virtual void compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMats); + + /** operator to compute mass matrix from MD */ + AtfShapeFunctionRestriction * nodalAtomicHeatCapacity_; + + /** physics specific filter initialization */ + void init_filter(); + + /** field mask for velocity integration */ + Array2D temperatureMask_; + + + double compute_lambda_power(int gid); + + /** kinetic temperature for post-processing */ + AtfShapeFunctionMdProjection * nodalAtomicKineticTemperature_; + + /** configurational temperature for post-processing */ + AtfShapeFunctionMdProjection * nodalAtomicConfigurationalTemperature_; + + /** workspace matrices for output */ + DENS_MAT _keTemp_, _peTemp_; + + // Add in fields for restarting + virtual void read_restart_data(string fileName_, RESTART_LIST & data); + virtual void write_restart_data(string fileName_, RESTART_LIST & data); + void pack_thermal_fields(RESTART_LIST & data); + + }; + +}; +#endif diff --git a/lib/atc/ATC_CouplingMass.cpp b/lib/atc/ATC_CouplingMass.cpp new file mode 100644 index 0000000000..dd586d59c4 --- /dev/null +++ b/lib/atc/ATC_CouplingMass.cpp @@ -0,0 +1,544 @@ +// ATC_Transfer headers +#include "ATC_CouplingMass.h" +#include "ATC_Error.h" +#include "FE_Engine.h" +#include "SpeciesTimeIntegrator.h" +#include "PrescribedDataManager.h" +#include "ExtrinsicModelElectrostatic.h" +#include "PoissonSolver.h" +#include "ChargeRegulator.h" +#include "ConcentrationRegulator.h" +#include "PerAtomQuantityLibrary.h" +#include "TransferOperator.h" +#include "AtomToMoleculeTransfer.h" +#include "MoleculeSet.h" +#include "FieldManager.h" + +// Other Headers +#include +#include +#include +#include + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ATC_CouplingMass + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ATC_CouplingMass::ATC_CouplingMass(string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile, + ExtrinsicModelType extrinsicModel) + : ATC_Coupling(groupName,perAtomArray,thisFix), + resetNlocal_(false) + { + // Allocate PhysicsModel + create_physics_model(SPECIES, matParamFile); + + // create extrinsic physics model + if (extrinsicModel != NO_MODEL) { + extrinsicModelManager_.create_model(extrinsicModel,matParamFile); + } + + // Defaults + set_time(); + bndyIntType_ = NO_QUADRATURE; + + + // set up field data based on physicsModel + physicsModel_->num_fields(fieldSizes_,fieldMask_); + + // regulator + atomicRegulator_ = new ConcentrationRegulator(this); + + // set up physics specific time integrator + //WIP_JAT should be species concentration + timeIntegrators_[MASS_DENSITY] = new SpeciesTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP); + + // output variable vector info: + // output[1] = system mass density + vectorFlag_ = 1; + sizeVector_ = 0; + scalarVectorFreq_ = 1; + extVector_ = 1; + if (extrinsicModel != NO_MODEL) + sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_); + sizeVector_ += atomicRegulator_->size_vector(sizeVector_); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ATC_CouplingMass::~ATC_CouplingMass() + { + interscaleManager_.clear(); + } + + //-------------------------------------------------------- + // modify + // parses inputs and modifies state + //-------------------------------------------------------- + bool ATC_CouplingMass::modify(int narg, char **arg) + { + bool match = false; + // check to see if it is a transfer class command + + // check derived class before base class + int argIndex = 0; + // pass-through to concentration regulator + if (strcmp(arg[argIndex],"control")==0) { + argIndex++; + if (strcmp(arg[argIndex],"concentration")==0) { + argIndex++; + match = atomicRegulator_->modify(narg-argIndex,&arg[argIndex]); + } + } + // no match, call base class parser + if (!match) { + match = ATC_Coupling::modify(narg, arg); + } + return match; + } + + //-------------------------------------------------------- + // initialize + // sets up all the necessary data + //-------------------------------------------------------- + void ATC_CouplingMass::initialize() + { + + fieldSizes_[SPECIES_CONCENTRATION] = speciesIds_.size(); + + // Base class initalizations + ATC_Coupling::initialize(); + + // check that only all atoms + + if (bndyIntType_ != NO_QUADRATURE) throw ATC_Error("ATC_CouplingMass: only all atoms simulations are supported"); + + // set consistent initial conditions, if requested + if (!timeFilterManager_.filter_dynamics()) { + if (consistentInitialization_) { + + DENS_MAT & massDensity(fields_[MASS_DENSITY].set_quantity()); + const DENS_MAT & atomicMassDensity(nodalAtomicFields_[MASS_DENSITY].quantity()); + + DENS_MAT & speciesConcentration(fields_[SPECIES_CONCENTRATION].set_quantity()); + //const DENS_MAT & atomicSpeciesConcentration(nodalAtomicFields_[SPECIES_CONCENTRATION].quantity()); + const DENS_MAT & atomicSpeciesConcentration(atomicFields_[SPECIES_CONCENTRATION]->quantity()); + + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (int i = 0; isecond)->initialize(); + } + } + extrinsicModelManager_.initialize(); // always needed to construct new Poisson solver + if (timeFilterManager_.need_reset()) { init_filter(); } + timeFilterManager_.initialize(); // clears need for reset + atomicRegulator_->initialize(); + + if (!initialized_) { + // initialize sources based on initial FE temperature + double dt = lammpsInterface_->dt(); + // set sources + + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + compute_atomic_sources(fieldMask_,fields_,atomicSources_); + + // read in field data if necessary + if (useRestart_) { + RESTART_LIST data; + read_restart_data(restartFileName_,data); + useRestart_ = false; + } + + initialized_ = true; + } + + // reset integration field mask + speciesMask_.reset(NUM_FIELDS,NUM_FLUX); + speciesMask_ = false; + + } + + //-------------------------------------------------------- + // construct_methods + // have managers instantiate requested algorithms + // and methods + //-------------------------------------------------------- + void ATC_CouplingMass::construct_methods() + { + ATC_Coupling::construct_methods(); + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->construct_methods(); + } + atomicRegulator_->construct_methods(); + } + + void ATC_CouplingMass::construct_transfers() + { + ATC_Coupling::construct_transfers(); + FieldManager fmgr(this); + atomicFields_[MASS_DENSITY] = fmgr.nodal_atomic_field(MASS_DENSITY, field_to_intrinsic_name(MASS_DENSITY)); + + if (has_tracked_species()) { + atomicFields_[SPECIES_CONCENTRATION] = fmgr.nodal_atomic_field(SPECIES_CONCENTRATION, field_to_intrinsic_name(SPECIES_CONCENTRATION)); + + //if (atomicRegulator_->needs_temperature()) { + + atomicFields_[TEMPERATURE] = fmgr.nodal_atomic_field(KINETIC_TEMPERATURE, field_to_intrinsic_name(TEMPERATURE)); + //atomicFields_[TEMPERATURE] = fmgr.nodal_atomic_field(TEMPERATURE, field_to_intrinsic_name(TEMPERATURE)); + field(TEMPERATURE) = atomicFields_[TEMPERATURE]->quantity(); + //} + } + else { + + throw ATC_Error("ATC_CouplingMass: no tracked species"); + } + + //========================================================================== + // add molecule mass density transfer operators + //========================================================================== + map >::const_iterator molecule; + FundamentalAtomQuantity * mass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, + PROC_GHOST); + + for (molecule = moleculeIds_.begin(); molecule != moleculeIds_.end(); molecule++) { + const string moleculeName = molecule->first; + SmallMoleculeSet * smallMoleculeSet = interscaleManager_.small_molecule_set(moleculeName); + SPAR_MAN * shpFcnMol = interscaleManager_.sparse_matrix("ShapeFunction"+moleculeName); + AtomToSmallMoleculeTransfer * moleculeMass = + new AtomToSmallMoleculeTransfer(this,mass,smallMoleculeSet); + interscaleManager_.add_dense_matrix(moleculeMass,"MoleculeMass"+moleculeName); + MotfShapeFunctionRestriction * nodalAtomicMoleculeMass = + new MotfShapeFunctionRestriction(moleculeMass,shpFcnMol); + interscaleManager_.add_dense_matrix(nodalAtomicMoleculeMass,"NodalMoleculeMass"+moleculeName); + + + AtfShapeFunctionMdProjection * nodalAtomicMoleculeMassDensity = + new AtfShapeFunctionMdProjection(this,nodalAtomicMoleculeMass,MASS_DENSITY); + interscaleManager_.add_dense_matrix(nodalAtomicMoleculeMassDensity,"NodalMoleculeMassDensity"+moleculeName); + } + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->construct_transfers(); + } + } + + void ATC_CouplingMass::init_filter() + { + + ATC_Coupling::init_filter(); + } + + void ATC_CouplingMass::compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMat) + { + + if (thisField == MASS_DENSITY || + thisField == SPECIES_CONCENTRATION) { + massMat.reset(nodalAtomicVolume_->quantity()); + } + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + + void ATC_CouplingMass::pack_species_fields(RESTART_LIST & data) + { + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMass::write_restart_data(string fileName, RESTART_LIST & data) + { + pack_species_fields(data); + ATC_Method::write_restart_data(fileName,data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMass::read_restart_data(string fileName, RESTART_LIST & data) + { + + pack_species_fields(data); + ATC_Method::read_restart_data(fileName,data); + } + + //-------------------------------------------------------- + // pre_force + // prior to calculation of forces + //-------------------------------------------------------- + void ATC_CouplingMass::pre_force() + { + ATC_Coupling::pre_force(); + atomicRegulator_->pre_force(); + } + + //-------------------------------------------------------- + // pre_exchange + // prior to exchange of atoms + //-------------------------------------------------------- + void ATC_CouplingMass::pre_exchange() + { + ATC_Coupling::pre_exchange(); + + //if (atomicRegulator_->needs_temperature()) { + field(TEMPERATURE) = atomicFields_[TEMPERATURE]->quantity(); +///} + atomicRegulator_->pre_exchange(); + if (resetNlocal_) { + this->reset_nlocal(); + resetNlocal_ = false; + } + } + + //-------------------------------------------------------- + // pre_init_integrate + // time integration before the lammps atomic + // integration of the Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMass::pre_init_integrate() + { + ATC_Coupling::pre_init_integrate(); + double dt = lammpsInterface_->dt(); + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate1(dt); + } + + // Apply thermostat force to atom velocities + atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep()); + + // Predict nodal temperatures and time derivatives based on FE data + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate2(dt); + } + extrinsicModelManager_.pre_init_integrate(); + } + + //-------------------------------------------------------- + // mid_init_integrate + // time integration between the velocity update and + // the position lammps update of Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMass::mid_init_integrate() + { + ATC_Coupling::mid_init_integrate(); + double dt = lammpsInterface_->dt(); + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->mid_initial_integrate1(dt); + } + + atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep()); + + extrinsicModelManager_.mid_init_integrate(); + } + + //-------------------------------------------------------- + // post_init_integrate + // time integration after the lammps atomic updates of + // Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMass::post_init_integrate() + { + double dt = lammpsInterface_->dt(); + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_initial_integrate1(dt); + } + + atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep()); + + extrinsicModelManager_.post_init_integrate(); + + set_fixed_nodes(); + update_time(0.5); // half step + ATC_Coupling::post_init_integrate(); + } + + //-------------------------------------------------------- + // post_final_integrate + // integration after the second stage lammps atomic + // update of Verlet step 2 + //-------------------------------------------------------- + void ATC_CouplingMass::post_final_integrate() + { + double dt = lammpsInterface_->dt(); + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_final_integrate1(dt); + } + + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.pre_final_integrate(); + + + if (timeIntegrators_[MASS_DENSITY]->has_final_predictor()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + + compute_atomic_sources(speciesMask_,fields_,atomicSources_); + } + + + + // set state-based RHS + // Determine FE contributions to dv/dt----------------------- + // Compute atom-integrated rhs + // parallel communication happens within FE_Engine + compute_rhs_vector(speciesMask_,fields_,rhs_,FE_DOMAIN); + // Compute and add atomic contributions to FE equations + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->add_to_rhs(); + } + atomicRegulator_->add_to_rhs(rhs_); + + // final phase predictor step + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate1(dt); + } + + set_fixed_nodes(); + + // corrector step extrinsic model + extrinsicModelManager_.post_final_integrate(); + + if (timeIntegrators_[MASS_DENSITY]->has_final_corrector()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + + compute_atomic_sources(speciesMask_,fields_,atomicSources_); + } + + // finish FE temperature update + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate2(dt); + } + + // apply corrector phase of thermostat + atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep()); + + // finalize time integration + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate3(dt); + } + + // Fix nodes, non-group bcs applied through FE + set_fixed_nodes(); + + update_time(0.5); + output(); + ATC_Coupling::post_final_integrate(); // addstep for computes + } + + //-------------------------------------------------------- + // output + // does post-processing steps and outputs data + //-------------------------------------------------------- + void ATC_CouplingMass::output() + { + if (output_now()) { + feEngine_->departition_mesh(); + OUTPUT_LIST outputData; + + // base class output + ATC_Coupling::output(); + + // push atc fields time integrator modifies into output arrays + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_process(); + } + + // auxilliary data + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->output(outputData); + } + extrinsicModelManager_.output(outputData); + atomicRegulator_->output(outputData); + + FIELD_POINTERS::iterator itr; + for (itr=atomicFields_.begin(); itr!=atomicFields_.end();itr++) { + FieldName name = itr->first; + const DENS_MAT & data = (itr->second)->quantity(); + outputData[field_to_intrinsic_name(name)] = & data; + } + // compute partial forces + int * type =lammpsInterface_->atom_type(); + double ** f =lammpsInterface_->fatom(); + for (unsigned int j = 0; j < typeList_.size(); j++) { + string speciesName = typeNames_[j]; + int sType = typeList_[j]; + double localF[3] = {0,0,0}, F[3] = {0,0,0}; + for (int i = 0; i < nLocal_; i++) { + int a = internalToAtom_(i); + if (sType == type[a]) { + double * fa = f[a]; + localF[0] += fa[0]; + localF[1] += fa[1]; + localF[2] += fa[2]; + } + } + lammpsInterface_->allsum(localF,F,3); + if (lammpsInterface_->rank_zero()) { + for (int i = 0; i < 3; ++i) { + feEngine_->add_global(speciesName+"_F"+to_string(i+1), F[i]); + } + } + } + if (lammpsInterface_->rank_zero()) { + // tagged data + + map::iterator densMan; + for (densMan = taggedDensMan_.begin(); densMan != taggedDensMan_.end(); densMan++) { + outputData[densMan->first] = & (densMan->second).set_quantity(); + } + + feEngine_->write_data(output_index(), fields_, & outputData); + } + // force reset of tagged data to keep in sync + map::iterator densMan; + for (densMan = taggedDensMan_.begin(); densMan != taggedDensMan_.end(); densMan++) + (densMan->second).force_reset(); + feEngine_->partition_mesh(); + } + } + + //-------------------------------------------------------------------- + // compute_vector + //-------------------------------------------------------------------- + // this is for direct output to lammps thermo + double ATC_CouplingMass::compute_vector(int n) + { + return atomicRegulator_->compute_vector(n); + } +}; diff --git a/lib/atc/ATC_CouplingMass.h b/lib/atc/ATC_CouplingMass.h new file mode 100644 index 0000000000..aa22cc512c --- /dev/null +++ b/lib/atc/ATC_CouplingMass.h @@ -0,0 +1,114 @@ +#ifndef ATC_COUPLING_MASS_H +#define ATC_COUPLING_MASS_H + +/** owned field/s: MASS_DENSITY */ + +// ATC headers +#include "ATC_Coupling.h" + +// Other headers +#include + +namespace ATC { + + // Forward declarations + class FE_Engine; + class SpeciesTimeIntegrator; + class ChargeRegulator; + class ConcentrationRegulator; + + /** + * @class ATC_CouplingMass + * @brief A class for atom-continuum transfers & control for species transport + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ATC_CouplingMass + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ATC_CouplingMass : public ATC_Coupling { + + public: + + // constructor + ATC_CouplingMass(std::string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile, + ExtrinsicModelType extrinsic = NO_MODEL); + + // destructor + virtual ~ATC_CouplingMass(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** prior to force */ + virtual void pre_force(); + /** prior to exchange */ + virtual void pre_exchange(); + virtual void reset_atoms() { resetNlocal_=true;} + /** first time, before atomic integration */ + virtual void pre_init_integrate(); + /** first time, after atomic velocity but before position integration */ + virtual void mid_init_integrate(); + /** first time, after atomic integration */ + virtual void post_init_integrate(); + /** second time, after atomic integration */ + virtual void post_final_integrate(); + + /** compute vector for output */ + virtual double compute_vector(int n); + + /** output routines */ + virtual void output(); + + protected: + + // functions + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** constructs all data which is updated with time integration, i.e. fields */ + //virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + /** sets the position/velocity of the ghost atoms */ + virtual void set_ghost_atoms(){}; + + /** compute the mass matrix components coming from MD integration */ + virtual void compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMats); + + /** physics specific filter initialization */ + void init_filter(); + + /** field mask for velocity integration */ + Array2D speciesMask_; + + // Add in fields for restarting + virtual void read_restart_data(string fileName_, RESTART_LIST & data); + virtual void write_restart_data(string fileName_, RESTART_LIST & data); + void pack_species_fields(RESTART_LIST & data); + + // DATA structures for tracking individual species and molecules + FIELD_POINTERS atomicFields_; + + bool resetNlocal_; + + + + // i.e. we only need the correct shape function matrix for restriction + }; + +}; + +#endif diff --git a/lib/atc/ATC_CouplingMomentum.cpp b/lib/atc/ATC_CouplingMomentum.cpp new file mode 100644 index 0000000000..53819529d1 --- /dev/null +++ b/lib/atc/ATC_CouplingMomentum.cpp @@ -0,0 +1,978 @@ +// ATC headers +#include "ATC_CouplingMomentum.h" +#include "ATC_Error.h" +#include "LammpsInterface.h" +#include "PrescribedDataManager.h" +#include "PerAtomQuantity.h" +#include "TransferOperator.h" + +// Other Headers +#include +#include +#include +#include + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ATC_CouplingMomentum + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ATC_CouplingMomentum::ATC_CouplingMomentum(string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile, + PhysicsType intrinsicModel, + ExtrinsicModelType extrinsicModel) + : ATC_Coupling(groupName,perAtomArray,thisFix), + nodalAtomicMass_(NULL), + nodalAtomicCount_(NULL), + boundaryDynamics_(PRESCRIBED), + gamma_(0),mu_(1),kappa_(1), + refPE_(0) + { + // Allocate PhysicsModel + create_physics_model(intrinsicModel, matParamFile); + + // create extrinsic physics model + if (extrinsicModel != NO_MODEL) { + extrinsicModelManager_.create_model(extrinsicModel,matParamFile); + } + + // set up field data based on physicsModel + physicsModel_->num_fields(fieldSizes_,fieldMask_); + + // Defaults + set_time(); + bndyIntType_ = FE_INTERPOLATION; + trackCharge_ = false; + + // use a kinetostat + atomicRegulator_ = new Kinetostat(this); + + // set time integrator and change any defaults based on model type + if (intrinsicModel == ELASTIC) { + trackDisplacement_ = true; + fieldSizes_[DISPLACEMENT] = fieldSizes_[VELOCITY]; + timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::VERLET); + } + else if (intrinsicModel == SHEAR) { + atomToElementMapType_ = EULERIAN; + atomToElementMapFrequency_ = 1; + timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::GEAR); + } + + // output variable vector info: + // output[1] = total coarse scale kinetic energy + // output[2] = total coarse scale potential energy + // output[3] = total coarse scale energy + scalarFlag_ = 1; + vectorFlag_ = 1; + sizeVector_ = 5; + scalarVectorFreq_ = 1; + extVector_ = 1; + thermoEnergyFlag_ = 1; + if (extrinsicModel != NO_MODEL) + sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ATC_CouplingMomentum::~ATC_CouplingMomentum() + { + interscaleManager_.clear(); + } + + //-------------------------------------------------------- + // initialize + // sets up all the necessary data + //-------------------------------------------------------- + void ATC_CouplingMomentum::initialize() + { + // clear displacement entries if requested + if (!trackDisplacement_) { + fieldSizes_.erase(DISPLACEMENT); + for (int i = 0; i < NUM_FLUX; i++) + fieldMask_(DISPLACEMENT,i) = false; + } + + // Base class initalizations + ATC_Coupling::initialize(); + + // check resetting precedence: + // time integrator -> kinetostat -> time filter + + // other initializations + if (reset_methods()) { + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->initialize(); + } + atomicRegulator_->initialize(); + } + extrinsicModelManager_.initialize(); + if (timeFilterManager_.need_reset()) { // reset kinetostat power + init_filter(); + } + timeFilterManager_.initialize(); // clears need for reset + + if (!initialized_) { + // initialize sources based on initial FE temperature + double dt = lammpsInterface_->dt(); + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(fieldMask_,fields_,atomicSources_); + + // read in field data if necessary + if (useRestart_) { + RESTART_LIST data; + read_restart_data(restartFileName_,data); + useRestart_ = false; + } + + // set consistent initial conditions, if requested + if (!timeFilterManager_.filter_dynamics()) { + if (consistentInitialization_) { + + DENS_MAT & velocity(fields_[VELOCITY].set_quantity()); + DENS_MAN * nodalAtomicVelocity(interscaleManager_.dense_matrix("NodalAtomicVelocity")); + const DENS_MAT & atomicVelocity(nodalAtomicVelocity->quantity()); + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (int i = 0; iquantity()); + for (int i = 0; isecond)->construct_methods(); + } + atomicRegulator_->construct_methods(); + } + + //-------------------------------------------------------- + // construct_transfers + // constructs needed transfer operators + //-------------------------------------------------------- + void ATC_CouplingMomentum::construct_transfers() + { + ATC_Coupling::construct_transfers(); + + // momentum of each atom + AtomicMomentum * atomicMomentum = new AtomicMomentum(this); + interscaleManager_.add_per_atom_quantity(atomicMomentum, + "AtomicMomentum"); + + // nodal momentum for RHS + AtfShapeFunctionRestriction * nodalAtomicMomentum = new AtfShapeFunctionRestriction(this, + atomicMomentum, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMomentum, + "NodalAtomicMomentum"); + + // nodal forces + FundamentalAtomQuantity * atomicForce = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE); + AtfShapeFunctionRestriction * nodalAtomicForce = new AtfShapeFunctionRestriction(this, + atomicForce, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicForce, + "NodalAtomicForce"); + + // nodal velocity derived only from atoms + AtfShapeFunctionMdProjection * nodalAtomicVelocity = new AtfShapeFunctionMdProjection(this, + nodalAtomicMomentum, + VELOCITY); + interscaleManager_.add_dense_matrix(nodalAtomicVelocity, + "NodalAtomicVelocity"); + + if (trackDisplacement_) { + // mass-weighted (center-of-mass) displacement of each atom + AtomicMassWeightedDisplacement * atomicMassWeightedDisplacement; + if (needXrefProcessorGhosts_ || groupbitGhost_) { // explicit construction on internal group + PerAtomQuantity * atomReferencePositions = interscaleManager_.per_atom_quantity("AtomicInternalReferencePositions"); + atomicMassWeightedDisplacement = new AtomicMassWeightedDisplacement(this,atomPositions_, + atomMasses_, + atomReferencePositions, + INTERNAL); + } + else + atomicMassWeightedDisplacement = new AtomicMassWeightedDisplacement(this); + interscaleManager_.add_per_atom_quantity(atomicMassWeightedDisplacement, + "AtomicMassWeightedDisplacement"); + + // nodal (RHS) mass-weighted displacement + AtfShapeFunctionRestriction * nodalAtomicMassWeightedDisplacement = new AtfShapeFunctionRestriction(this, + atomicMassWeightedDisplacement, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMassWeightedDisplacement, + "NodalAtomicMassWeightedDisplacement"); + + // nodal displacement derived only from atoms + AtfShapeFunctionMdProjection * nodalAtomicDisplacement = new AtfShapeFunctionMdProjection(this, + nodalAtomicMassWeightedDisplacement, + VELOCITY); + interscaleManager_.add_dense_matrix(nodalAtomicDisplacement, + "NodalAtomicDisplacement"); + } + + // atomic mass matrix data + if (!useFeMdMassMatrix_) { + // atomic momentum mass matrix + FundamentalAtomQuantity * atomicMass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); + nodalAtomicMass_ = new AtfShapeFunctionRestriction(this, + atomicMass, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMass_, + "AtomicMomentumMassMat"); + + // atomic dimensionless mass matrix + ConstantQuantity * atomicOnes = new ConstantQuantity(this,1); + interscaleManager_.add_per_atom_quantity(atomicOnes,"AtomicOnes"); + nodalAtomicCount_ = new AtfShapeFunctionRestriction(this, + atomicOnes, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicCount_, + "AtomicDimensionlessMassMat"); + } + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->construct_transfers(); + } + atomicRegulator_->construct_transfers(); + } + + //--------------------------------------------------------- + // init_filter + // sets up the time filtering operations in all objects + //--------------------------------------------------------- + void ATC_CouplingMomentum::init_filter() + { + + ATC_Coupling::init_filter(); + + if (timeFilterManager_.end_equilibrate() && equilibriumStart_) // set up correct initial lambda forces to enforce initial accerlation + if (atomicRegulator_->coupling_mode()==AtomicRegulator::FLUX || atomicRegulator_->coupling_mode()==AtomicRegulator::GHOST_FLUX) + // nothing needed in other cases since kinetostat force is balanced by boundary flux in FE equations + atomicRegulator_->reset_lambda_contribution(nodalAtomicFieldsRoc_[VELOCITY].quantity()); + } + + //--------------------------------------------------------- + // compute_md_mass_matrix + // compute the mass matrix arising from only atomistic + // quadrature and contributions as a summation + //--------------------------------------------------------- + void ATC_CouplingMomentum::compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMat) + { + + + if (thisField == DISPLACEMENT || thisField == VELOCITY) + massMat.reset(nodalAtomicMass_->quantity()); + else if (thisField == MASS_DENSITY) { // dimensionless mass matrix + massMat.reset(nodalAtomicCount_->quantity()); + } + } + + //-------------------------------------------------------- + // finish + // final clean up after a run + //-------------------------------------------------------- + void ATC_CouplingMomentum::finish() + { + // base class + ATC_Coupling::finish(); + + atomicRegulator_->finish(); + } + + //-------------------------------------------------------- + // modify + // parses inputs and modifies state of the filter + //-------------------------------------------------------- + bool ATC_CouplingMomentum::modify(int narg, char **arg) + { + bool foundMatch = false; + int argIndex = 0; + + // check to see if it is a transfer class command + // check derived class before base class + + // pass-through to kinetostat + if (strcmp(arg[argIndex],"control")==0) { + argIndex++; + foundMatch = atomicRegulator_->modify(narg-argIndex,&arg[argIndex]); + } + + // pass-through to timeIntegrator class + else if (strcmp(arg[argIndex],"time_integration")==0) { + argIndex++; + foundMatch = timeIntegrators_[VELOCITY]->modify(narg-argIndex,&arg[argIndex]); + } + + // switch for if displacement is tracked or not + /*! \page man_disp_control fix_modify AtC transfer track_displacement + \section syntax + fix_modify AtC transfer track_displacement \n + \section description + Determines whether displacement is tracked or not. For solids problems this is a useful quantity, but for fluids it is not relevant. + \section restrictions + Some constitutive models require the displacement field + \section default + on + */ + else if (strcmp(arg[argIndex],"track_displacement")==0) { + argIndex++; + if (strcmp(arg[argIndex],"on")==0) { + trackDisplacement_ = true; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"off")==0) { + trackDisplacement_ = false; + foundMatch = true; + } + if (foundMatch) { + needReset_ = true; + } + } + /*! \page man_boundary_dynamics fix_modify AtC transfer boundary_dynamics + \section syntax + fix_modify AtC transfer boundary_dynamics \n + \section description + \section restrictions + \section default + on + */ + else if (strcmp(arg[argIndex],"boundary_dynamics")==0) { + argIndex++; + gamma_ = 0; + kappa_ = 0; + mu_ = 0; + if (strcmp(arg[argIndex],"damped_harmonic")==0) { + argIndex++; + gamma_ = atof(arg[argIndex++]); + kappa_ = atof(arg[argIndex++]); + mu_ = atof(arg[argIndex++]); + boundaryDynamics_ = DAMPED_HARMONIC; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"prescribed")==0) { + boundaryDynamics_ = PRESCRIBED; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"coupled")==0) { + boundaryDynamics_ = COUPLED; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"none")==0) { + boundaryDynamics_ = NO_BOUNDARY_DYNAMICS; + foundMatch = true; + } + } + + // no match, call base class parser + if (!foundMatch) { + foundMatch = ATC_Coupling::modify(narg, arg); + } + + return foundMatch; + + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_CouplingMomentum::pack_elastic_fields(RESTART_LIST & data) + { + atomicRegulator_->pack_fields(data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMomentum::write_restart_data(string fileName, RESTART_LIST & data) + { + pack_elastic_fields(data); + ATC_Method::write_restart_data(fileName,data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMomentum::read_restart_data(string fileName, RESTART_LIST & data) + { + pack_elastic_fields(data); + ATC_Method::read_restart_data(fileName,data); + } + + //-------------------------------------------------------- + void ATC_CouplingMomentum::reset_nlocal() + { + ATC_Coupling::reset_nlocal(); + atomicRegulator_->reset_nlocal(); + } + + //-------------------------------------------------- + // reset_atom_materials + // update the atom materials map + //-------------------------------------------------- + void ATC_CouplingMomentum::reset_atom_materials() + { + ATC_Coupling::reset_atom_materials(); + atomicRegulator_->reset_atom_materials(elementToMaterialMap_, + atomElement_); + } + + //-------------------------------------------------------- + // pre_init_integrate + // time integration before the lammps atomic + // integration of the Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMomentum::pre_init_integrate() + { + ATC_Coupling::pre_init_integrate(); + double dt = lammpsInterface_->dt(); + + // get any initial data before its modified + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate1(dt); + } + + // apply kinetostat force, if needed + atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep()); + + // predict nodal velocities + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate2(dt); + } + extrinsicModelManager_.pre_init_integrate(); + } + + //-------------------------------------------------------- + // mid_init_integrate + // time integration between the velocity update and + // the position lammps update of Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMomentum::mid_init_integrate() + { + // CONTINUOUS VELOCITY UPDATE + + ATC_Coupling::mid_init_integrate(); + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1/2 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->mid_initial_integrate1(dt); + } + + atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep()); + + extrinsicModelManager_.mid_init_integrate(); + } + + //-------------------------------------------------------- + // post_init_integrate + // time integration after the lammps atomic updates of + // Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMomentum::post_init_integrate() + { + // CONTINUOUS DISPLACEMENT UPDATE + + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_initial_integrate1(dt); + } + + // Update kinetostat quantities if displacement is being regulated + atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep()); + + // Update extrisic model + extrinsicModelManager_.post_init_integrate(); + + // fixed values, non-group bcs handled through FE + set_fixed_nodes(); + + + // enforce atomic boundary conditions + if (boundaryDynamics_==PRESCRIBED) set_ghost_atoms(); + else if (boundaryDynamics_==DAMPED_HARMONIC) initial_integrate_ghost(); + else if (boundaryDynamics_==COUPLED) initial_integrate_ghost(); + + // update time by a half dt + update_time(0.5); + + ATC_Coupling::post_init_integrate(); + } + + //-------------------------------------------------------- + // pre_final_integrate + // integration before the second stage lammps atomic + // update of Verlet step 2 + //-------------------------------------------------------- + void ATC_CouplingMomentum::pre_final_integrate() + { + ATC_Coupling::pre_final_integrate(); + + if (boundaryDynamics_==DAMPED_HARMONIC) { + apply_ghost_forces(); + final_integrate_ghost(); + } + else if (boundaryDynamics_==COUPLED) { + add_ghost_forces(); + final_integrate_ghost(); + } + } + + //-------------------------------------------------------- + // post_final_integrate + // integration after the second stage lammps atomic + // update of Verlet step 2 + //-------------------------------------------------------- + void ATC_CouplingMomentum::post_final_integrate() + { + // COMPUTE FORCES FOR FE VELOCITY RHS + + double dt = lammpsInterface_->dt(); + + // updating of data based on atomic forces + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_final_integrate1(dt); + } + + // Set prescribed sources for current time + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + + // predictor step in extrinsic model + extrinsicModelManager_.pre_final_integrate(); + + + if (timeIntegrators_[VELOCITY]->has_final_predictor()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(velocityMask_,fields_,atomicSources_); + } + + // Compute kinetostat forces and add kinetostat contributions to FE equations + + atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); // computes but does not apply kstat, and only for StressFlux + + // set state-based RHS + // Determine FE contributions to dv/dt----------------------- + // Compute atom-integrated rhs + // parallel communication happens within FE_Engine + compute_rhs_vector(velocityMask_,fields_,rhs_,FE_DOMAIN); + // Compute and add atomic contributions to FE equations + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->add_to_rhs(); + } + // add in kinetostat contributions to FE equations + atomicRegulator_->add_to_rhs(rhs_); + + // final phase predictor step + 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(); + + // CONTINUOUS VELOCITY RHS UPDATE + + // corrector step extrinsic model + extrinsicModelManager_.post_final_integrate(); + + if (timeIntegrators_[VELOCITY]->has_final_corrector()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(velocityMask_,fields_,atomicSources_); + } + + // Finish update of FE velocity + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate2(dt); + } + + // Apply kinetostat to atoms + atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep()); + + // finalize time integration + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate3(dt); + } + + // Fix nodes, non-group bcs applied through FE + set_fixed_nodes(); + + // update time by a half dt + update_time(0.5); + + output(); + ATC_Coupling::post_final_integrate(); // addstep for computes + } + + //-------------------------------------------------------- + // min_pre_force + // add to interatomic forces for minimize + //-------------------------------------------------------- + void ATC_CouplingMomentum::min_pre_force() + { + } + + //-------------------------------------------------------- + // min_post_force + // add to interatomic forces for minimize + // this determines the search direction + //-------------------------------------------------------- + void ATC_CouplingMomentum::min_post_force() + { + // reset positions and shape functions + ATC_Method::min_post_force(); + + // Set sources + + prescribedDataMgr_->set_sources(time(),sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + extrinsicModelManager_.pre_final_integrate(); + + + + + if (outputNow_) { + update_time(1.0); + update_step(); + output(); + outputNow_ = false; + } + + + localStep_ += 1; + } + + //-------------------------------------------------------- + // output + // does post-processing steps and outputs data + //-------------------------------------------------------- + void ATC_CouplingMomentum::output() + { + if (output_now()) { + feEngine_->departition_mesh(); + OUTPUT_LIST outputData; + + // base class output + ATC_Method::output(); + + // push atc fields time integrator modifies into output arrays + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_process(); + } + + // auxilliary data + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->output(outputData); + } + atomicRegulator_->output(outputData); + extrinsicModelManager_.output(outputData); + + DENS_MAT & velocity(nodalAtomicFields_[VELOCITY].set_quantity()); + DENS_MAT & rhs(rhs_[VELOCITY].set_quantity()); + if (lammpsInterface_->rank_zero()) { + // mesh data + outputData["NodalAtomicVelocity"] = &velocity; + outputData["FE_Force"] = &rhs; + if (trackDisplacement_) { + outputData["NodalAtomicDisplacement"] = & nodalAtomicFields_[DISPLACEMENT].set_quantity(); + } + + feEngine_->write_data(output_index(), fields_, & outputData); + } + // force optional variables to reset to keep in sync + if (trackDisplacement_) { + nodalAtomicFields_[DISPLACEMENT].force_reset(); + } + + feEngine_->partition_mesh(); + } + } + + //-------------------------------------------------------- + // set_ghost_atoms + // sets ghost atom positions to finite element + // displacements based on shape functions + //-------------------------------------------------------- + void ATC_CouplingMomentum::set_ghost_atoms() + { + // set atomic displacements based on FE displacements + double ** x = lammpsInterface_->xatom(); + // prolong + DenseMatrix ghostAtomData(nLocalGhost_,nsd_); + if (nLocalGhost_>0) + ghostAtomData = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + + for (int i = 0; i < nLocalGhost_; ++i) + for (int j = 0; j < nsd_; ++j) + x[ghostToAtom_(i)][j] = ghostAtomData(i,j)+xref_[ghostToAtom_(i)][j]; + + + } + + //-------------------------------------------------------- + // add_ghost_forces + // add forces to dynamic ghosts + //-------------------------------------------------------- + void ATC_CouplingMomentum::add_ghost_forces() + { + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + + // add forces + DENS_MAT coarseDisp(nLocalGhost_,nsd_); + DENS_MAT coarseVel(nLocalGhost_,nsd_); + if (nLocalGhost_>0) { + coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity()); + } + // dynamics one-way coupled to real atoms in a well tied to coarse scale + for (int i = 0; i < nLocalGhost_; ++i) { + for (int j = 0; j < nsd_; ++j) { + double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j]; + double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j]; + f[ghostToAtom_(i)][j] += mu_*du + gamma_*dv; + + } + } + } + + void ATC_CouplingMomentum::apply_ghost_forces() + { + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + + // add forces + DENS_MAT coarseDisp(nLocalGhost_,nsd_); + DENS_MAT coarseVel(nLocalGhost_,nsd_); + if (nLocalGhost_>0) { + coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity()); + } + // dynamics one-way coupled to real atoms in a well tied to coarse scale + for (int i = 0; i < nLocalGhost_; ++i) { + for (int j = 0; j < nsd_; ++j) { + double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j]; + double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j]; + f[ghostToAtom_(i)][j] = mu_*du + gamma_*dv; + + } + } + } + + //-------------------------------------------------------- + // initial_integrate_ghost + // does the first step of the Verlet integration for + // ghost atoms, to be used with non-reflecting BCs + //-------------------------------------------------------- + void ATC_CouplingMomentum::initial_integrate_ghost() + { + double dtfm; + + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + const int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + double dtv = lammpsInterface_->dt(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / mu_; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } + + //-------------------------------------------------------- + // final_integrate_ghost + // does the second step of the Verlet integration for + // ghost atoms, to be used with non-reflecting BCs + //-------------------------------------------------------- + void ATC_CouplingMomentum::final_integrate_ghost() + { + double dtfm; + + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + const int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / mu_; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + } + + //-------------------------------------------------------------------- + // compute_scalar : added energy + // this is used in the line search + //-------------------------------------------------------------------- + double ATC_CouplingMomentum::compute_scalar(void) + { + double energy = extrinsicModelManager_.compute_scalar(); + return energy; + } + + //-------------------------------------------------------------------- + // kinetic energy + //-------------------------------------------------------------------- + double ATC_CouplingMomentum::kinetic_energy(const IntegrationDomainType domain) // const + { + const MATRIX & M = massMats_[VELOCITY].quantity(); + const DENS_MAT & velocity(fields_[VELOCITY].quantity()); + double kineticEnergy = 0; + for (int j = 0; j < nsd_; j++) { + CLON_VEC v = column(velocity,j); + kineticEnergy += v.dot(M*v); + } + if (domain == FE_DOMAIN) { + + Array massMask(1); + massMask(0) = VELOCITY; + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel_,atomMaterialGroups_, + atomVolume_->quantity(),shpFcn_->quantity(), + Ma_); + const MATRIX & Ma = Ma_[VELOCITY].quantity(); + for (int j = 0; j < nsd_; j++) { + CLON_VEC v = column(velocity,j); + kineticEnergy -= v.dot(Ma*v); + } + } + double mvv2e = lammpsInterface_->mvv2e(); + kineticEnergy *= 0.5*mvv2e; // convert to LAMMPS units + + return kineticEnergy; + } + //-------------------------------------------------------------------- + // potential/strain energy + //-------------------------------------------------------------------- + double ATC_CouplingMomentum::potential_energy(const IntegrationDomainType domain) const + { + Array mask(1); + mask(0) = VELOCITY; + FIELD_MATS energy; + feEngine_->compute_energy(mask, + fields_, + physicsModel_, + elementToMaterialMap_, + energy, + &(elementMask_->quantity()), + domain); + double potentialEnergy = energy[VELOCITY].col_sum(); + double mvv2e = lammpsInterface_->mvv2e(); + potentialEnergy *= mvv2e; // convert to LAMMPS units + return potentialEnergy-refPE_; + } + //-------------------------------------------------------------------- + // compute_vector + //-------------------------------------------------------------------- + // this is for direct output to lammps thermo + double ATC_CouplingMomentum::compute_vector(int n) + { + // output[1] = total coarse scale kinetic energy + // output[2] = total coarse scale potential energy + // output[3] = total coarse scale energy + // output[4] = fe-only coarse scale kinetic energy + // output[5] = fe-only coarse scale potential energy + + + + if (n == 0) { + return kinetic_energy(); + } + else if (n == 1) { + return potential_energy(); + } + else if (n == 2) { + return kinetic_energy()+potential_energy(); + } + else if (n == 3) { + return kinetic_energy(FE_DOMAIN); + } + else if (n == 4) { + return potential_energy(FE_DOMAIN); + } + else if (n > 4) { + double extrinsicValue = extrinsicModelManager_.compute_vector(n); + return extrinsicValue; + } + return 0.; + + } + +}; diff --git a/lib/atc/ATC_CouplingMomentum.h b/lib/atc/ATC_CouplingMomentum.h new file mode 100644 index 0000000000..7ded389f21 --- /dev/null +++ b/lib/atc/ATC_CouplingMomentum.h @@ -0,0 +1,149 @@ +#ifndef ATC_COUPLING_MOMENTUM_H +#define ATC_COUPLING_MOMENTUM_H + +/** owned field/s: DISPLACEMENT, VELOCITY */ + +// ATC headers +#include "ATC_Coupling.h" +#include "Kinetostat.h" +#include "ElasticTimeIntegrator.h" + +// Other headers +#include + +namespace ATC { + + // Forward declarations + class AtfShapeFunctionRestriction; + class AtfShapeFunctionMdProjection; + + /** + * @class ATC_CouplingMomentum + * @brief A class for atom-continuum transfers & control involving mechanical motion + * (owned field/s: DISPLACEMENT, VELOCITY) + */ + + class ATC_CouplingMomentum : public ATC_Coupling { + + public: + + // constructor + ATC_CouplingMomentum(std::string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile, + PhysicsType intrinsicModel, + ExtrinsicModelType extrinsicModel = NO_MODEL); + + // destructor + virtual ~ATC_CouplingMomentum(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** flags whether a methods reset is required */ + virtual bool reset_methods() const { + bool resetMethods = ATC_Method::reset_methods() ||atomicRegulator_->need_reset(); + _ctiIt_ = timeIntegrators_.find(VELOCITY); + if (_ctiIt_ == timeIntegrators_.end()) return resetMethods; + return resetMethods || (_ctiIt_->second)->need_reset(); + }; + + /** post time integration */ + virtual void finish(); + + /** first time, before atomic integration */ + virtual void pre_init_integrate(); + /** first time, after atomic velocity but before position integration */ + virtual void mid_init_integrate(); + /** first time, after atomic integration */ + virtual void post_init_integrate(); + + /** second time, before atomic integration */ + virtual void pre_final_integrate(); + /** second time, after atomic integration */ + virtual void post_final_integrate(); + + /** pre/post atomic force calculation in minimize */ + virtual void min_pre_force(); + virtual void min_post_force(); + + /** compute scalar for output - added energy */ + virtual double compute_scalar(void); + + /** compute vector for output */ + virtual double compute_vector(int n); + double kinetic_energy(const IntegrationDomainType domain=FULL_DOMAIN); // const; + double potential_energy(const IntegrationDomainType domain=FULL_DOMAIN) const; + + /** output routines */ + virtual void output(void); + + + /** set up atom to material identification */ + virtual void reset_atom_materials(); + + protected: + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** constructs all data which is updated with time integration, i.e. fields */ + //virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + // ghost atom routines + /** set ghost function positions based on FE state */ + virtual void set_ghost_atoms(); + /** analagous to FixATC initial_integrate applied to ghosts */ + void initial_integrate_ghost(); + /** analagous to FixATC final_integrate applied to ghosts */ + void final_integrate_ghost(); + /** forces on dynamic ghosts */ + void add_ghost_forces(); + void apply_ghost_forces(); + + /** adds resetting of any kinetostat arrays associated with local atom count */ + virtual void reset_nlocal(); + + /** compute the mass matrix components coming from MD integration */ + virtual void compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMats); + + /** operator to compute the mass matrix for the momentum equation from MD integration */ + AtfShapeFunctionRestriction * nodalAtomicMass_; + + /** operator to compute the dimensionless mass matrix from MD integration */ + AtfShapeFunctionRestriction * nodalAtomicCount_; + + /** physics specific filter initialization */ + void init_filter(); + + /** field mask for velocity integration */ + Array2D velocityMask_; + + // Add in fields for restarting + virtual void read_restart_data(string fileName_, RESTART_LIST & data); + virtual void write_restart_data(string fileName_, RESTART_LIST & data); + void pack_elastic_fields(RESTART_LIST & data); + + // boundary dynamics + BoundaryDynamicsType boundaryDynamics_; + double gamma_, mu_, kappa_; + + // data + double refPE_; + + /** mass matrix computed with atomic quadrature for KE output */ + MASS_MATS Ma_; + }; + +}; + +#endif diff --git a/lib/atc/ATC_CouplingMomentumEnergy.cpp b/lib/atc/ATC_CouplingMomentumEnergy.cpp new file mode 100644 index 0000000000..e7ed73d4b5 --- /dev/null +++ b/lib/atc/ATC_CouplingMomentumEnergy.cpp @@ -0,0 +1,1061 @@ +// ATC headers +#include "ATC_CouplingMomentumEnergy.h" +#include "KinetoThermostat.h" +#include "ATC_Error.h" +#include "PrescribedDataManager.h" + +// Other Headers +#include +#include +#include +#include +#include + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ATC_CouplingMomentumEnergy + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ATC_CouplingMomentumEnergy::ATC_CouplingMomentumEnergy(string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile, + ExtrinsicModelType extrinsicModel) + : ATC_Coupling(groupName,perAtomArray,thisFix), + nodalAtomicMass_(NULL), + nodalAtomicCount_(NULL), + nodalAtomicHeatCapacity_(NULL), + nodalAtomicKineticTemperature_(NULL), + nodalAtomicConfigurationalTemperature_(NULL), + boundaryDynamics_(PRESCRIBED), + gamma_(0),mu_(1),kappa_(1), + refPE_(0) + { + // Allocate PhysicsModel + create_physics_model(THERMO_ELASTIC, matParamFile); + + // create extrinsic physics model + if (extrinsicModel != NO_MODEL) { + extrinsicModelManager_.create_model(extrinsicModel,matParamFile); + } + + // Defaults + set_time(); + bndyIntType_ = FE_INTERPOLATION; + trackDisplacement_ = true; + + // set up field data based on physicsModel + physicsModel_->num_fields(fieldSizes_,fieldMask_); + fieldSizes_[DISPLACEMENT] = fieldSizes_[VELOCITY]; + + // set up atomic regulator + atomicRegulator_ = new KinetoThermostat(this); + + // default to not track charge + trackCharge_ = false; + + // set up physics specific time integrator and thermostat + timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP); + timeIntegrators_[TEMPERATURE] = new ThermalTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP); + + // default physics + temperatureDef_ = KINETIC; + + // output variable vector info: + // output[1] = total coarse scale mechanical kinetic energy + // output[2] = total coarse scale mechanical potential energy + // output[3] = total coarse scale mechanical energy + // output[1] = total coarse scale thermal energy + // output[2] = average temperature + scalarFlag_ = 1; + vectorFlag_ = 1; + sizeVector_ = 5; + scalarVectorFreq_ = 1; + extVector_ = 1; + if (extrinsicModel != NO_MODEL) + sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_); + + // create PE per atom ccompute + lammpsInterface_->create_compute_pe_peratom(); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ATC_CouplingMomentumEnergy::~ATC_CouplingMomentumEnergy() + { + // clear out all managed memory to avoid conflicts with dependencies on class member data + interscaleManager_.clear(); + } + + //-------------------------------------------------------- + // initialize + // sets up all the necessary data + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::initialize() + { + // clear displacement entries if requested + if (!trackDisplacement_) { + fieldSizes_.erase(DISPLACEMENT); + for (int i = 0; i < NUM_FLUX; i++) + fieldMask_(DISPLACEMENT,i) = false; + } + + // Base class initalizations + ATC_Coupling::initialize(); + + // resetting precedence: + // time integrator -> kinetostat/thermostat -> time filter + // init_filter uses fieldRateNdFiltered which comes from the time integrator, + // which is why the time integrator is initialized first + + // set the reference potential, if necessary, because the nodal energy is needed to initialize the time integrator + if (!initialized_) { + if (temperatureDef_==TOTAL) { + PerAtomQuantity * atomicReferencePotential = interscaleManager_.per_atom_quantity("AtomicReferencePotential"); + PerAtomQuantity * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + atomicReferencePotential->set_quantity() = atomicPotentialEnergy->quantity(); + } + } + + // other initializations + + if (reset_methods()) { + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->force_reset(); + } + atomicRegulator_->force_reset(); + } + if (reset_methods()) { + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->initialize(); + } + atomicRegulator_->initialize(); + extrinsicModelManager_.initialize(); + } + if (timeFilterManager_.need_reset()) // reset thermostat power + init_filter(); + timeFilterManager_.initialize(); // clears need for reset + + if (!initialized_) { + // initialize sources based on initial FE temperature + double dt = lammpsInterface_->dt(); + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(fieldMask_,fields_,atomicSources_); + + // read in field data if necessary + if (useRestart_) { + RESTART_LIST data; + read_restart_data(restartFileName_,data); + useRestart_ = false; + } + + // set consistent initial conditions, if requested + if (!timeFilterManager_.filter_dynamics()) { + if (consistentInitialization_) { + + DENS_MAT & velocity(fields_[VELOCITY].set_quantity()); + DENS_MAN * nodalAtomicVelocity(interscaleManager_.dense_matrix("NodalAtomicVelocity")); + const DENS_MAT & atomicVelocity(nodalAtomicVelocity->quantity()); + DENS_MAT & temperature(fields_[TEMPERATURE].set_quantity()); + DENS_MAN * nodalAtomicTemperature(interscaleManager_.dense_matrix("NodalAtomicTemperature")); + const DENS_MAT & atomicTemperature(nodalAtomicTemperature->quantity()); + const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); + for (int i = 0; iquantity()); + for (int i = 0; isecond)->construct_methods(); + } + atomicRegulator_->construct_methods(); + } + + //-------------------------------------------------------- + // construct_transfers + // constructs needed transfer operators + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::construct_transfers() + { + ATC_Coupling::construct_transfers(); + + // momentum of each atom + AtomicMomentum * atomicMomentum = new AtomicMomentum(this); + interscaleManager_.add_per_atom_quantity(atomicMomentum, + "AtomicMomentum"); + + // nodal momentum for RHS + AtfShapeFunctionRestriction * nodalAtomicMomentum = new AtfShapeFunctionRestriction(this, + atomicMomentum, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMomentum, + "NodalAtomicMomentum"); + + // nodal forces + FundamentalAtomQuantity * atomicForce = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE); + AtfShapeFunctionRestriction * nodalAtomicForce = new AtfShapeFunctionRestriction(this, + atomicForce, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicForce, + "NodalAtomicForce"); + + // nodal velocity derived only from atoms + AtfShapeFunctionMdProjection * nodalAtomicVelocity = new AtfShapeFunctionMdProjection(this, + nodalAtomicMomentum, + VELOCITY); + interscaleManager_.add_dense_matrix(nodalAtomicVelocity, + "NodalAtomicVelocity"); + + if (trackDisplacement_) { + // mass-weighted (center-of-mass) displacement of each atom + AtomicMassWeightedDisplacement * atomicMassWeightedDisplacement; + if (needXrefProcessorGhosts_ || groupbitGhost_) { // explicit construction on internal group + PerAtomQuantity * atomReferencePositions = interscaleManager_.per_atom_quantity("AtomicInternalReferencePositions"); + atomicMassWeightedDisplacement = new AtomicMassWeightedDisplacement(this,atomPositions_, + atomMasses_, + atomReferencePositions, + INTERNAL); + } + else + atomicMassWeightedDisplacement = new AtomicMassWeightedDisplacement(this); + interscaleManager_.add_per_atom_quantity(atomicMassWeightedDisplacement, + "AtomicMassWeightedDisplacement"); + + // nodal (RHS) mass-weighted displacement + AtfShapeFunctionRestriction * nodalAtomicMassWeightedDisplacement = new AtfShapeFunctionRestriction(this, + atomicMassWeightedDisplacement, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMassWeightedDisplacement, + "NodalAtomicMassWeightedDisplacement"); + + // nodal displacement derived only from atoms + AtfShapeFunctionMdProjection * nodalAtomicDisplacement = new AtfShapeFunctionMdProjection(this, + nodalAtomicMassWeightedDisplacement, + VELOCITY); + interscaleManager_.add_dense_matrix(nodalAtomicDisplacement, + "NodalAtomicDisplacement"); + } + + // always need kinetic energy + FtaShapeFunctionProlongation * atomicMeanVelocity = new FtaShapeFunctionProlongation(this,&fields_[VELOCITY],shpFcn_); + interscaleManager_.add_per_atom_quantity(atomicMeanVelocity, + "AtomicMeanVelocity"); + AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceFluctuatingKineticEnergy(this); + AtomicEnergyForTemperature * atomEnergyForTemperature = NULL; + + // Appropriate per-atom quantity based on desired temperature definition + if (temperatureDef_==KINETIC) { + atomEnergyForTemperature = atomicTwiceKineticEnergy; + } + else if (temperatureDef_==TOTAL) { + if (timeIntegrators_[TEMPERATURE]->time_integration_type() != TimeIntegrator::FRACTIONAL_STEP) + throw ATC_Error("ATC_CouplingMomentumEnergy:construct_transfers() on the fractional step time integrator can be used with non-kinetic defitions of the temperature"); + + // kinetic energy + interscaleManager_.add_per_atom_quantity(atomicTwiceKineticEnergy, + "AtomicTwiceKineticEnergy"); + + // atomic potential energy + ComputedAtomQuantity * atomicPotentialEnergy = new ComputedAtomQuantity(this,lammpsInterface_->compute_pe_name(), + 1./(lammpsInterface_->mvv2e())); + interscaleManager_.add_per_atom_quantity(atomicPotentialEnergy, + "AtomicPotentialEnergy"); + + // reference potential energy + AtcAtomQuantity * atomicReferencePotential; + if (!initialized_) { + atomicReferencePotential = new AtcAtomQuantity(this); + interscaleManager_.add_per_atom_quantity(atomicReferencePotential, + "AtomicReferencePotential"); + atomicReferencePotential->set_memory_type(PERSISTENT); + } + else { + atomicReferencePotential = static_cast * >(interscaleManager_.per_atom_quantity("AtomicReferencePotential")); + } + AtfShapeFunctionRestriction * nodalAtomicReferencePotential = new AtfShapeFunctionRestriction(this, + atomicReferencePotential, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicReferencePotential, + "NodalAtomicReferencePotential"); + + // fluctuating potential energy + AtomicEnergyForTemperature * atomicFluctuatingPotentialEnergy = new FluctuatingPotentialEnergy(this, + atomicPotentialEnergy, + atomicReferencePotential); + interscaleManager_.add_per_atom_quantity(atomicFluctuatingPotentialEnergy, + "AtomicFluctuatingPotentialEnergy"); + + // atomic total energy + atomEnergyForTemperature = new MixedKePeEnergy(this,1,1); + + // kinetic temperature measure for post-processing + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicTwiceKineticEnergy = new AtfShapeFunctionRestriction(this, + atomicTwiceKineticEnergy, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicTwiceKineticEnergy, + "NodalAtomicTwiceKineticEnergy"); + nodalAtomicKineticTemperature_ = new AtfShapeFunctionMdProjection(this, + nodalAtomicTwiceKineticEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicKineticTemperature_, + "NodalAtomicKineticTemperature"); + + // potential temperature measure for post-processing (must multiply by 2 for configurational temperature + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicFluctuatingPotentialEnergy = new AtfShapeFunctionRestriction(this, + atomicFluctuatingPotentialEnergy, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicFluctuatingPotentialEnergy, + "NodalAtomicFluctuatingPotentialEnergy"); + nodalAtomicConfigurationalTemperature_ = new AtfShapeFunctionMdProjection(this, + nodalAtomicFluctuatingPotentialEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicConfigurationalTemperature_, + "NodalAtomicConfigurationalTemperature"); + } + + // register the per-atom quantity for the temperature definition + interscaleManager_.add_per_atom_quantity(atomEnergyForTemperature, + "AtomicEnergyForTemperature"); + + // nodal restriction of the atomic energy quantity for the temperature definition + AtfShapeFunctionRestriction * nodalAtomicEnergy = new AtfShapeFunctionRestriction(this, + atomEnergyForTemperature, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicEnergy, + "NodalAtomicEnergy"); + + // nodal atomic temperature field + + AtfShapeFunctionMdProjection * nodalAtomicTemperature = new AtfShapeFunctionMdProjection(this, + nodalAtomicEnergy, + TEMPERATURE); + interscaleManager_.add_dense_matrix(nodalAtomicTemperature, + "NodalAtomicTemperature"); + + if (!useFeMdMassMatrix_) { + // atomic momentum mass matrix + FundamentalAtomQuantity * atomicMass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); + nodalAtomicMass_ = new AtfShapeFunctionRestriction(this, + atomicMass, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicMass_, + "AtomicMomentumMassMat"); + + // atomic dimensionless mass matrix + ConstantQuantity * atomicOnes = new ConstantQuantity(this,1); + interscaleManager_.add_per_atom_quantity(atomicOnes,"AtomicOnes"); + nodalAtomicCount_ = new AtfShapeFunctionRestriction(this, + atomicOnes, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicCount_, + "AtomicDimensionlessMassMat"); + + // classical thermodynamic heat capacity of the atoms + HeatCapacity * heatCapacity = new HeatCapacity(this); + interscaleManager_.add_per_atom_quantity(heatCapacity, + "AtomicHeatCapacity"); + + // atomic thermal mass matrix + nodalAtomicHeatCapacity_ = new AtfShapeFunctionRestriction(this, + heatCapacity, + shpFcn_); + interscaleManager_.add_dense_matrix(nodalAtomicHeatCapacity_, + "NodalAtomicHeatCapacity"); + } + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->construct_transfers(); + } + atomicRegulator_->construct_transfers(); + } + + //--------------------------------------------------------- + // init_filter + // sets up the time filtering operations in all objects + //--------------------------------------------------------- + void ATC_CouplingMomentumEnergy::init_filter() + { + if (timeIntegrators_[TEMPERATURE]->time_integration_type() != TimeIntegrator::FRACTIONAL_STEP) { + throw ATC_Error("ATC_CouplingMomentumEnergy::initialize - method only valid with fractional step time integration"); + } + + + ATC_Coupling::init_filter(); + + + + + if (timeFilterManager_.end_equilibrate() && equilibriumStart_) { + if (atomicRegulator_->coupling_mode(VELOCITY)==AtomicRegulator::FLUX || atomicRegulator_->coupling_mode(VELOCITY)==AtomicRegulator::GHOST_FLUX) + // nothing needed in other cases since kinetostat force is balanced by boundary flux in FE equations + atomicRegulator_->reset_lambda_contribution(nodalAtomicFieldsRoc_[VELOCITY].quantity(),VELOCITY); + + DENS_MAT powerMat(-1.*(nodalAtomicFields_[TEMPERATURE].quantity())); + atomicRegulator_->reset_lambda_contribution(powerMat,TEMPERATURE); + } + } + + //--------------------------------------------------------- + // compute_md_mass_matrix + // compute the mass matrix arising from only atomistic + // quadrature and contributions as a summation + //--------------------------------------------------------- + void ATC_CouplingMomentumEnergy::compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMat) + { + + + if (thisField == DISPLACEMENT || thisField == VELOCITY) { + massMat.reset(nodalAtomicMass_->quantity()); + } + else if (thisField == MASS_DENSITY) { // dimensionless mass matrix + massMat.reset(nodalAtomicCount_->quantity()); + } + else if (thisField == TEMPERATURE) { + massMat.reset(nodalAtomicHeatCapacity_->quantity()); + } + } + + //-------------------------------------------------------- + // finish + // final clean up after a run + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::finish() + { + // base class + ATC_Coupling::finish(); + + atomicRegulator_->finish(); + } + + //-------------------------------------------------------- + // modify + // parses inputs and modifies state of the filter + //-------------------------------------------------------- + bool ATC_CouplingMomentumEnergy::modify(int narg, char **arg) + { + bool foundMatch = false; + int argIndex = 0; + return foundMatch; + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::pack_quantity_fields(RESTART_LIST & data) + { + atomicRegulator_->pack_fields(data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::write_restart_data(string fileName, RESTART_LIST & data) + { + pack_quantity_fields(data); + ATC_Method::write_restart_data(fileName,data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::read_restart_data(string fileName, RESTART_LIST & data) + { + pack_quantity_fields(data); + ATC_Method::read_restart_data(fileName,data); + } + + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::reset_nlocal() + { + ATC_Coupling::reset_nlocal(); + atomicRegulator_->reset_nlocal(); + } + + //-------------------------------------------------- + // reset_atom_materials + // update the atom materials map + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::reset_atom_materials() + { + ATC_Coupling::reset_atom_materials(); + atomicRegulator_->reset_atom_materials(elementToMaterialMap_, + atomElement_); + } + + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::pre_init_integrate() + { + ATC_Coupling::pre_init_integrate(); + double dt = lammpsInterface_->dt(); + + // Perform any initialization, no actual integration + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate1(dt); + } + + // Apply controllers to atom velocities, if needed + atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep()); + + // Predict nodal temperatures and time derivatives based on FE data + // predict nodal velocities + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_initial_integrate2(dt); + } + extrinsicModelManager_.pre_init_integrate(); + } + + //-------------------------------------------------------- + // mid_init_integrate + // time integration between the velocity update and + // the position lammps update of Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::mid_init_integrate() + { + // CONTINUOUS VELOCITY UPDATE + + ATC_Coupling::mid_init_integrate(); + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1/2 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->mid_initial_integrate1(dt); + } + atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep()); + extrinsicModelManager_.mid_init_integrate(); + } + + //-------------------------------------------------------- + // post_init_integrate + // time integration after the lammps atomic updates of + // Verlet step 1 + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::post_init_integrate() + { + + // CONTINUOUS DISPLACEMENT UPDATE + + double dt = lammpsInterface_->dt(); + + // Compute nodal velocity at n+1 + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_initial_integrate1(dt); + } + + // Update kinetostat quantities if displacement is being regulated + atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep()); + + // Update extrisic model + extrinsicModelManager_.post_init_integrate(); + + // fixed values, non-group bcs handled through FE + set_fixed_nodes(); + + + // enforce atomic boundary conditions + if (boundaryDynamics_==PRESCRIBED) set_ghost_atoms(); + else if (boundaryDynamics_==DAMPED_HARMONIC) initial_integrate_ghost(); + else if (boundaryDynamics_==COUPLED) initial_integrate_ghost(); + + // update time by a half dt + update_time(0.5); + + ATC_Coupling::post_init_integrate(); + } + + //-------------------------------------------------------- + // pre_final_integrate + // integration before the second stage lammps atomic + // update of Verlet step 2 + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::pre_final_integrate() + { + ATC_Coupling::pre_final_integrate(); + + if (boundaryDynamics_==DAMPED_HARMONIC) { + apply_ghost_forces(); + final_integrate_ghost(); + } + else if (boundaryDynamics_==COUPLED) { + add_ghost_forces(); + final_integrate_ghost(); + } + } + + //-------------------------------------------------- + void ATC_CouplingMomentumEnergy::post_final_integrate() + { + // CONTINUOUS VELOCITY RHS UPDATE + + double dt = lammpsInterface_->dt(); + + // update of atomic contributions for fractional step methods + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->pre_final_integrate1(dt); + } + + // Set sources + prescribedDataMgr_->set_sources(time()+0.5*dt,sources_); + extrinsicModelManager_.pre_final_integrate(); + if (timeIntegrators_[TEMPERATURE]->has_final_predictor() || timeIntegrators_[MOMENTUM]->has_final_predictor()) { + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(velocityMask_,fields_,atomicSources_); + } + atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); + + // Compute atom-integrated rhs + // parallel communication happens within FE_Engine + compute_rhs_vector(velocityMask_,fields_,rhs_,FE_DOMAIN); + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->add_to_rhs(); + } + atomicRegulator_->add_to_rhs(rhs_); + + // Compute and add atomic contributions to FE equations + 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(); + + // corrector step extrinsic model + extrinsicModelManager_.post_final_integrate(); + if (timeIntegrators_[TEMPERATURE]->has_final_corrector() || timeIntegrators_[MOMENTUM]->has_final_corrector()) { + // set state-based sources + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + atomicRegulator_->compute_boundary_flux(fields_); + compute_atomic_sources(velocityMask_,fields_,atomicSources_); + } + + // Finish update of FE velocity + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate2(dt); + } + + // apply corrector phase of thermostat + atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep()); + + // final phase of time integration + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_final_integrate3(dt); + } + + // Fix nodes, non-group bcs applied through FE + set_fixed_nodes(); + + update_time(0.5); + + output(); + ATC_Coupling::post_final_integrate(); // adds next step to computes + } + + //-------------------------------------------------------------------- + // compute_scalar : added energy + //-------------------------------------------------------------------- + double ATC_CouplingMomentumEnergy::compute_scalar(void) + { + double energy = 0.0; + energy += extrinsicModelManager_.compute_scalar(); + return energy; + } + + //-------------------------------------------------------------------- + // total kinetic energy + //-------------------------------------------------------------------- + double ATC_CouplingMomentumEnergy::kinetic_energy(void) + { + const MATRIX & M = massMats_[VELOCITY].quantity(); + + const DENS_MAT & velocity(fields_[VELOCITY].quantity()); + double mvv2e = lammpsInterface_->mvv2e(); + double kineticEnergy = 0; + DENS_VEC velocitySquared(nNodes_); + for (int i = 0; i < nNodes_; i++) + for (int j = 0; j < nsd_; j++) + velocitySquared(i) += velocity(i,j)*velocity(i,j); + kineticEnergy = (M*velocitySquared).sum(); + kineticEnergy *= mvv2e; // convert to LAMMPS units + return kineticEnergy; + } + //-------------------------------------------------------------------- + // total potential energy + //-------------------------------------------------------------------- + double ATC_CouplingMomentumEnergy::potential_energy(void) + { + Array mask(1); + mask(0) = VELOCITY; + FIELD_MATS energy; + feEngine_->compute_energy(mask, + fields_, + physicsModel_, + elementToMaterialMap_, + energy, + &(elementMask_->quantity())); + double potentialEnergy = energy[VELOCITY].col_sum(); + double mvv2e = lammpsInterface_->mvv2e(); + potentialEnergy *= mvv2e; // convert to LAMMPS units + return potentialEnergy-refPE_; + } + + //-------------------------------------------------------------------- + // compute_vector + //-------------------------------------------------------------------- + // this is for direct output to lammps thermo + double ATC_CouplingMomentumEnergy::compute_vector(int n) + { + // output[1] = total coarse scale kinetic energy + // output[2] = total coarse scale potential energy + // output[3] = total coarse scale energy + // output[4] = total coarse scale thermal energy + // output[5] = average temperature + + double mvv2e = lammpsInterface_->mvv2e(); // convert to lammps energy units + + if (n == 0) { + return kinetic_energy(); + } + else if (n == 1) { + return potential_energy(); + } + else if (n == 2) { + return kinetic_energy()+potential_energy(); + } + else if (n == 4) { + Array mask(1); + FIELD_MATS energy; + mask(0) = TEMPERATURE; + + feEngine_->compute_energy(mask, + fields_, + physicsModel_, + elementToMaterialMap_, + energy, + &(elementMask_->quantity())); + + double phononEnergy = mvv2e * energy[TEMPERATURE].col_sum(); + return phononEnergy; + } + else if (n == 5) { + double aveT = (fields_[TEMPERATURE].quantity()).col_sum()/nNodes_; + return aveT; + } + else if (n > 5) { + double extrinsicValue = extrinsicModelManager_.compute_vector(n); + return extrinsicValue; + } + + return 0.; + + } + + //-------------------------------------------------------------------- + // output + //-------------------------------------------------------------------- + void ATC_CouplingMomentumEnergy::output() + { + if (output_now()) { + feEngine_->departition_mesh(); + // avoid possible mpi calls + if (nodalAtomicKineticTemperature_) + _keTemp_ = nodalAtomicKineticTemperature_->quantity(); + if (nodalAtomicConfigurationalTemperature_) + _peTemp_ = nodalAtomicConfigurationalTemperature_->quantity(); + + OUTPUT_LIST outputData; + + // base class output + ATC_Method::output(); + + // push atc fields time integrator modifies into output arrays + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->post_process(); + } + + // auxilliary data + + for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { + (_tiIt_->second)->output(outputData); + } + atomicRegulator_->output(outputData); + extrinsicModelManager_.output(outputData); + + DENS_MAT & velocity(nodalAtomicFields_[VELOCITY].set_quantity()); + DENS_MAT & rhs(rhs_[VELOCITY].set_quantity()); + DENS_MAT & temperature(nodalAtomicFields_[TEMPERATURE].set_quantity()); + DENS_MAT & dotTemperature(dot_fields_[TEMPERATURE].set_quantity()); + DENS_MAT & ddotTemperature(ddot_fields_[TEMPERATURE].set_quantity()); + DENS_MAT & rocTemperature(nodalAtomicFieldsRoc_[TEMPERATURE].set_quantity()); + DENS_MAT & fePower(rhs_[TEMPERATURE].set_quantity()); + if (lammpsInterface_->rank_zero()) { + // global data + double T_mean = (fields_[TEMPERATURE].quantity()).col_sum(0)/nNodes_; + feEngine_->add_global("temperature_mean", T_mean); + double T_stddev = (fields_[TEMPERATURE].quantity()).col_stdev(0); + feEngine_->add_global("temperature_std_dev", T_stddev); + double Ta_mean = (nodalAtomicFields_[TEMPERATURE].quantity()).col_sum(0)/nNodes_; + feEngine_->add_global("atomic_temperature_mean", Ta_mean); + double Ta_stddev = (nodalAtomicFields_[TEMPERATURE].quantity()).col_stdev(0); + feEngine_->add_global("atomic_temperature_std_dev", Ta_stddev); + + // different temperature measures, if appropriate + if (nodalAtomicKineticTemperature_) + outputData["kinetic_temperature"] = & _keTemp_; + + if (nodalAtomicConfigurationalTemperature_) { + _peTemp_ *= 2; // account for full temperature + outputData["configurational_temperature"] = & _peTemp_; + } + + // mesh data + outputData["NodalAtomicVelocity"] = &velocity; + outputData["FE_Force"] = &rhs; + if (trackDisplacement_) + outputData["NodalAtomicDisplacement"] = & nodalAtomicFields_[DISPLACEMENT].set_quantity(); + outputData["NodalAtomicTemperature"] = &temperature; + outputData["dot_temperature"] = &dotTemperature; + outputData["ddot_temperature"] = &ddotTemperature; + outputData["NodalAtomicPower"] = &rocTemperature; + outputData["fePower"] = &fePower; + + feEngine_->write_data(output_index(), fields_, & outputData); + } + + // hence propagation is performed on proc 0 but not others. + // The real fix is to have const data in the output list + // force optional variables to reset to keep in sync + if (trackDisplacement_) { + nodalAtomicFields_[DISPLACEMENT].force_reset(); + } + fields_[VELOCITY].propagate_reset(); + + feEngine_->partition_mesh(); + } + } + + //-------------------------------------------------------- + // set_ghost_atoms + // sets ghost atom positions to finite element + // displacements based on shape functions + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::set_ghost_atoms() + { + // set atomic displacements based on FE displacements + double ** x = lammpsInterface_->xatom(); + // prolong + DenseMatrix ghostAtomData(nLocalGhost_,nsd_); + if (nLocalGhost_>0) + ghostAtomData = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + + for (int i = 0; i < nLocalGhost_; ++i) + for (int j = 0; j < nsd_; ++j) + x[ghostToAtom_(i)][j] = ghostAtomData(i,j)+xref_[ghostToAtom_(i)][j]; + + + } + + //-------------------------------------------------------- + // add_ghost_forces + // add forces to dynamic ghosts + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::add_ghost_forces() + { + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + + // add forces + DENS_MAT coarseDisp(nLocalGhost_,nsd_); + DENS_MAT coarseVel(nLocalGhost_,nsd_); + if (nLocalGhost_>0) { + coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity()); + } + // dynamics one-way coupled to real atoms in a well tied to coarse scale + for (int i = 0; i < nLocalGhost_; ++i) { + for (int j = 0; j < nsd_; ++j) { + double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j]; + double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j]; + f[ghostToAtom_(i)][j] += mu_*du + gamma_*dv; + + } + } + } + + void ATC_CouplingMomentumEnergy::apply_ghost_forces() + { + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + + // add forces + DENS_MAT coarseDisp(nLocalGhost_,nsd_); + DENS_MAT coarseVel(nLocalGhost_,nsd_); + if (nLocalGhost_>0) { + coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity()); + coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity()); + } + // dynamics one-way coupled to real atoms in a well tied to coarse scale + for (int i = 0; i < nLocalGhost_; ++i) { + for (int j = 0; j < nsd_; ++j) { + double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j]; + double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j]; + f[ghostToAtom_(i)][j] = mu_*du + gamma_*dv; + + } + } + } + + //-------------------------------------------------------- + // initial_integrate_ghost + // does the first step of the Verlet integration for + // ghost atoms, to be used with non-reflecting BCs + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::initial_integrate_ghost() + { + double dtfm; + + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + int *type = lammpsInterface_->atom_type(); + const int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + double dtv = lammpsInterface_->dt(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + double *mass = lammpsInterface_->atom_mass(); + + if (mass) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + + } else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } + + } + + //-------------------------------------------------------- + // final_integrate_ghost + // does the second step of the Verlet integration for + // ghost atoms, to be used with non-reflecting BCs + //-------------------------------------------------------- + void ATC_CouplingMomentumEnergy::final_integrate_ghost() + { + double dtfm; + + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + int *type = lammpsInterface_->atom_type(); + const int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + + double *mass = lammpsInterface_->atom_mass(); + if (mass) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + + } else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbitGhost_) { + dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + } + + } +}; diff --git a/lib/atc/ATC_CouplingMomentumEnergy.h b/lib/atc/ATC_CouplingMomentumEnergy.h new file mode 100644 index 0000000000..dd107295d3 --- /dev/null +++ b/lib/atc/ATC_CouplingMomentumEnergy.h @@ -0,0 +1,156 @@ +#ifndef ATC_COUPLING_MOMENTUM_ENERGY_H +#define ATC_COUPLING_MOMENTUM_ENERGY_H + +// ATC headers +#include "ATC_Coupling.h" +#include "Kinetostat.h" +#include "Thermostat.h" +#include "ElasticTimeIntegrator.h" +#include "ThermalTimeIntegrator.h" + +// Other headers +#include + +namespace ATC { + + class AtfShapeFunctionRestriction; + class AtfShapeFunctionMdProjection; + + /** + * @class ATC_CouplingMomentumEnergy + * @brief A class for atom-continuum transfers & control involving momentum and heat transport + * (owned field/s: DISPLACEMENT, VELOCITY, TEMPERATURE) + */ + + class ATC_CouplingMomentumEnergy : public ATC_Coupling { + + public: + + // constructor + ATC_CouplingMomentumEnergy(std::string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile, + ExtrinsicModelType extrinsic = NO_MODEL); + + // destructor + virtual ~ATC_CouplingMomentumEnergy(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** flags whether a methods reset is required */ + + virtual bool reset_methods() const { + bool resetMethods = ATC_Method::reset_methods() || atomicRegulator_->need_reset(); + for (_ctiIt_ = timeIntegrators_.begin(); _ctiIt_ != timeIntegrators_.end(); ++_ctiIt_) { + resetMethods |= (_ctiIt_->second)->need_reset(); + } + return resetMethods; + }; + + /** post time integration */ + virtual void finish(); + + /** first time, before atomic integration */ + virtual void pre_init_integrate(); + /** first time, after atomic velocity but before position integration */ + virtual void mid_init_integrate(); + /** first time, after atomic integration */ + virtual void post_init_integrate(); + + /** second time, before atomic integration */ + virtual void pre_final_integrate(); + /** second time, after atomic integration */ + virtual void post_final_integrate(); + + /** compute scalar for output - added energy */ + virtual double compute_scalar(void); + + /** compute vector for output */ + virtual double compute_vector(int n); + double kinetic_energy(); + double potential_energy(); + + /** output */ + virtual void output(); + + + /** set up atom to material identification */ + virtual void reset_atom_materials(); + + protected: + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** constructs all data which is updated with time integration, i.e. fields */ + //virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + /** sets the position/velocity of the ghost atoms */ + virtual void set_ghost_atoms(); + /** analagous to FixATC initial_integrate applied to ghosts */ + void initial_integrate_ghost(); + /** analagous to FixATC final_integrate applied to ghosts */ + void final_integrate_ghost(); + /** forces on dynamic ghosts */ + void add_ghost_forces(); + void apply_ghost_forces(); + + /** adds resetting of any kinetostat/thermostat arrays associated with local atom count */ + virtual void reset_nlocal(); + + /** compute the mass matrix components coming from MD integration */ + virtual void compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMats); + + /** operator to compute the mass matrix for the momentum equation from MD integration */ + AtfShapeFunctionRestriction * nodalAtomicMass_; + + /** operator to compute the dimensionless mass matrix from MD integration */ + AtfShapeFunctionRestriction * nodalAtomicCount_; + + /** operator to compute mass matrix from MD */ + AtfShapeFunctionRestriction * nodalAtomicHeatCapacity_; + + /** physics specific filter initialization */ + void init_filter(); + + /** field mask for velocity integration */ + Array2D velocityMask_; + + /** field mask for temperature integration */ + Array2D temperatureMask_; + + /** kinetic temperature for post-processing */ + AtfShapeFunctionMdProjection * nodalAtomicKineticTemperature_; + + /** configurational temperature for post-processing */ + AtfShapeFunctionMdProjection * nodalAtomicConfigurationalTemperature_; + + /** workspace matrices for output */ + DENS_MAT _keTemp_, _peTemp_; + + // Add in fields for restarting + virtual void read_restart_data(string fileName_, RESTART_LIST & data); + virtual void write_restart_data(string fileName_, RESTART_LIST & data); + void pack_quantity_fields(RESTART_LIST & data); + + + // boundary dynamics + BoundaryDynamicsType boundaryDynamics_; + double gamma_, mu_, kappa_; + + // data + double refPE_; + }; + +}; +#endif diff --git a/lib/atc/ATC_Error.h b/lib/atc/ATC_Error.h new file mode 100644 index 0000000000..08a9bf0ec2 --- /dev/null +++ b/lib/atc/ATC_Error.h @@ -0,0 +1,56 @@ +// ATC_Error : a base class for atom-continuum errors + +#ifndef ATC_ERROR +#define ATC_ERROR + +#include + +// the following two convert __LINE__ to a string +#define STRING2(x) #x +#define STRING(x) STRING2(x) +// prints file and line number for error messages +#define ERROR(x) __FILE__":"STRING(__LINE__)" "x +//#define FILELINE __FILE__+to_string(__LINE__) +#define FILELINE __FILE__ + +#define ERROR_FOR_BACKTRACE +#define HACK(l,m) + + + + + + +namespace ATC { + /** + * @class ATC_Error + * @brief Base class for throwing run-time errors with descriptions + */ + +class ATC_Error { + + public: + // constructor + ATC_Error(std::string errorDescription) + { + errorDescription_ = "ERROR: " + errorDescription; + ERROR_FOR_BACKTRACE + }; + + ATC_Error(std::string location, std::string errorDescription) + { + errorDescription_ = "ERROR: " + location + ": "+ errorDescription; + ERROR_FOR_BACKTRACE + }; + + std::string error_description() { + return errorDescription_; + }; + + private: + // string describing the type of error + std::string errorDescription_; +}; + +} +#endif diff --git a/lib/atc/ATC_Method.cpp b/lib/atc/ATC_Method.cpp new file mode 100644 index 0000000000..7adc2ec7cd --- /dev/null +++ b/lib/atc/ATC_Method.cpp @@ -0,0 +1,2194 @@ +// ATC headers +#include "ATC_Method.h" +#include "LammpsInterface.h" +#include "FE_Engine.h" +#include "Array.h" +#include "Array2D.h" +#include "ATC_Error.h" +#include "Function.h" +#include "PrescribedDataManager.h" +#include "TimeIntegrator.h" +#include "PhysicsModel.h" +#include "PerAtomQuantityLibrary.h" +#include "TransferLibrary.h" +#include "KernelFunction.h" +#include "Utility.h" + +#include +#include + +using ATC_Utility::sgn; + +namespace ATC { + + ATC_Method::ATC_Method(string groupName, double ** & perAtomArray, LAMMPS_NS::Fix * thisFix) : + nodalAtomicVolume_(NULL), + needReset_(true), + lammpsInterface_(LammpsInterface::instance()), + interscaleManager_(this), + timeFilterManager_(this), + feEngine_(NULL), + initialized_(false), + meshDataInitialized_(false), + localStep_(0), + sizeComm_(8), // 3 positions + 1 material id * 2 for output + atomCoarseGrainingPositions_(NULL), + atomGhostCoarseGrainingPositions_(NULL), + atomProcGhostCoarseGrainingPositions_(NULL), + atomReferencePositions_(NULL), + nsd_(lammpsInterface_->dimension()), +#ifdef EXTENDED_ERROR_CHECKING + atomSwitch_(false), +#endif + xref_(NULL), + readXref_(false), + needXrefProcessorGhosts_(false), + trackDisplacement_(false), + needsAtomToElementMap_(true), + atomElement_(NULL), + atomGhostElement_(NULL), + atomMasses_(NULL), + atomPositions_(NULL), + atomVelocities_(NULL), + atomForces_(NULL), + parallelConsistency_(true), + outputNow_(false), + outputTime_(true), + outputFrequency_(0), + sampleFrequency_(0), + sampleCounter_(0), + peScale_(1./(lammpsInterface_->mvv2e())), + keScale_(1.), + scalarFlag_(0), + vectorFlag_(0), + sizeVector_(0), + scalarVectorFreq_(0), + sizePerAtomCols_(4), + perAtomOutput_(NULL), + perAtomArray_(perAtomArray), + extScalar_(0), + extVector_(0), + extList_(NULL), + thermoEnergyFlag_(0), + atomVolume_(NULL), + atomicWeightsWriteFlag_(false), + atomicWeightsWriteFrequency_(0), + atomWeightType_(LATTICE), + domainDecomposition_(REPLICATED_MEMORY), + groupbit_(0), + groupbitGhost_(0), + needProcGhost_(false), + groupTag_(groupName), + nLocal_(0), + nLocalTotal_(0), + nLocalGhost_(0), + nInternal_(0), + nGhost_(0), + atomToElementMapType_(LAGRANGIAN), + atomToElementMapFrequency_(0), + regionID_(-1), + mdMassNormalization_(false), + kernelBased_(false), + kernelOnTheFly_(false), + kernelFunction_(NULL), + bondOnTheFly_(false), + accumulant_(NULL), + accumulantMol_(NULL), + accumulantMolGrad_(NULL), + accumulantWeights_(NULL), + accumulantInverseVolumes_(&invNodeVolumes_), + useRestart_(false), + hasRefPE_(false), + setRefPE_(false), + setRefPEvalue_(false), + refPEvalue_(0.), + readRefPE_(false), + simTime_(0.0), + stepCounter_(0) + { + lammpsInterface_->set_fix_pointer(thisFix); + interscaleManager_.set_lammps_data_prefix(); + grow_arrays(lammpsInterface_->nmax()); + feEngine_ = new FE_Engine(lammpsInterface_->world()); + + + lammpsInterface_->create_compute_pe_peratom(); + } + + ATC_Method::~ATC_Method() + { + lammpsInterface_->destroy_2d_double_array(xref_); + lammpsInterface_->destroy_2d_double_array(perAtomOutput_); + if (feEngine_) delete feEngine_; + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_Method::pack_fields(RESTART_LIST & data) + { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + string fieldName = field_to_string(thisField); + string matrixName; + // copy all fields from ATC_Method.h + matrixName = "fields_" + fieldName; + data[matrixName] = & fields_[thisField].set_quantity(); + matrixName = "dot_fields_" + fieldName; + data[matrixName] = & dot_fields_[thisField].set_quantity(); + matrixName = "ddot_fields_" + fieldName; + data[matrixName] = & ddot_fields_[thisField].set_quantity(); + matrixName = "dddot_fields_" + fieldName; + data[matrixName] = & dddot_fields_[thisField].set_quantity(); + matrixName = "NodalAtomicFieldsRoc_" + fieldName; + data[matrixName] = & nodalAtomicFieldsRoc_[thisField].set_quantity(); + } + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_Method::write_restart_data(string fileName, RESTART_LIST & data) + { + pack_fields(data); + feEngine_->write_restart_file(fileName,&data); + } + + //-------------------------------------------------- + // read_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_Method::read_restart_data(string fileName, RESTART_LIST & data) + { + pack_fields(data); + feEngine_->read_restart_file(fileName,&data); + } + + //-------------------------------------------------- + // Interactions with LAMMPS fix commands + // parse input command and pass on to finite element engine + // or physics specific transfers if necessary + // revert to physics-specific transfer if no command matches input + // first keyword is unique to particular class + // base class keyword matching must apply to ALL physics + // order: derived, base, owned objects + //-------------------------------------------------- + bool ATC_Method::modify(int narg, char **arg) + { + int argIdx=0; + bool match = false; + + // gateways to other modules e.g. extrinsic, control, mesh + // pass off to fe engine + if (strcmp(arg[argIdx],"mesh")==0) { + match = feEngine_->modify(narg, arg); + if (feEngine_->has_mesh() && !meshDataInitialized_) + this->initialize_mesh_data(); + } + // pass off to time filter + else if (strcmp(arg[argIdx],"filter")==0) { + argIdx++; + match = timeFilterManager_.modify(narg-argIdx,&arg[argIdx]); + + // consistentInitialization_ = false; + } + // pass off to kernel function manager + else if (strcmp(arg[argIdx],"kernel")==0) { + argIdx++; + + if (kernelFunction_) { + //delete kernelFunction_; + //resetKernelFunction_ = true; + } + kernelFunction_ = KernelFunctionMgr::instance()->function(&arg[argIdx],narg-argIdx); + if (kernelFunction_) match = true; + else ATC_Error("no matching kernel found"); + feEngine_->set_kernel(kernelFunction_); + + accumulantMol_=&kernelAccumulantMol_; // KKM add + accumulantMolGrad_=&kernelAccumulantMolGrad_; // KKM add + } + + // parsing handled here + else { + if (strcmp(arg[argIdx],"parallel_consistency")==0) { + argIdx++; + //if (!kernelFunction_) { throw ATC_Error("on_the_fly requires a kernel function"); } + if (strcmp(arg[argIdx],"off")==0) parallelConsistency_ = false; + else parallelConsistency_ = true; + match = true; + } + /*! \page man_hardy_on_the_fly fix_modify AtC on_the_fly + \section syntax + fix_modify AtC on_the_fly \n - bond | kernel (keyword) = specifies on-the-fly calculation of bond or +kernel matrix elements \n + - on | off (keyword) = activate or discontinue on-the-fly mode \n + \section examples + fix_modify AtC on_the_fly bond on \n fix_modify AtC on_the_fly kernel \n + fix_modify AtC on_the_fly kernel off \n + \section description + Overrides normal mode of pre-calculating and storing bond pair-to-node a +nd + kernel atom-to-node matrices. If activated, will calculate elements of t +hese + matrices during repeated calls of field computations (i.e. "on-the-fly") and not store them for + future use. \n on flag is optional - if omitted, on_the_fly will be activated for the s +pecified + matrix. Can be deactivated using off flag. \n + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + By default, on-the-fly calculation is not active (i.e. off). However, co +de does a memory allocation + check to determine if it can store all needed bond and kernel matrix ele +ments. If this allocation fails, on-the-fly is activated. \n + */ + + else if (strcmp(arg[argIdx],"on_the_fly")==0) { + argIdx++; + //if (!kernelFunction_) { throw ATC_Error("on_the_fly requires a kernel function"); } + if (strcmp(arg[argIdx],"bond")==0) { + argIdx++; + bondOnTheFly_ = true; + if (narg > argIdx && strcmp(arg[argIdx],"off")==0) bondOnTheFly_ = false; + } + else if (strcmp(arg[argIdx],"kernel")==0) { + argIdx++; + kernelOnTheFly_ = true; + if (narg > argIdx && strcmp(arg[argIdx],"off")==0) kernelOnTheFly_ = false; + } + else { throw ATC_Error("unsupported on_the_fly type"); } + match = true; + } + + /*! \page man_output fix_modify AtC output + \section syntax + fix_modify AtC output + [text | full_text | binary | vector_components | tensor_components ] + fix_modify AtC output index [step | time ] + - filename_prefix (string) = prefix for data files + - frequency (integer) = frequency of output in time-steps + - options (keyword/s): \n + text = creates text output of index, step and nodal variable values for unique nodes \n + full_text = creates text output index, nodal id, step, nodal coordinates and nodal variable values for unique and image nodes \n + binary = creates binary Ensight output \n + vector_components = outputs vectors as scalar components \n + tensor_components = outputs tensor as scalar components + (use this for Paraview)\n + + \section examples + fix_modify AtC output heatFE 100 \n + fix_modify AtC output hardyFE 1 text tensor_components \n + fix_modify AtC output hardyFE 10 text binary tensor_components \n + fix_modify AtC output index step \n + \section description + Creates text and/or binary (Ensight, "gold" format) output of nodal/mesh data + which is transfer/physics specific. Output indexed by step or time is possible. + \section restrictions + \section related + see \ref man_fix_atc + \section default + output indexed by time + */ + else if (strcmp(arg[argIdx],"output")==0) { + argIdx++; + /*! \page man_output_nodeset fix_modify AtC output + \section syntax + fix_modify AtC output nodeset + - nodeset_name (string) = name of nodeset to be operated on + - operation (keyword/s): \n + sum = creates nodal sum over nodes in specified nodeset \n + \section examples + fix_modify AtC output nodeset nset1 sum \n + \section description + Performs operation over the nodes belonging to specified nodeset + and outputs resulting variable values to GLOBALS file. + \section restrictions + \section related + see \ref man_fix_atc + \section default + none + */ + if (strcmp(arg[argIdx],"nodeset")==0) { + argIdx++; + string nset = arg[argIdx++]; + if (strcmp(arg[argIdx],"sum")==0) { + argIdx++; + string field = arg[argIdx]; + pair < string, FieldName > id(nset,string_to_field(field)); + nsetData_[id] = NODESET_SUM; + match = true; + } + else if (strcmp(arg[argIdx],"average")==0) { + argIdx++; + string field = arg[argIdx]; + pair < string, FieldName > id(nset,string_to_field(field)); + nsetData_[id] = NODESET_AVERAGE; + match = true; + } + } + + + /*! \page man_boundary_integral fix_modify AtC boundary_integral + \section syntax + fix_modify AtC boundary_integral [field] faceset [name] + - field (string) : name of hardy field + - name (string) : name of faceset + \section examples + fix_modify AtC boundary_integral stress faceset loop1 \n + \section description + Calculates a surface integral of the given field dotted with the + outward normal of the faces and puts output in the "GLOBALS" file + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + none + */ + + /*! \page man_contour_integral fix_modify AtC contour_integral + \section syntax + fix_modify AtC output contour_integral [field] faceset [name] + - field (string) : name of hardy field + - name (string) : name of faceset + - axis (string) : x or y or z + \section examples + fix_modify AtC contour_integral stress faceset loop1 \n + \section description + Calculates a surface integral of the given field dotted with the + outward normal of the faces and puts output in the "GLOBALS" file + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + none + */ + + else if ( (strcmp(arg[argIdx],"boundary_integral")==0) + || (strcmp(arg[argIdx],"contour_integral")==0) ) { + FacesetIntegralType type = BOUNDARY_INTEGRAL; + if (strcmp(arg[argIdx],"contour_integral")==0) + type = CONTOUR_INTEGRAL; + argIdx++; + string field(arg[argIdx++]); + if(strcmp(arg[argIdx],"faceset")==0) { + argIdx++; + string name(arg[argIdx++]); + pair pair_name(name,field); + fsetData_[pair_name] = type; + match = true; + } + } // end "boundary_integral" || "contour_integral" + + /*! \page man_output_elementset fix_modify AtC output + \section syntax + 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 + \section examples + fix_modify AtC output eset1 mass_density \n + \section description + Performs volume integration of specified field over elementset + and outputs resulting variable values to GLOBALS file. + \section restrictions + \section related + see \ref man_fix_atc + \section default + none + */ + + else if ( (strcmp(arg[argIdx],"volume_integral")==0) ) { + argIdx++; + string name(arg[argIdx++]); + string field(arg[argIdx++]); + pair pair_name(name,string_to_field(field)); + if (++argIdx < narg) { // keyword average + esetData_[pair_name] = ELEMENTSET_AVERAGE; + } + else { + esetData_[pair_name] = ELEMENTSET_TOTAL; + } + match = true; + } + + else if (strcmp(arg[argIdx],"now")==0) { + argIdx++; + double dt = 1.0; + if (argIdx < narg) { + dt = atof(arg[argIdx++]); + } + update_time(dt); + update_step(); + outputNow_ = true; + this->output(); + outputNow_ = false; + match = true; + } + else + if (strcmp(arg[argIdx],"index")==0) { + argIdx++; + if (strcmp(arg[argIdx],"step")==0) { outputTime_ = false; } + else { outputTime_ = true; } + match = true; + } + else { + outputPrefix_ = arg[argIdx++]; + outputFrequency_ = atoi(arg[argIdx++]); + bool ensight_output = false, full_text_output = false; + bool text_output = false, vect_comp = false, tensor_comp = false; + int rank = lammpsInterface_->comm_rank(); + for (int i = argIdx; i0) { + set otypes; + if (full_text_output || text_output) { + lammpsInterface_->print_msg_once("Warning : text output can create _LARGE_ files"); + } + if (full_text_output) otypes.insert(FULL_GNUPLOT); + if (text_output) otypes.insert(GNUPLOT); + if (ensight_output) otypes.insert(ENSIGHT); + if (ntracked() > 0) { + string fstem = field_to_string(SPECIES_CONCENTRATION); + string istem = field_to_intrinsic_name(SPECIES_CONCENTRATION); + vector tnames = tracked_names(); + vector fnames; + vector inames; + for (unsigned int i = 0; i < tnames.size(); i++) { + fnames.push_back(fstem+tnames[i]); + inames.push_back(istem+tnames[i]); + } + feEngine_->add_field_names(fstem,fnames); + feEngine_->add_field_names(istem,inames); + } + feEngine_->initialize_output(rank,outputPrefix_,otypes); + if (vect_comp) + feEngine_->output_manager() + ->set_option(OUTPUT_VECTOR_COMPONENTS,true); + if (tensor_comp) + feEngine_->output_manager() + ->set_option(OUTPUT_TENSOR_COMPONENTS,true); + } + match = true; + } + } + // add a species for tracking + /*! \page man_mass_control fix_modify AtC add_species group|type + \section syntax + fix_modify_AtC add_species group|type \n + + - = tag for tracking a species \n + - group|type = LAMMPS defined group or type of atoms \n + \section examples + fix_modify AtC add_species gold type 1 \n + group GOLDGROUP type 1 \n + fix_modify AtC add_species gold group GOLDGROUP + \section description + Associates a tag with all atoms of a specified type or within a specified group. \n + \section restrictions + \section related + \section default + No defaults for this command. + */ + else if (strcmp(arg[argIdx],"add_species")==0) { + argIdx++; + string speciesTag = arg[argIdx]; + string tag = arg[argIdx]; + argIdx++; + IdType idType; + if (strcmp(arg[argIdx],"group")==0) { + idType = ATOM_GROUP; + if (narg-argIdx == 2) { + string name = arg[++argIdx]; + int id = lammpsInterface_->group_bit(name); + groupList_.push_back(id); + groupNames_.push_back(tag); + + speciesIds_[speciesTag] = pair(idType,id); // OBSOLETE + } + else { + while (++argIdx < narg) { + string name = arg[argIdx]; + int id = lammpsInterface_->group_bit(name); + string tag = speciesTag+"-"+name; + groupList_.push_back(id); + groupNames_.push_back(tag); + speciesIds_[speciesTag] = pair(idType,id); // OBSOLETE + } + } + } + else if (strcmp(arg[argIdx],"type")==0) { + idType = ATOM_TYPE; + if (narg-argIdx == 2) { + int id = atoi(arg[++argIdx]); + typeList_.push_back(id); + typeNames_.push_back(tag); + speciesIds_[speciesTag] = pair(idType,id); // OBSOLETE + } + else { + while (++argIdx < narg) { + int id = atoi(arg[argIdx]); + string tag = speciesTag+"_"+to_string(id); + typeList_.push_back(id); + typeNames_.push_back(tag); + speciesIds_[speciesTag] = pair(idType,id); // OBSOLETE + } + } + } + else { + throw ATC_Error("ATC_Method: only groups or types for add_species"); } + match = true; + } + + // remove species from tracking + + /*! \page man_disp_control fix_modify AtC remove_species + \section syntax + fix_modify_AtC remove_species \n + + - = tag for tracking a species \n + \section examples + fix_modify AtC remove_species gold \n + \section description + Removes tag designated for tracking a specified species. \n + \section restrictions + \section related + \section default + No defaults for this command. + */ + else if (strcmp(arg[argIdx],"remove_species")==0) { + argIdx++; + string speciesTag = arg[argIdx]; + speciesIds_.erase(speciesTag); + taggedDensMan_.erase(speciesTag); + + + } + + // add a molecule for tracking + /*! \page man_mass_control fix_modify AtC add_molecule small|large + \section syntax + fix_modify_AtC add_molecule small|large \n + + - = tag for tracking a species \n + - small|large = can be small if molecule size < cutoff radius, must be large otherwise \n + \section examples + group WATERGROUP type 1 2 \n + fix_modify AtC add_molecule small water WATERGROUP \n + \section description + Associates a tag with all molecules corresponding to a specified group. \n + \section restrictions + \section related + \section default + No defaults for this command. + */ + else if (strcmp(arg[argIdx],"add_molecule")==0) { + argIdx++; + MolSize size; + if (strcmp(arg[argIdx],"small")==0) { + size = MOL_SMALL; + //needXrefProcessorGhosts_ = true; + needProcGhost_ = true; + } + else + throw ATC_Error("ATC_CouplingMass: Bad molecule size in add_molecule"); + argIdx++; + string moleculeTag = arg[argIdx]; + + argIdx++; + int groupBit = lammpsInterface_->group_bit(arg[argIdx]); + moleculeIds_[moleculeTag] = pair(size,groupBit); + match = true; + } + + // remove molecule from tracking + /*! \page man_disp_control fix_modify AtC remove_molecule + \section syntax + fix_modify_AtC remove_molecule \n + + - = tag for tracking a molecule type \n + \section examples + fix_modify AtC remove_molecule water \n + \section description + Removes tag designated for tracking a specified set of molecules. \n + \section restrictions + \section related + \section default + No defaults for this command. + */ + else if (strcmp(arg[argIdx],"remove_molecule")==0) { + argIdx++; + string moleculeTag = arg[argIdx]; + moleculeIds_.erase(moleculeTag); + + taggedDensMan_.erase(moleculeTag); + } + + /*! \page man_boundary fix_modify AtC boundary + \section syntax + fix_modify AtC boundary type + - = type id for atoms that represent a ficticious + boundary internal to the FE mesh + \section examples + fix_modify AtC boundary type ghost_atoms + \section description + Command to define the atoms that represent the ficticious + boundary internal to the FE mesh. For fully overlapped MD/FE + domains with periodic boundary conditions no boundary atoms should + be defined. + \section restrictions + \section related + see \ref man_internal + \section default + none + */ + else if (strcmp(arg[argIdx],"boundary")==0) { + argIdx++; + groupTagGhost_ = arg[argIdx++]; + match = true; + } + + /*! \page man_atom_weight fix_modify AtC atom_weight + \section syntax + fix_modify AtC atom_weight + - = \n + value: atoms in specified group assigned constant value given \n + lattice: volume per atom for specified lattice type (e.g. fcc) and parameter \n + element: element volume divided among atoms within element \n + region: \n + group: \n + read_in: list of values for atoms are read-in from specified file \n + \section examples + fix_modify atc atom_weight constant myatoms 11.8 \n + fix_modify atc atom_weight lattice \n + fix_modify atc atom_weight read-in atm_wt_file.txt \n + \section description + Command for assigning the value of atomic weights used for atomic integration in + atom-continuum coupled simulations. + \section restrictions + Use of lattice option requires a lattice type and parameter is already specified. + \section related + \section default + lattice + */ + else if (strcmp(arg[argIdx],"atom_weight")==0) { + argIdx++; + if (strcmp(arg[argIdx],"constant")==0) { + argIdx++; + atomWeightType_ = USER; + int groupbit = -1; + if (strcmp(arg[argIdx],"all")==0) { + } + else { + groupbit = lammpsInterface_->group_bit(arg[argIdx]); + } + argIdx++; + double value = atof(arg[argIdx]); + Valpha_[groupbit] = value; + match = true; + } + else if (strcmp(arg[argIdx],"lattice")==0) { + atomWeightType_ = LATTICE; + match = true; + } + else if (strcmp(arg[argIdx],"element")==0) { + atomWeightType_ = ELEMENT; + match = true; + } + else if (strcmp(arg[argIdx],"region")==0) { + atomWeightType_ = REGION; + match = true; + } + else if (strcmp(arg[argIdx],"group")==0) { + atomWeightType_ = GROUP; + match = true; + } + else if (strcmp(arg[argIdx],"multiscale")==0) { + atomWeightType_ = MULTISCALE; + match = true; + } + else if (strcmp(arg[argIdx],"node")==0) { + atomWeightType_ = NODE; + match = true; + } + else if (strcmp(arg[argIdx],"node_element")==0) { + atomWeightType_ = NODE_ELEMENT; + match = true; + } + else if (strcmp(arg[argIdx],"read_in")==0) { + atomWeightType_ = READ_IN; + argIdx++; + atomicWeightsFile_ = arg[argIdx]; + match = true; + } + if (match) { + needReset_ = true; + } + } + + /*! \page man_decomposition fix_modify AtC decomposition + \section syntax + fix_modify AtC decomposition + - = \n + replicated_memory: nodal information replicated on each processor \n + distributed_memory: only owned nodal information on processor \n + \section examples + fix_modify atc decomposition distributed_memory \n + \section description + Command for assigning the distribution of work and memory for parallel runs. + \section restrictions + replicated_memory is appropriate for simulations were the number of nodes << number of atoms + \section related + \section default + replicated_memory + */ + else if (strcmp(arg[argIdx],"decomposition")==0) { + argIdx++; + if (strcmp(arg[argIdx],"replicated_memory")==0) { + domainDecomposition_ = REPLICATED_MEMORY; + match = true; + } + else if (strcmp(arg[argIdx],"distributed_memory")==0) { + domainDecomposition_ = DISTRIBUTED_MEMORY; + match = true; + } + } + + /*! \page man_write_atom_weights fix_modify AtC write_atom_weights + \section syntax + fix_modify AtC write_atom_weights + - = name of file that atomic weights are written to \n + - = how often writes will occur \n + \section examples + fix_modify atc write_atom_weights atm_wt_file.txt 10 \n + \section description + Command for writing the values of atomic weights to a specified file. + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIdx],"write_atom_weights")==0) { + argIdx++; + atomicWeightsFile_ = arg[argIdx]; + argIdx++; + atomicWeightsWriteFrequency_ = atoi(arg[argIdx]); + atomicWeightsWriteFlag_ = true; + match = true; + } + + + /*! \page man_initial_atomic fix_modify AtC initial_atomic + \section syntax + \section examples + \section description + \section restrictions + \section related + \section default + */ + // set initial atomic displacements/velocities + /** Example commmand for initial atomic displacements/velocities + fix_modify atc initial_atomic displacement x all function gaussian */ + else if (strcmp(arg[argIdx],"initial_atomic")==0) { + XT_Function* function = NULL; + argIdx = 3; + if (strcmp(arg[argIdx],"all")==0) { + + if (strcmp(arg[argIdx+1],"function")==0) { + string fName = arg[argIdx+2]; + int nargs = narg - 3 - argIdx; + double argF[nargs]; + for (int i = 0; i < nargs; ++i) argF[i] = atof(arg[3+argIdx+i]); + function = XT_Function_Mgr::instance()->function(fName,nargs,argF); + } + } + if (function) { + argIdx--; + string sdim = arg[argIdx--]; + int idim = 0; + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + if (string_to_index(sdim,idim)) { + if (strcmp(arg[argIdx],"displacement")==0) { + for (int i = 0; i < lammpsInterface_->nlocal(); ++i) { + x[i][idim] += function->f(x[i],0); + } + } + else if (strcmp(arg[argIdx],"velocity")==0) { + for (int i = 0; i < lammpsInterface_->nlocal(); ++i) { + v[i][idim] = function->f(x[i],0); + } + } + } + match = true; + } + } + + /*! \page man_reset_time fix_modify AtC reset_time + \section syntax + fix_modify AtC reset_time + \section examples + fix_modify atc reset_time 0.0 \n + \section description + Resets the simulation time counter. + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIdx],"reset_time")==0) { + argIdx++; + set_time(); + if (narg > argIdx) { + double time = atof(arg[argIdx]); + set_time(time); + } + match = true; + } + + /*! \page man_reset_atomic_reference_positions fix_modify AtC reset_atomic_reference_positions + \section syntax + fix_modify AtC reset_atomic_reference_positions + \section examples + fix_modify atc reset_atomic_reference_positions + \section description + Resets the atomic positions ATC uses to perform point to field operations. + In can be used to use perfect lattice sites in ATC but a thermalized or + deformed lattice in LAMMPS. + \section restrictions + \section related + \section default + Default is off + */ + else if (strcmp(arg[argIdx],"reset_atomic_reference_positions")==0) { + argIdx++; + xRefFile_ = arg[argIdx]; + readXref_ = true; + match = true; + } + + /*! \page man_set fix_modify AtC set + \section syntax + fix_modify AtC set reference_potential_energy + - value (double) : optional user specified zero point for PE in native LAMMPS energy units + \section examples + fix_modify AtC set reference_potential_energy \n + fix_modify AtC set reference_potential_energy -0.05 \n + \section description + Used to set various quantities for the post-processing algorithms. + Currently it only + sets the zero point for the potential energy density using + the value provided for all nodes, or from the current + configuration of the lattice if no value is provided + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + Defaults to lammps zero point i.e. isolated atoms + */ + else if (strcmp(arg[argIdx],"set")==0) { + argIdx++; + if (strcmp(arg[argIdx],"reference_potential_energy")==0) { + argIdx++; + setRefPE_ = true; + if (narg > argIdx) { + string a(arg[argIdx]); + if (is_dbl(a)) { + double value = atof(arg[argIdx]); + refPEvalue_ = value; + setRefPEvalue_ = true; + } + else { + nodalRefPEfile_ = arg[argIdx]; + readRefPE_ = true; + } + } + match = true; + } + } // end "set" + + + + /*! \page man_atom_element_map fix_modify AtC atom_element_map + \section syntax + fix_modify AtC atom_element_map \n + - frequency (int) : frequency of updating atom-to-continuum maps based on the + current configuration - only for eulerian + \section examples + fix_modify atc atom_element_map eulerian 100 + \section description + Changes frame of reference from eulerian to lagrangian and sets the + frequency for which the map from atoms to elements is reformed and + all the attendant data is recalculated. + \section restrictions + Cannot change map type after initialization. + \section related + \section default + lagrangian + */ + else if (strcmp(arg[argIdx],"atom_element_map")==0) { + argIdx++; + if (strcmp(arg[argIdx],"eulerian")==0) { + atomToElementMapType_ = EULERIAN; + argIdx++; + atomToElementMapFrequency_ = atoi(arg[argIdx]); + } + else { + atomToElementMapType_ = LAGRANGIAN; + atomToElementMapFrequency_ = 0; + } + match = true; + needReset_ = true; + } + + /*! \page man_read_restart fix_modify AtC read_restart + \section syntax + fix_modify AtC read_restart [file_name] \n + \section examples + fix_modify AtC read_restart ATC_state \n + \section description + Reads the current state of the fields from a named text-based restart + file. + \section restrictions + The restart file only contains fields and their time derivatives. + The reference positions of the atoms and the commands that initialize + the fix are not saved e.g. an identical mesh containing the same atoms + will have to be recreated. + \section related + see write_restart \ref man_write_restart + \section default + none + */ + else if (strcmp(arg[argIdx],"read_restart")==0) { + argIdx++; + restartFileName_ = arg[argIdx]; + useRestart_ = true; + match = true; + } + + /*! \page man_write_restart fix_modify AtC write_restart + \section syntax + fix_modify AtC write_restart [file_name] \n + \section examples + fix_modify AtC write_restart restart.mydata \n + \section description + Dumps the current state of the fields to a named text-based restart file. + This done when the command is invoked and not repeated, unlike the + similar lammps command. + \section restrictions + The restart file only contains fields and their time derivatives. + The reference positions of the atoms and the commands that initialize + the fix are not saved e.g. an identical mesh containing the same atoms + will have to be recreated. + \section related + see read_restart \ref man_read_restart + \section default + none + */ + else if (strcmp(arg[argIdx],"write_restart")==0) { + argIdx++; + string restartFileName(arg[argIdx]); + RESTART_LIST data; + write_restart_data(restartFileName,data); + match = true; + } + + } // end else + + return match; // return to FixATC + + } + + //-------------------------------------------------- + // 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, int & thisIndex) + { + string thisName = args[argIdx++]; + if (args[argIdx] == NULL) { + throw ATC_Error("Need to give field '"+thisName+"' more args"); + } + thisField = string_to_field(thisName); + map::const_iterator iter = fieldSizes_.find(thisField); + if (iter == fieldSizes_.end()) { + throw ATC_Error("Bad field name: "+thisName); + } + string thisDim = args[argIdx]; + thisIndex = 0; + if (string_to_index(thisDim,thisIndex)) { + if ( !( thisIndex < fieldSizes_[thisField]) ) { + throw ATC_Error("Bad field dimension "+thisDim); + } + argIdx++; + } + } + + //------------------------------------------------------------------- + // this sets the peratom output + + void ATC_Method::update_peratom_output() + { + perAtomArray_ = perAtomOutput_; + // copy values + for (int i = 0; i < lammpsInterface_->nlocal(); i++) { + for (int j = 0; j < nsd_; j++) { + perAtomOutput_[i][j] = xref_[i][j]; + } + for (int j = nsd_; j < sizePerAtomCols_; j++) { + perAtomOutput_[i][j] = 0; + } + } + int indx = nsd_; + if (atomVolume_->nRows() > 0) { // kernel Hardy does not compute these + const DIAG_MAT & myAtomicWeights(atomVolume_->quantity()); + for (int i = 0; i < nLocal_; i++) { + double wg = myAtomicWeights(i,i); + if (wg > 0) { + int ii = internalToAtom_(i); + perAtomOutput_[ii][indx] = 1./wg; + } + } + } + } + + void ATC_Method::adjust_xref_pbc() + { + + int nlocal = lammpsInterface_->nlocal(); + int xperiodic = lammpsInterface_->xperiodic(); + int yperiodic = lammpsInterface_->yperiodic(); + int zperiodic = lammpsInterface_->zperiodic(); + double **x = lammpsInterface_->xatom(); + double boxxlo,boxxhi; + double boxylo,boxyhi; + double boxzlo,boxzhi; + + lammpsInterface_->box_bounds(boxxlo,boxxhi, + boxylo,boxyhi, + boxzlo,boxzhi); +// bool changed = false; + for (int i = 0; i < nlocal; i++) { + if (xperiodic) { + if (x[i][0] < boxxlo) { + xref_[i][0] += Xprd_; +// changed = true; + } + if (x[i][0] >= boxxhi) { + xref_[i][0] -= Xprd_; +// changed = true; + } + } + + if (yperiodic) { + if (x[i][1] < boxylo) { + xref_[i][1] += Yprd_; +// changed = true; + } + if (x[i][1] >= boxyhi) { + xref_[i][1] -= Yprd_; +// changed = true; + } + } + + if (zperiodic) { + if (x[i][2] < boxzlo) { + xref_[i][2] += Zprd_; +// changed = true; + } + if (x[i][2] >= boxzhi) { + xref_[i][2] -= Zprd_; +// changed = true; + } + } + } + + // propagate reset if needed + if (atomToElementMapType_ == LAGRANGIAN) { + if (atomCoarseGrainingPositions_) { + atomCoarseGrainingPositions_->force_reset(); + } + } + else if (atomReferencePositions_) { + atomReferencePositions_->force_reset(); + } + + } + //------------------------------------------------------------------- + void ATC_Method::initialize() + { + feEngine_->partition_mesh(); + // initialized_ is set to true by derived class initialize() + // localStep_ is a counter within a run or minimize + localStep_ = 0; + // STEP 1) get basic information data from Lammps/fix + // 1a) group ids for all internal atoms + groupbit_ = lammpsInterface_->group_bit(groupTag_); + + // 1b) group ids for ghost atoms + groupbitGhost_ = 0; + if (!groupTagGhost_.empty()) { + groupbitGhost_ = lammpsInterface_->group_bit(groupTagGhost_); + } + + // 1c) periodicity and box bounds/lengths + if (!initialized_) { + + lammpsInterface_->box_periodicity(periodicity[0], + periodicity[1], + periodicity[2]); + lammpsInterface_->box_bounds(box_bounds[0][0],box_bounds[1][0], + box_bounds[0][1],box_bounds[1][1], + box_bounds[0][2],box_bounds[1][2]); + for (int k = 0; k < nsd_; k++) { + box_length[k] = box_bounds[1][k] - box_bounds[0][k]; + } + + lammpsInterface_->set_reference_box(); + + // get periodicity data from lammps for parallel exchange to adjust for periodicity + Xprd_ = lammpsInterface_->domain_xprd(); + Yprd_ = lammpsInterface_->domain_yprd(); + Zprd_ = lammpsInterface_->domain_zprd(); +// box_length[0] = Xprd_; +// box_length[1] = Yprd_; +// box_length[2] = Zprd_; + XY_ = lammpsInterface_->domain_xy(); + XZ_ = lammpsInterface_->domain_xz(); + YZ_ = lammpsInterface_->domain_yz(); + } + + // STEP 2 computational geometry + // 2a) get basic information from continuum/FE + this->set_continuum_data(); + + // STEP 2b) set up data structures for computational geometry + if (this->reset_methods()) { + // clear memory manager + interscaleManager_.clear_temporary_data(); + atomVolume_ = NULL; + + // reference positions and energy + if (!initialized_) { + double **x = lammpsInterface_->xatom(); + for (int i = 0; i < lammpsInterface_->nmax(); i++) { + for (int j = 0; j < nsd_; j++) { + xref_[i][j] = x[i][j]; + } + } + + // re-write non-ghosts' xref with values from a file + if (readXref_) { + bool success = read_atomic_ref_positions(xRefFile_.c_str()); + if (!success) + throw ATC_Error("Error reading atomic reference positions"); + readXref_ = false; + } + + // set up maps from lammps to atc indexing + reset_nlocal(); + } + + this->set_computational_geometry(); + } + + // 2c) basic data regarding atomic system, e.g. atom coordinates + if (atomToElementMapType_ == EULERIAN) { + reset_coordinates(); + } + + // STEP 3) set up variables which will be integrated in time + this->construct_time_integration_data(); + + // STEP 4) instantiate all the various specific algorithms and methods + this->construct_methods(); + + // STEP 5) construct dependency-managed data + // 5b) all other transfer operators + // needs to be done before every run in case options have changed or the atoms have been changed by the user + + if (this->reset_methods()) { + // construct all the needed data structures + this->construct_transfers(); + + // allocate all space needed for lammps arrays + interscaleManager_.grow_arrays(lammpsInterface_->nmax()); + } + // reset all computes invoked flags and lammps data + interscaleManager_.lammps_force_reset(); + + // STEP 6) initialize data + // 6b) size quantities which use pack_comm + interscaleManager_.size_comm_quantities(); + + // 6c) set coarse-graining functions and atomic weights + if (!initialized_) { + // FE_Engine allocates all required memory + // assume initial atomic position is the reference position for now + + // \int_\Omega N_I dV : static if the mesh is + NodeVolumes_.reset(nNodes_,nNodes_); + invNodeVolumes_.reset(nNodes_,nNodes_); + feEngine_->compute_lumped_mass_matrix(NodeVolumes_); + invNodeVolumes_.set_quantity() = NodeVolumes_.inv(); + } + atomVolume_->set_reset(); + + // + this->set_reference_potential_energy(); + + // 6d) atomic output for 0th step + update_peratom_output(); + + // clear need for resets + needReset_ = false; + + } + //------------------------------------------------------------------- + void ATC_Method::set_continuum_data() + { + // initialize finite element engine and get basic properties + if (!initialized_) { + feEngine_->initialize(); + if (nsd_!=feEngine_->nsd()) { + throw ATC_Error("Spatial dimensions inconsistent between LAMMPS and ATC"); + } + nNodes_ = feEngine_->num_nodes(); + } + } + + //------------------------------------------------------------------- + void ATC_Method::set_computational_geometry() + { + // set positions used for coarse-graining operators + + + + + if (!initialized_) { + if (atomToElementMapType_ == EULERIAN) { + FundamentalAtomQuantity * atomPositionsAll = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION,ALL); + ClonedAtomQuantity * myAtomPositions = + new ClonedAtomQuantity(this,atomPositionsAll,INTERNAL); + atomCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicCoarseGrainingPositions"); + + if (trackDisplacement_) { + XrefWrapper * myAtomReferencePositions = new XrefWrapper(this); + atomReferencePositions_ = myAtomReferencePositions; + interscaleManager_.add_per_atom_quantity(myAtomReferencePositions, + "AtomicReferencePositions"); + atomReferencePositions_->set_memory_type(PERSISTENT); + } + + if (groupbitGhost_) { + myAtomPositions = new ClonedAtomQuantity(this,atomPositionsAll,GHOST); + atomGhostCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicGhostCoarseGrainingPositions"); + } + if(needProcGhost_){ + FundamentalAtomQuantity * atomPositionsAll = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION,PROC_GHOST); + ClonedAtomQuantity * myAtomPositions = + new ClonedAtomQuantity(this,atomPositionsAll,PROC_GHOST); + atomProcGhostCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicProcGhostCoarseGrainingPositions"); + } + } + else { + XrefWrapper * myAtomPositions = new XrefWrapper(this); + atomCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicCoarseGrainingPositions"); + atomReferencePositions_ = atomCoarseGrainingPositions_; + + if (groupbitGhost_) { + myAtomPositions = new XrefWrapper(this,GHOST); + atomGhostCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicGhostCoarseGrainingPositions"); + } + if (needProcGhost_) { + XrefWrapper * myAtomPositions = new XrefWrapper(this); + atomProcGhostCoarseGrainingPositions_ = myAtomPositions; + interscaleManager_.add_per_atom_quantity(myAtomPositions, + "AtomicProcGhostCoarseGrainingPositions"); + } + } + atomCoarseGrainingPositions_->set_memory_type(PERSISTENT); + if (atomGhostCoarseGrainingPositions_) atomGhostCoarseGrainingPositions_->set_memory_type(PERSISTENT); + if (atomProcGhostCoarseGrainingPositions_) atomProcGhostCoarseGrainingPositions_->set_memory_type(PERSISTENT); + } + + // Add in atom to element map if using shape functions + if (needsAtomToElementMap_) { + atomElement_ = new AtomToElementMap(this); + interscaleManager_.add_per_atom_int_quantity(atomElement_,"AtomElement"); + } + } + + //------------------------------------------------------------------- + void ATC_Method::construct_transfers() + { + + // per-atom quantities from lammps + atomMasses_ = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); + atomPositions_ = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION); + atomVelocities_ = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); + atomForces_ = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE); + ComputedAtomQuantity * atomicPotentialEnergy = new ComputedAtomQuantity(this,lammpsInterface_->compute_pe_name(), peScale_); + interscaleManager_.add_per_atom_quantity(atomicPotentialEnergy, + "AtomicPotentialEnergy"); + } + //------------------------------------------------------------------- + PerAtomDiagonalMatrix * ATC_Method::create_atom_volume() + { + // WIP_JAT have check for reset to see if we should freshen atomVolume_ + if (atomVolume_) { + return atomVolume_; + } + else { + // set variables to compute atomic weights + DENS_MAN * nodalVolume(NULL); + switch (atomWeightType_) { + case USER: + atomVolume_ = new AtomVolumeUser(this,Valpha_); + break; + case LATTICE: + atomVolume_ = new AtomVolumeLattice(this); + break; + case ELEMENT: + atomVolume_ = new AtomVolumeElement(this); + break; + case REGION: + atomVolume_ = new AtomVolumeRegion(this); + break; + case GROUP: + atomVolume_ = new AtomVolumeGroup(this,Valpha_); + break; + case MULTISCALE: + if (!shpFcn_) { + throw ATC_Error("ATC_Method::create_atom_volume - Multiscale algorithm requires an interpolant"); + } + nodalVolume = new NodalAtomVolume(this,shpFcn_); + interscaleManager_.add_dense_matrix(nodalVolume,"NodalAtomVolume"); + atomVolume_ = new FtaShapeFunctionProlongationDiagonalMatrix(this,nodalVolume,shpFcn_); + break; + case NODE: + if (!shpFcn_) { + throw ATC_Error("ATC_Method::create_atom_volume - Node algorithm requires an interpolant"); + } + nodalVolume = new NodalVolume(this,shpFcn_); + interscaleManager_.add_dense_matrix(nodalVolume,"NodalVolume"); + atomVolume_ = new FtaShapeFunctionProlongationDiagonalMatrix(this,nodalVolume,shpFcn_); + break; + case NODE_ELEMENT: + if (!shpFcn_) { + throw ATC_Error("ATC_Method::create_atom_volume - Node-Element algorithm requires an interpolant"); + } + nodalVolume = new NodalAtomVolumeElement(this,shpFcn_); + interscaleManager_.add_dense_matrix(nodalVolume,"NodalAtomVolumeElement"); + atomVolume_ = new FtaShapeFunctionProlongationDiagonalMatrix(this,nodalVolume,shpFcn_); + break; + case READ_IN: + atomVolume_ = new AtomVolumeFile(this,atomicWeightsFile_); + break; + } + if (atomVolume_) { + interscaleManager_.add_per_atom_diagonal_matrix(atomVolume_,"AtomVolume"); + } + else { + throw ATC_Error("ATC_Method::create_atom_volume - bad option for atom volume algorithm"); + } + + return atomVolume_; + } + } + //------------------------------------------------------------------- + void ATC_Method::pre_exchange() + { + adjust_xref_pbc(); + // call interscale manager to sync atc per-atom data with lammps array ahead of parallel communication + interscaleManager_.prepare_exchange(); + } + //------------------------------------------------------------------- + void ATC_Method::setup_pre_exchange() + { + adjust_xref_pbc(); + // call interscale manager to sync atc per-atom data with lammps array ahead of parallel communication + interscaleManager_.prepare_exchange(); + } + //------------------------------------------------------------------- + void ATC_Method::pre_neighbor() + { +#ifdef EXTENDED_ERROR_CHECKING + if (atomSwitch_) { + lammpsInterface_->print_msg("Atoms left this processor"); + atomSwitch_ = false; + } +#endif + // reset quantities arising from atom exchange + reset_nlocal(); + + interscaleManager_.post_exchange(); + + // forward_comm should go here + } + //------------------------------------------------------------------- + void ATC_Method::min_post_force() + { + post_force(); + } + //------------------------------------------------------------------- + void ATC_Method::post_force() + { + // this resets allow for the possibility of other fixes modifying positions and velocities, e.g. walls, but reduces efficiency + interscaleManager_.lammps_force_reset(); + if ((atomToElementMapType_ == EULERIAN) && (step() % atomToElementMapFrequency_ == 0)) { + reset_coordinates(); + } + } + //------------------------------------------------------------------- + void ATC_Method::post_final_integrate() + { + if (atomicWeightsWriteFlag_ && (step() % atomicWeightsWriteFrequency_ == 0)) { + write_atomic_weights(atomicWeightsFile_,atomVolume_->quantity()); + } + } + //------------------------------------------------------------------- + void ATC_Method::end_of_step() + { + localStep_ += 1; + } + + //-------------------------------------------------------------- + void ATC_Method::finish() + { + // FE Engine + if (feEngine_) feEngine_->finish(); + feEngine_->departition_mesh(); + } + + //-------------------------------------------------------------- + /** method to add new fields to the included list */ + //-------------------------------------------------------------- + void ATC_Method::add_fields(map & newFieldSizes) + { + map::const_iterator field; + for (field = newFieldSizes.begin(); field!=newFieldSizes.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + if (fieldSizes_.find(thisField)==fieldSizes_.end()) { + fieldSizes_[thisField] = thisSize; + } + } + } + +//------------------------------------------------------------------- + void ATC_Method::set_reference_potential_energy(void) + { + // set the reference value for nodal PE + if (setRefPE_) { + if (setRefPEvalue_) { + nodalRefPotentialEnergy_ = refPEvalue_; + setRefPEvalue_ = false; + } + else if (readRefPE_) { + if (LammpsInterface::instance()->rank_zero()) { + stringstream ss; + ss << "reading reference potential energy from " << nodalRefPEfile_; + LammpsInterface::instance()->print_msg(ss.str()); + } + nodalRefPotentialEnergy_.from_file(nodalRefPEfile_); + readRefPE_ = false; + } + else { + /* NOTE const */ PerAtomQuantity * atomicPotentialEnergy + + + + + + =interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + AtfProjection * pe = new AtfProjection(this, atomicPotentialEnergy, + accumulant_, accumulantInverseVolumes_); + nodalRefPotentialEnergy_ = pe->quantity(); + delete pe; + } + setRefPE_ = false; + hasRefPE_ = true; + } + } +//------------------------------------------------------------------- + + + //================================================================= + // memory management and processor information exchange + //================================================================= + + //----------------------------------------------------------------- + // memory usage of local atom-based arrays + //----------------------------------------------------------------- + int ATC_Method::memory_usage() + { + + int bytes = 4; + bytes += interscaleManager_.memory_usage(); + bytes *= lammpsInterface_->nmax() * sizeof(double); + return bytes; + } + + //----------------------------------------------------------------- + // allocate local atom-based arrays + //----------------------------------------------------------------- + void ATC_Method::grow_arrays(int nmax) + { + xref_ = + lammpsInterface_->grow_2d_double_array(xref_,nmax,3,"fix_atc:xref"); + + perAtomOutput_ = + lammpsInterface_->grow_2d_double_array(perAtomOutput_,nmax,sizePerAtomCols_,"fix_atc:perAtomOutput"); + interscaleManager_.grow_arrays(nmax); + } + + //----------------------------------------------------------------- + // copy values within local atom-based arrays + //----------------------------------------------------------------- + void ATC_Method::copy_arrays(int i, int j) + { + xref_[j][0] = xref_[i][0]; + xref_[j][1] = xref_[i][1]; + xref_[j][2] = xref_[i][2]; + + for (int ii = 0 ; ii < sizePerAtomCols_ ; ii++ ) { + perAtomOutput_[j][ii] = perAtomOutput_[i][ii]; + } + interscaleManager_.copy_arrays(i,j); + } + + //----------------------------------------------------------------- + // pack values in local atom-based arrays for exchange with another proc + //----------------------------------------------------------------- + int ATC_Method::pack_exchange(int i, double *buf) + { +#ifdef EXTENDED_ERROR_CHECKING + atomSwitch_ = true; +#endif + buf[0] = xref_[i][0]; + buf[1] = xref_[i][1]; + buf[2] = xref_[i][2]; + + int j = 4; + for (int ii = 0 ; ii < sizePerAtomCols_ ; ii++ ) { + buf[j++] = perAtomOutput_[i][ii]; + } + int interscaleSizeComm = interscaleManager_.pack_exchange(i,&buf[j]); + return sizeComm_ + interscaleSizeComm; + } + + //----------------------------------------------------------------- + // unpack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + int ATC_Method::unpack_exchange(int nlocal, double *buf) + { + xref_[nlocal][0] = buf[0]; + xref_[nlocal][1] = buf[1]; + xref_[nlocal][2] = buf[2]; + + int j = 4; + for (int ii = 0 ; ii < sizePerAtomCols_ ; ii++ ) { + perAtomOutput_[nlocal][ii] = buf[j++]; + } + int interscaleSizeComm = interscaleManager_.unpack_exchange(nlocal,&buf[j]); + return sizeComm_ + interscaleSizeComm; + } + + //----------------------------------------------------------------- + // pack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + int ATC_Method::pack_comm(int n, int *list, double *buf, + int pbc_flag, int *pbc) + { + int i,j,m; + double dx = 0,dy = 0,dz = 0; + + int * num_bond = lammpsInterface_->num_bond(); + int ** bond_atom = lammpsInterface_->bond_atom(); + + m = 0; + if (pbc_flag == 0) { + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xref_[j][0]; + buf[m++] = xref_[j][1]; + buf[m++] = xref_[j][2]; + + if (num_bond) { + buf[m++] = num_bond[j]; + for (int ii = 0; ii < lammpsInterface_->bond_per_atom(); ii++) { + buf[m++] = bond_atom[j][ii]; + } + } + } + } + else { + if (lammpsInterface_->domain_triclinic() == 0) { + dx = pbc[0]*Xprd_; + dy = pbc[1]*Yprd_; + dz = pbc[2]*Zprd_; + } + else { + dx = pbc[0]*Xprd_ + pbc[5]*XY_ + pbc[4]*XZ_; + dy = pbc[1]*Yprd_ + pbc[3]*YZ_; + dz = pbc[2]*Zprd_; + } + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xref_[j][0] + dx; + buf[m++] = xref_[j][1] + dy; + buf[m++] = xref_[j][2] + dz; + + if (num_bond) { + buf[m++] = num_bond[j]; + for (int ii = 0; ii < lammpsInterface_->bond_per_atom(); ii++) { + buf[m++] = bond_atom[j][ii]; + } + } + + } + } + + int mySize = 3; + if (num_bond) + mySize += 1 + lammpsInterface_->bond_per_atom(); + return mySize; + } + + //----------------------------------------------------------------- + // unpack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + void ATC_Method::unpack_comm(int n, int first, double *buf) + { + int i,m,last; + + int * num_bond = lammpsInterface_->num_bond(); + int ** bond_atom = lammpsInterface_->bond_atom(); + + m = 0; + last = first + n; + for (i = first; i < last; i++) { + xref_[i][0] = buf[m++]; + xref_[i][1] = buf[m++]; + xref_[i][2] = buf[m++]; + + if (num_bond) { + num_bond[i] = static_cast(buf[m++]); + for (int ii = 0; ii < lammpsInterface_->bond_per_atom(); ii++) { + bond_atom[i][ii] = static_cast(buf[m++]); + } + } + + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Method::reset_nlocal() + { + nLocalTotal_ = lammpsInterface_->nlocal(); + const int * mask = lammpsInterface_->atom_mask(); + nLocal_ = 0; + nLocalGhost_ = 0; + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbit_) nLocal_++; + if (mask[i] & groupbitGhost_) nLocalGhost_++; + } + int local_data[2] = {nLocal_, nLocalGhost_}; + int data[2] = {0, 0}; + lammpsInterface_->int_allsum(local_data,data,2); + nInternal_ = data[0]; + nGhost_ = data[1]; + + // set up internal & ghost maps & coordinates + + if (nLocal_>0) { + // set map + internalToAtom_.reset(nLocal_); + int j = 0; + // construct internalToAtom map + // : internal index -> local lammps atom index + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbit_) internalToAtom_(j++) = i; + } + // construct reverse map + atomToInternal_.clear(); + for (int i = 0; i < nLocal_; ++i) { + atomToInternal_[internalToAtom_(i)] = i; + } + } + if (nLocalGhost_>0) { + // set map + ghostToAtom_.reset(nLocalGhost_); + int j = 0; + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbitGhost_) ghostToAtom_(j++) = i; + } + } + + //WIP_JAT this should not be needed at all, but a memory problem with sparse matrices requires them to be reset (possibly related to note in SparseMatrix-inl.h::_delete()) + interscaleManager_.reset_nlocal(); + + } + + //------------------------------------------------------------------- + void ATC_Method::reset_coordinates() + { + // update coarse graining positions for internal and ghost atoms + atomCoarseGrainingPositions_->unfix_quantity(); + atomCoarseGrainingPositions_->quantity(); + atomCoarseGrainingPositions_->fix_quantity(); + if (atomGhostCoarseGrainingPositions_) { + atomGhostCoarseGrainingPositions_->unfix_quantity(); + atomGhostCoarseGrainingPositions_->quantity(); + atomGhostCoarseGrainingPositions_->fix_quantity(); + } + if (atomProcGhostCoarseGrainingPositions_) { + atomProcGhostCoarseGrainingPositions_->unfix_quantity(); + atomProcGhostCoarseGrainingPositions_->quantity(); + atomProcGhostCoarseGrainingPositions_->fix_quantity(); + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Method::write_atomic_weights(const string filename, const DIAG_MAT & atomicVolumeMatrix) + { + int nlocal = lammpsInterface_->nlocal(); + int nlocalmax; + LammpsInterface::instance()->int_allmax(&nlocal,&nlocalmax); + int natoms = int(lammpsInterface_->natoms()); + ofstream out; + const char* fname = &filename[0]; + + // create tag to local id map for this processor + map id2tag; + map ::const_iterator itr; + int * atom_tag = lammpsInterface_->atom_tag(); + for (int i = 0; i < nlocal; ++i) { + id2tag[i] = atom_tag[i]; + } + + int comm_rank = LammpsInterface::instance()->comm_rank(); + int nprocs; + LammpsInterface::instance()->int_allmax(&comm_rank,&nprocs); + nprocs += 1; + + if (comm_rank == 0) { + out.open(fname); + // print header lines + out << "Atomic Weights for LAMMPS/atc analysis\n"; + out << " \n"; + out << natoms << " Atoms in system\n"; + out << " \n"; + // print atomic weights from proc 0 + for(int i = 0; i < nlocal; i++) { + out << id2tag[i] << " " << atomicVolumeMatrix(i,i) << "\n"; + } + } + + if (nprocs > 1) { + int max_size,send_size; + send_size = nlocal; + LammpsInterface::instance()->int_allmax(&send_size,&max_size); + + if (comm_rank == 0) { + int intbuf[max_size]; + double buf[max_size]; + for (int iproc = 1; iproc < nprocs; iproc++) { + LammpsInterface::instance()->int_recv(intbuf,max_size,iproc); + LammpsInterface::instance()->recv(buf,max_size,iproc); + for (int i = 0; i < max_size; i++) { + out << intbuf[i] << " " << buf[i] << "\n"; + } + } + } else { + int intbuf[send_size]; + double buf[send_size]; + for (int i = 0; i < send_size; i++) { + intbuf[i] = id2tag[i]; + buf[i] = atomicVolumeMatrix(i,i); + } + LammpsInterface::instance()->int_send(intbuf,send_size); + LammpsInterface::instance()->send(buf,send_size); + } + } + + if (comm_rank == 0) { + out.close(); + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Method::compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix, + SPAR_MAT & mdMassMatrix) const + { + + int nCols = shapeFunctionMatrix.nCols(); + DENS_MAT massMatrixLocal(nCols,nCols); + DENS_MAT denseMdMassMatrix(nCols,nCols); + if (nLocal_>0) + massMatrixLocal = shapeFunctionMatrix.transMat(shapeFunctionMatrix); + + lammpsInterface_->allsum(massMatrixLocal.ptr(), + denseMdMassMatrix.ptr(), + denseMdMassMatrix.size()); + mdMassMatrix.reset(denseMdMassMatrix,1.e-10); + } + + //================================================================= + // Interscale operators + //================================================================= + // in the spirit of the current design of ATC: atoms local, nodes global + + + + + bool ATC_Method::nodal_influence(const int groupbit, + set & nset, set & aset, double tol) + { + int nghost = nodal_influence(groupbit,nset,aset,true,tol); + int local_nghost = nghost; + lammpsInterface_->int_allsum(&local_nghost,&nghost); + if (nghost == 0) { + nodal_influence(groupbit,nset,aset,false,tol); + } + return (nghost > 0); + } + int ATC_Method::nodal_influence(const int groupbit, + set & nset, set & aset, bool ghost, double tol) + { + Array & amap = (ghost) ? ghostToAtom_ : internalToAtom_; + int natoms = (ghost) ? nLocalGhost_ : nLocal_; + DENS_MAT influence(nNodes_,1); + DENS_MAT atomInfluence(natoms,1); + const int *mask = lammpsInterface_->atom_mask(); + for (int i = 0; i < natoms; i++) { + if (mask[amap(i)] & groupbit) { + atomInfluence(i,0) = 1; + aset.insert(i); + } + } + // relies on shape functions + if (ghost) { + restrict_volumetric_quantity(atomInfluence,influence,shpFcnGhost_->quantity()); + } + else { + restrict_volumetric_quantity(atomInfluence,influence); + } + + DENS_MAT localInfluence = influence; + lammpsInterface_->allsum(localInfluence.ptr(), + influence.ptr(), + influence.size()); + + for (int i = 0; i < nNodes_; i++) { + if (fabs(influence(i,0)) > tol) { nset.insert(i); } + } + return aset.size(); + } + + + //-------------------------------------------------------- + void ATC_Method::restrict_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + const SPAR_MAT & shpFcn) + { + // computes nodeData = N*DeltaVAtom*atomData where N are the shape functions + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + //DENS_MAT workNodeArray; + + + if (atomData.nRows() > 0) { // or shpFcn_??? + workNodeArray = shpFcn.transMat(atomData); + } + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count); + return; + } + + + //-------------------------------------------------------- + void ATC_Method::restrict_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData) + { + restrict_volumetric_quantity(atomData,nodeData,shpFcn_->quantity()); + return; + } + + + //-------------------------------------------------------- + void ATC_Method::prolong(const MATRIX & nodeData, + MATRIX & atomData) + { + // computes the finite element interpolation at atoms atomData = N*nodeData + if (nLocal_>0) { + atomData = (shpFcn_->quantity())*nodeData; + } + return; + } + + //======================================================== + // FE functions + //======================================================== + + //-------------------------------------------------------- + void ATC_Method::output() + { +// if (lammpsInterface_->comm_rank() == 0) { + compute_nodeset_output(); + compute_faceset_output(); + compute_elementset_output(); +// } + } + //-------------------------------------------------------- + void ATC_Method::compute_nodeset_output(void) + { + map< pair , NodesetOperationType >::const_iterator iter; + for (iter = nsetData_.begin(); iter != nsetData_.end();iter++){ + pair id = iter->first; + string nsetName = id.first; + FieldName field = id.second; + double sum = 0.0; + const set nset = feEngine_->fe_mesh()->nodeset(nsetName); + const DENS_MAT & thisField = (fields_.find(field)->second).quantity(); + set< int >::const_iterator itr; + for (itr = nset.begin(); itr != nset.end();itr++){ + int node = *itr; + sum += thisField(node,0); + } + string name = nsetName + "_" + field_to_string(field); + if (iter->second == NODESET_AVERAGE) { + sum /= nset.size(); + name = "average_"+name; + } + feEngine_->add_global(name, sum); + } + } + //-------------------------------------------------------- + void ATC_Method::compute_faceset_output(void) + { + map < pair, FacesetIntegralType >::const_iterator iter; + DENS_MAT values; + for (iter = fsetData_.begin(); iter != fsetData_.end(); iter++) { + string bndyName = (iter->first).first; + string fieldName = (iter->first).second; + const set< PAIR > & faceSet = (feEngine_->fe_mesh())->faceset(bndyName); + ATOMIC_DATA::iterator data_iter = filteredData_.find(fieldName); + if (data_iter == filteredData_.end()) { + string msg = "Specified fieldName "+fieldName+ + " not found in filteredData_ while attempting surface integration"; + throw ATC_Error(msg); + } + const DENS_MAT & data = ((data_iter->second).quantity()); + string stem = bndyName + "_" + fieldName + "_"; + bool tf = false; + if (iter->second == CONTOUR_INTEGRAL) { + stem = "contour_"+stem; + tf = true; + } + feEngine_->field_surface_flux(data,faceSet,values,tf); + for (int i = 0; i < values.nRows() ; ++i ) { + string name = stem + to_string(i+1); + feEngine_->add_global(name, values(i,0)); + } + } + } + //-------------------------------------------------------- + void ATC_Method::compute_elementset_output(void) + { + map< pair , ElementsetOperationType >::const_iterator iter; + for (iter = esetData_.begin(); iter != esetData_.end();iter++){ + pair id = iter->first; + string esetName = id.first; + FieldName field = id.second; + const ESET eset = feEngine_->fe_mesh()->elementset(esetName); + const DENS_MAT & thisField = (fields_.find(field)->second).quantity(); + DENS_VEC total = feEngine_->integrate(thisField,eset); + string name = esetName + "_" + field_to_string(field); + if (iter->second == ELEMENTSET_AVERAGE) { + DENS_MAT ones(nNodes_,0); ones = 1; + DENS_VEC V = feEngine_->integrate(ones,eset); + total /= V[0]; + name = "average_"+name; + } + if (total.size() == 1) { + feEngine_->add_global(name, total[0]); + } + else { + for (int i = 0; i < total.size(); i++) { + feEngine_->add_global(name+to_string(i), total[i]); + } + } + } + } + + + //================================================================= + // + //================================================================= + //-------------------------------------------------------- + bool ATC_Method::read_atomic_ref_positions(const char * filename) + { + int nlocal = lammpsInterface_->nlocal(); + ifstream in; + const int lineSize = 256; + char line[lineSize]; + + // create tag to local id map for this processor + map tag2id; + map ::const_iterator itr; + int * atom_tag = lammpsInterface_->atom_tag(); + for (int i = 0; i < nlocal; ++i) { + tag2id[atom_tag[i]] = i; + } + + // get number of atoms + int natoms = 0; + if (LammpsInterface::instance()->rank_zero()) { + in.open(filename); + string msg; + string name = filename; + msg = "no "+name+" reference file found"; + if (! in.good()) throw ATC_Error(msg); + in.getline(line,lineSize); // header + in.getline(line,lineSize); // blank line + in >> natoms; + in.close(); + stringstream ss; + ss << "found " << natoms << " atoms in reference file"; + LammpsInterface::instance()->print_msg(ss.str()); + } + LammpsInterface::instance()->int_broadcast(&natoms); + + // read atoms and assign + if (LammpsInterface::instance()->rank_zero()) { + in.open(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; + } + } + } + int nread = 0,type = -1, tag = -1, count = 0; + double x[3]={0,0,0}; + while (nread < natoms) { + if (LammpsInterface::instance()->rank_zero()) { + in.getline(line,lineSize); + stringstream ss (line,stringstream::in | stringstream::out); + ss >> tag >> type >> x[0] >> x[1] >> x[2]; + nread++; + } + LammpsInterface::instance()->int_broadcast(&nread); + LammpsInterface::instance()->int_broadcast(&tag); + LammpsInterface::instance()->broadcast(x,3); + itr = tag2id.find(tag); + if (itr != tag2id.end()) { + int iatom = itr->second; + xref_[iatom][0] = x[0]; + xref_[iatom][1] = x[1]; + xref_[iatom][2] = x[2]; + count++; + } + } + if (LammpsInterface::instance()->rank_zero()) { + in.close(); + stringstream ss; + ss << "read " << natoms << " reference positions"; + LammpsInterface::instance()->print_msg(ss.str()); + } + if (count != nlocal) + throw ATC_Error("reset "+to_string(count)+" atoms vs "+to_string(nlocal)); + + + return true; + } + +//-------------------------------------------------------- + void ATC_Method::remap_ghost_ref_positions(void) + { + + int nlocal = lammpsInterface_->nlocal(); + int nghost = lammpsInterface_->nghost(); + + double box_bounds[2][3]; + lammpsInterface_->box_bounds(box_bounds[0][0],box_bounds[1][0], + box_bounds[0][1],box_bounds[1][1], + box_bounds[0][2],box_bounds[1][2]); + double xlo = box_bounds[0][0], xhi = box_bounds[1][0]; + double ylo = box_bounds[0][1], yhi = box_bounds[1][1]; + double zlo = box_bounds[0][2], zhi = box_bounds[1][2]; + + double box_length[3]; + for (int k = 0; k < 3; k++) { + box_length[k] = box_bounds[1][k] - box_bounds[0][k]; + } + double Lx = box_length[0], Ly = box_length[1], Lz = box_length[2]; + + // create tag to local id map for this processor + map tag2id; + map ::const_iterator itr; + int * atom_tag = lammpsInterface_->atom_tag(); + for (int i = 0; i < nlocal; ++i) { + tag2id[atom_tag[i]] = i; + } + + // loop over ghosts + double ** x = lammpsInterface_->xatom(); + for (int j = nlocal; j < nlocal + nghost; j++) { + int tag = atom_tag[j]; + int i = tag2id[tag]; + //itr = tag2id.find(tag); + //if (itr != tag2id.end()) + double* xj = x[j]; + double* Xj = xref_[j]; + //double Xj[3]; + double* Xi = xref_[i]; + // the assumption is that xref_[j] has been shuffled + // so make an image of xref_[i] that is close to x[j] + if (xj[0] <= xlo) Xj[0] = Xi[0] -Lx; + if (xj[0] >= xhi) Xj[0] = Xi[0] +Lx; + if (xj[1] <= ylo) Xj[1] = Xi[1] -Ly; + if (xj[1] >= yhi) Xj[1] = Xi[1] +Ly; + if (xj[2] <= zlo) Xj[2] = Xi[2] -Lz; + if (xj[2] >= zhi) Xj[2] = Xi[2] +Lz; + } + } +}; diff --git a/lib/atc/ATC_Method.h b/lib/atc/ATC_Method.h new file mode 100644 index 0000000000..79d00f54c3 --- /dev/null +++ b/lib/atc/ATC_Method.h @@ -0,0 +1,854 @@ +#ifndef ATC_METHOD_H +#define ATC_METHOD_H + +// ATC_Method headers +#include "ATC_TypeDefs.h" +#include "PhysicsModel.h" +#include "MatrixLibrary.h" +#include "Array.h" +#include "Array2D.h" +#include "OutputManager.h" +#include "Function.h" +#include "FE_Element.h" +#include "TimeFilter.h" +#include "LammpsInterface.h" +#include "FE_Engine.h" +#include "ExtrinsicModel.h" +#include "InterscaleOperators.h" +#include "TransferLibrary.h" + +// Other headers +#include +#include + +using namespace std; + + + +using LAMMPS_NS::Fix; + +namespace ATC { + + /** + * @class ATC_Method + * @brief Base class for atom-continuum coupling or transfer operators + */ + + class ATC_Method { + + public: /** methods */ + + /** constructor */ + ATC_Method(string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix); + + /** destructor */ + virtual ~ATC_Method(); + + string version() {return "2.0";} + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + void parse_field(/*const*/ char ** args, int &argIndex, + FieldName &thisField, int &thisIndex); + + /** initialize any computes that will be needed prior to the first timestep */ + virtual void init_computes() { + lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()); + }; + + /** pre integration run */ + virtual void initialize(); + + /** Predictor phase, executed before Verlet */ + virtual void pre_init_integrate() { + feEngine_->partition_mesh(); + update_step(); + }; + + /** Predictor phase, Verlet first step for velocity */ + virtual void init_integrate_velocity(){}; + + /** Predictor phase, executed between velocity and position Verlet */ + virtual void mid_init_integrate(){}; + + /** Predictor phase, Verlet first step for position */ + virtual void init_integrate_position(){}; + + /** Predictor phase, executed after Verlet */ + virtual void post_init_integrate(){}; + + /** Corrector phase, executed before Verlet */ + virtual void pre_final_integrate(){}; + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(){}; + + /** Corrector phase, executed after Verlet*/ + virtual void post_final_integrate(); + + /** post integration run : called at end of run or simulation */ + virtual void finish(); + + /** pre/post atomic force calculation */ + virtual void pre_force(){}; + + /** pre/post atomic force calculation in minimize */ + virtual void min_pre_force(){}; + virtual void min_post_force(); + + /** called at end of step for run or minimize */ + virtual void end_of_step(); + + //--------------------------------------------------------------- + /** \name memory management and processor information exchange */ + //--------------------------------------------------------------- + /*@{*/ + /** pre_exchange is our indicator that atoms have moved across processors */ + virtual void pre_exchange(); + void setup_pre_exchange(); + virtual void pre_neighbor(); + virtual void post_force(); + virtual int memory_usage(); + virtual void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int comm_forward(void) {return sizeComm_;} + int pack_comm(int , int *, double *, int, int *); + void unpack_comm(int, int, double *); + /*@}*/ + + //--------------------------------------------------------------- + /** \name managers */ + //--------------------------------------------------------------- + /*@{*/ + /** access to FE engine */ + const FE_Engine * fe_engine() const {return feEngine_;}; + /** access to interscale manager */ + InterscaleManager & interscale_manager() {return interscaleManager_;}; + /** access to lammps interface */ + + LammpsInterface const * lammps_interface() const {return lammpsInterface_;}; + /** access to time filter */ + TimeFilterManager * time_filter_manager() {return &timeFilterManager_;}; + /*@}*/ + + //--------------------------------------------------------------- + /** \name access methods for output computation data */ + //--------------------------------------------------------------- + /*@{*/ + /** compute scalar for output */ + virtual double compute_scalar() {return 0.;} + /** compute vector for output */ + virtual double compute_vector(int n) {return 0.;} + /** compute vector for output */ + virtual double compute_array(int irow, int icol) {return 0.;}; + int scalar_flag() const {return scalarFlag_;} + int vector_flag() const {return vectorFlag_;} + int size_vector() const {return sizeVector_;} + int peratom_flag() const {return sizePerAtomCols_ > 0;} + int size_peratom_cols() const {return sizePerAtomCols_;} + int peratom_freq() const {return 1;} + void set_peratom_pointer(double ** & ptr) { ptr = perAtomOutput_; } + int global_freq() const {return scalarVectorFreq_;}; + int extscalar() const {return extScalar_;}; + int extvector() const {return extVector_;}; + int * extlist() {return extList_;}; + int thermo_energy_flag() const {return thermoEnergyFlag_;}; + bool parallel_consistency() const {return parallelConsistency_;}; + + /** access to step number */ + int step() const {return stepCounter_;}; + double time() const {return simTime_;}; + double dt() const {return lammpsInterface_->dt();} + + /** time/step functions */ + bool sample_now(void) const + { + int s = step(); + bool now = ( (sampleFrequency_ > 0) && (s % sampleFrequency_ == 0)); + return now; + } + bool output_now(void) const + { + int s = step(); + + bool now = ( (outputFrequency_ > 0) && (s == 1 || s % outputFrequency_ == 0) ); + now = now || outputNow_; + return now; + } + double output_index(void) const + { + if (outputTime_) return time(); + else return step(); + } + + /** print tracked types and groups */ + int print_tracked() const + { + string msg = "species:\n"; + for(unsigned int i = 0; i < typeList_.size(); i++) { + msg+=" type:"+to_string(typeList_[i])+" name: "+ typeNames_[i]+"\n"; } + for(unsigned int i = 0; i < groupList_.size(); i++) { + msg+=" group (bit):"+to_string(groupList_[i])+" name: "+ groupNames_[i]+"\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(msg); + return typeList_.size()+groupList_.size(); + } + vector tracked_names() const + { + vector names(typeList_.size()+groupList_.size()); + int j = 0; + for(unsigned int i = 0; i < typeList_.size(); i++) { + names[j++] = typeNames_[i]; + } + for(unsigned int i = 0; i < groupList_.size(); i++) { + names[j++] = groupNames_[i]; + } + return names; + } + int tag_to_type(string tag) const { + for(unsigned int i = 0; i < typeList_.size(); i++) { + if (tag == typeNames_[i]) return typeList_[i]; + } + return -1; + } + int type_index(int t) const { + for(unsigned int i = 0; i < typeList_.size(); i++) { + if (t == typeList_[i]) return i; + } + return -1; + } + /*@}*/ + + //--------------------------------------------------------------- + /** \name Access methods for sizes */ + //--------------------------------------------------------------- + /*@{*/ + /** get number of unique FE nodes */ + int num_nodes() const {return nNodes_;}; + /** get number of spatial dimensions */ + int nsd() const {return nsd_;}; + /** get number of ATC internal atoms on this processor */ + int nlocal() const {return nLocal_;}; + /** get total number of LAMMPS atoms on this processor */ + int nlocal_total() const {return nLocalTotal_;}; + /** get number of ATC ghost atoms on this processor */ + int nlocal_ghost() const {return nLocalGhost_;}; + /** get the number of all LAMMPS real and parallel ghost atoms on this processor */ + int nproc_ghost() const {return nLocalTotal_ + lammpsInterface_->nghost();}; + /** get number of ATC internal atoms */ + int ninternal() const {return nInternal_;} + /** get number of ATC ghost atoms */ + int nghost() const {return nGhost_;}; + /** match group bits */ + bool is_ghost_group(int grpbit) { return (grpbit == groupbitGhost_); } + bool is_internal_group(int grpbit) { return (grpbit == groupbit_); } + unsigned int ntracked() { return typeList_.size()+groupList_.size(); } + bool has_tracked_species() { return typeList_.size()+groupList_.size() > 0; } + /*@}*/ + + virtual void initialize_mesh_data(void){meshDataInitialized_=true;} + + //--------------------------------------------------------------- + /** \name Access methods for data used by various methods */ + //--------------------------------------------------------------- + /*@{*/ + + + + /** access to name FE fields */ + DENS_MAN &field(FieldName thisField){return fields_[thisField];}; + /** access to FE field time derivatives */ + DENS_MAT &get_dot_field(FieldName thisField){return dot_fields_[thisField].set_quantity();}; + DENS_MAN &dot_field(FieldName thisField){return dot_fields_[thisField];}; + /** access to nodal fields of atomic variables */ + DENS_MAT &get_atomic_field(FieldName thisField) + { return nodalAtomicFields_[thisField].set_quantity(); }; + DENS_MAN &nodal_atomic_field(FieldName thisField) + { return nodalAtomicFields_[thisField]; }; + /** access to all fields */ + FIELDS &fields() {return fields_;}; + /** access to all fields rates of change (roc) */ + FIELDS &fields_roc() {return dot_fields_;}; + /** add a new field */ + void add_fields(map & newFieldSizes); + /** access FE rate of change */ + DENS_MAT &get_field_roc(FieldName thisField) + { return dot_fields_[thisField].set_quantity(); }; + DENS_MAN &field_roc(FieldName thisField) + { return dot_fields_[thisField]; }; + /** access atomic rate of change contributions to finite element equation */ + + DENS_MAT &get_nodal_atomic_field_roc(FieldName thisField) + { return nodalAtomicFieldsRoc_[thisField].set_quantity(); }; + DENS_MAN &nodal_atomic_field_roc(FieldName thisField) + { return nodalAtomicFieldsRoc_[thisField]; }; + /** access to second time derivative (2roc) */ + + DENS_MAT &get_field_2roc(FieldName thisField) + { return ddot_fields_[thisField].set_quantity(); }; + DENS_MAN &field_2roc(FieldName thisField) + { return ddot_fields_[thisField]; }; + /** access to third time derivative (3roc) */ + DENS_MAT &get_field_3roc(FieldName thisField) + { return dddot_fields_[thisField].set_quantity(); }; + DENS_MAN &field_3roc(FieldName thisField) + { return dddot_fields_[thisField]; }; + /** group bit */ + int groupbit() {return groupbit_;}; + /** group bit for ghosts */ + int groupbit_ghost() {return groupbitGhost_;}; + /** internal atom to global map */ + const Array &internal_to_atom_map() {return internalToAtom_;}; + /** ghost atom to global map */ + const Array &ghost_to_atom_map() {return ghostToAtom_;}; + const map & atom_to_internal_map() {return atomToInternal_;}; + /** access to xref */ + double ** xref() {return xref_;}; + /** access to faceset names */ + const set &faceset(const string & name) const {return (feEngine_->fe_mesh())->faceset(name);}; + DENS_VEC copy_nodal_coordinates(int i) const { return feEngine_->fe_mesh()->nodal_coordinates(i); } + /** access to set of DENS_MANs accessed by tagging */ + DENS_MAN & tagged_dens_man(const string & tag) {return taggedDensMan_[tag];}; + /** access to atom to element type map */ + AtomToElementMapType atom_to_element_map_type() {return atomToElementMapType_;}; + /** access to atom to element update frequency */ + int atom_to_element_map_frequency() {return atomToElementMapFrequency_;}; + /** flag on whether atc is initialized */ + bool is_initialized() const {return initialized_;}; + /** step number within a run or minimize */ + int local_step() const {return localStep_;}; + /** flags whether a methods reset is required */ + virtual bool reset_methods() const {return (!initialized_) || timeFilterManager_.need_reset() || timeFilterManager_.end_equilibrate();}; + /** sizes of each field being considered */ + const map & field_sizes() {return fieldSizes_;}; + /*@}*/ + /** compute the consistent MD mass matrix */ + void compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix, + SPAR_MAT & mdMassMatrix) const; + /** access to species ids */ + const map > & species_ids() const {return speciesIds_;}; + /** access to molecule ids */ + const map > & molecule_ids() const {return moleculeIds_;}; + + //---------------------------------------------------------------- + /** \name mass matrix operations */ + //---------------------------------------------------------------- + // inverted using GMRES + void apply_inverse_mass_matrix(MATRIX & data, FieldName thisField) + { + + if (useConsistentMassMatrix_(thisField)) { + //data = consistentMassInverse_*data; + data = (consistentMassMatsInv_[thisField].quantity())*data; + return; + } + data = (massMatsInv_[thisField].quantity())*data; + }; + /** multiply inverse mass matrix times given data and return result */ + void apply_inverse_mass_matrix(const MATRIX & data_in, MATRIX & data_out, + FieldName thisField) + { + if (useConsistentMassMatrix_(thisField)) { + //data_out = consistentMassInverse_*data_in; + data_out = (consistentMassMatsInv_[thisField].quantity())*data_in; + return; + } + data_out = (massMatsInv_[thisField].quantity())*data_in; + }; + void apply_inverse_md_mass_matrix(const MATRIX & data_in, MATRIX & data_out, + FieldName thisField) + { data_out = (massMatsMdInv_[thisField].quantity())*data_in; }; + + + DIAG_MAN &mass_mat(FieldName thisField) + { return massMats_[thisField];}; + + //--------------------------------------------------------------- + /** \name mass matrices */ + //--------------------------------------------------------------- + /*@{*/ + /** access to mass matrices */ + /** access to inverse mass matrices */ + DIAG_MAT &get_mass_mat_inv(FieldName thisField) + { return massMatsInv_[thisField].set_quantity();}; + DIAG_MAN &mass_mat_inv(FieldName thisField) + { return massMatsInv_[thisField];}; + /** nodal volumes associated with the atoms, used for the atomic mass matrix */ + AdmtfShapeFunctionRestriction * nodalAtomicVolume_; + + void register_mass_matrix_dependency(DependencyManager * dependent, + FieldName thisField) + { + if (useConsistentMassMatrix_(thisField)) { + consistentMassMatsInv_[thisField].register_dependence(dependent); + return; + } + massMatsInv_[thisField].register_dependence(dependent); + }; + + void apply_inverse_md_mass_matrix(MATRIX & data, FieldName thisField) + { data = (massMatsMdInv_[thisField].quantity())*data; }; + void register_md_mass_matrix_dependency(DependencyManager * dependent, + FieldName thisField) + {massMatsMdInv_[thisField].register_dependence(dependent);} +// /** determine weighting method for atomic integration */ +// void compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix, + // SPAR_MAT & mdMassMatrix); + virtual void compute_md_mass_matrix(FieldName thisField, + DIAG_MAT & massMat) {}; +/** access to md mass matrices */ + + DIAG_MAN &mass_mat_md_inv(FieldName thisField) + { return massMatsMdInv_[thisField];}; + DIAG_MAN &set_mass_mat_md(FieldName thisField) + { return massMatsMd_[thisField]; }; + const DIAG_MAN &mass_mat_md(FieldName thisField) const + { + MASS_MATS::const_iterator man = massMatsMd_.find(thisField); + if (man == massMatsMd_.end() ) { + string msg = " MD mass for " + field_to_string(thisField) + " does not exist"; + throw ATC_Error(msg); + } + return man->second; + }; + /*@}*/ + + //---------------------------------------------------------------- + /** \name Interscale operators */ + //---------------------------------------------------------------- + bool use_md_mass_normalization() const { return mdMassNormalization_;} + bool kernel_based() { return kernelBased_; } + bool kernel_on_the_fly() const { return kernelOnTheFly_;} + bool has_kernel_function() { return kernelFunction_ != NULL; } + KernelFunction * kernel_function() { return kernelFunction_; } + vector & type_list() { return typeList_; } + vector & group_list() { return groupList_; } + SPAR_MAN* interpolant() { return shpFcn_; } + SPAR_MAN* accumulant() { return accumulant_; } + DIAG_MAN* accumulant_weights() { return accumulantWeights_;} + DIAG_MAN* accumulant_inverse_volumes() { return accumulantInverseVolumes_; } + PerAtomQuantity * atom_coarsegraining_positions() { return atomCoarseGrainingPositions_; } + PerAtomQuantity * atom_reference_positions() { return atomReferencePositions_; } + PerAtomQuantity * atom_to_element_map() { return atomElement_;} + + double ke_scale() { return keScale_; } + double pe_scale() { return peScale_; } + + /** from a atom group, find the nodes that have non-zero shape function contributions */ + + bool nodal_influence(const int groupbit, set& nset, set& aset, double tol =1.e-8); + int nodal_influence(const int groupbit, set& nset, set& aset, + bool ghost, + double tol =1.e-8); + /*@{*/ + + + + /** Restrict based on atomic volume integration for volumetric quantities : given w_\alpha, w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + void restrict_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData); + void restrict_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + const SPAR_MAT & shpFcn); + + /** Prolong : given w_I, w_\alpha = \sum_I N_{I\alpha} w_I */ + void prolong(const MATRIX &nodeData, MATRIX &atomData); + + //--------------------------------------------------------------- + /** \name quadrature weights */ + //--------------------------------------------------------------- + PerAtomDiagonalMatrix * create_atom_volume(); + + //--------------------------------------------------------------- + /** \name access to potential energy reference */ + //--------------------------------------------------------------- + /*@{*/ + bool has_ref_pe(void) const { return hasRefPE_; } + const DENS_MAT * nodal_ref_potential_energy(void) { return &nodalRefPotentialEnergy_; } + + protected: /** methods */ + /** time functions */ + void set_time(double t=0) {simTime_=t;}; + void update_time(double alpha = 1.0) + { + double dt = lammpsInterface_->dt(); + simTime_ += alpha*dt; + if (dt == 0.0) simTime_ = stepCounter_; + } + // note step counter different than lammps step e.g. min + void update_step(void) { ++stepCounter_; } + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** gets baseline data from continuum model */ + virtual void set_continuum_data(); + /** sets up all data necessary to define the computational geometry */ + virtual void set_computational_geometry(); + /** constructs all data which is updated with time integration, i.e. fields */ + virtual void construct_time_integration_data() = 0; + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods() = 0; + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + /** update the peratom output pointers */ + void update_peratom_output(void); + + virtual void read_restart_data(string fileName_, RESTART_LIST & data); + virtual void write_restart_data(string fileName_, RESTART_LIST & data); + void pack_fields(RESTART_LIST & data); + + /** mass matrices */ + MASS_MATS massMats_; + MASS_MATS massMatsInv_; + MASS_MATS massMatsMd_; + MASS_MATS massMatsMdInstantaneous_; + MASS_MATS massMatsMdInv_; + MASS_MATS massMatsFE_; + MASS_MATS massMatsAq_; + MASS_MATS massMatsAqInstantaneous_; + Array useConsistentMassMatrix_; + map consistentMassMats_; + map consistentMassMatsInv_; + map massMatTimeFilters_; + + //--------------------------------------------------------------- + /** \name quadrature weight function */ + //--------------------------------------------------------------- + /*@{*/ + void write_atomic_weights(const string filename,const DIAG_MAT & atomicVolumeMatrix); + + /** resets shape function matrices based on atoms on this processor */ + virtual void reset_nlocal(); + virtual void reset_coordinates(); + /*@}*/ + + /** re-read reference positions */ + bool read_atomic_ref_positions(const char * filename); + void remap_ghost_ref_positions(void); + void adjust_xref_pbc(); + + //--------------------------------------------------------------- + /** \name output functions */ + //--------------------------------------------------------------- + /*@{*/ + virtual void output(); + void compute_nodeset_output(void); + void compute_faceset_output(void); + void compute_elementset_output(void); + /*@}*/ + + //--------------------------------------------------------------- + /** \name types, groups, and molecules */ + //--------------------------------------------------------------- + /*@{*/ + /** map from species string tag to LAMMPS type id or group bit */ + map > speciesIds_; // OBSOLETE + map > moleculeIds_; + /** a list of lammps types & groups ATC tracks */ + vector typeNames_; + vector groupNames_; + vector typeList_; + vector groupList_; + /*@}*/ + + void reset_fields(); + + private: /** methods */ + ATC_Method(); // do not define + + protected: /** data */ + + /* parsed input requires changes */ + bool needReset_; + + // managers + /** pointer to lammps interface class */ + LammpsInterface * lammpsInterface_; + + /** manager for atomic quantities and interscale operations */ + InterscaleManager interscaleManager_; + + TimeFilterManager timeFilterManager_; + + /** finite element handler */ + FE_Engine * feEngine_; + + // status flags + /** flag on if initialization has been performed */ + bool initialized_; + bool meshDataInitialized_; + + /** counter for steps of a run or minimize */ + int localStep_; + + // sizes + /** size of per atom communication */ + int sizeComm_; + + /** atomic coordinates for coarse graining */ + PerAtomQuantity * atomCoarseGrainingPositions_; + PerAtomQuantity * atomGhostCoarseGrainingPositions_; + PerAtomQuantity * atomProcGhostCoarseGrainingPositions_; + PerAtomQuantity * atomReferencePositions_; + + + /** number of unique FE nodes */ + int nNodes_; + + /** Number of Spatial Dimensions */ + int nsd_; +#ifdef EXTENDED_ERROR_CHECKING + /** data for handling atoms crossing processors */ + bool atomSwitch_; +#endif + /** reference position of the atoms */ + double ** xref_; + bool readXref_; + bool needXrefProcessorGhosts_; + string xRefFile_; + + /** flag for tracking displacements or not, depending on physics */ + bool trackDisplacement_; + + /** map from reference positions to element id, pointer is to internal only */ + + bool needsAtomToElementMap_; + PerAtomQuantity * atomElement_; + PerAtomQuantity * atomGhostElement_; + + /** atomic ATC material tag */ + + + double Xprd_,Yprd_,Zprd_; // lengths of periodic box in reference frame + double XY_,YZ_,XZ_; + double boxXlo_,boxXhi_; // lo/hi bounds of periodic box in reference frame + double boxYlo_,boxYhi_; // lo/hi bounds of periodic box in reference frame + double boxZlo_,boxZhi_; // lo/hi bounds of periodic box in reference frame + // next data members are for consistency with existing ATC_Transfer, but are redundant and do not + // conform to naming standards, and should be accessible through the mesh + /** periodicity flags and lengths */ + int periodicity[3]; + double box_bounds[2][3]; + double box_length[3]; + + /** pointers to needed atom quantities and transfers */ + FundamentalAtomQuantity * atomMasses_; + FundamentalAtomQuantity * atomPositions_; + FundamentalAtomQuantity * atomVelocities_; + FundamentalAtomQuantity * atomForces_; + + //--------------------------------------------------------------- + /** \name output data */ + //--------------------------------------------------------------- + /*@{*/ + + //private: + bool parallelConsistency_; + + /** base name for output files */ + string outputPrefix_; + + /** output flag */ + + bool outputNow_; + /** output time or step (for lammps compatibility) */ + bool outputTime_; + + /** output frequency */ + int outputFrequency_; + + /** sample frequency */ + int sampleFrequency_; + + /** sample counter */ + int sampleCounter_; + + TAG_FIELDS filteredData_; + + double peScale_,keScale_; + + //protected: + /*@}*/ + + //--------------------------------------------------------------- + /** \name member data related to compute_scalar() and compute_vector() */ + //--------------------------------------------------------------- + /*@{*/ + int scalarFlag_; // 0/1 if compute_scalar() function exists + int vectorFlag_; // 0/1 if compute_vector() function exists + int sizeVector_; // N = size of global vector + int scalarVectorFreq_; // frequency compute s/v data is available at + int sizePerAtomCols_; // N = size of per atom vector to dump + + double **perAtomOutput_; // per atom data + double **&perAtomArray_; // per atom data + int extScalar_; // 0/1 if scalar is intensive/extensive + int extVector_; // 0/1/-1 if vector is all int/ext/extlist + int *extList_; // list of 0/1 int/ext for each vec component + int thermoEnergyFlag_; // 0/1 if fix adds to overall energy + /*@}*/ + + //--------------------------------------------------------------- + /** \name fields and necessary data for FEM */ + //--------------------------------------------------------------- + /*@{*/ + map fieldSizes_; + FIELDS fields_; + /*@}*/ + + + //--------------------------------------------------------------- + /** \name time integration and filtering fields */ + //--------------------------------------------------------------- + /*@{*/ + + FIELDS dot_fields_; + FIELDS ddot_fields_; + FIELDS dddot_fields_; + + /** Restricted Fields */ + + FIELDS nodalAtomicFields_; // replaces fieldNdFiltered_ + FIELDS nodalAtomicFieldsRoc_; + + /*@}*/ + + //--------------------------------------------------------------- + /** \name quadrature weights */ + //--------------------------------------------------------------- + /*@{*/ + + DIAG_MAT NodeVolumes_; + DIAG_MAN invNodeVolumes_; + /** atomic quadrature integration weights (V_\alpha) */ + ProtectedAtomDiagonalMatrix * atomVolume_; + string atomicWeightsFile_; + bool atomicWeightsWriteFlag_; + int atomicWeightsWriteFrequency_; + double atomicVolume_; // global atomic volume for homogeneous set of atoms + map Valpha_; + AtomicWeightType atomWeightType_; + /*@}*/ + + //--------------------------------------------------------------- + /** \name domain decomposition */ + //--------------------------------------------------------------- + /*@{*/ + DomainDecompositionType domainDecomposition_; + /*@}*/ + + //--------------------------------------------------------------- + /** \name atom data */ + //--------------------------------------------------------------- + /*@{*/ + /** bitwise comparisons for boundary (ghost) atoms */ + int groupbit_; + int groupbitGhost_; + bool needProcGhost_; + string groupTag_; + string groupTagGhost_; + + /** number of atoms of correct type, + ghosts are atoms outside our domain of interest + boundary are atoms contributing to boundary flux terms */ + /** Number of "internal" atoms on this processor */ + int nLocal_; + /** Number of atoms on this processor */ + int nLocalTotal_; + int nLocalGhost_; + int nInternal_; + int nGhost_; + Array internalToAtom_; + std::map atomToInternal_; + Array ghostToAtom_; + /*@}*/ + //---------------------------------------------------------------- + /** \name maps and masks */ + //---------------------------------------------------------------- + /*@{*/ + + AtomToElementMapType atomToElementMapType_; + int atomToElementMapFrequency_; + int regionID_; + /*@}*/ + + //---------------------------------------------------------------- + /** \name shape function matrices */ + //---------------------------------------------------------------- + /*@{*/ + // sparse matrix where columns correspond to global node numbering + SPAR_MAN * shpFcn_; + VectorDependencyManager * shpFcnDerivs_; + SPAR_MAN * shpFcnGhost_; + VectorDependencyManager * shpFcnDerivsGhost_; + /** map from species string tag to the species density */ + map taggedDensMan_; + + /** weighted shape function matrices at overlap nodes + for use with thermostats */ + SPAR_MAN NhatOverlap_; + /*@}*/ + + //---------------------------------------------------------------- + /** \name accumulant matrices */ + //---------------------------------------------------------------- + /*@{*/ + /** compute kernel shape functions on-the-fly w/o storing N_Ia */ + bool mdMassNormalization_; + bool kernelBased_; + bool kernelOnTheFly_; + class KernelFunction * kernelFunction_; + bool bondOnTheFly_; + SPAR_MAN* accumulant_; + SPAR_MAN* accumulantMol_; // KKM add + SPAR_MAN* accumulantMolGrad_; // KKM add + SPAR_MAN kernelAccumulantMol_; // KKM add + SPAR_MAN kernelAccumulantMolGrad_; // KKM add + DIAG_MAN* accumulantWeights_; + DIAG_MAN* accumulantInverseVolumes_; + /*@}*/ + + + //--------------------------------------------------------------- + /** \name restart procedures */ + //--------------------------------------------------------------- + bool useRestart_; + string restartFileName_; + + //--------------------------------------------------------------- + /** \name data specific to node/faceset for global output */ + //--------------------------------------------------------------- + /** group computes : type, group_id -> value */ + map< pair < string, FieldName > , NodesetOperationType> nsetData_; + map < pair, FacesetIntegralType > fsetData_; + map < pair,ElementsetOperationType > esetData_; + + + //--------------------------------------------------------------- + /** \name reference data */ + //--------------------------------------------------------------- + bool hasRefPE_; + bool setRefPE_; + bool setRefPEvalue_; + double refPEvalue_; + bool readRefPE_; + string nodalRefPEfile_; + DENS_MAT nodalRefPotentialEnergy_; + void set_reference_potential_energy(void); + + private: /** data */ + /** current time in simulation */ + double simTime_; + + /** step counter */ + int stepCounter_; + + }; + +}; + +#endif diff --git a/lib/atc/ATC_Transfer.cpp b/lib/atc/ATC_Transfer.cpp new file mode 100644 index 0000000000..01e9f9066c --- /dev/null +++ b/lib/atc/ATC_Transfer.cpp @@ -0,0 +1,2064 @@ +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "ATC_Error.h" +#include "FE_Engine.h" +#include "LammpsInterface.h" +#include "Quadrature.h" +#include "VoigtOperations.h" +#include "TransferLibrary.h" +#include "Stress.h" +#include "KernelFunction.h" +#include "PerPairQuantity.h" +#include "FieldManager.h" +#define ESHELBY_VIRIAL +#include "LinearSolver.h" + + +// Other Headers +#include +#include +#include +#include +#include +#include +#include + + + +// PLAN: +//* energies +//* filters - make filterFields class +//* output directly +//* enum, tagged, computes, mat(field to field) functions +//* grads & rates +//* on-the-fly +// * remove derived classes + +using namespace std; +using namespace ATC_Utility; +using namespace voigt3; +//using ATC_Utility::to_string; +//using voigt3::vector_to_matrix; +//using voigt3::vector_to_symm_matrix; +//using voigt3::matrix_to_vector; +//using voigt3::symm_matrix_to_vector; + +namespace ATC { + + const int numFields_ = 16; + FieldName indices_[numFields_] = { + CHARGE_DENSITY, + MASS_DENSITY, + SPECIES_CONCENTRATION, + NUMBER_DENSITY, + MOMENTUM, + VELOCITY, + PROJECTED_VELOCITY, + DISPLACEMENT, + POTENTIAL_ENERGY, + KINETIC_ENERGY, + KINETIC_TEMPERATURE, + TEMPERATURE, + CHARGE_FLUX, + SPECIES_FLUX, + THERMAL_ENERGY}; + //ELECTRIC_POTENTIAL}; + + ATC_Transfer::ATC_Transfer(string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile) + : ATC_Method(groupName,perAtomArray,thisFix), + xPointer_(NULL), + outputStepZero_(true), + neighborReset_(false), + pairMap_(NULL), + bondMatrix_(NULL), + pairVirial_(NULL), + pairHeatFlux_(NULL), + nComputes_(0), + hasPairs_(true), + hasBonds_(false), + resetKernelFunction_(false), + dxaExactMode_(true), + cauchyBornStress_(NULL) + { + nTypes_ = lammpsInterface_->ntypes(); + + peScale_=1.; + keScale_= lammpsInterface_->mvv2e(); + // if surrogate model of md (no physics model created) + if (matParamFile != "none") { + fstream fileId(matParamFile.c_str(), std::ios::in); + if (!fileId.is_open()) throw ATC_Error("cannot open material file"); + CbData cb; + LammpsInterface *lmp = LammpsInterface::instance(); + lmp->lattice(cb.cell_vectors, cb.basis_vectors); + cb.inv_atom_volume = 1.0 / lmp->volume_per_atom(); + cb.e2mvv = 1.0 / lmp->mvv2e(); + cb.atom_mass = lmp->atom_mass(1); + cb.boltzmann = lmp->boltz(); + cb.hbar = lmp->hbar(); + cauchyBornStress_ = new StressCauchyBorn(fileId, cb); + } + + // Defaults + set_time(); + + outputFlags_.reset(NUM_TOTAL_FIELDS); + outputFlags_ = false; + fieldFlags_.reset(NUM_TOTAL_FIELDS); + fieldFlags_ = false; + gradFlags_.reset(NUM_TOTAL_FIELDS); + gradFlags_ = false; + rateFlags_.reset(NUM_TOTAL_FIELDS); + rateFlags_ = false; + + outputFields_.resize(NUM_TOTAL_FIELDS); + for (int i = 0; i < NUM_TOTAL_FIELDS; i++) { outputFields_[i] = NULL; } + + // Hardy requires ref positions for processor ghosts for bond list + + //needXrefProcessorGhosts_ = true; + } + + //------------------------------------------------------------------- + ATC_Transfer::~ATC_Transfer() + { + interscaleManager_.clear(); + if (cauchyBornStress_) delete cauchyBornStress_; + } + + //------------------------------------------------------------------- + // called before the beginning of a "run" + void ATC_Transfer::initialize() + { + ATC_Method::initialize(); + + if (!initialized_) { + if (cauchyBornStress_) cauchyBornStress_->initialize(); + } + + if (!initialized_ || ATC::LammpsInterface::instance()->atoms_sorted() || resetKernelFunction_) { + // initialize kernel funciton matrix N_Ia + if (! kernelOnTheFly_) { + try{ + if (!moleculeIds_.empty()) compute_kernel_matrix_molecule(); //KKM add + } + catch(bad_alloc&) { + ATC::LammpsInterface::instance()->print_msg("kernel will be computed on-the-fly"); + kernelOnTheFly_ = true; + } + } + resetKernelFunction_ = false; + } + + // initialize bond matrix B_Iab + if ((! bondOnTheFly_) + && ( ( fieldFlags_(STRESS) + || fieldFlags_(ESHELBY_STRESS) + || fieldFlags_(HEAT_FLUX) ) ) ) { + try { + compute_bond_matrix(); + } + catch(bad_alloc&) { + ATC::LammpsInterface::instance()->print_msg("stress/heat_flux will be computed on-the-fly"); + + bondOnTheFly_ = true; + } + } + + // set sample frequency to output if sample has not be specified + if (sampleFrequency_ == 0) sampleFrequency_ = outputFrequency_; + + // output for step 0 + if (!initialized_) { + if (outputFrequency_ > 0) { + // initialize filtered data + compute_fields(); + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if(fieldFlags_(index)) { + string name = field_to_string((FieldName) index); + filteredData_[name] = hardyData_[name]; + timeFilters_(index)->initialize(filteredData_[name].quantity()); + } + if (rateFlags_(index)) { + string name = field_to_string((FieldName) index); + string rate_field = name + "_rate"; + filteredData_[rate_field] = hardyData_[rate_field]; + } + if (gradFlags_(index)) { + string name = field_to_string((FieldName) index); + string grad_field = name + "_gradient"; + filteredData_[grad_field] = hardyData_[grad_field]; + } + } + int index = NUM_TOTAL_FIELDS; + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + filteredData_[tag] = hardyData_[tag]; + timeFilters_(index)->initialize(filteredData_[tag].quantity()); +#ifdef ESHELBY_VIRIAL + if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) { + filteredData_["eshelby_virial"] = hardyData_["eshelby_virial"]; + } +#endif + index++; + } + output(); + } + } + + initialized_ = true; + + lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+sampleFrequency_); + + + //remap_ghost_ref_positions(); + update_peratom_output(); + } + + //------------------------------------------------------------------- + void ATC_Transfer::set_continuum_data() + { + ATC_Method::set_continuum_data(); + if (!initialized_) { + nNodesGlobal_ = feEngine_->fe_mesh()->num_nodes(); + } + } + + //------------------------------------------------------------------- + void ATC_Transfer::construct_time_integration_data() + { + if (!initialized_) { + // ground state for PE + nodalRefPotentialEnergy_.reset(nNodes_,1); + + // size arrays for requested/required fields + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (fieldFlags_(index)) { + + int size = FieldSizes[index]; + if (atomToElementMapType_ == EULERIAN) { + if (index == STRESS) size=6; + if (index == CAUCHY_BORN_STRESS) size=6; + } + if (size == 0) { + if (index == SPECIES_CONCENTRATION) size=typeList_.size()+groupList_.size(); + } + string name = field_to_string((FieldName) index); + hardyData_ [name].reset(nNodes_,size); + filteredData_[name].reset(nNodes_,size); + } + } + + // size arrays for projected compute fields + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + COMPUTE_POINTER cmpt = lammpsInterface_->compute_pointer(tag); + int ncols = lammpsInterface_->compute_ncols_peratom(cmpt); + hardyData_ [tag].reset(nNodes_,ncols); + filteredData_[tag].reset(nNodes_,ncols); +#ifdef ESHELBY_VIRIAL + if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) { + string esh = "eshelby_virial"; + int size = FieldSizes[ESHELBY_STRESS]; + hardyData_ [esh].reset(nNodes_,size); + filteredData_[esh].reset(nNodes_,size); + } +#endif + } + } + } + //-------------------------------------------------------- + // set_computational_geometry + // constructs needed transfer operators which define + // hybrid atom/FE computational geometry + //-------------------------------------------------------- + void ATC_Transfer::set_computational_geometry() + { + ATC_Method::set_computational_geometry(); + } + + //------------------------------------------------------------------- + // constructs quantities + void ATC_Transfer::construct_transfers() + { + + // set pointer to positions + // REFACTOR use method's handling of xref/xpointer + set_xPointer(); + + ATC_Method::construct_transfers(); + + if (!(kernelOnTheFly_)) { + // finite element shape functions for interpolants + PerAtomShapeFunction * atomShapeFunctions = new PerAtomShapeFunction(this); + interscaleManager_.add_per_atom_sparse_matrix(atomShapeFunctions,"Interpolant"); + shpFcn_ = atomShapeFunctions; + } + + + this->create_atom_volume(); + + // accumulants + if (kernelFunction_) { + // kernel-based accumulants + if (kernelOnTheFly_) { + ConstantQuantity * atomCount = new ConstantQuantity(this,1.); + interscaleManager_.add_per_atom_quantity(atomCount,"AtomCount"); + OnTheFlyKernelAccumulation * myWeights + = new OnTheFlyKernelAccumulation(this, + atomCount, kernelFunction_, atomCoarseGrainingPositions_); + interscaleManager_.add_dense_matrix(myWeights, + "KernelInverseWeights"); + accumulantWeights_ = new OnTheFlyKernelWeights(myWeights); + } + else { + PerAtomKernelFunction * atomKernelFunctions = new PerAtomKernelFunction(this); + interscaleManager_.add_per_atom_sparse_matrix(atomKernelFunctions, + "Accumulant"); + accumulant_ = atomKernelFunctions; + accumulantWeights_ = new AccumulantWeights(accumulant_); + } + accumulantInverseVolumes_ = new KernelInverseVolumes(this,kernelFunction_); + interscaleManager_.add_diagonal_matrix(accumulantInverseVolumes_, + "AccumulantInverseVolumes"); + interscaleManager_.add_diagonal_matrix(accumulantWeights_, + "AccumulantWeights"); + } + else { + // mesh-based accumulants + if (kernelOnTheFly_) { + ConstantQuantity * atomCount = new ConstantQuantity(this,1.); + interscaleManager_.add_per_atom_quantity(atomCount,"AtomCount"); + OnTheFlyMeshAccumulation * myWeights + = new OnTheFlyMeshAccumulation(this, + atomCount, atomCoarseGrainingPositions_); + interscaleManager_.add_dense_matrix(myWeights, + "KernelInverseWeights"); + accumulantWeights_ = new OnTheFlyKernelWeights(myWeights); + } else { + accumulant_ = shpFcn_; + accumulantWeights_ = new AccumulantWeights(accumulant_); + interscaleManager_.add_diagonal_matrix(accumulantWeights_, + "AccumulantWeights"); + } + } + + + bool needsBondMatrix = (! bondOnTheFly_ ) && + (fieldFlags_(STRESS) + || fieldFlags_(ESHELBY_STRESS) + || fieldFlags_(HEAT_FLUX)); + if (needsBondMatrix) { + if (hasPairs_ && hasBonds_) { + pairMap_ = new PairMapBoth(lammpsInterface_,groupbit_); + } + else if (hasBonds_) { + pairMap_ = new PairMapBond(lammpsInterface_,groupbit_); + } + else if (hasPairs_) { + pairMap_ = new PairMapNeighbor(lammpsInterface_,groupbit_); + } + } + if (pairMap_) interscaleManager_.add_pair_map(pairMap_,"PairMap"); + //if (pairMap_ && !initialized_) interscaleManager_.add_pair_map(pairMap_,"PairMap"); + + + //const PerAtomQuantity * x0= interscaleManager_.per_atom_quantity("AtomicReferencePositions"); + //const PerAtomQuantity * x0= interscaleManager_.per_atom_quantity("AtomicCoarseGrainingPositions"); + //const PerAtomQuantity * x0= interscaleManager_.per_atom_quantity("AtomicReferencePositions"); + + if ( fieldFlags_(STRESS) || fieldFlags_(ESHELBY_STRESS) || fieldFlags_(HEAT_FLUX) ) { + + const FE_Mesh * fe_mesh = feEngine_->fe_mesh(); + if (!kernelBased_) { + bondMatrix_ = new BondMatrixPartitionOfUnity(lammpsInterface_,*pairMap_,xPointer_,fe_mesh,accumulantInverseVolumes_); + } + else { + bondMatrix_ = new BondMatrixKernel(lammpsInterface_,*pairMap_,xPointer_,fe_mesh,kernelFunction_); + } + } + if (bondMatrix_) interscaleManager_.add_sparse_matrix(bondMatrix_,"BondMatrix"); + + if ( fieldFlags_(STRESS) || fieldFlags_(ESHELBY_STRESS) ) { + if (atomToElementMapType_ == LAGRANGIAN) { +// pairVirial_ = new PairVirialLagrangian(lammpsInterface_,*pairMap_,x0); + pairVirial_ = new PairVirialLagrangian(lammpsInterface_,*pairMap_,xref_); + } + else if (atomToElementMapType_ == EULERIAN) { + pairVirial_ = new PairVirialEulerian(lammpsInterface_,*pairMap_); + } + else { + throw ATC_Error("no atom to element map specified"); + } + } + if (pairVirial_) interscaleManager_.add_dense_matrix(pairVirial_,"PairVirial"); + + if ( fieldFlags_(HEAT_FLUX) ) { + if (atomToElementMapType_ == LAGRANGIAN) { + pairHeatFlux_ = new PairPotentialHeatFluxLagrangian(lammpsInterface_,*pairMap_,xref_); + } + else if (atomToElementMapType_ == EULERIAN) { + pairHeatFlux_ = new PairPotentialHeatFluxEulerian(lammpsInterface_,*pairMap_); + } + else { + throw ATC_Error("no atom to element map specified"); + } + } + if (pairHeatFlux_) interscaleManager_.add_dense_matrix(pairHeatFlux_,"PairHeatFlux"); + + // gradient matrix + if (gradFlags_.has_member(true)) { + NativeShapeFunctionGradient * gradientMatrix = new NativeShapeFunctionGradient(this); + interscaleManager_.add_vector_sparse_matrix(gradientMatrix,"GradientMatrix"); + gradientMatrix_ = gradientMatrix; + } + + // molecule centroid, molecule charge, dipole moment and quadrupole moment calculations KKM add + if (!moleculeIds_.empty()) { + map >::const_iterator molecule; + InterscaleManager & interscaleManager = this->interscale_manager(); // KKM add, may be we do not need this as interscaleManager_ already exists. + PerAtomQuantity * atomProcGhostCoarseGrainingPositions_ = interscaleManager.per_atom_quantity("AtomicProcGhostCoarseGrainingPositions"); + FundamentalAtomQuantity * mass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS,PROC_GHOST); + molecule = moleculeIds_.begin(); + int groupbit = (molecule->second).second; + smallMoleculeSet_ = new SmallMoleculeSet(this,groupbit); + smallMoleculeSet_->initialize(); // KKM add, why should we? + interscaleManager_.add_small_molecule_set(smallMoleculeSet_,"MoleculeSet"); + moleculeCentroid_ = new SmallMoleculeCentroid(this,mass,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_); + interscaleManager_.add_dense_matrix(moleculeCentroid_,"MoleculeCentroid"); + AtomToSmallMoleculeTransfer * moleculeMass = + new AtomToSmallMoleculeTransfer(this,mass,smallMoleculeSet_); + interscaleManager_.add_dense_matrix(moleculeMass,"MoleculeMass"); + FundamentalAtomQuantity * atomicCharge = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE,PROC_GHOST); + AtomToSmallMoleculeTransfer * moleculeCharge = + new AtomToSmallMoleculeTransfer(this,atomicCharge,smallMoleculeSet_); + interscaleManager_.add_dense_matrix(moleculeCharge,"MoleculeCharge"); + dipoleMoment_ = new SmallMoleculeDipoleMoment(this,atomicCharge,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_,moleculeCentroid_); + interscaleManager_.add_dense_matrix(dipoleMoment_,"DipoleMoment"); + quadrupoleMoment_ = new SmallMoleculeQuadrupoleMoment(this,atomicCharge,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_,moleculeCentroid_); + interscaleManager_.add_dense_matrix(quadrupoleMoment_,"QuadrupoleMoment"); + } + + FieldManager fmgr(this); + +// for(int index=0; index < NUM_TOTAL_FIELDS; ++index) + for(int i=0; i < numFields_; ++i) { + FieldName index = indices_[i]; + if (fieldFlags_(index)) { + outputFields_[index] = fmgr.nodal_atomic_field(index); + } + } + +// WIP REJ + if (fieldFlags_(ELECTRIC_POTENTIAL)) { + restrictedCharge_ = fmgr.restricted_atom_quantity(CHARGE_DENSITY); + } + + // computes + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + ComputedAtomQuantity * c = new ComputedAtomQuantity(this, tag); + interscaleManager_.add_per_atom_quantity(c,tag); + int projection = iter->second; + DIAG_MAN * w = NULL; + if (projection == VOLUME_NORMALIZATION ) + { w = accumulantInverseVolumes_; } + else if (projection == NUMBER_NORMALIZATION ) + { w = accumulantWeights_; } + if (kernelFunction_ && kernelOnTheFly_) { + OnTheFlyKernelAccumulationNormalized * C = new OnTheFlyKernelAccumulationNormalized(this, c, kernelFunction_, atomCoarseGrainingPositions_, w); + interscaleManager_.add_dense_matrix(C,tag); + outputFieldsTagged_[tag] = C; + } + else { + AtfProjection * C = new AtfProjection(this, c, accumulant_, w); + interscaleManager_.add_dense_matrix(C,tag); + outputFieldsTagged_[tag] = C; + } + } + + } + + //------------------------------------------------------------------- + // sets initial values of filtered quantities + void ATC_Transfer::construct_methods() + { + if ((!initialized_) || timeFilterManager_.need_reset()) { + timeFilters_.reset(NUM_TOTAL_FIELDS+nComputes_); + sampleCounter_ = 0; + + // for filtered fields + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (fieldFlags_(index)) { + string name = field_to_string((FieldName) index); + filteredData_[name] = 0.0; + timeFilters_(index) = timeFilterManager_.construct(); + } + } + + // for filtered projected computes + + // lists/accessing of fields ( & computes) + map ::const_iterator iter; + int index = NUM_TOTAL_FIELDS; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + filteredData_[tag] = 0.0; + timeFilters_(index) = timeFilterManager_.construct(); + index++; + } + } + } + + + //------------------------------------------------------------------- + // called after the end of a "run" + void ATC_Transfer::finish() + { + // base class + ATC_Method::finish(); + } + + //------------------------------------------------------------------- + // this is the parser + bool ATC_Transfer::modify(int narg, char **arg) + { + bool match = false; + + int argIdx = 0; + // check to see if it is a transfer class command + /*! \page man_hardy_fields fix_modify AtC fields + \section syntax + fix_modify AtC fields \n + fix_modify AtC fields \n + - all | none (keyword) = output all or no fields \n + - add | delete (keyword) = add or delete the listed output fields \n + - fields (keyword) = \n + density : mass per unit volume \n + displacement : displacement vector \n + momentum : momentum per unit volume \n + velocity : defined by momentum divided by density \n + projected_velocity : simple kernel estimation of atomic velocities \n + temperature : temperature derived from the relative atomic kinetic energy (as done by ) \n + kinetic_temperature : temperature derived from the full kinetic energy \n + number_density : simple kernel estimation of number of atoms per unit volume \n + stress : + Cauchy stress tensor for eulerian analysis (atom_element_map), or + 1st Piola-Kirchhoff stress tensor for lagrangian analysis \n + transformed_stress : + 1st Piola-Kirchhoff stress tensor for eulerian analysis (atom_element_map), or + Cauchy stress tensor for lagrangian analysis \n + heat_flux : spatial heat flux vector for eulerian, + or referential heat flux vector for lagrangian \n + potential_energy : potential energy per unit volume \n + kinetic_energy : kinetic energy per unit volume \n + thermal_energy : thermal energy (kinetic energy - continuum kinetic energy) per unit volume \n + internal_energy : total internal energy (potential + thermal) per unit volume \n + energy : total energy (potential + kinetic) per unit volume \n + number_density : number of atoms per unit volume \n + eshelby_stress: configurational stress (energy-momentum) tensor defined by Eshelby + [References: Philos. Trans. Royal Soc. London A, Math. Phys. Sci., Vol. 244, + No. 877 (1951) pp. 87-112; J. Elasticity, Vol. 5, Nos. 3-4 (1975) pp. 321-335] \n + vacancy_concentration: volume fraction of vacancy content \n + type_concentration: volume fraction of a specific atom type \n + \section examples + fix_modify AtC fields add velocity temperature + \section description + Allows modification of the fields calculated and output by the + transfer class. The commands are cumulative, e.g.\n + fix_modify AtC fields none \n + followed by \n + fix_modify AtC fields add velocity temperature \n + will only output the velocity and temperature fields. + \section restrictions + Must be used with the hardy/field type of AtC fix, see \ref man_fix_atc. + Currently, the stress and heat flux formulas are only correct for + central force potentials, e.g. Lennard-Jones and EAM + but not Stillinger-Weber. + \section related + See \ref man_hardy_gradients , \ref man_hardy_rates and \ref man_hardy_computes + \section default + By default, no fields are output + */ + if (strcmp(arg[argIdx],"fields")==0) { + argIdx++; + if (strcmp(arg[argIdx],"all")==0) { + outputFlags_ = true; + match = true; + } + else if (strcmp(arg[argIdx],"none")==0) { + outputFlags_ = false; + match = true; + } + else if (strcmp(arg[argIdx],"add")==0) { + argIdx++; + for (int i = argIdx; i < narg; ++i) { + FieldName field_name = string_to_field(arg[i]); + outputFlags_(field_name) = true; + } + match = true; + } + else if (strcmp(arg[argIdx],"delete")==0) { + argIdx++; + for (int i = argIdx; i < narg; ++i) { + FieldName field_name = string_to_field(arg[i]); + outputFlags_(field_name) = false; + } + match = true; + } + check_field_dependencies(); + if (fieldFlags_(DISPLACEMENT)) { trackDisplacement_ = true; } + } + + /*! \page man_hardy_gradients fix_modify AtC gradients + \section syntax + fix_modify AtC gradients \n + - add | delete (keyword) = add or delete the calculation of gradients for the listed output fields \n + - fields (keyword) = \n + gradients can be calculated for all fields listed in \ref man_hardy_fields + + \section examples + fix_modify AtC gradients add temperature velocity stress \n + fix_modify AtC gradients delete velocity \n + \section description + Requests calculation and ouput of gradients of the fields from the + transfer class. These gradients will be with regard to spatial or material + coordinate for eulerian or lagrangian analysis, respectively, as specified by + atom_element_map (see \ref man_atom_element_map ) + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + No gradients are calculated by default + */ + else if (strcmp(arg[argIdx],"gradients")==0) { + argIdx++; + if (strcmp(arg[argIdx],"add")==0) { + argIdx++; + FieldName field_name; + for (int i = argIdx; i < narg; ++i) { + field_name = string_to_field(arg[i]); + gradFlags_(field_name) = true; + } + match = true; + } + else if (strcmp(arg[argIdx],"delete")==0) { + argIdx++; + FieldName field_name; + for (int i = argIdx; i < narg; ++i) { + field_name = string_to_field(arg[i]); + gradFlags_(field_name) = false; + } + match = true; + } + } + + /*! \page man_hardy_rates fix_modify AtC rates + \section syntax + fix_modify AtC rates \n + - add | delete (keyword) = add or delete the calculation of rates (time derivatives) for the listed output fields \n + - fields (keyword) = \n + rates can be calculated for all fields listed in \ref man_hardy_fields + + \section examples + fix_modify AtC rates add temperature velocity stress \n + fix_modify AtC rates delete stress \n + \section description + Requests calculation and ouput of rates (time derivatives) of the fields from the + transfer class. For eulerian analysis (see \ref man_atom_element_map ), these rates + are the partial time derivatives of the nodal fields, not the full (material) time + derivatives. \n + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + No rates are calculated by default + */ + else if (strcmp(arg[argIdx],"rates")==0) { + argIdx++; + if (strcmp(arg[argIdx],"add")==0) { + argIdx++; + FieldName field_name; + for (int i = argIdx; i < narg; ++i) { + field_name = string_to_field(arg[i]); + rateFlags_(field_name) = true; + } + match = true; + } + else if (strcmp(arg[argIdx],"delete")==0) { + argIdx++; + FieldName field_name; + for (int i = argIdx; i < narg; ++i) { + field_name = string_to_field(arg[i]); + rateFlags_(field_name) = false; + } + match = true; + } + } + + + /*! \page man_pair_interactions fix_modify AtC pair_interactions on|off + \section syntax + fix_modify AtC pair_interactions on|off \n + fix_modify AtC bond_interactions on|off \n + + \section examples + fix_modify AtC bond_interactions on \n + \section description + include bonds and/or pairs in the stress and heat flux computations + \section restrictions + \section related + \section default + pair interactions: on, bond interactions: off + */ + if (strcmp(arg[argIdx],"pair_interactions")==0) { // default true + argIdx++; + if (strcmp(arg[argIdx],"on")==0) { hasPairs_ = true; } + else { hasPairs_ = false;} + match = true; + } + if (strcmp(arg[argIdx],"bond_interactions")==0) { // default false + argIdx++; + if (strcmp(arg[argIdx],"on")==0) { hasBonds_ = true; } + else { hasBonds_ = false;} + match = true; + } + + /*! \page man_hardy_computes fix_modify AtC computes + \section syntax + fix_modify AtC computes [per-atom compute id] \n + - add | delete (keyword) = add or delete the calculation of an equivalent continuum field + for the specified per-atom compute as volume or number density quantity \n + - per-atom compute id = name/id for per-atom compute, + fields can be calculated for all per-atom computes available from LAMMPS \n + - volume | number (keyword) = field created is a per-unit-volume quantity + or a per-atom quantity as weighted by kernel functions \n + + \section examples + compute virial all stress/atom \n + fix_modify AtC computes add virial volume \n + fix_modify AtC computes delete virial \n + \n + compute centrosymmetry all centro/atom \n + fix_modify AtC computes add centrosymmetry number \n + \section description + Calculates continuum fields corresponding to specified per-atom computes created by LAMMPS \n + \section restrictions + Must be used with the hardy/field type of AtC fix ( see \ref man_fix_atc ) \n + Per-atom compute must be specified before corresponding continuum field can be requested \n + \section related + See manual page for compute + \section default + No defaults exist for this command + */ + else if (strcmp(arg[argIdx],"computes")==0) { + argIdx++; + if (strcmp(arg[argIdx],"add")==0) { + argIdx++; + string tag(arg[argIdx++]); + int normalization = NO_NORMALIZATION; + if (narg > argIdx) { + if (strcmp(arg[argIdx],"volume")==0) { + normalization = VOLUME_NORMALIZATION; + } + else if (strcmp(arg[argIdx],"number")==0) { + normalization = NUMBER_NORMALIZATION; + } + else if (strcmp(arg[argIdx],"mass")==0) { + normalization = MASS_NORMALIZATION; + throw ATC_Error("mass normalized not implemented"); + } + } + computes_[tag] = normalization; + nComputes_++; + match = true; + } + else if (strcmp(arg[argIdx],"delete")==0) { + argIdx++; + string tag(arg[argIdx]); + if (computes_.find(tag) != computes_.end()) { + computes_.erase(tag); + nComputes_--; + } + else { + throw ATC_Error(tag+" compute is not in list"); + } + match = true; + } + } + + + /*! \page man_hardy_dxa_exact_mode fix_modify AtC dxa_exact_mode + \section syntax + fix_modify AtC dxa_exact_mode \n + - on | off (keyword) = use "exact"/serial mode for DXA-based + calculation of dislocation density, or not \n + + \section examples + fix_modify AtC dxa_exact_mode \n + fix_modify AtC dxa_exact_mode on \n + fix_modify AtC dxa_exact_mode off \n + \section description + Overrides normal "exact"/serial mode for DXA code to extract dislocation segments, + as opposed to an "inexact" mode that's more efficient for parallel computation of + large systems. \n + \section restrictions + Must be used with the hardy/field type of AtC fix + ( see \ref man_fix_atc ) + \section related + \section default + By default, the DXA "exact"/serial mode is used (i.e. on). \n + */ + else if (strcmp(arg[argIdx],"dxa_exact_mode")==0) { + argIdx++; + dxaExactMode_ = true; + if (narg > argIdx && strcmp(arg[argIdx],"off")==0) dxaExactMode_ = false; + match = true; + } + + /*! \page man_sample_frequency fix_modify AtC sample_frequency + \section syntax + fix_modify AtC sample_frequency [freq] + - freq (int) : frequency to sample field in number of steps + \section examples + fix_modify AtC sample_frequency 10 + \section description + Calculates a surface integral of the given field dotted with the + outward normal of the faces and puts output in the "GLOBALS" file + \section restrictions + Must be used with the hardy/field AtC fix ( see \ref man_fix_atc ) + and is only relevant when time filters are being used. + \section related + \section default + none + */ + else if (strcmp(arg[argIdx],"sample_frequency")==0) { + argIdx++; + int value = outputFrequency_; // default to output frequency + if (narg > 1) { + if (atoi(arg[argIdx]) > 0) value = atoi(arg[argIdx]); + } + sampleFrequency_ = value; + match = true; + } // end "sample_frequency" + + // no match, call base class parser + if (!match) { + match = ATC_Method::modify(narg, arg); + } + + return match; + } + + //------------------------------------------------------------------- + // called at the beginning of a timestep + void ATC_Transfer::pre_init_integrate() + { + ATC_Method::pre_init_integrate(); + } + + //------------------------------------------------------------------- + // called at the begining of second half timestep + // REFACTOR move this to post_neighbor + void ATC_Transfer::pre_final_integrate() + { + // update time + update_time(); // time uses step if dt = 0 + + + + if ( neighborReset_ && sample_now() ) { + if (! kernelOnTheFly_ ) { + if (!moleculeIds_.empty()) compute_kernel_matrix_molecule(); //KKM add + } + neighborReset_ = false; + } + } + + //------------------------------------------------------------------- + // called at the end of second half timestep + void ATC_Transfer::post_final_integrate() + { + // compute spatially smoothed quantities + double dt = lammpsInterface_->dt(); + if ( sample_now() ) { + + bool needsBond = (! bondOnTheFly_ ) && + (fieldFlags_(STRESS) + || fieldFlags_(ESHELBY_STRESS) + || fieldFlags_(HEAT_FLUX)); + + if ( needsBond ) { + if (pairMap_->need_reset()) { +// ATC::LammpsInterface::instance()->print_msg("Recomputing bond matrix due to atomReset_ value"); + compute_bond_matrix(); + } + } + time_filter_pre (dt); + compute_fields(); + time_filter_post(dt); + lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+sampleFrequency_); + } + + // output + if ( output_now() && !outputStepZero_ ) output(); + outputStepZero_ = false; + + } + + //------------------------------------------------------------------- + void ATC_Transfer::compute_bond_matrix(void) + { + bondMatrix_->reset(); + } + //------------------------------------------------------------------- + void ATC_Transfer::compute_fields(void) + { + + // keep per-atom computes fresh. JAZ and REJ not sure why; + // need to confer with JAT. (JAZ, 4/5/12) + interscaleManager_.lammps_force_reset(); + + // (1) direct quantities + for(int i=0; i < numFields_; ++i) { + FieldName index = indices_[i]; + if (fieldFlags_(index)) { + hardyData_[field_to_string(index)].set_quantity() + = (outputFields_[index])->quantity(); + } + } + if (fieldFlags_(INTERNAL_ENERGY)) + compute_internal_energy(hardyData_["internal_energy"].set_quantity()); + if (fieldFlags_(ENERGY)) + compute_energy(hardyData_["energy"].set_quantity()); + if (fieldFlags_(STRESS)) + compute_stress(hardyData_["stress"].set_quantity()); + if (fieldFlags_(HEAT_FLUX)) + compute_heatflux(hardyData_["heat_flux"].set_quantity()); +// molecule data + if (fieldFlags_(DIPOLE_MOMENT)) + compute_dipole_moment(hardyData_["dipole_moment"].set_quantity()); + if (fieldFlags_(QUADRUPOLE_MOMENT)) + compute_quadrupole_moment(hardyData_["quadrupole_moment"].set_quantity()); + if (fieldFlags_(DISLOCATION_DENSITY)) + compute_dislocation_density(hardyData_["dislocation_density"].set_quantity()); + + // (2) derived quantities + // compute: gradients + if (gradFlags_.has_member(true)) { + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (gradFlags_(index)) { + string field= field_to_string((FieldName) index); + string grad_field = field + "_gradient"; + if (hardyData_.find(field) == hardyData_.end() ) { + throw ATC_Error("field " + field + " needs to be defined for gradient"); + } + gradient_compute(hardyData_[field].quantity(), hardyData_[grad_field].set_quantity()); + } + } + } + // compute: eshelby stress + if (fieldFlags_(ESHELBY_STRESS)) { + { + compute_eshelby_stress(hardyData_["eshelby_stress"].set_quantity(), + hardyData_["internal_energy"].quantity(), + hardyData_["stress"].quantity(), + hardyData_["displacement_gradient"].quantity()); + } + } + if (fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)) { + DENS_MAT & H = hardyData_["displacement_gradient"].set_quantity(); + DENS_MAT E(H.nRows(),1); + ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature"); + const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity()); + //DENS_MAT & T = hardyData_["temperature"]; + //cauchy_born_entropic_energy(H,E,T); E += hardyData_["internal_energy"]; + cauchy_born_energy(H, E, temp); + E -= nodalRefPotentialEnergy_; + + compute_eshelby_stress(hardyData_["cauchy_born_eshelby_stress"].set_quantity(), + E,hardyData_["stress"].quantity(), + hardyData_["displacement_gradient"].quantity()); + } + // compute: cauchy born stress + if (fieldFlags_(CAUCHY_BORN_STRESS)) { + ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature"); + const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity()); + cauchy_born_stress(hardyData_["displacement_gradient"].quantity(), + hardyData_["cauchy_born_stress"].set_quantity(), temp); + } + // compute: cauchy born energy + if (fieldFlags_(CAUCHY_BORN_ENERGY)) { + ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature"); + const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity()); + cauchy_born_energy(hardyData_["displacement_gradient"].quantity(), + hardyData_["cauchy_born_energy"].set_quantity(), temp); + } + // 1st PK transformed to cauchy (lag) or cauchy transformed to 1st PK (eul) + if (fieldFlags_(TRANSFORMED_STRESS)) { + compute_transformed_stress(hardyData_["transformed_stress"].set_quantity(), + hardyData_["stress"].quantity(), + hardyData_["displacement_gradient"].quantity()); + } + if (fieldFlags_(VACANCY_CONCENTRATION)) { + compute_vacancy_concentration(hardyData_["vacancy_concentration"].set_quantity(), + hardyData_["displacement_gradient"].quantity(), + hardyData_["number_density"].quantity()); + } + if (fieldFlags_(ELECTRIC_POTENTIAL)) { + compute_electric_potential( + hardyData_[field_to_string(ELECTRIC_POTENTIAL)].set_quantity()); + } + // compute: rotation and/or stretch from deformation gradient + if (fieldFlags_(ROTATION) || fieldFlags_(STRETCH)) { + compute_polar_decomposition(hardyData_["rotation"].set_quantity(), + hardyData_["stretch"].set_quantity(), + hardyData_["displacement_gradient"].quantity()); + } + // compute: rotation and/or stretch from deformation gradient + if (fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) { + compute_elastic_deformation_gradient2(hardyData_["elastic_deformation_gradient"].set_quantity(), + hardyData_["stress"].quantity(), + hardyData_["displacement_gradient"].quantity()); + } + + // (3) computes + lammpsInterface_->computes_clearstep(); + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + COMPUTE_POINTER cmpt = lammpsInterface_->compute_pointer(tag); + int projection = iter->second; + int ncols = lammpsInterface_->compute_ncols_peratom(cmpt);; + DENS_MAT atomicData(nLocal_,ncols); + if (ncols == 1) { + double * atomData = lammpsInterface_->compute_vector_peratom(cmpt); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + atomicData(i,0) = atomData[atomIdx]; + } + } + else { + double ** atomData = lammpsInterface_->compute_array_peratom(cmpt); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int k = 0; k < ncols; k++) { + atomicData(i,k) = atomData[atomIdx][k]; + } + } + } + // REFACTOR -- make dep manage + if (projection == NO_NORMALIZATION) { + project(atomicData,hardyData_[tag].set_quantity()); + } + else if (projection == VOLUME_NORMALIZATION) { + project_volume_normalized(atomicData,hardyData_[tag].set_quantity()); + } + else if (projection == NUMBER_NORMALIZATION) { + project_count_normalized(atomicData,hardyData_[tag].set_quantity()); + } + else if (projection == MASS_NORMALIZATION) { + throw ATC_Error("unimplemented normalization"); + } + else { + throw ATC_Error("unimplemented normalization"); + } +#ifdef ESHELBY_VIRIAL + if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) { + if (atomToElementMapType_ == LAGRANGIAN) { + DENS_MAT tmp = hardyData_[tag].quantity(); + DENS_MAT & myData(hardyData_[tag].set_quantity()); + myData.reset(nNodes_,FieldSizes[STRESS]); + DENS_MAT F(3,3),FT(3,3),FTINV(3,3),CAUCHY(3,3),PK1(3,3); + const DENS_MAT& H(hardyData_["displacement_gradient"].quantity()); + for (int k = 0; k < nNodes_; k++ ) { + vector_to_symm_matrix(k,tmp,CAUCHY); + vector_to_matrix(k,H,F); + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + FT = F.transpose(); + FTINV = inv(FT); + + // volumes are already reference volumes. + PK1 = CAUCHY*FTINV; + matrix_to_vector(k,PK1,myData); + } + } + compute_eshelby_stress(hardyData_["eshelby_virial"].set_quantity(), + hardyData_["internal_energy"].quantity(),hardyData_[tag].quantity(), + hardyData_["displacement_gradient"].quantity()); + } +#endif + } + + }// end of compute_fields routine + + //------------------------------------------------------------------- + void ATC_Transfer::time_filter_pre(double dt) + { + sampleCounter_++; + string name; + double delta_t = dt*sampleFrequency_; + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (fieldFlags_(index)) { + name = field_to_string((FieldName) index); + timeFilters_(index)->apply_pre_step1(filteredData_[name].set_quantity(), + hardyData_[name].quantity(), delta_t); + } + } + map ::const_iterator iter; + int index = NUM_TOTAL_FIELDS; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + timeFilters_(index)->apply_pre_step1(filteredData_[tag].set_quantity(), + hardyData_[tag].quantity(), delta_t); + index++; + } + } + + //------------------------------------------------------------------- + void ATC_Transfer::time_filter_post(double dt) + { + sampleCounter_++; + string name; + double delta_t = dt*sampleFrequency_; + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (fieldFlags_(index)) { + name = field_to_string((FieldName) index); + timeFilters_(index)->apply_post_step2(filteredData_[name].set_quantity(), + hardyData_[name].quantity(), delta_t); + } + } + if (rateFlags_.has_member(true)) { + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (rateFlags_(index)) { + string field= field_to_string((FieldName) index); + string rate_field = field + "_rate"; + timeFilters_(index)->rate(hardyData_[rate_field].set_quantity(), + filteredData_[field].quantity(), + hardyData_[field].quantity(), delta_t); + } + } + } + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (rateFlags_(index)) { + name = field_to_string((FieldName) index); + string rate_field = name + "_rate"; + filteredData_[rate_field] = hardyData_[rate_field]; + } + if (gradFlags_(index)) { + name = field_to_string((FieldName) index); + string grad_field = name + "_gradient"; + filteredData_[grad_field] = hardyData_[grad_field]; + } + } + + // lists/accessing of fields ( & computes) + map ::const_iterator iter; + int index = NUM_TOTAL_FIELDS; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + timeFilters_(index)->apply_post_step2(filteredData_[tag].set_quantity(), + hardyData_[tag].quantity(), delta_t); +#ifdef ESHELBY_VIRIAL + if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) { + filteredData_["eshelby_virial"] = hardyData_["eshelby_virial"]; + } +#endif + index++; + } + } + + //------------------------------------------------------------------- + void ATC_Transfer::output() + { + feEngine_->departition_mesh(); + + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (outputFlags_(index)) { + FieldName fName = (FieldName) index; + string name= field_to_string(fName); + fields_[fName] = filteredData_[name]; + } + } + + ATC_Method::output(); + if (lammpsInterface_->comm_rank() == 0) { + // data + OUTPUT_LIST output_data; +#ifdef REFERENCE_PE_OUTPUT + output_data["reference_potential_energy"] = & nodalRefPotentialEnergy_; +#endif + for(int index=0; index < NUM_TOTAL_FIELDS; ++index) { + if (outputFlags_(index)) { + string name= field_to_string((FieldName) index); + output_data[name] = & ( filteredData_[name].set_quantity()); + } + if (rateFlags_(index)) { + string name= field_to_string((FieldName) index); + string rate_name = name + "_rate"; + output_data[rate_name] = & ( filteredData_[rate_name].set_quantity()); + } + if (gradFlags_(index)) { + string name= field_to_string((FieldName) index); + string grad_name = name + "_gradient"; + output_data[grad_name] = & ( filteredData_[grad_name].set_quantity()); + } + } + + // lists/accessing of fields ( & computes) + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + output_data[tag] = & ( filteredData_[tag].set_quantity()); +#ifdef ESHELBY_VIRIAL + if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) { + output_data["eshelby_virial"] = & ( filteredData_["eshelby_virial"].set_quantity() ); + } +#endif + } + + // output + feEngine_->write_data(output_index(), & output_data); + } + feEngine_->partition_mesh(); + } + +/////// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/////// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + //------------------------------------------------------------------- + // computes nodeData = N*atomData + void ATC_Transfer::project(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + if (! kernelOnTheFly_ ) { + nodeData.reset(nNodes_,atomData.nCols(),true); + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + if (nLocal_>0) workNodeArray = (accumulant_->quantity()).transMat(atomData); + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count); + } + else { + compute_projection(atomData,nodeData); + } + } + + //------------------------------------------------------------------- + // computes nodeData = N*molData specially for molecules + void ATC_Transfer::project_molecule(const DENS_MAT & molData, + DENS_MAT & nodeData) + { + if (! kernelOnTheFly_ ) { + nodeData.reset(nNodes_,molData.nCols(),true); + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + if (nLocal_>0) workNodeArray = (accumulantMol_->quantity()).transMat(molData); + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count); + } + else { + compute_projection(molData,nodeData); + } + } + + //------------------------------------------------------------------- + // computes nodeData = gradient of N*molData specially for molecules + void ATC_Transfer::project_molecule_gradient(const DENS_MAT & molData, + DENS_MAT & nodeData) + { + if (! kernelOnTheFly_ ) { + nodeData.reset(nNodes_,molData.nCols(),true); + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + if (nLocal_>0) workNodeArray = (accumulantMolGrad_->quantity()).transMat(molData); + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count); + } + else { + compute_projection(molData,nodeData); + } + } + + //------------------------------------------------------------------- + // count normalized + void ATC_Transfer::project_count_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + DENS_MAT tmp; + project(atomData,tmp); + nodeData = (accumulantWeights_->quantity())*tmp; + } + + //------------------------------------------------------------------- + // volume normalized + void ATC_Transfer::project_volume_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + DENS_MAT tmp; + project(atomData,tmp); + nodeData = (accumulantInverseVolumes_->quantity())*tmp; + } + + //------------------------------------------------------------------- + // volume normalized molecule + void ATC_Transfer::project_volume_normalized_molecule(const DENS_MAT & molData, + DENS_MAT & nodeData) + { + DENS_MAT tmp; + project_molecule(molData,tmp); + nodeData = (accumulantInverseVolumes_->quantity())*tmp; + } + + //------------------------------------------------------------------- + // volume normalized molecule_gradient + void ATC_Transfer::project_volume_normalized_molecule_gradient(const DENS_MAT & molData, + DENS_MAT & nodeData) + { + DENS_MAT tmp; + project_molecule_gradient(molData,tmp); + nodeData = (accumulantInverseVolumes_->quantity())*tmp; + } + + + //------------------------------------------------------------------- + void ATC_Transfer::gradient_compute(const DENS_MAT & inNodeData, + DENS_MAT & outNodeData) + { + int nrows = inNodeData.nRows(); + int ncols = inNodeData.nCols(); + outNodeData.reset(nrows,ncols*nsd_); + int index = 0; + for (int n = 0; n < ncols; n++) { //output v1,1 v1,2 v1,3 ... + for (int m = 0; m < nsd_; m++) { + CLON_VEC inData(inNodeData,CLONE_COL,n); + CLON_VEC outData(outNodeData,CLONE_COL,index); + outData = (*((gradientMatrix_->quantity())[m]))*inData; + ++index; + } + } + } + + + + //------------------------------------------------------------------- + void ATC_Transfer::compute_force_matrix() + { + atomicForceMatrix_ = pairVirial_->quantity(); + } + + //------------------------------------------------------------------- + // computes "virial" part of heat flux + // This is correct ONLY for pair potentials. + void ATC_Transfer::compute_heat_matrix() + { + atomicHeatMatrix_ = pairHeatFlux_->quantity(); + } + + //------------------------------------------------------------------- + // set xPointer_ to xref or xatom depending on Lagrangian/Eulerian analysis + void ATC_Transfer::set_xPointer() + { + xPointer_ = xref_; + if (atomToElementMapType_ == EULERIAN) { + xPointer_ = lammpsInterface_->xatom(); + } + } + + //------------------------------------------------------------------- +// SOON TO BE OBSOLETE + // check consistency of fieldFlags_ + void ATC_Transfer::check_field_dependencies() + { + fieldFlags_ = outputFlags_; + if (fieldFlags_(TRANSFORMED_STRESS)) { + fieldFlags_(STRESS) = true; + fieldFlags_(DISPLACEMENT) = true; + } + if (fieldFlags_(ESHELBY_STRESS)) { + fieldFlags_(STRESS) = true; + fieldFlags_(INTERNAL_ENERGY) = true; + fieldFlags_(DISPLACEMENT) = true; + } + if (fieldFlags_(CAUCHY_BORN_STRESS) + || fieldFlags_(CAUCHY_BORN_ENERGY) + || fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS) + || fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) { + if (! (cauchyBornStress_) ) { + throw ATC_Error("can't compute cauchy-born stress w/o cauchy born model"); + } + } + if (fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) { + fieldFlags_(STRESS) = true; + } + if (fieldFlags_(CAUCHY_BORN_STRESS) + || fieldFlags_(CAUCHY_BORN_ENERGY)) { + fieldFlags_(TEMPERATURE) = true; + fieldFlags_(DISPLACEMENT) = true; + } + if (fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)) { + fieldFlags_(TEMPERATURE) = true; + fieldFlags_(DISPLACEMENT) = true; + fieldFlags_(STRESS) = true; + } + if (fieldFlags_(VACANCY_CONCENTRATION)) { + fieldFlags_(DISPLACEMENT) = true; + fieldFlags_(NUMBER_DENSITY) = true; + } + if (fieldFlags_(INTERNAL_ENERGY)) { + fieldFlags_(POTENTIAL_ENERGY) = true; + fieldFlags_(THERMAL_ENERGY) = true; + } + if (fieldFlags_(ENERGY)) { + fieldFlags_(POTENTIAL_ENERGY) = true; + fieldFlags_(KINETIC_ENERGY) = true; + } + if (fieldFlags_(TEMPERATURE) || fieldFlags_(HEAT_FLUX) || + fieldFlags_(KINETIC_ENERGY) || fieldFlags_(THERMAL_ENERGY) || + fieldFlags_(ENERGY) || fieldFlags_(INTERNAL_ENERGY) || + fieldFlags_(KINETIC_ENERGY) || (fieldFlags_(STRESS) && + atomToElementMapType_ == EULERIAN) ) { + fieldFlags_(VELOCITY) = true; + fieldFlags_(MASS_DENSITY) = true; + } + + if (fieldFlags_(VELOCITY)) { + fieldFlags_(MASS_DENSITY) = true; + fieldFlags_(MOMENTUM) = true; + } + if (fieldFlags_(DISPLACEMENT)) { + fieldFlags_(MASS_DENSITY) = true; + } + if (fieldFlags_(TEMPERATURE) ) { + fieldFlags_(NUMBER_DENSITY) = true; + } + + if (fieldFlags_(ROTATION) || + fieldFlags_(STRETCH)) { + fieldFlags_(DISPLACEMENT) = true; + } + if (fieldFlags_(ESHELBY_STRESS) + || fieldFlags_(CAUCHY_BORN_STRESS) + || fieldFlags_(CAUCHY_BORN_ENERGY) + || fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS) + || fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT) + || fieldFlags_(VACANCY_CONCENTRATION) + || fieldFlags_(ROTATION) + || fieldFlags_(STRETCH) ) { + gradFlags_(DISPLACEMENT) = true; + } + + // check whether single_enable==0 for stress/heat flux calculation + if (fieldFlags_(STRESS) || fieldFlags_(HEAT_FLUX)) { + if (lammpsInterface_->single_enable()==0) { + throw ATC_Error("Calculation of stress field not possible with selected pair type."); + } + } + + } + +//============== THIN WRAPPERS ==================================== +// OBSOLETE +// HARDY COMPUTES +// ***************UNCONVERTED************************** + + //------------------------------------------------------------------- + // total energy + + void ATC_Transfer::compute_energy(DENS_MAT & energy) + { + PerAtomQuantity * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + atomicScalar_=atomicPotentialEnergy->quantity(); + double mvv2e = lammpsInterface_->mvv2e(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ** vatom = lammpsInterface_->vatom(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + // compute kinetic energy per atom + double* v = vatom[atomIdx]; + double atomKE = 0.0; + for (int k = 0; k < nsd_; k++) { atomKE += v[k]*v[k]; } + atomKE *= 0.5*ma; + // add up total energy per atom + atomicScalar_(i,0) += atomKE; + } + project_volume_normalized(atomicScalar_, energy); + } + // internal energy + + void ATC_Transfer::compute_internal_energy(DENS_MAT & energy) + { + PerAtomQuantity * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); + PerAtomQuantity * atomicProlongedVelocity = interscaleManager_.per_atom_quantity("ProlongedVelocity"); + atomicScalar_=atomicPotentialEnergy->quantity(); + atomicVector_=atomicProlongedVelocity->quantity(); + double mvv2e = lammpsInterface_->mvv2e(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ** vatom = lammpsInterface_->vatom(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + // compute kinetic energy per atom + double* v = vatom[atomIdx]; + double atomKE = 0.0; + for (int k = 0; k < nsd_; k++) { + atomKE += (v[k]-atomicVector_(i,k))*(v[k]-atomicVector_(i,k)); + } + atomKE *= 0.5*ma; + // add up total energy per atom + atomicScalar_(i,0) += atomKE; + } + project_volume_normalized(atomicScalar_, energy); + } + //------------------------------------------------------------------- + // MOLECULE + //------------------------------------------------------------------- + void ATC_Transfer::compute_dipole_moment(DENS_MAT & dipole_moment) + { + const DENS_MAT & molecularVector(dipoleMoment_->quantity()); + project_volume_normalized_molecule(molecularVector,dipole_moment); // KKM add + // + } + //------------------------------------------------------------------- + void ATC_Transfer::compute_quadrupole_moment(DENS_MAT & quadrupole_moment) + { + const DENS_MAT & molecularVector(quadrupoleMoment_->quantity()); + project_volume_normalized_molecule_gradient(molecularVector,quadrupole_moment); // KKM add + // + } + //------------------------------------------------------------------- + void ATC_Transfer::compute_stress(DENS_MAT & stress) + { + // table of bond functions already calculated in initialize function + // get conversion factor for nktV to p units + double nktv2p = lammpsInterface_->nktv2p(); + + // calculate kinetic energy tensor part of stress for Eulerian analysis + if (atomToElementMapType_ == EULERIAN && nLocal_>0) { + compute_kinetic_stress(stress); + } + else { + // zero stress table for Lagrangian analysis or if nLocal_ = 0 + stress.zero(); + } + // add-in potential part of stress tensor + int nrows = stress.nRows(); + int ncols = stress.nCols(); + DENS_MAT local_potential_hardy_stress(nrows,ncols); + if (nLocal_>0) { + if (bondOnTheFly_) { + compute_potential_stress(local_potential_hardy_stress); + } + else { + // compute table of force & position dyad + compute_force_matrix(); + // calculate force part of stress tensor + local_potential_hardy_stress = atomicBondMatrix_*atomicForceMatrix_; + local_potential_hardy_stress *= 0.5; + } + } + // global summation of potential part of stress tensor + DENS_MAT potential_hardy_stress(nrows,ncols); + int count = nrows*ncols; + lammpsInterface_->allsum(local_potential_hardy_stress.ptr(), + potential_hardy_stress.ptr(), count); + stress += potential_hardy_stress; + stress = nktv2p*stress; + } + + //------------------------------------------------------------------- + // kinetic energy portion of stress + void ATC_Transfer::compute_kinetic_stress(DENS_MAT& stress) + { + const DENS_MAT& density = hardyData_["mass_density"].quantity(); + const DENS_MAT& velocity = hardyData_["velocity"].quantity(); + + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ** vatom = lammpsInterface_->vatom(); + double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy] + + atomicTensor_.reset(nLocal_,6); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + double* v = vatom[atomIdx]; + atomicTensor_(i,0) -= ma*v[0]*v[0]; + atomicTensor_(i,1) -= ma*v[1]*v[1]; + atomicTensor_(i,2) -= ma*v[2]*v[2]; + atomicTensor_(i,3) -= ma*v[0]*v[1]; + atomicTensor_(i,4) -= ma*v[0]*v[2]; + atomicTensor_(i,5) -= ma*v[1]*v[2]; + } + project_volume_normalized(atomicTensor_, stress); + + for (int i = 0; i < nNodes_; i++) { + double rho_i = mvv2e*density(i,0); + stress(i,0) += rho_i*velocity(i,0)*velocity(i,0); + stress(i,1) += rho_i*velocity(i,1)*velocity(i,1); + stress(i,2) += rho_i*velocity(i,2)*velocity(i,2); + stress(i,3) += rho_i*velocity(i,0)*velocity(i,1); + stress(i,4) += rho_i*velocity(i,0)*velocity(i,2); + stress(i,5) += rho_i*velocity(i,1)*velocity(i,2); + } + } + + //------------------------------------------------------------------- + void ATC_Transfer::compute_heatflux(DENS_MAT & flux) + { + // calculate kinetic part of heat flux + if (atomToElementMapType_ == EULERIAN && nLocal_>0) { + compute_kinetic_heatflux(flux); + } + else { + flux.zero(); // zero stress table for Lagrangian analysis + } + // add potential part of heat flux vector + int nrows = flux.nRows(); + int ncols = flux.nCols(); + DENS_MAT local_hardy_heat(nrows,ncols); + if (nLocal_>0) { + if (bondOnTheFly_) { + compute_potential_heatflux(local_hardy_heat); + } + else { + // calculate force/potential-derivative part of heat flux + compute_heat_matrix(); + local_hardy_heat = atomicBondMatrix_*atomicHeatMatrix_; + } + } + // global summation of potential part of heat flux vector + DENS_MAT hardy_heat(nrows,ncols); + int count = nrows*ncols; + lammpsInterface_->allsum(local_hardy_heat.ptr(), + hardy_heat.ptr(), count); + flux += hardy_heat; + } + + //------------------------------------------------------------------- + // compute kinetic part of heat flux + void ATC_Transfer::compute_kinetic_heatflux(DENS_MAT& flux) + { + const DENS_MAT& velocity = hardyData_["velocity"].quantity(); + const DENS_MAT& energy = hardyData_["mass_density"].quantity(); + const DENS_MAT& stress = hardyData_["stress"].quantity(); + + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ** vatom = lammpsInterface_->vatom(); + double mvv2e = lammpsInterface_->mvv2e(); + double * atomPE = lammpsInterface_->compute_pe_peratom(); + double atomKE, atomEnergy; + atomicVector_.reset(nLocal_,3); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + double* v = vatom[atomIdx]; + atomKE = 0.0; + for (int k = 0; k < nsd_; k++) { atomKE += v[k]*v[k]; } + atomKE *= 0.5*ma; + atomEnergy = atomKE + atomPE[atomIdx]; + for (int j = 0; j < nsd_; j++) { + atomicVector_(i,j) += atomEnergy*v[j]; + } + } + project_volume_normalized(atomicVector_,flux); + + // - e^0_I v_I + \sigma^T_I v_I + for (int i = 0; i < nNodes_; i++) { + double e_i = energy(i,0); + flux(i,0) += (e_i + stress(i,0))*velocity(i,0) + + stress(i,3)*velocity(i,1)+ stress(i,4)*velocity(i,2); + flux(i,1) += (e_i + stress(i,1))*velocity(i,1) + + stress(i,3)*velocity(i,0)+ stress(i,5)*velocity(i,2); + flux(i,2) += (e_i + stress(i,2))*velocity(i,2) + + stress(i,4)*velocity(i,0)+ stress(i,5)*velocity(i,1); + } + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_electric_potential(DENS_MAT & phi) + { + // Poisson solve with insulating bcs + const DENS_MAT & rho = (restrictedCharge_->quantity()); + SPAR_MAT K; + feEngine_->stiffness_matrix(K); + double permittivity = lammpsInterface_->dielectric(); + permittivity *= LammpsInterface::instance()->epsilon0(); + K *= permittivity; + BC_SET bcs; + bcs.insert(pair(0,0)); + LinearSolver solver(K,bcs); + CLON_VEC x = column(phi,0); + CLON_VEC b = column(rho,0); + solver.solve(x,b); +//x.print("x:phi"); +//b.print("b:rho"); + //LinearSolver solver(K,AUTO_SOLVE,true); + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_vacancy_concentration(DENS_MAT & Cv, + const DENS_MAT & H, const DENS_MAT & rhoN) + { + int * type = lammpsInterface_->atom_type(); + DENS_MAT new_rho(nNodes_,1); + DENS_MAT atomCnt(nLocal_,1); + double atomic_weight_sum = 0.0; + double site_weight_sum = 0.0; + int number_atoms = 0; + const DIAG_MAT & myAtomicWeights(atomVolume_->quantity()); + + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (type[atomIdx] != 13) { + atomCnt(i,0) = myAtomicWeights(i,i); + atomic_weight_sum += myAtomicWeights(i,i); + number_atoms++; + } + site_weight_sum += myAtomicWeights(i,i); + } + project_volume_normalized(atomCnt, new_rho); + DENS_MAT F(3,3); + for (int i = 0; i < nNodes_; i++) { + if (atomToElementMapType_ == EULERIAN) { + // for Eulerian analysis: F = (1-H)^{-1} + DENS_MAT G(3,3); + vector_to_matrix(i,H,G); + G *= -1.; + G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0; + F = inv(G); + } + else if (atomToElementMapType_ == LAGRANGIAN) { + // for Lagrangian analysis: F = (1+H) + vector_to_matrix(i,H,F); + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + } + double J = det(F); + Cv(i,0) = 1.0 - J*new_rho(i,0); + } + } + + //-------------------------------------------------------------------- + void ATC_Transfer::compute_eshelby_stress(DENS_MAT & M, + const DENS_MAT & E, const DENS_MAT & S, const DENS_MAT & H) + { + // eshelby stress:M, energy:E, stress:S, displacement gradient: H + // eshelby stress = W I - F^T.P = W I - C.S [energy] + // symmetric if isotropic S = a_0 I + a_1 C + a_2 C^2 + M.reset(nNodes_,FieldSizes[ESHELBY_STRESS]); + double nktv2p = lammpsInterface_->nktv2p(); + DENS_MAT P(3,3),F(3,3),FT(3,3),FTP(3,3),ESH(3,3); + for (int i = 0; i < nNodes_; i++) { + double W = E(i,0); + ESH.identity(); + ESH *= W; + + // copy to local + if (atomToElementMapType_ == LAGRANGIAN) { + // Stress notation convention:: 0:11 1:12 2:13 3:21 4:22 5:23 6:31 7:32 8:33 + vector_to_matrix(i,S,P); + + + vector_to_matrix(i,H,F); +#ifndef H_BASED + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; +#endif + FT = F.transpose(); + } + else if (atomToElementMapType_ == EULERIAN) { + vector_to_symm_matrix(i,S,P); + vector_to_matrix(i,H,F); + FT = F.transpose(); + } + FTP = (1.0/nktv2p)*FT*P; + ESH -= FTP; + if (atomToElementMapType_ == EULERIAN) { + // For Eulerian analysis, M = F^T*(w-H^T.CauchyStress) + DENS_MAT Q(3,3); + Q.identity(); + // Q stores (1-H) + Q -= FT.transpose(); + DENS_MAT F(3,3); + F = inv(Q); + FT = F.transpose(); + ESH = FT*ESH; + } + // copy to global + matrix_to_vector(i,ESH,M); + } + } + //--------------------------------------------------------------------------- + // Computes the Cauchy Born stress tensor, T given displacement gradient, H + // and optional temperature argument (passed by pointer), TEMP + //--------------------------------------------------------------------------- + void ATC_Transfer::cauchy_born_stress(const DENS_MAT &H, DENS_MAT &T, const DENS_MAT *temp) + { + FIELD_MATS uField; // uField should contain temperature. + DENS_MAT_VEC tField; + GRAD_FIELD_MATS hField; + DENS_MAT_VEC &h = hField[DISPLACEMENT]; + h.assign(nsd_, DENS_MAT(nNodes_,nsd_)); + tField.assign(nsd_, DENS_MAT(nNodes_,nsd_)); + // each row is the CB stress at a node stored in voigt form + T.reset(nNodes_,FieldSizes[CAUCHY_BORN_STRESS]); + const double nktv2p = lammpsInterface_->nktv2p(); + const double fact = -lammpsInterface_->mvv2e()*nktv2p; + + // reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient + vector_to_dens_mat_vec(H,h); + + // if temperature is provided, then set it + if (temp) uField[TEMPERATURE] = *temp; + + // Computes the stress at each node. + cauchyBornStress_->stress(uField, hField, tField); + + // reshapes the stress, T to a (#node,6) DenseMatrix. + DENS_MAT S(nNodes_,6); + symm_dens_mat_vec_to_vector(tField,S); + S *= fact; + + // tField/S holds the 2nd P-K stress tensor. Transform to + // Cauchy for EULERIAN analysis, transform to 1st P-K + // for LAGRANGIAN analysis. + DENS_MAT PK2(3,3),G(3,3),F(3,3),FT(3,3),STRESS(3,3); + for (int i = 0; i < nNodes_; i++) { + + vector_to_symm_matrix(i,S,PK2); + + if (atomToElementMapType_ == EULERIAN) { + + // for Eulerian analysis: F = (1-H)^{-1} + vector_to_matrix(i,H,G); + G *= -1.; + + G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0; + F = inv(G); + FT = transpose(F); + double J = det(F); + STRESS = F*PK2*FT; + STRESS *= 1/J; + symm_matrix_to_vector(i,STRESS,T); + } + else{ + // for Lagrangian analysis: F = 1 + H + vector_to_matrix(i,H,F); + + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + STRESS = F*PK2; + matrix_to_vector(i,STRESS,T); + } + + } + } + //--------------------------------------------------------------------------- + // Computes the Cauchy Born energy density, E given displacement gradient, H + // and optional temperature argument (passed by pointer), TEMP + //--------------------------------------------------------------------------- + void ATC_Transfer::cauchy_born_energy(const DENS_MAT &H, DENS_MAT &E, const DENS_MAT *temp) + { + FIELD_MATS uField; // uField should contain temperature. + GRAD_FIELD_MATS hField; + DENS_MAT_VEC &h = hField[DISPLACEMENT]; + h.assign(nsd_, DENS_MAT(nNodes_,nsd_)); + + // reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient + vector_to_dens_mat_vec(H,h); + + // if temperature is provided, then set it + if (temp) uField[TEMPERATURE] = *temp; + + // Computes the free/potential energy at each node. + cauchyBornStress_->elastic_energy(uField, hField, E); + + // convert back to energy units for ( ATC coupling uses MLT units) + double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy] + E *= mvv2e; + + // for Eulerian analysis, convert energy density to per-unit deformed volume + if (atomToElementMapType_ == EULERIAN) { + DENS_MAT G(3,3),F(3,3); + for (int i = 0; i < nNodes_; i++) { + // for Eulerian analysis: F = (1-H)^{-1} + vector_to_matrix(i,H,G); + G *= -1.; + + G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0; + F = inv(G); + double J = det(F); + E(i,0) *= 1/J; + } + } + + // subtract zero point energy + E -= nodalRefPotentialEnergy_; + } + //--------------------------------------------------------------------------- + // Computes the M/LH entropic energy density + //--------------------------------------------------------------------------- + void ATC_Transfer::cauchy_born_entropic_energy(const DENS_MAT &H, DENS_MAT &E, const DENS_MAT &T) + { + FIELD_MATS uField; // uField should contain temperature. + uField[TEMPERATURE] = T; + GRAD_FIELD_MATS hField; + DENS_MAT_VEC &h = hField[DISPLACEMENT]; + h.assign(nsd_, DENS_MAT(nNodes_,nsd_)); + + // reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient + vector_to_dens_mat_vec(H,h); + + // Computes the free/potential energy at each node. + cauchyBornStress_->entropic_energy(uField, hField, E); + + // convert back to energy units for ( ATC coupling uses MLT units) + double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy] + E *= mvv2e; + + // for Eulerian analysis, convert energy density to per-unit deformed volume + if (atomToElementMapType_ == EULERIAN) { + DENS_MAT G(3,3),F(3,3); + for (int i = 0; i < nNodes_; i++) { + // for Eulerian analysis: F = (1-H)^{-1} + vector_to_matrix(i,H,G); + G *= -1.; + + G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0; + F = inv(G); + double J = det(F); + E(i,0) *= 1/J; + } + } + + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_transformed_stress(DENS_MAT & stress, + const DENS_MAT & T, const DENS_MAT & H) + { + stress.reset(nNodes_,FieldSizes[TRANSFORMED_STRESS]); + DENS_MAT S(3,3),FT(3,3),P(3,3); + for (int i = 0; i < nNodes_; i++) { + if (atomToElementMapType_ == EULERIAN) { + vector_to_symm_matrix(i,T,P); + // for Eulerian analysis: F^T = (1-H)^{-T} + DENS_MAT G(3,3); + vector_to_matrix(i,H,G); + G *= -1.; + + G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0; + FT = inv(G.transpose()); + } + else{ + vector_to_matrix(i,T,P); + // for Lagrangian analysis: F^T = (1+H)^T + DENS_MAT F(3,3); + vector_to_matrix(i,H,F); + + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + FT = F.transpose(); + } + // + double J = det(FT); + FT *= 1/J; + if (atomToElementMapType_ == EULERIAN) { + FT = inv(FT); + } + S = P*FT; + matrix_to_vector(i,S,stress); + } + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_polar_decomposition(DENS_MAT & rotation, + DENS_MAT & stretch, const DENS_MAT & H) + { + DENS_MAT F(3,3),R(3,3),U(3,3); + for (int i = 0; i < nNodes_; i++) { + vector_to_matrix(i,H,F); + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + if (atomToElementMapType_ == EULERIAN) { + polar_decomposition(F,R,U,false); } // F = V R + else { + polar_decomposition(F,R,U); } // F = R U + // copy to local + if ( fieldFlags_(ROTATION) ) { + + matrix_to_vector(i,R,rotation); + } + if ( fieldFlags_(STRETCH) ) { + matrix_to_vector(i,U,stretch); + } + } + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_elastic_deformation_gradient(DENS_MAT & Fe, + const DENS_MAT & P, const DENS_MAT & H) + + { + // calculate Fe for every node + const double nktv2p = lammpsInterface_->nktv2p(); + const double fact = 1.0/ ( lammpsInterface_->mvv2e()*nktv2p ); + for (int i = 0; i < nNodes_; i++) { + DENS_VEC Pv = global_vector_to_vector(i,P); + Pv *= fact; + CBElasticTangentOperator tangent(cauchyBornStress_, Pv); + NonLinearSolver solver(&tangent); + DENS_VEC Fv = global_vector_to_vector(i,H); // pass in initial guess + add_identity_voigt_unsymmetric(Fv); + solver.solve(Fv); + vector_to_global_vector(i,Fv,Fe); + } + } + //-------------------------------------------------------------------- + void ATC_Transfer::compute_elastic_deformation_gradient2(DENS_MAT & Fe, + const DENS_MAT & P, const DENS_MAT & H) + { + // calculate Fe for every node + const double nktv2p = lammpsInterface_->nktv2p(); + const double fact = 1.0/ ( lammpsInterface_->mvv2e()*nktv2p ); + DENS_MAT F(3,3),R(3,3),U(3,3),PP(3,3),S(3,3); + for (int i = 0; i < nNodes_; i++) { + // get F = RU + vector_to_matrix(i,H,F); + F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0; + if (atomToElementMapType_ == EULERIAN) { + polar_decomposition(F,R,U,false); } // F = V R + else { + polar_decomposition(F,R,U); } // F = R U + // get S + vector_to_matrix(i,P,PP); + //S = PP*transpose(F); + S = inv(F)*PP; + + S += S.transpose(); S *= 0.5; // symmetrize + DENS_VEC Sv = to_voigt(S); + Sv *= fact; + // solve min_U || S - S_CB(U) || + CB2ndElasticTangentOperator tangent(cauchyBornStress_, Sv); + NonLinearSolver solver(&tangent); + //DENS_VEC Uv = to_voigt_unsymmetric(U); // pass in initial guess + DENS_VEC Uv = to_voigt(U); // pass in initial guess + //DENS_VEC Uv(6); Uv(0)=1;Uv(1)=1;Uv(2)=1;Uv(3)=0;Uv(4)=0;Uv(5)=0; + solver.solve(Uv); + DENS_MAT Ue = from_voigt(Uv); + DENS_MAT FFe = R*Ue; + matrix_to_vector(i,FFe,Fe); + } + } +// =========== Analytical solutions ========================== +} // end namespace ATC + + diff --git a/lib/atc/ATC_Transfer.h b/lib/atc/ATC_Transfer.h new file mode 100644 index 0000000000..4b12c7354e --- /dev/null +++ b/lib/atc/ATC_Transfer.h @@ -0,0 +1,253 @@ +/** ATC_Transfer : coarse-graining methods */ +#ifndef ATC_TRANSFER_H +#define ATC_TRANSFER_H + +// ATC headers +#include "ATC_Method.h" +#include "MoleculeSet.h" +#include "AtomToMoleculeTransfer.h" + +// Other headers +#include +#include +using std::list; + +namespace ATC { + +// Forward declarations +class FE_Engine; +class StressCauchyBorn; +class TimeFilter; + +class ATC_Transfer : public ATC_Method { + + public: + + // constructor + ATC_Transfer(std::string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile = "none"); + + // destructor + virtual ~ATC_Transfer(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** post time integration */ + virtual void finish(); + + /** first time substep routines */ + virtual void pre_init_integrate(); + + /** second time substep routine */ + virtual void pre_final_integrate(); + //virtual void final_integrate(){}; + virtual void post_final_integrate(); + + /** communication routines */ + virtual void pre_neighbor() {ATC_Method::pre_neighbor(); neighborReset_ = true;}; + + /** output function */ + virtual void output(); + + /** external access to hardy data and other information*/ + const DENS_MAT * hardy_data(string field) { return &hardyData_[field].quantity(); } + + protected: + + /** pointer to position data : either x_reference or x_current */ + double ** xPointer_; + + /** data */ + TAG_FIELDS hardyData_; + SmallMoleculeSet * smallMoleculeSet_; // KKM add + SmallMoleculeCentroid * moleculeCentroid_; // KKM add + SmallMoleculeDipoleMoment * dipoleMoment_; // KKM add + SmallMoleculeQuadrupoleMoment * quadrupoleMoment_; // KKM add + /** container for dependency managed data */ + vector < DENS_MAN * > outputFields_; + + map < string, DENS_MAN * > outputFieldsTagged_; + + DENS_MAN * restrictedCharge_; // WIP/TEMP + + /** work space */ + DENS_MAT atomicScalar_; + DENS_MAT atomicVector_; + DENS_MAT atomicTensor_; + + /** calculation flags */ + Array fieldFlags_; + Array outputFlags_; + Array gradFlags_; + Array rateFlags_; + map computes_; + bool outputStepZero_; + + /** check whether atoms have shifted box or element or neighbors changed */ + bool neighborReset_; + + //--------------------------------------------------------------- + /** initialization routines */ + //--------------------------------------------------------------- + /** gets baseline data from continuum model */ + virtual void set_continuum_data(); + /** sets up all data necessary to define the computational geometry */ + virtual void set_computational_geometry(); + /** constructs all data which is updated with time integration, i.e. fields */ + virtual void construct_time_integration_data(); + /** create methods, e.g. time integrators, filters */ + virtual void construct_methods(); + /** set up data which is dependency managed */ + virtual void construct_transfers(); + + /** compute atom to nodal quantities */ +// OBSOLETE + void compute_energy(DENS_MAT & energy); + void compute_internal_energy(DENS_MAT & energy); + void compute_stress(DENS_MAT & stress); + void compute_heatflux(DENS_MAT & flux); + /** derived quantities: compute nodal to nodal quantities */ + void compute_eshelby_stress(DENS_MAT & eshebly_stress, + const DENS_MAT & energy, const DENS_MAT & stress, + const DENS_MAT & displacement_gradient); + void cauchy_born_stress(const DENS_MAT &dudx, DENS_MAT &T, const DENS_MAT *temp=0); + void cauchy_born_energy(const DENS_MAT &dudx, DENS_MAT &T, const DENS_MAT *temp=0); + void cauchy_born_entropic_energy(const DENS_MAT &dudx, DENS_MAT &E, const DENS_MAT & T); + void compute_transformed_stress(DENS_MAT & stress, + const DENS_MAT & T, const DENS_MAT & displacement_gradient); + void compute_polar_decomposition(DENS_MAT & rotation, + DENS_MAT & stretch, const DENS_MAT & displacement_gradient); + void compute_elastic_deformation_gradient(DENS_MAT & elastic_def_grad, + const DENS_MAT & stress, const DENS_MAT & displacement_gradient); + void compute_elastic_deformation_gradient2(DENS_MAT & elastic_def_grad, + const DENS_MAT & stress, const DENS_MAT & displacement_gradient); + /** hybrid computes? */ + void compute_electric_potential(DENS_MAT & electric_potential); + void compute_vacancy_concentration(DENS_MAT & vacancy_concentration, + const DENS_MAT & displacement_gradient, + const DENS_MAT & number_density); + /** calculate kinetic part of stress */ + virtual void compute_kinetic_stress(DENS_MAT& stress); + /** calculate stress on-the-fly */ + virtual void compute_potential_stress(DENS_MAT& stress) = 0; + /** calculate kinetic part of heat flux */ + virtual void compute_kinetic_heatflux(DENS_MAT& flux); + /** calculate force part of the heat flux on-the-fly */ + virtual void compute_potential_heatflux(DENS_MAT& flux) = 0; + /** compute molecule to nodal quantities */ + void compute_dipole_moment(DENS_MAT & dipole_moment); + void compute_quadrupole_moment(DENS_MAT & quadrupole_moment); + + /** calculate dislocation density tensor from DXA output */ + virtual void compute_dislocation_density(DENS_MAT & dislocation_density) = 0; + + /** compute smooth fields */ + void compute_fields(void); + void time_filter_pre (double dt); + void time_filter_post(double dt); + + /** mapping of atomic pairs to pair index value */ + class PairMap * pairMap_; + class BondMatrix * bondMatrix_; + class PairVirial * pairVirial_; + class PairPotentialHeatFlux * pairHeatFlux_; + + /** routine to calculate matrix of force & position dyads */ + void compute_force_matrix(); + + /** routine to calculate matrix of heat flux vector components */ + void compute_heat_matrix(); + + /** routine to calculate matrix of kernel functions */ + virtual void compute_kernel_matrix_molecule() = 0; //KKM add + + /** calculate projection on the fly*/ + // REFACTOR use AtfKernelFunctionRestriction and derivatives + virtual void compute_projection(const DENS_MAT & atomData, + DENS_MAT & nodeData) = 0; + + /** routine to calculate matrix of bond functions */ + virtual void compute_bond_matrix(); + + /** routine to set xPointer to xref or xatom */ + void set_xPointer(); + + /** number of atom types */ + int nTypes_; + + /** project : given w_\alpha, + w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + // REFACTOR AtfShapeFunctionRestriction + void project(const DENS_MAT & atomData, + DENS_MAT & nodeData); + void project_molecule(const DENS_MAT & molData, + DENS_MAT & nodeData); //KKM add + void project_molecule_gradient(const DENS_MAT & molData, + DENS_MAT & nodeData); //KKM add + + /** project (number density): given w_\alpha, + w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + // REFACTOR AtfNodeWeightedShapeFunctionRestriction + void project_count_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData); + + /** hardy_project (volume density): given w_\alpha, + w_I = 1/\Omega_I \sum_\alpha N_{I\alpha} w_\alpha + where \Omega_I = \int_{support region of node I} N_{I} dV */ + // REFACTOR AtfNodeWeightedShapeFunctionRestriction + void project_volume_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData); + void project_volume_normalized_molecule(const DENS_MAT & molData, + DENS_MAT & nodeData); // KKM add + void project_volume_normalized_molecule_gradient(const DENS_MAT & molData, + DENS_MAT & nodeData); // KKM add + + + /** gradient_compute: given w_I, + w_J = \sum_I N_I'{xJ} \dyad w_I + where N_I'{xJ} is the gradient of the normalized + shape function of node I evaluated at node J */ + // REFACTOR MatToGradBySparse + void gradient_compute(const DENS_MAT & inNodeData, + DENS_MAT & outNodeData); + + int nNodesGlobal_; + int nComputes_; + + /** workset data */ + VectorDependencyManager * gradientMatrix_; + + + SPAR_MAT atomicBondMatrix_; + DENS_MAT atomicForceMatrix_; + DENS_MAT atomicHeatMatrix_; + + /** use pair/bond forces */ + bool hasPairs_; + bool hasBonds_; + + /** need to reset kernel function and bond matrix */ + bool resetKernelFunction_; + + /** use "exact" serial mode when using DXA to compute dislocation densities */ + bool dxaExactMode_; + + /** a continuum model to compare to and/or estimate quantities */ + StressCauchyBorn * cauchyBornStress_; + + Array timeFilters_; + + /** check consistency of fieldFlags_ */ + void check_field_dependencies(); + +}; + +}; + +#endif diff --git a/lib/atc/ATC_TransferKernel.cpp b/lib/atc/ATC_TransferKernel.cpp new file mode 100644 index 0000000000..c4e2a2928d --- /dev/null +++ b/lib/atc/ATC_TransferKernel.cpp @@ -0,0 +1,484 @@ +// ATC headers +#include "ATC_TransferKernel.h" +#include "ATC_Error.h" +#include "FE_Engine.h" +#include "KernelFunction.h" +#include "LammpsInterface.h" +#include "Quadrature.h" +#include "PerPairQuantity.h" +#include "Stress.h" +#ifdef HAS_DXA +#include "DislocationExtractor.h" +#endif + +// Other Headers +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace ATC { + +using ATC_Utility::to_string; + + ATC_TransferKernel::ATC_TransferKernel(string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile) + : ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile) + { + kernelBased_ = true; + } + + //------------------------------------------------------------------- + ATC_TransferKernel::~ATC_TransferKernel() + { + } + + //------------------------------------------------------------------- + bool ATC_TransferKernel::modify(int narg, char **arg) + { + bool match = false; + + /*! \page man_hardy_kernel fix_modify AtC kernel + \section syntax + fix_modify AtC kernel + - type (keyword) = step, cell, cubic_bar, cubic_cylinder, cubic_sphere, + quartic_bar, quartic_cylinder, quartic_sphere \n + - parameters :\n + step = radius (double) \n + cell = hx, hy, hz (double) or h (double) \n + cubic_bar = half-width (double) \n + cubic_cylinder = radius (double) \n + cubic_sphere = radius (double) \n + quartic_bar = half-width (double) \n + quartic_cylinder = radius (double) \n + quartic_sphere = radius (double) \n + \section examples + fix_modify AtC kernel cell 1.0 1.0 1.0 + fix_modify AtC kernel quartic_sphere 10.0 + \section description + + \section restrictions + Must be used with the hardy AtC fix \n + For bar kernel types, half-width oriented along x-direction \n + For cylinder kernel types, cylindrical axis is assumed to be in z-direction \n + ( see \ref man_fix_atc ) + \section related + \section default + No default + */ + + // no match, call base class parser + if (!match) { + match = ATC_Transfer::modify(narg, arg); + } + + return match; + + } + + //------------------------------------------------------------------- + void ATC_TransferKernel::compute_kernel_matrix_molecule(void) // KKM add + { + int nLocalMol = smallMoleculeSet_->local_molecule_count(); + if (nLocal_>0) { + SPAR_MAT & N(kernelAccumulantMol_.set_quantity()); + N.reset(nLocalMol,nNodes_); + SPAR_MAT & dN(kernelAccumulantMolGrad_.set_quantity()); + dN.reset(nLocalMol,nNodes_); + DENS_VEC derivKer(nsd_); + DENS_VEC xI(nsd_),xm(nsd_),xmI(nsd_); + const DENS_MAT & centroidMolMatrix(moleculeCentroid_->quantity()); + ATC::LammpsInterface::instance()->stream_msg_once("computing kernel matrix molecule ",true,false); + int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10); + for (int i = 0; i < nNodes_; i++) { + if (i % heartbeatFreq == 0 ) { + ATC::LammpsInterface::instance()->stream_msg_once(".",false,false); + } + xI = (feEngine_->fe_mesh())->nodal_coordinates(i); + for (int j = 0; j < nLocalMol; j++) { + for (int k = 0; k < nsd_; k++) { + xm(k) = centroidMolMatrix(j,k); + } + xmI = xm - xI; + lammpsInterface_->periodicity_correction(xmI.ptr()); + double val = kernelFunction_->value(xmI); + if (val > 0) N.add(j,i,val); + kernelFunction_->derivative(xmI,derivKer); + double val_grad = derivKer(2); + if (val_grad!= 0) dN.add(j,i,val_grad); + } + } + // reset kernelShpFunctions with the weights of molecules on processors + DENS_VEC fractions(N.nRows()); + DENS_VEC fractions_deriv(dN.nRows()); + for (int i = 0; i < nLocalMol; i++) { + fractions(i) = smallMoleculeSet_->local_fraction(i); + } + N.row_scale(fractions); + N.compress(); + dN.row_scale(fractions); + dN.compress(); + if (lammpsInterface_->rank_zero()) { + ATC::LammpsInterface::instance()->stream_msg_once("done",false,true); + } + } + } + + //------------------------------------------------------------------- + void ATC_TransferKernel::compute_projection(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + DENS_MAT workNodeArray(nNodes_, atomData.nCols()); + workNodeArray.zero(); + nodeData.reset(workNodeArray.nRows(),workNodeArray.nCols()); + nodeData.zero(); + + if (nLocal_>0) { + DENS_VEC xI(nsd_),xa(nsd_),xaI(nsd_); + double val; + for (int i = 0; i < nNodes_; i++) { + xI = (feEngine_->fe_mesh())->nodal_coordinates(i); + for (int j = 0; j < nLocal_; j++) { + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + xaI = xa - xI; + lammpsInterface_->periodicity_correction(xaI.ptr()); + val = kernelFunction_->value(xaI); + if (val > 0) { + for (int k=0; k < atomData.nCols(); k++) { + workNodeArray(i,k) += val*atomData(j,k); + } + } + } + } + } + + // accumulate across processors + int count = workNodeArray.nRows()*workNodeArray.nCols(); + lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count); + } + + + //------------------------------------------------------------------- + void ATC_TransferKernel::compute_bond_matrix() + { + atomicBondMatrix_=bondMatrix_->quantity(); + } + + //------------------------------------------------------------------- + // on-the-fly calculation of stress + void ATC_TransferKernel::compute_potential_stress(DENS_MAT& stress) + { + set_xPointer(); + stress.zero(); + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + double lam1,lam2; + double bond_value; + // process differently for mesh vs translation-invariant kernels + ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false); + int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10); + // "normal" kernel functions + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + for (int i = 0; i < nNodes_; i++) { + if (i % heartbeatFreq == 0 ) { + ATC::LammpsInterface::instance()->stream_msg_once(".",false,false); + } + // point + xI = (feEngine_->fe_mesh())->nodal_coordinates(i); + if (!kernelFunction_->node_contributes(xI)) { + continue; + } + int inode = i; + for (int j = 0; j < nLocal_; j++) { + // second (neighbor) atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + // difference vector + xaI = xa - xI; + lammpsInterface_->periodicity_correction(xaI.ptr()); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + // first atom location + xb.copy(xPointer_[lammps_k],3); + // difference vector + xba = xb - xa; + xbI = xba + xaI; + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + // compute virial + if (lam1 < lam2) { + bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // dbl count + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + stress(inode,0) +=-delx*fforce*delX*bond_value; + stress(inode,1) +=-delx*fforce*delY*bond_value; + stress(inode,2) +=-delx*fforce*delZ*bond_value; + stress(inode,3) +=-dely*fforce*delX*bond_value; + stress(inode,4) +=-dely*fforce*delY*bond_value; + stress(inode,5) +=-dely*fforce*delZ*bond_value; + stress(inode,6) +=-delz*fforce*delX*bond_value; + stress(inode,7) +=-delz*fforce*delY*bond_value; + stress(inode,8) +=-delz*fforce*delZ*bond_value; + } + else { //EULERIAN + stress(inode,0) +=-delx*delx*fforce*bond_value; + stress(inode,1) +=-dely*dely*fforce*bond_value; + stress(inode,2) +=-delz*delz*fforce*bond_value; + stress(inode,3) +=-delx*dely*fforce*bond_value; + stress(inode,4) +=-delx*delz*fforce*bond_value; + stress(inode,5) +=-dely*delz*fforce*bond_value; + } + } + } + } + } + ATC::LammpsInterface::instance()->stream_msg_once("done",false,true); + } + + //------------------------------------------------------------------- + // on-the-fly calculation of the heat flux + void ATC_TransferKernel::compute_potential_heatflux(DENS_MAT& flux) + { + set_xPointer(); + flux.zero(); + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + double ** vatom = lammpsInterface_->vatom(); + + double lam1,lam2; + double bond_value; + // process differently for mesh vs translation-invariant kernels + // "normal" kernel functions + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + for (int i = 0; i < nNodes_; i++) { + int inode = i; + // point + xI = (feEngine_->fe_mesh())->nodal_coordinates(i); + if (!kernelFunction_->node_contributes(xI)) { + continue; + } + for (int j = 0; j < nLocal_; j++) { + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + // difference vector + xaI = xa - xI; + lammpsInterface_->periodicity_correction(xaI.ptr()); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + // first atom location + xb.copy(xPointer_[lammps_k],3); + // difference vector + xba = xb - xa; + xbI = xba + xaI; + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + // compute virial + if (lam1 < lam2) { + bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // dbl count + double * v = vatom[lammps_j]; + fforce *= (delx*v[0] + dely*v[1] + delz*v[2]); + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + flux(inode,0) +=fforce*delX*bond_value; + flux(inode,1) +=fforce*delY*bond_value; + flux(inode,2) +=fforce*delZ*bond_value; + } + else { // EULERIAN + flux(inode,0) +=fforce*delx*bond_value; + flux(inode,1) +=fforce*dely*bond_value; + flux(inode,2) +=fforce*delz*bond_value; + } + } + } + } + } + } + + //------------------------------------------------------------------- + // calculation of the dislocation density tensor + void ATC_TransferKernel::compute_dislocation_density(DENS_MAT & A) + { + A.reset(nNodes_,9); +#ifdef HAS_DXA + double cnaCutoff = lammpsInterface_->near_neighbor_cutoff(); + // Extract dislocation lines within the processor's domain. + DXADislocationExtractor extractor(lammpsInterface_->lammps_pointer(),dxaExactMode_); + extractor.extractDislocations(lammpsInterface_->neighbor_list(), cnaCutoff); + + // Calculate scalar dislocation density and density tensor. + double dislocationDensity = 0.0; + double dislocationDensityTensor[9] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; + + + const std::vector& segments = extractor.getSegments(); + int localNumberLines = (int) segments.size(); + int totalNumberLines; + lammpsInterface_->int_allsum(&localNumberLines,&totalNumberLines,1); + if (totalNumberLines == 0) { + ATC::LammpsInterface::instance()->print_msg_once("no dislocation lines found"); + return; + } + + // for output + int nPt = 0, nSeg = 0; + for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) { + DislocationSegment* segment = segments[segmentIndex]; + const std::deque& line = segment->line; + nPt += line.size(); + nSeg += line.size()-1; + } + + DENS_MAT segCoor(3,nPt); + Array2D segConn(2,nSeg); + DENS_MAT segBurg(nPt,3); + + + DENS_MAT local_A(nNodes_,9); + local_A.zero(); + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + int iPt = 0, iSeg= 0; + for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) { + + DislocationSegment* segment = segments[segmentIndex]; + const std::deque& line = segment->line; + + Vector3 burgers = segment->burgersVectorWorld; + Point3 x1, x2; + for(std::deque::const_iterator p1 = line.begin(), p2 = line.begin() + 1; p2 < line.end(); ++p1, ++p2) { + x1 = (*p1); + x2 = (*p2); + Vector3 delta = x2 - x1; + // totals + dislocationDensity += Length(delta); + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 3; j++) { + dislocationDensityTensor[3*j+i] += delta[i] * burgers[j]; + } + } + // nodal partition + for(int k = 0; k < 3; k++) { + xa(k) = x1[k]; + xb(k) = x2[k]; + xba(k) = delta[k]; + } + for (int I = 0; I < nNodes_; I++) { + xI = (feEngine_->fe_mesh())->nodal_coordinates(I); + if (!kernelFunction_->node_contributes(xI)) { + continue; + } + xaI = xa - xI; + lammpsInterface_->periodicity_correction(xaI.ptr()); + xbI = xba + xaI; + double lam1=0,lam2=0; + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + if (lam1 < lam2) { + double bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + local_A(I,0) += xba(0)*burgers[0]*bond_value; + local_A(I,1) += xba(0)*burgers[1]*bond_value; + local_A(I,2) += xba(0)*burgers[2]*bond_value; + local_A(I,3) += xba(1)*burgers[0]*bond_value; + local_A(I,4) += xba(1)*burgers[1]*bond_value; + local_A(I,5) += xba(1)*burgers[2]*bond_value; + local_A(I,6) += xba(2)*burgers[0]*bond_value; + local_A(I,7) += xba(2)*burgers[1]*bond_value; + local_A(I,8) += xba(2)*burgers[2]*bond_value; + } + } + segCoor(0,iPt) = x1[0]; + segCoor(1,iPt) = x1[1]; + segCoor(2,iPt) = x1[2]; + segBurg(iPt,0) = burgers[0]; + segBurg(iPt,1) = burgers[1]; + segBurg(iPt,2) = burgers[2]; + segConn(0,iSeg) = iPt; + segConn(1,iSeg) = iPt+1; + iPt++; + iSeg++; + } + segCoor(0,iPt) = x2[0]; + segCoor(1,iPt) = x2[1]; + segCoor(2,iPt) = x2[2]; + segBurg(iPt,0) = burgers[0]; + segBurg(iPt,1) = burgers[1]; + segBurg(iPt,2) = burgers[2]; + iPt++; + } + + int count = nNodes_*9; + lammpsInterface_->allsum(local_A.ptr(),A.ptr(),count); + + double totalDislocationDensity; + lammpsInterface_->allsum(&dislocationDensity,&totalDislocationDensity,1); + double totalDislocationDensityTensor[9]; + lammpsInterface_->allsum(dislocationDensityTensor,totalDislocationDensityTensor,9); + int totalNumberSegments; + lammpsInterface_->int_allsum(&nSeg,&totalNumberSegments,1); + + // output + double volume = lammpsInterface_->domain_volume(); + stringstream ss; + ss << "total dislocation line length = " << totalDislocationDensity; + ss << " lines = " << totalNumberLines << " segments = " << totalNumberSegments; + ss << "\n "; + ss << "total dislocation density tensor = \n"; + for(int i = 0; i < 3; i++) { + ss << " "; + for(int j = 0; j < 3; j++) { + totalDislocationDensityTensor[3*j+i] /= volume; + ss << totalDislocationDensityTensor[3*j+i] << " "; + } + ss << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); + if (nSeg > 0) { + set otypes; + otypes.insert(VTK); + otypes.insert(FULL_GNUPLOT); + string name = "dislocation_segments_step=" ; + name += to_string(output_index()); + OutputManager segOutput(name,otypes); + segOutput.write_geometry(&segCoor,&segConn); + OUTPUT_LIST segOut; + segOut["burgers_vector"] = &segBurg; + segOutput.write_data(0,&segOut); + } +#else + throw ATC_Error("unimplemented function compute_dislocation_density (DXA support not included"); +#endif + } + +} // end namespace ATC + diff --git a/lib/atc/ATC_TransferKernel.h b/lib/atc/ATC_TransferKernel.h new file mode 100644 index 0000000000..2443940e7e --- /dev/null +++ b/lib/atc/ATC_TransferKernel.h @@ -0,0 +1,53 @@ +/** ATC_Transfer : smoothing */ +#ifndef ATC_TRANSFER_KERNEL_H +#define ATC_TRANSFER_KERNEL_H + +// ATC headers +#include "ATC_Transfer.h" + +namespace ATC { + +class KernelFunction; + +class ATC_TransferKernel : public ATC_Transfer { + + public: + + // constructor + ATC_TransferKernel(std::string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile = "none"); + + // destructor + virtual ~ATC_TransferKernel(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + protected: + /** routine to calculate matrix of kernel functions */ + + virtual void compute_kernel_matrix_molecule(); + + /** calculate projection on the fly*/ + virtual void compute_projection(const DENS_MAT & atomData, + DENS_MAT & nodeData); + + /** routine to calculate matrix of bond functions */ + virtual void compute_bond_matrix(); + + /** routine to calculate stress on-the-fly */ + virtual void compute_potential_stress(DENS_MAT& stress); + + /** routine to calculate force part of the heat flux on-the-fly */ + virtual void compute_potential_heatflux(DENS_MAT& flux); + + /** calculate dislocation density tensor from DXA output */ + virtual void compute_dislocation_density(DENS_MAT & dislocation_density); + +}; + +}; + +#endif diff --git a/lib/atc/ATC_TransferPartitionOfUnity.cpp b/lib/atc/ATC_TransferPartitionOfUnity.cpp new file mode 100644 index 0000000000..a847a8ee3b --- /dev/null +++ b/lib/atc/ATC_TransferPartitionOfUnity.cpp @@ -0,0 +1,506 @@ +// ATC headers +#include "ATC_TransferPartitionOfUnity.h" +#include "ATC_Error.h" +#include "FE_Engine.h" +#include "LammpsInterface.h" +#include "Quadrature.h" +#include "PerPairQuantity.h" +#ifdef HAS_DXA +#include "DislocationExtractor.h" +#endif + + +// Other Headers +#include +#include +#include +#include +#include +#include + +using namespace std; + + +static const int line_ngauss = 10; +static double line_xg[line_ngauss], line_wg[line_ngauss]; + +namespace ATC { + + ATC_TransferPartitionOfUnity::ATC_TransferPartitionOfUnity( + string groupName, + double ** & perAtomArray, + LAMMPS_NS::Fix * thisFix, + string matParamFile) + : ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile) + { + ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg); + // transform gauss points from [-1,1] to [0,1] + double lam1 = 0.0, lam2 = 1.0; + double del_lambda = 0.5*(lam2 - lam1); + double avg_lambda = 0.5*(lam2 + lam1); + for (int i = 0; i < line_ngauss; i++) { + double lambda = del_lambda*line_xg[i] +avg_lambda; + line_xg[i] = lambda; + line_wg[i] *= 0.5; + } + } + + //------------------------------------------------------------------- + ATC_TransferPartitionOfUnity::~ATC_TransferPartitionOfUnity() + { + // clear out all managed memory to avoid conflicts with dependencies on class member data + interscaleManager_.clear(); + } + + //------------------------------------------------------------------- + void ATC_TransferPartitionOfUnity::compute_projection( + const DENS_MAT & atomData, DENS_MAT & nodeData) + { + throw ATC_Error("unimplemented function"); + } + + //------------------------------------------------------------------- + void ATC_TransferPartitionOfUnity::compute_bond_matrix() + { + atomicBondMatrix_ = bondMatrix_->quantity(); + } + + //------------------------------------------------------------------- + // kinetic energy portion of stress + + /** + * @class KineticTensor + * @brief Class for computing the quantity - m v' (x) v' + */ + + void ATC_TransferPartitionOfUnity::compute_kinetic_stress( + DENS_MAT& stress) + { + compute_variation_velocity(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy] + + DENS_MAT & v = variationVelocity_; + + atomicTensor_.reset(nLocal_,6); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + atomicTensor_(i,0) -= ma*v(i,0)*v(i,0); + atomicTensor_(i,1) -= ma*v(i,1)*v(i,1); + atomicTensor_(i,2) -= ma*v(i,2)*v(i,2); + atomicTensor_(i,3) -= ma*v(i,0)*v(i,1); + atomicTensor_(i,4) -= ma*v(i,0)*v(i,2); + atomicTensor_(i,5) -= ma*v(i,1)*v(i,2); + } + project_volume_normalized(atomicTensor_, stress); + } + + //------------------------------------------------------------------- + // on-the-fly calculation of stress + void ATC_TransferPartitionOfUnity::compute_potential_stress(DENS_MAT& stress) + { + int nCols; + if (atomToElementMapType_ == LAGRANGIAN) + nCols = 9; + else // EULERIAN + nCols = 6; + stress.reset(nNodes_,nCols); + + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + // process differently for mesh vs translation-invariant kernels + ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false); + int heartbeatFreq = (nLocal_ <= 10 ? 1 : (int) nLocal_ / 10); + // mesh-based kernel functions + int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); + Array node_list(nodesPerElement); + DENS_VEC shp(nodesPerElement); + DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); + DENS_VEC virial(nCols); + for (int j = 0; j < nLocal_; j++) { + if (j % heartbeatFreq == 0 ) { + ATC::LammpsInterface::instance()->stream_msg_once(".",false,false); + } + // first atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + //if (lammps_k < lammps_j) continue; // full neighbor list + // second (neighbor) atom location + xb.copy(xPointer_[lammps_k],3); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // 1/2 sum_ab = sum_(ab) + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + virial[0] =-delx*fforce*delX; + virial[1] =-delx*fforce*delY; + virial[2] =-delx*fforce*delZ; + virial[3] =-dely*fforce*delX; + virial[4] =-dely*fforce*delY; + virial[5] =-dely*fforce*delZ; + virial[6] =-delz*fforce*delX; + virial[7] =-delz*fforce*delY; + virial[8] =-delz*fforce*delZ; + } + else {// EULERIAN + virial[0] =-delx*delx*fforce; + virial[1] =-dely*dely*fforce; + virial[2] =-delz*delz*fforce; + virial[3] =-delx*dely*fforce; + virial[4] =-delx*delz*fforce; + virial[5] =-dely*delz*fforce; + } + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + double lambda = line_xg[i]; + xlambda = lambda*xab + xb; + + lammpsInterface_->periodicity_correction(xlambda.ptr()); + feEngine_->shape_functions(xlambda,shp,node_list); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodesPerElement; I++) { + int inode = node_list(I); + double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); + double bond_value = inv_vol*shp(I)*line_wg[i]; + for (int j = 0; j < nCols; j++) + stress(inode,j) += virial[j]*bond_value; + } + } + } + } + if (lammpsInterface_->comm_rank() == 0) { + ATC::LammpsInterface::instance()->stream_msg_once("done",false,true); + } + } + + //------------------------------------------------------------------- + // compute kinetic part of heat flux + void ATC_TransferPartitionOfUnity::compute_kinetic_heatflux( + DENS_MAT& flux) + { + compute_variation_velocity(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double mvv2e = lammpsInterface_->mvv2e(); + double * atomPE = lammpsInterface_->compute_pe_peratom(); + double atomKE, atomEnergy; + atomicVector_.reset(nLocal_,3); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx]; + ma *= mvv2e; // convert mass to appropriate units + atomKE = 0.0; + for (int j = 0; j < nsd_; j++) { + atomKE += 0.5*ma*(variationVelocity_(i,j)*variationVelocity_(i,j)); + } + atomEnergy = atomKE + atomPE[atomIdx]; + for (int j = 0; j < nsd_; j++) { + atomicVector_(i,j) += atomEnergy*variationVelocity_(i,j); + } + } + project_volume_normalized(atomicVector_,flux); + } + + //------------------------------------------------------------------- + // on-the-fly calculation of the heat flux + void ATC_TransferPartitionOfUnity::compute_potential_heatflux(DENS_MAT& flux) + { + compute_variation_velocity(); + flux.zero(); + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + // process differently for mesh vs translation-invariant kernels + // mesh-based kernel functions + int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); + Array node_list(nodesPerElement); + DENS_VEC shp(nodesPerElement); + DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); + for (int j = 0; j < nLocal_; j++) { + // first atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + // second (neighbor) atom location + xb.copy(xPointer_[lammps_k],3); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // 1/2 sum_ab = sum_(ab) + fforce *= (delx*variationVelocity_(j,0) + + dely*variationVelocity_(j,1) + + delz*variationVelocity_(j,2)); + double flux_vec[3]; + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + flux_vec[0] =fforce*delX; + flux_vec[1] =fforce*delY; + flux_vec[2] =fforce*delZ; + } + else {// EULERIAN + flux_vec[0] =fforce*delx; + flux_vec[1] =fforce*dely; + flux_vec[2] =fforce*delz; + } + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + double lambda = line_xg[i]; + xlambda = lambda*xab + xb; + + lammpsInterface_->periodicity_correction(xlambda.ptr()); + feEngine_->shape_functions(xlambda,shp,node_list); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodesPerElement; I++) { + int inode = node_list(I); + double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); + double bond_value = inv_vol*shp(I)*line_wg[i]; + flux(inode,0) += flux_vec[0]*bond_value; + flux(inode,1) += flux_vec[1]*bond_value; + flux(inode,2) += flux_vec[2]*bond_value; + } + } + } + } + } + + //------------------------------------------------------------------- + void ATC_TransferPartitionOfUnity::compute_variation_velocity() + { + // now compute v'_a = v_a - N_Ia v_I + variationVelocity_.reset(nLocal_,nsd_); + if (nLocal_>0) { + // interpolate nodal velocities to the atoms + vbar_.reset(nLocal_,nsd_); + double ** v = lammpsInterface_->vatom(); + PerAtomQuantity * vbar = interscaleManager_.per_atom_quantity(field_to_prolongation_name(VELOCITY)); + if (!vbar) { + DENS_MAN * nodeVelocity = interscaleManager_.dense_matrix(field_to_string(VELOCITY)); + if (this->kernel_on_the_fly()) { + vbar = new OnTheFlyShapeFunctionProlongation(this, + nodeVelocity,this->atom_coarsegraining_positions()); + } else { + vbar = new FtaShapeFunctionProlongation(this, + nodeVelocity,this->interpolant()); + } + interscaleManager_.add_per_atom_quantity(vbar, + field_to_prolongation_name(VELOCITY)); + } + // use of prolong assumes atom system contained within mesh + vbar_ = vbar->quantity(); + // compute and store variation velocities of atoms + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + variationVelocity_(i,j) = v[atomIdx][j] - vbar_(i,j); + } + } + } + } + + //------------------------------------------------------------------- + // calculation of the dislocation density tensor + void ATC_TransferPartitionOfUnity::compute_dislocation_density(DENS_MAT & A) + { + + A.reset(nNodes_,9); +#ifdef HAS_DXA + double cnaCutoff = lammpsInterface_->near_neighbor_cutoff(); + // Extract dislocation lines within the processor's domain. + DXADislocationExtractor extractor(lammpsInterface_->lammps_pointer(),dxaExactMode_); + extractor.extractDislocations(lammpsInterface_->neighbor_list(), cnaCutoff); + + // Calculate scalar dislocation density and density tensor. + double dislocationDensity = 0.0; + double dislocationDensityTensor[9] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; + + + const std::vector& segments = extractor.getSegments(); + int localNumberLines = (int) segments.size(); + int totalNumberLines; + lammpsInterface_->int_allsum(&localNumberLines,&totalNumberLines,1); + if (totalNumberLines == 0) { + ATC::LammpsInterface::instance()->print_msg_once("no dislocation lines found"); + return; + } + + // for output + int nPt = 0, nSeg = 0; + for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) { + DislocationSegment* segment = segments[segmentIndex]; + const std::deque& line = segment->line; + nPt += line.size(); + nSeg += line.size()-1; + } + + DENS_MAT segCoor(3,nPt); + Array2D segConn(2,nSeg); + DENS_MAT segBurg(nPt,3); + + + DENS_MAT local_A(nNodes_,9); + local_A.zero(); + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + // mesh-based kernel functions + int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element(); + Array node_list(nodesPerElement); + DENS_VEC shp(nodesPerElement); + DENS_VEC xa(nsd_),xb(nsd_),xba(nsd_),xlambda(nsd_); + int iPt = 0, iSeg= 0; + for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) { + + DislocationSegment* segment = segments[segmentIndex]; + const std::deque& line = segment->line; + + Vector3 burgers = segment->burgersVectorWorld; + Point3 x1, x2; + for(std::deque::const_iterator p1 = line.begin(), p2 = line.begin() + 1; p2 < line.end(); ++p1, ++p2) { + x1 = (*p1); + x2 = (*p2); + Vector3 delta = x2 - x1; + // totals + dislocationDensity += Length(delta); + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 3; j++) { + dislocationDensityTensor[3*j+i] += delta[i] * burgers[j]; + } + } + // nodal partition + for(int k = 0; k < 3; k++) { + xa(k) = x1[k]; + xb(k) = x2[k]; + xba(k) = delta[k]; + } + for (int i = 0; i < line_ngauss; i++) { + double lambda = line_xg[i]; + xlambda = lambda*xba + xa; + + lammpsInterface_->periodicity_correction(xlambda.ptr()); + feEngine_->shape_functions(xlambda,shp,node_list); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodesPerElement; I++) { + int inode = node_list(I); + double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode); + double bond_value = inv_vol*shp(I)*line_wg[i]; + local_A(inode,0) += xba(0)*burgers[0]*bond_value; + local_A(inode,1) += xba(0)*burgers[1]*bond_value; + local_A(inode,2) += xba(0)*burgers[2]*bond_value; + local_A(inode,3) += xba(1)*burgers[0]*bond_value; + local_A(inode,4) += xba(1)*burgers[1]*bond_value; + local_A(inode,5) += xba(1)*burgers[2]*bond_value; + local_A(inode,6) += xba(2)*burgers[0]*bond_value; + local_A(inode,7) += xba(2)*burgers[1]*bond_value; + local_A(inode,8) += xba(2)*burgers[2]*bond_value; + } + } + segCoor(0,iPt) = x1[0]; + segCoor(1,iPt) = x1[1]; + segCoor(2,iPt) = x1[2]; + segBurg(iPt,0) = burgers[0]; + segBurg(iPt,1) = burgers[1]; + segBurg(iPt,2) = burgers[2]; + segConn(0,iSeg) = iPt; + segConn(1,iSeg) = iPt+1; + iPt++; + iSeg++; + } + segCoor(0,iPt) = x2[0]; + segCoor(1,iPt) = x2[1]; + segCoor(2,iPt) = x2[2]; + segBurg(iPt,0) = burgers[0]; + segBurg(iPt,1) = burgers[1]; + segBurg(iPt,2) = burgers[2]; + iPt++; + } + + int count = nNodes_*9; + lammpsInterface_->allsum(local_A.ptr(),A.ptr(),count); + + double totalDislocationDensity; + lammpsInterface_->allsum(&dislocationDensity,&totalDislocationDensity,1); + double totalDislocationDensityTensor[9]; + lammpsInterface_->allsum(dislocationDensityTensor,totalDislocationDensityTensor,9); + int totalNumberSegments; + lammpsInterface_->int_allsum(&nSeg,&totalNumberSegments,1); + + // output + double volume = lammpsInterface_->domain_volume(); + stringstream ss; + ss << "total dislocation line length = " << totalDislocationDensity; + ss << " lines = " << totalNumberLines << " segments = " << totalNumberSegments; + ss << "\n "; + ss << "total dislocation density tensor = \n"; + for(int i = 0; i < 3; i++) { + ss << " "; + for(int j = 0; j < 3; j++) { + totalDislocationDensityTensor[3*j+i] /= volume; + ss << totalDislocationDensityTensor[3*j+i] << " "; + } + ss << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); + ss.str(""); + DENS_VEC A_avg(9); + for (int i = 0; i < nNodes_; i++) { + for (int j = 0; j < 9; j++) { + A_avg(j) += A(i,j); + } + } + A_avg /= nNodes_; + ss << "average nodal dislocation density tensor = \n"; + ss << A_avg(0) << " " << A_avg(1) << " " << A_avg(2) << "\n"; + ss << A_avg(3) << " " << A_avg(4) << " " << A_avg(5) << "\n"; + ss << A_avg(6) << " " << A_avg(7) << " " << A_avg(8) << "\n"; + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); + + if (nSeg > 0) { + set otypes; + otypes.insert(VTK); + otypes.insert(FULL_GNUPLOT); + string name = "dislocation_segments_step=" ; + name += to_string(output_index()); + OutputManager segOutput(name,otypes); + segOutput.write_geometry(&segCoor,&segConn); + OUTPUT_LIST segOut; + segOut["burgers_vector"] = &segBurg; + segOutput.write_data(0,&segOut); + } +#else + throw ATC_Error("TransferParititionOfUnity::compute_dislocaton_density - unimplemented function"); +#endif + } + +} // end namespace ATC + diff --git a/lib/atc/ATC_TransferPartitionOfUnity.h b/lib/atc/ATC_TransferPartitionOfUnity.h new file mode 100644 index 0000000000..5c224d7c8e --- /dev/null +++ b/lib/atc/ATC_TransferPartitionOfUnity.h @@ -0,0 +1,59 @@ +#ifndef ATC_TRANSFER_PARTITION_OF_UNITY_H +#define ATC_TRANSFER_PARTITION_OF_UNITY_H + +// ATC headers +#include "ATC_Transfer.h" + +namespace ATC { + +class ATC_TransferPartitionOfUnity : public ATC_Transfer { + + public: + + // constructor + ATC_TransferPartitionOfUnity(std::string groupName, + double **& perAtomArray, + LAMMPS_NS::Fix * thisFix, + std::string matParamFile = "none"); + + // destructor + virtual ~ATC_TransferPartitionOfUnity(); + + protected: + /** routine to calculate matrix of kernel functions */ + virtual void compute_kernel_matrix_molecule() {}; + + /** calculate projection on the fly*/ + virtual void compute_projection(const DENS_MAT & atomData, + DENS_MAT & nodeData); + + /** routine to calculate matrix of bond functions */ + virtual void compute_bond_matrix(); + + /** routine to calculate kinetic part of stress */ + virtual void compute_kinetic_stress(DENS_MAT& stress); + + /** routine to calculate stress on-the-fly */ + virtual void compute_potential_stress(DENS_MAT& stress); + + /** routine to calculate kinetic part of heat flux */ + virtual void compute_kinetic_heatflux(DENS_MAT& flux); + + /** routine to calculate force part of the heat flux on-the-fly */ + virtual void compute_potential_heatflux(DENS_MAT& flux); + + /** compute velocity & difference betw atomic and (local) mean velocity */ + void compute_variation_velocity(); + + /** calculate dislocation density tensor from DXA output */ + virtual void compute_dislocation_density(DENS_MAT & dislocation_density); + + private: + + DENS_MAT variationVelocity_; + DENS_MAT vbar_; +}; + +} + +#endif diff --git a/lib/atc/ATC_TypeDefs.h b/lib/atc/ATC_TypeDefs.h new file mode 100644 index 0000000000..e3e5f794cf --- /dev/null +++ b/lib/atc/ATC_TypeDefs.h @@ -0,0 +1,622 @@ +#ifndef ATC_TYPEDEFS_H +#define ATC_TYPEDEFS_H + +#include +using std::pair; +using std::set; + +#ifdef NEW_LAMMPS +#include "lmptype.h" +#endif + +#include "Array.h" +#include "Array2D.h" + +using namespace ATC_matrix; + +#include "MatrixLibrary.h" +#include "DependencyManager.h" + +namespace ATC +{ + /** physical constants */ + static const double kBeV_ = 8.617343e-5;// [eV/K] + + /** unsigned ints, when needed */ + typedef int INDEX; + + /** elementset integral */ + enum ElementsetOperationType { + ELEMENTSET_TOTAL=0, + ELEMENTSET_AVERAGE + }; + /** faceset integral */ + enum FacesetIntegralType { + BOUNDARY_INTEGRAL=0, + CONTOUR_INTEGRAL + }; + + /** nodeset operation */ + enum NodesetOperationType { + NODESET_SUM=0, + NODESET_AVERAGE + }; + + /** boundary integration */ + enum BoundaryIntegrationType { + NO_QUADRATURE=0, + FE_QUADRATURE, + FE_INTERPOLATION + }; + /** domain integration */ + enum IntegrationDomainType { + FULL_DOMAIN=0, + ATOM_DOMAIN, + FE_DOMAIN, + FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE + }; + /** domain decomposition */ + enum DomainDecompositionType { + REPLICATED_MEMORY=0, + DISTRIBUTED_MEMORY + }; + /** atomic weight specification */ + enum AtomicWeightType { + USER=0, + LATTICE, + ELEMENT, + REGION, + GROUP, + MULTISCALE, + NODE, + NODE_ELEMENT, + READ_IN + }; + /** geometry location with respect to MD domain */ + enum GeometryType { + FE_ONLY = 0, + MD_ONLY, + BOUNDARY + }; + /** enumerated type for atomic reference frame */ + enum AtomToElementMapType { + LAGRANGIAN=0, + EULERIAN + }; + /* enumerated type for coupling matrix structure */ + enum MatrixStructure { + FULL=0, // contributions from all nodes + LOCALIZED, // contributions only from nodes with sources + LUMPED // row-sum lumped version of full matrix + }; + /* enumerated type for distinguishing ghost from internal atoms */ + enum AtomType { + INTERNAL=0, + GHOST, + ALL, + PROC_GHOST, + NO_ATOMS, + NUM_ATOM_TYPES + }; + /** field types */ + enum FieldName { + TIME=-2, + POSITION=-1, + TEMPERATURE=0, // Intrinsic Fields + DISPLACEMENT, + VELOCITY, + MASS_DENSITY, + CHARGE_DENSITY, + SPECIES_CONCENTRATION, + ELECTRON_DENSITY, // Extrinsic Fields + ELECTRON_VELOCITY, + ELECTRON_TEMPERATURE, + ELECTRIC_POTENTIAL, + ELECTRON_WAVEFUNCTION, + ELECTRON_WAVEFUNCTIONS, + ELECTRON_WAVEFUNCTION_ENERGIES, + FERMI_ENERGY, + MOMENTUM, + PROJECTED_VELOCITY, + KINETIC_TEMPERATURE, + THERMAL_ENERGY, + KINETIC_ENERGY, + STRESS, + HEAT_FLUX, + CHARGE_FLUX, + SPECIES_FLUX, + INTERNAL_ENERGY, + REFERENCE_POTENTIAL_ENERGY, + POTENTIAL_ENERGY, + ENERGY, + NUMBER_DENSITY, + ESHELBY_STRESS, + CAUCHY_BORN_STRESS, + CAUCHY_BORN_ENERGY, + CAUCHY_BORN_ESHELBY_STRESS, + TRANSFORMED_STRESS, + VACANCY_CONCENTRATION, + ROTATION, + STRETCH, + DIPOLE_MOMENT, + QUADRUPOLE_MOMENT, + CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT, + DISLOCATION_DENSITY, + NUM_TOTAL_FIELDS + }; + const int NUM_FIELDS = ELECTRON_WAVEFUNCTION+1; + +#define NDIM 3 + static const int FieldSizes[NUM_TOTAL_FIELDS] = { + 1, // TEMPERATURE + NDIM, // DISPLACEMENT + NDIM, // VELOCITY + 1, // MASS_DENSITY + 1, // CHARGE_DENSITY + 0, // SPECIES_CONCENTRATION - VARIABLE + 1, // ELECTRON_DENSITY + NDIM, // ELECTRON_VELOCITY + 1, // ELECTRON_TEMPERATURE + 1, // ELECTRIC_POTENTIAL + 1, // ELECTRON_WAVEFUNCTION ? + 0, // ELECTRON_WAVEFUNCTIONS - VARIABLE + 0, // ELECTRON_WAVEFUNCTION_ENERGIES - VARIABLE + 1, // FERMI_ENERGY + NDIM, // MOMENTUM + NDIM, // PROJECTED_VELOCITY + 1, // KINETIC_TEMPERATURE + 1, // THERMAL_ENERGY + 1, // KINETIC_ENERGY + NDIM*NDIM, // STRESS + NDIM, // HEAT_FLUX + NDIM, // CHARGE_FLUX + 0, // SPECIES_FLUX - VARIABLE + 1, // INTERNAL_ENERGY + 1, // REFERENCE_POTENTIAL_ENERGY + 1, // POTENTIAL_ENERGY + 1, // ENERGY + 1, // NUMBER_DENSITY + NDIM*NDIM, // ESHELBY_STRESS + NDIM*NDIM, // CAUCHY_BORN_STRESS, + 1, // CAUCHY_BORN_ENERGY, + NDIM*NDIM, // CAUCHY_BORN_ESHELBY_STRESS, + NDIM*NDIM, // TRANSFORMED_STRESS, + 1, // VACANCY_CONCENTRATION, + NDIM*NDIM, // ROTATION, + NDIM*NDIM, // STRETCH, + NDIM, // DIPOLE_MOMENT, + NDIM, // QUADRUPOLE_MOMENT, + NDIM*NDIM, // CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT, + NDIM*NDIM // DISLOCATION_DENSITY + }; + + enum hardyNormalization { + NO_NORMALIZATION=0, + VOLUME_NORMALIZATION, NUMBER_NORMALIZATION, MASS_NORMALIZATION + }; + + /** enums for FE Element and Interpolate classes */ + enum FeEltGeometry {HEXA, TETRA}; + enum FeIntQuadrature {NODAL, GAUSS1, GAUSS2, GAUSS3, FACE}; + + /** field name enum to string */ + inline FeIntQuadrature string_to_FIQ(const string &str) + { + if (str == "nodal") + return NODAL; + else if (str == "gauss1") + return GAUSS1; + else if (str == "gauss2") + return GAUSS2; + else if (str == "gauss3") + return GAUSS3; + else if (str == "face") + return FACE; + else + throw ATC_Error("Bad quadrature input" + str + "."); + } + + /** field name enum to string */ + inline string field_to_string(const FieldName index) + { + switch (index) { + case TEMPERATURE: + return "temperature"; + case DISPLACEMENT: + return "displacement"; + case VELOCITY: + return "velocity"; + case MASS_DENSITY: + return "mass_density"; + case CHARGE_DENSITY: + return "charge_density"; + case ELECTRON_DENSITY: + return "electron_density"; + case ELECTRON_VELOCITY: + return "electron_velocity"; + case ELECTRON_TEMPERATURE: + return "electron_temperature"; + case ELECTRIC_POTENTIAL: + return "electric_potential"; + case ELECTRON_WAVEFUNCTION: + return "electron_wavefunction"; + case ELECTRON_WAVEFUNCTIONS: + return "electron_wavefunctions"; + case ELECTRON_WAVEFUNCTION_ENERGIES: + return "electron_wavefunction_energies"; + case FERMI_ENERGY: + return "fermi_energy"; + case MOMENTUM: + return "momentum"; + case PROJECTED_VELOCITY: + return "projected_velocity"; + case KINETIC_TEMPERATURE: + return "kinetic_temperature"; + case THERMAL_ENERGY: + return "thermal_energy"; + case KINETIC_ENERGY: + return "kinetic_energy"; + case STRESS: + return "stress"; + case ESHELBY_STRESS: + return "eshelby_stress"; + case CAUCHY_BORN_STRESS: + return "cauchy_born_stress"; + case CAUCHY_BORN_ENERGY: + return "cauchy_born_energy"; + case CAUCHY_BORN_ESHELBY_STRESS: + return "cauchy_born_eshelby_stress"; + case HEAT_FLUX: + return "heat_flux"; + case CHARGE_FLUX: + return "charge_flux"; + case SPECIES_FLUX: + return "species_flux"; + case INTERNAL_ENERGY: + return "internal_energy"; + case POTENTIAL_ENERGY: + return "potential_energy"; + case REFERENCE_POTENTIAL_ENERGY: + return "reference_potential_energy"; + case ENERGY: + return "energy"; + case NUMBER_DENSITY: + return "number_density"; + case TRANSFORMED_STRESS: + return "transformed_stress"; + case VACANCY_CONCENTRATION: + return "vacancy_concentration"; + case SPECIES_CONCENTRATION: + return "species_concentration"; + case ROTATION: + return "rotation"; + case STRETCH: + return "stretch"; + case DIPOLE_MOMENT: + return "dipole_moment"; + case QUADRUPOLE_MOMENT: + return "quadrupole_moment"; + default: + throw ATC_Error("field not found in field_to_string"); + } + }; + + /** string to field enum */ + inline FieldName string_to_field(const string & name) + { + if (name=="temperature") + return TEMPERATURE; + else if (name=="displacement") + return DISPLACEMENT; + else if (name=="velocity") + return VELOCITY; + else if (name=="mass_density") + return MASS_DENSITY; + else if (name=="charge_density") + return CHARGE_DENSITY; + else if (name=="electron_density") + return ELECTRON_DENSITY; + else if (name=="electron_velocity") + return ELECTRON_VELOCITY; + else if (name=="electron_temperature") + return ELECTRON_TEMPERATURE; + else if (name=="electric_potential") + return ELECTRIC_POTENTIAL; + else if (name=="electron_wavefunction") + return ELECTRON_WAVEFUNCTION; + else if (name=="electron_wavefunctions") + return ELECTRON_WAVEFUNCTIONS; + else if (name=="electron_wavefunction_energies") + return ELECTRON_WAVEFUNCTION_ENERGIES; + else if (name=="fermi_energy") + return FERMI_ENERGY; + else if (name=="momentum") + return MOMENTUM; + else if (name=="projected_velocity") + return PROJECTED_VELOCITY; + else if (name=="kinetic_temperature") + return KINETIC_TEMPERATURE; // temperature from total KE + else if (name=="thermal_energy") + return THERMAL_ENERGY; + else if (name=="kinetic_energy") + return KINETIC_ENERGY; + else if (name=="stress") + return STRESS; + else if (name=="eshelby_stress") + return ESHELBY_STRESS; + else if (name=="cauchy_born_stress") + return CAUCHY_BORN_STRESS; + else if (name=="cauchy_born_energy") + return CAUCHY_BORN_ENERGY; + else if (name=="cauchy_born_eshelby_stress") + return CAUCHY_BORN_ESHELBY_STRESS; + else if (name=="heat_flux") + return HEAT_FLUX; + else if (name=="charge_flux") + return CHARGE_FLUX; + else if (name=="species_flux") + return SPECIES_FLUX; + else if (name=="internal_energy") + return INTERNAL_ENERGY; + else if (name=="reference_potential_energy") + return REFERENCE_POTENTIAL_ENERGY; + else if (name=="potential_energy") + return POTENTIAL_ENERGY; + else if (name=="energy") + return ENERGY; + else if (name=="number_density") + return NUMBER_DENSITY; + else if (name=="transformed_stress") + return TRANSFORMED_STRESS; + else if (name=="vacancy_concentration") + return VACANCY_CONCENTRATION; + else if (name=="species_concentration") + return SPECIES_CONCENTRATION; + else if (name=="rotation") + return ROTATION; + else if (name=="stretch") + return STRETCH; + else if (name=="dipole_moment") + return DIPOLE_MOMENT; + else if (name=="quadrupole_moment") + return QUADRUPOLE_MOMENT; + else + throw ATC_Error(name + " is not a valid field"); + }; + + inline bool is_intrinsic(const FieldName & field_enum) + { + if (field_enum==TEMPERATURE + || field_enum==DISPLACEMENT + || field_enum==VELOCITY + || field_enum==MASS_DENSITY + || field_enum==CHARGE_DENSITY + || field_enum==SPECIES_CONCENTRATION + || field_enum==KINETIC_TEMPERATURE + || field_enum==POTENTIAL_ENERGY + || field_enum==REFERENCE_POTENTIAL_ENERGY + ) return true; + else return false; + }; + + inline string field_to_intrinsic_name(const FieldName index) + { + if (is_intrinsic(index)) { + return "NodalAtomic"+ATC_Utility::to_cap(field_to_string(index)); + } + else { + throw ATC_Error("field "+field_to_string(index)+" is not an intrinsic field"); + } + } + inline string field_to_restriction_name(const FieldName index) + { + if (is_intrinsic(index)) { + return "Restricted"+ATC_Utility::to_cap(field_to_string(index)); + } + else { + throw ATC_Error("field "+field_to_string(index)+" is not an intrinsic field"); + } + } + inline string field_to_prolongation_name(const FieldName index) + { + return "Prolonged"+ATC_Utility::to_cap(field_to_string(index)); + } + + + /** types of temperature definitions */ + enum TemperatureDefType { + NONE = 0, + KINETIC, + TOTAL + }; + + /** types of ghost boundary conditions in momentum */ + enum BoundaryDynamicsType { + NO_BOUNDARY_DYNAMICS=0, + PRESCRIBED, + DAMPED_HARMONIC, + COUPLED + }; + + /** string to temperature definition enum */ + inline bool string_to_temperature_def(const string & name, TemperatureDefType & index) { + if (name=="none") + index = NONE; + else if (name=="kinetic") + index = KINETIC; + else if (name=="total") + index = TOTAL; + else { + throw ATC_Error("temperature operator type "+name+" not valid"); + return false; + } + + return true; + }; + + /** solver types */ + enum SolverType { DIRECT=0, ITERATIVE}; + enum DirichletType {DIRICHLET_PENALTY=0, DIRICHLET_CONDENSE}; + + /** physics types */ + enum PhysicsType + { + NO_PHYSICS=0, // for post-processing only + THERMAL, + ELASTIC, + SHEAR, + THERMO_ELASTIC, + SPECIES // aka Mass + }; + + /** rhs types */ + enum FluxType + { + FLUX = 0, // has a source weighted by gradient of shape function + SOURCE, // has a source term weighted by the shape function + PRESCRIBED_SOURCE, // has a prescribed source term + ROBIN_SOURCE, // has a Robin source term + EXTRINSIC_SOURCE, // has an extrinsic source term + NUM_FLUX + }; + + /** stiffness/ derivative of rhs types */ + enum StiffnessType + { + BB_STIFFNESS = 0, + NN_STIFFNESS, + BN_STIFFNESS, + NB_STIFFNESS, + NUM_STIFFNESS + }; + + /** LAMMPS atom type identifiers */ + enum IdType { + ATOM_TYPE=0, + ATOM_GROUP + }; + + /** molecule size identifiers */ + enum MolSize { + MOL_SMALL=0, + MOL_LARGE + }; + + /** basic */ + typedef pair PAIR; + + /** typedefs for compact set of bc values */ + typedef set < pair < int, double> > BC_SET; // node, value + typedef vector< BC_SET > BCS; // dof (node, value) + typedef set NSET; // nodeset + typedef set FSET; // faceset + typedef set ESET; // elementset + + /** typedefs for N and B integrand functions */ + typedef set ARG_NAMES; + typedef map > ARGS; + typedef MatrixDependencyManager FIELD; + typedef vector > GRAD_FIELD; + typedef map > FIELDS; + typedef map * > FIELD_POINTERS; + typedef map > FIELD_MATS; + typedef map > TAG_FIELDS; + typedef map > > GRAD_FIELDS; + typedef map > > GRAD_FIELD_MATS; + typedef map > MASS_MATS; + typedef map > CON_MASS_MATS; + typedef MatrixDependencyManager DENS_MAN; + typedef MatrixDependencyManager SPAR_MAN; + typedef MatrixDependencyManager PAR_SPAR_MAN; + typedef MatrixDependencyManager DIAG_MAN; + typedef MatrixDependencyManager PAR_DIAG_MAN; + + /** typedefs for WeakEquation evaluation */ + typedef Array2D RHS_MASK; + + /** typedefs for input/output */ + typedef map*> OUTPUT_LIST; + typedef map*> RESTART_LIST; + + typedef pair ID_PAIR; + typedef vector< pair > ID_LIST; + + /** misc typedefs */ + class XT_Function; + class UXT_Function; + typedef map > > SURFACE_SOURCE; + typedef map > > ROBIN_SURFACE_SOURCE; + typedef map > VOLUME_SOURCE; + typedef map > ATOMIC_DATA; + + /** typedefs for FE_Mesh */ + typedef map > NODE_SET_MAP; + typedef map > ELEMENT_SET_MAP; + typedef map > FACE_SET_MAP; + + /** string to index */ + // inline vs. static is used to avoid compiler warnings that the function isn't used + // the compiler seems to just check if static functions are used in the file they're + // declared in rather than all the files that include the header, + // same for arrays (but not primitives, e.g. ints) hopefully this also speeds up the code + inline bool string_to_index(const string & dim, int & index, int & sgn) + { + char dir; + if (dim.empty()) return false; + sgn = (dim[0] == '-') ? -1 : 1; + dir = dim[dim.size()-1]; // dir is last character + if (dir == 'x') index = 0; + else if (dir == 'y') index = 1; + else if (dir == 'z') index = 2; + else return false; + return true; + }; + + /** string to index */ + inline string index_to_string(const int &index) + { + if (index==0) return "x"; + else if (index==1) return "y"; + else if (index==2) return "z"; + return "unknown"; + }; + + /** string to index */ + inline bool string_to_index(const string &dim, int &index) + { + if (dim=="x") + index = 0; + else if (dim=="y") + index = 1; + else if (dim=="z") + index = 2; + else + return false; + + return true; + }; + + inline string print_mask(const Array2D & rhsMask) + { + string msg; + for (int i = 0; i < NUM_FIELDS; i++) { + FieldName field = (FieldName) i; + string name = field_to_string(field); + if (rhsMask(field,FLUX) + || rhsMask(field,SOURCE) + || rhsMask(field,PRESCRIBED_SOURCE) + || rhsMask(field,ROBIN_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,EXTRINSIC_SOURCE)) msg += " extrinsic_src"; + } + } + return msg; + } +} + +#endif diff --git a/lib/atc/Array.h b/lib/atc/Array.h new file mode 100644 index 0000000000..9df6cbbda2 --- /dev/null +++ b/lib/atc/Array.h @@ -0,0 +1,320 @@ +#ifndef ARRAY_H +#define ARRAY_H + +#include +#include +#include +#include + +// for macros +#include "MatrixDef.h" + +namespace ATC_matrix { + + /** + * @class Array + * @brief Base class for creating, sizing and operating on 1-D arrays of data + */ + +template +class Array { +public: + Array(); + Array(int len); + Array(const Array& A); + virtual ~Array(); + + // Resize and reinitialize the array + virtual void reset(int len); + //* resizes the matrix, copy what fits default to OFF + virtual void resize(int len, bool copy=false); + // Access method to get the element i: + T& operator() (int i); + const T& operator() (int i) const; + // Assignment + virtual Array& operator= (const Array &other); + virtual Array& operator= (const T &value); + // Get length of array + int size() const; + // Do I have this element? + bool has_member(T val) const; + // range + bool check_range(T min, T max) const; + void range(T & min, T & max) const; + // search an ordered array + int index(T& val) const; + // Return pointer to internal data + const T* data() const; + T* ptr() const; + // print + void print(std::string name = "") const; + // Dump templated type to disk; operation not safe for all types + void write_restart(FILE *f) const; + +protected: + int len_; + T *data_; +}; + +template +class AliasArray { +public: + AliasArray(); + AliasArray(const Array& A); + AliasArray(const AliasArray& A); + AliasArray(int len, T * A); + virtual ~AliasArray(); + virtual AliasArray& operator= (const Array &other); + virtual AliasArray& operator= (const T &value); + + const T& operator() (int i) const; + int size() const; + T* ptr() const; +protected: + int len_; + T *data_; +}; + +template +Array::Array(void) { + len_ = 0; + data_ = NULL; +} + +template +Array::Array(int len) { + len_ = len; + data_ = new T[len_]; +} + +template +Array::Array(const Array& A) { + len_ = A.len_; + if (A.data_==NULL) + data_ = NULL; + else { + data_ = new T[len_]; + for(int i=0;i +Array::~Array() { + if (data_ != NULL) delete[] data_; +} + +template +void Array::reset(int len) { + if (len_ == len) { // no size change; don't realloc memory + return; + } + else { // size change, realloc memory + len_ = len; + if (data_ != NULL) + delete[] data_; + if (len_ > 0) + data_ = new T[len_]; + else { + data_ = NULL; + len_ = 0; + } + } +} + +template +void Array::resize(int len, bool copy) { + if (len_ == len) { // no size change; don't realloc memory + return; + } + else { // size change, realloc memory + len_ = len; + if (len_ > 0) { + if (copy && data_ != NULL) { + Array temp(*this); + delete[] data_; + data_ = new T[len_]; + for (int i = 0 ; i < len_; i++) { + if (i < temp.size()) + data_[i] = temp.data_[i]; + } + } + else { + if (data_ != NULL) delete[] data_; + data_ = new T[len_]; + } + } + else { + data_ = NULL; + len_ = 0; + } + } +} + +template +T& Array::operator() (int i) { + return data_[i]; +} + +template +Array& Array::operator= (const Array &other) { + if (data_ == NULL) { // initialize my internal storage to match LHS + len_ = other.len_; + if (other.data_==NULL) + data_ = NULL; + else + data_ = new T[len_]; + } + for(int i=0;i +Array& Array::operator= (const T &value) { + for(int i=0;i +const T& Array::operator() (int i) const { + return data_[i]; +} + +template +int Array::size(void) const { + return len_; +} + +template +bool Array::has_member(T val) const { + int i; + bool retval = false; + for(i=0;i +bool Array::check_range(T min, T max) const { + int i; + for(i=0;i max) return false; + else if (val < min) return false; + } + return true; +} + +template +void Array::range(T& min, T& max) const { + int i; + min = max = data_[0]; + for(i=1;i max) max = val; + else if (val < min) min = val; + } +} + + +template +int Array::index(T& val) const { + int idx = -1; + int i; + for(i=0;i +void Array::write_restart(FILE *f) const { + fwrite(&len_,sizeof(int),1,f); + if (len_ > 0) + fwrite(data_,sizeof(T),len_,f); +} + +template +const T* Array::data() const { + return data_; +} +template +T* Array::ptr() const { + return data_; +} + +template +void Array::print(std::string name) const { + std::cout << "------- Begin "< +AliasArray::AliasArray(void) { +} + +template +AliasArray::AliasArray(const AliasArray & other) { + len_ = other.size(); + data_ = other.ptr(); +} + +// for a mem continguous slice +template +AliasArray::AliasArray(int len, T * ptr) { + len_ = len; + data_ = ptr; +} + +template +AliasArray::AliasArray(const Array& A) { + len_ = A.len_; + data_ = A.ptr(); +} + +template +AliasArray::~AliasArray(void) { + len_ = 0; + data_ = NULL; // trick base class into not deleting parent data +} + +template +AliasArray& AliasArray::operator= (const Array &other) { + len_ = other.size(); + data_ = other.ptr(); + return *this; +} + +template +AliasArray& AliasArray::operator= (const T &value) { + for(int i=0;i < len_;i++) + data_[i] = value; + return *this; +} + +template +const T& AliasArray::operator() (int i) const { + return data_[i]; +} + +template +int AliasArray::size(void) const { + return len_; +} + +template +T* AliasArray::ptr() const { + return data_; +} + + +} // end namespace +#endif // Array.h diff --git a/lib/atc/Array2D.h b/lib/atc/Array2D.h new file mode 100644 index 0000000000..8d3af47e12 --- /dev/null +++ b/lib/atc/Array2D.h @@ -0,0 +1,194 @@ +#ifndef ARRAY2D_H +#define ARRAY2D_H + +#include +#include +#include +#include +#include + +#include "Array.h" + +// for macros +#include "MatrixDef.h" + +namespace ATC_matrix { + + /** + * @class Array2D + * @brief Base class for creating, sizing and operating on 2-D arrays of data + */ + +template +class Array2D { +public: + Array2D(); + Array2D(int nrows, int ncols); + Array2D(const Array2D& A); // copy constructor + ~Array2D(); + + // Resize and reinitalize matrix + void reset(int nrows, int ncols); + // Access method to get the (i,j) element: + T& operator() (int i, int j); + // Access method to get the i-th col + AliasArray column(int i) const; + // Access method to get the (i,j) element: + const T& operator() (int i, int j) const; + // Copy operator + Array2D& operator= (const Array2D& other); + // assignment operator + Array2D& operator= (const T other); + // Get size of Array2D + int nRows() const; + int nCols() const; + // Do I have this element? + bool has_member(T val) const; + // print + void print(std::string name ="") const; + // Dump templated type to disk; operation not safe for all types + void write_restart(FILE *f) const; + +private: + int nrows_, ncols_; + T *data_; +}; + +template +Array2D::Array2D() { + nrows_ = 0; + ncols_ = 0; + data_ = NULL; +} + +template +Array2D::Array2D(int nrows, int ncols) { + nrows_ = nrows; + ncols_ = ncols; + data_ = new T[nrows_ * ncols_]; +} + +template +Array2D::Array2D(const Array2D& A) { + nrows_ = A.nrows_; + ncols_ = A.ncols_; + if (A.data_==NULL) + data_ = NULL; + else { + data_ = new T[nrows_ * ncols_]; + for(int i=0;i +void Array2D::reset(int nrows, int ncols) { + if (nrows_ == nrows && ncols_ == ncols) { // no size change; don't realloc memory + return; + } + else { // size changed; realloc memory + nrows_ = nrows; + ncols_ = ncols; + if (data_ != NULL) + delete [] data_; + if (ncols_ > 0 && nrows_ > 0) + data_ = new T[nrows_ * ncols_]; + else { + data_ = NULL; + nrows_ = 0; + ncols_ = 0; + } + } +} + +template +T& Array2D::operator() (int row, int col) { + // Array bounds checking + return data_[col*nrows_ + row]; +} + +template +const T& Array2D::operator() (int row, int col) const { + // Array bounds checking + return data_[col*nrows_ + row]; +} + +template +AliasArray Array2D::column(int col) const { + // Array bounds checking + return AliasArray(nrows_,&(data_[col*nrows_])); +} + +template +Array2D& Array2D::operator= (const Array2D& other) { + if (data_ == NULL) { // initialize my internal storage to match LHS + nrows_ = other.nrows_; + ncols_ = other.ncols_; + if (other.data_==NULL) + data_ = NULL; + else + data_ = new T[nrows_ * ncols_]; + } + for(int i=0;i +Array2D& Array2D::operator= (const T other) { + for(int i=0;i +int Array2D::nRows() const { + return nrows_; +} + +template +int Array2D::nCols() const { + return ncols_; +} + +template +bool Array2D::has_member(T val) const { + int i; + bool retval = false; + for(i=0;i +void Array2D::write_restart(FILE *f) const { + fwrite(&nrows_,sizeof(int),1,f); + fwrite(&ncols_,sizeof(int),1,f); + if (nrows_*ncols_ > 0) + fwrite(data_,sizeof(T),nrows_*ncols_,f); +} + +template +Array2D::~Array2D() { + if (data_ != NULL) + delete[] data_; +} + +template +void Array2D::print(std::string name) const { + std::cout << "------- Begin "< * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions) : AtomToSmallMoleculeTransfer(atc, source, smallMoleculeSet), atomPositions_(atomPositions) + { + atomPositions_->register_dependence(this); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + SmallMoleculeCentroid::~SmallMoleculeCentroid() + { + atomPositions_->remove_dependence(this); + } + + //-------------------------------------------------------- + // Quantity + //-------------------------------------------------------- + void SmallMoleculeCentroid::reset_quantity() const + { + const LammpsInterface * lammps(atc_->lammps_interface()); + const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i \x_i + double xi[3], xj[3], xjImage[3]; + const DENS_MAT & atomPosMatrix(atomPositions_->quantity()); + int nLocalMol = smallMoleculeSet_->local_molecule_count(); + quantity_.reset(nLocalMol,atc_->nsd()); + for (int i = 0; i < nLocalMol; i++) { + const set & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i); + set::const_iterator atomsLocalMolID; + double totalSourceMol = 0.0; // for total source + for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end(); atomsLocalMolID++) { + totalSourceMol += sourceMatrix(*atomsLocalMolID,0); + } // compute total source + atomsLocalMolID = atomsLocalMolArray.begin(); + for (int j = 0; j < atc_->nsd(); j++) { + xi[j] = atomPosMatrix(*atomsLocalMolID,j); + } + for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end(); atomsLocalMolID++) { + + for (int j = 0; j < atc_->nsd(); j++){ + xj[j] = atomPosMatrix(*atomsLocalMolID,j); + } + lammps->closest_image(xi,xj,xjImage); + for (int j = 0; j < atc_->nsd() ; j++) { + quantity_(i,j) += sourceMatrix(*atomsLocalMolID,0) * xjImage[j]/totalSourceMol; + } + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class SmallMoleculeDipoleMoment + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + + SmallMoleculeDipoleMoment::SmallMoleculeDipoleMoment(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions, SmallMoleculeCentroid * centroid) : SmallMoleculeCentroid(atc, source, smallMoleculeSet, atomPositions), centroid_(centroid) //check here + { + centroid_->register_dependence(this); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + + SmallMoleculeDipoleMoment::~SmallMoleculeDipoleMoment() + { + centroid_->remove_dependence(this); + } + + //-------------------------------------------------------- + // Quantity + //-------------------------------------------------------- + void SmallMoleculeDipoleMoment::reset_quantity() const + { + const LammpsInterface * lammps(atc_->lammps_interface()); + const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i + const DENS_MAT & atomPosMatrix(atomPositions_->quantity()); + int nLocalMol = smallMoleculeSet_->local_molecule_count(); + int nsd = atc_->nsd(); + quantity_.reset(nLocalMol,nsd); + double dx[3]; + + //call the SmallMoleculeCentroid here to find Centroid .... + const DENS_MAT & centroidMolMatrix(centroid_->quantity()); + for (int i = 0; i < nLocalMol; i++) { + const set & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i); + set::const_iterator atomsLocalMolID;; + for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end();atomsLocalMolID++) { + for (int j = 0; j < nsd; j++) { + dx[j] = atomPosMatrix(*atomsLocalMolID,j) - centroidMolMatrix(i,j); + } + lammps->minimum_image(dx[0], dx[1], dx[2]); + for(int j = 0; j < nsd; j++) { + quantity_(i,j) += sourceMatrix(*atomsLocalMolID,0) * dx[j]; + } + } + } + } + + //-------------------------------------------------------- + // Class SmallMoleculeQuadrupoleMoment + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + + SmallMoleculeQuadrupoleMoment::SmallMoleculeQuadrupoleMoment(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions, SmallMoleculeCentroid * centroid) : SmallMoleculeCentroid(atc, source, smallMoleculeSet, atomPositions), centroid_(centroid) + { + centroid_->register_dependence(this); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + + SmallMoleculeQuadrupoleMoment::~SmallMoleculeQuadrupoleMoment() + { + centroid_->remove_dependence(this); + } + + //-------------------------------------------------------- + // Quantity + //-------------------------------------------------------- + + void SmallMoleculeQuadrupoleMoment::reset_quantity() const + { + const LammpsInterface * lammps(atc_->lammps_interface()); + const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i + const DENS_MAT & atomPosMatrix(atomPositions_->quantity()); + int nLocalMol = smallMoleculeSet_->local_molecule_count(); + int nsd = atc_->nsd(); + quantity_.reset(nLocalMol,nsd); + double dx[3]; + //call the SmallMoleculeCentroid here to find Centroid .... + const DENS_MAT & centroidMolMatrix(centroid_->quantity()); + for (int i = 0; i < nLocalMol; i++) { + const set & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i); + set::const_iterator atomsLocalMolID;; + for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end();atomsLocalMolID++) { + for (int j = 0; j < nsd; j++) { + dx[j] = atomPosMatrix(*atomsLocalMolID,j) - centroidMolMatrix(i,j); + } + lammps->minimum_image(dx[0], dx[1], dx[2]); + for(int j = 0; j < nsd; j++) { + quantity_(i,j) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j] * dx[2]; + /* quantity_(i,3*j) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[0]; + quantity_(i,3*j+1) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[1]; + quantity_(i,3*j+2) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[2]; */ + } + } + } + } + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + MotfShapeFunctionRestriction::MotfShapeFunctionRestriction(PerMoleculeQuantity * source, + SPAR_MAN * shapeFunction) : + MatToMatTransfer(source), + shapeFunction_(shapeFunction) + { + shapeFunction_->register_dependence(this); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + MotfShapeFunctionRestriction::~MotfShapeFunctionRestriction() + { + shapeFunction_->remove_dependence(this); + } + + //-------------------------------------------------------- + // reset_quantity + //-------------------------------------------------------- + void MotfShapeFunctionRestriction::reset_quantity() const + { + // computes nodeData = N*atomData where N are the shape functions + const DENS_MAT & sourceMatrix(source_->quantity()); + // reallocate memory only if sizing has changed + const SPAR_MAT & shapeFunctionMatrix(shapeFunction_->quantity()); + quantity_.resize(shapeFunctionMatrix.nCols(),sourceMatrix.nCols()); + + local_restriction(sourceMatrix,shapeFunctionMatrix); + + // communicate for total restriction + int count = quantity_.nRows()*quantity_.nCols(); + lammpsInterface_->allsum(_workspace_.ptr(),quantity_.ptr(),count); + } + + //-------------------------------------------------------- + // local_restriction + //-------------------------------------------------------- + void MotfShapeFunctionRestriction::local_restriction(const DENS_MAT & sourceMatrix, + const SPAR_MAT & shapeFunctionMatrix) const + { + if (sourceMatrix.nRows() > 0) + _workspace_ = shapeFunctionMatrix.transMat(sourceMatrix); + else + _workspace_.reset(quantity_.nRows(),quantity_.nCols()); + } + +} // end namespace diff --git a/lib/atc/AtomToMoleculeTransfer.h b/lib/atc/AtomToMoleculeTransfer.h new file mode 100644 index 0000000000..95cf61fe99 --- /dev/null +++ b/lib/atc/AtomToMoleculeTransfer.h @@ -0,0 +1,239 @@ +// A class for defining transfer operations molecular centers of mass (centroid), dipole moments, quadrupole moments + +#ifndef ATOM_TO_MOLECULE_TRANSFER_H +#define ATOM_TO_MOLECULE_TRANSFER_H + +// ATC_Method headers +#include "TransferOperator.h" +#include "MoleculeSet.h" +#include "PaqAtcUtility.h" + +using namespace std; + +namespace ATC { + + // forward declarations + class ATC_Method; + + /** + * @class PerMoleculeQuantity + * + */ + + template + class PerMoleculeQuantity : public DenseMatrixTransfer { + + public: + + PerMoleculeQuantity(ATC_Method * atc):DenseMatrixTransfer(), atc_(atc) {}; + + virtual ~PerMoleculeQuantity() {}; + + protected: + + /** utility object for atc information */ + ATC_Method * atc_; + + private: + + //do not define + PerMoleculeQuantity(); + + }; + + + /** + * @class AtomtoSmallMoleculeTransfer + * @brief Class for defining objects to transfer total mass or charge of a molecule + */ + template + class AtomToSmallMoleculeTransfer : public PerMoleculeQuantity { + + public: + + //constructor + // had to define all functions in header, not sure why (JAT, 12/14/11) + AtomToSmallMoleculeTransfer(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet) : + PerMoleculeQuantity(atc), + source_(source), + smallMoleculeSet_(smallMoleculeSet) + { + source_->register_dependence(this); + smallMoleculeSet_->register_dependence(this); + }; + + //destructor + virtual ~AtomToSmallMoleculeTransfer() + { + source_->remove_dependence(this); + smallMoleculeSet_->remove_dependence(this); + }; + + // apply transfer operator + void reset_quantity() const + { + const DenseMatrix & sourceMatrix(source_->quantity()); + int nLocalMol = smallMoleculeSet_->local_molecule_count(); + (this->quantity_).reset(nLocalMol,sourceMatrix.nCols()); + for (int i = 0; i < nLocalMol ; i++) { + const set & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i); + set::const_iterator atomsLocalMolID; + for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID!= atomsLocalMolArray.end();atomsLocalMolID++) { + for (int j = 0; j < sourceMatrix.nCols(); j++){ + (this->quantity_)(i,j) += sourceMatrix(*atomsLocalMolID,j); + } + } + } + }; + + protected: + + // pointer to source atomic quantity data + PerAtomQuantity * source_; + // pointer to molecule data + SmallMoleculeSet * smallMoleculeSet_; + + private: + + // do not define + AtomToSmallMoleculeTransfer(); + + }; + + /** + * @class SmallMoleculeCentroid + * @brief Class for defining objects to transfer molecular centroid (center of mass) + */ + + + class SmallMoleculeCentroid : public AtomToSmallMoleculeTransfer { + + public: + + //constructor + SmallMoleculeCentroid(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions); + + //destructor + virtual ~SmallMoleculeCentroid(); + + // apply transfer operator + virtual void reset_quantity() const; + + protected: + + // pointer to source atomic quantity date : positions of atoms in a molecule + PerAtomQuantity * atomPositions_; + + private: + + //do not define + SmallMoleculeCentroid(); + + }; + + /** + * @class SmallMoleculeDipoleMoment + * @brief Class for defining objects to transfer molecular dipole moments + */ + + class SmallMoleculeDipoleMoment : public SmallMoleculeCentroid { + + public: + + //constructor + SmallMoleculeDipoleMoment(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions, SmallMoleculeCentroid * centroid); + + //destructor + virtual ~SmallMoleculeDipoleMoment(); + + // apply transfer operator + virtual void reset_quantity() const; + + protected: + + //pointer to the centroid data + SmallMoleculeCentroid * centroid_; + + private: + + //do not define + SmallMoleculeDipoleMoment(); + +}; + + /** + * @class AtomToFeTransfer + * @brief Class for defining objects to transfer molecular quadrupole moments + */ + + + class SmallMoleculeQuadrupoleMoment : public SmallMoleculeCentroid { + + public: + + //constructor + SmallMoleculeQuadrupoleMoment(ATC_Method * atc, PerAtomQuantity * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity * atomPositions, SmallMoleculeCentroid * centroid); + + //destructor + virtual ~SmallMoleculeQuadrupoleMoment(); + + //apply transfer operator + virtual void reset_quantity() const; + + protected: + + //pointer to the centroid data + SmallMoleculeCentroid * centroid_; + + private: + + //do not define + SmallMoleculeQuadrupoleMoment(); + + }; + + /** + * @class MotfShapeFunctionRestriction + * @brief Class for defining objects that transfer atomistic quantities to FE using shape functions + * (implements restrict_volumetric_quantity) + */ + + class MotfShapeFunctionRestriction : public MatToMatTransfer { + + public: + + // constructor + MotfShapeFunctionRestriction(PerMoleculeQuantity * source, + SPAR_MAN * shapeFunction); + + // destructor + virtual ~MotfShapeFunctionRestriction(); + + /** apply transfer operator */ + virtual void reset_quantity() const; + + protected: + + /** reference to shape function matrix */ + SPAR_MAN * shapeFunction_; + + /** persistant workspace */ + + + mutable DENS_MAT _workspace_; + + /** applies restriction operation on this processor */ + virtual void local_restriction(const DENS_MAT & sourceMatrix, + const SPAR_MAT & shapeFunctionMatrix) const; + + private: + + // do not define + MotfShapeFunctionRestriction(); + + }; + +} +#endif + + diff --git a/lib/atc/AtomicRegulator.cpp b/lib/atc/AtomicRegulator.cpp new file mode 100644 index 0000000000..f353d0e4ed --- /dev/null +++ b/lib/atc/AtomicRegulator.cpp @@ -0,0 +1,976 @@ +// ATC Headers +#include "AtomicRegulator.h" +#include "ATC_Error.h" +#include "ATC_Coupling.h" +#include "PrescribedDataManager.h" +#include "TimeIntegrator.h" +#include "LinearSolver.h" + +namespace ATC { + + + // only one regulator method at time, i.e. fixed & flux, thermo & elastic + // regulator manages lambda variables, creates new ones when requested with dimensions and zero ics (map of tag to lambda) + // regulator keeps track of which lambda are being used, unused lambdas deleted (map of tag to bool), all tags set to unused on start of initialization + // method requests needed lambda from regulator + // method sets up all needed linear solvers, null linear solver does nothing + // regulator adds nodes to fixed or fluxed lists it owns, based on localization and type + // method gets lists of fixed nodes and fluxed nodes + // method lumps fluxed lambdas and truncates fixed lambdas based on single localized bool in regulator + // inherited methods should be fixed, fluxed, combined + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicRegulator + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + AtomicRegulator::AtomicRegulator(ATC_Coupling * atc, + const string & regulatorPrefix) : + atc_(atc), + howOften_(1), + needReset_(true), + maxIterations_(myMaxIterations), + tolerance_(myTolerance), + regulatorTarget_(NONE), + couplingMode_(UNCOUPLED), + nNodes_(0), + nsd_(atc_->nsd()), + nLocal_(0), + useLocalizedLambda_(false), + useLumpedLambda_(false), + timeFilter_(NULL), + regulatorMethod_(NULL), + boundaryIntegrationType_(NO_QUADRATURE), + regulatorPrefix_(regulatorPrefix) + { + applyInDirection_.resize(atc_->nsd(),true); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + AtomicRegulator::~AtomicRegulator() + { + delete_method(); + set_all_data_to_unused(); + delete_unused_data(); + } + + //-------------------------------------------------------- + // delete_method: + // deletes the method + //-------------------------------------------------------- + void AtomicRegulator::delete_method() + { + if (regulatorMethod_) + delete regulatorMethod_; + } + + //-------------------------------------------------------- + // delete_unused_data: + // deletes all data that is currently not in use + //-------------------------------------------------------- + void AtomicRegulator::delete_unused_data() + { + map >::iterator it; + for (it = regulatorData_.begin(); it != regulatorData_.end(); it++) { + if (((it->second).first)) { + delete (it->second).second; + regulatorData_.erase(it); + } + } + } + + //-------------------------------------------------------- + // get_regulator_data: + // gets a pointer to the requested data, is crated if + // if doesn't exist + //-------------------------------------------------------- + DENS_MAN * AtomicRegulator::regulator_data(const string tag, int nCols) + { + DENS_MAN * data(NULL); + map >::iterator it = regulatorData_.find(tag); + if (it == regulatorData_.end()) { + data = new DENS_MAN(nNodes_,nCols); + regulatorData_.insert(pair >(tag,pair(false,data))); + } + else { + data = (it->second).second; + if ((data->nRows() != nNodes_) || (data->nCols() != nCols)) { + data->reset(nNodes_,nCols); + } + (it->second).first = false; + } + return data; + } + + //-------------------------------------------------------- + // get_regulator_data: + // gets a pointer to the requested data, or NULL if + // if doesn't exist + //-------------------------------------------------------- + const DENS_MAN * AtomicRegulator::regulator_data(const string tag) const + { + map >::const_iterator it = regulatorData_.find(tag); + if (it == regulatorData_.end()) { + return NULL; + } + else { + return const_cast((it->second).second); + } + } + + //-------------------------------------------------------- + // set_all_data_to_unused: + // sets bool such that all data is unused + //-------------------------------------------------------- + void AtomicRegulator::set_all_data_to_unused() + { + map >::iterator it; + for (it = regulatorData_.begin(); it != regulatorData_.end(); it++) { + (it->second).first = true; + } + } + + //-------------------------------------------------------- + // set_all_data_to_used: + // sets bool such that all data is used + //-------------------------------------------------------- + void AtomicRegulator::set_all_data_to_used() + { + map >::iterator it; + for (it = regulatorData_.begin(); it != regulatorData_.end(); it++) { + (it->second).first = false; + } + } + + //-------------------------------------------------------- + // modify: + // parses and adjusts controller state based on + // user input, in the style of LAMMPS user input + //-------------------------------------------------------- + bool AtomicRegulator::modify(int narg, char **arg) + { + bool foundMatch = false; + + // set parameters for numerical matrix solutions + /*! \page man_control fix_modify AtC control + \section syntax + fix_modify AtC control \n + - physics_type (string) = thermal | momentum\n + - solution_parameter (string) = max_iterations | tolerance\n + + fix_modify AtC transfer control max_iterations \n + - max_iterations (int) = maximum number of iterations that will be used by iterative matrix solvers\n + + fix_modify AtC transfer control tolerance \n + - tolerance (float) = relative tolerance to which matrix equations will be solved\n + + \section examples + fix_modify AtC control thermal max_iterations 10 \n + fix_modify AtC control momentum tolerance 1.e-5 \n + \section description + Sets the numerical parameters for the matrix solvers used in the specified control algorithm. Many solution approaches require iterative solvers, and these methods enable users to provide the maximum number of iterations and the relative tolerance. + \section restrictions + only for be used with specific controllers : + thermal, momentum \n + They are ignored if a lumped solution is requested + \section related + \section default + max_iterations is the number of rows in the matrix\n + tolerance is 1.e-10 + */ + int argIndex = 0; + if (strcmp(arg[argIndex],"max_iterations")==0) { + argIndex++; + maxIterations_ = atoi(arg[argIndex]); + if (maxIterations_ < 1) { + throw ATC_Error("Bad maximum iteration count"); + } + needReset_ = true; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"tolerance")==0) { + argIndex++; + tolerance_ = atof(arg[argIndex]); + if (tolerance_ < 0.) { + throw ATC_Error("Bad tolerance value"); + } + needReset_ = true; + foundMatch = true; + } + + /*! \page man_localized_lambda fix_modify AtC control localized_lambda + \section syntax + fix_modify AtC control localized_lambda + \section examples + fix_modify atc control localized_lambda on \n + \section description + Turns on localization algorithms for control algorithms to restrict the influence of FE coupling or boundary conditions to a region near the boundary of the MD region. Control algorithms will not affect atoms in elements not possessing faces on the boundary of the region. Flux-based control is localized via row-sum lumping while quantity control is done by solving a truncated matrix equation. + \section restrictions + \section related + \section default + Default is off. + */ + else if (strcmp(arg[argIndex],"localized_lambda")==0) { + argIndex++; + if (strcmp(arg[argIndex],"on")==0) { + useLocalizedLambda_ = true; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"off")==0) { + useLocalizedLambda_ = false; + foundMatch = true; + } + } + + + + /*! \page man_lumped_lambda_solve fix_modify AtC control lumped_lambda_solve + \section syntax + fix_modify AtC control lumped_lambda_solve + \section examples + fix_modify atc control lumped_lambda_solve on \n + \section description + Command use or not use lumped matrix for lambda solve + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIndex],"lumped_lambda_solve")==0) { + argIndex++; + if (strcmp(arg[argIndex],"on")==0) { + useLumpedLambda_ = true; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"off")==0) { + useLumpedLambda_ = false; + foundMatch = true; + } + } + + /*! \page man_mask_direction fix_modify AtC control mask_direction + \section syntax + fix_modify AtC control mask_direction + \section examples + fix_modify atc control mask_direction 0 on \n + \section description + Command to mask out certain dimensions from the atomic regulator + \section restrictions + \section related + \section default + */ + else if (strcmp(arg[argIndex],"mask_direction")==0) { + argIndex++; + int dir = atoi(arg[argIndex]); + argIndex++; + if (strcmp(arg[argIndex],"on")==0) { + applyInDirection_[dir] = false; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"off")==0) { + applyInDirection_[dir] = true; + foundMatch = true; + } + } + + return foundMatch; + } + + //-------------------------------------------------------- + // reset_nlocal: + // resizes lambda force if necessary + //-------------------------------------------------------- + void AtomicRegulator::reset_nlocal() + { + nLocal_ = atc_->nlocal(); + if (regulatorMethod_) + regulatorMethod_->reset_nlocal(); + } + //-------------------------------------------------------- + // reset_atom_materials: + // resets the localized atom to material map + //-------------------------------------------------------- + void AtomicRegulator::reset_atom_materials(const Array & elementToMaterialMap, + const MatrixDependencyManager * atomElement) + { + if (regulatorMethod_) + regulatorMethod_->reset_atom_materials(elementToMaterialMap, + atomElement); + } + //-------------------------------------------------------- + // reset_method: + // sets up methods, if necessary + //-------------------------------------------------------- + void AtomicRegulator::reset_method() + { + // set up defaults for anything that didn't get set + if (!regulatorMethod_) + regulatorMethod_ = new RegulatorMethod(this); + if (!timeFilter_) + timeFilter_ = (atc_->time_filter_manager())->construct(); + } + //-------------------------------------------------------- + // md_fixed_nodes: + // determines if any fixed nodes overlap the MD region + //-------------------------------------------------------- + bool AtomicRegulator::md_fixed_nodes(FieldName fieldName) const + { + FixedNodes fixedNodes(atc_,fieldName); + const set & myNodes(fixedNodes.quantity()); + if (myNodes.size() == 0) { + return false; + } + else { + return true; + } + } + //-------------------------------------------------------- + // md_flux_nodes: + // determines if any nodes with fluxes overlap the MD region + //-------------------------------------------------------- + bool AtomicRegulator::md_flux_nodes(FieldName fieldName) const + { + FluxNodes fluxNodes(atc_,fieldName); + const set & myNodes(fluxNodes.quantity()); + if (myNodes.size() == 0) { + return false; + } + else { + return true; + } + } + //-------------------------------------------------------- + // construct_methods: + // sets up methods before a run + //-------------------------------------------------------- + void AtomicRegulator::construct_methods() + { + // get base-line data that was set in stages 1 & 2 of ATC_Method::initialize + // computational geometry + nNodes_ = atc_->num_nodes(); + + // make sure consistent boundary integration is being used + atc_->set_boundary_integration_type(boundaryIntegrationType_); + } + + //-------------------------------------------------------- + // construct_transfers: + // pass through to appropriate transfer constuctors + //-------------------------------------------------------- + void AtomicRegulator::construct_transfers() + { + regulatorMethod_->construct_transfers(); + } + + //-------------------------------------------------------- + // initialize: + // sets up methods before a run + //-------------------------------------------------------- + void AtomicRegulator::initialize() + { + regulatorMethod_->initialize(); + needReset_ = false; + } + + //-------------------------------------------------------- + // output: + // pass through to appropriate output methods + //-------------------------------------------------------- + void AtomicRegulator::output(OUTPUT_LIST & outputData) const + { + regulatorMethod_->output(outputData); + } + + //-------------------------------------------------------- + // finish: + // pass through to appropriate end-of-run methods + //-------------------------------------------------------- + void AtomicRegulator::finish() + { + regulatorMethod_->finish(); + set_all_data_to_unused(); + } + + //-------------------------------------------------------- + // apply_pre_predictor: + // applies the controller in the pre-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_pre_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_pre_predictor(dt); + } + + //-------------------------------------------------------- + // apply_mid_predictor: + // applies the controller in the mid-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_mid_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_mid_predictor(dt); + } + + //-------------------------------------------------------- + // apply_post_predictor: + // applies the controller in the post-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_post_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_post_predictor(dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // applies the controller in the pre-corrector phase + // of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_pre_corrector(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_pre_corrector(dt); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // applies the controller in the post-corrector phase + // of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_post_corrector(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_post_corrector(dt); + } + + //-------------------------------------------------------- + // pre_exchange + //-------------------------------------------------------- + void AtomicRegulator::pre_exchange() + { + regulatorMethod_->pre_exchange(); + } + + //-------------------------------------------------------- + // pre_force + //-------------------------------------------------------- + void AtomicRegulator::pre_force() + { + regulatorMethod_->post_exchange(); + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void AtomicRegulator::pack_fields(RESTART_LIST & data) + { + map >::iterator it; + for (it = regulatorData_.begin(); it != regulatorData_.end(); it++) { + data[(it->first)] = &(((it->second).second)->set_quantity()); + } + } + + //-------------------------------------------------------- + // compute_boundary_flux: + // computes the boundary flux to be consistent with + // the controller + //-------------------------------------------------------- + void AtomicRegulator::compute_boundary_flux(FIELDS & fields) + { + regulatorMethod_->compute_boundary_flux(fields); + } + + //-------------------------------------------------------- + // add_to_rhs: + // adds any controller contributions to the FE rhs + //-------------------------------------------------------- + void AtomicRegulator::add_to_rhs(FIELDS & rhs) + { + regulatorMethod_->add_to_rhs(rhs); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + RegulatorMethod::RegulatorMethod(AtomicRegulator * atomicRegulator, + const string & regulatorPrefix) : + atomicRegulator_(atomicRegulator), + atc_(atomicRegulator_->atc_transfer()), + boundaryFlux_(atc_->boundary_fluxes()), + fieldMask_(NUM_FIELDS,NUM_FLUX), + nNodes_(atomicRegulator_->num_nodes()), + regulatorPrefix_(atomicRegulator->regulator_prefix()+regulatorPrefix), + shpFcnDerivs_(NULL) + { + fieldMask_ = false; + } + + //-------------------------------------------------------- + // compute_boundary_flux + // default computation of boundary flux based on + // finite + //-------------------------------------------------------- + void RegulatorMethod::compute_boundary_flux(FIELDS & fields) + { + atc_->compute_boundary_flux(fieldMask_, + fields, + boundaryFlux_, + atomMaterialGroups_, + shpFcnDerivs_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorShapeFunction + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + RegulatorShapeFunction::RegulatorShapeFunction(AtomicRegulator * atomicRegulator, + const string & regulatorPrefix) : + RegulatorMethod(atomicRegulator,regulatorPrefix), + lambda_(NULL), + atomLambdas_(NULL), + shapeFunctionMatrix_(NULL), + linearSolverType_(AtomicRegulator::NO_SOLVE), + maxIterations_(atomicRegulator->max_iterations()), + tolerance_(atomicRegulator->tolerance()), + matrixSolver_(NULL), + regulatedNodes_(NULL), + applicationNodes_(NULL), + boundaryNodes_(NULL), + shpFcn_(NULL), + atomicWeights_(NULL), + elementMask_(NULL), + lambdaAtomMap_(NULL), + weights_(NULL), + nsd_(atomicRegulator_->nsd()), + nLocal_(atomicRegulator_->nlocal()) + { + // do nothing + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + RegulatorShapeFunction::~RegulatorShapeFunction() + { + if (matrixSolver_) + delete matrixSolver_; + } + + //-------------------------------------------------------- + // create_node_maps + // - creates the node mappings between all nodes and the + // subset which are regulated + //-------------------------------------------------------- + void RegulatorShapeFunction::create_node_maps() + { + this->construct_regulated_nodes(); + + InterscaleManager & interscaleManager(atc_->interscale_manager()); + nodeToOverlapMap_ = static_cast(interscaleManager.dense_matrix_int("NodeToOverlapMap")); + if (!nodeToOverlapMap_) { + nodeToOverlapMap_ = new NodeToSubset(atc_,regulatedNodes_); + interscaleManager.add_dense_matrix_int(nodeToOverlapMap_, + regulatorPrefix_+"NodeToOverlapMap"); + } + overlapToNodeMap_ = static_cast(interscaleManager.dense_matrix_int("OverlapToNodeMap")); + if (!overlapToNodeMap_) { + overlapToNodeMap_ = new SubsetToNode(nodeToOverlapMap_); + interscaleManager.add_dense_matrix_int(overlapToNodeMap_, + regulatorPrefix_+"OverlapToNodeMap"); + } + + } + + //-------------------------------------------------------- + // construct_transfers + // - create all the needed transfer operators, in this + // case weights for the lambda matrix + //-------------------------------------------------------- + void RegulatorShapeFunction::construct_transfers() + { + this->set_weights(); // construct specific weighting matrix transfer + + // specialized quantities for boundary flux integration if the lambda atom map exists + if (lambdaAtomMap_ && (atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION)) { + InterscaleManager & interscaleManager(atc_->interscale_manager()); + + // atomic weights + PerAtomDiagonalMatrix * atomWeights(interscaleManager.per_atom_diagonal_matrix("AtomVolume")); + atomicWeights_ = new MappedDiagonalMatrix(atc_, + atomWeights, + lambdaAtomMap_); + interscaleManager.add_diagonal_matrix(atomicWeights_, + regulatorPrefix_+"RegulatorAtomWeights"); + + // shape function + shpFcn_ = new RowMappedSparseMatrix(atc_, + interscaleManager.per_atom_sparse_matrix("Interpolant"), + lambdaAtomMap_); + interscaleManager.add_sparse_matrix(shpFcn_, + regulatorPrefix_+"RegulatorShapeFunction"); + + // shape function derivatives + shpFcnDerivs_ = new RowMappedSparseMatrixVector(atc_, + interscaleManager.vector_sparse_matrix("InterpolantGradient"), + lambdaAtomMap_); + interscaleManager.add_vector_sparse_matrix(shpFcnDerivs_, + regulatorPrefix_+"RegulatorShapeFunctionGradient"); + } + } + + //-------------------------------------------------------- + // initialize + // - pre-run work, in this cases constructs the linear + // solver + //-------------------------------------------------------- + void RegulatorShapeFunction::initialize() + { + if (!shapeFunctionMatrix_) { + throw ATC_Error("RegulatorShapeFunction::initialize - shapeFunctionMatrix_ must be created before the initialize phase"); + } + if (matrixSolver_) + delete matrixSolver_; + + if (linearSolverType_ == AtomicRegulator::RSL_SOLVE) { + matrixSolver_ = new LambdaMatrixSolverLumped(matrixTemplate_, + shapeFunctionMatrix_, + maxIterations_, + tolerance_, + applicationNodes_, + nodeToOverlapMap_); + } + else if (linearSolverType_ == AtomicRegulator::CG_SOLVE) { + matrixSolver_ = new LambdaMatrixSolverCg(matrixTemplate_, + shapeFunctionMatrix_, + maxIterations_, + tolerance_); + } + else { + throw ATC_Error("RegulatorShapeFunction::initialize - unsupported solver type"); + } + + compute_sparsity(); + } + + //-------------------------------------------------------- + // compute_sparsity + // - creates sparsity template + //-------------------------------------------------------- + void RegulatorShapeFunction::compute_sparsity(void) + { + + // first get local pattern from N N^T + int nNodeOverlap = nodeToOverlapMap_->size(); + DENS_MAT tmpLocal(nNodeOverlap,nNodeOverlap); + DENS_MAT tmp(nNodeOverlap,nNodeOverlap); + const SPAR_MAT & myShapeFunctionMatrix(shapeFunctionMatrix_->quantity()); + if (myShapeFunctionMatrix.nRows() > 0) { + tmpLocal = myShapeFunctionMatrix.transMat(myShapeFunctionMatrix); + } + + // second accumulate total pattern across processors + LammpsInterface::instance()->allsum(tmpLocal.ptr(), tmp.ptr(), tmp.size()); + // third extract non-zero entries & construct sparse template + SPAR_MAT & myMatrixTemplate(matrixTemplate_.set_quantity()); + myMatrixTemplate.reset(nNodeOverlap,nNodeOverlap); + for (int i = 0; i < nNodeOverlap; i++) { + for (int j = 0; j < nNodeOverlap; j++) { + if (abs(tmp(i,j))>0) { + myMatrixTemplate.add(i,j,0.); + } + } + } + myMatrixTemplate.compress(); + } + + //-------------------------------------------------------- + // solve_for_lambda + // solves matrix equation for lambda using given rhs + //-------------------------------------------------------- + void RegulatorShapeFunction::solve_for_lambda(const DENS_MAT & rhs, + DENS_MAT & lambda) + { + + // assemble N^T W N with appropriate weighting matrix + + DIAG_MAT weights; + if (shapeFunctionMatrix_->nRows() > 0) { + weights.reset(weights_->quantity()); + } + matrixSolver_->assemble_matrix(weights); + + // solve on overlap nodes + int nNodeOverlap = nodeToOverlapMap_->size(); + DENS_MAT rhsOverlap(nNodeOverlap,rhs.nCols()); + map_unique_to_overlap(rhs, rhsOverlap); + DENS_MAT lambdaOverlap(nNodeOverlap,lambda.nCols()); + + for (int i = 0; i < rhs.nCols(); i++) { + CLON_VEC tempLambda(lambdaOverlap,CLONE_COL,i); + if (atomicRegulator_->apply_in_direction(i)) { + CLON_VEC tempRHS(rhsOverlap,CLONE_COL,i); + matrixSolver_->execute(tempRHS,tempLambda); + } + else { + tempLambda = 0.; + } + } + + // map solution back to all nodes + map_overlap_to_unique(lambdaOverlap,lambda); + } + + //-------------------------------------------------------- + // reset_nlocal: + // resets data dependent on local atom count + //-------------------------------------------------------- + void RegulatorShapeFunction::reset_nlocal() + { + RegulatorMethod::reset_nlocal(); + nLocal_ = atomicRegulator_->nlocal(); + + + + //compute_sparsity(); + } + + //-------------------------------------------------------- + // reset_atom_materials: + // resets the localized atom to material map + //-------------------------------------------------------- + void RegulatorShapeFunction::reset_atom_materials(const Array & elementToMaterialMap, + const MatrixDependencyManager * atomElement) + { + // specialized quantities for boundary flux integration if the lambda atom map exists + if (lambdaAtomMap_ && (atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION)) { + int nMaterials = (atc_->physics_model())->nMaterials(); + atomMaterialGroups_.reset(nMaterials); + const INT_ARRAY & atomToElementMap(atomElement->quantity()); + const INT_ARRAY & map(lambdaAtomMap_->quantity()); + int idx; + for (int i = 0; i < nLocal_; i++) { + idx = map(i,0); + if (idx > -1) { + atomMaterialGroups_(elementToMaterialMap(atomToElementMap(i,0))).insert(idx); + } + } + } + } + + //-------------------------------------------------------- + // map_unique_to_overlap: + // maps unique node data to overlap node data + //-------------------------------------------------------- + void RegulatorShapeFunction::map_unique_to_overlap(const MATRIX & uniqueData, + MATRIX & overlapData) + { + const INT_ARRAY & nodeToOverlapMap(nodeToOverlapMap_->quantity()); + for (int i = 0; i < nNodes_; i++) { + if (nodeToOverlapMap(i,0) > -1) { + for (int j = 0; j < uniqueData.nCols(); j++) { + overlapData(nodeToOverlapMap(i,0),j) = uniqueData(i,j); + } + } + } + } + + //-------------------------------------------------------- + // map_overlap_to_unique: + // maps overlap node data to unique node data + //-------------------------------------------------------- + void RegulatorShapeFunction::map_overlap_to_unique(const MATRIX & overlapData, + MATRIX & uniqueData) + { + const INT_ARRAY & overlapToNodeMap(overlapToNodeMap_->quantity()); + uniqueData.resize(nNodes_,overlapData.nCols()); + for (int i = 0; i < overlapToNodeMap.size(); i++) { + for (int j = 0; j < overlapData.nCols(); j++) { + uniqueData(overlapToNodeMap(i,0),j) = overlapData(i,j); + } + } + } + //-------------------------------------------------------- + // construct_regulated_nodes: + // constructs the set of nodes being regulated + //-------------------------------------------------------- + void RegulatorShapeFunction::construct_regulated_nodes() + { + InterscaleManager & interscaleManager(atc_->interscale_manager()); + regulatedNodes_ = static_cast(interscaleManager.set_int("RegulatedNodes")); + + if (!regulatedNodes_) { + if (!(atomicRegulator_->use_localized_lambda())) { + regulatedNodes_ = new RegulatedNodes(atc_); + } + else { + regulatedNodes_ = new AllRegulatedNodes(atc_); + } + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"RegulatedNodes"); + } + + // application and regulated are same, unless specified + applicationNodes_ = regulatedNodes_; + // boundary and regulated nodes are same, unless specified + boundaryNodes_ = regulatedNodes_; + + // special set of boundary elements + if (atomicRegulator_->use_localized_lambda()) { + elementMask_ = new ElementMaskNodeSet(atc_,boundaryNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } + } + + //-------------------------------------------------------- + // compute_boundary_flux + // default computation of boundary flux based on + // finite + //-------------------------------------------------------- + void RegulatorShapeFunction::compute_boundary_flux(FIELDS & fields) + { + atc_->compute_boundary_flux(fieldMask_, + fields, + boundaryFlux_, + atomMaterialGroups_, + shpFcnDerivs_, + shpFcn_, + atomicWeights_, + elementMask_, + boundaryNodes_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolver + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolver::LambdaMatrixSolver(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance) : + matrixTemplate_(matrixTemplate), + shapeFunctionMatrix_(shapeFunctionMatrix), + maxIterations_(maxIterations), + tolerance_(tolerance) + { + // do nothing + } + + //-------------------------------------------------------- + // assemble_matrix + // Assemble the matrix using the shape function + // matrices and weights. This improves efficiency + // when multiple solves or iterations are required. + //-------------------------------------------------------- + void LambdaMatrixSolver::assemble_matrix(DIAG_MAT & weights) + { + // form matrix : sum_a N_Ia * W_a * N_Ja + + SPAR_MAT lambdaMatrixLocal(matrixTemplate_.quantity()); + if (weights.nRows()>0) + lambdaMatrixLocal.weighted_least_squares(shapeFunctionMatrix_->quantity(),weights); + + // swap contributions + lambdaMatrix_ = matrixTemplate_.quantity(); + LammpsInterface::instance()->allsum(lambdaMatrixLocal.ptr(), + lambdaMatrix_.ptr(), lambdaMatrix_.size()); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverLumped + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const RegulatedNodes * applicationNodes, const NodeToSubset * nodeToOverlapMap) : + LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance), + applicationNodes_(applicationNodes), + nodeToOverlapMap_(nodeToOverlapMap) + { + // do nothing + } + + //-------------------------------------------------------- + // assemble_matrix + // Assemble the matrix using the shape function + // matrices and weights. This improves efficiency + // when multiple solves or iterations are required. + //-------------------------------------------------------- + void LambdaMatrixSolverLumped::assemble_matrix(DIAG_MAT & weights) + { + LambdaMatrixSolver::assemble_matrix(weights); + + lumpedMatrix_ = lambdaMatrix_.row_sum_lump(); + } + + void LambdaMatrixSolverLumped::execute(VECTOR & rhs, VECTOR & lambda) + { + + // solve lumped equation + const set & applicationNodes(applicationNodes_->quantity()); + const INT_ARRAY & nodeToOverlapMap(nodeToOverlapMap_->quantity()); + lambda = 0.; + set::const_iterator iset; + for (iset = applicationNodes.begin(); iset != applicationNodes.end(); iset++) { + int node = nodeToOverlapMap(*iset,0); + lambda(node) = rhs(node)/lumpedMatrix_(node,node); + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverCg + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolverCg::LambdaMatrixSolverCg(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance) : + LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance) + { + // do nothing + } + + void LambdaMatrixSolverCg::execute(VECTOR & rhs, VECTOR & lambda) + { + if (lambdaMatrix_.size()<1) + throw ATC_Error("solver given zero size matrix in LambdaMatrixSolverCg::execute()"); + + + LinearSolver solver(lambdaMatrix_, ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC, true); + int myMaxIt = maxIterations_ > 0 ? maxIterations_ : 2*lambdaMatrix_.nRows(); + solver.set_max_iterations(myMaxIt); + solver.set_tolerance(tolerance_); + solver.solve(lambda,rhs); + } +}; diff --git a/lib/atc/AtomicRegulator.h b/lib/atc/AtomicRegulator.h new file mode 100644 index 0000000000..74d7195376 --- /dev/null +++ b/lib/atc/AtomicRegulator.h @@ -0,0 +1,625 @@ +/** Atomic Regulator : a base class class for atom-continuum control */ + +#ifndef ATOMICREGULATOR_H +#define ATOMICREGULATOR_H + +// ATC headers +#include "ATC_TypeDefs.h" + +// other headers +#include +#include +#include + +namespace ATC { + + static const int myMaxIterations = 0; + static const double myTolerance = 1.e-10; + + // forward declarations + class TimeFilter; + class RegulatorMethod; + class LambdaMatrixSolver; + class ATC_Coupling; + class NodeToSubset; + class SubsetToNode; + class RegulatedNodes; + class ElementMaskNodeSet; + class LargeToSmallAtomMap; + template + class PerAtomQuantity; + template + class ProtectedAtomQuantity; + template + class PerAtomSparseMatrix; + + /** + * @class AtomicRegulator + * @brief Base class for atom-continuum control + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicRegulator + //-------------------------------------------------------- + //-------------------------------------------------------- + + class AtomicRegulator { + + public: + + /** linear solver types */ + enum LinearSolverType { + NO_SOLVE=0, + CG_SOLVE, // conjugate gradient + RSL_SOLVE // row-sum lumping solution + }; + + /** regulator target variable */ + enum RegulatorTargetType { + NONE=0, + FIELD, + DERIVATIVE, + DYNAMICS + }; + + enum RegulatorCouplingType { + UNCOUPLED=0, + FLUX, + GHOST_FLUX, + FIXED + }; + + // constructor + AtomicRegulator(ATC_Coupling * atc, + const string & regulatorPrefix = ""); + + // destructor + virtual ~AtomicRegulator(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** instantiate up the desired method(s) */ + virtual void construct_methods() = 0; + + /** method(s) create all necessary transfer operators */ + virtual void construct_transfers(); + + /** initialization of method data */ + virtual void initialize(); + + /** add output information */ + virtual void output(OUTPUT_LIST & outputData) const; + virtual double compute_vector(int n) const {return 0;} + + /** final work at the end of a run */ + virtual void finish(); + + /** 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); + + // application steps + /** 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_force(); + /** prior to exchanges */ + virtual void pre_exchange(); + + /** pack fields for restart */ + virtual void pack_fields(RESTART_LIST & data); + + /** thermo output */ + virtual int size_vector(int s) const {return 0;}; + + // coupling to FE state + /** FE state variable regulator is applied to */ + virtual RegulatorTargetType regulator_target() const {return regulatorTarget_;}; + /** type of boundary coupling */ + //TEMP_JAT field variable should be removed + virtual RegulatorCouplingType coupling_mode(const FieldName field=NUM_TOTAL_FIELDS) const {return couplingMode_;}; + /** compute the thermal boundary flux, must be consistent with regulator */ + virtual void compute_boundary_flux(FIELDS & fields); + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs); + + // data access, intended for method objects + /** returns a pointer to the DENS_MAN associated with the tag, creates a new data member if necessary */ + DENS_MAN * regulator_data(const string tag, int nCols); + /** can externally set regulator dynamic contributions */ + virtual void reset_lambda_contribution(const DENS_MAT & target, const FieldName field=NUM_TOTAL_FIELDS) {}; + /** returns a const pointer to the DENS_MAN associated with the tag, or NULL */ + const DENS_MAN * regulator_data(const string tag) const; + /** return the maximum number of iterations */ + int max_iterations() {return maxIterations_;}; + /** return the solver tolerance */ + double tolerance() {return tolerance_;}; + /** access for ATC transfer */ + ATC_Coupling * atc_transfer() {return atc_;}; + /** access for time filter */ + TimeFilter * time_filter() {return timeFilter_;}; + /** access for number of nodes */ + int num_nodes() {return nNodes_;}; + /** access for number of spatial dimensions */ + int nsd() {return nsd_;}; + /** access for number of local atoms */ + int nlocal() {return nLocal_;}; + /** access for boundary integration methods */ + BoundaryIntegrationType boundary_integration_type() + {return boundaryIntegrationType_;}; + /** access for boundary face sets */ + const set< pair > * face_sets() + { return boundaryFaceSet_;}; + /** access for needing a reset */ + bool need_reset() const {return needReset_;}; + /** force a reset to occur */ + void force_reset() {needReset_ = true;}; + /** check if lambda is localized */ + bool use_localized_lambda() const {return useLocalizedLambda_;}; + /** check if matrix should be lumpted for lambda solve */ + bool use_lumped_lambda_solve() const {return useLumpedLambda_;}; + /** check to see if this direction is being used */ + bool apply_in_direction(int i) const {return applyInDirection_[i];}; + + + /** checks if there are any fixed nodes in the MD region */ + bool md_fixed_nodes(FieldName fieldName = NUM_TOTAL_FIELDS) const; + + /** checks if there are any flux nodes in the MD region */ + bool md_flux_nodes(FieldName fieldName = NUM_TOTAL_FIELDS) const; + + /** returns prefix tag for regulator */ + const string & regulator_prefix() const {return regulatorPrefix_;}; + + protected: + + // methods + /** deletes the current regulator method */ + void delete_method(); + + /** deletes all unused data */ + void delete_unused_data(); + + /** sets all data to be unused */ + void set_all_data_to_unused(); + + /** sets all data to be used */ + void set_all_data_to_used(); + + // data + /** point to atc_transfer object */ + ATC_Coupling * atc_; + + /** how often in number of time steps regulator is applied */ + int howOften_; + + // reset/reinitialize flags + /** flag to reset data */ + bool needReset_; + /** reinitialize method */ + void reset_method(); + + // regulator data + /** container for all data, string is tag, bool is false if currently in use */ + map > regulatorData_; + /** maximum number of iterations used in solving for lambda */ + int maxIterations_; + /** tolerance used in solving for lambda */ + double tolerance_; + + /** regulator target flag */ + RegulatorTargetType regulatorTarget_; + /** regulator fe coupling type flag */ + RegulatorCouplingType couplingMode_; + + /** number of nodes */ + int nNodes_; + /** number of spatial dimensions */ + int nsd_; + /** number of local atoms */ + int nLocal_; + + /** use of localization techniques */ + bool useLocalizedLambda_; + bool useLumpedLambda_; + + /** restrict application in certain directions */ + vector applyInDirection_; + + // method pointers + /** time filtering object */ + TimeFilter * timeFilter_; + /** sets up and solves the regulator equations */ + RegulatorMethod * regulatorMethod_; + + // boundary flux information + BoundaryIntegrationType boundaryIntegrationType_; + const set< pair > * boundaryFaceSet_; + + /** prefix string for registering data */ + const string regulatorPrefix_; + + private: + + // DO NOT define this + AtomicRegulator(); + + }; + + /** + * @class RegulatorMethod + * @brief Base class for implementation of control algorithms + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + class RegulatorMethod { + + public: + + RegulatorMethod(AtomicRegulator * atomicRegulator, + const string & regulatorPrefix = ""); + + virtual ~RegulatorMethod(){}; + + /** instantiate all needed data */ + virtual void construct_transfers(){}; + + /** pre-"run" initialization */ + virtual void initialize(){}; + + /** 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){}; + + /** applies regulator to atoms in the pre-predictor phase */ + virtual void apply_pre_predictor(double dt){}; + + /** applies regulator to atoms in the mid-predictor phase */ + virtual void apply_mid_predictor(double dt){}; + + /** applies regulator to atoms in the post-predictor phase */ + virtual void apply_post_predictor(double dt){}; + + /** applies regulator to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt){}; + + /** applies regulator to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + /** applies regulator to atoms in the pre-corrector phase */ + virtual void apply_pre_force(double dt){}; + + /** applies regulator to atoms in the post-corrector phase */ + virtual void apply_post_force(double dt){}; + + /** applies regulator in pre-force phase */ + virtual void pre_force(){}; + + /** applies regulator in pre-exchange phase */ + virtual void pre_exchange(){}; + + /** applies regulator in post-exchange phase */ + virtual void post_exchange(){}; + + /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs){}; + + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData){}; + virtual double compute_vector(int n) const {return 0;} + + /** final work at the end of a run */ + virtual void finish(){}; + + /** pack fields for restart */ + virtual void pack_fields(RESTART_LIST & data){}; + + protected: + + //data + /** pointer to atomic regulator object for data */ + AtomicRegulator * atomicRegulator_; + + /** pointer to ATC_transfer object */ + ATC_Coupling * atc_; + + /** boundary flux */ + FIELDS & boundaryFlux_; + + /** field mask for specifying boundary flux */ + Array2D fieldMask_; + + /** number of nodes */ + int nNodes_; + + /** prefix string for registering data */ + const string regulatorPrefix_; + + /** mapping for atom materials for atomic FE quadrature */ + Array > atomMaterialGroups_; + + /** shape function derivative matrices for boundary atoms */ + VectorDependencyManager * shpFcnDerivs_; + + private: + + // DO NOT define this + RegulatorMethod(); + + }; + + /** + * @class RegulatorShapeFunction + * @brief Base class for implementation of regulation algorithms using the shape function matrices + */ + // DESIGN each regulator handles only one lambda, but solvers and data are added later + // add a new function to set the linear solver based on enum CG_SOLVE or RSL_SOLVE and shape function matrix + // followed by call to compute sparsity pattern + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorShapeFunction + // base class for all regulators of general form + // of N^T w N lambda = rhs + //-------------------------------------------------------- + //-------------------------------------------------------- + + class RegulatorShapeFunction : public RegulatorMethod { + + public: + + RegulatorShapeFunction(AtomicRegulator * atomicRegulator, + const string & regulatorPrefix = ""); + + virtual ~RegulatorShapeFunction(); + + /** instantiate all needed data */ + virtual void construct_transfers(); + + /** pre-"run" initialization */ + virtual void initialize(); + + /** 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); + + /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + + /** determine if local shape function matrices are needed */ + virtual bool use_local_shape_functions() const {return false;}; + + protected: + + // methods + /** compute sparsity for matrix */ + void compute_sparsity(void); + + /** solve matrix equation */ + void solve_for_lambda(const DENS_MAT & rhs, + DENS_MAT & lambda); + + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights() = 0; + + /** Mapping between unique nodes and nodes overlapping MD region */ + void map_unique_to_overlap(const MATRIX & uniqueData, + MATRIX & overlapData); + + /** Mapping between nodes overlapping MD region to unique nodes */ + void map_overlap_to_unique(const MATRIX & overlapData, + MATRIX & uniqueData); + + /** sets up the transfer which is the set of nodes being regulated */ + virtual void construct_regulated_nodes(); + + /** creates data structure needed for all to regulated node maps */ + virtual void create_node_maps(); + + // member data + /** lambda coupling parameter */ + DENS_MAN * lambda_; + + /** lambda at atomic locations */ + ProtectedAtomQuantity * atomLambdas_; + + /** shape function matrix for use in GLC solve */ + PerAtomSparseMatrix * shapeFunctionMatrix_; + + /** algorithm being used for the linear solver */ + AtomicRegulator::LinearSolverType linearSolverType_; + + /** pre-templated sparsity pattern for N^T * T * N */ + SPAR_MAN matrixTemplate_; + + /** maximum number of iterations used in solving for lambda */ + int maxIterations_; + + /** tolerance used in solving for lambda */ + double tolerance_; + + /** matrix solver object */ + LambdaMatrixSolver * matrixSolver_; + + /** set of nodes used to construct matrix */ + RegulatedNodes * regulatedNodes_; + + /** set of nodes on which lambda is non-zero */ + RegulatedNodes * applicationNodes_; + + /** set of nodes needed for localized boundary quadrature */ + RegulatedNodes * boundaryNodes_; + + /** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */ + NodeToSubset * nodeToOverlapMap_; + + /** mapping from overlap nodes to unique nodes */ + SubsetToNode * overlapToNodeMap_; + + /** shape function matrix for boundary atoms */ + SPAR_MAN * shpFcn_; + + /** atomic weights for boundary atoms */ + DIAG_MAN * atomicWeights_; + + /** element mask for boundary elements corresponding to nodeToOverlapMap_ */ + ElementMaskNodeSet * elementMask_; + + /** maps atoms from atc indexing to regulator indexing */ + LargeToSmallAtomMap * lambdaAtomMap_; + + /** weight per-atom transfer */ + PerAtomQuantity * weights_; + + /** number of spatial dimensions */ + int nsd_; + + /** number of ATC internal atoms on this processor */ + int nLocal_; + + private: + + // DO NOT define this + RegulatorShapeFunction(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolver + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolver { + + public: + + LambdaMatrixSolver(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance); + + virtual ~LambdaMatrixSolver(){}; + + /** assemble the matrix */ + virtual void assemble_matrix(DIAG_MAT & weights); + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda)=0; + + protected: + + /** sparse template for the matrix */ + SPAR_MAN & matrixTemplate_; + + /** non-symmetric part of the matrix */ + SPAR_MAN * shapeFunctionMatrix_; + + /** matrix used to solve for lambda */ + SPAR_MAT lambdaMatrix_; + + /** maximum number of iterations */ + int maxIterations_; + + /** relative tolerance to solve to */ + double tolerance_; + + private: + + // DO NOT define this + LambdaMatrixSolver(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverLumped + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolverLumped : public LambdaMatrixSolver { + + public: + + LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const RegulatedNodes * applicationNodes, const NodeToSubset * nodeToOverlapMap); + + virtual ~LambdaMatrixSolverLumped(){}; + + /** assemble the matrix */ + virtual void assemble_matrix(DIAG_MAT & weights); + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda); + + protected: + + /** lumped version of the matrix governing lamda */ + DIAG_MAT lumpedMatrix_; + + /** set of regulated nodes */ + const RegulatedNodes * applicationNodes_; + + /** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */ + const NodeToSubset * nodeToOverlapMap_; + + private: + + // DO NOT define this + LambdaMatrixSolverLumped(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverCg + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolverCg : public LambdaMatrixSolver { + + public: + + LambdaMatrixSolverCg(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance); + + virtual ~LambdaMatrixSolverCg(){}; + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda); + + protected: + + + private: + + // DO NOT define this + LambdaMatrixSolverCg(); + + }; + +}; + +#endif diff --git a/lib/atc/BodyForce.cpp b/lib/atc/BodyForce.cpp new file mode 100644 index 0000000000..8bc76f2c58 --- /dev/null +++ b/lib/atc/BodyForce.cpp @@ -0,0 +1,32 @@ +#include "BodyForce.h" +#include "ATC_Error.h" + +#include +#include + +using ATC_Utility::command_line; +using ATC_Utility::str2dbl; +using ATC_Utility::str2int; + +namespace ATC { + +BodyForceViscous::BodyForceViscous( + fstream &fileId, map & parameters) + : BodyForce(), gamma_(0) +{ + if (!fileId.is_open()) throw ATC_Error("cannot open material file"); + vector line; + while(fileId.good()) { + command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + double value = str2dbl(line[1]); + if (line[0] == "gamma") { + gamma_ = value; + parameters["gamma"] = gamma_; + } + + } +} + +} diff --git a/lib/atc/BodyForce.h b/lib/atc/BodyForce.h new file mode 100644 index 0000000000..4e312f1720 --- /dev/null +++ b/lib/atc/BodyForce.h @@ -0,0 +1,72 @@ +#ifndef BODY_FORCE_H +#define BODY_FORCE_H + +#include +#include + +using std::map; +using std::string; + +#include "ATC_TypeDefs.h" +#include "Function.h" + +namespace ATC { + + /** + * @class BodyForce + * @brief Base class for models of body forces in the momentum eqn + */ + + class BodyForce + { + public: + BodyForce() {}; + virtual ~BodyForce() {}; + virtual bool body_force(const FIELD_MATS &fields, + DENS_MAT &flux) const { return false; }; + }; + + /** + * @class BodyForceViscous + * @brief viscous body forces + */ + class BodyForceViscous : public BodyForce + { + public: + BodyForceViscous(fstream &matfile,map & parameters); + virtual ~BodyForceViscous() {}; + virtual bool body_force(const FIELD_MATS &fields, + DENS_MAT &flux) const + { + FIELD_MATS::const_iterator v_field = fields.find(VELOCITY); + const DENS_MAT & v = v_field->second; + flux = -gamma_*v; + return true; + } + protected: + double gamma_; + }; + /** + * @class BodyForceElectricField + * @brief electric field body forces + */ + class BodyForceElectricField : public BodyForce + { + public: + BodyForceElectricField(fstream &matfile,map & parameters) + { throw ATC_Error("unimplemented due to issues with accessing electric field"); } + virtual ~BodyForceElectricField() {}; + virtual bool body_force(const FIELD_MATS &fields, + DENS_MAT &flux) const + { + FIELD_MATS::const_iterator v_field = fields.find(VELOCITY); + const DENS_MAT & v = v_field->second; + int nNodes = v.nRows(); + flux.reset(nNodes,1); + return true; + } + }; +} +#endif + + diff --git a/lib/atc/CBLattice.cpp b/lib/atc/CBLattice.cpp new file mode 100644 index 0000000000..67c2cf6be0 --- /dev/null +++ b/lib/atc/CBLattice.cpp @@ -0,0 +1,202 @@ +#include "CBLattice.h" +#include "CbPotential.h" + +namespace ATC { + // removes any overlapping atoms (avoid calling because it scales n^2.) + INDEX AtomCluster::remove_overlap() + { + INDEX removed_count = 0; + std::vector::iterator r(cur_bond_len_.begin()); + std::vector::iterator R(ref_coords_.begin()), Rp; + const double TOL = 1.0e-6 * R->dot(*R); + + for (; R!=ref_coords_.end(); R++, r++) { + for (Rp=R+1; Rp!=ref_coords_.end(); Rp++) { + if (sum_difference_squared(*Rp, *R) < TOL) { + ref_coords_.erase(R--); + cur_bond_len_.erase(r--); + ++removed_count; + break; + } + } + } + return removed_count; + } + //========================================================================= + // writes cluster to 3 column data format + //========================================================================= + void AtomCluster::write_to_dat(std::string path, bool current_config) + { + const int npts = int(ref_coords_.size()); + if (path.substr(path.size()-5,4) != ".dat") path += ".dat"; + fstream fid(path.c_str(), std::ios::out); + + for (int i=0; iphi_r(d)/d); + } + return f; + } + //============================================================================= + // Computes the force constant matrix between atoms I and 0. + //============================================================================= + DENS_MAT AtomCluster::force_constants(INDEX I, const CbPotential *p) const + { + DENS_MAT D(3,3); + for (INDEX i=0; i<3; i++) { + DENS_VEC du(3); + du(i) = 1.0e-6; // take central difference + row(D,i) = perturbed_force(p, I, &du); + du(i) = -du(i); + row(D,i) -= perturbed_force(p, I, &du); + } + D *= 0.5e6; + return D; + } + //============================================================================= + // performs an iterative search for all neighbors within cutoff + //============================================================================= + void CBLattice::_FindAtomsInCutoff(AtomCluster &v) + { + static const int dir[2] = {-1, 1}; + int a, b, c, abc; + std::stack queue(queue0_); + std::set done; + // search in each direction + while (!queue.empty()) { + abc = queue.top(); + queue.pop(); + if (done.find(abc) == done.end()) { // value not in set + unhash(abc,a,b,c); // convert abc to a,b,c + if (_CheckUnitCell(a, b, c, v)) { + done.insert(abc); + // add direct 'outward' neighbors to queue + queue.push(hash(a+dir[a>0], b, c)); + queue.push(hash(a, b+dir[b>0], c)); + queue.push(hash(a, b, c+dir[c>0])); + } + } + } + } + //=========================================================================== + // Computes \f$r^2 = \Vert a n_1 + b n_2 +c n_3 + b_d \Vert^2 \f$ + // and adds atom (a,b,c,d) if \f$r^2 < r_{cutoff}^2 \f$ + // @param a cell x-index + // @param b cell y-index + // @param c cell z-index + //=========================================================================== + bool CBLattice::_CheckUnitCell(char a, char b, char c, AtomCluster &v) + { + const int nsd = n_.nRows(); // number of spatial dimensions + const double A=double(a), B=double(b), C=double(c); + bool found=false; + DENS_VEC r0(nsd,false), R0(nsd,false), Rd(nsd,false); // don't initialize + + for (int i=0; i +#include +#include +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +using std::vector; +namespace ATC +{ + class CbPotential; // forward definition for potentials. + class StressArgs; + + /** + * @class AtomCluster + * @brief Base class for a container for a cluster of atoms around an atom at the origin + * (which is not included in the lists). + * Provides routines for outputting the atoms in the cluster to a vtk file, + * and checking for any overlapping atoms (which should not happen). + */ + + class AtomCluster + { + friend class CBLattice; + friend DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args); + friend int test_FCB(const StressArgs &args); + public: + //* Returns the number of atoms in the cluster. + INDEX size() const { return cur_bond_len_.size(); } + //* removes all atoms from the cluster + void clear() { cur_bond_len_.clear(), ref_coords_.clear(); } + //* Removes any overlapping atoms (if they exist). + INDEX remove_overlap(); + //* Writes the atom positions of the cluster to a dat file. + void write_to_dat(std::string path, bool current_config=true); + //* Writes the atom positions of the cluster to a vtk file. + void write_to_vtk(std::string path, bool current_config=true); + + //* Returns an atom coordinate in the reference configuration. + const DENS_VEC &R(INDEX i) const { return ref_coords_[i]; } + //* Returns an atom coordinate in the current configuration. + DENS_VEC r(INDEX i) const { return F_*R(i); } + + //* Returns the ith atoms bond length to the central atom. + double bond_length(INDEX i) const { return cur_bond_len_[i]; } + //* Returns a reference to the deformation gradient tensor. + const DENS_MAT& deformation_gradient() const { return F_; } + + //* Computes forces on central atom, with atom I displaced by u. + DENS_VEC perturbed_force(const CbPotential *p, int I, DENS_VEC *u) const; + //* Computes the force constant matrix between atoms I and 0. + DENS_MAT force_constants(INDEX I, const CbPotential *p) const; + + private: + + std::vector cur_bond_len_; //*> Bond lengths (current) + std::vector ref_coords_; //*> Atom coordinates (ref) + DENS_MAT F_; //*> Deformation gradient + }; + + /** + * @class CBLattice + * @brief Base class that generates a virtual atom clusters given a lattice and + * a deformation gradient. + */ + + class CBLattice + { + protected: + double RC2_; //*> cutoff radius^2 + std::stack queue0_; //*> cell nghbrs of representative cell + DENS_MAT n_, b_; //*> cols are def base and basis vectors + const MATRIX &N_, &B_; //*> cols are ref base and basis vectors + + public: + //* Operator that outputs the lattice and basis to a stream. + friend std::ostream& operator<<(std::ostream& o, const CBLattice& lattice); + CBLattice(const MATRIX &N, const MATRIX &B); + //* generates the virtual atom cluster + void atom_cluster(const MATRIX &F, double cutoff, AtomCluster &v); + + + protected: + void _FindAtomsInCutoff(AtomCluster &v); + bool _CheckUnitCell(char a, char b, char c, AtomCluster &v); + }; + // hash functions: a, b, c must in range [-128, 127] + inline int hash(int a,int b,int c) {return(a+128)|((b+128)<<8)|((c+128)<<16);} + inline void unhash(int r, int &a, int &b, int &c) + { + a = (r&0xFF) - 128; + b = ((r>>8)&0xFF) - 128; + c = ((r>>16)&0xFF) - 128; + } +} +#endif diff --git a/lib/atc/CG.h b/lib/atc/CG.h new file mode 100644 index 0000000000..7821a495b9 --- /dev/null +++ b/lib/atc/CG.h @@ -0,0 +1,84 @@ +//***************************************************************** +// Iterative template routine -- CG +// +// CG solves the symmetric positive definite linear +// system Ax=b using the Conjugate Gradient method. +// +// CG follows the algorithm described on p. 15 in the +// SIAM Templates book. +// +// The return value indicates convergence within max_iter (input) +// iterations (0), or no convergence within max_iter iterations (1). +// +// Upon successful return, output arguments have the following values: +// +// x -- approximate solution to Ax = b +// max_iter -- the number of iterations performed before the +// tolerance was reached +// tol -- the residual after the final iteration +// +//***************************************************************** + + /** + * @class CG + * @brief Base class for solving the linear system Ax=b using the Conjugate Gradient method + */ + +template < class Matrix, class Vector, class DataVector, class Preconditioner, class Real > +int +CG(const Matrix &A, Vector &x, const DataVector &b, const Preconditioner &M, int &max_iter, Real &tol) { + Real resid; + DenseVector p, z, q; + Real alpha, beta, rho, rho_1(0); + DenseVector tmp; + tmp.reset(b.size()); + + p.reset(b.size()); + z.reset(b.size()); + q.reset(b.size()); + + Real normb = b.norm(); + DenseVector r; + tmp = A*x; + r = b - tmp; + // Implicit assumption that only diagonal matrices are being used for preconditioning + Preconditioner Minv = M.inv(); + + if (normb == 0.0) + normb = 1; + + if ((resid = r.norm() / normb) <= tol) { + tol = resid; + max_iter = 0; + return 0; + } + + for (int i = 0; i < max_iter; i++) { + z = Minv*r; + rho = r.dot(z); + + if (i == 0) + p = z; + else { + beta = rho / rho_1; + tmp = p*beta; + p = z + tmp; + } + + q = A*p; + alpha = rho / p.dot(q); + + x += p*alpha; + r -= q*alpha; + + if ((resid = r.norm() / normb) <= tol) + { + tol = resid; + max_iter = i+1; + return 0; + } + rho_1 = rho; + } + tol = resid; + return 1; +} diff --git a/lib/atc/CauchyBorn.cpp b/lib/atc/CauchyBorn.cpp new file mode 100644 index 0000000000..85b100ca64 --- /dev/null +++ b/lib/atc/CauchyBorn.cpp @@ -0,0 +1,572 @@ +#include "CauchyBorn.h" +#include "VoigtOperations.h" +#include "CBLattice.h" +#include "CbPotential.h" + +using voigt3::to_voigt; + +namespace ATC { + //============================================================================ + // Computes the electron density for EAM potentials + //============================================================================ + double cb_electron_density(const StressArgs &args ) + { + double e_density = 0.0; + for (INDEX a=0; arho(pair.d); + } + return e_density; + } + //============================================================================ + // Computes the stress at a quadrature point + //============================================================================ + void cb_stress(const StressArgs &args, StressAtIP &s, double *F) + { + const double &T = args.temperature; + const bool finite_temp = T > 0.0; + DENS_MAT D; // dynamical matrix (finite temp) + DENS_MAT_VEC dDdF; // derivative of dynamical matrix (finite temp) + double e_density(0.),embed(0.),embed_p(0.),embed_pp(0.),embed_ppp(0.); + DENS_VEC l0; + DENS_MAT L0; + DENS_MAT_VEC M0; + + // If temperature is nonzero then allocate space for + // dynamical matrix and its derivative with respect to F. + if (finite_temp) { + D.reset(3,3); + dDdF.assign(6, DENS_MAT(3,3)); + M0.assign(3, DENS_MAT(3,3)); + L0.reset(3,3); + l0.reset(3); + } + + if (F) *F = 0.0; + + // if using EAM potential, calculate embedding function and derivatives + if (args.potential->terms.embedding) { + + for (INDEX a=0; arho(pair.d); + pair.r = args.vac.r(a); + pair.rho_r = args.potential->rho_r(pair.d); + pair.rho_rr = args.potential->rho_rr(pair.d); + if (finite_temp) { + l0 += pair.r*pair.di*pair.rho_r; + DENS_MAT rR = tensor_product(pair.r, pair.R); + L0.add_scaled(rR, pair.di*pair.rho_r); + DENS_MAT rr = tensor_product(pair.r, pair.r); + rr *= pair.di*pair.di*(pair.rho_rr - pair.di*pair.rho_r); + diagonal(rr) += pair.di*pair.rho_r; + for (int i = 0; i < 3; i++) { + for (int k = 0; k < 3; k++) { + for (int L = 0; L < 3; L++) { + M0[i](k,L) += rr(i,k)*args.vac.R(a)(L); + } + } + } + } + } + embed = args.potential->F(e_density); // "F" in usual EAM symbology + embed_p = args.potential->F_p(e_density); + embed_pp = args.potential->F_pp(e_density); + embed_ppp = args.potential->F_ppp(e_density); + if (F) *F += embed; + if (finite_temp) { + const DENS_MAT ll = tensor_product(l0, l0); + D.add_scaled(ll, embed_pp); + const DENS_VEC llvec = to_voigt(ll); + for (int v = 0; v < 6; v++) { + dDdF[v].add_scaled(L0, embed_ppp*llvec(v)); + } + dDdF[0].add_scaled(M0[0], 2*embed_pp*l0(0)); + dDdF[1].add_scaled(M0[1], 2*embed_pp*l0(1)); + dDdF[2].add_scaled(M0[2], 2*embed_pp*l0(2)); + dDdF[3].add_scaled(M0[1], embed_pp*l0(2)); + dDdF[3].add_scaled(M0[2], embed_pp*l0(1)); + dDdF[4].add_scaled(M0[0], embed_pp*l0(2)); + dDdF[4].add_scaled(M0[2], embed_pp*l0(0)); + dDdF[5].add_scaled(M0[0], embed_pp*l0(1)); + dDdF[5].add_scaled(M0[1], embed_pp*l0(0)); + } + } + + // Loop on all cluster atoms (origin atom not included). + for (INDEX a=0; aterms.pairwise) { + if (F) *F += 0.5*args.potential->phi(pair.d); + pair.phi_r = args.potential->phi_r(pair.d); + pairwise_stress(pair, s); + } + if (args.potential->terms.embedding) { + pair.F_p = embed_p; + pair.rho_r = args.potential->rho_r(pair.d); + embedding_stress(pair, s); + } + + if (finite_temp) { // Compute finite T terms. + pair.r = args.vac.r(a); + if (args.potential->terms.pairwise) { + pair.phi_rr = args.potential->phi_rr(pair.d); + pair.phi_rrr = args.potential->phi_rrr(pair.d); + pairwise_thermal(pair, D, &dDdF); + } + if (args.potential->terms.embedding) { + pair.rho_rr = args.potential->rho_rr(pair.d); + pair.rho_rrr = args.potential->rho_rrr(pair.d); + pair.F_pp = embed_pp; + pair.F_ppp = embed_ppp; + embedding_thermal(pair,D,L0,&dDdF); + } + } + // if has three-body terms ... TODO compute three-body terms + } + + // Finish finite temperature Cauchy-Born. + if (finite_temp) { + const DENS_MAT &F = args.vac.deformation_gradient(); + thermal_end(dDdF, D, F, T, args.boltzmann_constant, s); + } + } + //=========================================================================== + // Computes the elastic energy (free or potential if T=0). + //=========================================================================== + double cb_energy(const StressArgs &args) + { + const double &T = args.temperature; + bool finite_temp = (T > 0.0); + //const bool finite_temp = T > 0.0; + DENS_MAT D; // dynamical matrix (finite temp) + double e_density,embed,embed_p(0.),embed_pp(0.),embed_ppp(0.); + DENS_VEC l0; + DENS_MAT L0; + DENS_MAT_VEC M0; + + // If temperature is nonzero then allocate space for dynamical matrix. + if (finite_temp) { + D.reset(3,3); + l0.reset(3); + } + + double F = 0.0; + // Do pairwise terms, loop on all cluster atoms (origin atom not included). + // if using EAM potential, calculate embedding function and derivatives + if (args.potential->terms.embedding) { + e_density = 0.0; + for (INDEX a=0; arho(pair.d); + pair.r = args.vac.r(a); + if (finite_temp) { + l0 += pair.r*pair.di*pair.rho_r; + } + } + embed = args.potential->F(e_density); + embed_p = args.potential->F_p(e_density); + embed_pp = args.potential->F_pp(e_density); + embed_ppp = args.potential->F_ppp(e_density); + F += embed; + if (finite_temp) { + const DENS_MAT ll = tensor_product(l0, l0); + D.add_scaled(ll, embed_pp); + } + } + + for (INDEX a=0; aterms.pairwise) { + F += 0.5*args.potential->phi(pair.d); + } + + if (finite_temp) { // Compute finite T terms. + pair.r = args.vac.r(a); + if (args.potential->terms.pairwise) { + pair.phi_r = args.potential->phi_r(pair.d); + pair.phi_rr = args.potential->phi_rr(pair.d); + pair.phi_rrr = args.potential->phi_rrr(pair.d); + pairwise_thermal(pair, D); + } + if (args.potential->terms.embedding) { + pair.rho_r = args.potential->rho_r(pair.d); + pair.rho_rr = args.potential->rho_rr(pair.d); + pair.rho_rrr = args.potential->rho_rrr(pair.d); + pair.F_p = embed_p; + pair.F_pp = embed_pp; + pair.F_ppp = embed_ppp; + embedding_thermal(pair,D,L0); + } + } + // if has three-body terms ... TODO compute three-body terms + } + // Finish finite temperature Cauchy-Born. + const double kB = args.boltzmann_constant; + const double hbar = args.planck_constant; + if (finite_temp) { + F += kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D))); + } + //if (finite_temp) F += 0.5*args.boltzmann_constant*T*log(det(D)); + return F; + } + //=========================================================================== + // Computes the entropic energy TS (minus c_v T) + //=========================================================================== + double cb_entropic_energy(const StressArgs &args) + { + const double &T = args.temperature; + DENS_MAT D(3,3); // dynamical matrix (finite temp) + double e_density,embed_p(0.),embed_pp(0.),embed_ppp(0.); + DENS_VEC l0(3); + DENS_MAT L0; + DENS_MAT_VEC M0; + + // if using EAM potential, calculate embedding function and derivatives + if (args.potential->terms.embedding) { + e_density = 0.0; + for (INDEX a=0; arho(pair.d); + pair.r = args.vac.r(a); + l0 += pair.r*pair.di*pair.rho_r; + //DENS_MAT rR = tensor_product(pair.r, pair.R); + //L0.add_scaled(rR, pair.di*args.potential->rho_r(pair.d)); + } + //embed = args.potential->F(e_density); + embed_p = args.potential->F_p(e_density); + embed_pp = args.potential->F_pp(e_density); + embed_ppp = args.potential->F_ppp(e_density); + const DENS_MAT ll = tensor_product(l0, l0); + D.add_scaled(ll, embed_pp); + } + + // Compute the dynamical matrix + // Loop on all cluster atoms (origin atom not included). + for (INDEX a=0; aterms.pairwise) { + pair.phi_r = args.potential->phi_r(pair.d); + pair.phi_rr = args.potential->phi_rr(pair.d); + pair.phi_rrr = args.potential->phi_rrr(pair.d); + pairwise_thermal(pair, D); + } + if (args.potential->terms.embedding) { + pair.rho_r = args.potential->rho_r(pair.d); + pair.rho_rr = args.potential->rho_rr(pair.d); + pair.rho_rrr = args.potential->rho_rrr(pair.d); + pair.F_p = embed_p; + pair.F_pp = embed_pp; + pair.F_ppp = embed_ppp; + embedding_thermal(pair,D,L0); + } + } + // Finish finite temperature Cauchy-Born. + const double kB = args.boltzmann_constant; + const double hbar = args.planck_constant;; + double F = kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D))); + return F; + } + //=========================================================================== + // Computes the stress contribution given the pairwise parameters. + //=========================================================================== + inline void pairwise_stress(const PairParam &p, StressAtIP &s) + { + for (INDEX i=0; i(6,6),Iu*Iu-IIu); + da -= voigt3::derivative_of_square(C); + + dU = tensor_product(U, dfct); + dU.add_scaled(da, fct); + U *= fct; + } + //============================================================================ + // Computes the dynamical matrix (TESTING FUNCTION) + //============================================================================ + DENS_MAT compute_dynamical_matrix(const StressArgs &args) + { + DENS_MAT D(3,3); + for (INDEX a=0; aphi_r(pair.d); + pair.r = args.vac.r(a); + pair.phi_rr = args.potential->phi_rr(pair.d); + pair.phi_rrr = args.potential->phi_rrr(pair.d); + pairwise_thermal(pair, D); + } + return D; + } + //============================================================================ + // Computes the determinant of the dynamical matrix (TESTING FUNCTION) + //============================================================================ + double compute_detD(const StressArgs &args) + { + return det(compute_dynamical_matrix(args)); + } + //============================================================================ + // Computes the derivative of the dynamical matrix (TESTING FUNCTION) + //============================================================================ + DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args) + { + const double EPS = 1.0e-6; + DENS_MAT_VEC dDdF(6, DENS_MAT(3,3)); + for (INDEX i=0; i<3; i++) { + for (INDEX j=0; j<3; j++) { + // store original F + const double Fij = args.vac.F_(i,j); + args.vac.F_(i,j) = Fij + EPS; + DENS_MAT Da = compute_dynamical_matrix(args); + args.vac.F_(i,j) = Fij - EPS; + DENS_MAT Db = compute_dynamical_matrix(args); + args.vac.F_(i,j) = Fij; + + dDdF[0](i,j) = (Da(0,0)-Db(0,0))*(0.5/EPS); + dDdF[1](i,j) = (Da(1,1)-Db(1,1))*(0.5/EPS); + dDdF[2](i,j) = (Da(2,2)-Db(2,2))*(0.5/EPS); + dDdF[3](i,j) = (Da(1,2)-Db(1,2))*(0.5/EPS); + dDdF[4](i,j) = (Da(0,2)-Db(0,2))*(0.5/EPS); + dDdF[5](i,j) = (Da(0,1)-Db(0,1))*(0.5/EPS); + } + } + return dDdF; + } +} diff --git a/lib/atc/CauchyBorn.h b/lib/atc/CauchyBorn.h new file mode 100644 index 0000000000..bc7028d5e4 --- /dev/null +++ b/lib/atc/CauchyBorn.h @@ -0,0 +1,113 @@ +#ifndef CAUCHYBORN_H +#define CAUCHYBORN_H +#include "CbPotential.h" +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +// This file provides all routines necessary for computing the Cauchy-Born +// stresses. + +namespace ATC { + // forward declaration of CbPotential + class CbPotential; + class AtomCluster; + + /** + * @class StressAtIP + * @brief Class for passing the vector of stresses at quadrature points + * Done by storing the quadrature point and providing indexing + */ + + class StressAtIP { + INDEX q; //*> Quadrature point. + DENS_MAT_VEC &S; //*> Stress components at all quad points. + public: + //* Constructor - sets stress reference and current quadrature point. + StressAtIP(DENS_MAT_VEC &s, INDEX _q=0) : q(_q), S(s) {} + //* Indexing operator, gets stress components for the quadrature point. + double& operator()(INDEX i, INDEX j) const { return S[i](q, j); } + //* Sets quadrature point to a new index. + void set_quadrature_point(INDEX qp) { q = qp; } + //* Operator that outputs the stress + friend std::ostream& operator<<(std::ostream& o, const StressAtIP& s) + { o << "stress\n"; + o << s(0,0) << " " << s(0,1) << " " << s(0,2) << "\n"; + o << s(1,0) << " " << s(1,1) << " " << s(1,2) << "\n"; + o << s(2,0) << " " << s(2,1) << " " << s(2,2) << "\n"; + return o; + } + }; + + /** + * @class StressArgs + * @brief Class for storing parameters needed for computing the Cauchy-Born stress + */ + + struct StressArgs { + StressArgs(AtomCluster &v, CbPotential *p, double kB, double hbar, double T) + : vac(v), potential(p), boltzmann_constant(kB), planck_constant(hbar), + temperature(T) {} + AtomCluster &vac; + CbPotential *potential; + double boltzmann_constant; + double planck_constant; + double temperature; + }; + + /** + * @class PairParam + * @brief Class for storing parameters used in pairwise stress computations + */ + struct PairParam { + PairParam(const DENS_VEC &_R, double _d) : R(_R), d(_d), di(1.0/_d) {} + const DENS_VEC &R; //*> Reference bond vector. + DENS_VEC r; //*> Current bond vector. + double d, di; //*> Current bond length and its inverse. + double phi_r; //*> First derivative of pairwise term. + double phi_rr; //*> Second derivative of pairwise term. + double phi_rrr; //*> Third derivative of pairwise term. + double rho_r; + double rho_rr; + double rho_rrr; + double F_p; + double F_pp; + double F_ppp; + }; + + //* for EAM, calculate electron density + double cb_electron_density(const StressArgs &args); + //* Compute stress, from virtual atom cluster, potential + //* temperature (can be 0K), and StressAtIP object to write to. + void cb_stress(const StressArgs &args, StressAtIP &s, double *F=0); + //* Computes the elastic energy (free or potential if T=0). + double cb_energy(const StressArgs &args); + //* Computes the entropic energy + double cb_entropic_energy(const StressArgs &args); + //* Auxiliary functions for cb_stress + //@{ + //* Computes the stress contribution given the pairwise parameters. + void pairwise_stress(const PairParam &p, StressAtIP &s); + //* Computes the stress contribution given the embedding parameters. + void embedding_stress(const PairParam &p, StressAtIP &s); + //* Computes the pairwise thermal components for the stress and free energy. + void pairwise_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT_VEC *dDdF=0); + //* Computes the embedding thermal components for the stress and free energy. + void embedding_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT &L0, DENS_MAT_VEC *dDdF=0); + //* Last stage of the pairwise finite-T Cauchy-Born stress computation. + void thermal_end(const DENS_MAT_VEC &DF, const DENS_MAT &D, const DENS_MAT &F, + const double &T, const double &kb, StressAtIP &s, double *F_w=0); + //* Returns the stretch tensor and its derivative with respect to C (R C-G). + void stretch_tensor_derivative(const DENS_VEC &C, DENS_VEC &U, DENS_MAT &dU); + //@} + + //* Testing functions (to be removed when all CB code is completed) + //@{ + //* Computes the dynamical matrix (TESTING FUNCTION) + DENS_MAT compute_dynamical_matrix(const StressArgs &args); + //* Computes the determinant of the dynamical matrix (TESTING FUNCTION) + double compute_detD(const StressArgs &args); + //* Computes the derivative of the dynamical matrix (TESTING FUNCTION) + DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args); + //@} +} +#endif diff --git a/lib/atc/CbEam.h b/lib/atc/CbEam.h new file mode 100644 index 0000000000..2cf83ef0d7 --- /dev/null +++ b/lib/atc/CbEam.h @@ -0,0 +1,212 @@ +#ifndef CBEAM_H +#define CBEAM_H + +#include +#include +#include "CbPotential.h" +#include "LammpsInterface.h" +#include "pair_eam.h" + +//#define MIN(a,b) ((a) < (b) ? (a) : (b)) +//#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +namespace ATC +{ + + /** + * @class CbEam + * @brief Class for computing Cauchy-Born quantities for an Embeded-Atom Method material + * (A factor of one-half is already included to split the + * bond energy between atoms) + */ + + class CbEam : public CbPotential + { + public: + //! Constructor + CbEam(void) : CbPotential(Interactions(PAIRWISE,EAM)) { + // get pointer to lammps' pair_eam object + lammps_eam = ATC::LammpsInterface::instance()->pair_eam(); + nrho = &lammps_eam->nrho; + nr = &lammps_eam->nr; + nfrho = &lammps_eam->nfrho; + nrhor = &lammps_eam->nrhor; + nz2r = &lammps_eam->nz2r; + type2frho = lammps_eam->type2frho; + type2rhor = lammps_eam->type2rhor; + type2z2r = lammps_eam->type2z2r; + dr = &lammps_eam->dr; + rdr = &lammps_eam->rdr; + drho = &lammps_eam->drho; + rdrho = &lammps_eam->rdrho; + rhor_spline = &lammps_eam->rhor_spline; + frho_spline = &lammps_eam->frho_spline; + z2r_spline = &lammps_eam->z2r_spline; + cutmax = &lammps_eam->cutmax; + } + + //! Returns the cutoff readius of the EAM potential functions rho and z2r. + double cutoff_radius() const { return *cutmax; } + //! Returns the EAM pair energy + double phi(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*z2r_spline)[type2z2r[1][1]][m]; + double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]; + return z2/r; + } + //! Returns the first derivative of the pair energy. + double phi_r(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*z2r_spline)[type2z2r[1][1]][m]; + double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]; + double z2p = (coeff[0]*p + coeff[1])*p + coeff[2]; + return (1.0/r)*(z2p-z2/r); + } + //! Returns the second derivative of the pair energy. + double phi_rr(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*z2r_spline)[type2z2r[1][1]][m]; + double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]; + double z2p = (coeff[0]*p + coeff[1])*p + coeff[2]; + double z2pp = (*rdr)*(2.0*coeff[0]*p + coeff[1]); + return (1.0/r)*(z2pp-2.0*z2p/r+2.0*z2/(r*r)); + } + //! Returns the third derivative of the pair energy. + double phi_rrr(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*z2r_spline)[type2z2r[1][1]][m]; + double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]; + double z2p = (coeff[0]*p + coeff[1])*p + coeff[2]; + double z2pp = (*rdr)*(2.0*coeff[0]*p + coeff[1]); + double z2ppp = (*rdr)*(*rdr)*2.0*coeff[0]; + return (1.0/r)*(z2ppp-3.0*z2pp/r+6.0*z2p/(r*r)-6.0*z2/(r*r*r)); + } + //! Returns the EAM atomic charge density. + double rho(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*rhor_spline)[type2rhor[1][1]][m]; + return (((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]); + } + //! Returns the first derivative of the atomic charge density. + double rho_r(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*rhor_spline)[type2rhor[1][1]][m]; + return ((coeff[0]*p + coeff[1])*p + coeff[2]); + } + //! Returns the second derivative of the atomic charge density. + double rho_rr(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*rhor_spline)[type2rhor[1][1]][m]; + return ((*rdr)*(2.0*coeff[0]*p + coeff[1])); + } + //! Returns the third derivative of the atomic charge density. + double rho_rrr(const double &r) const + { + double p = r*(*rdr) + 1.0; + int m = static_cast (p); + m = MIN(m,(*nr)-1); + p -= m; + p = MIN(p,1.0); + // for now, assume itype = jtype = 1 + double *coeff = (*rhor_spline)[type2rhor[1][1]][m]; + return ((*rdr)*(*rdr)*2.0*coeff[0]); + } + //! Returns the EAM embedding energy. + double F(const double &p) const + { + double q = p*(*rdrho) + 1.0; + int m = static_cast (q); + m = MIN(m,(*nrho)-1); + q -= m; + q = MIN(q,1.0); + // for now, assume itype = 1 + double *coeff = (*frho_spline)[type2frho[1]][m]; + return (((coeff[3]*q + coeff[4])*q + coeff[5])*q + coeff[6]); + } + //! Returns the first derivative of the embedding energy. + double F_p(const double &p) const + { + double q = p*(*rdrho) + 1.0; + int m = static_cast (q); + m = MIN(m,(*nrho)-1); + q -= m; + q = MIN(q,1.0); + // for now, assume itype = 1 + double *coeff = (*frho_spline)[type2frho[1]][m]; + return ((coeff[0]*q + coeff[1])*q + coeff[2]); + } + //! Returns the second derivative of the atomic charge density. + double F_pp(const double &p) const + { + double q = p*(*rdrho) + 1.0; + int m = static_cast (q); + m = MIN(m,(*nrho)-1); + q -= m; + q = MIN(q,1.0); + // for now, assume itype = 1 + double *coeff = (*frho_spline)[type2frho[1]][m]; + return ((*rdrho)*(2.0*coeff[0]*q + coeff[1])); + } + //! Returns the third derivative of the atomic charge density. + double F_ppp(const double &p) const + { + double q = p*(*rdrho) + 1.0; + int m = static_cast (q); + m = MIN(m,(*nrho)-1); + q -= m; + q = MIN(q,1.0); + // for now, assume itype = 1 + double *coeff = (*frho_spline)[type2frho[1]][m]; + return ((*rdrho)*(*rdrho)*2.0*coeff[0]); + } + int *nrho,*nr,*nfrho,*nrhor,*nz2r; + int *type2frho,**type2rhor,**type2z2r; + double *cutmax; + double *dr,*rdr,*drho,*rdrho; + double ****rhor_spline,****frho_spline,****z2r_spline; + LAMMPS_NS::PairEAM* lammps_eam; + }; +} +#endif diff --git a/lib/atc/CbLjCut.h b/lib/atc/CbLjCut.h new file mode 100644 index 0000000000..53402f281c --- /dev/null +++ b/lib/atc/CbLjCut.h @@ -0,0 +1,62 @@ +#ifndef CBLJCUT_H +#define CBLJCUT_H + +#include +#include +#include "CbPotential.h" +namespace ATC +{ + + /** + * @class CbLjCut + * @brief Class for computing Cauchy-Born quantities for a Lennard-Jones/cut material + * (A factor of one-half is already included to split the + * bond energy between atoms) + */ + + class CbLjCut : public CbPotential + { + public: + //! Constructor - initializes coefficients and enables PAIRWISE term. + CbLjCut(double eps, double sig, double cutoff_radius) + : CbPotential(Interactions(PAIRWISE)), + A (4.0*eps*pow(sig, 12)), + B (4.0*eps*pow(sig, 6)), + rcut(cutoff_radius) + { } + + //! Returns the cutoff readius of the LJ potential. + double cutoff_radius() const { return rcut; } + //! Returns the LJ energy between two interacting atoms (6,12). + double phi(const double &r) const + { + const double r6i = 1.0/((r*r*r)*(r*r*r)); + return r6i*(A*r6i - B); + } + //! Returns the first derivative of the LJ energy (7,13). + double phi_r(const double &r) const + { + const double ri = 1.0/r; + const double r6i = (ri*ri*ri)*(ri*ri*ri); + return r6i*ri*(6.0*B - 12.0*A*r6i); + } + //! Returns the second derivative of the LJ energy (8,14). + double phi_rr(const double &r) const + { + const double r2i = 1.0/(r*r); + const double r6i = r2i*r2i*r2i; + return r6i*r2i*(13.0*12.0*A*r6i - 7.0*6.0*B); + } + //! Returns the third derivative of the LJ bond energy (9,15). + double phi_rrr(const double &r) const + { + const double r3i = 1.0/(r*r*r); + const double r9i = r3i*r3i*r3i; + return r9i*(8.0*7.0*6.0*B - 14.0*13.0*12.0*A*r3i*r3i); + } + + const double A, B; //!< phi = Ar^-12 + Br^-6 + const double rcut; //!< cutoff force + }; +} +#endif diff --git a/lib/atc/CbLjSmoothLinear.h b/lib/atc/CbLjSmoothLinear.h new file mode 100644 index 0000000000..d74b2b5df9 --- /dev/null +++ b/lib/atc/CbLjSmoothLinear.h @@ -0,0 +1,88 @@ +#ifndef CBLJSMOOTHLINEAR_H +#define CBLJSMOOTHLINEAR_H + +#include +#include +#include "CbPotential.h" +namespace ATC +{ + + /** + * @class CbLjSmoothLinear + * @brief Class for computing Cauchy-Born quantities for a Lennard-Jones/smooth/linear material + * (A factor of one-half is already included to split the + * bond energy between atoms) + */ + + class CbLjSmoothLinear : public CbPotential + { + public: + //! Constructor - initializes coefficients and enables PAIRWISE term. + CbLjSmoothLinear(double eps, double sig, double cutoff_radius) + : CbPotential(Interactions(PAIRWISE)), + A (4.0*eps*pow(sig, 12)), + B (4.0*eps*pow(sig, 6)), + rcut(cutoff_radius) + { + ricut = 1.0/rcut; + r6icut = (ricut*ricut*ricut)*(ricut*ricut*ricut); + phicut = r6icut*(A*r6icut - B); + dphicut = r6icut*ricut*(6.0*B - 12.0*A*r6icut); + } + + //! Returns the cutoff readius of the LJ potential. + double cutoff_radius() const { return rcut; } + //! Returns the LJ energy between two interacting atoms (6,12). + double phi(const double &r) const + { + const double r6i = 1.0/((r*r*r)*(r*r*r)); + if (r < rcut) { + return (r6i*(A*r6i - B) - phicut - (r-rcut)*dphicut); + } + else { + return 0.0; + } + } + //! Returns the first derivative of the LJ energy (7,13). + double phi_r(const double &r) const + { + const double ri = 1.0/r; + const double r6i = (ri*ri*ri)*(ri*ri*ri); + if (r < rcut) { + return (r6i*ri*(6.0*B - 12.0*A*r6i) - dphicut); + } + else { + return 0.0; + } + } + //! Returns the second derivative of the LJ energy (8,14). + double phi_rr(const double &r) const + { + const double r2i = 1.0/(r*r); + const double r6i = r2i*r2i*r2i; + if (r < rcut) { + return r6i*r2i*(13.0*12.0*A*r6i - 7.0*6.0*B); + } + else { + return 0.0; + } + } + //! Returns the third derivative of the LJ bond energy (9,15). + double phi_rrr(const double &r) const + { + const double r3i = 1.0/(r*r*r); + const double r9i = r3i*r3i*r3i; + if (r < rcut) { + return r9i*(8.0*7.0*6.0*B - 14.0*13.0*12.0*A*r3i*r3i); + } + else { + return 0.0; + } + } + + const double A, B; //!< phi = Ar^-12 + Br^-6 + const double rcut; //!< cutoff radius + double ricut, r6icut, phicut, dphicut; + }; +} +#endif diff --git a/lib/atc/CbPotential.cpp b/lib/atc/CbPotential.cpp new file mode 100644 index 0000000000..741e75eb73 --- /dev/null +++ b/lib/atc/CbPotential.cpp @@ -0,0 +1,88 @@ +#include "CbPotential.h" +#include +namespace ATC +{ + static const double EPS = 1.0e-8; + + // Approximates the derivative of phi + double CbPotential::phi_r(const double &r) const + { + const double dr = r*EPS; + return (phi(r+dr)-phi(r)) / dr; + } + // Approximates the second derivative of phi + double CbPotential::phi_rr(const double &r) const + { + const double dr = r*EPS; + return (phi_r(r+dr)-phi_r(r)) / dr; + } + // Approximates the third derivative of phi + double CbPotential::phi_rrr(const double &r) const + { + const double dr = r*EPS; + return (phi_rr(r+dr)-phi_rr(r)) / dr; + } + // Approximates the derivative of rho + double CbPotential::rho_r(const double &r) const + { + const double dr = r*EPS; + return (rho(r+dr)-rho(r)) / dr; + } + // Approximates the second derivative of rho + double CbPotential::rho_rr(const double &r) const + { + const double dr = r*EPS; + return (rho_r(r+dr)-rho_r(r)) / dr; + } + // Approximates the third derivative of rho + double CbPotential::rho_rrr(const double &r) const + { + const double dr = r*EPS; + return (rho_rr(r+dr)-rho_rr(r)) / dr; + } + // Approximates the derivative of the embedding function + double CbPotential::F_p(const double &p) const + { + const double dp = p*EPS; + return (F(p+dp)-F(p)) / dp; + } + // Approximates the second derivative of the embedding function + double CbPotential::F_pp(const double &p) const + { + const double dp = p*EPS; + return (F_p(p+dp)-F_p(p)) / dp; + } + // Approximates the third derivative of the embedding function + double CbPotential::F_ppp(const double &p) const + { + const double dp = p*EPS; + return (F_pp(p+dp)-F_pp(p)) / dp; + } + // Approximates the derivative of phi3. + double CbPotential::phi3_q (const double &q) const + { + const double dq = q*EPS; + return (phi3(q+dq)-phi3(q)) / dq; + } + // Approximates the second derivative of phi3. + double CbPotential::phi3_qq(const double &q) const + { + const double dq = q*EPS; + return (phi3_q(q+dq)-phi3_q(q)) / dq; + } + // Compute bond angle jik from the squared length of vectors ij,ik,kj. + double calculate_theta(double ij2, double ik2, double jk2) + { + return acos( 0.5*(ik2+ij2-jk2)/sqrt(ij2*ik2) ); + } + // Initializes atomic interactions for up to three different terms. + Interactions::Interactions(int a, int b, int c) + { + // bitwise OR combines the terms that are listed + const int abc = a|b|c; + pairwise = (abc&PAIRWISE)>0; + embedding = (abc&EAM)>0; + three_body = (abc&THREE_BDY)>0; + angle_bending = (abc&ANGLE_BND)>0; + } +} diff --git a/lib/atc/CbPotential.h b/lib/atc/CbPotential.h new file mode 100644 index 0000000000..44b9b7c1de --- /dev/null +++ b/lib/atc/CbPotential.h @@ -0,0 +1,70 @@ +#ifndef CBPOTENTIAL_H +#define CBPOTENTIAL_H + +namespace ATC +{ + //! Enumerated list of interaction types + enum Interaction{PAIRWISE=1, EAM=2, THREE_BDY=4, ANGLE_BND=8}; + //! Booleans that enable types of terms the potential uses. + struct Interactions { + //! Enables up to 3 interaction types. (order independant) + Interactions(int a=0, int b=0, int c=0); + bool pairwise; //!< Pairwise interaction terms exist. + bool embedding; //!< Embedding interaction terms (EAM) exist. + bool three_body; //!< Three-body interaction terms exist. + bool angle_bending; //!< Angle bending interaction terms exist. + }; + + //! Compute bond angle jik from the squared length of vectors ij,ik,kj. + double calculate_theta(double ij2, double ik2, double jk2); + + /** + * @class CbPotential + * @brief Base class for computing Cauchy-Born quantities for an interatomic potential material + * (assume all terms return 0) + */ + + class CbPotential + { + protected: + //! CbPotential base constructor: + //! Initializes which terms are included in energy computation. + //@param potential_terms Switches for atomic interaction terms. + CbPotential(Interactions interaction_terms) : terms(interaction_terms) {} + public: + virtual ~CbPotential() {} + const Interactions terms; //!< switches for types of potential terms. + + //! Returns the minimum distance that all interactions get neglected. + virtual double cutoff_radius() const=0; + + //! @name Pairwise interaction term and derivatives. + //@{ + virtual double phi (const double &r) const { return 0.0; } + virtual double phi_r (const double &r) const; + virtual double phi_rr (const double &r) const; + virtual double phi_rrr(const double &r) const; + //@} + + //! @name Embedding terms. Electron cloud density and embedding functions + //@{ + virtual double rho (const double &r) const { return 0.0; } + virtual double rho_r (const double &r) const; + virtual double rho_rr(const double &r) const; + virtual double rho_rrr(const double &r) const; + virtual double F (const double &p) const { return 0.0; } + virtual double F_p (const double &p) const; + virtual double F_pp(const double &p) const; + virtual double F_ppp(const double &p) const; + //@} + + //! @name Three-body terms and derivatives + //@{ + virtual double phi3 (const double &q) const {return 0.0; } + virtual double phi3_q (const double &q) const; + virtual double phi3_qq(const double &q) const; + //@} + + }; +} +#endif diff --git a/lib/atc/ChargeRegulator.cpp b/lib/atc/ChargeRegulator.cpp new file mode 100644 index 0000000000..dacafebc50 --- /dev/null +++ b/lib/atc/ChargeRegulator.cpp @@ -0,0 +1,705 @@ +#include "ChargeRegulator.h" +#include "PoissonSolver.h" +#include "LammpsInterface.h" +#include "ATC_Coupling.h" +#include "ATC_Error.h" + +#include "Function.h" +#include "PrescribedDataManager.h" + + + +namespace ATC { + + //======================================================== + // Class ChargeRegulator + //======================================================== + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ChargeRegulator::ChargeRegulator(ATC_Coupling * atc) : + AtomicRegulator(atc) + { + // do nothing + } + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ChargeRegulator::~ChargeRegulator() + { + map::iterator it; + for (it = regulators_.begin(); it != regulators_.end(); it++) { + if (it->second) delete it->second; + } + } + + + //-------------------------------------------------------- + // modify: + // parses and adjusts charge regulator state based on + // user input, in the style of LAMMPS user input + //-------------------------------------------------------- + bool ChargeRegulator::modify(int narg, char **arg) + { + bool foundMatch = false; + return foundMatch; + } + + //-------------------------------------------------------- + // construct methods + //-------------------------------------------------------- + void ChargeRegulator::construct_methods() + { + AtomicRegulator::construct_methods(); + + if (atc_->reset_methods()) { + // eliminate existing methods + delete_method(); + // consruct new ones + map::iterator itr; + for (itr = parameters_.begin(); + itr != parameters_.end(); itr++) { + string tag = itr->first; + if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag]; + ChargeRegulatorParameters & p = itr->second; + LammpsInterface * lammpsInterface = LammpsInterface::instance(); + p.groupBit = lammpsInterface->group_bit(tag); + if (! p.groupBit) + throw ATC_Error("ChargeRegulator::initialize group not found"); + switch (p.method) { + case NONE: { + regulators_[tag] = new ChargeRegulatorMethod(this,p); + break; + } + case FEEDBACK: { + regulators_[tag] = new ChargeRegulatorMethodFeedback(this,p); + break; + } + case IMAGE_CHARGE: { + regulators_[tag] = new ChargeRegulatorMethodImageCharge(this,p); + break; + } + case EFFECTIVE_CHARGE: { + regulators_[tag] = new ChargeRegulatorMethodEffectiveCharge(this,p); + break; + } + default: + throw ATC_Error("ChargeRegulator::construct_method unknown charge regulator type"); + } + } + } + } + + //-------------------------------------------------------- + // initialize: + //-------------------------------------------------------- + void ChargeRegulator::initialize() + { + + + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->initialize(); } + + atc_->set_boundary_integration_type(boundaryIntegrationType_); + AtomicRegulator::reset_nlocal(); + AtomicRegulator::delete_unused_data(); + needReset_ = false; + } + + //-------------------------------------------------------- + // apply pre force + //-------------------------------------------------------- + void ChargeRegulator::apply_pre_force(double dt) + { + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->apply_pre_force(dt);} + } + //-------------------------------------------------------- + // apply post force + //-------------------------------------------------------- + void ChargeRegulator::apply_post_force(double dt) + { + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->apply_post_force(dt);} + } + //-------------------------------------------------------- + // output + //-------------------------------------------------------- + void ChargeRegulator::output(OUTPUT_LIST & outputData) + { + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->output(outputData);} + } + + //======================================================== + // Class ChargeRegulatorMethod + //======================================================== + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and ChargeRegulator + //-------------------------------------------------------- + ChargeRegulatorMethod::ChargeRegulatorMethod + (ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & p) + : RegulatorShapeFunction(chargeRegulator), + chargeRegulator_(chargeRegulator), + lammpsInterface_(LammpsInterface::instance()), + rC_(0), rCsq_(0), + targetValue_(NULL), + targetPhi_(p.value), + surface_(p.faceset), + atomGroupBit_(p.groupBit), + boundary_(false), + depth_(p.depth), + surfaceType_(p.surfaceType), + permittivity_(p.permittivity), + initialized_(false) + { + const FE_Mesh * feMesh = atc_->fe_engine()->fe_mesh(); + feMesh->faceset_to_nodeset(surface_,nodes_); + // assume flat get normal and primary coord + PAIR face = *(surface_.begin()); + normal_.reset(nsd_); + feMesh->face_normal(face,0,normal_); + DENS_MAT faceCoords; + feMesh->face_coordinates(face,faceCoords); + point_.reset(nsd_); + for (int i=0; i < nsd_; i++) { point_(i) = faceCoords(i,0); } +#ifdef ATC_VERBOSE + stringstream ss; ss << "point: (" << point_(0) << "," << point_(1) << "," << point_(2) << ") normal: (" << normal_(0) << "," << normal_(1) << "," << normal_(2) << ") depth: " << depth_; + lammpsInterface_->print_msg_once(ss.str()); +#endif + sum_.reset(nsd_); + } + + //-------------------------------------------------------- + // Initialize + + //-------------------------------------------------------- + + +// nomenclature might be a bit backwark: control --> nodes that exert the control, & influence --> atoms that feel the influence + void ChargeRegulatorMethod::initialize(void) + { + interscaleManager_ = &(atc_->interscale_manager()); + + poissonSolver_ =chargeRegulator_->poisson_solver(); + if (! poissonSolver_) throw ATC_Error("need a poisson solver to initialize charge regulator"); + + // atomic vectors + // nodal information + nNodes_ = atc_->num_nodes(); + // constants + rC_ = lammpsInterface_->pair_cutoff(); + rCsq_ = rC_*rC_; + qV2e_ = lammpsInterface_->qv2e(); + qqrd2e_ = lammpsInterface_->qqrd2e(); + + // note derived method set intialized to true + } + + + int ChargeRegulatorMethod::nlocal() { return atc_->nlocal(); } + + void ChargeRegulatorMethod::set_greens_functions(void) + { + // set up Green's function per node + for (int i = 0; i < nNodes_; i++) { + set localNodes; + for (int j = 0; j < nNodes_; j++) + localNodes.insert(j); + // call Poisson solver to get Green's function for node i + DENS_VEC globalGreensFunction; + poissonSolver_->greens_function(i,globalGreensFunction); + // store green's functions as sparse vectors only on local nodes + set::const_iterator thisNode; + SparseVector sparseGreensFunction(nNodes_); + for (thisNode = localNodes.begin(); thisNode != localNodes.end(); thisNode++) + sparseGreensFunction(*thisNode) = globalGreensFunction(*thisNode); + greensFunctions_.push_back(sparseGreensFunction); + } + } + //-------------------------------------------------------- + // output + //-------------------------------------------------------- + void ChargeRegulatorMethod::output(OUTPUT_LIST & outputData) + { + //vector localSum(sum_.size()); + //lammpsInteface_->allsum(localSum.pointer,sum_.pointer,sum_.size()); + DENS_VEC localSum(sum_.size()); + lammpsInterface_->allsum(localSum.ptr(),sum_.ptr(),sum_.size()); + for (int i = 0; i < sum_.size(); i++) { + string name = "charge_regulator_influence_"+to_string(i); +// atc_->fe_engine()->add_global(name,sum_[i]); + } + } + + //======================================================== + // Class ChargeRegulatorMethodFeedback + //======================================================== + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ChargeRegulatorMethodFeedback::ChargeRegulatorMethodFeedback + (ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & p) + : ChargeRegulatorMethod (chargeRegulator, p), + controlNodes_(nodes_), + influenceGroupBit_(p.groupBit) + { + nControlNodes_ = controlNodes_.size(); + sum_.resize(1); + } + //-------------------------------------------------------- + // Initialize + //-------------------------------------------------------- + void ChargeRegulatorMethodFeedback::initialize(void) + { + ChargeRegulatorMethod::initialize(); + if (surfaceType_ != ChargeRegulator::CONDUCTOR) + throw ATC_Error("currently charge feedback can only mimic a conductor"); + set_influence(); + set_influence_matrix(); + initialized_ = true; + } + //-------------------------------------------------------- + // find measurement atoms and nodes + //-------------------------------------------------------- + void ChargeRegulatorMethodFeedback::set_influence(void) + { + + // get nodes that overlap influence atoms & compact list of influence atoms + boundary_ = + atc_->nodal_influence(influenceGroupBit_,influenceNodes_,influenceAtoms_); + nInfluenceAtoms_ = influenceAtoms_.size(); // local + nInfluenceNodes_ = influenceNodes_.size(); // global + stringstream ss; ss << "control nodes: " << nControlNodes_ << " influence nodes: " << nInfluenceNodes_ << " local influence atoms: " << nInfluenceAtoms_ ; + lammpsInterface_->print_msg(ss.str()); + if (nInfluenceNodes_ == 0) throw ATC_Error("no influence nodes"); + + const Array & map = (boundary_) ? atc_->ghost_to_atom_map() : atc_->internal_to_atom_map(); + for (set::const_iterator itr = influenceAtoms_.begin(); itr != influenceAtoms_.end(); itr++) { + influenceAtomsIds_.insert(map(*itr)); + } + } + //-------------------------------------------------------- + // constuct a Green's submatrix + //-------------------------------------------------------- + void ChargeRegulatorMethodFeedback::set_influence_matrix(void) + { + // construct control-influence matrix bar{G}^-1: ds{p} = G{p,m}^-1 dphi{m} + + +// + if (nInfluenceNodes_ < nControlNodes_) throw ATC_Error(" least square not implmented "); + if (nInfluenceNodes_ > nControlNodes_) throw ATC_Error(" solve not possible "); + DENS_MAT G(nInfluenceNodes_,nControlNodes_); + DENS_VEC G_I; + set::const_iterator itr,itr2,itr3; + const Array & nmap = atc_->fe_engine()->fe_mesh()->global_to_unique_map(); + int i = 0; + for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) { + poissonSolver_->greens_function(*itr, G_I); + int j = 0; + for (itr2 = controlNodes_.begin(); itr2 != controlNodes_.end(); itr2++) { + int jnode = nmap(*itr2); + G(i,j++) = G_I(jnode); + } + i++; + } + invG_ = inv(G); + + // construct the prolong-restrict projector N N^T for influence nodes only + + InterscaleManager & interscaleManager(atc_->interscale_manager()); + const SPAR_MAT & N_Ia = (boundary_) ? + (interscaleManager.per_atom_sparse_matrix("InterpolantGhost"))->quantity(): + (interscaleManager.per_atom_sparse_matrix("Interpolant"))->quantity(); + NT_.reset(nInfluenceAtoms_,nInfluenceNodes_); + DENS_MAT NNT(nInfluenceNodes_,nInfluenceNodes_); + int k = 0; + for (itr3 = influenceAtoms_.begin(); itr3 != influenceAtoms_.end(); itr3++) { + int katom = *itr3; + int i = 0; + for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) { + int Inode = *itr; + int j = 0; + NT_(k,i) = N_Ia(katom,Inode); + for (itr2 = influenceNodes_.begin(); itr2 != influenceNodes_.end(); itr2++) { + int Jnode = *itr2; + NNT(i,j++) += N_Ia(katom,Inode)*N_Ia(katom,Jnode); + } + i++; + } + k++; + } + // swap contributions across processors + DENS_MAT localNNT = NNT; + int count = NNT.nRows()*NNT.nCols(); + lammpsInterface_->allsum(localNNT.ptr(),NNT.ptr(),count); + invNNT_ = inv(NNT); + + // total influence matrix + if (nInfluenceAtoms_ > 0) { NTinvNNTinvG_ = NT_*invNNT_*invG_; } + + } + + //-------------------------------------------------------- + // change potential/charge pre-force calculation + //-------------------------------------------------------- + void ChargeRegulatorMethodFeedback::apply_pre_force(double dt) + { + + sum_ = 0; + if (nInfluenceAtoms_ == 0) return; // nothing to do + apply_feedback_charges(); + } + //-------------------------------------------------------- + // apply feedback charges to atoms + //-------------------------------------------------------- + void ChargeRegulatorMethodFeedback::apply_feedback_charges() + { + double * q = lammpsInterface_->atom_charge(); + // calculate error in potential on the control nodes + + const DENS_MAT & phiField = (atc_->field(ELECTRIC_POTENTIAL)).quantity(); + DENS_MAT dphi(nControlNodes_,1); + int i = 0; + set::const_iterator itr; + for (itr = controlNodes_.begin(); itr != controlNodes_.end(); itr++) { + dphi(i++,0) = targetPhi_ - phiField(*itr,0); + } + + // construct the atomic charges consistent with the correction + DENS_MAT dq = NTinvNNTinvG_*dphi; + i = 0; + for (itr = influenceAtomsIds_.begin(); itr != influenceAtomsIds_.end(); itr++) { + sum_(0) += dq(i,0); + q[*itr] += dq(i++,0); + } + + (interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE))->force_reset(); + (interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE, GHOST))->force_reset(); + } + + //======================================================== + // Class ChargeRegulatorMethodImageCharge + //======================================================== + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ChargeRegulatorMethodImageCharge::ChargeRegulatorMethodImageCharge + (ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & p) + : ChargeRegulatorMethod (chargeRegulator, p), + imageNodes_(nodes_) + { + } + //-------------------------------------------------------- + // Initialize + //-------------------------------------------------------- + void ChargeRegulatorMethodImageCharge::initialize(void) + { + ChargeRegulatorMethod::initialize(); + if (surfaceType_ != ChargeRegulator::DIELECTRIC) throw ATC_Error("currently image charge can only mimic a dielectric"); + double eps1 = permittivity_;// dielectric + double eps2 = lammpsInterface_->dielectric();// ambient + permittivityRatio_ = (eps2-eps1)/(eps2+eps1); +#ifdef ATC_VERBOSE + stringstream ss; ss << "permittivity ratio: " << permittivityRatio_; + lammpsInterface_->print_msg_once(ss.str()); +#endif + set_greens_functions(); + +/////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////// + initialized_ = true; + } + + //-------------------------------------------------------- + // change potential/charge post-force calculation + //-------------------------------------------------------- + void ChargeRegulatorMethodImageCharge::apply_post_force(double dt) + { + sum_ = 0; + apply_local_forces(); + + //correct_forces(); + } + + //-------------------------------------------------------- + // apply local coulomb forces + // -- due to image charges + //-------------------------------------------------------- + void ChargeRegulatorMethodImageCharge::apply_local_forces() + { + + int inum = lammpsInterface_->neighbor_list_inum(); + int * ilist = lammpsInterface_->neighbor_list_ilist(); + int * numneigh = lammpsInterface_->neighbor_list_numneigh(); + int ** firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + + const int *mask = lammpsInterface_->atom_mask(); +///.............................................. + double ** x = lammpsInterface_->xatom(); + double ** f = lammpsInterface_->fatom(); + double * q = lammpsInterface_->atom_charge(); + + // loop over neighbor list + for (int ii = 0; ii < inum; ii++) { + int i = ilist[ii]; + double qi = q[i]; + if ((mask[i] & atomGroupBit_) && qi != 0.) { + double* fi = f[i]; + DENS_VEC xi(x[i],nsd_); + // distance to surface + double dn = reflect(xi); + // all ions near the interface/wall + // (a) self image + if (dn < rC_) { // close enough to wall to have explicit image charges + double factor_coul = 1; + double dx = 2.*dn; // distance to image charge + double fn = factor_coul*qi*qi*permittivityRatio_/dx; + fi[0] += fn*normal_[0]; + fi[1] += fn*normal_[1]; + fi[2] += fn*normal_[2]; + sum_ += fn*normal_; + // (b) neighbor images + int * jlist = firstneigh[i]; + int jnum = numneigh[i]; + for (int jj = 0; jj < jnum; jj++) { + int j = jlist[jj]; + // this changes j + double factor_coul = lammpsInterface_->coulomb_factor(j); + double qj = q[j]; + if (qj != 0.) { // all charged neighbors + DENS_VEC xj(x[j],nsd_); + dn = reflect(xj); + DENS_VEC dx = xi-xj; + double r2 = dx.norm_sq(); + // neighbor image j' inside cutoff from i + if (r2 < rCsq_) { + double fm = factor_coul*qi*qj*permittivityRatio_/r2; + fi[0] += fm*dx(0); + fi[1] += fm*dx(1); + fi[2] += fm*dx(2); + sum_ += fm*dx; + } + } + } + } // end i < rC if + } + } + // update managed data + (interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_FORCE))->force_reset(); + } + + //-------------------------------------------------------- + // correct charge densities + // - to reflect image charges + //-------------------------------------------------------- + void ChargeRegulatorMethodImageCharge::correct_charge_densities() + { + } + + + //-------------------------------------------------------- + // correct_forces + // - due to image charge density used in short-range solution + //-------------------------------------------------------- + void ChargeRegulatorMethodImageCharge::correct_forces() + { + } + + + //======================================================== + // Class ChargeRegulatorMethodEffectiveCharge + //======================================================== + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + + ChargeRegulatorMethodEffectiveCharge::ChargeRegulatorMethodEffectiveCharge( + ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & p) + : ChargeRegulatorMethod (chargeRegulator, p), + chargeDensity_(p.value), + useSlab_(false) + { + } + //-------------------------------------------------------- + // add_charged_surface + //-------------------------------------------------------- + void ChargeRegulatorMethodEffectiveCharge::initialize( ) + { + ChargeRegulatorMethod::initialize(); + boundary_ = atc_->is_ghost_group(atomGroupBit_); + // set face sources to all point at unit function for use in integration + SURFACE_SOURCE faceSources; + map > & fs(faceSources[ELECTRIC_POTENTIAL]); + XT_Function * f = XT_Function_Mgr::instance()->constant_function(1.); + set< PAIR >::const_iterator fsItr; + for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) { + Array < XT_Function * > & dof = fs[*fsItr]; + dof.reset(1); + dof(0) = f; + } + + // computed integrals of nodal shape functions on face + FIELDS nodalFaceWeights; + Array fieldMask(NUM_FIELDS); fieldMask(ELECTRIC_POTENTIAL) = true; + (atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,nodalFaceWeights); + const DENS_MAT & w = (nodalFaceWeights[ELECTRIC_POTENTIAL].quantity()); + + // Get coordinates of each node in face set + for (set::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) { + DENS_VEC x = atc_->fe_engine()->fe_mesh()->nodal_coordinates(*n); + // compute effective charge at each node I + // multiply charge density by integral of N_I over face + double v = w(*n,0)*chargeDensity_; + pair p(x,v); + nodeXFMap_[*n] = p; + } + + // set up data structure holding charged faceset information + FIELDS sources; + double k = lammpsInterface_->coulomb_constant(); + string fname = "radial_power"; + double xtArgs[8]; + xtArgs[0] = 0; xtArgs[1] = 0; xtArgs[2] = 0; + xtArgs[3] = 1; xtArgs[4] = 1; xtArgs[5] = 1; + xtArgs[6] = k*chargeDensity_; + xtArgs[7] = -1.; + const DENS_MAT & s(sources[ELECTRIC_POTENTIAL].quantity()); + NODE_TO_XF_MAP::iterator XFitr; + for (XFitr = nodeXFMap_.begin(); XFitr != nodeXFMap_.end(); XFitr++) { + // evaluate voltage at each node I + // set up X_T function for integration: k*chargeDensity_/||x_I - x_s|| + // integral is approximated in two parts: + // 1) near part with all faces within r < rcrit evaluated as 2 * pi * rcrit * k sigma A/A0, A is area of this region and A0 = pi * rcrit^2, so 2 k sigma A / rcrit + // 2) far part evaluated using Gaussian quadrature on faceset + DENS_VEC x((XFitr->second).first); + xtArgs[0] = x(0); xtArgs[1] = x(1); xtArgs[2] = x(2); + f = XT_Function_Mgr::instance()->function(fname,8,xtArgs); + for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) { + fs[*fsItr] = f; + } + + // perform integration to get quantities at nodes on facesets + // V_J' = int_S N_J k*sigma/|x_I - x_s| dS + (atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,sources); + + // sum over all nodes in faceset to get total potential: + // V_I = sum_J VJ' + int node = XFitr->first; + nodalChargePotential_[node] = s(node,0); + double totalPotential = 0.; + for (set::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) { + totalPotential += s(*n,0); } + + // assign an XT function per each node and + // then call the prescribed data manager and fix each node individually. + f = XT_Function_Mgr::instance()->constant_function(totalPotential); + (atc_->prescribed_data_manager())->fix_field(node,ELECTRIC_POTENTIAL,0,f); + } + initialized_ = true; + } + + //-------------------------------------------------------- + // add effective forces post LAMMPS force call + //-------------------------------------------------------- + void ChargeRegulatorMethodEffectiveCharge::apply_post_force(double dt) + { + apply_local_forces(); + } + + //-------------------------------------------------------- + // apply_charged_surfaces + //-------------------------------------------------------- + void ChargeRegulatorMethodEffectiveCharge::apply_local_forces() + { + double * q = lammpsInterface_->atom_charge(); + _atomElectricalForce_.resize(nlocal(),nsd_); + + double penalty = poissonSolver_->penalty_coefficient(); + if (penalty <= 0.0) throw ATC_Error("ExtrinsicModelElectrostatic::apply_charged_surfaces expecting non zero penalty"); + + double dx[3]; + const DENS_MAT & xa((interscaleManager_->per_atom_quantity("AtomicCoarseGrainingPositions"))->quantity()); + +// WORKSPACE - most are static + SparseVector dv(nNodes_); + vector > derivativeVectors; + derivativeVectors.reserve(nsd_); + const SPAR_MAT_VEC & shapeFunctionDerivatives((interscaleManager_->vector_sparse_matrix("InterpolateGradient"))->quantity()); + + DenseVector nodeIndices; + DENS_VEC nodeValues; + + NODE_TO_XF_MAP::const_iterator inode; + for (inode = nodeXFMap_.begin(); inode != nodeXFMap_.end(); inode++) { + + int node = inode->first; + DENS_VEC xI = (inode->second).first; + double qI = (inode->second).second; + double phiI = nodalChargePotential_[node]; + for (int i = 0; i < nlocal(); i++) { + int atom = (atc_->internal_to_atom_map())(i); + double qa = q[atom]; + if (qa != 0) { + double dxSq = 0.; + for (int j = 0; j < nsd_; j++) { + dx[j] = xa(i,j) - xI(j); + dxSq += dx[j]*dx[j]; + } + if (dxSq < rCsq_) { + // first apply pairwise coulombic interaction + if (!useSlab_) { + double coulForce = qqrd2e_*qI*qa/(dxSq*sqrtf(dxSq)); + for (int j = 0; j < nsd_; j++) { + _atomElectricalForce_(i,j) += dx[j]*coulForce; } + } + + // second correct for FE potential induced by BCs + // determine shape function derivatives at atomic location + // and construct sparse vectors to store derivative data + + + for (int j = 0; j < nsd_; j++) { + shapeFunctionDerivatives[j]->row(i,nodeValues,nodeIndices); + derivativeVectors.push_back(dv); + for (int k = 0; k < nodeIndices.size(); k++) { + derivativeVectors[j](nodeIndices(k)) = nodeValues(k); } + } + + // compute greens function from charge quadrature + + SparseVector shortFePotential(nNodes_); + shortFePotential.add_scaled(greensFunctions_[node],penalty*phiI); + + // compute electric field induced by charge + DENS_VEC efield(nsd_); + for (int j = 0; j < nsd_; j++) { + efield(j) = -.1*dot(derivativeVectors[j],shortFePotential); } + + // apply correction in atomic forces + double c = qV2e_*qa; + for (int j = 0; j < nsd_; j++) { + if ((!useSlab_) || (j==nsd_)) { + _atomElectricalForce_(i,j) -= c*efield(j); + } + } + } + } + } + } + + } + +}; // end namespace diff --git a/lib/atc/ChargeRegulator.h b/lib/atc/ChargeRegulator.h new file mode 100644 index 0000000000..515ffd450c --- /dev/null +++ b/lib/atc/ChargeRegulator.h @@ -0,0 +1,281 @@ +#ifndef CHARGE_REGULATOR_H +#define CHARGE_REGULATOR_H + +// ATC headers +#include "AtomicRegulator.h" + +//class PoissonSolver; +#include "PoissonSolver.h" + +// other headers +#include + +namespace ATC { + + /** + * @class ChargeRegulator + * @brief Manager class for atom-continuum control of charge and potential + */ + + class ChargeRegulatorMethod; + class ChargeRegulator : public AtomicRegulator { + + public: + + /** charge regulator types */ + enum ChargeRegulatorType { + NONE=0, + FEEDBACK, + IMAGE_CHARGE, + EFFECTIVE_CHARGE + }; + enum ChargedSurfaceType { + INSULATOR=0, + DIELECTRIC, + CONDUCTOR + }; + + /** parser data */ + struct ChargeRegulatorParameters { + ChargeRegulatorParameters(): + method(NONE), + value(0), + groupBit(0), + depth(0), + permittivity(0), + surfaceType(INSULATOR) { }; + ChargeRegulatorType method; + double value; + int groupBit; + string groupTag; + double depth; + double permittivity; + ChargedSurfaceType surfaceType; + FSET faceset; + }; + + /** constructor */ + ChargeRegulator(ATC_Coupling *atc); + + /** destructor */ + ~ChargeRegulator(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + virtual void construct_methods(); + virtual void initialize(); + virtual void apply_pre_force(double dt); + virtual void apply_post_force(double dt); + virtual void output(OUTPUT_LIST & outputData); + virtual double compute_vector(int n) {return 0;} // TODO + + void assign_poisson_solver(PoissonSolver * solver) { poissonSolver_ = solver;} + PoissonSolver * poisson_solver(void) { return poissonSolver_;} + + protected: + + /** poisson solver */ + PoissonSolver * poissonSolver_; + + + /** registry charge regulators */ + map regulators_; + map parameters_; + + private: + ChargeRegulator(); // DO NOT define this + }; + + /** + * @class ChargeRegulatorMethod + * @brief Base class for implementation of ChargeRegulator algorithms + */ + + class ChargeRegulatorMethod : public RegulatorShapeFunction { + + public: + ChargeRegulatorMethod(ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & parameters); + ~ChargeRegulatorMethod(){}; + virtual void initialize(void); + void set_greens_functions(); + virtual void apply_pre_force(double dt){}; + virtual void apply_post_force(double dt){}; + virtual void set_weights() {}; + const DENS_VEC & total_influence() const { return sum_;} + virtual void output(OUTPUT_LIST & outputData); + + protected: + int nlocal(); + /** pointer to charge regulator object for data */ + ChargeRegulator * chargeRegulator_; + /** interscale manager */ + class InterscaleManager * interscaleManager_; + /** direct interface to lammps */ + class LammpsInterface * lammpsInterface_; + /** poisson solver */ + PoissonSolver * poissonSolver_; + /** (subset) of Green's functions */ + vector > greensFunctions_; + /** cutoff radius */ + double rC_,rCsq_; + /** conversion constants */ + double qV2e_, qqrd2e_; + /** member data */ + XT_Function * targetValue_; + double targetPhi_; + // controlling + FSET surface_; + NSET nodes_; + int atomGroupBit_; + bool boundary_; // atoms on boundary + DENS_VEC point_; + DENS_VEC normal_; + double depth_; + ChargeRegulator::ChargedSurfaceType surfaceType_; + double permittivity_; + bool initialized_; + DENS_VEC sum_; + /** workspace */ + mutable DENS_MAT _atomElectricalForce_; + private: + ChargeRegulatorMethod(); // DO NOT define this + }; + + /** + * @class ChargeRegulatorMethodFeedback + * @brief ChargeRegulator with feedback and ad hoc positions + * can be used for conductor (1 charged sheet) or insulator (2 sheets) + */ + + class ChargeRegulatorMethodFeedback : public ChargeRegulatorMethod { + + public: + + /** constructor */ + ChargeRegulatorMethodFeedback(ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & parameters); + + /** destructor */ + ~ChargeRegulatorMethodFeedback(){}; + + /** initialize */ + virtual void initialize(void); + + /** set influence nodes and atoms */ + void set_influence(); + + void set_influence_matrix(void); + + /** post first verlet step */ + virtual void apply_pre_force(double dt); + + void apply_feedback_charges(); + + protected: + + int nControlNodes_; + NSET & controlNodes_; + // measurement/controlled + int influenceGroupBit_; + int nInfluenceAtoms_; + NSET influenceAtoms_, influenceAtomsIds_; + int nInfluenceNodes_; + NSET influenceNodes_; + + + DENS_MAT invG_; + DENS_MAT invNNT_; + DENS_MAT NT_; + DENS_MAT NTinvNNTinvG_; + + private: + ChargeRegulatorMethodFeedback(); // DO NOT define this + }; + + /** + * @class ChargeRegulatorMethodImageCharge + * @brief ChargeRegulator with image charges + */ + + class ChargeRegulatorMethodImageCharge : public ChargeRegulatorMethod { + + public: + + /** constructor */ + ChargeRegulatorMethodImageCharge(ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & parameters); + + /** destructor */ + ~ChargeRegulatorMethodImageCharge(){}; + + /** initialize */ + virtual void initialize(void); + + /** post first verlet step */ + virtual void apply_post_force(double dt); + + protected: + double reflect(DENS_VEC & x) const { + double dn = (x-point_).dot(normal_); + x -= 2*dn*normal_; + return dn; + } + // internal functions + void apply_local_forces(); + void correct_charge_densities(); + void correct_forces(); + + double layerDepth_; + double permittivityRatio_; + NSET & imageNodes_; + DENS_MAT imageTransferOp_; + + private: + ChargeRegulatorMethodImageCharge(); // DO NOT define this + }; + + /** + * @class ChargeRegulatorMethodEffectiveCharge + * @brief ChargeRegulator with effective charges at FE quadrature pts + */ + + class ChargeRegulatorMethodEffectiveCharge : public ChargeRegulatorMethod { + + typedef map > NODE_TO_XF_MAP; + + public: + + /** constructor */ + ChargeRegulatorMethodEffectiveCharge(ChargeRegulator *chargeRegulator, + ChargeRegulator::ChargeRegulatorParameters & parameters); + + /** destructor */ + ~ChargeRegulatorMethodEffectiveCharge(){}; + + /** initialize */ + virtual void initialize(void); + + /** post first verlet step */ + virtual void apply_post_force(double dt); + + protected: + // internal functions + void apply_local_forces(); + + // member data + double chargeDensity_; + map nodalChargePotential_; + + NODE_TO_XF_MAP nodeXFMap_; + + bool useSlab_; + + + private: + ChargeRegulatorMethodEffectiveCharge(); // DO NOT define this + }; + +}; + +#endif diff --git a/lib/atc/CloneVector.h b/lib/atc/CloneVector.h new file mode 100644 index 0000000000..5f747012fd --- /dev/null +++ b/lib/atc/CloneVector.h @@ -0,0 +1,253 @@ +#ifndef CLONEVECTOR_H +#define CLONEVECTOR_H + +#include "Vector.h" + +namespace ATC_matrix { + + /** + * @class CloneVector + * @brief Class for creating objects that wrap matrix data for manipulation through vector operations + */ + +template +class CloneVector : public Vector +{ +public: + CloneVector(); // do not implement + CloneVector(const Vector &c); + CloneVector(const Matrix &c, int dim, INDEX idx=0); + CloneVector(const DiagonalMatrix &c, INDEX idx=0); + + // overloaded virtual functions + T& operator[](INDEX i); + T operator[](INDEX i) const; + T operator()(INDEX i, INDEX j=0) const; + T& operator()(INDEX i, INDEX j=0); + INDEX nRows() const; + + CloneVector& operator=(const T &v); + CloneVector& operator=(const CloneVector &C); + CloneVector& operator=(const Matrix &C); + + virtual bool memory_contiguous() const; + T* ptr() const; + void resize(INDEX nRows, INDEX nCols=0, bool copy=false); + void reset(INDEX nRows, INDEX nCols=0, bool zero=true); + void copy(const T * ptr, INDEX nRows, INDEX nCols=0); + +private: + void _resize(INDEX nRows, INDEX nCols, bool copy, bool zero); + + Vector * const _baseV; // ptr to a base vector + Matrix * const _baseM; // ptr to a base matrix + int _clone_type; // what to clone (see enum CLONE_TYPE) + INDEX _idx; // index of matrix dimension to clone +}; +/////////////////////////////////////////////////////////////////////////////// +// Template definitions /////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// Construct from another vector +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const Vector &c) + : Vector(), _baseV(const_cast*>(&c)), _baseM(NULL) +{} +//----------------------------------------------------------------------------- +// Construct from a matrix, the const_cast isn't pretty +/* CloneVector(const Matrix &c, int dim, INDEX idx) +/ attaches to a slice of a matrix +/ Arguments: c = pointer to the matrix +/ dim = type of slice CLONE_ROW, CLONE_COL, CLONE_DIAG +/ idx = index of row or column (no effect on diag currently) +*/ +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const Matrix &c, int dim, INDEX idx) + : Vector(), _baseV(NULL), _baseM(const_cast*>(&c)) + , _clone_type(dim), _idx(idx) +{} +//----------------------------------------------------------------------------- +// Construct from a DiagonalMatrix +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const DiagonalMatrix &c, INDEX idx) + : Vector(), _baseV(NULL), _baseM(const_cast*>(&c)) + , _clone_type(CLONE_DIAG), _idx(0) +{} +//----------------------------------------------------------------------------- +// value (const) indexing operator +//----------------------------------------------------------------------------- +template +T CloneVector::operator()(INDEX i, INDEX j) const +{ + return (*this)[i]; +} +//----------------------------------------------------------------------------- +// reference index operator +//----------------------------------------------------------------------------- +template +T& CloneVector::operator()(INDEX i, INDEX j) +{ + return (*this)[i]; +} +//----------------------------------------------------------------------------- +// Indexes the cloned vector either from another vector or a matrix +//----------------------------------------------------------------------------- +template +T CloneVector::operator[](INDEX i) const +{ + if (_baseV) return (*_baseV)(i); + if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i); + else if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx); + else if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i); + return 0; +} +//----------------------------------------------------------------------------- +// Indexes the cloned vector either from another vector or a matrix +//----------------------------------------------------------------------------- +template +T& CloneVector::operator[](INDEX i) +{ + if (_baseV) return (*_baseV)(i); + if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i); + if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx); + if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i); + return (*_baseV)(i); +} +//----------------------------------------------------------------------------- +// Returns the size of the base vector or of the row/col of the base matrix +//----------------------------------------------------------------------------- +template +INDEX CloneVector::nRows() const +{ + using std::min; + if (_baseV) return _baseV->size(); + if (_clone_type == CLONE_ROW) return _baseM->nCols(); + if (_clone_type == CLONE_COL) return _baseM->nRows(); + if (_clone_type == CLONE_DIAG) return min(_baseM->nRows(), _baseM->nCols()); + return 0; +} +//----------------------------------------------------------------------------- +// assigns all elements to a constant +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const T &v) +{ + this->set_all_elements_to(v); + return *this; +} +//----------------------------------------------------------------------------- +// assigns all elements to the corresponding elements in C +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const CloneVector &C) +{ + GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator="); + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) (*this)[i] = C[i]; + return *this; +} +//----------------------------------------------------------------------------- +// assigns all elements to the corresponding elements in C +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const Matrix &C) +{ + GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator="); + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) (*this)[i] = C[i]; + return *this; +} +//----------------------------------------------------------------------------- +// returns true only if its guaranteed memory is contiguous +//----------------------------------------------------------------------------- +template +bool CloneVector::memory_contiguous() const +{ + // drill down through clone of clones + if (_baseV) return _baseV->memory_contiguous(); + // could be okay if DiagonalMatrix, but can't guarantee this + if (_clone_type == CLONE_DIAG) return false; +#ifdef ROW_STORAGE + return _clone_type == CLONE_ROW; +#else + return _clone_type == CLONE_COL; +#endif +} +//----------------------------------------------------------------------------- +// Returns a pointer to the data unless the data is a column of a matrix +//----------------------------------------------------------------------------- +template +T* CloneVector::ptr() const +{ + if (_baseV) return _baseV->ptr(); +#ifdef ROW_STORAGE + if (_clone_type == CLONE_ROW) return _baseM->ptr() + this->size()*_idx; + if (_clone_type == CLONE_COL) return _baseM->ptr() + this->size(); + if (_clone_type == CLONE_DIAG) return _baseM->ptr(); +#else + if (_clone_type == CLONE_COL) return _baseM->ptr() + this->size()*_idx; + if (_clone_type == CLONE_ROW) return _baseM->ptr() + this->size(); + if (_clone_type == CLONE_DIAG) return _baseM->ptr(); +#endif + return 0; +} +//----------------------------------------------------------------------------- +// general resize function, can handle parents that are matrices or vectors +//----------------------------------------------------------------------------- +template +void CloneVector::_resize(INDEX nRows, INDEX nCols, bool copy, bool zero) +{ + if (_baseV) + { + if (copy) _baseV->resize(nRows, nCols, copy); + else _baseV->reset (nRows, nCols, zero); + return; + } + // parent is a matrix, need to decide what the Vector is cloning + switch (_clone_type) + { + case CLONE_ROW: // now the leading dimension is rows + nCols = nCols ? nCols : _baseM->nCols(); + break; + case CLONE_COL: // now the leading dimension is columns + nCols = nCols ? nCols : _baseM->nRows(); + ATC_Utility::swap(nRows, nCols); + break; + case CLONE_DIAG: // lets just hope you knew what you were doing + break; + default: + return; + } + if (zero) _baseM->reset(nRows, nCols, zero); // zero overrides copy + else _baseM->resize(nRows, nCols, copy); +} +//----------------------------------------------------------------------------- +// resizes the matrix and optionally copies what fits +//----------------------------------------------------------------------------- +template +void CloneVector::resize(INDEX nRows, INDEX nCols, bool copy) +{ + _resize(nRows, nCols, copy, false); +} +//----------------------------------------------------------------------------- +// resizes the matrix and optionally zeros it out +//----------------------------------------------------------------------------- +template +void CloneVector::reset(INDEX nRows, INDEX nCols, bool zero) +{ + _resize(nRows, nCols, false, zero); +} +//----------------------------------------------------------------------------- +// resizes the matrix and copies data +//----------------------------------------------------------------------------- +template +void CloneVector::copy(const T * ptr, INDEX nRows, INDEX nCols) +{ + _resize(nRows, nCols, false, false); + memcpy(this->ptr(), ptr, this->size()*sizeof(T)); +} + +} // end namespace +#endif diff --git a/lib/atc/ConcentrationRegulator.cpp b/lib/atc/ConcentrationRegulator.cpp new file mode 100644 index 0000000000..47f80cf92f --- /dev/null +++ b/lib/atc/ConcentrationRegulator.cpp @@ -0,0 +1,617 @@ +#include "ConcentrationRegulator.h" +#include "LammpsInterface.h" +#include "ATC_Coupling.h" +#include "ATC_Error.h" + +namespace ATC { + +const double kMinScale_ = 10000.; + + //======================================================== + // Class ConcentrationRegulator + //======================================================== + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ConcentrationRegulator::ConcentrationRegulator(ATC_Coupling * atc) : + AtomicRegulator(atc) + { + // do nothing + } + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ConcentrationRegulator::~ConcentrationRegulator() + { + if (regulators_.size()) { + map::iterator it; + for (it = regulators_.begin(); it != regulators_.end(); ++it) { + delete it->second; + } + regulators_.clear(); + } + if (parameters_.size()) parameters_.clear(); + } + + //-------------------------------------------------------- + // modify: + // parses and adjusts charge regulator state based on + // user input, in the style of LAMMPS user input + //-------------------------------------------------------- + bool ConcentrationRegulator::modify(int narg, char **arg) + { + bool foundMatch = false; + return foundMatch; + } + + //-------------------------------------------------------- + // construct_methods: + //-------------------------------------------------------- + void ConcentrationRegulator::construct_methods() + { + AtomicRegulator::construct_methods(); + + if (atc_->reset_methods()) { + // eliminate existing methods + delete_method(); + // consruct new ones + map::iterator itr; + for (itr = parameters_.begin(); + itr != parameters_.end(); itr++) { + string tag = itr->first; + if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag]; + ConcentrationRegulatorParameters & p = itr->second; + switch (p.method) { + case NONE: { + regulators_[tag] = new ConcentrationRegulatorMethod(this); + break; + } + case TRANSITION: { + p.type = atc_->tag_to_type(tag); + p.groupbit = LammpsInterface::instance()->type_to_groupbit(p.type); + p.transitionType = atc_->tag_to_type(p.transitionTag); + regulators_[tag] = new ConcentrationRegulatorMethodTransition(this,p); + break; + } + default: + throw ATC_Error("ConcentrationRegulator::initialize unknown concentration regulator type"); + } + } + } + } + //-------------------------------------------------------- + // initialize: + //-------------------------------------------------------- + void ConcentrationRegulator::initialize() + { + + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->initialize(); } + + atc_->set_boundary_integration_type(boundaryIntegrationType_); + AtomicRegulator::reset_nlocal(); + AtomicRegulator::delete_unused_data(); + needReset_ = false; + } + + //-------------------------------------------------------- + // pre_exchange + //-------------------------------------------------------- + void ConcentrationRegulator::pre_exchange() + { + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->pre_exchange();} + } + + //-------------------------------------------------------- + // pre_force + //-------------------------------------------------------- + void ConcentrationRegulator::pre_force() + { + map::iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->pre_force();} + } + + //-------------------------------------------------------- + // output + //-------------------------------------------------------- + void ConcentrationRegulator::output(OUTPUT_LIST & outputData) const + { + map::const_iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { itr->second->output(outputData);} + } + + //-------------------------------------------------------- + // compute vector + //-------------------------------------------------------- + double ConcentrationRegulator::compute_vector(int n) const + { + int s = regulators_.size(); + if (s == 0) return 0; + int m = n / s; + n = n % s; + int c = 0; + + map::const_iterator itr; + for (itr = regulators_.begin(); + itr != regulators_.end(); itr++) { + if (c++ == n) { return itr->second->compute_vector(m); } + } + return 0.; + + } + //-------------------------------------------------------- + // size vector + //-------------------------------------------------------- + int ConcentrationRegulator::size_vector(int i) const + { + int n = (regulators_.size())*5; + if (n==0) n = 20; + return n; + } + + //======================================================== + // Class ConcentrationRegulatorMethodTransition + //======================================================== + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and ConcentrationRegulator + //-------------------------------------------------------- + ConcentrationRegulatorMethodTransition::ConcentrationRegulatorMethodTransition + (ConcentrationRegulator *concReg, + ConcentrationRegulator::ConcentrationRegulatorParameters & p) + : ConcentrationRegulatorMethod(concReg), + concentrationRegulator_(concReg), + interscaleManager_(NULL), + lammpsInterface_(LammpsInterface::instance()), + list_(NULL), + targetConcentration_(p.value), + targetCount_(0), + elemset_(p.elemset), + p_(NULL), + randomNumberGenerator_(NULL), + q0_(0), + controlType_(p.type), + controlIndex_(0), + transitionType_(p.transitionType), + transitionInterval_(p.transitionInterval), + transitionCounter_(0), + nInTransition_(0), + transitionFactor_(0), + controlMask_(p.groupbit), + frequency_(p.frequency), + maxEnergy_(p.maxEnergy), + maxExchanges_(p.maxExchanges), + maxAttempts_(p.maxAttempts), + nexchanges_(0), + initialized_(false), + _rngUniformCounter_(0), + _rngNormalCounter_(0) + { + controlIndex_ = atc_->type_index(controlType_); + LammpsInterface * li = LammpsInterface::instance(); + q0_ = li->type_to_charge(controlType_); + double kB = (li->boltz())/(li->mvv2e()); // E/T*m*v^2/E = m v^2/T + double m = li->atom_mass(controlType_); + sigma_ = sqrt(kB/m); // v / sqrt(T) + randomNumberGenerator_ = li->random_number_generator(); + } + + //-------------------------------------------------------- + // Initialize + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::initialize(void) + { +#ifdef ATC_VERBOSE + lammpsInterface_->print_msg_once( + "\ncontrol type: "+to_string(controlType_)+ + "\ntransistion type:"+to_string(transitionType_)+ + "\ncontrol mask:"+to_string(controlMask_)+ + "\nfrequency:"+to_string(frequency_)+ + "\nmax exchanges:"+to_string(maxExchanges_)+ + "\nmax attempts:"+to_string(maxAttempts_)+ + "\nmax energy:"+to_string(maxEnergy_) + ); +#endif + interscaleManager_ = &(atc_->interscale_manager()); + + PerAtomQuantity * a2el = atc_->atom_to_element_map(); + list_ = new AtomInElementSet(atc_,a2el,elemset_,controlType_); + + nNodes_ = atc_->num_nodes(); + DENS_MAT conc(nNodes_,1); conc = targetConcentration_; + DENS_VEC integral = atc_->fe_engine()->integrate(conc,elemset_); + targetCount_ = rnd(integral(0)) ; + + volumes_.resize(elemset_.size()); + ESET::const_iterator itr; + int i = 0; + DENS_MAT c(nNodes_,1); c = 1; + V_ = 0.; + for (itr = elemset_.begin(); itr != elemset_.end(); itr++, i++) { + ESET e; e.insert(*itr); + DENS_VEC v = atc_->fe_engine()->integrate(c,e); + volumes_(i) = v(0); + V_ += v(0); + } + volumes_ *= 1./V_; + for (int i = 1; i < volumes_.size(); i++) { + volumes_(i) += volumes_(i-1); + } + + // record orginal energetic properties + int ntypes = lammpsInterface_->ntypes(); + epsilon0_.reset(ntypes); + p_ = lammpsInterface_->potential(); + lammpsInterface_->epsilons(controlType_,p_,epsilon0_.ptr()); + +#ifdef ATC_VERBOSE + string msg = "type "+to_string(controlType_)+" target count " + to_string(targetCount_); + msg += " volume " + to_string(V_); + msg += " current count " + to_string(count()); + ATC::LammpsInterface::instance()->print_msg_once(msg); + msg = "WARNING: ensure neighboring happens at least every "+to_string(frequency_); + ATC::LammpsInterface::instance()->print_msg_once(msg); +#endif + } + double ConcentrationRegulatorMethodTransition::uniform() const { + _rngUniformCounter_++; + return lammpsInterface_->random_uniform(randomNumberGenerator_); + } + double ConcentrationRegulatorMethodTransition::normal() const { + _rngNormalCounter_++; + return lammpsInterface_->random_normal(randomNumberGenerator_); + } + //-------------------------------------------------------- + // pre exchange + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::pre_exchange(void) + { + // return if should not be called on this timestep + if ( ! lammpsInterface_->now(frequency_)) return; + nexchanges_ = excess(); + int n = abs(nexchanges_); + bool success = false; + if (nexchanges_ > 0) { success = delete_atoms(n); } + else if (nexchanges_ < 0) { success = insert_atoms(n); } + else return; + if (!success) throw ATC_Error("insertions/deletions did not succeed"); + + if (nexchanges_ !=0) { + nInTransition_ = -nexchanges_; + lammpsInterface_->reset_ghosts(-nexchanges_); + atc_->reset_atoms(); + } + transitionCounter_=0; + transition(); + } + //-------------------------------------------------------- + // pre force + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::pre_force(void) + { + transition(); + } + //-------------------------------------------------------- + // accept + //-------------------------------------------------------- + bool ConcentrationRegulatorMethodTransition::accept(double energy, double T) const + { +#ifdef ATC_VERBOSE2 + if (energy < maxEnergy_) lammpsInterface_->print_msg(" energy "+to_string(energy)+" "+to_string(rngCounter_)); +#endif + return (energy < maxEnergy_); + } + //-------------------------------------------------------- + // energy + //-------------------------------------------------------- + double ConcentrationRegulatorMethodTransition::energy(int id) const + { + double e = lammpsInterface_->shortrange_energy(id,maxEnergy_); +#ifdef ATC_VERBOSE +{ + int * tag = lammpsInterface_->atom_tag(); + lammpsInterface_->print_msg(to_string(controlType_)+" deletion energy "+to_string(e)+" id "+to_string(tag[id])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_)); +} +#endif + return e; + } + double ConcentrationRegulatorMethodTransition::energy(double * x) const + { + double e = lammpsInterface_->shortrange_energy(x,controlType_,maxEnergy_); +#ifdef ATC_VERBOSE +{ + lammpsInterface_->print_msg(to_string(controlType_)+" insertion energy "+to_string(e)+" x "+to_string(x[0])+","+to_string(x[1])+","+to_string(x[2])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_)); +} +#endif + return e; + } + //-------------------------------------------------------- + // excess + //-------------------------------------------------------- + int ConcentrationRegulatorMethodTransition::excess(void) const + { + int nexcess = count()-targetCount_; + nexcess = max(min(nexcess,maxExchanges_),-maxExchanges_); + return nexcess; + } + //-------------------------------------------------------- + // count + //-------------------------------------------------------- + int ConcentrationRegulatorMethodTransition::count(void) const + { + // integrate concentration over region + const DENS_MAT & c = (atc_->field(SPECIES_CONCENTRATION)).quantity(); + DENS_VEC integral = atc_->fe_engine()->integrate(c,elemset_); + return rnd(integral(controlIndex_)) ; + } + //-------------------------------------------------------- + // delete atoms + //-------------------------------------------------------- + bool ConcentrationRegulatorMethodTransition::delete_atoms(int n) + { + ID_PAIR idPair; + + deletionIds_.clear(); + int deletions = 0; + int attempts = 0; + while(deletions < n && attempts < maxAttempts_){ + if(accept(deletion_id(idPair))) { + deletionIds_.push_back(idPair); + deletions += 1; + } + deletions = lammpsInterface_->int_allmax(deletions); + attempts++; + } + ID_LIST::iterator itr; + for (itr = deletionIds_.begin(); itr != deletionIds_.end(); itr++) { + lammpsInterface_->delete_atom(itr->second); + } +#ifdef ATC_VERBOSE + string c = to_string(controlType_); + lammpsInterface_->all_print(attempts, c+"-attempts "); + lammpsInterface_->all_print(deletions,c+" deletions "); + lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform "); + lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal "); +// lammpsInterface_->all_print(uniform()," RANDOM "); +#endif + return (n == deletions); // success + } + //-------------------------------------------------------- + // pick id + //-------------------------------------------------------- + double ConcentrationRegulatorMethodTransition::deletion_id(ID_PAIR & id) const + { + if (atc_->parallel_consistency()) return deletion_id_consistent(id); + else return deletion_id_free(id); + } + double ConcentrationRegulatorMethodTransition::deletion_id_consistent(ID_PAIR & id) const + { + id.first = -1; + id.second = -1; + int ntotal = lammpsInterface_->natoms(); + double r = uniform(); + r *= ntotal; + const ID_LIST & list = list_->quantity(); + ID_LIST::const_iterator itr; + int i=0, idx = -1; + double min = ntotal; + int * tag = lammpsInterface_->atom_tag(); + for (itr = list.begin(); itr != list.end(); itr++) { + int atag = tag[itr->second]; + double d = abs(atag-r); + if (d < min) { + min = d; + idx = i; + } + i++; + } + int imin = kMinScale_*min; + if(imin == lammpsInterface_->int_allmin(imin)) { + if (idx < 0) throw ATC_Error("deletion_id failed to find a suitable atom"); + id = list_->item(idx); + // avoid repeats + ID_LIST & l = list_->set_quantity(); + l.erase(l.begin()+idx); + return energy(id.second); + } + else { + return maxEnergy_; + } + } + double ConcentrationRegulatorMethodTransition::deletion_id_free(ID_PAIR & id) const + { + id.first = -1; + id.second = -1; + int n = list_->size(); + double nrank = lammpsInterface_->int_scansum(n); + int ntotal = lammpsInterface_->int_allsum(n); + if (ntotal == 0) throw ATC_Error("control type "+to_string(controlType_)+" is depleted"); + double r = uniform(); + r *= ntotal; + if ( (r >= nrank-n) && (r < nrank)) { // pick processor + + r = uniform(); + int idx = rnd(r*(n-1)); + id = list_->item(idx); + // avoid repeats + ID_LIST & l = list_->set_quantity(); + l.erase(l.begin()+idx); + return energy(id.second); + } + else { + return maxEnergy_; + } + } + //-------------------------------------------------------- + // insert atoms + //-------------------------------------------------------- + bool ConcentrationRegulatorMethodTransition::insert_atoms(int n) + { + + insertionIds_.clear(); + DENS_VEC x(3); x = 0; + DENS_VEC v(3); v = 0; + const DENS_MAN & T = atc_->field(TEMPERATURE); + int additions = 0; + int attempts = 0; + while(additions < n && attempts < maxAttempts_){ + if(accept(insertion_location(x))) { + DENS_VEC Tv = atc_->fe_engine()->interpolate_field(x,T); +Tv(0) = 300.; + pick_velocity(v,Tv(0)); // 3 normal + int nlocal = lammpsInterface_->insert_atom(transitionType_,controlMask_,x.ptr(),v.ptr()); // no charge + insertionIds_.push_back(pair(-1,nlocal)); // atc id unknown + additions += 1; +#ifdef ATC_VERBOSE2 + lammpsInterface_->print_msg(">>> insert x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+" v:"+to_string(v(0))+" "+to_string(v(1))+" "+to_string(v(2))+" "+to_string(rngCounter_)); +#endif + } + attempts++; + //lammpsInterface_->barrier(); + additions = lammpsInterface_->int_allmax(additions); +#ifdef ATC_VERBOSE +{ + string c = to_string(controlType_); + lammpsInterface_->all_print(_rngUniformCounter_,c+" rng-uniform "); + lammpsInterface_->all_print(_rngNormalCounter_,c+" rng-normal "); +// lammpsInterface_->all_print(uniform()," random "); +} +#endif + if (atc_->parallel_consistency()) { sync_random_number_generators(); } +#ifdef ATC_VERBOSE2 + lammpsInterface_->print_msg("attempts: "+to_string(attempts)+" additions "+to_string(additions)+" : "+to_string(rngCounter_)); +#endif +#ifdef ATC_VERBOSE +{ + string c = to_string(controlType_); + lammpsInterface_->all_print(attempts, c+"+attempts "); + lammpsInterface_->all_print(additions,c+" additions "); + lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform "); + lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal "); +// lammpsInterface_->all_print(uniform()," RANDOM "); +} +#endif + } + return (n == additions); // success + } + //-------------------------------------------------------- + // sync random number generators + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::sync_random_number_generators() const + { + // normal + int n = lammpsInterface_->int_allmax(_rngNormalCounter_); + int dn = n - _rngNormalCounter_; + lammpsInterface_->advance_random_normal(randomNumberGenerator_,dn); + _rngNormalCounter_ = n; + // uniform + int u = lammpsInterface_->int_allmax(_rngUniformCounter_); + int du = u - _rngUniformCounter_; + lammpsInterface_->advance_random_uniform(randomNumberGenerator_,du); + _rngUniformCounter_ = u; + } + //-------------------------------------------------------- + // pick location + //-------------------------------------------------------- + double ConcentrationRegulatorMethodTransition::insertion_location(DENS_VEC & x) const + { + // pick random element + int elem = pick_element(); // 1 uniform + // pick random local coordinate + DENS_VEC xi(3); + pick_coordinates(elem,xi,x); // 3 uniform +// if (! lammpsInterface_->in_box(x.ptr())) { throw ATC_Error("new atom is not in box");} + if (lammpsInterface_->in_my_processor_box(x.ptr())) { +#ifdef ATC_VERBOSE2 + lammpsInterface_->print_msg(">>> insertion_location e:" +to_string(elem)+" xi:"+to_string(xi(0))+" "+to_string(xi(1))+" "+to_string(xi(2))+" x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+ " energy "+to_string(energy(x.ptr()))+" "+true_false(accept(energy(x.ptr())))+" "+to_string(rngUniformCounter_)); +#endif + return energy(x.ptr()); + } + else { + return maxEnergy_; + } + } + //-------------------------------------------------------- + // pick element + //-------------------------------------------------------- + int ConcentrationRegulatorMethodTransition::pick_element() const + { + double r = uniform(); + ESET::iterator itr = elemset_.begin(); // global? + for (int i = 0; i < volumes_.size() ; ++i) { + if (r < volumes_(i)) return *itr; + itr++; + } + return *itr; + } + //-------------------------------------------------------- + // pick coordinates + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::pick_coordinates(const int elem, + DENS_VEC & xi, + DENS_VEC & x) const + { + xi.reset(3); + xi(0) = 2.*uniform()-1.; + xi(1) = 2.*uniform()-1.; + xi(2) = 2.*uniform()-1.; + atc_->fe_engine()->fe_mesh()->position(elem,xi,x); + + } + //-------------------------------------------------------- + // pick velocity + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::pick_velocity(DENS_VEC & v,double T) const + { + double s = sigma_*sqrt(T); + v(0) = s*normal(); + v(1) = s*normal(); + v(2) = s*normal(); +//v = 0; + } + //-------------------------------------------------------- + // transition + //-------------------------------------------------------- + void ConcentrationRegulatorMethodTransition::transition() + { + transitionCounter_++; + //if (insertionIds_.size() == 0) return; // + if (transitionCounter_> transitionInterval_) { + nInTransition_ = 0; + return; + } + else if (transitionCounter_==transitionInterval_) { + nInTransition_ -= lammpsInterface_->change_type(transitionType_,controlType_); + } + else { + transitionFactor_ = insertion_factor(transitionCounter_); + if (nInTransition_ < 0) transitionFactor_ = 1-transitionFactor_; + double q = 0; + lammpsInterface_->set_charge(transitionType_,q); + DENS_VEC eps = epsilon0_; + + lammpsInterface_->set_epsilons(transitionType_,p_,eps.ptr()); + lammpsInterface_->pair_reinit(); // epsilon + } + } + //-------------------------------------------------------- + // diagnostics + //-------------------------------------------------------- + double ConcentrationRegulatorMethodTransition::compute_vector(int n) const + { + if (n==0) return count() - targetCount_; + else if (n==1) return count()/V_; + else if (n==2) return (1.-transitionFactor_)*nInTransition_; + else if (n==3) return _rngUniformCounter_; + else if (n==4) return _rngNormalCounter_; + else if (n==5) return lammpsInterface_->random_state(randomNumberGenerator_); + else return 0; + } +}; // end namespace diff --git a/lib/atc/ConcentrationRegulator.h b/lib/atc/ConcentrationRegulator.h new file mode 100644 index 0000000000..147f85496a --- /dev/null +++ b/lib/atc/ConcentrationRegulator.h @@ -0,0 +1,212 @@ +#ifndef CONCENTRATION_REGULATOR_H +#define CONCENTRATION_REGULATOR_H + +// ATC headers +#include "AtomicRegulator.h" +#include "LammpsInterface.h" + + +namespace ATC { + + /** + * @class ConcentrationRegulator + * @brief Manager class for atom-continuum control of charge and potential + */ + + class ConcentrationRegulatorMethod; + class ConcentrationRegulator : public AtomicRegulator { + + public: + enum ConcentrationRegulatorType {NONE=0,TRANSITION}; + /** parser data */ + struct ConcentrationRegulatorParameters { + ConcentrationRegulatorParameters(): + method(NONE), + type(0), + groupbit(0), + transitionType(0), + value(0), + frequency(0), + transitionInterval(0), + maxEnergy(0), + maxExchanges(0), + maxAttempts(0) { }; + ConcentrationRegulatorType method; + int type; + int groupbit; + int transitionType; + double value; + int frequency; + int transitionInterval; + double maxEnergy; + int maxExchanges; + int maxAttempts; + string transitionTag; + ESET elemset; + }; + + /** constructor */ + ConcentrationRegulator(ATC_Coupling *atc); + /** destructor */ + ~ConcentrationRegulator(); + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + /** construct methods */ + virtual void construct_methods(); + /** pre time integration */ + virtual void initialize(); + + //WIP_JAT need a nicer way to consistently handle sets of regulators, not sure how yet + // application steps + /** 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){}; + /** compute the thermal boundary flux, must be consistent with regulator */ + virtual void compute_boundary_flux(FIELDS & fields){}; + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs){}; + + /** prior to exchanges */ + virtual void pre_force(); + /** prior to exchanges */ + virtual void pre_exchange(); + /** ATC output */ + virtual void output(OUTPUT_LIST & outputData) const; + /** scalar output */ + virtual double compute_vector(int n) const; + virtual int size_vector(int s) const; + + bool needs_temperature() { return regulators_.size() > 0; } + + protected: + /** registry charge regulators */ + map regulators_; + map parameters_; + + private: + ConcentrationRegulator(); // DO NOT define this + }; + + /** + * @class ConcentrationRegulatorMethod + * @brief Base class for ConcentrationRegulator algorithms + */ + + class ConcentrationRegulatorMethod : public RegulatorShapeFunction { + + public: + ConcentrationRegulatorMethod(ConcentrationRegulator *c) + : RegulatorShapeFunction(c) {}; + ~ConcentrationRegulatorMethod() {}; + void initialize(void) {}; + virtual void pre_force() {}; + virtual void pre_exchange() {}; + virtual void set_weights() {}; + virtual double compute_vector(int n) const { return 0;} + virtual void output(OUTPUT_LIST & outputData){}; + private: + ConcentrationRegulatorMethod(); // DO NOT define this + }; + + /** + * @class ConcentrationRegulatorMethodTransition + * @brief GCMC + thermodynamic integration + */ + class ConcentrationRegulatorMethodTransition : public ConcentrationRegulatorMethod { + + public: + /** constructor */ + ConcentrationRegulatorMethodTransition( + ConcentrationRegulator *concentrationRegulator, + ConcentrationRegulator::ConcentrationRegulatorParameters & parameters); + /** destructor */ + ~ConcentrationRegulatorMethodTransition(){ + if (randomNumberGenerator_) delete randomNumberGenerator_; + } + /** initialize */ + void initialize(void); + /** prior to force evaluation */ + virtual void pre_force(); + /** prior to exchanges */ + virtual void pre_exchange(); + /** "thermo" output */ + virtual double compute_vector(int n) const; + protected: + /** set transition state: epsilon and charge */ + int mask(int type, int groupbit) {return 0;} + int count(void) const; + int excess(void) const; + double energy(int id) const; + double energy(double * x) const; + double insertion_location(DENS_VEC & x) const; + double deletion_id(ID_PAIR & id) const ; + double deletion_id_consistent(ID_PAIR & id) const ; + double deletion_id_free(ID_PAIR & id) const ; + double insertion_factor(int c) const // a ramp + { + if (c < transitionInterval_) return ((double) c)/transitionInterval_; + else return 1.0; + } + void transition(); + bool accept(double energy, double T = 0) const; + bool delete_atoms(int n); + bool insert_atoms(int n); + int pick_element() const; + void pick_coordinates(const int elem, DENS_VEC & xi, DENS_VEC& x ) const; + void pick_velocity(DENS_VEC & v, const double T ) const; + double uniform() const; + double normal() const; + + /** pointer to conc regulator object for data */ + ConcentrationRegulator * concentrationRegulator_; + + /** interscale manager */ + class InterscaleManager * interscaleManager_; + + /** interscale manager */ + class LammpsInterface * lammpsInterface_; + + /** member data */ + class AtomInElementSet * list_; + double targetConcentration_; + double targetCount_; + ESET elemset_; + POTENTIAL p_; + RNG_POINTER randomNumberGenerator_; + DENS_VEC volumes_; + double V_; + double q0_; + int controlType_; + int controlIndex_; + int transitionType_; + int transitionInterval_;; + int transitionCounter_; + int nInTransition_; + double transitionFactor_; + DENS_VEC epsilon0_; + ID_LIST deletionIds_, insertionIds_; + int controlMask_; + int frequency_; + double maxEnergy_; + int maxExchanges_; + int maxAttempts_; + int nexchanges_; + bool initialized_; + double sigma_; + + void sync_random_number_generators() const; + mutable int _rngUniformCounter_; // for parallel consistency + mutable int _rngNormalCounter_; // for parallel consistency + + private: + ConcentrationRegulatorMethodTransition(); // DO NOT define this + }; +}; +#endif diff --git a/lib/atc/DenseMatrix.h b/lib/atc/DenseMatrix.h new file mode 100644 index 0000000000..bbd6e04b5f --- /dev/null +++ b/lib/atc/DenseMatrix.h @@ -0,0 +1,417 @@ +#ifndef DENSEMATRIX_H +#define DENSEMATRIX_H + +#include "Matrix.h" + +#include + +namespace ATC_matrix { + + /** + * @class DenseMatrix + * @brief Class for storing data in a "dense" matrix form + */ + +template +class DenseMatrix : public Matrix +{ +public: + DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1) { _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); } +// const SparseMatrix * p = sparse_cast(&c); +// (p) ? p->dense_copy(*this) : _copy(c); } + ~DenseMatrix() { _delete();} + + void reset (INDEX rows, INDEX cols, bool zero=true); + void resize(INDEX rows, INDEX cols, bool copy=false); + void copy (const T * ptr, INDEX rows, INDEX cols); + /** returns transpose(this) * B */ + DenseMatrix transMat(const DenseMatrix& B) const; + /** returns by element multiply A_ij = this_ij * B_ij */ + DenseMatrix mult_by_element(const DenseMatrix& B) const; + /** returns by element multiply A_ij = this_ij / B_ij */ + DenseMatrix div_by_element(const DenseMatrix& B) const; + + /** overloaded virtual functions */ + //T& operator()(INDEX i, INDEX j) { MICK(i,j) return DATA(i,j); } + T& operator()(INDEX i, INDEX j) { MICK(i,j) return DATA(i,j); } + T operator()(INDEX i, INDEX j) const { MICK(i,j) return DATA(i,j); } + T operator[](INDEX i) const { VICK(i) return _data[i]; } + T& operator[](INDEX i) { VICK(i) return _data[i]; } + INDEX nRows() const { return _nRows; } + INDEX nCols() const { return _nCols; } + T * ptr() const { return _data; } + void write_restart(FILE *f) const; + void from_file(string & name); + void set_all_elements_to(const T &v); + DiagonalMatrix diag() const; + + DenseMatrix& operator=(const T &v); + DenseMatrix& operator=(const Matrix &c); + DenseMatrix& operator=(const DenseMatrix &c); + DenseMatrix& operator=(const SparseMatrix &c); + + //* checks if all values are within the prescribed range + virtual bool check_range(T min, T max) const; + +protected: + void _delete(); + void _create(INDEX rows, INDEX cols, bool zero=false); + void _copy(const Matrix &c); + + T *_data; + INDEX _nRows, _nCols; +}; + +//! Computes the cofactor matrix of A. +template +DenseMatrix adjugate(const Matrix &A, bool symmetric=false); + +//! Returns a the tensor product of two vectors +template +DenseMatrix tensor_product(const Vector &a, const Vector &b); + +//---------------------------------------------------------------------------- +// Returns an identity matrix, defaults to 3x3. +//---------------------------------------------------------------------------- +template +DenseMatrix eye(INDEX rows=3, INDEX cols=3) +{ + const double dij[] = {0.0, 1.0}; + DENS_MAT I(rows, cols, false); // do not need to pre-zero + for (INDEX j=0; j +void DenseMatrix::reset(INDEX rows, INDEX cols, bool zero) +{ + if (!this->is_size(rows, cols)) + { + _delete(); + _create(rows, cols); + } + if (zero) this->zero(); +} +//---------------------------------------------------------------------------- +// resizes the matrix and optionally copies over what still fits +//---------------------------------------------------------------------------- +template +void DenseMatrix::resize(INDEX rows, INDEX cols, bool copy) +{ + if (this->is_size(rows, cols)) return; // if is correct size, done + if (!copy) + { + _delete(); + _create(rows, cols); + return; + } + DenseMatrix temp(*this); + _delete(); + _create(rows, cols); + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + (*this)(i,j) = temp.in_range(i,j) ? temp(i,j) : T(0); +} +//---------------------------------------------------------------------------- +// resizes the matrix and copies data +//---------------------------------------------------------------------------- +template +void DenseMatrix::copy(const T * ptr, INDEX rows, INDEX cols) +{ + resize(rows, cols, false); + memcpy(_data, ptr, this->size()*sizeof(T)); +} +//---------------------------------------------------------------------------- +// returns transpose(this) * B +//---------------------------------------------------------------------------- +template +DenseMatrix DenseMatrix::transMat(const DenseMatrix& B) const +{ + DenseMatrix C; + MultAB(*this, B, C, true); + return C; +} +//---------------------------------------------------------------------------- +// returns this_ij * B_ij +//---------------------------------------------------------------------------- +template +DenseMatrix DenseMatrix::mult_by_element(const DenseMatrix& B) const +{ + DenseMatrix C; + C.reset(_nRows,_nCols); + if (B.nCols() == _nCols) { + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + C(i,j) = (*this)(i,j)*B(i,j); + } + else if (B.nCols() == 1) { + cout << "MULTIPLYING\n"; + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + C(i,j) = (*this)(i,j)*B(i,0); + } + else { + SSCK(B, *this, "DenseMatrix::mult_by_element"); + } + return C; +} +//---------------------------------------------------------------------------- +// returns this_ij / B_ij +//---------------------------------------------------------------------------- +template +DenseMatrix DenseMatrix::div_by_element(const DenseMatrix& B) const +{ + DenseMatrix C; + C.reset(_nRows,_nCols); + + if (B.nCols() == _nCols) { + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + C(i,j) = (*this)(i,j)/B(i,j); + } + else if (B.nCols() == 1) { + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + C(i,j) = (*this)(i,j)/B(i,0); + } + else { + SSCK(B, *this, "DenseMatrix::div_by_element"); + } + return C; +} +//---------------------------------------------------------------------------- +// writes the matrix data to a file +//---------------------------------------------------------------------------- +template +void DenseMatrix::write_restart(FILE *f) const +{ + fwrite(&_nRows, sizeof(INDEX),1,f); + fwrite(&_nCols, sizeof(INDEX),1,f); + if (this->size()) fwrite(_data, sizeof(T), this->size(), f); +} +//---------------------------------------------------------------------------- +// reads matrix from text file (matrix needs to be sized) +//---------------------------------------------------------------------------- +template +void DenseMatrix::from_file(string & name) +{ + GCHK(_nRows == 0,"from_file needs nRows > 0"); + GCHK(_nCols == 0,"from_file needs nCols > 0"); + std::ifstream in(name.c_str(),std::ifstream::in); + const int lineSize = 256; + char line[lineSize]; + if (! in.good()) gerror(name+" is not available"); + in.getline(line,lineSize); // header + int szi = this->nRows(); + int szj = this->nCols(); + for (INDEX i = 0; i < szi; i++) + for (INDEX j = 0; j < szj; j++) + in >> (*this)(i,j); +} +//---------------------------------------------------------------------------- +// sets all elements to a value (optimized) +//---------------------------------------------------------------------------- +template +inline void DenseMatrix::set_all_elements_to(const T &v) +{ + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) _data[i] = v; +} +//----------------------------------------------------------------------------- +// Return a diagonal matrix containing the diagonal entries of this matrix +//----------------------------------------------------------------------------- +template +DiagonalMatrix DenseMatrix::diag() const +{ + DiagonalMatrix D(nRows(), true); // initialized to zero + INDEX i; + for (i=0; i +void DenseMatrix::_delete() +{ + _nRows = _nCols = 0; + if (_data){ + delete [] _data; + } +} +//---------------------------------------------------------------------------- +// allocates memory for an rows by cols DenseMatrix +//---------------------------------------------------------------------------- +template +void DenseMatrix::_create(INDEX rows, INDEX cols, bool zero) +{ + + _nRows=rows; + _nCols=cols; + _data = (this->size() ? new T [_nCols*_nRows] : NULL); + if (zero) this->zero(); +} +//---------------------------------------------------------------------------- +// creates a deep memory copy from a general matrix +//---------------------------------------------------------------------------- +template +void DenseMatrix::_copy(const Matrix &c) +{ + if (!_data || this->size()!=c.size()) + { + _delete(); + _create(c.nRows(), c.nCols()); + } + else + { + _nRows = c.nRows(); + _nCols = c.nCols(); + } + memcpy(_data, c.ptr(), c.size()*sizeof(T)); +} +//---------------------------------------------------------------------------- +// sets all elements to a constant +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const T &v) +{ + this->set_all_elements_to(v); + return *this; +} +//---------------------------------------------------------------------------- +// copys c with a deep copy +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const Matrix &c) +{ + _copy(c); + return *this; +} +//---------------------------------------------------------------------------- +// copys c with a deep copy +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const DenseMatrix &c) +{ + _copy(c); + return *this; +} +//----------------------------------------------------------------------------- +// copys c with a deep copy, including zeros +//----------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const SparseMatrix &c) +{ + _delete(); + _create(c.nRows(), c.nCols(), true); + SparseMatrix::compress(c); + for (INDEX i=0; i x = c.triplet(i); + cout << "x.i: "<< x.i << "\nx.j: "<< x.j << "\nv.j: "<< x.v << std::endl << std::endl; + (*this)(x.i, x.j) = x.v; + } + return *this; +} + +//* Returns the transpose of the cofactor matrix of A. +//* see http://en.wikipedia.org/wiki/Adjugate_matrix +//* symmetric flag only affects cases N>3 +template +DenseMatrix adjugate(const Matrix &A, bool symmetric) +{ + if (!A.is_square()) gerror("adjugate can only be computed for square matrices."); + DenseMatrix C(A.nRows(), A.nRows()); + switch (A.nRows()) { + case 1: + gerror("adjugate must be computed for matrixes of size greater than 1"); + case 2: + C(0,0) = A(1,1); C(0,1) =-A(0,1); + C(1,0) =-A(1,0); C(1,1) = A(0,0); + break; + case 3: // 3x3 case was tested vs matlab + C(0,0) = A(1,1)*A(2,2)-A(1,2)*A(2,1); + C(1,0) =-A(1,0)*A(2,2)+A(1,2)*A(2,0); // i+j is odd (reverse sign) + C(2,0) = A(1,0)*A(2,1)-A(1,1)*A(2,0); + C(0,1) =-A(0,1)*A(2,2)+A(0,2)*A(2,1); // i+j is odd + C(1,1) = A(0,0)*A(2,2)-A(0,2)*A(2,0); + C(2,1) =-A(0,0)*A(2,1)+A(0,1)*A(2,0); // i+j is odd + C(0,2) = A(0,1)*A(1,2)-A(0,2)*A(1,1); + C(1,2) =-A(0,0)*A(1,2)+A(0,2)*A(1,0); // i+j is odd + C(2,2) = A(0,0)*A(1,1)-A(0,1)*A(1,0); + break; + default: + + // this feature is neither tested nor optimal - use at your own risk!!! + DenseMatrix m(A.nRows()-1, A.nRows()-1); + double sign[] = {1.0, -1.0}; + for (INDEX j=0; j=i), mj+(mj>=j)); // skip row i and col j + } + } + if (!symmetric) C(j,i)=det(m)*sign[(i+j)&1]; + if (symmetric && i>=j) C(i,j)=C(j,i)=det(m)*sign[(i+j)&1]; + } + } + } + return C; +} + +// Returns a the tensor product of two vectors +template +DenseMatrix tensor_product(const Vector &a, const Vector &b) +{ + DenseMatrix ab(a.size(), b.size(),false); + for (INDEX j=0; j +DenseMatrix rand(INDEX rows, INDEX cols, int seed=1234) +{ + srand(seed); + const double rand_max_inv = 1.0 / double(RAND_MAX); + DenseMatrix R(rows, cols, false); + for (INDEX i=0; i +inline bool DenseMatrix::check_range(T min, T max) const +{ + for (INDEX i = 0; i < this->size(); i++) + if ( (_data[i] > max) || (_data[i] < min) ) return false; + return true; +} + +} // end namespace +#endif + diff --git a/lib/atc/DenseVector.h b/lib/atc/DenseVector.h new file mode 100644 index 0000000000..77d06799f0 --- /dev/null +++ b/lib/atc/DenseVector.h @@ -0,0 +1,168 @@ +#ifndef DENSEVECTOR_H +#define DENSEVECTOR_H + +#include "Vector.h" + +namespace ATC_matrix { + +template + + /** + * @class DenseVector + * @brief Class for storing data in a "dense" vector form + */ + +class DenseVector : public Vector +{ +public: + explicit DenseVector(INDEX n=0, bool z=1) { _create(n,z); } + DenseVector(const DenseVector &c) : _data(NULL) { _copy(c); } + DenseVector(const Vector &c) : _data(NULL) { _copy(c); } + DenseVector(const T * ptr, INDEX nrows) : _data(NULL) { copy(ptr,nrows); } + virtual ~DenseVector() { _delete(); } + + //* resizes the Vector, ignores nCols, optionally copys what fits + void resize(INDEX rows, INDEX cols=1, bool copy=false); + //* resizes the Vector, ignores nCols, optionally zeros it out + void reset (INDEX rows, INDEX cols=1, bool zero=true); + //* resizes the Vector and copies data, ignores nCols + void copy(const T * ptr, INDEX rows, INDEX cols=1); + + // overloaded inline virtual functions + T operator[](INDEX i) const { VICK(i) return _data[i]; } + T& operator[](INDEX i) { VICK(i) return _data[i]; } + T operator()(INDEX i, INDEX j=0) const { VICK(i) return _data[i]; } + T& operator()(INDEX i, INDEX j=0) { VICK(i) return _data[i]; } + void set_all_elements_to(const T &v) { + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) _data[i] = v; + } + INDEX nRows() const { return _size; } + + T* ptr() const { return _data; } + + DenseVector& operator=(const T &v); + DenseVector& operator=(const Vector &c); + DenseVector& operator=(const DenseVector &c); + + void write_restart(FILE *f) const; + +private: + void _delete(); + void _create(INDEX n, bool zero=0); + void _copy(const Vector &c); + + T *_data; + INDEX _size; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Template definitions /////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// resizes the matrix and optionally copies over what still fits, ignores cols +//----------------------------------------------------------------------------- +template +void DenseVector::resize(INDEX rows, INDEX cols, bool copy) +{ + if (_size==rows) return; // if is correct size, done + if (!copy) + { + _delete(); + _create(rows); + return; + } + DenseVector temp(*this); + _delete(); + _create(rows); + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) + _data[i] = i +void DenseVector::reset(INDEX rows, INDEX cols, bool zero) +{ + if (_size!=rows) + { + _delete(); + _create(rows); + } + if (zero) this->zero(); +} +/////////////////////////////////////////////////////////////////////////////// +//* resizes the matrix and optionally zeros it out +template +void DenseVector::copy(const T * ptr, INDEX rows, INDEX cols) +{ + resize(rows, 1, false); + memcpy(_data, ptr, this->size()*sizeof(T)); +} +/////////////////////////////////////////////////////////////////////////////// +//* writes the matrix data to a file +template +void DenseVector::write_restart(FILE *f) const +{ + fwrite(&_size, sizeof(INDEX),1,f); + if(_size) fwrite(_data, sizeof(T), _size, f); +} +/////////////////////////////////////////////////////////////////////////////// +//* clears allocated memory +template +inline void DenseVector::_delete() +{ + if (_data) delete [] _data; + _size = 0; +} +/////////////////////////////////////////////////////////////////////////////// +//* allocates memory for an rows by cols DenseMatrix +template +inline void DenseVector::_create(INDEX n, bool zero) +{ + _size=n; + _data = _size ? new T [_size] : NULL ; + if (zero) this->zero(); +} +/////////////////////////////////////////////////////////////////////////////// +//* creates a deep memory copy from a general matrix +template +inline void DenseVector::_copy(const Vector &c) +{ + if (!_data || _size!=c.size()) + { + _delete(); + _create(c.size(), false); + } + else _size = c.size(); + memcpy(_data, c.ptr(), _size*sizeof(T)); +} +/////////////////////////////////////////////////////////////////////////////// +//* assigns v to all values in the vector +template +DenseVector& DenseVector::operator=(const T &v) +{ + int sz = this->size(); + for (INDEX i = 0; i < sz; i++) (*this)[i] = v; + return *this; +} +/////////////////////////////////////////////////////////////////////////////// +//* copys c with a deep copy +template +DenseVector& DenseVector::operator=(const Vector &c) +{ + _copy(c); + return *this; +} +/////////////////////////////////////////////////////////////////////////////// +//* copys c with a deep copy +template +DenseVector& DenseVector::operator=(const DenseVector &c) +{ + _copy(c); + return *this; +} + +} // end namespace + +#endif diff --git a/lib/atc/DependencyManager.h b/lib/atc/DependencyManager.h new file mode 100644 index 0000000000..9cadf829b1 --- /dev/null +++ b/lib/atc/DependencyManager.h @@ -0,0 +1,328 @@ +// A class for wrapping matrix operations with dependency information to speed up execution + +#ifndef DEPENDENCY_MANAGER_H +#define DEPENDENCY_MANAGER_H + +#include "ATC_TypeDefs.h" +#include "MatrixLibrary.h" +#include "ATC_Error.h" +#include "MPI_Wrappers.h" + +using namespace MPI_Wrappers; + +namespace ATC { + + class InterscaleManager; + + /** memory type */ + enum MemoryType + { + TEMPORARY = 0, + PERSISTENT + }; + + /** + * @class DependencyManager + * @brief Base class for defining objects that manage the dependencies of various objects + */ + + class DependencyManager { + + public: + + // used as a friend so it can perform a depth-first search to have safe deletions of managed dependencies + friend class InterscaleManager; + + // constructor + DependencyManager() : needReset_(true), isFixed_(false), memoryType_(TEMPORARY), dfsFound_(false) {}; + + // destructor + virtual ~DependencyManager() {}; + + /** registration by other PerAtomQuantity objects */ + void register_dependence(DependencyManager * dependentQuantity) + {dependentQuantities_.insert(dependentQuantity);}; + + /** removes dependencies from the set */ + void remove_dependence(DependencyManager * dependentQuantity) + {dependentQuantities_.erase(dependentQuantity);}; + + /** check if a reset is required */ + bool need_reset() const {return needReset_ && !isFixed_;}; + + /** propagate need to reset to to dependencies */ + void propagate_reset() + { + if (!isFixed_) { + set::iterator it; + for (it = dependentQuantities_.begin(); it != dependentQuantities_.end(); it++) + (*it)->force_reset(); + } + }; + + /** actions associated with indicating this quantity requires a reset */ + void set_reset() + { + needReset_ = true; + } + + /** flip this object to needing a reset, and get dependencies */ + void force_reset() + { + set_reset(); + propagate_reset(); + } + + /** force quantity to be held fixed, enables dependent quantity to be used as persistent storage */ + void fix_quantity() {isFixed_ = true;}; + + /** unfix the quantity */ + void unfix_quantity() + { + if (isFixed_) { + isFixed_ = false; + if (needReset_) propagate_reset(); + } + }; + + /** check on the memory type of the quantity */ + MemoryType memory_type() const {return memoryType_;}; + + /** set the memory type of the quantity */ + void set_memory_type(MemoryType memoryType) {memoryType_ = memoryType;}; + + + protected: + + /** list of dependent atomic quantities */ + set dependentQuantities_; + + /** flag for needing a recent */ + // mutable is applied because there can be internal updates because we update when needed rather than when pushed + mutable bool needReset_; + + /** flag for if quantity is being held fixed */ + bool isFixed_; + + /** flag for if the quantity is temporary (per-run) */ + MemoryType memoryType_; + + /** flag for if the node has been found in depth-first search */ + bool dfsFound_; + + }; + + /** + * @class MatrixDependencyManager + * @brief Class for defining objects that manage the dependencies of matrices + */ + + // Matrix class T, underlying type U + template