diff --git a/lib/atc/ATC_Coupling.cpp b/lib/atc/ATC_Coupling.cpp index b60063883..b02158418 100644 --- a/lib/atc/ATC_Coupling.cpp +++ b/lib/atc/ATC_Coupling.cpp @@ -1,2160 +1,2358 @@ // 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" #include "FieldManager.h" using std::string; using std::map; using std::pair; using std::set; +using std::ifstream; +using std::stringstream; +using ATC_Utility::is_numeric; +using ATC_Utility::to_string; namespace ATC { //-------------------------------------------------- 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), elementMaskMass_(NULL), elementMaskMassMd_(NULL), nodalAtomicMass_(NULL), nodalAtomicCount_(NULL), nodalAtomicHeatCapacity_(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; // default: perform velocity verlet integrateInternalAtoms_ = 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> <nodeset> <constant | function> - <field> = field name valid for type of physics, temperature | electron_temperature - <nodeset> = name of set of nodes to apply initial condition - <constant | function> = value or name of function followed by its parameters \section examples <TT> fix_modify atc initial temperature groupNAME 10. </TT> \section description Sets the initial values for the specified field at the specified nodes. \section restrictions keyword 'all' reserved in nodeset name \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> <nodeset> <constant | function> + fix_modify AtC fix <field> <nodeset> <constant | function | > - <field> = field name valid for type of physics - <nodeset> = name of set of nodes to apply boundary condition - - <constant | function> = value or name of function followed by its - parameters + - <constant | function | > = value or name of function followed by its + parameters or nothing to fix the field at its current state \section examples <TT> fix_modify AtC fix temperature groupNAME 10. </TT> \n <TT> fix_modify AtC fix temperature groupNAME 0 0 0 10.0 0 0 1.0 </TT> \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; // fix current value if (narg == argIdx) { set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); set<int>::const_iterator iset; const DENS_MAT & field =(fields_.find(thisField)->second).quantity(); for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { int inode = *iset; double v = field(inode,thisIndex); f = XT_Function_Mgr::instance()->constant_function(v); set<int> one; one.insert(inode); prescribedDataMgr_->fix_field(one,thisField,thisIndex,f); } } - // parse constant + // parse constant or file else if (narg == argIdx+1) { - f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); - prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + string a(arg[argIdx]); + if (is_numeric(a)) { // constant + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + } + else { + ATC::LammpsInterface::instance()->print_msg("reading "+field_to_string(thisField)+" on nodeset "+nsetName+" from file "+a); + string s = ATC::LammpsInterface::instance()->read_file(a); + stringstream ss; ss << s; + double v; + set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set<int>::const_iterator iset; + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + int i; + ss >> i >> v; + if (i != inode) ATC::LammpsInterface::instance()->print_msg_once("WARNING: node mismatch in file read"); + f = XT_Function_Mgr::instance()->constant_function(v); + set<int> one; one.insert(inode); + prescribedDataMgr_->fix_field(one,thisField,thisIndex,f); + } + } } // 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> <nodeset> - <field> = field name valid for type of physics - <nodeset> = name of set of nodes \section examples <TT> fix_modify AtC unfix temperature groupNAME </TT> \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> <element_set> <value | function> - <field> = field name valid for type of physics - <element_set> = name of set of elements \section examples <TT> fix_modify atc source temperature middle temporal_ramp 10. 0. </TT> \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])); + string a(arg[argIdx]); + if (is_numeric(a)) { // constant + f = XT_Function_Mgr::instance()->constant_function(atof(arg[argIdx])); + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); + } + else { + ATC::LammpsInterface::instance()->print_msg("reading "+field_to_string(thisField)+" source on node set "+esetName+" from file "+a); + string s = ATC::LammpsInterface::instance()->read_file(arg[argIdx]); + stringstream ss; ss << s; + double v; + set<int> nset = (feEngine_->fe_mesh())->nodeset(esetName); + set< pair < int, double > > src; + set<int>::const_iterator iset; + double sum = 0.; + for (iset = nset.begin(); iset != nset.end(); iset++) { + int inode = *iset; + int i; + ss >> i >> v; + if (i != inode) ATC::LammpsInterface::instance()->print_msg_once("WARNING: node mismatch in file read"); + src.insert(pair<int,double> (inode,v)); + sum += v; + } + if (ss.gcount()) ATC::LammpsInterface::instance()->print_msg_once("WARNING: not all of file read"); + ATC::LammpsInterface::instance()->print_msg_once("total source: "+to_string(sum)); + prescribedDataMgr_->fix_source(thisField,thisIndex,src); + } } // parse function else { f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); } - prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); fieldMask_(thisField,PRESCRIBED_SOURCE) = true; match = true; } /*! \page man_remove_source fix_modify AtC remove_source \section syntax fix_modify AtC remove_source <field> <element_set> - <field> = field name valid for type of physics - <element_set> = name of set of elements \section examples <TT> fix_modify atc remove_source temperature groupNAME </TT> \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; } + else if (strcmp(arg[argIdx],"write_source")==0) { + argIdx++; + FieldName thisField; + int thisIndex; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + string filename(arg[argIdx++]); + set_sources(); + FIELDS * s = & sources_; // PRESCRIBED_SOURCES + if (argIdx < narg && strcmp(arg[argIdx],"extrinsic")==0) s = & extrinsicSources_; +//s = & extrinsicSources_; + stringstream f; + set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set<int>::const_iterator iset; + const DENS_MAT & source =(s->find(thisField)->second).quantity(); + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + double v = source(inode,thisIndex); + f << inode << " " << std::setprecision(17) << v << "\n"; + } + LammpsInterface::instance()->write_file(filename,f.str()); + match = true; + } + /*! \page man_fix_flux fix_modify AtC fix_flux \section syntax fix_modify AtC fix_flux <field> <face_set> <value | function> - <field> = field name valid for type of physics, temperature | electron_temperature - <face_set> = name of set of element faces \section examples <TT> fix_modify atc fix_flux temperature faceSet 10.0 </TT> \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> <face_set> <value | function> - <field> = field name valid for type of physics, temperature | electron_temperature - <face_set> = name of set of element faces \section examples <TT> fix_modify atc unfix_flux temperature faceSet </TT> \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 <faceset | interpolate | no_boundary> [args] \section examples <TT> fix_modify atc fe_md_boundary interpolate </TT> \n \section description Specifies different methods for computing fluxes between between the MD and FE integration regions. Faceset defines a faceset separating the MD and FE regions and uses finite element face quadrature to compute the flux. Interpolate uses a reconstruction scheme to approximate the flux, which is more robust but less accurate if the MD/FE boundary does correspond to a faceset. No boundary results in no fluxes between the systems being computed. \section restrictions If faceset is used, all the AtC non-boundary atoms must lie within and completely fill the domain enclosed by the faceset. \section related see \man_boundary_faceset for how to specify the faceset name. \section default Interpolate. */ 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 <is | add> [args] \section examples fix_modify AtC boundary_faceset is obndy \section description This command species the faceset name when using a faceset to compute the MD/FE boundary fluxes. The faceset must already exist. \section restrictions This is only valid when fe_md_boundary is set to faceset. \section related \man_fe_md_boundary \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 <on | off> [region] \section examples <TT> fix_modify atc internal_quadrature off </TT> \section description Command to 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; } + + else if (strcmp(arg[argIdx],"fix_open")==0) { + argIdx++; + parse_field(arg,argIdx,thisField); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->fix_open(fsetName,thisField); + fieldMask_(thisField,OPEN_SOURCE) = true; + match = true; + } + else if (strcmp(arg[argIdx],"unfix_open")==0) { + argIdx++; + parse_field(arg,argIdx,thisField); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_open(fsetName,thisField); + fieldMask_(thisField,OPEN_SOURCE) = false; + match = true; + } + /*! \page man_atomic_charge fix_modify AtC atomic_charge \section syntax fix_modify AtC <include | omit> atomic_charge - <include | omit> = switch to activiate/deactiviate inclusion of intrinsic atomic charge in ATC \section examples <TT> fix_modify atc compute include atomic_charge </TT> \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 \section syntax fix_modify AtC source_integration < fe | atom> \section examples <TT> fix_modify atc source_integration atom </TT> \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 \section syntax fix_modify AtC consistent_fe_initialization <on | off> - <on|off> = switch to activiate/deactiviate the intial setting of FE intrinsic field to match the projected MD field \section examples <TT> fix_modify atc consistent_fe_initialization on </TT> \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 <on|off> \section examples <TT> fix_modify atc equilibrium_start on </TT> \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 \section syntax fix_modify AtC mass_matrix <fe | md_fe> - <fe | md_fe> = activiate/deactiviate using the FE mass matrix in the MD region \section examples <TT> fix_modify atc mass_matrix fe </TT> \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 <TT> fix_modify AtC material gap_region 2</TT> \section description Sets the material model in elementset_name to be of type material_id. \section restrictions The element set must already be created and the material must be specified in the material file given the the atc fix on construction \section related \section default All elements default to the first material in the material file. */ else if (strcmp(arg[argIdx],"material")==0) { argIdx++; string elemsetName(arg[argIdx++]); int matId = physicsModel_->material_index(arg[argIdx++]); using std::set; set<int> elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); if(elementToMaterialMap_.size() == 0) { throw ATC_Error("need mesh before material command"); } // set elementToMaterialMap set<int>::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"); } } //-------------------------------------------------------- // construct_methods // have managers instantiate requested algorithms // and methods //-------------------------------------------------------- void ATC_Coupling::construct_methods() { ATC_Method::construct_methods(); // construct needed time filters for mass matrices if (timeFilterManager_.need_reset()) { init_filter(); map<FieldName,int>::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); } } for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { (_tiIt_->second)->construct_methods(); } atomicRegulator_->construct_methods(); } //------------------------------------------------------------------- void ATC_Coupling::init_filter() { if (timeFilterManager_.need_reset()) { map<FieldName,int>::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<FieldName,int>::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<FieldName,int>::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_); - + prescribedDataMgr_->set_sources(time(),sources_); // PRESCRIBED_SOURCE + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); // EXTRINSIC_SOURCE } //----------------------------------------------------------------- // 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) { if (shpFcnMask_) { feEngine_->compute_source(rhsMask, fields, physicsModel, atomMaterialGroupsMask_, atomicWeightsMask_->quantity(), shpFcnMask_->quantity(), shpFcnDerivsMask_->quantity(), atomicSources); } else { for (FIELDS::const_iterator field = fields.begin(); field != fields.end(); field++) { FieldName thisFieldName = field->first; FIELDS::const_iterator fieldItr = fields.find(thisFieldName); const DENS_MAT & f = (fieldItr->second).quantity(); atomicSources[thisFieldName].reset(f.nRows(),f.nCols()); } } } //----------------------------------------------------------------- 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::masked_atom_domain_rhs_tangent( const pair<FieldName,FieldName> row_col, const RHS_MASK & rhsMask, const FIELDS & fields, SPAR_MAT & stiffness, const PhysicsModel * physicsModel) { if (shpFcnMask_) { feEngine_->compute_tangent_matrix(rhsMask, row_col, fields, physicsModel, atomMaterialGroupsMask_, atomicWeightsMask_->quantity(), shpFcnMask_->quantity(), shpFcnDerivsMask_->quantity(),stiffness); } else { stiffness.reset(nNodes_,nNodes_); } } //----------------------------------------------------------------- void ATC_Coupling::compute_rhs_tangent( const pair<FieldName,FieldName> 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); + masked_atom_domain_rhs_tangent(row_col, + rhsMaskMD, + fields, + stiffnessAtomDomain_, + physicsModel); + stiffness += stiffnessAtomDomain_; + + } + else { feEngine_->compute_tangent_matrix(rhsMask, row_col, fields , physicsModel, elementToMaterialMap_, stiffness); + } ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); feEngine_->add_robin_tangent(rhsMask, fields, time(), robinFcn, stiffness); + OPEN_SURFACE & openFaces = *(prescribedDataMgr_->open_faces()); + feEngine_->add_open_tangent(rhsMask, fields, openFaces, stiffness); + } + //----------------------------------------------------------------- + void ATC_Coupling::tangent_matrix( + const pair<FieldName,FieldName> row_col, + const RHS_MASK & rhsMask, + const PhysicsModel * physicsModel, + SPAR_MAT & stiffness) + { + feEngine_->compute_tangent_matrix(rhsMask, row_col, + fields_ , physicsModel, elementToMaterialMap_, stiffness); } //----------------------------------------------------------------- void ATC_Coupling::compute_rhs_vector(const RHS_MASK & rhsMask, 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()); + ROBIN_SURFACE_SOURCE & robinFcn = *(prescribedDataMgr_->robin_functions()); feEngine_->add_robin_fluxes(rhsMask, fields, time(), robinFcn, rhs); + OPEN_SURFACE & openFaces = *(prescribedDataMgr_->open_faces()); + feEngine_->add_open_fluxes(rhsMask, fields, openFaces, rhs); } //----------------------------------------------------------------- void ATC_Coupling::masked_atom_domain_rhs_integral( const Array2D<bool> & rhsMask, const FIELDS & fields, FIELDS & rhs, const PhysicsModel * physicsModel) { if (shpFcnMask_) { feEngine_->compute_rhs_vector(rhsMask, fields, physicsModel, atomMaterialGroupsMask_, atomicWeightsMask_->quantity(), shpFcnMask_->quantity(), shpFcnDerivsMask_->quantity(), rhs); } else { for (FIELDS::const_iterator field = fields.begin(); field != fields.end(); field++) { FieldName thisFieldName = field->first; FIELDS::const_iterator fieldItr = fields.find(thisFieldName); const DENS_MAT & f = (fieldItr->second).quantity(); (rhs[thisFieldName].set_quantity()).reset(f.nRows(),f.nCols()); } } } //----------------------------------------------------------------- void ATC_Coupling::evaluate_rhs_integral( const Array2D<bool> & 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, + rhs, false, &(elementMask_->quantity())); masked_atom_domain_rhs_integral(rhsMask, fields, rhsAtomDomain_, physicsModel); 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) { masked_atom_domain_rhs_integral(rhsMask, fields, rhs, physicsModel); } + else if (integrationType == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE) { + RHS_MASK rhsMaskFE = rhsMask; + RHS_MASK rhsMaskMD = rhsMask; rhsMaskMD = false; + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if ( rhsMaskFE(thisFieldName,SOURCE) ) { + rhsMaskFE(thisFieldName,SOURCE) = false; + rhsMaskMD(thisFieldName,SOURCE) = true; + } + } + feEngine_->compute_rhs_vector(rhsMaskFE, + fields, + physicsModel, + elementToMaterialMap_, + rhs); + masked_atom_domain_rhs_integral(rhsMaskMD, + fields, + rhsAtomDomain_, + physicsModel); + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + + if ( ((rhs[thisFieldName].quantity()).size() > 0) + && ((rhsAtomDomain_[thisFieldName].quantity()).size() > 0) ) + rhs[thisFieldName] += rhsAtomDomain_[thisFieldName].quantity(); + } + } + else if (integrationType == FULL_DOMAIN_FREE_ONLY) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs, true); + } else { // domain == FULL_DOMAIN feEngine_->compute_rhs_vector(rhsMask, fields, physicsModel, elementToMaterialMap_, rhs); } } //-------------------------------------------------- bool ATC_Coupling::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; } //-------------------------------------------------- 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<FieldName,int>::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); // 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 // 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 thermostat power init_filter(); } // clears need for reset timeFilterManager_.initialize(); ghostManager_.initialize(); 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() && consistentInitialization_) { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); if (fieldSizes_.find(VELOCITY) != fieldSizes_.end()) { DENS_MAT & velocity(fields_[VELOCITY].set_quantity()); DENS_MAN * nodalAtomicVelocity(interscaleManager_.dense_matrix("NodalAtomicVelocity")); const DENS_MAT & atomicVelocity(nodalAtomicVelocity->quantity()); for (int i = 0; i<nNodes_; ++i) { if (nodeType(i,0)==MD_ONLY) { for (int j = 0; j < nsd_; j++) { velocity(i,j) = atomicVelocity(i,j); } } } } if (fieldSizes_.find(TEMPERATURE) != fieldSizes_.end()) { DENS_MAT & temperature(fields_[TEMPERATURE].set_quantity()); DENS_MAN * nodalAtomicTemperature(interscaleManager_.dense_matrix("NodalAtomicTemperature")); const DENS_MAT & atomicTemperature(nodalAtomicTemperature->quantity()); for (int i = 0; i<nNodes_; ++i) { if (nodeType(i,0)==MD_ONLY) { temperature(i,0) = atomicTemperature(i,0); } } } if (fieldSizes_.find(DISPLACEMENT) != fieldSizes_.end()) { DENS_MAT & displacement(fields_[DISPLACEMENT].set_quantity()); DENS_MAN * nodalAtomicDisplacement(interscaleManager_.dense_matrix("NodalAtomicDisplacement")); const DENS_MAT & atomicDisplacement(nodalAtomicDisplacement->quantity()); for (int i = 0; i<nNodes_; ++i) { if (nodeType(i,0)==MD_ONLY) { for (int j = 0; j < nsd_; j++) { displacement(i,j) = atomicDisplacement(i,j); } } } } //WIP_JAT update next two when full species time integrator is added if (fieldSizes_.find(MASS_DENSITY) != fieldSizes_.end()) { DENS_MAT & massDensity(fields_[MASS_DENSITY].set_quantity()); const DENS_MAT & atomicMassDensity(atomicFields_[MASS_DENSITY]->quantity()); for (int i = 0; i<nNodes_; ++i) { if (nodeType(i,0)==MD_ONLY) { massDensity(i,0) = atomicMassDensity(i,0); } } } if (fieldSizes_.find(SPECIES_CONCENTRATION) != fieldSizes_.end()) { DENS_MAT & speciesConcentration(fields_[SPECIES_CONCENTRATION].set_quantity()); const DENS_MAT & atomicSpeciesConcentration(atomicFields_[SPECIES_CONCENTRATION]->quantity()); for (int i = 0; i<nNodes_; ++i) { if (nodeType(i,0)==MD_ONLY) { for (int j = 0; j < atomicSpeciesConcentration.nCols(); ++j) { speciesConcentration(i,j) = atomicSpeciesConcentration(i,j); } } } } } initialized_ = true; } } //------------------------------------------------------------------- void ATC_Coupling::construct_time_integration_data() { if (!initialized_) { map<FieldName,int>::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 } } } } } //-------------------------------------------------------- // create_full_element_mask // constructs element mask which only masks out // null elements //-------------------------------------------------------- MatrixDependencyManager<DenseMatrix, bool> * ATC_Coupling::create_full_element_mask() { MatrixDependencyManager<DenseMatrix, bool> * elementMaskMan = new MatrixDependencyManager<DenseMatrix, bool>(feEngine_->num_elements(),1); DenseMatrix<bool> & elementMask(elementMaskMan->set_quantity()); elementMask = true; const set<int> & nullElements = feEngine_->null_elements(); set<int>::const_iterator iset; for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { int ielem = *iset; elementMask(ielem,0) = false; } return elementMaskMan; } //-------------------------------------------------------- // create_element_set_mask // constructs element mask based on an element set, // uses ints for MPI communication later //-------------------------------------------------------- MatrixDependencyManager<DenseMatrix, int> * ATC_Coupling::create_element_set_mask(const string & elementSetName) { MatrixDependencyManager<DenseMatrix, int> * elementMaskMan = new MatrixDependencyManager<DenseMatrix, int>(feEngine_->num_elements(),1); DenseMatrix<int> & elementMask(elementMaskMan->set_quantity()); elementMask = false; const set<int> & elementSet((feEngine_->fe_mesh())->elementset(elementSetName)); set<int>::const_iterator iset; for (iset = elementSet.begin(); iset != elementSet.end(); ++iset) { int ielem = *iset; elementMask(ielem,0) = true; } const set<int> & nullElements = feEngine_->null_elements(); for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { int ielem = *iset; elementMask(ielem,0) = false; } return elementMaskMan; } //-------------------------------------------------------- // 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(); // does element contain internal atoms if (internalElementSet_.size()) { // set up elements and maps based on prescribed element sets internalElement_ = create_element_set_mask(internalElementSet_); } else { internalElement_ = new AtomTypeElement(this,atomElement_); } interscaleManager_.add_dense_matrix_int(internalElement_, "ElementHasInternal"); if (groupbitGhost_) { atomGhostElement_ = new AtomToElementMap(this, atomGhostCoarseGrainingPositions_, GHOST); interscaleManager_.add_per_atom_int_quantity(atomGhostElement_, "AtomGhostElement"); // does element contain ghost atoms ghostElement_ = new AtomTypeElement(this,atomGhostElement_); interscaleManager_.add_dense_matrix_int(ghostElement_, "ElementHasGhost"); } // element masking for approximate right-hand side FE atomic quadrature if (atomQuadForInternal_) { elementMask_ = create_full_element_mask(); } else { if (internalElementSet_.size()) { // when geometry is based on elements, there are no mixed elements elementMask_ = new MatrixDependencyManager<DenseMatrix, bool>; - (elementMask_->set_quantity()).reset(feEngine_->num_elements(),1,false); + (elementMask_->set_quantity()).reset(feEngine_->num_elements(),1); } 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"); if (useFeMdMassMatrix_) { if (atomQuadForInternal_) { elementMaskMass_ = elementMask_; } else { elementMaskMass_ = create_full_element_mask(); interscaleManager_.add_dense_matrix_bool(elementMaskMass_, "NonNullElementMask"); } elementMaskMassMd_ = new AtomElementMask(this); interscaleManager_.add_dense_matrix_bool(elementMaskMassMd_, "InternalElementMask"); } // assign element and node types for computational geometry if (internalElementSet_.size()) { nodalGeometryType_ = new NodalGeometryTypeElementSet(this); } else { nodalGeometryType_ = new NodalGeometryType(this); } interscaleManager_.add_dense_matrix_int(nodalGeometryType_, "NodalGeometryType"); } //-------------------------------------------------------- // construct_interpolant - // constructs: interpolatn, accumulant, weights, and spatial derivatives + // constructs: interpolant, accumulant, weights, and spatial derivatives //-------------------------------------------------------- void ATC_Coupling::construct_interpolant() { // finite element shape functions for interpolants PerAtomShapeFunction * atomShapeFunctions = new PerAtomShapeFunction(this); interscaleManager_.add_per_atom_sparse_matrix(atomShapeFunctions,"Interpolant"); shpFcn_ = 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; } this->create_atom_volume(); // masked atom weights if (atomQuadForInternal_) { atomicWeightsMask_ = atomVolume_; } else { atomicWeightsMask_ = new MappedDiagonalMatrix(this, atomVolume_, internalToMask_); interscaleManager_.add_diagonal_matrix(atomicWeightsMask_, "AtomWeightsMask"); } // 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"); // shape function derivatives, masked shape function and derivatives if needed for FE quadrature in atomic domain if (atomQuadForInternal_) { shpFcnDerivs_ = new PerAtomShapeFunctionGradient(this); interscaleManager_.add_vector_sparse_matrix(shpFcnDerivs_, "InterpolantGradient"); shpFcnMask_ = shpFcn_; shpFcnDerivsMask_ = shpFcnDerivs_; } else { bool hasMaskedElt = false; const DenseMatrix<bool> & elementMask(elementMask_->quantity()); for (int i = 0; i < elementMask.size(); ++i) { if (elementMask(i,0)) { hasMaskedElt = true; break; } } if (hasMaskedElt) { shpFcnDerivs_ = new PerAtomShapeFunctionGradient(this); interscaleManager_.add_vector_sparse_matrix(shpFcnDerivs_, "InterpolantGradient"); 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"); } } } //-------------------------------------------------------- // construct_molecule_transfers //-------------------------------------------------------- void ATC_Coupling::construct_molecule_transfers() { map<string,pair<MolSize,int> >::const_iterator molecule; PerAtomQuantity<double> * 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); } } //-------------------------------------------------------- // construct_transfers // constructs needed transfer operators //-------------------------------------------------------- void ATC_Coupling::construct_transfers() { ATC_Method::construct_transfers(); if (!useFeMdMassMatrix_) { // transfer for MD mass matrices based on requested intrinsic fields if (fieldSizes_.find(TEMPERATURE) != fieldSizes_.end()) { // 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"); } if ((fieldSizes_.find(VELOCITY) != fieldSizes_.end()) || (fieldSizes_.find(DISPLACEMENT) != fieldSizes_.end())) { // 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"); } if (fieldSizes_.find(MASS_DENSITY) != fieldSizes_.end()) { // atomic dimensionless mass matrix ConstantQuantity<double> * atomicOnes = new ConstantQuantity<double>(this,1); interscaleManager_.add_per_atom_quantity(atomicOnes,"AtomicOnes"); nodalAtomicCount_ = new AtfShapeFunctionRestriction(this, atomicOnes, shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicCount_, "AtomicDimensionlessMassMat"); } } 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<FieldName> 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 if (! is_intrinsic(thisField)) { + Array<FieldName> massMask(1); + massMask(0) = thisField; + + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel, + elementToMaterialMap_,massMats_, + &(elementMask_->quantity())); + const DIAG_MAT & myMassMat(massMats_[thisField].quantity()); + DIAG_MAT & myMassMatInv(massMatsInv_[thisField].set_quantity()); + for (int iNode = 0; iNode < nNodes_; iNode++) { + + if (fabs(myMassMat(iNode,iNode))>0) + myMassMatInv(iNode,iNode) = 1./myMassMat(iNode,iNode); + else + myMassMatInv(iNode,iNode) = 0.; + } + } else { // lumped mass matrix // compute FE mass matrix in full domain Array<FieldName> massMask(1); massMask(0) = thisField; if (useFeMdMassMatrix_) { feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel, elementToMaterialMap_,massMats_, &(elementMaskMass_->quantity())); const DIAG_MAT & myMassMat(massMats_[thisField].quantity()); DIAG_MAT & myMassMatInv(massMatsInv_[thisField].set_quantity()); DIAG_MAT & myMassMatMdInv(massMatsMdInv_[thisField].set_quantity()); feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel, elementToMaterialMap_,massMatsMd_, &(elementMaskMassMd_->quantity())); const DIAG_MAT & myMassMatMd(massMatsMd_[thisField].quantity()); // 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.; if (fabs(myMassMatMd(iNode,iNode))>0) myMassMatMdInv(iNode,iNode) = 1./myMassMatMd(iNode,iNode); else myMassMatMdInv(iNode,iNode) = 0.; } } 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()); + //myMassMatFE.print("MMFE"); 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 if (shpFcnMask_) { feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel,atomMaterialGroupsMask_, atomicWeightsMask_->quantity(),shpFcnMask_->quantity(), massMatsAqInstantaneous_); } else { (massMatsAqInstantaneous_[thisField].set_quantity()).reset(nNodes_,nNodes_); } // 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.; } } //--------------------------------------------------------- // compute_md_mass_matrix // compute the mass matrix arising from only atomistic // quadrature and contributions as a summation //--------------------------------------------------------- void ATC_Coupling::compute_md_mass_matrix(FieldName thisField, DIAG_MAT & massMat) { if (thisField == TEMPERATURE) { massMat.shallowreset(nodalAtomicHeatCapacity_->quantity()); } else if (thisField == DISPLACEMENT || thisField == VELOCITY) { massMat.shallowreset(nodalAtomicMass_->quantity()); } else if (thisField == MASS_DENSITY || thisField == SPECIES_CONCENTRATION) { massMat.shallowreset(nodalAtomicVolume_->quantity()); } } //-------------------------------------------------- // write_restart_file // bundle matrices that need to be saved and call // fe_engine to write the file //-------------------------------------------------- void ATC_Coupling::write_restart_data(string fileName, RESTART_LIST & data) { atomicRegulator_->pack_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_Coupling::read_restart_data(string fileName, RESTART_LIST & data) { atomicRegulator_->pack_fields(data); ATC_Method::read_restart_data(fileName,data); } //-------------------------------------------------- void ATC_Coupling::reset_nlocal() { ATC_Method::reset_nlocal(); atomicRegulator_->reset_nlocal(); } //-------------------------------------------------------- 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); } } } atomicRegulator_->reset_atom_materials(elementToMaterialMap_, atomElement_); } //-------------------------------------------------------- // pre_init_integrate // time integration before the lammps atomic // integration of the Verlet step 1 //-------------------------------------------------------- void ATC_Coupling::pre_init_integrate() { ATC_Method::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 fields and time derivatives for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { (_tiIt_->second)->pre_initial_integrate2(dt); } extrinsicModelManager_.pre_init_integrate(); } //-------------------------------------------------------- // init_integrate // time integration of lammps atomic quantities //-------------------------------------------------------- void ATC_Coupling::init_integrate() { atomTimeIntegrator_->init_integrate_velocity(dt()); ghostManager_.init_integrate_velocity(dt()); // account for other fixes doing time integration interscaleManager_.fundamental_force_reset(LammpsInterface::ATOM_VELOCITY); // apply constraints to velocity atomicRegulator_->apply_mid_predictor(dt(),lammpsInterface_->ntimestep()); atomTimeIntegrator_->init_integrate_position(dt()); ghostManager_.init_integrate_position(dt()); // account for other fixes doing time integration interscaleManager_.fundamental_force_reset(LammpsInterface::ATOM_POSITION); } ///-------------------------------------------------------- // post_init_integrate // time integration after the lammps atomic updates of // Verlet step 1 //-------------------------------------------------------- void ATC_Coupling::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); // ghost update, if needed ATC_Method::post_init_integrate(); // Apply time filtering to mass matrices, if needed if ((atomToElementMapType_ == EULERIAN) && timeFilterManager_.filter_dynamics() && !useFeMdMassMatrix_) { map<FieldName,int>::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(); } //-------------------------------------------------------- // pre_force // prior to calculation of forces //-------------------------------------------------------- void ATC_Coupling::pre_force() { ATC_Method::pre_force(); atomicRegulator_->pre_force(); } //-------------------------------------------------------- void ATC_Coupling::post_force() { ATC_Method::post_force(); if ( (atomToElementMapType_ == EULERIAN) && (step() % atomToElementMapFrequency_ == 0) ) { reset_atom_materials(); if (!useFeMdMassMatrix_) { map<FieldName,int>::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<FieldName,int>::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(); } //-------------------------------------------------------- // post_final_integrate // integration after the second stage lammps atomic // update of Verlet step 2 //-------------------------------------------------------- void ATC_Coupling::post_final_integrate() { 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(); - + bool needsSources = false; for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { if ((_tiIt_->second)->has_final_predictor()) { needsSources = true; break; } } if (needsSources) { extrinsicModelManager_.set_sources(fields_,extrinsicSources_); atomicRegulator_->compute_boundary_flux(fields_); compute_atomic_sources(intrinsicMask_,fields_,atomicSources_); } atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); - + // Compute atom-integrated rhs // parallel communication happens within FE_Engine compute_rhs_vector(intrinsicMask_,fields_,rhs_,FE_DOMAIN); 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(); // set state-based sources needsSources = false; for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { if ((_tiIt_->second)->has_final_corrector()) { needsSources = true; break; } } if (needsSources) { extrinsicModelManager_.set_sources(fields_,extrinsicSources_); atomicRegulator_->compute_boundary_flux(fields_); compute_atomic_sources(intrinsicMask_,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 + set_fixed_nodes(); 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(); lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+1); // adds next step to computes //ATC_Method::post_final_integrate(); } //================================================================= // //================================================================= void ATC_Coupling::finish() { ATC_Method::finish(); // Time integrator for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) { (_tiIt_->second)->finish(); } atomicRegulator_->finish(); } //================================================================= // //================================================================= void ATC_Coupling::compute_boundary_flux(const Array2D<bool> & rhsMask, const FIELDS & fields, FIELDS & rhs, const Array< set <int> > atomMaterialGroups, const VectorDependencyManager<SPAR_MAT * > * shpFcnDerivs, const SPAR_MAN * shpFcn, const DIAG_MAN * atomicWeights, const MatrixDependencyManager<DenseMatrix, bool> * elementMask, - const RegulatedNodes * nodeSet) + const SetDependencyManager<int> * 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<bool> & rhsMask, const FIELDS & fields, GRAD_FIELD_MATS & flux, - const PhysicsModel * physicsModel) + const PhysicsModel * physicsModel, + bool project) { if (! physicsModel) { physicsModel = physicsModel_; } feEngine_->compute_flux(rhsMask, fields, physicsModel, elementToMaterialMap_, flux); + if (project) { + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName name = field->first; + if ( rhsMask(name,FLUX) ) { + for(int i=0; i < nsd_ ; ++i) { + DENS_MAT & f = flux[name][i]; +if (i==0) f.print("pre flux_"+field_to_string(name)+"_"+ATC_Utility::to_string(i)); + apply_inverse_mass_matrix(f); +if (i==0) f.print("flux_"+field_to_string(name)+"_"+ATC_Utility::to_string(i)); + } + } + } + } } - //-------------------------------------------------------- void ATC_Coupling::nodal_projection(const FieldName & fieldName, const PhysicsModel * physicsModel, FIELD & field) { FIELDS rhs; rhs[fieldName].reset(nNodes_,field.nCols()); Array2D <bool> 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<int,int> > * 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 index 7a51ba3b8..002041b53 100644 --- a/lib/atc/ATC_Coupling.h +++ b/lib/atc/ATC_Coupling.h @@ -1,456 +1,500 @@ #ifndef ATC_COUPLING_H #define ATC_COUPLING_H #include <set> #include <map> #include <string> #include <utility> // ATC headers #include "ATC_Method.h" #include "ExtrinsicModel.h" 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; + friend class GlobalSliceSchrodingerPoissonSolver; /** constructor */ ATC_Coupling(std::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(); /** flags whether a methods reset is required */ virtual bool reset_methods() const; /** post integration run : called at end of run or simulation */ virtual void finish(); /** first time, before atomic integration */ virtual void pre_init_integrate(); /** Predictor phase, Verlet first step for velocity and position */ virtual void init_integrate(); /** Predictor phase, executed after Verlet */ virtual void post_init_integrate(); /** Corrector phase, executed after Verlet*/ virtual void post_final_integrate(); /** 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 std::set<std::string> & 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 std::set< std::pair<int,int> > * boundaryFaceSet) {bndyFaceSet_ = boundaryFaceSet;}; BoundaryIntegrationType parse_boundary_integration (int narg, char **arg, const std::set< std::pair<int,int> > * 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<bool> & rhs_mask, const FIELDS &fields, FIELDS &rhs, const Array< std::set <int> > atomMaterialGroups, const VectorDependencyManager<SPAR_MAT * > * shpFcnDerivs, const SPAR_MAN * shpFcn = NULL, const DIAG_MAN * atomicWeights = NULL, const MatrixDependencyManager<DenseMatrix, bool> * elementMask = NULL, - const RegulatedNodes * nodeSet = NULL); + const SetDependencyManager<int> * nodeSet = NULL); /** access to full right hand side / forcing vector */ FIELDS &rhs() {return rhs_;}; + Array2D <bool> rhs_mask() const { + Array2D <bool> mask(NUM_FIELDS,NUM_FLUX); + mask = false; + return mask; + } DENS_MAN &field_rhs(FieldName thisField) { return rhs_[thisField]; }; /** allow FE_Engine to construct ATC structures after mesh is constructed */ virtual void initialize_mesh_data(void); // public for FieldIntegrator bool source_atomic_quadrature(FieldName field) - { return false; } + { 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); /** computes tangent matrix using atomic quadrature near FE region */ void masked_atom_domain_rhs_tangent(const std::pair<FieldName,FieldName> row_col, const RHS_MASK & rhsMask, const FIELDS & fields, SPAR_MAT & stiffness, const PhysicsModel * physicsModel); /** 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 std::pair<FieldName,FieldName> row_col, const RHS_MASK & rhsMask, const FIELDS & fields, SPAR_MAT & stiffness, const IntegrationDomainType integrationType, const PhysicsModel * physicsModel=NULL); + void tangent_matrix(const std::pair<FieldName,FieldName> row_col, + const RHS_MASK & rhsMask, + const PhysicsModel * physicsModel, + SPAR_MAT & stiffness); /** 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 + // TODO rename to "mass_matrix" DIAG_MAT &get_mass_mat(FieldName thisField) { return massMats_[thisField].set_quantity();}; + /** access to underlying mass matrices */ + MATRIX * mass_matrix(FieldName thisField) + { + if (!useConsistentMassMatrix_(thisField)) { + return & massMats_[thisField].set_quantity(); + } + else { + return & consistentMassMats_[thisField].set_quantity(); + } + } + /** const access to underlying mass matrices */ + const MATRIX * mass_matrix(FieldName thisField) const + { + if (!useConsistentMassMatrix_(thisField)) { + MASS_MATS::const_iterator it = massMats_.find(thisField); + if (it != massMats_.end()) { + return & (it->second).quantity(); + } + else { + return NULL; + } + } + else { + CON_MASS_MATS::const_iterator it = consistentMassMats_.find(thisField); + if (it != consistentMassMats_.end()) { + return & (it->second).quantity(); + } + else { + return NULL; + } + } + } /** */ DENS_MAN &atomic_source(FieldName thisField){return atomicSources_[thisField];}; //--------------------------------------------------------------- /** \name materials */ //--------------------------------------------------------------- /*@{*/ /** access to element to material map */ Array<int> &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, std::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); virtual void read_restart_data(std::string fileName_, RESTART_LIST & data); virtual void write_restart_data(std::string fileName_, 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<bool> & 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<bool> &field_mask() {return fieldMask_;}; /** create field mask */ void reset_flux_mask(); /** field mask for intrinsic integration */ Array2D<bool> intrinsicMask_; /** wrapper for FE_Engine's compute_flux functions */ void compute_flux(const Array2D<bool> & rhs_mask, const FIELDS &fields, GRAD_FIELD_MATS &flux, - const PhysicsModel * physicsModel=NULL); + const PhysicsModel * physicsModel=NULL, + const bool normalize = false); /** evaluate rhs on the atomic domain which is near the FE region */ void masked_atom_domain_rhs_integral(const Array2D<bool> & rhs_mask, const FIELDS &fields, FIELDS &rhs, const PhysicsModel * physicsModel); /** evaluate rhs on a specified domain defined by mask and physics model */ void evaluate_rhs_integral(const Array2D<bool> & 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); /** compute the mass matrix components coming from MD integration */ virtual void compute_md_mass_matrix(FieldName thisField, DIAG_MAT & massMats); 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(); /** sets up mol transfers */ virtual void construct_molecule_transfers(); /** sets up accumulant & interpolant */ virtual void construct_interpolant(); /** reset number of local atoms */ virtual void reset_nlocal(); //--------------------------------------------------------------- /** 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 */ std::map<FieldName,TimeIntegrator * > timeIntegrators_; /** time integrator iterator */ mutable std::map<FieldName,TimeIntegrator * >::iterator _tiIt_; /** time integrator const iterator */ mutable std::map<FieldName,TimeIntegrator * >::const_iterator _ctiIt_; /*@}*/ //--------------------------------------------------------------- /** materials */ //--------------------------------------------------------------- /*@{*/ Array<int> elementToMaterialMap_; // ATOMIC_TAG * elementToMaterialMap_; /** atomic ATC material tag */ Array< std::set <int> > atomMaterialGroups_; // ATOMIC_TAG*atomMaterialGroups_; Array< std::set <int> > atomMaterialGroupsMask_; // ATOMIC_TAG*atomMaterialGroupsMask_; /*@}*/ //--------------------------------------------------------------- /** computational geometry */ //--------------------------------------------------------------- /*@{*/ bool atomQuadForInternal_; MatrixDependencyManager<DenseMatrix, bool> * elementMask_; MatrixDependencyManager<DenseMatrix, bool> * elementMaskMass_; MatrixDependencyManager<DenseMatrix, bool> * elementMaskMassMd_; /** 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_; MatrixDependencyManager<DenseMatrix, bool> * create_full_element_mask(); MatrixDependencyManager<DenseMatrix, int> * create_element_set_mask(const std::string & elementSetName); LargeToSmallAtomMap * internalToMask_; MatrixDependencyManager<DenseMatrix, int> * internalElement_; MatrixDependencyManager<DenseMatrix, int> * ghostElement_; DenseMatrixTransfer<int> * nodalGeometryType_; /*@}*/ /** \name boundary integration */ /*@{*/ /** boundary flux quadrature */ int bndyIntType_; const std::set< std::pair<int,int> > * bndyFaceSet_; std::set<std::string> boundaryFaceNames_; /*@}*/ //---------------------------------------------------------------- /** \name shape function matrices */ //---------------------------------------------------------------- /*@{*/ DIAG_MAN * atomicWeightsMask_; SPAR_MAN * shpFcnMask_; VectorDependencyManager<SPAR_MAT * > * shpFcnDerivsMask_; Array<bool> atomMask_; SPAR_MAN atomToOverlapMat_; DIAG_MAN nodalMaskMat_; /*@}*/ //--------------------------------------------------------------- /** \name PDE data */ //--------------------------------------------------------------- /*@{*/ /** mask for computation of fluxes */ Array2D<bool> 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 // DATA structures for tracking individual species and molecules FIELD_POINTERS atomicFields_; /*@}*/ // workspace variables mutable DENS_MAT _deltaQuantity_; }; }; #endif diff --git a/lib/atc/ATC_CouplingEnergy.cpp b/lib/atc/ATC_CouplingEnergy.cpp index 8ba10c4f8..033805490 100644 --- a/lib/atc/ATC_CouplingEnergy.cpp +++ b/lib/atc/ATC_CouplingEnergy.cpp @@ -1,412 +1,412 @@ // ATC_Transfer headers #include "ATC_CouplingEnergy.h" #include "Thermostat.h" #include "ATC_Error.h" #include "PrescribedDataManager.h" #include "FieldManager.h" // Other Headers #include <vector> #include <set> #include <utility> #include <typeinfo> using std::string; 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), 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_); } //-------------------------------------------------------- // 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(); // reset integration field mask intrinsicMask_.reset(NUM_FIELDS,NUM_FLUX); intrinsicMask_ = false; for (int i = 0; i < NUM_FLUX; i++) intrinsicMask_(TEMPERATURE,i) = fieldMask_(TEMPERATURE,i); } //-------------------------------------------------------- // 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"); // reference potential energy AtcAtomQuantity<double> * atomicReferencePotential; if (!initialized_) { atomicReferencePotential = new AtcAtomQuantity<double>(this); interscaleManager_.add_per_atom_quantity(atomicReferencePotential, "AtomicReferencePotential"); atomicReferencePotential->set_memory_type(PERSISTENT); } else { atomicReferencePotential = static_cast<AtcAtomQuantity<double> * >(interscaleManager_.per_atom_quantity("AtomicReferencePotential")); } nodalRefPotentialEnergy_ = new AtfShapeFunctionRestriction(this, atomicReferencePotential, shpFcn_); interscaleManager_.add_dense_matrix(nodalRefPotentialEnergy_, "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_); + atomicTwiceKineticEnergy, + shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicTwiceKineticEnergy, - "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"); + "NodalAtomicFluctuatingPotentialEnergy"); nodalAtomicConfigurationalTemperature_ = new AtfShapeFunctionMdProjection(this, nodalAtomicFluctuatingPotentialEnergy, TEMPERATURE); interscaleManager_.add_dense_matrix(nodalAtomicConfigurationalTemperature_, - "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_); + atomEnergyForTemperature, + shpFcn_); interscaleManager_.add_dense_matrix(nodalAtomicEnergy, - "NodalAtomicEnergy"); + "NodalAtomicEnergy"); // nodal atomic temperature field AtfShapeFunctionMdProjection * nodalAtomicTemperature = new AtfShapeFunctionMdProjection(this, nodalAtomicEnergy, TEMPERATURE); interscaleManager_.add_dense_matrix(nodalAtomicTemperature, "NodalAtomicTemperature"); 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); } } } } //-------------------------------------------------------- // 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 <kinetic|total> \section examples <TT> fix_modify atc temperature_definition kinetic </TT> \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_); if (temperatureDef_ == TOTAL) { setRefPE_ = true; } foundMatch = true; needReset_ = true; } // no match, call base class parser if (!foundMatch) { foundMatch = ATC_Coupling::modify(narg, arg); } return foundMatch; } //-------------------------------------------------------------------- // 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<FieldName> 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_CouplingMomentumEnergy.cpp b/lib/atc/ATC_CouplingMomentumEnergy.cpp index 7c1de1bbc..a29603a61 100644 --- a/lib/atc/ATC_CouplingMomentumEnergy.cpp +++ b/lib/atc/ATC_CouplingMomentumEnergy.cpp @@ -1,508 +1,512 @@ // ATC headers #include "ATC_CouplingMomentumEnergy.h" #include "KinetoThermostat.h" #include "ATC_Error.h" #include "PrescribedDataManager.h" +#include "FieldManager.h" // Other Headers #include <vector> #include <map> #include <set> #include <utility> #include <typeinfo> #include <iostream> using std::string; 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), nodalAtomicKineticTemperature_(NULL), nodalAtomicConfigurationalTemperature_(NULL), refPE_(0) { // Allocate PhysicsModel create_physics_model(THERMO_ELASTIC, 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; // set up atomic regulator atomicRegulator_ = new KinetoThermostat(this); // set up physics specific time integrator and thermostat trackDisplacement_ = true; fieldSizes_[DISPLACEMENT] = fieldSizes_[VELOCITY]; timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP); timeIntegrators_[TEMPERATURE] = new ThermalTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP); ghostManager_.set_boundary_dynamics(GhostManager::PRESCRIBED); // 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_); } //-------------------------------------------------------- // 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(); // reset integration field mask intrinsicMask_.reset(NUM_FIELDS,NUM_FLUX); intrinsicMask_ = false; for (int i = 0; i < NUM_FLUX; i++) intrinsicMask_(VELOCITY,i) = fieldMask_(VELOCITY,i); for (int i = 0; i < NUM_FLUX; i++) intrinsicMask_(TEMPERATURE,i) = fieldMask_(TEMPERATURE,i); refPE_=0; refPE_=potential_energy(); } //-------------------------------------------------------- // 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<double> * 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 + // always need fluctuating velocity and kinetic energy + FtaShapeFunctionProlongation * atomicMeanVelocity = new FtaShapeFunctionProlongation(this,&fields_[VELOCITY],shpFcn_); interscaleManager_.add_per_atom_quantity(atomicMeanVelocity, - "AtomicMeanVelocity"); - AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceFluctuatingKineticEnergy(this); + field_to_prolongation_name(VELOCITY)); + FieldManager fieldManager(this); + PerAtomQuantity<double> * fluctuatingAtomicVelocity = fieldManager.per_atom_quantity("AtomicFluctuatingVelocity"); // also creates ProlongedVelocity + AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceKineticEnergy(this,fluctuatingAtomicVelocity); AtomicEnergyForTemperature * atomEnergyForTemperature = NULL; // Appropriate per-atom quantity based on desired temperature definition 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<double> * atomicReferencePotential; if (!initialized_) { atomicReferencePotential = new AtcAtomQuantity<double>(this); interscaleManager_.add_per_atom_quantity(atomicReferencePotential, "AtomicReferencePotential"); atomicReferencePotential->set_memory_type(PERSISTENT); } else { atomicReferencePotential = static_cast<AtcAtomQuantity<double> * >(interscaleManager_.per_atom_quantity("AtomicReferencePotential")); } nodalRefPotentialEnergy_ = new AtfShapeFunctionRestriction(this, atomicReferencePotential, shpFcn_); interscaleManager_.add_dense_matrix(nodalRefPotentialEnergy_, "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"); 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); } } //-------------------------------------------------------- // 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; } //-------------------------------------------------------------------- // 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<FieldName> 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<FieldName> 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(); } } }; diff --git a/lib/atc/ATC_Error.h b/lib/atc/ATC_Error.h index 91d3b4dcf..fc356dbec 100644 --- a/lib/atc/ATC_Error.h +++ b/lib/atc/ATC_Error.h @@ -1,53 +1,52 @@ // ATC_Error : a base class for atom-continuum errors #ifndef ATC_ERROR #define ATC_ERROR #include <string> // 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 index 6acf25efd..b1f3fa254 100644 --- a/lib/atc/ATC_Method.cpp +++ b/lib/atc/ATC_Method.cpp @@ -1,2311 +1,2427 @@ // 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 "FieldManager.h" #include <fstream> #include <sstream> #include <iostream> using ATC_Utility::sgn; using ATC_Utility::to_string; using ATC_Utility::is_dbl; using std::stringstream; using std::ifstream; using std::ofstream; using std::string; using std::map; using std::set; using std::vector; using std::pair; 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), integrateInternalAtoms_(false), atomTimeIntegrator_(NULL), ghostManager_(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), + nNodes_(0), nsd_(lammpsInterface_->dimension()), xref_(NULL), readXref_(false), needXrefProcessorGhosts_(false), trackDisplacement_(false), needsAtomToElementMap_(true), atomElement_(NULL), atomGhostElement_(NULL), internalElementSet_(""), 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), 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_), accumulantBandwidth_(0), useRestart_(false), hasRefPE_(false), setRefPE_(false), setRefPEvalue_(false), refPEvalue_(0.), readRefPE_(false), nodalRefPotentialEnergy_(NULL), simTime_(0.0), stepCounter_(0) { lammpsInterface_->print_msg_once("version "+version()); 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 (atomTimeIntegrator_) delete atomTimeIntegrator_; 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<FieldName,int>::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 } // pass off to ghost manager else if (strcmp(arg[argIdx],"boundary_dynamics")==0) { argIdx++; match = ghostManager_.modify(narg-argIdx,&arg[argIdx]); } // 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 <bond | kernel> <optional on | off> \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 <TT> fix_modify AtC on_the_fly bond on </TT> \n <TT> fix_modify AtC on_the_fly kernel </TT> \n <TT> fix_modify AtC on_the_fly kernel off </TT> \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, code 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 <filename_prefix> <frequency> [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 <TT> fix_modify AtC output heatFE 100 </TT> \n <TT> fix_modify AtC output hardyFE 1 text tensor_components </TT> \n <TT> fix_modify AtC output hardyFE 10 text binary tensor_components </TT> \n <TT> fix_modify AtC output index step </TT> \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 no default format output indexed by time */ else if (strcmp(arg[argIdx],"output")==0) { argIdx++; /*! \page man_output_nodeset fix_modify AtC output nodeset \section syntax fix_modify AtC output nodeset <nodeset_name> <operation> - 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 <TT> fix_modify AtC output nodeset nset1 sum </TT> \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 output boundary_integral \section syntax fix_modify AtC output boundary_integral [field] faceset [name] - field (string) : name of hardy field - name (string) : name of faceset \section examples <TT> fix_modify AtC output boundary_integral stress faceset loop1 </TT> \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 output contour_integral \section syntax fix_modify AtC output contour_integral [field] faceset [name] <axis [x | y | z ]> - field (string) : name of hardy field - name (string) : name of faceset - axis (string) : x or y or z \section examples <TT> fix_modify AtC output contour_integral stress faceset loop1 </TT> \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 <string,string> pair_name(name,field); fsetData_[pair_name] = type; match = true; } } // end "boundary_integral" || "contour_integral" /*! \page man_output_elementset fix_modify AtC output elementset \section syntax - fix_modify AtC output volume_integral <eset_name> <field> {` + fix_modify AtC output volume_integral <eset_name> <field> - 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 <TT> fix_modify AtC output eset1 mass_density </TT> \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 <string,FieldName> 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; i<narg; ++i) { if (strcmp(arg[i],"full_text")==0) full_text_output = true; else if (strcmp(arg[i],"text")==0) text_output = true; else if (strcmp(arg[i],"binary")==0) ensight_output = true; else if (strcmp(arg[i],"vector_components")==0) vect_comp = true; else if (strcmp(arg[i],"tensor_components")==0) tensor_comp = true; else { throw ATC_Error(" output: unknown keyword "); } } if (outputFrequency_>0) { set<int> 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<string> tnames = tracked_names(); vector<string> fnames; vector<string> 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; } } + else if (strcmp(arg[argIdx],"write")==0) { + argIdx++; + FieldName thisField; + int thisIndex; + parse_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + string filename(arg[argIdx++]); + stringstream f; + set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nsetName); + set<int>::const_iterator iset; + const DENS_MAT & field =(fields_.find(thisField)->second).quantity(); + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + double v = field(inode,thisIndex); + f << inode << " " << std::setprecision(17) << v << "\n"; + } + LammpsInterface::instance()->write_file(filename,f.str()); + match = true; + } // add a species for tracking /*! \page man_add_species fix_modify AtC add_species \section syntax fix_modify_AtC add_species <TAG> <group|type> <ID> \n - <TAG> = tag for tracking a species \n - group|type = LAMMPS defined group or type of atoms \n - <ID> = name of group or type number \n \section examples <TT> fix_modify AtC add_species gold type 1 </TT> \n <TT> group GOLDGROUP type 1 </TT> \n <TT> fix_modify AtC add_species gold group GOLDGROUP </TT> \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++; if (strcmp(arg[argIdx],"group")==0) { if (narg-argIdx == 2) { string name = arg[++argIdx]; int id = lammpsInterface_->group_bit(name); groupList_.push_back(id); groupNames_.push_back(tag); } 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); } } } else if (strcmp(arg[argIdx],"type")==0) { if (narg-argIdx == 2) { int id = atoi(arg[++argIdx]); typeList_.push_back(id); typeNames_.push_back(tag); } else { while (++argIdx < narg) { int id = atoi(arg[argIdx]); string tag = speciesTag+"_"+to_string(id); typeList_.push_back(id); typeNames_.push_back(tag); } } } else { throw ATC_Error("ATC_Method: add_species only handles groups or types"); } match = true; } // remove species from tracking /*! \page man_remove_species fix_modify AtC remove_species \section syntax fix_modify_AtC delete_species <TAG> \n - <TAG> = tag for tracking a species \n \section examples <TT> fix_modify AtC remove_species gold </TT> \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],"delete_species")==0) { argIdx++; string tag = arg[argIdx++]; if (strcmp(arg[argIdx],"group")==0) { for (unsigned int j = 0; j < groupList_.size(); j++) { if (tag == groupNames_[j]) { groupList_.erase(groupList_.begin()+j); groupNames_.erase(groupNames_.begin()+j); break; } } } else if (strcmp(arg[argIdx],"type")==0) { for (unsigned int j = 0; j < typeList_.size(); j++) { if (tag == typeNames_[j]) { typeList_.erase(typeList_.begin()+j); typeNames_.erase(typeNames_.begin()+j); break; } } } else { throw ATC_Error("ATC_Method: delete_species only handles groups or types"); } match = true; } // add a molecule for tracking /*! \page man_add_molecule fix_modify AtC add_molecule \section syntax fix_modify_AtC add_molecule <small|large> <TAG> <GROUP_NAME> \n - small|large = can be small if molecule size < cutoff radius, must be large otherwise \n - <TAG> = tag for tracking a species \n - <GROUP_NAME> = name of group that tracking will be applied to \n \section examples <TT> group WATERGROUP type 1 2 </TT> \n <TT> fix_modify AtC add_molecule small water WATERGROUP </TT> \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<MolSize,int>(size,groupBit); match = true; } // remove molecule from tracking /*! \page man_remove_molecule fix_modify AtC remove_molecule \section syntax fix_modify_AtC remove_molecule <TAG> \n - <TAG> = tag for tracking a molecule type \n \section examples <TT> fix_modify AtC remove_molecule water </TT> \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 <atom-type-id> - <atom-type-id> = type id for atoms that represent a ficticious boundary internal to the FE mesh \section examples <TT> fix_modify AtC boundary type ghost_atoms </TT> \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 default none */ else if (strcmp(arg[argIdx],"boundary")==0) { argIdx++; groupTagGhost_ = arg[argIdx++]; match = true; } /*! \page man_internal_atom_integrate fix_modify AtC internal_atom_integrate \section syntax fix_modify AtC internal_atom_integrate <on | off> <TT> fix_modify AtC internal_atom_integrate on </TT> \section description Has AtC perform time integration for the atoms in the group on which it operates. This does not include boundary atoms. \section restrictions AtC must be created before any fixes doing time integration. It must be on for coupling methods which impose constraints on velocities during the first verlet step, e.g. control momentum glc_velocity. \section default on for coupling methods, off for post-processors off */ else if (strcmp(arg[argIdx],"internal_atom_integrate")==0) { argIdx++; if (strcmp(arg[argIdx],"off")==0) { integrateInternalAtoms_ = false; match = true; } else { integrateInternalAtoms_ = true; match = true; } } /*! \page man_internal_element_set fix_modify AtC internal_element_set \section syntax fix_modify AtC internal_element_set <element-set-name> - <element-set-name> = name of element set defining internal region, or off \section examples <TT> fix_modify AtC internal_element_set myElementSet </TT> <TT> fix_modify AtC internal_element_set off </TT> \section description Enables AtC to base the region for internal atoms to be an element set. If no ghost atoms are used, all the AtC atoms must be constrained to remain in this element set by the user, e.g., with walls. If boundary atoms are used in conjunction with Eulerian atom maps AtC will partition all atoms of a boundary or internal type to be of type internal if they are in the internal region or to be of type boundary otherwise. \section restrictions If boundary atoms are used in conjunction with Eulerian atom maps, the Eulerian reset frequency must be an integer multiple of the Lammps reneighbor frequency \section related see \ref atom_element_map_type and \ref boundary \section default off */ else if (strcmp(arg[argIdx],"internal_element_set")==0) { argIdx++; if (strcmp(arg[argIdx],"off")==0) { internalElementSet_ = ""; match = true; } else { internalElementSet_ = string(arg[argIdx]); const set<int> & elementSet((feEngine_->fe_mesh())->elementset(internalElementSet_)); // check it exists and is not trivial if (elementSet.size()==0) throw ATC_Error("internal_element_set - element set " + internalElementSet_ + " has no elements"); match = true; } } /*! \page man_atom_weight fix_modify AtC atom_weight \section syntax fix_modify AtC atom_weight <method> <arguments> - <method> = \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: volume per atom determined based on the atom count in the MD regions and their volumes. Note: meaningful only if atoms completely fill all the regions. \n group: volume per atom determined based on the atom count in a group and its volume\n read_in: list of values for atoms are read-in from specified file \n \section examples <TT> fix_modify atc atom_weight constant myatoms 11.8 </TT> \n <TT> fix_modify atc atom_weight lattice </TT> \n <TT> fix_modify atc atom_weight read-in atm_wt_file.txt </TT> \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 <type> - <type> = \n replicated_memory: nodal information replicated on each processor \n distributed_memory: only owned nodal information on processor \n \section examples <TT> fix_modify atc decomposition distributed_memory </TT> \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 <filename> <frequency> - <filename> = name of file that atomic weights are written to \n - <frequency> = how often writes will occur \n \section examples <TT> fix_modify atc write_atom_weights atm_wt_file.txt 10 </TT> \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_reset_time fix_modify AtC reset_time \section syntax fix_modify AtC reset_time <value> \section examples <TT> fix_modify atc reset_time 0.0 </TT> \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_time fix_modify AtC kernel_bandwidth \section syntax fix_modify AtC kernel_bandwidth <value> \section examples - <TT> fix_modify atc reset_time 8 </TT> \n + <TT> fix_modify atc kernel_bandwidth 8 </TT> \n \section description Sets a maximum parallel bandwidth for the kernel functions during parallel communication. If the command is not issued, the default will be to assume the bandwidth of the kernel matrix corresponds to the number of sampling locations. \section restrictions Only is used if kernel functions are being used. \section related \section default Number of sample locations. */ else if (strcmp(arg[argIdx],"kernel_bandwidth")==0) { argIdx++; accumulantBandwidth_ = atoi(arg[argIdx]); 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 <TT> 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_or_filename(optional)> - value (double) : optional user specified zero point for PE in native LAMMPS energy units \n - filename (string) : optional user specified string for file of nodal PE values to be read-in \section examples <TT> fix_modify AtC set reference_potential_energy </TT> \n <TT> fix_modify AtC set reference_potential_energy -0.05 </TT> \n <TT> fix_modify AtC set reference_potential_energy myPEvalues </TT> \n \section description Used to set various quantities for the post-processing algorithms. It 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, or values provided within the specified filename. \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 <eulerian|lagrangian> <frequency> \n - frequency (int) : frequency of updating atom-to-continuum maps based on the current configuration - only for eulerian \section examples <TT> fix_modify atc atom_element_map eulerian 100 </TT> \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 <TT> fix_modify AtC read_restart ATC_state </TT> \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 <TT> fix_modify AtC write_restart restart.mydata </TT> \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) + { + string thisName = args[argIdx++]; + thisField = string_to_field(thisName); + map<FieldName,int>::const_iterator iter = fieldSizes_.find(thisField); + if (iter == fieldSizes_.end()) { + throw ATC_Error("Bad field name: "+thisName); + } + } + //-------------------------------------------------- // helper function for parser // handles : "displacement x" and "temperature" by indexing argIdx // 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<FieldName,int>::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; } + // ensure initial configuration is consistent with element set + + if (internalElementSet_.size() && groupbitGhost_) { + int *mask = lammpsInterface_->atom_mask(); + int nlocal = lammpsInterface_->nlocal(); + const FE_Mesh * feMesh = feEngine_->fe_mesh(); + const set<int> & elementSet(feMesh->elementset(internalElementSet_)); + int element; + DENS_VEC coords(nsd_); + for (int i = 0; i < nlocal; ++i) { + if (mask[i] & groupbit_ || mask[i] & groupbitGhost_) { + for (int j = 0; j < nsd_; j++) { + coords(j) = xref_[i][j]; + } + element = feMesh->map_to_element(coords); + if (elementSet.find(element) == elementSet.end()) { + mask[i] |= groupbitGhost_; + mask[i] &= ~groupbit_; + } + else { + mask[i] &= ~groupbitGhost_; + mask[i] |= groupbit_; + } + } + } + } + // set up maps from lammps to atc indexing reset_nlocal(); } 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(); // 6d) reference values this->set_reference_potential_energy(); // 6e) atomic output for 0th step update_peratom_output(); + massMatInv_.reset(nNodes_,nNodes_); + feEngine_->compute_lumped_mass_matrix(massMatInv_); + for (int i = 0; i < nNodes_; ++i) { + massMatInv_(i,i) = 1./massMatInv_(i,i); + } + // clear need for resets needReset_ = false; } //------------------------------------------------------------------- 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<double> * myAtomPositions = new ClonedAtomQuantity<double>(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<double>(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<double> * myAtomPositions = new ClonedAtomQuantity<double>(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_methods() { if (this->reset_methods()) { if (atomTimeIntegrator_) delete atomTimeIntegrator_; if (integrateInternalAtoms_) { atomTimeIntegrator_ = new AtomTimeIntegratorType(this,INTERNAL); } else { atomTimeIntegrator_ = new AtomTimeIntegrator(); } // set up integration schemes for ghosts ghostManager_.construct_methods(); } } //------------------------------------------------------------------- void ATC_Method::construct_transfers() { this->construct_interpolant(); this->construct_molecule_transfers(); atomTimeIntegrator_->construct_transfers(); ghostManager_.construct_transfers(); } //------------------------------------------------------------------- PerAtomDiagonalMatrix<double> * ATC_Method::create_atom_volume() { 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::init_integrate() { atomTimeIntegrator_->init_integrate_velocity(dt()); ghostManager_.init_integrate_velocity(dt()); // account for other fixes doing time integration interscaleManager_.fundamental_force_reset(LammpsInterface::ATOM_VELOCITY); atomTimeIntegrator_->init_integrate_position(dt()); ghostManager_.init_integrate_position(dt()); // account for other fixes doing time integration interscaleManager_.fundamental_force_reset(LammpsInterface::ATOM_POSITION); } //------------------------------------------------------------------- void ATC_Method::post_init_integrate() { ghostManager_.post_init_integrate(); } //------------------------------------------------------------------- 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(); // change types based on moving from internal region to ghost region if ((atomToElementMapType_ == EULERIAN) && (step() % atomToElementMapFrequency_ == 0)) { ghostManager_.pre_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() { // 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(); } //-------------------------------------------------------- void ATC_Method::final_integrate() { atomTimeIntegrator_->final_integrate(dt()); ghostManager_.final_integrate(dt()); // account for other fixes doing time integration interscaleManager_.fundamental_force_reset(LammpsInterface::ATOM_VELOCITY); } //------------------------------------------------------------------- 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<FieldName,int> & newFieldSizes) { map<FieldName,int>::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) { if (setRefPE_) { if (setRefPEvalue_) { nodalRefPotentialEnergy_->set_quantity() = 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_->set_quantity()).from_file(nodalRefPEfile_); readRefPE_ = false; } else { hasRefPE_ = false; SPAR_MAN * referenceAccumulant = interscaleManager_.sparse_matrix("ReferenceAccumulant"); if (referenceAccumulant) { referenceAccumulant->set_quantity() = accumulant_->quantity(); } DIAG_MAN * referenceAccumulantInverseVolumes = interscaleManager_.diagonal_matrix("ReferenceAccumulantInverseVolumes"); if (referenceAccumulantInverseVolumes) { referenceAccumulantInverseVolumes->set_quantity() = accumulantInverseVolumes_->quantity(); } PAQ * atomicRefPe = interscaleManager_.per_atom_quantity("AtomicReferencePotential"); if (!atomicRefPe) { throw ATC_Error("ATC_Method::set_reference_potential_energy - atomic reference PE object was not created during construct_transfers"); } PAQ* pe = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); if (!pe) { throw ATC_Error("ATC_Method::set_reference_potential_energy - atomic PE object was not created during construct_transfers"); } atomicRefPe->set_quantity() = pe->quantity(); atomicRefPe->fix_quantity(); } setRefPE_ = false; hasRefPE_ = true; } } //------------------------------------------------------------------- //================================================================= // memory management and processor information exchange //================================================================= //----------------------------------------------------------------- // number of doubles //----------------------------------------------------------------- int ATC_Method::doubles_per_atom() const { int doubles = 4; doubles += interscaleManager_.memory_usage(); return doubles; } //----------------------------------------------------------------- // memory usage of local atom-based arrays //----------------------------------------------------------------- int ATC_Method::memory_usage() { int bytes = doubles_per_atom(); 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) { 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]; } } } } - - return m; - //int mySize = 3; - //if (num_bond) - // mySize += 1 + lammpsInterface_->bond_per_atom(); - //return mySize; + return m; // total amount of data sent } //----------------------------------------------------------------- // 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<int>(buf[m++]); for (int ii = 0; ii < lammpsInterface_->bond_per_atom(); ii++) { bond_atom[i][ii] = static_cast<int>(buf[m++]); } } } } + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + int ATC_Method::comm_forward() + { + int size = 3; + if (lammpsInterface_->num_bond()) + { size += lammpsInterface_->bond_per_atom()+1; } + return size; + } + //----------------------------------------------------------------- // //----------------------------------------------------------------- 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_++; } // set up internal & ghost maps if (nLocal_>0) { // set map internalToAtom_.resize(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; } #ifdef EXTENDED_ERROR_CHECKING stringstream ss; ss << "Nlocal = " << nLocal_ << " but only found " << j << "atoms"; if (j!=nLocal_) throw ATC_Error(ss.str()); #endif // construct reverse map atomToInternal_.clear(); for (int i = 0; i < nLocal_; ++i) { atomToInternal_[internalToAtom_(i)] = i; } } if (nLocalGhost_>0) { // set map ghostToAtom_.resize(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 <int,int> id2tag; map <int,int>::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<int> & nset, set<int> & 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<int> & nset, set<int> & aset, bool ghost, double tol) { Array<int> & 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,(interscaleManager_.per_atom_sparse_matrix("InterpolantGhost"))->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 <string, FieldName>, NodesetOperationType >::const_iterator iter; for (iter = nsetData_.begin(); iter != nsetData_.end();iter++){ pair <string, FieldName> id = iter->first; string nsetName = id.first; FieldName field = id.second; double sum = 0.0; const set<int> 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<string,string>, 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 <string, FieldName>, ElementsetOperationType >::const_iterator iter; for (iter = esetData_.begin(); iter != esetData_.end();iter++){ pair <string, FieldName> 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 <int,int> tag2id; map <int,int>::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; + int ncols = 0; + int style = LammpsInterface::CHARGE_STYLE; 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"; + ss << "found " << natoms << " atoms in reference " << filename ; + while(in.good()) { + in.getline(line,lineSize); + string str(line); + int pos = str.find("Atoms"); + if (pos > -1) { + in.getline(line,lineSize); // blank line + break; + } + } + in.getline(line,lineSize); + std::vector<std::string> tokens; + ATC_Utility::command_strings(line, tokens); + ncols = tokens.size(); + switch (ncols) { + // atomic: id type x y z + case 5: + case 8: + ss << " style:atomic"; + style = LammpsInterface::ATOMIC_STYLE; + break; + // charge: id type q x y z + // molecule : id molecule-ID type x y z + case 6: + ss << " style:charge"; + style = LammpsInterface::CHARGE_STYLE; + break; + // full : id molecule-ID type q x y z + case 7: + ss << " style:full"; + style = LammpsInterface::FULL_STYLE; + break; + default: + throw ATC_Error("cannot determine atom style, columns:"+to_string(ncols)); + break; + } LammpsInterface::instance()->print_msg(ss.str()); + in.close(); } LammpsInterface::instance()->int_broadcast(&natoms); // 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}; + int nread = 0,type = -1, tag = -1, count = 0, mId = -1; + double x[3]={0,0,0}, q =0; while (nread < natoms) { if (LammpsInterface::instance()->rank_zero()) { in.getline(line,lineSize); stringstream ss (line,stringstream::in | stringstream::out); + if (style == LammpsInterface::CHARGE_STYLE) + ss >> tag >> type >> q >> x[0] >> x[1] >> x[2]; + else if (style == LammpsInterface::FULL_STYLE) + ss >> tag >> mId >> type >> q >> x[0] >> x[1] >> x[2]; + else ss >> tag >> type >> x[0] >> x[1] >> x[2]; nread++; } 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 <int,int> tag2id; map <int,int>::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 index d7708d298..aa8f23872 100644 --- a/lib/atc/ATC_Method.h +++ b/lib/atc/ATC_Method.h @@ -1,863 +1,870 @@ #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" #include "GhostManager.h" // Other headers #include <vector> #include <set> #include <utility> #include <string> #include <map> namespace ATC { // forward declarations class AtomTimeIntegrator; /** * @class ATC_Method * @brief Base class for atom-continuum coupling or transfer operators */ class ATC_Method { public: /** methods */ /** constructor */ ATC_Method(std::string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix); /** destructor */ virtual ~ATC_Method(); std::string version() {return "2.0";} /** parser/modifier */ virtual bool modify(int narg, char **arg); + void parse_field(/*const*/ char ** args, int &argIndex, + FieldName &thisField); void parse_field(/*const*/ char ** args, int &argIndex, FieldName &thisField, int &thisIndex); /** 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 and position */ virtual void init_integrate(); /** 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(); int doubles_per_atom() const; 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 comm_forward(void); 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 { std::string msg = "species:\n"; for(unsigned int i = 0; i < typeList_.size(); i++) { msg+=" type:"+ATC_Utility::to_string(typeList_[i])+" name: "+ typeNames_[i]+"\n"; } for(unsigned int i = 0; i < groupList_.size(); i++) { msg+=" group (bit):"+ATC_Utility::to_string(groupList_[i])+" name: "+ groupNames_[i]+"\n"; } ATC::LammpsInterface::instance()->print_msg_once(msg); return typeList_.size()+groupList_.size(); } std::vector<std::string> tracked_names() const { std::vector<std::string> 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(std::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();}; /** 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(std::map<FieldName,int> & 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<int> &internal_to_atom_map() {return internalToAtom_;}; /** ghost atom to global map */ const Array<int> &ghost_to_atom_map() {return ghostToAtom_;}; const std::map<int,int> & atom_to_internal_map() {return atomToInternal_;}; /** access to xref */ double ** xref() {return xref_;}; /** access to faceset names */ const std::set<PAIR> &faceset(const std::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 std::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() || ghostManager_.need_reset();}; /** sizes of each field being considered */ const std::map<FieldName,int> & 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 molecule ids */ const std::map<std::string,std::pair<MolSize,int> > & molecule_ids() const {return moleculeIds_;}; /** access to internal element set */ const std::string & internal_element_set() {return internalElementSet_;}; //---------------------------------------------------------------- /** \name mass matrix operations */ //---------------------------------------------------------------- + void apply_inverse_mass_matrix(MATRIX & data) { + data = massMatInv_*data; + } // inverted using GMRES void apply_inverse_mass_matrix(MATRIX & data, FieldName thisField) { 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() ) { std::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_; } std::vector<int> & type_list() { return typeList_; } std::vector<int> & 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_; } int accumulant_bandwidth() const { if (accumulantBandwidth_) return accumulantBandwidth_; else return feEngine_->num_nodes(); }; PerAtomQuantity<double> * atom_coarsegraining_positions() { return atomCoarseGrainingPositions_; } PerAtomQuantity<double> * atom_reference_positions() { return atomReferencePositions_; } PerAtomQuantity<int> * 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, std::set<int>& nset, std::set<int>& aset, double tol =1.e-8); int nodal_influence(const int groupbit, std::set<int>& nset, std::set<int>& 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<double> * create_atom_volume(); //--------------------------------------------------------------- /** \name access to potential energy reference */ //--------------------------------------------------------------- /*@{*/ DENS_MAN * 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(); /** set up data which is dependency managed */ virtual void construct_transfers(); /** sets up accumulant & interpolant */ virtual void construct_interpolant()=0; /** sets up mol transfers */ virtual void construct_molecule_transfers()=0; /** update the peratom output pointers */ void update_peratom_output(void); virtual void read_restart_data(std::string fileName_, RESTART_LIST & data); virtual void write_restart_data(std::string fileName_, RESTART_LIST & data); void pack_fields(RESTART_LIST & data); /** mass matrices */ + DIAG_MAT massMatInv_; 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<bool> useConsistentMassMatrix_; std::map<FieldName,SPAR_MAN> consistentMassMats_; std::map<FieldName,DENS_MAN> consistentMassMatsInv_; std::map<FieldName,TimeFilter * > massMatTimeFilters_; //--------------------------------------------------------------- /** \name quadrature weight function */ //--------------------------------------------------------------- /*@{*/ void write_atomic_weights(const std::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 */ std::map<std::string,std::pair<MolSize,int> > moleculeIds_; /** a list of lammps types & groups ATC tracks */ std::vector<std::string> typeNames_; std::vector<std::string> groupNames_; std::vector<int> typeList_; std::vector<int> 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_; /** check to see if we are integrating the atoms */ bool integrateInternalAtoms_; /** object which integrates atoms */ AtomTimeIntegrator * atomTimeIntegrator_; /** objects which handles integration and modification of ghost atoms */ GhostManager ghostManager_; /** 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<double> * atomCoarseGrainingPositions_; PerAtomQuantity<double> * atomGhostCoarseGrainingPositions_; PerAtomQuantity<double> * atomProcGhostCoarseGrainingPositions_; PerAtomQuantity<double> * 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_; std::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<int> * atomElement_; PerAtomQuantity<int> * atomGhostElement_; /* use element sets to define internal and/or ghost regions */ std::string internalElementSet_; /** 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 */ std::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 */ //--------------------------------------------------------------- /*@{*/ std::map<FieldName,int> 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<double> * atomVolume_; std::string atomicWeightsFile_; bool atomicWeightsWriteFlag_; int atomicWeightsWriteFrequency_; double atomicVolume_; // global atomic volume for homogeneous set of atoms std::map<int,double> Valpha_; AtomicWeightType atomWeightType_; /*@}*/ //--------------------------------------------------------------- /** \name domain decomposition */ //--------------------------------------------------------------- /*@{*/ DomainDecompositionType domainDecomposition_; /*@}*/ //--------------------------------------------------------------- /** \name atom data */ //--------------------------------------------------------------- /*@{*/ /** bitwise comparisons for boundary (ghost) atoms */ int groupbit_; int groupbitGhost_; bool needProcGhost_; std::string groupTag_; std::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_; Array<int> internalToAtom_; std::map<int,int> atomToInternal_; Array<int> 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<SPAR_MAT * > * shpFcnDerivs_; /** map from species std::string tag to the species density */ std::map<std::string,DENS_MAN> 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_; int accumulantBandwidth_; /*@}*/ //--------------------------------------------------------------- /** \name restart procedures */ //--------------------------------------------------------------- bool useRestart_; std::string restartFileName_; //--------------------------------------------------------------- /** \name data specific to node/faceset for global output */ //--------------------------------------------------------------- /** group computes : type, group_id -> value */ std::map< std::pair<std::string, FieldName > , NodesetOperationType> nsetData_; std::map< std::pair<std::string,std::string>, FacesetIntegralType > fsetData_; std::map< std::pair<std::string, FieldName>,ElementsetOperationType > esetData_; //--------------------------------------------------------------- /** \name reference data */ //--------------------------------------------------------------- bool hasRefPE_; bool setRefPE_; bool setRefPEvalue_; double refPEvalue_; bool readRefPE_; std::string nodalRefPEfile_; DENS_MAN* 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_TypeDefs.h b/lib/atc/ATC_TypeDefs.h index 8906d5c63..5567af13c 100644 --- a/lib/atc/ATC_TypeDefs.h +++ b/lib/atc/ATC_TypeDefs.h @@ -1,625 +1,631 @@ #ifndef ATC_TYPEDEFS_H #define ATC_TYPEDEFS_H #include <set> #include <vector> #include <map> #include <utility> #include <string> #ifdef NEW_LAMMPS #include "lmptype.h" #endif #include "Array.h" #include "Array2D.h" #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 + FE_DOMAIN, + FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE, + FULL_DOMAIN_FREE_ONLY }; /** 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, KINETIC_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*NDIM, // KINETIC_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 NodalAtomicFieldNormalization { NO_NORMALIZATION=0, VOLUME_NORMALIZATION, NUMBER_NORMALIZATION, MASS_NORMALIZATION, MASS_MATRIX }; inline FieldName use_mass_matrix(FieldName in) { if (in == TEMPERATURE) return in; else return MASS_DENSITY; } /** 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 std::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 std::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 KINETIC_STRESS: return "kinetic_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 std::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=="kinetic_stress") return KINETIC_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 std::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 std::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 std::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 }; /** string to temperature definition enum */ inline bool string_to_temperature_def(const std::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 + OPEN_SOURCE, // has a open boundary 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 std::pair<int, int> PAIR; /** typedefs for compact set of bc values */ typedef std::set < std::pair < int, double> > BC_SET; // node, value typedef std::vector< BC_SET > BCS; // dof (node, value) typedef std::set<int> NSET; // nodeset typedef std::set<PAIR> FSET; // faceset typedef std::set<int> ESET; // elementset /** typedefs for N and B integrand functions */ typedef std::set<FieldName> ARG_NAMES; typedef std::map<FieldName, ATC_matrix::DenseMatrix<double> > ARGS; typedef ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> FIELD; typedef std::vector<ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> > GRAD_FIELD; typedef std::map<FieldName, ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> > FIELDS; typedef std::map<FieldName, ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> * > FIELD_POINTERS; typedef std::map<FieldName, ATC_matrix::DenseMatrix<double> > FIELD_MATS; typedef std::map<std::string, ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> > TAG_FIELDS; typedef std::map<FieldName, std::vector<ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> > > GRAD_FIELDS; typedef std::map<FieldName, std::vector<ATC_matrix::DenseMatrix<double> > > GRAD_FIELD_MATS; typedef std::map<FieldName, ATC::MatrixDependencyManager<DiagonalMatrix, double> > MASS_MATS; typedef std::map<FieldName, ATC::MatrixDependencyManager<SparseMatrix, double> > CON_MASS_MATS; typedef ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> DENS_MAN; typedef ATC::MatrixDependencyManager<SparseMatrix, double> SPAR_MAN; typedef ATC::MatrixDependencyManager<ParSparseMatrix, double> PAR_SPAR_MAN; typedef ATC::MatrixDependencyManager<DiagonalMatrix, double> DIAG_MAN; typedef ATC::MatrixDependencyManager<ParDiagonalMatrix, double> PAR_DIAG_MAN; /** typedefs for WeakEquation evaluation */ typedef Array2D<bool> RHS_MASK; /** typedefs for input/output */ typedef std::map<std::string, const ATC_matrix::Matrix<double>*> OUTPUT_LIST; typedef std::map<std::string, ATC_matrix::Matrix<double>*> RESTART_LIST; typedef std::pair<int, int> ID_PAIR; typedef std::vector< std::pair<int, int> > ID_LIST; /** misc typedefs */ class XT_Function; class UXT_Function; typedef std::map<FieldName, std::map<PAIR, Array<XT_Function*> > > SURFACE_SOURCE; typedef std::map<FieldName, std::map<PAIR, Array<UXT_Function*> > > ROBIN_SURFACE_SOURCE; + typedef std::map<FieldName, std::set<PAIR> > OPEN_SURFACE; typedef std::map<FieldName, Array2D<XT_Function *> > VOLUME_SOURCE; typedef std::map<std::string, ATC::MatrixDependencyManager<ATC_matrix::DenseMatrix, double> > ATOMIC_DATA; /** typedefs for FE_Mesh */ typedef std::map<std::string, std::set<int > > NODE_SET_MAP; typedef std::map<std::string, std::set<int > > ELEMENT_SET_MAP; typedef std::map<std::string, std::set<PAIR> > 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 std::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 std::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 std::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 std::string print_mask(const Array2D<bool> & rhsMask) { std::string msg; for (int i = 0; i < NUM_FIELDS; i++) { FieldName field = (FieldName) i; std::string name = field_to_string(field); if (rhsMask(field,FLUX) || rhsMask(field,SOURCE) || rhsMask(field,PRESCRIBED_SOURCE) || rhsMask(field,ROBIN_SOURCE) + || rhsMask(field,OPEN_SOURCE) || rhsMask(field,EXTRINSIC_SOURCE)) { msg = "RHS_MASK: " + name; if (rhsMask(field,FLUX)) msg += " flux"; if (rhsMask(field,SOURCE)) msg += " source"; if (rhsMask(field,PRESCRIBED_SOURCE)) msg += " prescribed_src"; if (rhsMask(field,ROBIN_SOURCE)) msg += " robin_src"; + if (rhsMask(field,OPEN_SOURCE)) msg += " open_src"; if (rhsMask(field,EXTRINSIC_SOURCE)) msg += " extrinsic_src"; } } return msg; } } #endif diff --git a/lib/atc/AtomicRegulator.cpp b/lib/atc/AtomicRegulator.cpp index ec04b8556..9d329852a 100644 --- a/lib/atc/AtomicRegulator.cpp +++ b/lib/atc/AtomicRegulator.cpp @@ -1,987 +1,990 @@ // ATC Headers #include "AtomicRegulator.h" #include "ATC_Error.h" #include "ATC_Coupling.h" #include "PrescribedDataManager.h" #include "TimeIntegrator.h" #include "LinearSolver.h" using std::map; using std::string; using std::set; using std::pair; 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<string, pair<bool,DENS_MAN * > >::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<string, pair<bool,DENS_MAN * > >::iterator it = regulatorData_.find(tag); if (it == regulatorData_.end()) { data = new DENS_MAN(nNodes_,nCols); regulatorData_.insert(pair<string, pair<bool,DENS_MAN * > >(tag,pair<bool,DENS_MAN * >(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<string, pair<bool,DENS_MAN * > >::const_iterator it = regulatorData_.find(tag); if (it == regulatorData_.end()) { return NULL; } else { return const_cast<DENS_MAN * >((it->second).second); } } //-------------------------------------------------------- // set_all_data_to_unused: // sets bool such that all data is unused //-------------------------------------------------------- void AtomicRegulator::set_all_data_to_unused() { map<string, pair<bool,DENS_MAN * > >::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<string, pair<bool,DENS_MAN * > >::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 <physics_type> <solution_parameter> <value>\n - physics_type (string) = thermal | momentum\n - solution_parameter (string) = max_iterations | tolerance\n fix_modify AtC transfer <physics_type> control max_iterations <max_iterations>\n - max_iterations (int) = maximum number of iterations that will be used by iterative matrix solvers\n fix_modify AtC transfer <physics_type> control tolerance <tolerance> \n - tolerance (float) = relative tolerance to which matrix equations will be solved\n \section examples <TT> fix_modify AtC control thermal max_iterations 10 </TT> \n <TT> fix_modify AtC control momentum tolerance 1.e-5 </TT> \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 <on|off> \section examples <TT> fix_modify atc control localized_lambda on </TT> \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 <on|off> \section examples <TT> fix_modify atc control lumped_lambda_solve on </TT> \n \section description Command to 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 <direction> <on|off> \section examples <TT> fix_modify atc control mask_direction 0 on </TT> \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<int> & elementToMaterialMap, const MatrixDependencyManager<DenseMatrix, int> * 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<int> & 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<int> & 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<string, pair<bool,DENS_MAN * > >::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<NodeToSubset * >(interscaleManager.dense_matrix_int("NodeToOverlapMap")); + nodeToOverlapMap_ = static_cast<NodeToSubset * >(interscaleManager.dense_matrix_int(regulatorPrefix_+"NodeToOverlapMap")); if (!nodeToOverlapMap_) { nodeToOverlapMap_ = new NodeToSubset(atc_,regulatedNodes_); interscaleManager.add_dense_matrix_int(nodeToOverlapMap_, - regulatorPrefix_+"NodeToOverlapMap"); + regulatorPrefix_+"NodeToOverlapMap"); } - overlapToNodeMap_ = static_cast<SubsetToNode * >(interscaleManager.dense_matrix_int("OverlapToNodeMap")); + overlapToNodeMap_ = static_cast<SubsetToNode * >(interscaleManager.dense_matrix_int(regulatorPrefix_+"OverlapToNodeMap")); if (!overlapToNodeMap_) { overlapToNodeMap_ = new SubsetToNode(nodeToOverlapMap_); interscaleManager.add_dense_matrix_int(overlapToNodeMap_, - regulatorPrefix_+"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<double> * 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 VectorDependencyManager<SPAR_MAT * > * interpolantGradient = interscaleManager.vector_sparse_matrix("InterpolantGradient"); if (!interpolantGradient) { interpolantGradient = new PerAtomShapeFunctionGradient(atc_); interscaleManager.add_vector_sparse_matrix(interpolantGradient, "InterpolantGradient"); } shpFcnDerivs_ = new RowMappedSparseMatrixVector(atc_, 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(); } //-------------------------------------------------------- // 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<int> & elementToMaterialMap, const MatrixDependencyManager<DenseMatrix, int> * 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<RegulatedNodes *>(interscaleManager.set_int("RegulatedNodes")); + regulatedNodes_ = 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"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + 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) : + LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const SetDependencyManager<int> * 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<int> & applicationNodes(applicationNodes_->quantity()); const INT_ARRAY & nodeToOverlapMap(nodeToOverlapMap_->quantity()); lambda = 0.; set<int>::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 index eadfce66d..5d1e59f60 100644 --- a/lib/atc/AtomicRegulator.h +++ b/lib/atc/AtomicRegulator.h @@ -1,625 +1,631 @@ /** Atomic Regulator : a base class class for atom-continuum control */ #ifndef ATOMICREGULATOR_H #define ATOMICREGULATOR_H #include "ATC_TypeDefs.h" #include <map> #include <set> #include <vector> #include <utility> #include <string> 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 <typename T> class PerAtomQuantity; template <typename T> class ProtectedAtomQuantity; template <typename T> 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 std::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<int> & elementToMaterialMap, const MatrixDependencyManager<DenseMatrix, int> * 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 std::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 std::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 std::set< std::pair<int,int> > * 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 std::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 */ std::map<std::string, std::pair<bool,DENS_MAN * > > 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 */ std::vector<bool> applyInDirection_; // method pointers /** time filtering object */ TimeFilter * timeFilter_; /** sets up and solves the regulator equations */ RegulatorMethod * regulatorMethod_; // boundary flux information BoundaryIntegrationType boundaryIntegrationType_; const std::set< std::pair<int,int> > * boundaryFaceSet_; /** prefix string for registering data */ const std::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 std::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<int> & elementToMaterialMap, const MatrixDependencyManager<DenseMatrix, int> * 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<bool> fieldMask_; /** number of nodes */ int nNodes_; /** prefix string for registering data */ const std::string regulatorPrefix_; /** mapping for atom materials for atomic FE quadrature */ Array<std::set<int> > atomMaterialGroups_; /** shape function derivative matrices for boundary atoms */ VectorDependencyManager<SPAR_MAT * > * 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 std::string & regulatorPrefix = ""); virtual ~RegulatorShapeFunction(); /** instantiate all needed data */ virtual void construct_transfers(); /** pre-"run" initialization */ virtual void initialize(); + /** sets lambda to an initial value */ + virtual void set_lambda_to_value(double value) {*lambda_ = value;} + /** reset number of local atoms, as well as atomic data */ virtual void reset_nlocal(); /** set up atom to material identification */ virtual void reset_atom_materials(const Array<int> & elementToMaterialMap, const MatrixDependencyManager<DenseMatrix, int> * atomElement); /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields); + /** access to nodes the thermostat is applied to */ + const SetDependencyManager<int> * application_nodes() const {return applicationNodes_;}; + /** 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<double> * atomLambdas_; + PerAtomQuantity<double> * atomLambdas_; /** shape function matrix for use in GLC solve */ PerAtomSparseMatrix<double> * 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_; + SetDependencyManager<int> * regulatedNodes_; /** set of nodes on which lambda is non-zero */ - RegulatedNodes * applicationNodes_; + SetDependencyManager<int> * applicationNodes_; /** set of nodes needed for localized boundary quadrature */ - RegulatedNodes * boundaryNodes_; + SetDependencyManager<int> * 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_; + MatrixDependencyManager<ATC_matrix::DenseMatrix, bool> * elementMask_; /** maps atoms from atc indexing to regulator indexing */ LargeToSmallAtomMap * lambdaAtomMap_; /** weight per-atom transfer */ PerAtomQuantity<double> * 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); + LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const SetDependencyManager<int> * 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_; + const SetDependencyManager<int> * 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/DenseMatrix.h b/lib/atc/DenseMatrix.h index 39e267081..e77e2ac85 100644 --- a/lib/atc/DenseMatrix.h +++ b/lib/atc/DenseMatrix.h @@ -1,417 +1,437 @@ #ifndef DENSEMATRIX_H #define DENSEMATRIX_H #include "Matrix.h" #include <iostream> namespace ATC_matrix { /** * @class DenseMatrix * @brief Class for storing data in a "dense" matrix form */ template <typename T> class DenseMatrix : public Matrix<T> { public: - DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1) { _create(rows, cols, z); } + DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1): _data(NULL){ _create(rows, cols, z); } DenseMatrix(const DenseMatrix<T>& c) : _data(NULL){ _copy(c); } DenseMatrix(const SparseMatrix<T>& c): _data(NULL){ c.dense_copy(*this);} DenseMatrix(const Matrix<T>& c) : _data(NULL){ _copy(c); } // const SparseMatrix<T> * p = sparse_cast(&c); // (p) ? p->dense_copy(*this) : _copy(c); } ~DenseMatrix() { _delete();} void reset (INDEX rows, INDEX cols, bool zero=true); + void reset (const DenseMatrix<T>& c) {_delete(); _copy(c); }; + void reset (const SparseMatrix<T> & c) {_delete(); c.dense_copy(*this);}; + void reset (const Matrix<T>& c) {_delete(); _copy(c); } void resize(INDEX rows, INDEX cols, bool copy=false); void copy (const T * ptr, INDEX rows, INDEX cols); /** returns transpose(this) * B */ DenseMatrix<T> transMat(const DenseMatrix<T>& B) const; /** returns by element multiply A_ij = this_ij * B_ij */ DenseMatrix<T> mult_by_element(const DenseMatrix<T>& B) const; /** returns by element multiply A_ij = this_ij / B_ij */ DenseMatrix<T> div_by_element(const DenseMatrix<T>& 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(std::string & name); void set_all_elements_to(const T &v); DiagonalMatrix<T> diag() const; DenseMatrix<T>& operator=(const T &v); DenseMatrix<T>& operator=(const Matrix<T> &c); DenseMatrix<T>& operator=(const DenseMatrix<T> &c); DenseMatrix<T>& operator=(const SparseMatrix<T> &c); //* checks if all values are within the prescribed range virtual bool check_range(T min, T max) const; protected: + void _set_equal(const Matrix<T> &r); void _delete(); void _create(INDEX rows, INDEX cols, bool zero=false); void _copy(const Matrix<T> &c); T *_data; INDEX _nRows, _nCols; }; //! Computes the cofactor matrix of A. template<typename T> DenseMatrix<T> adjugate(const Matrix<T> &A, bool symmetric=false); //! Returns a the tensor product of two vectors template<typename T> DenseMatrix<T> tensor_product(const Vector<T> &a, const Vector<T> &b); //---------------------------------------------------------------------------- // Returns an identity matrix, defaults to 3x3. //---------------------------------------------------------------------------- template<typename T> DenseMatrix<T> 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<cols; j++) for (INDEX i=0; i<rows; i++) I(i,j) = dij[i==j]; return I; } //---------------------------------------------------------------------------- // resizes the matrix and optionally zeros it out (default - zero) //---------------------------------------------------------------------------- template <typename T> void DenseMatrix<T>::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 <typename T> void DenseMatrix<T>::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<T> 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 <typename T> void DenseMatrix<T>::copy(const T * ptr, INDEX rows, INDEX cols) { resize(rows, cols, false); memcpy(_data, ptr, this->size()*sizeof(T)); } //---------------------------------------------------------------------------- // returns transpose(this) * B //---------------------------------------------------------------------------- template <typename T> DenseMatrix<T> DenseMatrix<T>::transMat(const DenseMatrix<T>& B) const { DenseMatrix C; MultAB(*this, B, C, true); return C; } //---------------------------------------------------------------------------- // returns this_ij * B_ij //---------------------------------------------------------------------------- template <typename T> DenseMatrix<T> DenseMatrix<T>::mult_by_element(const DenseMatrix<T>& 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) { std::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 <typename T> DenseMatrix<T> DenseMatrix<T>::div_by_element(const DenseMatrix<T>& 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 <typename T> void DenseMatrix<T>::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 <typename T> void DenseMatrix<T>::from_file(std::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 <typename T> inline void DenseMatrix<T>::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<typename T> DiagonalMatrix<T> DenseMatrix<T>::diag() const { DiagonalMatrix<T> D(nRows(), true); // initialized to zero INDEX i; for (i=0; i<nRows(); i++) { D(i,i) = DATA(i,i); } return D; } //---------------------------------------------------------------------------- // clears allocated memory //---------------------------------------------------------------------------- template <typename T> void DenseMatrix<T>::_delete() { _nRows = _nCols = 0; if (_data){ delete [] _data; + _data = NULL; } } //---------------------------------------------------------------------------- // allocates memory for an rows by cols DenseMatrix //---------------------------------------------------------------------------- template <typename T> void DenseMatrix<T>::_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 <typename T> void DenseMatrix<T>::_copy(const Matrix<T> &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 <typename T> DenseMatrix<T>& DenseMatrix<T>::operator=(const T &v) { this->set_all_elements_to(v); return *this; } //---------------------------------------------------------------------------- // copys c with a deep copy //---------------------------------------------------------------------------- template <typename T> DenseMatrix<T>& DenseMatrix<T>::operator=(const Matrix<T> &c) { _copy(c); return *this; } //---------------------------------------------------------------------------- // copys c with a deep copy //---------------------------------------------------------------------------- template <typename T> DenseMatrix<T>& DenseMatrix<T>::operator=(const DenseMatrix<T> &c) { _copy(c); return *this; } //----------------------------------------------------------------------------- // copys c with a deep copy, including zeros //----------------------------------------------------------------------------- template <typename T> DenseMatrix<T>& DenseMatrix<T>::operator=(const SparseMatrix<T> &c) { _delete(); _create(c.nRows(), c.nCols(), true); SparseMatrix<T>::compress(c); for (INDEX i=0; i<c.size(); i++) { TRIPLET<T> x = c.triplet(i); std::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; } - +//---------------------------------------------------------------------------- +// general matrix assignment (for densely packed matrices) +//---------------------------------------------------------------------------- +template<typename T> +void DenseMatrix<T>::_set_equal(const Matrix<T> &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix<T> *pr = &r; + const DenseMatrix<T> *pdd = dynamic_cast<const DenseMatrix<T>*> (pr); + if (pdd) this->reset(*pdd); + else + { + std::cout <<"Error in general dense matrix assignment\n"; + exit(1); + } +} //* Returns the transpose of the cofactor matrix of A. //* see http://en.wikipedia.org/wiki/Adjugate_matrix //* symmetric flag only affects cases N>3 template<typename T> DenseMatrix<T> adjugate(const Matrix<T> &A, bool symmetric) { if (!A.is_square()) gerror("adjugate can only be computed for square matrices."); DenseMatrix<T> 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<T> m(A.nRows()-1, A.nRows()-1); double sign[] = {1.0, -1.0}; for (INDEX j=0; j<A.nCols(); j++) { for (INDEX i=0; i<A.nRows(); i++) { for (INDEX mj=0; mj<m.nCols(); mj++) { for (INDEX mi=0; mi<m.nRows(); mi++) { m(mi, mj) = A(mi+(mi>=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<typename T> DenseMatrix<T> tensor_product(const Vector<T> &a, const Vector<T> &b) { DenseMatrix<T> ab(a.size(), b.size(),false); for (INDEX j=0; j<b.size(); j++) for (INDEX i=0; i<a.size(); i++) ab(i,j) = a[i]*b[j]; return ab; } //* Returns a DenseMatrix with random values (like matlab rand(m,n) template<typename T> DenseMatrix<T> rand(INDEX rows, INDEX cols, int seed=1234) { srand(seed); const double rand_max_inv = 1.0 / double(RAND_MAX); DenseMatrix<T> R(rows, cols, false); for (INDEX i=0; i<R.size(); i++) R[i]=double(::rand())*rand_max_inv; return R; } //----------------------------------------------------------------------------- //* returns true if no value is outside of the range template<typename T> inline bool DenseMatrix<T>::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/DiagonalMatrix.h b/lib/atc/DiagonalMatrix.h index 2ec096cb5..ce51c3f36 100644 --- a/lib/atc/DiagonalMatrix.h +++ b/lib/atc/DiagonalMatrix.h @@ -1,506 +1,506 @@ #ifndef DIAGONALMATRIX_H #define DIAGONALMATRIX_H #include "MatrixDef.h" namespace ATC_matrix { /** * @class DiagonalMatrix * @brief Class for storing data as a diagonal matrix */ template<typename T> class DiagonalMatrix : public Matrix<T> { public: explicit DiagonalMatrix(INDEX nRows=0, bool zero=0); DiagonalMatrix(const DiagonalMatrix<T>& c); DiagonalMatrix(const Vector<T>& v); virtual ~DiagonalMatrix(); //* resizes the matrix, ignores nCols, optionally zeros void reset(INDEX rows, INDEX cols=0, bool zero=true); //* resizes the matrix, ignores nCols, optionally copies what fits void resize(INDEX rows, INDEX cols=0, bool copy=false); //* resets based on full copy of vector v void reset(const Vector<T>& v); //* resets based on full copy of a DiagonalMatrix void reset(const DiagonalMatrix<T>& v); //* resets based on a one column DenseMatrix void reset(const DenseMatrix<T>& c); //* resizes the matrix, ignores nCols, optionally copies what fits void copy(const T * ptr, INDEX rows, INDEX cols=0); //* resets based on a "shallow" copy of a vector void shallowreset(const Vector<T> &v); //* resets based on a "shallow" copy of a DiagonalMatrix void shallowreset(const DiagonalMatrix<T> &c); //* resets based on a "shallow" copy of one column DenseMatrix void shallowreset(const DenseMatrix<T> &c); T& operator()(INDEX i, INDEX j); T operator()(INDEX i, INDEX j) const; T& operator[](INDEX i); T operator[](INDEX i) const; INDEX nRows() const; INDEX nCols() const; T* ptr() const; void write_restart(FILE *f) const; // Dump matrix contents to screen (not defined for all datatypes) - std::string to_string() const { return _data->to_string(); } + std::string to_string(int p=myPrecision) const { return _data->to_string(); } using Matrix<T>::matlab; void matlab(std::ostream &o, const std::string &s="D") const; // overloaded operators DiagonalMatrix<T>& operator=(const T s); DiagonalMatrix<T>& operator=(const DiagonalMatrix<T> &C); //DiagonalMatrix<T>& operator=(const Vector<T> &C); INDEX size() const { return _data->size(); } //* computes the inverse of this matrix DiagonalMatrix<T>& inv_this(); //* returns a copy of the inverse of this matrix DiagonalMatrix<T> inv() const; // DiagonalMatrix-matrix multiplication function virtual void MultAB(const Matrix<T>& B, DenseMatrix<T>& C) const { GCK(*this, B, this->nCols()!=B.nRows(), "DiagonalMatrix-Matrix multiplication"); for (INDEX i=0; i<C.nRows(); i++) { T value = (*this)[i]; for (INDEX j=0; j<C.nCols(); j++) C(i,j) = B(i,j) * value; } } protected: void _set_equal(const Matrix<T> &r); DiagonalMatrix& operator=(const Vector<T> &c) {} DiagonalMatrix& operator=(const Matrix<T> &c) {} private: void _delete(); Vector<T> *_data; }; //----------------------------------------------------------------------------- // DiagonalMatrix-DiagonalMatrix multiplication //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> operator*(const DiagonalMatrix<T>& A, const DiagonalMatrix<T>& B) { SSCK(A, B, "DiagonalMatrix-DiagonalMatrix multiplication"); DiagonalMatrix<T> R(A); for (INDEX i=0; i<R.nRows(); i++) R[i] *= B[i]; return R; } //----------------------------------------------------------------------------- // DiagonalMatrix-matrix multiplication //----------------------------------------------------------------------------- template<typename T> DenseMatrix<T> operator*(const DiagonalMatrix<T>& A, const Matrix<T> &B) { DenseMatrix<T> C(A.nRows(), B.nCols(), true); A.MultAB(B, C); return C; } //----------------------------------------------------------------------------- // matrix-DiagonalMatrix multiplication //----------------------------------------------------------------------------- template<typename T> DenseMatrix<T> operator*(const Matrix<T> &B, const DiagonalMatrix<T>& A) { GCK(B, A, B.nCols()!=A.nRows(), "Matrix-DiagonalMatrix multiplication"); DenseMatrix<T> R(B); // makes a copy of r to return for (INDEX j=0; j<R.nCols(); j++) for (INDEX i=0; i<R.nRows(); i++) R(i,j) *= A[j]; return R; } //----------------------------------------------------------------------------- // DiagonalMatrix-vector multiplication //----------------------------------------------------------------------------- template<typename T> DenseVector<T> operator*(const DiagonalMatrix<T>& A, const Vector<T> &b) { GCK(A, b, A.nCols()!=b.size(), "DiagonalMatrix-Vector multiplication"); DenseVector<T> r(b); // makes a copy of r to return for (INDEX i=0; i<r.size(); i++) r[i] *= A[i]; return r; } //----------------------------------------------------------------------------- // vector-DiagonalMatrix multiplication //----------------------------------------------------------------------------- template<typename T> DenseVector<T> operator*(const Vector<T> &b, const DiagonalMatrix<T>& A) { GCK(b, A, b.size()!=A.nRows(), "Matrix-DiagonalMatrix multiplication"); DenseVector<T> r(b); // makes a copy of r to return for (INDEX i=0; i<r.size(); i++) r[i] *= A[i]; return r; } //----------------------------------------------------------------------------- // DiagonalMatrix-SparseMatrix multiplication //----------------------------------------------------------------------------- template<typename T> SparseMatrix<T> operator*(const DiagonalMatrix<T> &A, const SparseMatrix<T>& B) { GCK(A, B, A.nCols()!=B.nRows() ,"DiagonalMatrix-SparseMatrix multiplication"); SparseMatrix<T> R(B); CloneVector<T> d(A); R.row_scale(d); return R; } //----------------------------------------------------------------------------- // DiagonalMatrix-scalar multiplication //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> operator*(DiagonalMatrix<T> &A, const T s) { DiagonalMatrix<T> R(A); R *= s; return R; } //----------------------------------------------------------------------------- // Commute with DiagonalMatrix * double //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> operator*(const T s, const DiagonalMatrix<T>& A) { DiagonalMatrix<T> R(A); R *= s; return R; } //----------------------------------------------------------------------------- // DiagonalMatrix addition //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> operator+(const DiagonalMatrix<T> &A, const DiagonalMatrix<T> &B) { DiagonalMatrix<T> R(A); R+=B; return R; } //----------------------------------------------------------------------------- // DiagonalMatrix subtraction //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> operator-(const DiagonalMatrix<T> &A, const DiagonalMatrix<T> &B) { DiagonalMatrix<T> R(A); return R-=B; } //----------------------------------------------------------------------------- // template member definitions //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Default constructor - optionally zeros the matrix //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>::DiagonalMatrix(INDEX rows, bool zero) : _data(NULL) { reset(rows, zero); } //----------------------------------------------------------------------------- // copy constructor - makes a full copy //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>::DiagonalMatrix(const DiagonalMatrix<T>& c) : _data(NULL) { reset(c); } //----------------------------------------------------------------------------- // copy constructor from vector //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>::DiagonalMatrix(const Vector<T>& v) : _data(NULL) { reset(v); } //----------------------------------------------------------------------------- // destructor //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>::~DiagonalMatrix() { _delete(); } //----------------------------------------------------------------------------- // deletes the data stored by this matrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::_delete() { if (_data) delete _data; } //----------------------------------------------------------------------------- // resizes the matrix, ignores nCols, optionally zeros //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::reset(INDEX rows, INDEX cols, bool zero) { _delete(); _data = new DenseVector<T>(rows, zero); } //----------------------------------------------------------------------------- // resizes the matrix, ignores nCols, optionally copies what fits //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::resize(INDEX rows, INDEX cols, bool copy) { _data->resize(rows, copy); } //----------------------------------------------------------------------------- // changes the diagonal of the matrix to a vector v (makes a copy) //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::reset(const Vector<T>& v) { if (&v == _data) return; // check for self-reset _delete(); _data = new DenseVector<T>(v); } //----------------------------------------------------------------------------- // copys from another DiagonalMatrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::reset(const DiagonalMatrix<T>& c) { reset(*(c._data)); } //----------------------------------------------------------------------------- // copys from a single column matrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::reset(const DenseMatrix<T>& c) { GCHK(c.nCols()!=1,"DiagonalMatrix reset from DenseMatrix"); copy(c.ptr(),c.nRows(),c.nRows()); } //----------------------------------------------------------------------------- // resizes the matrix and copies data //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::copy(const T * ptr, INDEX rows, INDEX cols) { if (_data) _data->reset(rows, false); else _data = new DenseVector<T>(rows, false); _data->copy(ptr,rows,cols); } //----------------------------------------------------------------------------- // shallow reset from another DiagonalMatrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::shallowreset(const DiagonalMatrix<T> &c) { _delete(); _data = new CloneVector<T>(*(c._data)); } //----------------------------------------------------------------------------- // shallow reset from Vector //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::shallowreset(const Vector<T> &v) { _delete(); _data = new CloneVector<T>(v); } //----------------------------------------------------------------------------- // shallow reset from a DenseMatrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::shallowreset(const DenseMatrix<T> &c) { _delete(); _data = new CloneVector<T>(c,CLONE_COL); } //----------------------------------------------------------------------------- // reference indexing operator - must throw an error if i!=j //----------------------------------------------------------------------------- template<typename T> T& DiagonalMatrix<T>::operator()(INDEX i, INDEX j) { GCK(*this,*this,i!=j,"DiagonalMatrix: tried to index off diagonal"); return (*this)[i]; } //----------------------------------------------------------------------------- // value indexing operator - returns 0 if i!=j //----------------------------------------------------------------------------- template<typename T> T DiagonalMatrix<T>::operator()(INDEX i, INDEX j) const { return (i==j) ? (*_data)(i) : (T)0; } //----------------------------------------------------------------------------- // flat reference indexing operator //----------------------------------------------------------------------------- template<typename T> T& DiagonalMatrix<T>::operator[](INDEX i) { return (*_data)(i); } //----------------------------------------------------------------------------- // flat value indexing operator //----------------------------------------------------------------------------- template<typename T> T DiagonalMatrix<T>::operator[](INDEX i) const { return (*_data)(i); } //----------------------------------------------------------------------------- // returns the number of rows //----------------------------------------------------------------------------- template<typename T> INDEX DiagonalMatrix<T>::nRows() const { return _data->size(); } //----------------------------------------------------------------------------- // returns the number of columns (same as nCols()) //----------------------------------------------------------------------------- template<typename T> INDEX DiagonalMatrix<T>::nCols() const { return _data->size(); } //----------------------------------------------------------------------------- // returns a pointer to the diagonal values, dangerous! //----------------------------------------------------------------------------- template<typename T> T* DiagonalMatrix<T>::ptr() const { return _data->ptr(); } //----------------------------------------------------------------------------- // writes the diagonal to a binary data restart file //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::write_restart(FILE *f) const { _data->write_restart(f); } //----------------------------------------------------------------------------- // sets the diagonal to a constant //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>& DiagonalMatrix<T>::operator=(const T v) { this->set_all_elements_to(v); return *this; } //----------------------------------------------------------------------------- // assignment operator with another diagonal matrix //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>& DiagonalMatrix<T>::operator=(const DiagonalMatrix<T>& C) { reset(C); return *this; } //----------------------------------------------------------------------------- // writes a matlab command to duplicate this sparse matrix //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::matlab(std::ostream &o, const std::string &s) const { _data->matlab(o, s); o << s <<"=diag("<<s<<",0);\n"; } //----------------------------------------------------------------------------- // inverts this matrix, returns a reference //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T>& DiagonalMatrix<T>::inv_this() { for(INDEX i=0; i<nRows(); i++) { if ((*this)[i]!=T(0)) (*this)[i] = 1.0/(*this)[i]; else { std::cout<<"DiagonalMatrix::inv(): ("<<i<<","<<i<<")=0\n"; ERROR_FOR_BACKTRACE exit(EXIT_FAILURE); } } // Error check info const double min_max = _data->minabs() / _data->maxabs(); if (min_max > 1e-14) return *this; std::cout << "DiagonalMatrix::inv_this(): Warning: Matrix is badly scaled."; std::cout << " RCOND = "<<min_max<<"\n"; return *this; } //----------------------------------------------------------------------------- // returns the inverse of this matrix //----------------------------------------------------------------------------- template<typename T> DiagonalMatrix<T> DiagonalMatrix<T>::inv() const { DiagonalMatrix<T> invA(*this); // Make copy of A to invert for(INDEX i=0; i<invA.nRows(); i++) { if ((*this)[i]!=T(0)) invA[i]=1.0/(*this)[i]; else { std::cout<<"DiagonalMatrix::inv(): ("<<i<<","<<i<<")=0\n"; ERROR_FOR_BACKTRACE exit(EXIT_FAILURE); } } // Error check info const double min_max = _data->minabs() / _data->maxabs(); if (min_max > 1e-14) return invA; std::cout << "DiagonalMatrix::inv(): Warning: Matrix is badly scaled."; std::cout << " RCOND = "<<min_max<<"\n"; return invA; } //----------------------------------------------------------------------------- // computes a matrix inverse //----------------------------------------------------------------------------- inline DiagonalMatrix<double> inv(const DiagonalMatrix<double>& A) { return A.inv(); } //----------------------------------------------------------------------------- // general diagonalmatrix assigment //----------------------------------------------------------------------------- template<typename T> void DiagonalMatrix<T>::_set_equal(const Matrix<T> &r) { this->resize(r.nRows(), r.nCols()); const Matrix<T> *pr = &r; const SparseMatrix<T> *ps = dynamic_cast<const SparseMatrix<T>*> (pr); const DiagonalMatrix<T> *pd = dynamic_cast<const DiagonalMatrix<T>*> (pr); const Vector<T> *pv = dynamic_cast<const Vector<T>*> (pr); if (ps) this->reset(ps->diag()); else if (pd) this->reset(*pd); else if (pv) this->reset(*pv); else { - std::cout <<"Error in general sparse matrix assignment\n"; + std::cout <<"Error in general diagonal matrix assignment\n"; exit(1); } } //----------------------------------------------------------------------------- // casts a generic matrix pointer into a DiagonalMatrix pointer - null if fail //----------------------------------------------------------------------------- template<typename T> const DiagonalMatrix<T> *diag_cast(const Matrix<T> *m) { return dynamic_cast<const DiagonalMatrix<T>*>(m); } } // end namespace #endif diff --git a/lib/atc/ElectronDragPower.cpp b/lib/atc/ElectronDragPower.cpp index b6bb45224..aff6be610 100644 --- a/lib/atc/ElectronDragPower.cpp +++ b/lib/atc/ElectronDragPower.cpp @@ -1,75 +1,77 @@ #include "ElectronDragPower.h" #include "Material.h" #include "ATC_Error.h" #include <iostream> #include <vector> using ATC_Utility::command_line; using ATC_Utility::str2dbl; using std::map; using std::string; using std::fstream; using std::vector; namespace ATC { ElectronDragPowerLinear::ElectronDragPowerLinear(fstream &fileId, map<string,double> & parameters, Material * material) : ElectronDragPower(), electronDragInvTau_(0), material_(material) { if (!fileId.is_open()) throw ATC_Error("cannot open material file"); vector<string> line; while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; double value = str2dbl(line[1]); if (line[0] == "inv_momentum_relaxation_time") { electronDragInvTau_ = value; parameters["inv_momentum_relaxation_time"] = electronDragInvTau_; } else { throw ATC_Error( "unrecognized material function "+line[0]); } } } bool ElectronDragPowerLinear::electron_drag_power(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT & flux) { FIELD_MATS::const_iterator evField = fields.find(ELECTRON_VELOCITY); const DENS_MAT & v = evField->second; // -1/tau (m_e * n * v) electron_drag_velocity_coefficient(fields,dragCoefWorkspace_); for (int i = 0; i < v.nRows(); i++) { double velocityMagnitude = 0.; for (int j = 0; j < v.nCols(); j++) velocityMagnitude -= v(i,j)*v(i,j); flux(i,0) += velocityMagnitude*dragCoefWorkspace_(i,0); // adds flux to phonon temperature } return true; } void ElectronDragPowerLinear::electron_drag_velocity_coefficient(const FIELD_MATS &fields, DENS_MAT & dragCoef) { FIELD_MATS::const_iterator enField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = enField->second; - + //n.print("DENS"); // -1/tau (m_e * n) material_->inv_effective_mass(fields,invEffMassWorkspace_); + //invEffMassWorkspace_.print("INV MASS"); dragCoef = n; dragCoef /= invEffMassWorkspace_; dragCoef *= -electronDragInvTau_; + //dragCoef.print("DRAG COEF"); } } diff --git a/lib/atc/ElectronFlux.h b/lib/atc/ElectronFlux.h index c40ea475b..013f9aac4 100644 --- a/lib/atc/ElectronFlux.h +++ b/lib/atc/ElectronFlux.h @@ -1,224 +1,225 @@ #ifndef ELECTRON_FLUX_H #define ELECTRON_FLUX_H #include <map> #include <string> #include "ATC_TypeDefs.h" namespace ATC { /** * @class ElectronFlux * @brief Base class for the flux appearing in the electron density transport equation */ class ElectronFlux { public: ElectronFlux(); virtual ~ElectronFlux() {}; /** computes flux */ virtual void electron_flux(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) { FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE); const DENS_MAT & etMat = etField->second; zeroWorkspace_.reset(etMat.nRows(),etMat.nCols()); flux[0] = zeroWorkspace_; flux[1] = zeroWorkspace_; flux[2] = zeroWorkspace_; }; void electron_convection(const FIELD_MATS &fields, DENS_MAT_VEC &flux) { FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY); FIELD_MATS::const_iterator evField = fields.find(ELECTRON_VELOCITY); const DENS_MAT & n = edField->second; const DENS_MAT & v = evField->second; const CLON_VEC vx(v,CLONE_COL,0); const CLON_VEC vy(v,CLONE_COL,1); const CLON_VEC vz(v,CLONE_COL,2); zeroWorkspace_.reset(v.nRows(),1); if (maskX_) { flux[0] = zeroWorkspace_; } else { flux[0] = vx; flux[0] *= n; // scale by n } if (maskY_) { flux[1] = zeroWorkspace_; } else { flux[1] = vy; flux[1] *= n; } if (maskZ_) { flux[2] = zeroWorkspace_; } else { flux[2] = vz; flux[2] *= n; } }; protected: bool maskX_, maskY_, maskZ_; DENS_MAT zeroWorkspace_; }; //----------------------------------------------------------------------- /** * @class ElectronFluxLinear * @brief Class for drift-diffusion electron flux with linear dependency on the electron density gradient */ class ElectronFluxLinear : public ElectronFlux { public: ElectronFluxLinear(std::fstream &matfile, std::map<std::string,double> & parameters); virtual ~ElectronFluxLinear() {}; virtual void electron_flux(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) { FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY); GRAD_FIELD_MATS::const_iterator dEdField = gradFields.find(ELECTRON_DENSITY); GRAD_FIELD_MATS::const_iterator dPhiField = gradFields.find(ELECTRIC_POTENTIAL); - // J_n = - \mu n grad \phi - D grad n + // J_n = - \mu n E - D grad n + // note electrons move counter to electric field grad E = - grad \phi const DENS_MAT & n = edField->second; const DENS_MAT_VEC & dn = dEdField->second; const DENS_MAT_VEC & dphi = dPhiField->second; //n.print("DENSITY"); //for (int i = 0; i < 3; i++) { // dn[i].print("GRAD N"); // dphi[i].print("GRAD PHI"); //} //cout << "------------------------------------------------====\n"; flux[0] = n; flux[1] = n; flux[2] = n; if (maskX_) flux[0] = 0.; else { - flux[0] *= -electronMobility_*dphi[0]; // scale by n to get : -n \mu grad(\phi) + flux[0] *= electronMobility_*dphi[0]; // scale by n to get : n \mu grad(\phi) flux[0] += -electronDiffusivity_* dn[0]; } if (maskY_) flux[1] = 0.; else { - flux[1] *= -electronMobility_* dphi[1] ; + flux[1] *= electronMobility_* dphi[1] ; flux[1] += -electronDiffusivity_* dn[1]; } if (maskZ_) flux[2] = 0.; else { - flux[2] *= -electronMobility_*dphi[2]; + flux[2] *= electronMobility_*dphi[2]; flux[2] += -electronDiffusivity_* dn[2]; } }; protected: double electronMobility_, electronDiffusivity_; }; //----------------------------------------------------------------------- /** * @class ElectronFluxThermopower * @brief Class for defining the electron flux (i.e., current) to include the elctron velocity or have a electron temperature-dependent mobility */ class ElectronFluxThermopower : public ElectronFlux { public: ElectronFluxThermopower(std::fstream &matfile,std::map<std::string,double> & parameters); virtual ~ElectronFluxThermopower() {}; virtual void electron_flux(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) { if (fields.find(ELECTRON_VELOCITY)!=fields.end()) { // J_n = - e n v, note the electron charge e is unity electron_convection(fields,flux); flux[0] *= -1; flux[1] *= -1; flux[2] *= -1; } else { FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY); FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE); GRAD_FIELD_MATS::const_iterator dEdField = gradFields.find(ELECTRON_VELOCITY); GRAD_FIELD_MATS::const_iterator dPhiField = gradFields.find(ELECTRIC_POTENTIAL); GRAD_FIELD_MATS::const_iterator dEtField = gradFields.find(ELECTRON_TEMPERATURE); // J_n = - \mu n grad \phi - \mu kB/e T_e grad n // - \mu S n grad T_e - \mu kB/e n grad T_e const DENS_MAT & n = edField->second; const DENS_MAT_VEC & dn = dEdField->second; const DENS_MAT_VEC & dphi = dPhiField->second; const DENS_MAT_VEC & dT = dEtField->second; flux[0] = -electronMobility_*dphi[0]; flux[1] = -electronMobility_*dphi[1]; flux[2] = -electronMobility_*dphi[2]; double coef = -electronMobility_*(seebeckCoef_ + kBeV_); flux[0] += coef* dT[0]; flux[1] += coef* dT[1]; flux[2] += coef* dT[2]; flux[0] *= n; // scale by n flux[1] *= n; flux[2] *= n; //GRAD_FIELD tmp = dn; const DENS_MAT & Te = etField->second; //tmp[0] *= Te; //tmp[1] *= Te; //tmp[2] *= Te; coef = -electronMobility_*kBeV_; //flux[0] += tmp[0]; flux[0] += dn[0].mult_by_element(Te); flux[1] += dn[1].mult_by_element(Te); flux[2] += dn[2].mult_by_element(Te); } }; protected: double electronMobility_, seebeckCoef_; }; //----------------------------------------------------------------------- /** * @class ElectronFluxConvection * @brief Class for electron flux based on the standard convection term */ class ElectronFluxConvection : public ElectronFlux { public: ElectronFluxConvection(std::fstream &matfile,std::map<std::string,double> & parameters); virtual ~ElectronFluxConvection() {}; virtual void electron_flux(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) { // flux = n v electron_convection(fields,flux); }; }; } #endif diff --git a/lib/atc/ExtrinsicModelDriftDiffusion.cpp b/lib/atc/ExtrinsicModelDriftDiffusion.cpp index f98467703..73beca3aa 100644 --- a/lib/atc/ExtrinsicModelDriftDiffusion.cpp +++ b/lib/atc/ExtrinsicModelDriftDiffusion.cpp @@ -1,558 +1,606 @@ // ATC Headers #include "ExtrinsicModelDriftDiffusion.h" #include "ATC_Error.h" #include "FieldEulerIntegrator.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PrescribedDataManager.h" #include "PhysicsModel.h" #include "LinearSolver.h" #include "PoissonSolver.h" #include "SchrodingerSolver.h" // timer #include "Utility.h" #include <utility> using ATC_Utility::to_string; +using std::cout; using std::string; using std::set; using std::pair; using std::vector; const double tol = 1.e-8; const double zero_tol = 1.e-12; const double f_tol = 1.e-8; namespace ATC { -enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; - //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusion //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusion::ExtrinsicModelDriftDiffusion (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModelTwoTemperature(modelManager,modelType,matFileName), continuityIntegrator_(NULL), poissonSolverType_(DIRECT), // ITERATIVE | DIRECT poissonSolver_(NULL), baseSize_(0), electronDensityEqn_(ELECTRON_CONTINUITY), fluxUpdateFreq_(1), schrodingerSolverType_(DIRECT), // ITERATIVE | DIRECT schrodingerSolver_(NULL), schrodingerPoissonMgr_(), schrodingerPoissonSolver_(NULL), maxConsistencyIter_(0), maxConstraintIter_(1), safe_dEf_(0.1), Ef_shift_(0.0), - oneD_(false), oneDcoor_(0), oneDconserve_(ONED_DENSITY) + oneD_(false), oneDcoor_(0), oneDconserve_(0) { // delete base class's version of the physics model if (physicsModel_) delete physicsModel_; if (modelType == DRIFT_DIFFUSION_EQUILIBRIUM) { physicsModel_ = new PhysicsModelDriftDiffusionEquilibrium(matFileName); electronDensityEqn_ = ELECTRON_EQUILIBRIUM; } else if (modelType == DRIFT_DIFFUSION_SCHRODINGER) { physicsModel_ = new PhysicsModelDriftDiffusionSchrodinger(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; maxConsistencyIter_ = 1; } else if (modelType == DRIFT_DIFFUSION_SCHRODINGER_SLICE) { physicsModel_ = new PhysicsModelDriftDiffusionSchrodingerSlice(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; maxConsistencyIter_ = 1; } else { physicsModel_ = new PhysicsModelDriftDiffusion(matFileName); } atc_->useConsistentMassMatrix_(ELECTRON_DENSITY) = true; + rhsMaskIntrinsic_(ELECTRON_TEMPERATURE,SOURCE) = true; + //atc_->fieldMask_(ELECTRON_TEMPERATURE,EXTRINSIC_SOURCE) = true; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusion::~ExtrinsicModelDriftDiffusion() { if(continuityIntegrator_) delete continuityIntegrator_; if(poissonSolver_) delete poissonSolver_; if(schrodingerSolver_) delete schrodingerSolver_; if(schrodingerPoissonSolver_) delete schrodingerPoissonSolver_; } //-------------------------------------------------------- // modify //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusion::modify(int narg, char **arg) { bool match = false; int argIndx = 0; if (!match) { match = ExtrinsicModelTwoTemperature::modify(narg, arg); } return match; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::initialize() { // xTTM sets rhsMaskIntrinsic_ ExtrinsicModelTwoTemperature::initialize(); nNodes_ = atc_->num_nodes(); rhs_[ELECTRON_DENSITY].reset(nNodes_,1); rhs_[ELECTRIC_POTENTIAL].reset(nNodes_,1); // set up electron continuity integrator Array2D <bool> rhsMask(NUM_TOTAL_FIELDS,NUM_FLUX); rhsMask = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRON_DENSITY,i) = atc_->fieldMask_(ELECTRON_DENSITY,i); } // need to create the bcs for the solver to configure properly atc_->set_fixed_nodes(); if (continuityIntegrator_) delete continuityIntegrator_; if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { continuityIntegrator_ = new FieldImplicitEulerIntegrator(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_, rhsMask); } else { continuityIntegrator_ = new FieldExplicitEulerIntegrator(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_, rhsMask); } atc_->compute_mass_matrix(ELECTRON_DENSITY,physicsModel_); //(atc_->consistentMassMats_[ELECTRON_DENSITY].quantity()).print("PHYS MASS MAT"); //DENS_MAT temp = atc_->consistentMassInverse_ - atc_->consistentMassMatInv_[ELECTRON_DENSITY]; //temp.print("DIFF In MATS"); // set up poisson solver rhsMask = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRIC_POTENTIAL,i) = atc_->fieldMask_(ELECTRIC_POTENTIAL,i); } int type = ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC; if (poissonSolverType_ == DIRECT) { type = ATC::LinearSolver::DIRECT_SOLVE; } if (poissonSolver_) delete poissonSolver_; poissonSolver_ = new PoissonSolver(ELECTRIC_POTENTIAL, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, rhsMask,type, true); poissonSolver_->initialize(); // set up schrodinger solver if ( electronDensityEqn_ == ELECTRON_SCHRODINGER ) { - int type = ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC; - if (schrodingerSolverType_ == DIRECT) { - type = ATC::LinearSolver::DIRECT_SOLVE; - } if ( schrodingerSolver_ ) delete schrodingerSolver_; if ( oneD_ ) { EfHistory_.reset(oneDslices_.size(),2); schrodingerSolver_ = new SliceSchrodingerSolver(ELECTRON_DENSITY, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, - oneDslices_, type, true); + oneDslices_,oneDdxs_); } else { schrodingerSolver_ = new SchrodingerSolver(ELECTRON_DENSITY, - physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, - type, true); + physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_); } schrodingerSolver_->initialize(); if ( schrodingerPoissonSolver_ ) delete schrodingerPoissonSolver_; schrodingerPoissonSolver_ = schrodingerPoissonMgr_.initialize( atc_, schrodingerSolver_, poissonSolver_, physicsModel_); } if (electronDensityEqn_ == ELECTRON_SCHRODINGER && !(atc_->is_initialized())) { ((atc_->fields())[ELECTRON_WAVEFUNCTION].set_quantity()).reset(nNodes_,1); ((atc_->fields())[ELECTRON_WAVEFUNCTIONS].set_quantity()).reset(nNodes_,nNodes_); ((atc_->fields())[ELECTRON_WAVEFUNCTION_ENERGIES].set_quantity()).reset(nNodes_,1); } +#if 0 + cout << " RHS MASK\n"; + print_mask(rhsMask); +#endif } //-------------------------------------------------------- // pre initial integration //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::pre_init_integrate() { double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); int step = atc_->step(); if (step % fluxUpdateFreq_ != 0) return; // set Dirchlet data atc_->set_fixed_nodes(); // set Neumann data (atc does not set these until post_final) atc_->set_sources(); // subcyle integration of fast electron variable/s double idt = dt/nsubcycle_; for (int i = 0; i < nsubcycle_ ; ++i) { if (electronDensityEqn_ == ELECTRON_CONTINUITY) { // update continuity eqn if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) continuityIntegrator_->update(idt,time,atc_->fields_,rhs_); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) poissonSolver_->solve(atc_->fields(),rhs_); } else if (electronDensityEqn_ == ELECTRON_SCHRODINGER) { - schrodingerPoissonSolver_->solve(rhs_,fluxes_); + if ( (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) + || (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) ) + schrodingerPoissonSolver_->solve(rhs_,fluxes_); } // update electron temperature - if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) + && temperatureIntegrator_ ) { +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->stream_msg_once("start temperature integration...",true,false); +#endif temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->stream_msg_once(" done",false,true); +#endif + } atc_->set_fixed_nodes(); } + } + //-------------------------------------------------------- + // set coupling source terms + //------------------------------------------------------- + void ExtrinsicModelDriftDiffusion::set_sources(FIELDS & fields, FIELDS & sources) + { + atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, + sources, + atc_->source_integration(), physicsModel_); + } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelDriftDiffusion::output(OUTPUT_LIST & outputData) - { + { +#ifdef ATC_VERBOSE +// ATC::LammpsInterface::instance()->print_msg_once("start output",true,false); +#endif ExtrinsicModelTwoTemperature::output(outputData); // fields outputData["dot_electron_density"] = & (atc_->dot_field(ELECTRON_DENSITY)).set_quantity(); - outputData["joule_heating"] = & rhs_[ELECTRON_TEMPERATURE].set_quantity(); + DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); + double totalJouleHeating =JE.sum(); + outputData["joule_heating"] = & JE; + atc_->feEngine_->add_global("total_joule_heating",totalJouleHeating); Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = true; rhsMask(ELECTRIC_POTENTIAL,FLUX) = true; atc_->compute_flux(rhsMask,atc_->fields_,fluxes_,physicsModel_); //(fluxes_[ELECTRON_DENSITY][0]).print("J_x"); outputData["electron_flux_x"] = & fluxes_[ELECTRON_DENSITY][0]; outputData["electron_flux_y"] = & fluxes_[ELECTRON_DENSITY][1]; outputData["electron_flux_z"] = & fluxes_[ELECTRON_DENSITY][2]; outputData["electric_field_x"] = & fluxes_[ELECTRIC_POTENTIAL][0]; outputData["electric_field_y"] = & fluxes_[ELECTRIC_POTENTIAL][1]; outputData["electric_field_z"] = & fluxes_[ELECTRIC_POTENTIAL][2]; if (electronDensityEqn_ == ELECTRON_SCHRODINGER ) { SPAR_MAT K; Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_WAVEFUNCTION,FLUX) = true; pair<FieldName,FieldName> row_col(ELECTRON_WAVEFUNCTION, ELECTRON_WAVEFUNCTION); atc_->feEngine_->compute_tangent_matrix( rhsMask, row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), K); phiTotal_.reset(K.nRows(),1); const DIAG_MAT & inv_dV = (atc_->invNodeVolumes_).quantity(); for (int i = 0; i < K.nRows() ; i++) { phiTotal_(i,0) = 0.0; for (int j = 0; j < K.nCols() ; j++) { phiTotal_(i,0) += K(i,j); } phiTotal_(i,0) *= inv_dV(i,i); } outputData["V_total"] = & phiTotal_; } // globals double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); atc_->feEngine_->add_global("total_electron_density",nSum); +#ifdef ATC_VERBOSE +// ATC::LammpsInterface::instance()->print_msg_once("... done",false,true); +#endif } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelDriftDiffusion::size_vector(int intrinsicSize) { int xSize = ExtrinsicModelTwoTemperature::size_vector(intrinsicSize); baseSize_ = intrinsicSize + xSize; - xSize += 1; + xSize += 2; return xSize; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusion::compute_vector(int n, double & value) { // output[1] = total electron density + // output[2] = total joule heating bool match = ExtrinsicModelTwoTemperature::compute_vector(n,value); if (match) return match; if (n == baseSize_) { double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); value = nSum; return true; } + if (n == baseSize_+1) { + DENS_MAT & JE = rhs_[ELECTRON_TEMPERATURE].set_quantity(); + double totalJouleHeating =JE.sum(); + value = totalJouleHeating; + return true; + } return false; } //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusionConvection //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusionConvection::ExtrinsicModelDriftDiffusionConvection (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModelDriftDiffusion(modelManager,modelType,matFileName), cddmPoissonSolver_(NULL), baseSize_(0) { // delete base class's version of the physics model if (physicsModel_) delete physicsModel_; if (modelType == CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER) { physicsModel_ = new PhysicsModelDriftDiffusionConvectionSchrodinger(matFileName); electronDensityEqn_ = ELECTRON_SCHRODINGER; } else { physicsModel_ = new PhysicsModelDriftDiffusionConvection(matFileName); } - atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY) = true; - atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE) = true; + atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY) = false; + atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE) = false; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelDriftDiffusionConvection::~ExtrinsicModelDriftDiffusionConvection() { if (cddmPoissonSolver_) delete cddmPoissonSolver_; for (vector<LinearSolver * >::const_iterator iter=velocitySolvers_.begin(); iter != velocitySolvers_.end(); iter++) if (*iter) delete *iter; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::initialize() { ExtrinsicModelDriftDiffusion::initialize(); - - // change temperature integrator to be Crank-Nicolson - if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { - if (temperatureIntegrator_) delete temperatureIntegrator_; - Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); - rhsMask = false; - for (int i = 0; i < NUM_FLUX; i++) { - rhsMask(ELECTRON_TEMPERATURE,i) = atc_->fieldMask_(ELECTRON_TEMPERATURE,i); - } - temperatureIntegrator_ = new FieldImplicitEulerIntegrator(ELECTRON_TEMPERATURE, - physicsModel_, - atc_->feEngine_, atc_, - rhsMask); - } - nNodes_ = atc_->num_nodes(); nsd_ = atc_->nsd(); rhs_[ELECTRON_VELOCITY].reset(nNodes_,nsd_); atc_->set_fixed_nodes(); // needed to correctly set BC data // initialize Poisson solver if (cddmPoissonSolver_) delete cddmPoissonSolver_; Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRIC_POTENTIAL,FLUX) = true; pair<FieldName,FieldName> row_col(ELECTRIC_POTENTIAL,ELECTRIC_POTENTIAL); SPAR_MAT stiffness; (atc_->feEngine_)->compute_tangent_matrix(rhsMask,row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), stiffness); const BC_SET & bcs = (atc_->prescribedDataMgr_->bcs(ELECTRIC_POTENTIAL))[0]; cddmPoissonSolver_ = new LinearSolver(stiffness, bcs, poissonSolverType_, -1, true); // initialize velocity solver const BCS & velocityBcs = atc_->prescribedDataMgr_->bcs(ELECTRON_VELOCITY); DENS_MAT velocityRhs(nNodes_,nsd_); atc_->compute_mass_matrix(ELECTRON_VELOCITY,physicsModel_); SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).set_quantity(); for (int i = 0; i < nsd_; i++ ) { LinearSolver * myVelocitySolver = new LinearSolver(velocityMassMat, velocityBcs[i], LinearSolver::AUTO_SOLVE, -1, true); velocitySolvers_.push_back(myVelocitySolver); } } //-------------------------------------------------------- // pre initial integration //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::pre_init_integrate() { double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); int step = atc_->step(); if (step % fluxUpdateFreq_ != 0) return; // set Dirchlet data atc_->set_fixed_nodes(); // set Neumann data (atc does not set these until post_final) atc_->set_sources(); // subcyle integration of fast electron variable/s double idt = dt/nsubcycle_; for (int i = 0; i < nsubcycle_ ; ++i) { // update electron temperature mass matrix atc_->compute_mass_matrix(ELECTRON_VELOCITY,physicsModel_); // update electron velocity if (!(atc_->prescribedDataMgr_)->all_fixed(ELECTRON_VELOCITY)) { //const BCS & bcs // = atc_->prescribedDataMgr_->bcs(ELECTRON_VELOCITY); Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_VELOCITY,SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,SOURCE); rhsMask(ELECTRON_VELOCITY,FLUX) = atc_->fieldMask_(ELECTRON_VELOCITY,FLUX); + rhsMask(ELECTRON_VELOCITY,OPEN_SOURCE) = atc_->fieldMask_(ELECTRON_VELOCITY,OPEN_SOURCE); FIELDS rhs; rhs[ELECTRON_VELOCITY].reset(nNodes_,nsd_); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); const DENS_MAT & velocityRhs = rhs[ELECTRON_VELOCITY].quantity(); + // add a solver for electron momentum DENS_MAT & velocity = (atc_->field(ELECTRON_VELOCITY)).set_quantity(); for (int j = 0; j < nsd_; ++j) { if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_VELOCITY,j) ) { - CLON_VEC v = column(velocity,j); - const CLON_VEC r = column(velocityRhs,j); - (velocitySolvers_[j])->solve(v,r); + if (atc_->useConsistentMassMatrix_(ELECTRON_VELOCITY)) { + //#ifdef ATC_PRINT_DEBUGGING + const SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).quantity(); + velocityMassMat.print("VMASS"); + //#endif + CLON_VEC v = column(velocity,j); + const CLON_VEC r = column(velocityRhs,j); + (velocitySolvers_[j])->solve(v,r); + } + else { + //velocityRhs.print("VRHS"); + //const DIAG_MAT & velocityMassMat = (atc_->massMats_[ELECTRON_VELOCITY]).quantity(); + //velocityMassMat.print("MASS"); + atc_->apply_inverse_mass_matrix(velocityRhs,velocity,ELECTRON_VELOCITY); + } } } } //atc_->set_fixed_nodes(); if (electronDensityEqn_ == ELECTRON_CONTINUITY) { // update continuity eqn + Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); + rhsMask = false; + rhsMask(ELECTRON_DENSITY,FLUX) = atc_->fieldMask_(ELECTRON_DENSITY,FLUX); + rhsMask(ELECTRON_DENSITY,SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,SOURCE); + rhsMask(ELECTRON_DENSITY,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRON_DENSITY,PRESCRIBED_SOURCE); + FIELDS rhs; + rhs[ELECTRON_DENSITY].reset(nNodes_,1); + atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) - continuityIntegrator_->update(idt,time,atc_->fields_,rhs_); + continuityIntegrator_->update(idt,time,atc_->fields_,rhs); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) { //poissonSolver_->solve(atc_->fields_,rhs_); - Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRIC_POTENTIAL,SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,SOURCE); rhsMask(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE) = atc_->fieldMask_(ELECTRIC_POTENTIAL,PRESCRIBED_SOURCE); - FIELDS rhs; + // FIELDS rhs; rhs[ELECTRIC_POTENTIAL].reset(nNodes_,1); atc_->compute_rhs_vector(rhsMask, atc_->fields_, rhs, atc_->source_integration(), physicsModel_); CLON_VEC x =column((atc_->field(ELECTRIC_POTENTIAL)).set_quantity(),0); const CLON_VEC r =column(rhs[ELECTRIC_POTENTIAL].quantity(),0); cddmPoissonSolver_->solve(x,r); } } else if (electronDensityEqn_ == ELECTRON_SCHRODINGER) { schrodingerPoissonSolver_->solve(rhs_,fluxes_); } atc_->set_fixed_nodes(); // update electron temperature mass matrix atc_->compute_mass_matrix(ELECTRON_TEMPERATURE,physicsModel_); // update electron temperature - if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) - temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) ) { + //if (atc_->useConsistentMassMatrix_(ELECTRON_TEMPERATURE)) { + temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + //} + //else { // lumped mass matrix + + //} + } atc_->set_fixed_nodes(); } } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::output(OUTPUT_LIST & outputData) { ExtrinsicModelDriftDiffusion::output(outputData); //FIELD jouleHeating(atc_->num_nodes(),1); //set_kinetic_energy_source(atc_->fields(),jouleHeating); outputData["joule_heating"] = & (atc_->extrinsic_source(TEMPERATURE)).set_quantity(); // globals DENS_MAT nodalKineticEnergy; compute_nodal_kinetic_energy(nodalKineticEnergy); double kineticEnergy = nodalKineticEnergy.sum(); atc_->feEngine_->add_global("total_electron_kinetic_energy",kineticEnergy); } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelDriftDiffusionConvection::size_vector(int intrinsicSize) { int xSize = ExtrinsicModelDriftDiffusion::size_vector(intrinsicSize); baseSize_ = intrinsicSize + xSize; xSize += 1; return xSize; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelDriftDiffusionConvection::compute_vector(int n, double & value) { // output[1] = total electron kinetic energy bool match = ExtrinsicModelDriftDiffusion::compute_vector(n,value); if (match) return match; if (n == baseSize_) { DENS_MAT nodalKineticEnergy; compute_nodal_kinetic_energy(nodalKineticEnergy); value = nodalKineticEnergy.sum(); return true; } return false; } //-------------------------------------------------------- // compute_kinetic_energy //-------------------------------------------------------- void ExtrinsicModelDriftDiffusionConvection::compute_nodal_kinetic_energy(DENS_MAT & kineticEnergy) { DENS_MAT & velocity((atc_->field(ELECTRON_VELOCITY)).set_quantity()); SPAR_MAT & velocityMassMat = (atc_->consistentMassMats_[ELECTRON_VELOCITY]).set_quantity(); kineticEnergy.reset(nNodes_,1); for (int j = 0; j < nsd_; j++) { CLON_VEC myVelocity(velocity,CLONE_COL,j); DENS_MAT velocityMat(nNodes_,1); for (int i = 0; i < nNodes_; i++) velocityMat(i,0) = myVelocity(i); kineticEnergy += velocityMat.mult_by_element(myVelocity); } kineticEnergy = 0.5*velocityMassMat*kineticEnergy; } }; diff --git a/lib/atc/ExtrinsicModelDriftDiffusion.h b/lib/atc/ExtrinsicModelDriftDiffusion.h index 92e2365dc..abc40e84d 100644 --- a/lib/atc/ExtrinsicModelDriftDiffusion.h +++ b/lib/atc/ExtrinsicModelDriftDiffusion.h @@ -1,187 +1,191 @@ #ifndef EXTRINSIC_MODEL_DRIFT_DIFFUSION #define EXTRINSIC_MODEL_DRIFT_DIFFUSION #include <set> #include <string> #include <vector> #include "ExtrinsicModelTwoTemperature.h" #include "SchrodingerSolver.h" namespace ATC { class ATC_Coupling; class PrescribedDataManager; class ExtrinsicModel; class PhysicsModel; class PoissonSolver; class LinearSolver; class SchrodingerSolver; class SchrodingerPoissonSolver; /** * @class ExtrinsicModelDriftDiffusion * @brief add electron temperature physics to phonon physics * owned fields ELECTRON_DENSITY */ //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusion //-------------------------------------------------------- //-------------------------------------------------------- class ExtrinsicModelDriftDiffusion : public ExtrinsicModelTwoTemperature { public: // constructor ExtrinsicModelDriftDiffusion(ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, std::string matFileName); // destructor virtual ~ExtrinsicModelDriftDiffusion(); /** parser/modifier */ virtual bool modify(int narg, char **arg); /** pre time integration */ virtual void initialize(); /** Predictor phase, executed before Verlet */ virtual void pre_init_integrate(); + /** Set sources to AtC equation */ + virtual void set_sources(FIELDS & fields, FIELDS & sources); + /** Add model-specific output data */ virtual void output(OUTPUT_LIST & outputData); /** set up LAMMPS display variables */ virtual int size_vector(int externalSize); /** get LAMMPS display variables */ virtual bool compute_vector(int n, double & value); protected: /** Poisson solve */ void poisson_solve(); /** Schrodinger-Poisson solve */ void schrodinger_poisson_solve(void); // wrapper void schrodinger_poisson_solve(int iterations); void slice_schrodinger_poisson_solve(int consistencyIter, int constraintIter); double update_fermi_energy(double target,bool first = false); /** time integrator for the continuity eqn */ FieldEulerIntegrator * continuityIntegrator_; /** poisson solver type */ SolverType poissonSolverType_; /** poisson solver */ PoissonSolver * poissonSolver_; /** offset/size for LAMMPS display output */ int baseSize_; /** ways to determine the electron density */ int electronDensityEqn_; enum electronDensityEqnType { ELECTRON_CONTINUITY, ELECTRON_EQUILIBRIUM, ELECTRON_SCHRODINGER}; /** frequency for updating the electron state */ int fluxUpdateFreq_; /** Schrodinger solver type */ SolverType schrodingerSolverType_; /** poisson solver */ SchrodingerSolver * schrodingerSolver_; /** schrodinger-poisson solver */ SchrodingerPoissonManager schrodingerPoissonMgr_; SchrodingerPoissonSolver * schrodingerPoissonSolver_; /** schrodinger-poisson data */ int maxConsistencyIter_, maxConstraintIter_; double safe_dEf_, Ef_shift_; DENS_MAT phiTotal_; double Tmax_; Array2D<double> EfHistory_; /** one dimensional restriction */ bool oneD_; int oneDcoor_; int oneDstride_; std::string oneDnodesetName_; std::set<int> oneDnodeset_; Array< std::set<int> > oneDslices_; + Array< double > oneDdxs_; int oneDconserve_; DENS_MAT JE_; }; /** * @class ExtrinsicModelDriftDiffusionConvection * @brief add electron temperature physics to phonon physics, including convective transport */ //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelDriftDiffusionConvection //-------------------------------------------------------- //-------------------------------------------------------- class ExtrinsicModelDriftDiffusionConvection : public ExtrinsicModelDriftDiffusion { public: // constructor ExtrinsicModelDriftDiffusionConvection(ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, std::string matFileName); // destructor virtual ~ExtrinsicModelDriftDiffusionConvection(); /** pre time integration */ virtual void initialize(); /** Predictor phase, executed before Verlet */ virtual void pre_init_integrate(); /** Set sources to AtC equation */ //virtual void set_sources(FIELDS & fields, FIELDS & sources); /** Add model-specific output data */ virtual void output(OUTPUT_LIST & outputData); /** set up LAMMPS display variables */ virtual int size_vector(int externalSize); /** get LAMMPS display variables */ virtual bool compute_vector(int n, double & value); protected: /** compute the total kinetic energy of the electrons */ void compute_nodal_kinetic_energy(DENS_MAT & kineticEnergy); /** Linear solver for velocity */ std::vector<LinearSolver * > velocitySolvers_; /** Linear solver for solving the poisson equations */ LinearSolver * cddmPoissonSolver_; /** offset/size for LAMMPS display output */ int baseSize_; double timerStart_, timerCurrent_; }; }; #endif diff --git a/lib/atc/ExtrinsicModelElectrostatic.cpp b/lib/atc/ExtrinsicModelElectrostatic.cpp index d07af21c0..5f5321f65 100644 --- a/lib/atc/ExtrinsicModelElectrostatic.cpp +++ b/lib/atc/ExtrinsicModelElectrostatic.cpp @@ -1,1014 +1,1014 @@ // ATC Headers #include "ExtrinsicModelElectrostatic.h" #include "PhysicsModel.h" #include "ATC_Error.h" #include "FieldEulerIntegrator.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PrescribedDataManager.h" #include "PoissonSolver.h" #include "PerAtomQuantityLibrary.h" #include "AtomToMoleculeTransfer.h" #include "MoleculeSet.h" #include "ChargeRegulator.h" #include <set> using std::string; using std::vector; using std::map; using std::pair; using std::set; static const double kTol_ = 1.0e-8; static const double tol_sparse = 1.e-30;//tolerance for compaction from dense namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelElectrostatic //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelElectrostatic::ExtrinsicModelElectrostatic (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModel(modelManager,modelType,matFileName), poissonSolverType_(DIRECT), // ITERATIVE | DIRECT poissonSolverTol_(0), poissonSolverMaxIter_(0), poissonSolver_(NULL), maxSolves_(0), baseSize_(0), chargeRegulator_(NULL), useSlab_(false), includeShortRange_(true), atomForces_(NULL), nodalAtomicCharge_(NULL), nodalAtomicGhostCharge_(NULL) { physicsModel_ = new PhysicsModelSpeciesElectrostatic(matFileName); // set up correct masks for coupling rhsMaskIntrinsic_.reset(NUM_FIELDS,NUM_FLUX); rhsMaskIntrinsic_ = false; if (atc_->track_charge()) { if (! chargeRegulator_) chargeRegulator_ = new ChargeRegulator(atc_); } } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelElectrostatic::~ExtrinsicModelElectrostatic() { if (poissonSolver_) delete poissonSolver_; if (chargeRegulator_) delete chargeRegulator_; } //-------------------------------------------------------- // modify //-------------------------------------------------------- bool ExtrinsicModelElectrostatic::modify(int narg, char **arg) { bool match = false; int argIndx = 0; /** */ if (strcmp(arg[argIndx],"poisson_solver")==0) { argIndx++; if (strcmp(arg[argIndx],"max_solves")==0) { argIndx++; maxSolves_ = atoi(arg[argIndx]) ; } else if (strcmp(arg[argIndx],"tolerance")==0) { argIndx++; poissonSolverTol_ = atof(arg[argIndx]); } else if (strcmp(arg[argIndx],"max_iterations")==0) { argIndx++; poissonSolverMaxIter_ = atoi(arg[argIndx]); } else if (strcmp(arg[argIndx],"iterative")==0) { poissonSolverType_ = ITERATIVE; } else { poissonSolverType_ = DIRECT; } match = true; } // end "poisson_solver" /** creates fixed charge on faceset units on surface charge density are lammps charge units / lammps length units ^ 2 fix_modify ATC extrinsic fix_charge faceset_id value */ #ifdef CHARGED_SURFACE else if (strcmp(arg[argIndx],"fix_charge")==0) { argIndx++; string facesetName(arg[argIndx]); argIndx++; double chargeDensity = atof(arg[argIndx]); surfaceCharges_[facesetName] = chargeDensity; match = true; } /** */ else if (strcmp(arg[argIndx],"unfix_charge")==0) { argIndx++; string fsetName(arg[argIndx]); throw ATC_Error("Ability to unfix charge not yet implemented"); match = true; } #endif else if (strcmp(arg[argIndx],"control")==0) { argIndx++; if (strcmp(arg[argIndx],"charge")==0) { argIndx++; if (!atc_->track_charge()) throw ATC_Error("must have charges to regulate"); match = chargeRegulator_->modify(narg-argIndx,&arg[argIndx]); } } /** switch to use slabbing */ else if (strcmp(arg[argIndx],"slab")==0) { argIndx++; if (strcmp(arg[argIndx],"on")==0) { useSlab_ = true; match = true; } else if (strcmp(arg[argIndx],"off")==0) { useSlab_ = false; match = true; } } /** switch to account for short range interaces */ else if (strcmp(arg[argIndx],"short_range")==0) { argIndx++; if (strcmp(arg[argIndx],"on")==0) { includeShortRange_ = true; match = true; } else if (strcmp(arg[argIndx],"off")==0) { includeShortRange_ = false; match = true; } } return match; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelElectrostatic::construct_transfers() { // add charge density transfer operator if (atc_->track_charge()) { InterscaleManager & interscaleManager(atc_->interscale_manager()); // make sure we have gradients at atoms VectorDependencyManager<SPAR_MAT * > * interpolantGradient = interscaleManager.vector_sparse_matrix("InterpolantGradient"); if (!interpolantGradient) { interpolantGradient = new PerAtomShapeFunctionGradient(atc_); interscaleManager.add_vector_sparse_matrix(interpolantGradient, "InterpolantGradient"); } FundamentalAtomQuantity * atomicCharge = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE); AtfShapeFunctionRestriction * nodalAtomicCharge = new AtfShapeFunctionRestriction(atc_,atomicCharge,atc_->accumulant()); interscaleManager.add_dense_matrix(nodalAtomicCharge,"NodalAtomicCharge"); AtfShapeFunctionMdProjection * nodalAtomicChargeDensity = new AtfShapeFunctionMdProjection(atc_,nodalAtomicCharge,MASS_DENSITY); interscaleManager.add_dense_matrix(nodalAtomicChargeDensity,"NodalAtomicChargeDensity"); // get the total charge and dipole moment at the node per molecule // small molecules require per atom quantities with ghosts const map<string,pair<MolSize,int> > & moleculeIds(atc_->molecule_ids()); map<string,pair<MolSize,int> >::const_iterator molecule; PerAtomQuantity<double> * atomProcGhostCoarseGrainingPositions = interscaleManager.per_atom_quantity("AtomicProcGhostCoarseGrainingPositions"); FundamentalAtomQuantity * charge = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE, PROC_GHOST); for (molecule = moleculeIds.begin(); molecule != moleculeIds.end(); molecule++) { const string moleculeName = molecule->first; SmallMoleculeSet * smallMoleculeSet = interscaleManager.small_molecule_set(moleculeName); // calculate nodal charge from the molecules AtomToSmallMoleculeTransfer<double> * moleculeCharge = new AtomToSmallMoleculeTransfer<double>(atc_,charge,smallMoleculeSet); interscaleManager.add_dense_matrix(moleculeCharge,"MoleculeCharge"+moleculeName); MotfShapeFunctionRestriction * nodalAtomicMoleculeCharge = new MotfShapeFunctionRestriction(moleculeCharge, interscaleManager.sparse_matrix("ShapeFunction"+moleculeName)); interscaleManager.add_dense_matrix(nodalAtomicMoleculeCharge,"NodalMoleculeCharge"+moleculeName); AtfShapeFunctionMdProjection * nodalAtomicMoleculeChargeDensity = new AtfShapeFunctionMdProjection(atc_,nodalAtomicMoleculeCharge,MASS_DENSITY); interscaleManager.add_dense_matrix(nodalAtomicMoleculeChargeDensity,"NodalMoleculeChargeDensity"+moleculeName); // dipole moment density // calculate the dipole moment of the molecules SmallMoleculeCentroid * moleculeCentroid = static_cast<SmallMoleculeCentroid*>(interscaleManager.dense_matrix("MoleculeCentroid"+moleculeName)); SmallMoleculeDipoleMoment * dipoleMoment = new SmallMoleculeDipoleMoment(atc_,charge,smallMoleculeSet,atomProcGhostCoarseGrainingPositions,moleculeCentroid); interscaleManager.add_dense_matrix(dipoleMoment,"DipoleMoment"+moleculeName); MotfShapeFunctionRestriction * nodalAtomicMoleculeDipole = new MotfShapeFunctionRestriction(dipoleMoment, interscaleManager.sparse_matrix("ShapeFunction"+moleculeName)); interscaleManager.add_dense_matrix(nodalAtomicMoleculeDipole,"NodalMoleculeDipole"+moleculeName); AtfShapeFunctionMdProjection * nodalAtomicMoleculeDipoleDensity = new AtfShapeFunctionMdProjection(atc_,nodalAtomicMoleculeDipole,MASS_DENSITY); interscaleManager.add_dense_matrix(nodalAtomicMoleculeDipoleDensity,"NodalMoleculeDipoleDensity"+moleculeName); } } } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelElectrostatic::initialize() { ExtrinsicModel::initialize(); InterscaleManager & interscaleManager = atc_->interscale_manager(); int nNodes = atc_->num_nodes(); atomForces_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE); rhs_[ELECTRIC_POTENTIAL].reset(nNodes,1); #ifdef CHARGED_SURFACE // set fixed potential surfaces form charged surfaces map<string,double>::const_iterator isurface; for (isurface = surfaceCharges_.begin(); isurface != surfaceCharges_.end(); isurface++) add_charged_surface(isurface->first,isurface->second); #endif // set up poisson solver rhsMask_.reset(NUM_FIELDS,NUM_FLUX); rhsMask_ = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask_(ELECTRIC_POTENTIAL,i) = atc_->fieldMask_(ELECTRIC_POTENTIAL,i); } rhsMask_(ELECTRIC_POTENTIAL,FLUX) = false;// for poisson solve & rhs compute // need to create the bcs for the solver to configure properly atc_->set_fixed_nodes(); if (poissonSolver_) delete poissonSolver_; int type = ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC; if (poissonSolverType_ == DIRECT) { type = ATC::LinearSolver::DIRECT_SOLVE; } poissonSolver_ = new PoissonSolver(ELECTRIC_POTENTIAL, physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_, rhsMask_,type, true); if (poissonSolverTol_) poissonSolver_->set_tolerance(poissonSolverTol_); if (poissonSolverMaxIter_) poissonSolver_->set_max_iterations(poissonSolverMaxIter_); poissonSolver_->initialize(); // initialize localized Green's function for FE electric field correction if (atc_->track_charge() && includeShortRange_) { greensFunctions_.reserve(nNodes); // set up Green's function per node for (int i = 0; i < nNodes; i++) { set<int> 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<int>::const_iterator thisNode; SparseVector<double> sparseGreensFunction(nNodes); for (thisNode = localNodes.begin(); thisNode != localNodes.end(); thisNode++) sparseGreensFunction(*thisNode) = globalGreensFunction(*thisNode); greensFunctions_.push_back(sparseGreensFunction); } } if (atc_->track_charge()) { double * q = LammpsInterface::instance()->atom_charge(); if (!q) throw ATC_Error(" charge tracking requested but charge pointer is null"); nodalAtomicCharge_ = interscaleManager.dense_matrix("NodalAtomicCharge"); if (! nodalAtomicCharge_) { FundamentalAtomQuantity * atomCharge = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE); nodalAtomicCharge_ = new AtfShapeFunctionRestriction(atc_,atomCharge, atc_->accumulant()); interscaleManager.add_dense_matrix(nodalAtomicCharge_,"NodalAtomicCharge"); } if (atc_->groupbitGhost_) { nodalAtomicGhostCharge_ = interscaleManager.dense_matrix("NodalAtomicGhostCharge"); if (! nodalAtomicGhostCharge_) { FundamentalAtomQuantity * ghostCharge = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE, GHOST); PerAtomSparseMatrix<double> * ghostShapeFunctions = interscaleManager.per_atom_sparse_matrix("InterpolantGhost"); if (!ghostShapeFunctions) { ghostShapeFunctions = new PerAtomShapeFunction(atc_, interscaleManager.per_atom_quantity("AtomicGhostCoarseGrainingPositions"), interscaleManager.per_atom_int_quantity("AtomGhostElement"), GHOST); interscaleManager.add_per_atom_sparse_matrix(ghostShapeFunctions,"InterpolantGhost"); } nodalAtomicGhostCharge_ = new AtfShapeFunctionRestriction(atc_,ghostCharge, ghostShapeFunctions); interscaleManager.add_dense_matrix(nodalAtomicGhostCharge_,"NodalAtomicGhostCharge"); } } } if (chargeRegulator_) { if (! poissonSolver_) throw ATC_Error("passing of Poisson solver from ExtrinsicModelElectrostatic to ChargeRegulator failed"); chargeRegulator_->assign_poisson_solver(poissonSolver_); chargeRegulator_->construct_methods(); chargeRegulator_->initialize(); } // set initial force post_force(); } //-------------------------------------------------------- // pre final integration //-------------------------------------------------------- void ExtrinsicModelElectrostatic::post_init_integrate() { if (chargeRegulator_) chargeRegulator_->apply_pre_force(atc_->dt()); } //-------------------------------------------------------- // post force //-------------------------------------------------------- void ExtrinsicModelElectrostatic::post_force() { if (chargeRegulator_) chargeRegulator_->apply_post_force(atc_->dt()); // add in correction accounting for lumped mass matrix in charge density // in atomistic part of domain & account for physics model fluxes,resets rhs // set Dirchlet data atc_->set_fixed_nodes(); // set sources (atc_->prescribed_data_manager())->set_sources(atc_->time()+0.5*(atc_->dt()),atc_->sources()); // compute Poisson equation RHS sources atc_->compute_rhs_vector(rhsMask_, atc_->fields_, rhs_, atc_->source_integration(), physicsModel_); // add atomic charges to rhs DENS_MAT & rhs = rhs_[ELECTRIC_POTENTIAL].set_quantity(); if (atc_->track_charge()) { rhs += nodalAtomicCharge_->quantity(); if (nodalAtomicGhostCharge_) { rhs += nodalAtomicGhostCharge_->quantity(); } } // solve poisson eqn for electric potential // electron charge density added to Poisson RHS in solver DENS_MAT & potential = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); if ( maxSolves_ == 0 || (atc_->local_step() < maxSolves_) ) { //potential.print("POT"); // rhs.print("RHS"); bool converged = poissonSolver_->solve(potential,rhs); if (! converged ) throw ATC_Error("Poisson solver did not converge in ExtrinsicModelElectrostatic"); } // do this for intrinsic charges or effective electron charges at atoms if (atc_->track_charge() || ( LammpsInterface::instance()->atom_charge() && atc_->source_atomic_quadrature(ELECTRIC_POTENTIAL) ) ) { _atomElectricalForce_.resize(atc_->nlocal(),atc_->nsd()); add_electrostatic_forces(potential); #ifdef CHARGED_SURFACE if (includeShortRange_) apply_charged_surfaces(potential); #endif InterscaleManager & interscaleManager_ = atc_->interscale_manager(); atomForces_ = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE); (*atomForces_) += _atomElectricalForce_; // f_E in ours, f in lammps ultimately } } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelElectrostatic::output(OUTPUT_LIST & outputData) { double scale = 1./(LammpsInterface::instance()->ftm2v()); double localF[3]; if (_atomElectricalForce_.nRows() > 0) { localF[0] = scale*(_atomElectricalForce_).col_sum(0); localF[1] = scale*(_atomElectricalForce_).col_sum(1); localF[2] = scale*(_atomElectricalForce_).col_sum(2); } else { localF[0] = 0.;localF[1] = 0.; localF[2] = 0.; } LammpsInterface::instance()->allsum(localF,totalElectricalForce_,3); if (LammpsInterface::instance()->rank_zero()) { atc_->feEngine_->add_global("electrostatic_force_x", totalElectricalForce_[0]); atc_->feEngine_->add_global("electrostatic_force_y", totalElectricalForce_[1]); atc_->feEngine_->add_global("electrostatic_force_z", totalElectricalForce_[2]); } // add in FE fields related to charge FIELDS & fields(atc_->fields()); FIELDS::const_iterator rhoField = fields.find(CHARGE_DENSITY); if (rhoField!=fields.end()) { InterscaleManager & interscaleManager(atc_->interscale_manager()); const DENS_MAN * atomicChargeDensity(interscaleManager.dense_matrix("NodalAtomicChargeDensity")); atc_->nodal_atomic_field(CHARGE_DENSITY) = atomicChargeDensity->quantity(); fields[CHARGE_DENSITY] = atomicChargeDensity->quantity(); DENS_MAT & chargeDensity(fields[CHARGE_DENSITY].set_quantity()); DENS_MAT & nodalAtomicChargeDensity((atc_->nodal_atomic_field(CHARGE_DENSITY)).set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData["charge_density"] = &chargeDensity; outputData["NodalAtomicChargeDensity"] = &nodalAtomicChargeDensity; } } if (fields.find(ELECTRON_DENSITY)==fields.end()) { fields[ELECTRON_DENSITY].reset(fields[CHARGE_DENSITY].nRows(),1); DENS_MAT & electronDensity(fields[ELECTRON_DENSITY].set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData["electron_density"] = &electronDensity; } } const map<string,pair<MolSize,int> > & moleculeIds(atc_->molecule_ids()); map<string,pair<MolSize,int> >::const_iterator molecule; for (molecule = moleculeIds.begin(); molecule != moleculeIds.end(); molecule++) { // net charge DENS_MAN & nodalMoleculeChargeDensityOut(atc_->tagged_dens_man("NodalMoleculeChargeDensity"+molecule->first)); DENS_MAN * nodalMoleculeChargeDensity((atc_->interscale_manager()).dense_matrix("NodalMoleculeChargeDensity"+molecule->first)); nodalMoleculeChargeDensityOut = nodalMoleculeChargeDensity->quantity(); // dipole moment DENS_MAN & nodalMoleculeDipoleDensityOut(atc_->tagged_dens_man("NodalMoleculeDipoleDensity"+molecule->first)); DENS_MAN * nodalMoleculeDipoleDensity((atc_->interscale_manager()).dense_matrix("NodalMoleculeDipoleDensity"+molecule->first)); nodalMoleculeDipoleDensityOut = nodalMoleculeDipoleDensity->quantity(); } if(chargeRegulator_) chargeRegulator_->output(outputData); } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelElectrostatic::size_vector(int intrinsicSize) { baseSize_ = intrinsicSize; return 5; } //-------------------------------------------------------- // compute_scalar : added energy = - f.x //-------------------------------------------------------- double ExtrinsicModelElectrostatic::compute_scalar(void) { //((atc_->interscale_manager()).fundamental_atom_quantity(LammpsInterface::ATOM_POSITION))->force_reset(); const DENS_MAT & atomPosition = ((atc_->interscale_manager()).fundamental_atom_quantity(LammpsInterface::ATOM_POSITION))->quantity(); double local_fdotx = 0, fdotx; for (int i = 0; i < _atomElectricalForce_.nRows() ; i++) { for (int j = 0; j < _atomElectricalForce_.nCols() ; j++) { local_fdotx -= _atomElectricalForce_(i,j)*atomPosition(i,j); } } LammpsInterface::instance()->allsum(&local_fdotx,&fdotx,1); // convert fdotx *= LammpsInterface::instance()->mvv2e(); return fdotx; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelElectrostatic::compute_vector(int n, double & value) { if (n == baseSize_) { double nSum = ((atc_->field(ELECTRON_DENSITY)).quantity()).col_sum(); value = nSum; return true; } else if (n > baseSize_ && n < baseSize_+4) { int dof = n-baseSize_-1; double localF = (_atomElectricalForce_).col_sum(dof), F=0; LammpsInterface::instance()->allsum(&localF,&F,1); double ftm2v = LammpsInterface::instance()->ftm2v(); value = F/ftm2v; return true; } else if (n == baseSize_+4) { double nSum = ((atc_->field(ELECTRIC_POTENTIAL)).quantity()).col_sum(); value = nSum; return true; } return false; } //-------------------------------------------------------- // add_electrostatic_forces //-------------------------------------------------------- void ExtrinsicModelElectrostatic::add_electrostatic_forces (MATRIX & potential) { //double qE2f = LammpsInterface::instance()->qe2f(); double qV2e = LammpsInterface::instance()->qv2e(); // charge volts to our energy units //double ** f = LammpsInterface::instance()->fatom(); double * q = LammpsInterface::instance()->atom_charge(); // f_ai = \sum_IJ N_Ia Bi_IJ phi_J = \sum_I N_Ia Ei_I int nsd = atc_->nsd(); int nLocal = atc_->nlocal(); DENS_MAT E(nLocal,nsd); const SPAR_MAT_VEC & shapeFucntionDerivatives(((atc_->interscale_manager()).vector_sparse_matrix("InterpolantGradient"))->quantity()); if (nLocal > 0) { for (int i=0; i < nsd; i++) { CLON_VEC Ei = column(E,i); Ei = -1.*(*(shapeFucntionDerivatives[i])*potential); } } int dimOffset = 0; if (useSlab_) dimOffset = nsd - 1; for (int i = 0; i < nLocal; i++) { int atomIdx = atc_->internalToAtom_(i); double c = qV2e*q[atomIdx]; for (int j = 0; j < dimOffset; j ++) _atomElectricalForce_(i,j) = 0.; for (int j = dimOffset; j < nsd; j ++) _atomElectricalForce_(i,j) = c*E(i,j); } // correct field for short range interactions if (includeShortRange_) correct_electrostatic_forces(); } //-------------------------------------------------------- // correct_electrostatic_forces //-------------------------------------------------------- void ExtrinsicModelElectrostatic::correct_electrostatic_forces() { // compute restricted sparse shape function set for each atom // to account for its Green's Function //double qE2f = LammpsInterface::instance()->qe2f(); double qV2e = LammpsInterface::instance()->qv2e(); double * q = LammpsInterface::instance()->atom_charge(); vector<SparseVector<double> > atomicFePotential; int nLocal = atc_->nlocal(); int nGhostLammps = LammpsInterface::instance()->nghost(); int nLocalLammps = LammpsInterface::instance()->nlocal(); int nLocalTotal = nLocalLammps + nGhostLammps; // total number of atoms on this processor atomicFePotential.reserve(nLocalTotal); SparseVector<double> dummy(atc_->num_nodes()); for (int i = 0; i < nLocalTotal; i++) atomicFePotential.push_back(dummy); // compute local potential contributions from atoms on this processor InterscaleManager & interscaleManager(atc_->interscale_manager()); const SPAR_MAT & myShpFcn((interscaleManager.per_atom_sparse_matrix("Interpolant"))->quantity()); for (int i = 0; i < nLocal; i++) { DenseVector<INDEX> nodeIndices; DENS_VEC nodeValues; myShpFcn.row(i,nodeValues,nodeIndices); int atomIdx = atc_->internalToAtom_(i); //double c = qE2f*q[atomIdx]; //double c = qV2e*q[atomIdx]; //nodeValues *= c; nodeValues *= q[atomIdx]; for (int j = 0; j < nodeIndices.size(); j++) atomicFePotential[atomIdx].add_scaled(greensFunctions_[nodeIndices(j)],nodeValues(j)); } // compute local potential contribtutions for lammps ghost atoms // which are known to ATC, // this will grab both processor and periodic neighbors, // so we need to add in neighbor contributions using lammps indices // rather than atc indices or we could potentially // double count periodic contributions double ** xatom = LammpsInterface::instance()->xatom(); const int * mask = LammpsInterface::instance()->atom_mask(); int nodesPerElement = ((atc_->feEngine_)->fe_mesh())->num_nodes_per_element(); int nsd = atc_->nsd(); for (int i = nLocalLammps; i < nLocalTotal; i++) { if (mask[i] & atc_->groupbit_) { DENS_VEC coords(nsd); coords.copy(xatom[i],nsd); Array<int> nodeIndices(nodesPerElement); DENS_VEC nodeValues(nodesPerElement); (atc_->feEngine_)->shape_functions(coords,nodeValues,nodeIndices); //double c = qV2e*q[i]; //nodeValues *= c; nodeValues *= q[i]; for (int j = 0; j < nodeIndices.size(); j++) { atomicFePotential[i].add_scaled(greensFunctions_[nodeIndices(j)],nodeValues(j)); } } } // Get sparse vectors of derivatives at each atom // to compute this only when the shape functions change vector<vector<SparseVector<double> > > atomicDerivatives; atomicDerivatives.reserve(nLocal); for (int i = 0; i < nLocal; i++) { // determine shape function derivatives at atomic location // and construct sparse vectors to store derivative data vector<SparseVector<double> > derivativeVectors; derivativeVectors.reserve(nsd); for (int j = 0; j < nsd; j++) derivativeVectors.push_back(dummy); atomicDerivatives.push_back(derivativeVectors); InterscaleManager & interscaleManager(atc_->interscale_manager()); const SPAR_MAT_VEC & shapeFucntionDerivatives((interscaleManager.vector_sparse_matrix("InterpolantGradient"))->quantity()); for (int j = 0; j < nsd; j++) { DenseVector<INDEX> nodeIndices; DENS_VEC nodeValues; shapeFucntionDerivatives[j]->row(i,nodeValues,nodeIndices); for (int k = 0; k < nodeIndices.size(); k++) atomicDerivatives[i][j](nodeIndices(k)) = nodeValues(k); } } // loop over all atoms and correct their efield based on all their // neighbor's local efield response // need to use specific coulombic cutoff from different pairs // see pair_coul_cut for an example of the data structures // unfortunately don't know how to get at this data in general // beyond a cast from the LAMMPS pair object (see force.h). // Until this is fixed, only use this method with the coulombic force // the same for all pairs and equal to the largest force cutoff. // Probably the best fix is to implement our own pair style for this. double cutoffRadius = LammpsInterface::instance()->pair_cutoff(); double cutoffSq = cutoffRadius*cutoffRadius; int inum = LammpsInterface::instance()->neighbor_list_inum(); int * ilist = LammpsInterface::instance()->neighbor_list_ilist(); int * numneigh = LammpsInterface::instance()->neighbor_list_numneigh(); int ** firstneigh = LammpsInterface::instance()->neighbor_list_firstneigh(); // loop over neighbors of my atoms for (int ii = 0; ii < inum; ii++) { int i = ilist[ii]; if (mask[i] & atc_->groupbit_) { double xtmp = xatom[i][0]; double ytmp = xatom[i][1]; double ztmp = xatom[i][2]; int * jlist = firstneigh[i]; int jnum = numneigh[i]; for (int jj = 0; jj < jnum; jj++) { int j = jlist[jj]; if (mask[j] & atc_->groupbit_) { //double factor_coul = LammpsInterface::instance()->coulomb_factor(j); LammpsInterface::instance()->neighbor_remap(j); double delx = xtmp - xatom[j][0]; double dely = ytmp - xatom[j][1]; double delz = ztmp - xatom[j][2]; double rsq = delx*delx + dely*dely + delz*delz; if (rsq < cutoffSq) { DENS_VEC efield(nsd); efield = 0.; int atcIdx = atc_->atomToInternal_[i]; for (int k = 0; k < nsd; k++) efield(k) = -1.*dot(atomicDerivatives[atcIdx][k],atomicFePotential[j]); // apply correction in atomic forces //double c = factor_coul*qE2f*q[i]; //double c = factor_coul*qV2e*q[i]; double c = qV2e*q[i]; for (int k = 0; k < nsd; k++) { if ((!useSlab_) || (k==nsd)) { //f[i][k] -= c*efield(k); _atomElectricalForce_(atcIdx,k) -= c*efield(k); } } } } } } } } #ifdef CHARGED_SURFACE //-------------------------------------------------------- // add_charged_surface //-------------------------------------------------------- void ExtrinsicModelElectrostatic::add_charged_surface(const string & facesetName, const double chargeDensity) { // get faceset information int nNodes = atc_->num_nodes(); const FE_Mesh * feMesh = (atc_->feEngine_)->fe_mesh(); const set< pair <int,int> > * faceset = & ( feMesh->faceset(facesetName)); // set face sources to all point at one function for use in integration SURFACE_SOURCE faceSources; XT_Function * f = XT_Function_Mgr::instance()->constant_function(1.); set< pair<int,int> >::const_iterator iset; for (iset = faceset->begin(); iset != faceset->end(); iset++) { pair<int,int> face = *iset; // allocate Array < XT_Function * > & dof = faceSources[ELECTRIC_POTENTIAL][face]; dof.reset(1); dof(0) = f; } // Get associated nodeset set<int> nodeset; feMesh->faceset_to_nodeset(facesetName,nodeset); // Get coordinates of each node in face set map<int,pair<DENS_VEC,double> > & myFaceset = chargedSurfaces_[facesetName]; set<int>::const_iterator myNode; for (myNode = nodeset.begin(); myNode != nodeset.end(); myNode++) { DENS_VEC myCoords = feMesh->nodal_coordinates(*myNode); pair<DENS_VEC,double> myPair(myCoords,0.); myFaceset[*myNode] = myPair; } // computed integrals of nodal shape functions on face FIELDS nodalFaceWeights; nodalFaceWeights[ELECTRIC_POTENTIAL].reset(nNodes,1); Array<bool> fieldMask(NUM_FIELDS); fieldMask(ELECTRIC_POTENTIAL) = true; (atc_->feEngine_)->add_fluxes(fieldMask,0.,faceSources,nodalFaceWeights); // set up data structure holding charged faceset information FIELDS sources; double coulombConstant = LammpsInterface::instance()->coulomb_constant(); map<int,pair<DENS_VEC,double> >::iterator myNodeData; for (myNodeData = myFaceset.begin(); myNodeData != myFaceset.end(); myNodeData++) { // 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 double rcritSq = LammpsInterface::instance()->pair_cutoff(); rcritSq *= rcritSq; int nodalIndex = myNodeData->first; DENS_VEC myCoords((myNodeData->second).first); double xtArgs[8]; xtArgs[0] = myCoords(0); xtArgs[1] = myCoords(1); xtArgs[2] = myCoords(2); xtArgs[3] = 1.; xtArgs[4] = 1.; xtArgs[5] = 1.; xtArgs[6] = coulombConstant*chargeDensity; xtArgs[7] = -1.; string radialPower = "radial_power"; f = XT_Function_Mgr::instance()->function(radialPower,8,xtArgs); for (iset = faceset->begin(); iset != faceset->end(); iset++) { pair<int,int> face = *iset; // allocate Array < XT_Function * > & dof = faceSources[ELECTRIC_POTENTIAL][face]; dof.reset(1); dof(0) = f; } // perform integration to get quantities at nodes on facesets // V_J' = int_S N_J k*sigma/|x_I - x_s| dS sources[ELECTRIC_POTENTIAL].reset(nNodes,1); (atc_->feEngine_)->add_fluxes(fieldMask,0.,faceSources,sources); double myPotential = 0.; // sum over all nodes in faceset to get total potential: // V_I = sum_J VJ' const DENS_MAT & myPotentialSource(sources[ELECTRIC_POTENTIAL].quantity()); nodalChargePotential_[facesetName][nodalIndex] = myPotentialSource(nodalIndex,0); for (myNode = nodeset.begin(); myNode != nodeset.end(); myNode++) myPotential += myPotentialSource(*myNode,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(myPotential); (atc_->prescribedDataMgr_)->fix_field(nodalIndex,ELECTRIC_POTENTIAL,0,f); // compute effective charge at each node I // multiply charge density by integral of N_I over face (myNodeData->second).second = (nodalFaceWeights[ELECTRIC_POTENTIAL].quantity())(nodalIndex,0)*chargeDensity; } } //-------------------------------------------------------- // apply_charged_surfaces //-------------------------------------------------------- void ExtrinsicModelElectrostatic::apply_charged_surfaces (MATRIX & potential) { //double qE2f = LammpsInterface::instance()->qe2f(); double qV2e = LammpsInterface::instance()->qv2e(); double qqrd2e = LammpsInterface::instance()->qqrd2e(); //double ** fatom = LammpsInterface::instance()->fatom(); - double * qatom = LammpsInterface::instance()->atom_charge(); + //double * qatom = LammpsInterface::instance()->atom_charge(); + InterscaleManager & interscaleManager(atc_->interscale_manager()); + const DENS_MAT & qatom((interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE))->quantity()); double cutoffRadius = LammpsInterface::instance()->pair_cutoff(); double cutoffSq = cutoffRadius*cutoffRadius; - int nLocal = atc_->nlocal(); + int nLocal = qatom.nRows(); int nsd = atc_->nsd(); int nNodes = atc_->num_nodes(); double penalty = poissonSolver_->penalty_coefficient(); if (penalty <= 0.0) throw ATC_Error("ExtrinsicModelElectrostatic::apply_charged_surfaces expecting non zero penalty"); SparseVector<double> dummy(atc_->num_nodes()); map<string,map<int,pair<DENS_VEC,double> > >::const_iterator isurface; for (isurface = chargedSurfaces_.begin(); isurface != chargedSurfaces_.end(); isurface++) { string facesetName = isurface->first; map<int,pair<DENS_VEC,double> >::const_iterator inode; for (inode = (isurface->second).begin(); inode != (isurface->second).end(); inode++) { int nodeId = inode->first; DENS_VEC nodalCoords = (inode->second).first; double nodalCharge = (inode->second).second; double nodalPotential = nodalChargePotential_[facesetName][nodeId]; PerAtomQuantity<double> * atomicCoords = (atc_->interscale_manager()).per_atom_quantity("AtomicCoarseGrainingPositions"); const DENS_MAT & myAtomicCoords(atomicCoords->quantity()); for (int i = 0; i < nLocal; i++) { - int atomIdx = atc_->internalToAtom_(i); - if (abs(qatom[atomIdx]) > 0) { + if (abs(qatom(i,0)) > 0) { double distanceSq = 0.; double deltaX[3]; for (int j = 0; j < nsd; j++) { deltaX[j] = myAtomicCoords(i,j) - nodalCoords(j); distanceSq += deltaX[j]*deltaX[j]; } if (distanceSq < cutoffSq) { // first apply pairwise coulombic interaction if (!useSlab_) { - double coulForce = qqrd2e*nodalCharge*qatom[atomIdx]/(distanceSq*sqrtf(distanceSq)); + double coulForce = qqrd2e*nodalCharge*qatom(i,0)/(distanceSq*sqrtf(distanceSq)); for (int j = 0; j < nsd; j++) //fatom[atomIdx][j] += deltaX[j]*coulForce; _atomElectricalForce_(i,j) += deltaX[j]*coulForce; } // second correct for FE potential induced by BCs // determine shape function derivatives at atomic location // and construct sparse vectors to store derivative data vector<SparseVector<double> > derivativeVectors; derivativeVectors.reserve(nsd); - InterscaleManager & interscaleManager(atc_->interscale_manager()); const SPAR_MAT_VEC & shapeFunctionDerivatives((interscaleManager.vector_sparse_matrix("InterpolantGradient"))->quantity()); for (int j = 0; j < nsd; j++) { DenseVector<INDEX> nodeIndices; DENS_VEC nodeValues; shapeFunctionDerivatives[j]->row(i,nodeValues,nodeIndices); derivativeVectors.push_back(dummy); for (int k = 0; k < nodeIndices.size(); k++) derivativeVectors[j](nodeIndices(k)) = nodeValues(k); } // compute greens function from charge quadrature SparseVector<double> shortFePotential(nNodes); shortFePotential.add_scaled(greensFunctions_[nodeId],penalty*nodalPotential); // compute electric field induced by charge DENS_VEC efield(nsd); efield = 0.; for (int j = 0; j < nsd; j++) efield(j) = -.1*dot(derivativeVectors[j],shortFePotential); // apply correction in atomic forces //double c = qE2f*qatom[atomIdx]; - double c = qV2e*qatom[atomIdx]; + double c = qV2e*qatom(i,0); for (int j = 0; j < nsd; j++) if ((!useSlab_) || (j==nsd)) { //fatom[atomIdx][j] -= c*efield(j); _atomElectricalForce_(i,j) -= c*efield(j); } } } } } } } #endif //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelElectrostaticMomentum //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelElectrostaticMomentum::ExtrinsicModelElectrostaticMomentum (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModelElectrostatic(modelManager,modelType,matFileName) { if (physicsModel_) delete physicsModel_; if (modelType == ELECTROSTATIC) { physicsModel_ = new PhysicsModelElectrostatic(matFileName); } else { physicsModel_ = new PhysicsModelElectrostaticEquilibrium(matFileName); } // set up correct masks for coupling rhsMaskIntrinsic_(VELOCITY,SOURCE) = true; atc_->fieldMask_(VELOCITY,EXTRINSIC_SOURCE) = true; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelElectrostaticMomentum::~ExtrinsicModelElectrostaticMomentum() { // do nothing } //-------------------------------------------------------- // modify //-------------------------------------------------------- bool ExtrinsicModelElectrostaticMomentum::modify(int narg, char **arg) { bool match = false; if (!match) match = ExtrinsicModelElectrostatic::modify(narg,arg); return match; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelElectrostaticMomentum::initialize() { ExtrinsicModelElectrostatic::initialize(); int nNodes = atc_->num_nodes(); int nsd = atc_->nsd(); rhs_[VELOCITY].reset(nNodes,nsd); } //-------------------------------------------------------- // set coupling source terms //-------------------------------------------------------- void ExtrinsicModelElectrostaticMomentum::set_sources(FIELDS & fields, FIELDS & sources) { // compute charge density if (modelType_ == ELECTROSTATIC_EQUILIBRIUM) { DENS_MAN & n = atc_->field(ELECTRON_DENSITY); atc_->nodal_projection(ELECTRON_DENSITY,physicsModel_,n); } // else { // FIELDS rhs; // Array2D<bool> mask; // mask(ELECTRON_DENSITY,SOURCE) = true; // atc_->evaluate_rhs_integral(mask,fields,rhs,FULL_DOMAIN,physicsModel_); // atc_->apply_inverse_mass_matrix(rhs[ELECTRON_DENSITY].quantity(),n.set_quantity(),ELECTRON_DENSITY); // } // compute source term with appropriate masking and physics model atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, sources, atc_->source_integration(), physicsModel_); //(sources[VELOCITY].quantity()).print("V SRC"); } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelElectrostaticMomentum::output(OUTPUT_LIST & outputData) { ExtrinsicModelElectrostatic::output(outputData); } }; diff --git a/lib/atc/ExtrinsicModelTwoTemperature.cpp b/lib/atc/ExtrinsicModelTwoTemperature.cpp index ddacbf229..2be6cfeb3 100644 --- a/lib/atc/ExtrinsicModelTwoTemperature.cpp +++ b/lib/atc/ExtrinsicModelTwoTemperature.cpp @@ -1,270 +1,295 @@ // ATC Headers #include "ExtrinsicModelTwoTemperature.h" #include "ATC_Error.h" #include "FieldEulerIntegrator.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PrescribedDataManager.h" #include "PhysicsModel.h" using std::string; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelTwoTemperature //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ExtrinsicModelTwoTemperature::ExtrinsicModelTwoTemperature (ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, string matFileName) : ExtrinsicModel(modelManager,modelType,matFileName), electronTimeIntegration_(TimeIntegrator::IMPLICIT), temperatureIntegrator_(NULL), nsubcycle_(1), exchangeFlag_(true), baseSize_(0) { physicsModel_ = new PhysicsModelTwoTemperature(matFileName); // set up correct masks for coupling rhsMaskIntrinsic_.reset(NUM_FIELDS,NUM_FLUX); rhsMaskIntrinsic_ = false; rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = true; atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = true; } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ExtrinsicModelTwoTemperature::~ExtrinsicModelTwoTemperature() { if (temperatureIntegrator_) delete temperatureIntegrator_; } //-------------------------------------------------------- // modify //-------------------------------------------------------- bool ExtrinsicModelTwoTemperature::modify(int narg, char **arg) { bool match = false; int argIndx = 0; // energy exchange switch /*! \page man_extrinsic_exchange fix_modify AtC extrinsic exchange \section syntax fix_modify AtC extrinsic exchange <on|off> \section examples <TT> fix_modify AtC extrinsic exchange on </TT> \n \section description Switches energy exchange between the MD system and electron system on and off \section restrictions Only valid for use with two_temperature type of AtC fix. \section related see \ref man_fix_atc \section default on */ if (strcmp(arg[argIndx],"exchange")==0) { argIndx++; if (strcmp(arg[argIndx],"off")==0) { exchangeFlag_ = false; rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = false; atc_->fieldMask_(ELECTRON_TEMPERATURE,SOURCE) = false; atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = false; } else { exchangeFlag_ = true; rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = true; atc_->fieldMask_(ELECTRON_TEMPERATURE,SOURCE) = true; atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = true; } match = true; } // end "exchange" // electron integration type /*! \page man_electron_integration fix_modify AtC extrinsic electron_integration \section syntax fix_modify AtC extrinsic electron_integration <integration_type> <num_subcyle_steps(optional)> \n - integration_type (string) = explicit | implicit | steady \n - num_subcycle_steps (int), optional = number of subcycle steps for the electron time integration \section examples <TT> fix_modify AtC extrinsic electron_integration implicit </TT> \n <TT> fix_modify AtC extrinsic electron_integration explicit 100 </TT> \n \section description Switches between integration scheme for the electron temperature. The number of subcyling steps used to integrate the electron temperature 1 LAMMPS timestep can be manually adjusted to capture fast electron dynamics. \section restrictions For use only with two_temperature type of AtC fix ( see \ref man_fix_atc ) \n \section default implicit\n subcycle_steps = 1 */ else if (strcmp(arg[argIndx],"electron_integration")==0) { argIndx++; nsubcycle_ = 1; - if (strcmp(arg[argIndx],"explicit")==0) { + if (strcmp(arg[argIndx],"explicit")==0) { electronTimeIntegration_ = TimeIntegrator::EXPLICIT; match = true; } else if (strcmp(arg[argIndx],"implicit")==0) { electronTimeIntegration_ = TimeIntegrator::IMPLICIT; match = true; } + else if (strcmp(arg[argIndx],"direct")==0) { + electronTimeIntegration_ = TimeIntegrator::DIRECT; + match = true; + } else if (strcmp(arg[argIndx],"steady")==0) { electronTimeIntegration_ = TimeIntegrator::STEADY; match = true; } + else if (strcmp(arg[argIndx],"off")==0) { + electronTimeIntegration_ = TimeIntegrator::NONE; + match = true; + } if (narg > ++argIndx) nsubcycle_ = atoi(arg[argIndx]); } // end "electron_integration" if (!match) { match = ExtrinsicModel::modify(narg, arg); } return match; } //-------------------------------------------------------- // initialize //-------------------------------------------------------- void ExtrinsicModelTwoTemperature::initialize() { ExtrinsicModel::initialize(); int nNodes = atc_->num_nodes(); rhs_[TEMPERATURE].reset(nNodes,1); rhs_[ELECTRON_TEMPERATURE].reset(nNodes,1); // set up electron temperature integrator Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; for (int i = 0; i < NUM_FLUX; i++) { rhsMask(ELECTRON_TEMPERATURE,i) = atc_->fieldMask_(ELECTRON_TEMPERATURE,i); } + if (electronTimeIntegration_ == TimeIntegrator::NONE) { + temperatureIntegrator_ = NULL; + return; + } if (temperatureIntegrator_) delete temperatureIntegrator_; if (electronTimeIntegration_ == TimeIntegrator::STEADY) { throw ATC_Error("not implemented"); } else if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { double alpha = 1; // backwards Euler temperatureIntegrator_ = new FieldImplicitEulerIntegrator( ELECTRON_TEMPERATURE, physicsModel_, atc_->feEngine_, atc_, rhsMask, alpha); } - else { + else if (electronTimeIntegration_ == TimeIntegrator::EXPLICIT) { temperatureIntegrator_ = new FieldExplicitEulerIntegrator( ELECTRON_TEMPERATURE, physicsModel_, atc_->feEngine_, atc_, rhsMask); } + double dt = atc_->lammpsInterface_->dt(); + double time = atc_->time(); + temperatureIntegrator_->initialize(dt,time,atc_->fields_); // set up mass matrix Array<FieldName> massMask(1); massMask = ELECTRON_TEMPERATURE; (atc_->feEngine_)->compute_lumped_mass_matrix(massMask,atc_->fields_,physicsModel_,atc_->elementToMaterialMap_,atc_->massMats_); atc_->massMatsInv_[ELECTRON_TEMPERATURE] = inv(atc_->massMats_[ELECTRON_TEMPERATURE].quantity()); } //-------------------------------------------------------- // pre initial integration //-------------------------------------------------------- void ExtrinsicModelTwoTemperature::pre_init_integrate() { +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->print_msg_once("start ttm evolution",true,false); +#endif + double dt = atc_->lammpsInterface_->dt(); double time = atc_->time(); // integrate fast electron variable/s // note: atc calls set_sources in pre_final_integrate atc_->set_fixed_nodes(); - double idt = dt/nsubcycle_; - for (int i = 0; i < nsubcycle_ ; ++i) { - temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) + && temperatureIntegrator_ ) { + double idt = dt/nsubcycle_; + for (int i = 0; i < nsubcycle_ ; ++i) { + temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + } } +#ifdef ATC_VERBOSE + ATC::LammpsInterface::instance()->print_msg_once("...done",false,true); +#endif } //-------------------------------------------------------- // set coupling source terms //-------------------------------------------------------- void ExtrinsicModelTwoTemperature::set_sources(FIELDS & fields, FIELDS & sources) { // compute source term with appropriate masking and physics model atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, sources, atc_->source_integration(), physicsModel_); } //-------------------------------------------------------- // output //-------------------------------------------------------- void ExtrinsicModelTwoTemperature::output(OUTPUT_LIST & outputData) { // nodal data outputData["dot_electron_temperature"] = & rhs_[ELECTRON_TEMPERATURE].set_quantity(); // global data if (atc_->lammpsInterface_->rank_zero()) { double T_mean = ((atc_->field(ELECTRON_TEMPERATURE)).quantity()).col_sum(0)/atc_->nNodes_; atc_->feEngine_->add_global("electron_temperature_mean", T_mean); double T_stddev = ((atc_->field(ELECTRON_TEMPERATURE)).quantity()).col_stdev(0); atc_->feEngine_->add_global("electron_temperature_std_dev", T_stddev); } } //-------------------------------------------------------- // size_vector //-------------------------------------------------------- int ExtrinsicModelTwoTemperature::size_vector(int intrinsicSize) { baseSize_ = intrinsicSize; return 2; } //-------------------------------------------------------- // compute_vector //-------------------------------------------------------- bool ExtrinsicModelTwoTemperature::compute_vector(int n, double & value) { // output[1] = total electron energy // output[2] = average electron temperature if (n == baseSize_) { Array<FieldName> mask(1); FIELD_MATS energy; mask(0) = ELECTRON_TEMPERATURE; (atc_->feEngine_)->compute_energy(mask, atc_->fields(), physicsModel_, atc_->elementToMaterialMap_, energy); // convert to lammps energy units double mvv2e = (atc_->lammps_interface())->mvv2e(); double electronEnergy = mvv2e * energy[ELECTRON_TEMPERATURE].col_sum(); value = electronEnergy; return true; } else if (n == baseSize_+1) { double electronTemperature = ((atc_->field(ELECTRON_TEMPERATURE)).quantity()).col_sum()/(atc_->nNodes_); value = electronTemperature; return true; } return false; } }; diff --git a/lib/atc/ExtrinsicModelTwoTemperature.h b/lib/atc/ExtrinsicModelTwoTemperature.h index 4c3038bba..bf1979f40 100644 --- a/lib/atc/ExtrinsicModelTwoTemperature.h +++ b/lib/atc/ExtrinsicModelTwoTemperature.h @@ -1,79 +1,79 @@ #ifndef EXTRINSIC_MODEL_TWO_TEMPERATURE #define EXTRINSIC_MODEL_TWO_TEMPERATURE #include <string> #include "ExtrinsicModel.h" #include "FieldEulerIntegrator.h" namespace ATC { class ATC_Coupling; class PrescribedDataManager; class ExtrinsicModel; class PhysicsModel; /** * @class ExtrinsicModelTwoTemperature * @brief add electron temperature physics to phonon physics * owned field: ELECTRON_TEMPERATURE */ //-------------------------------------------------------- //-------------------------------------------------------- // Class ExtrinsicModelTwoTemperature //-------------------------------------------------------- //-------------------------------------------------------- class ExtrinsicModelTwoTemperature : public ExtrinsicModel { public: // constructor ExtrinsicModelTwoTemperature(ExtrinsicModelManager * modelManager, ExtrinsicModelType modelType, std::string matFileName); // destructor virtual ~ExtrinsicModelTwoTemperature(); /** parser/modifier */ virtual bool modify(int narg, char **arg); /** pre time integration */ virtual void initialize(); - /** set up LAMMPS display variables */ - virtual int size_vector(int externalSize); - - /** get LAMMPS display variables */ - virtual bool compute_vector(int n, double & value); - /** Predictor phase, executed before Verlet */ virtual void pre_init_integrate(); /** Set sources to AtC equation */ virtual void set_sources(FIELDS & fields, FIELDS & sources); /** Add model-specific output data */ virtual void output(OUTPUT_LIST & outputData); + /** set up LAMMPS display variables */ + virtual int size_vector(int externalSize); + + /** get LAMMPS display variables */ + virtual bool compute_vector(int n, double & value); + protected: /** electron time integration flag */ TimeIntegrator::TimeIntegrationType electronTimeIntegration_; /** electron time integration flag */ FieldEulerIntegrator * temperatureIntegrator_; /** number of electron time integration subscycles */ int nsubcycle_; /** flag for turning off exchange during equilibration */ bool exchangeFlag_; /** offset/size for LAMMPS display output */ int baseSize_; }; }; #endif diff --git a/lib/atc/FE_Element.cpp b/lib/atc/FE_Element.cpp index 6bc9d390a..cfc59aa60 100644 --- a/lib/atc/FE_Element.cpp +++ b/lib/atc/FE_Element.cpp @@ -1,727 +1,747 @@ // ATC header files #include "ATC_Error.h" #include "FE_Mesh.h" #include "FE_Element.h" #include "FE_Interpolate.h" #include "LinearSolver.h" #include "PolynomialSolver.h" #include "Utility.h" // Other headers #include "math.h" using ATC_Utility::dbl_geq; using ATC_Utility::det3; using std::vector; namespace ATC { static const int localCoordinatesMaxIterations_ = 40; static const double localCoordinatesTolerance = 1.e-09; // ============================================================= // class FE_Element // ============================================================= FE_Element::FE_Element(const int nSD, int numFaces, int numNodes, int numFaceNodes, int numNodes1d) : nSD_(nSD), numFaces_(numFaces), numNodes_(numNodes), numFaceNodes_(numFaceNodes), numNodes1d_(numNodes1d), tolerance_(localCoordinatesTolerance), projectionGuess_(COORDINATE_ALIGNED) { feInterpolate_ = NULL; } FE_Element::~FE_Element() { if (feInterpolate_) delete feInterpolate_; } int FE_Element::num_ips() const { return feInterpolate_->num_ips(); } int FE_Element::num_face_ips() const { return feInterpolate_->num_face_ips(); } void FE_Element::face_coordinates(const DENS_MAT &eltCoords, const int faceID, DENS_MAT & faceCoords) const { faceCoords.reset(nSD_, numFaceNodes_); for (int inode=0; inode < numFaceNodes_; inode++) { int id = localFaceConn_(faceID,inode); for (int isd=0; isd<nSD_; isd++) { faceCoords(isd,inode) = eltCoords(isd,id); } } } void FE_Element::mapping(const int inode, vector<int> &mapping) const { for (int iSD=0; iSD<nSD_; ++iSD) { mapping[iSD] = static_cast<int>((localCoords_(iSD,inode)+1)/2* (numNodes1d_-1)); } } DENS_VEC FE_Element::local_coords_1d() const { DENS_VEC localCoords1d(numNodes1d_); for (int inode1d=0; inode1d<numNodes1d_; ++inode1d) { localCoords1d(inode1d) = (double(inode1d)/double(numNodes1d_-1))*2 - 1; } return localCoords1d; } void FE_Element::centroid(const DENS_MAT &eltCoords, DENS_VEC ¢roid) const { centroid.reset(nSD_); for (int i = 0; i < nSD_; i++) { centroid(i) = eltCoords.row_mean(i); } } // ------------------------------------------------------------- // generic conversion from global to local coordinates using // Newton's method to solve the nonliear equation that arises // ------------------------------------------------------------- bool FE_Element::local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { // initial guess DENS_VEC xiGuess(nSD_); this->initial_local_coordinates(eltCoords,x,xiGuess); // clip out-of-range guesses if (fabs(xiGuess(0)) > 1.0) xiGuess(0) = 0.; if (fabs(xiGuess(1)) > 1.0) xiGuess(1) = 0.; if (fabs(xiGuess(2)) > 1.0) xiGuess(2) = 0.; // iteratively solve the equation by calculating the global // position of the guess and bringing the difference between it // and the actual global position of x to zero // // uses Newton's method DENS_VEC N(numNodes_); DENS_MAT dNdr(nSD_,numNodes_); DENS_VEC xGuess(nSD_); DENS_VEC xDiff(nSD_); DENS_MAT eltCoordsT = transpose(eltCoords); int count = 0; bool converged = false; while (count < localCoordinatesMaxIterations_) { feInterpolate_->compute_N_dNdr(xiGuess,N,dNdr); xGuess = N*eltCoordsT; xDiff = xGuess-x; // determine if the guess is close enough. // if it is, take it and run // if not, use Newton's method to update the guess if (!dbl_geq(abs(xDiff(0)),tolerance_) && !dbl_geq(abs(xDiff(1)),tolerance_) && !dbl_geq(abs(xDiff(2)),tolerance_)) { converged = true; xi = xiGuess; break; } else { xiGuess = xiGuess - transpose(inv(dNdr*eltCoordsT))*xDiff; } ++count; } return converged; } // ------------------------------------------------------------- // guess for initial local coordinates // ------------------------------------------------------------- void FE_Element::initial_local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { xi.reset(nSD_); if (projectionGuess_ == COORDINATE_ALIGNED) { double min=0; double max=0; for (int i=0; i<nSD_; ++i) { bounds_in_dim(eltCoords,i,min,max); xi(i) = 2.0*(x(i)-min)/(max-min) - 1.0; } } else if (projectionGuess_ == CENTROID_LINEARIZED) { DENS_VEC xi0(nSD_); xi0 = 0; DENS_VEC x0(nSD_), dx(nSD_); centroid(eltCoords,x0); dx = x - x0; vector<DENS_VEC> ts; ts.reserve(nSD_); tangents(eltCoords,xi0,ts); DENS_VEC & t1 = ts[0]; DENS_VEC & t2 = ts[1]; DENS_VEC & t3 = ts[2]; double J = det3(t1.ptr(),t2.ptr(),t3.ptr()); double J1 = det3(dx.ptr(),t2.ptr(),t3.ptr()); double J2 = det3(t1.ptr(),dx.ptr(),t3.ptr()); double J3 = det3(t1.ptr(),t2.ptr(),dx.ptr()); xi(0) = J1/J; xi(1) = J2/J; xi(2) = J3/J; } + else if (projectionGuess_ == TWOD_ANALYTIC) { + // assume x-y planar and HEX8 + double x0 = x(0), y0 = x(1); + double X[4] = {eltCoords(0,0),eltCoords(0,1),eltCoords(0,2),eltCoords(0,3)}; + double Y[4] = {eltCoords(1,0),eltCoords(1,1),eltCoords(1,2),eltCoords(1,3)}; + double c[3]={0,0,0}; + c[0] = y0*X[0] - y0*X[1] - y0*X[2] + y0*X[3] - x0*Y[0] + (X[1]*Y[0])*0.5 + (X[2]*Y[0])*0.5 + x0*Y[1] - (X[0]*Y[1])*0.5 - (X[3]*Y[1])*0.5 + x0*Y[2] - (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - x0*Y[3] + (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; + c[1] = -(y0*X[0]) + y0*X[1] - y0*X[2] + y0*X[3] + x0*Y[0] - X[1]*Y[0] - x0*Y[1] + X[0]*Y[1] + x0*Y[2] - X[3]*Y[2] - x0*Y[3] + X[2]*Y[3]; + c[1] = (X[1]*Y[0])*0.5 - (X[2]*Y[0])*0.5 - (X[0]*Y[1])*0.5 + (X[3]*Y[1])*0.5 + (X[0]*Y[2])*0.5 - (X[3]*Y[2])*0.5 - (X[1]*Y[3])*0.5 + (X[2]*Y[3])*0.5; + double xi2[2]={0,0}; + int nroots = solve_quadratic(c,xi2); + if (nroots == 0) throw ATC_Error("no real roots in 2D analytic projection guess"); + double xi1[2]={0,0}; + xi1[0] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); + xi1[1] = (4*x0 - X[0] + xi2[0]*X[0] - X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0] - X[0] - xi2[0]*X[0])/(-X[0] + xi2[0]*X[0] + X[0] - xi2[0]*X[0] + X[0] + xi2[0]*X[0] - X[0] - xi2[0]*X[0]); + // choose which one gives back x + xi(0) = xi1[0]; + xi(1) = xi2[0]; + xi(2) = 0; + } } bool FE_Element::range_check(const DENS_MAT &eltCoords, const DENS_VEC &x) const { double min=0; double max=0; for (int i=0; i<nSD_; ++i) { bounds_in_dim(eltCoords,i,min,max); if (!dbl_geq(x(i),min) || !dbl_geq(max,x(i))) return false; } return true; } // ------------------------------------------------------------- // Note: Only works for convex elements with planar faces with // outward normals // ------------------------------------------------------------- bool FE_Element::contains_point(const DENS_MAT &eltCoords, const DENS_VEC &x) const { if (! range_check(eltCoords,x) ) return false; DENS_MAT faceCoords; DENS_VEC normal; normal.reset(nSD_); DENS_VEC faceToPoint; double dot; bool inside = true; for (int faceID=0; faceID<numFaces_; ++faceID) { face_coordinates(eltCoords, faceID, faceCoords); feInterpolate_->face_normal(faceCoords, 0, normal); faceToPoint = x - column(faceCoords, 0); dot = normal.dot(faceToPoint); if (dbl_geq(dot,0)) { inside = false; break; } } return inside; } // ------------------------------------------------------------- // returns the minimum and maximum values of an element in the // specified dimension // ------------------------------------------------------------- void FE_Element::bounds_in_dim(const DENS_MAT &eltCoords, const int dim, double &min, double &max) const { int it; // iterate over all nodes min = eltCoords(dim,0); it = 1; while (it < numNodes_) { if (dbl_geq(min,eltCoords(dim,it))) { if (dbl_geq(eltCoords(dim,it),min)) { ++it; } else { // set min to this node's coord in the specified dim, if it's // smaller than the value previously stored min = eltCoords(dim,it); } } else { ++it; } } max = eltCoords(dim,0); it = 1; while (it < numNodes_) { if (dbl_geq(max,eltCoords(dim,it))) { ++it; } else { // same, except max/larger max = eltCoords(dim,it); } } } // ------------------------------------------------------------- // shape_function calls should stay generic at all costs // ------------------------------------------------------------- void FE_Element::shape_function(const VECTOR &xi, DENS_VEC &N) const { feInterpolate_->shape_function(xi, N); } void FE_Element::shape_function(const DENS_MAT eltCoords, const VECTOR &x, DENS_VEC &N) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function(xi, N); } void FE_Element::shape_function(const DENS_MAT eltCoords, const VECTOR &x, DENS_VEC &N, DENS_MAT &dNdx) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function(eltCoords, xi, N, dNdx); } void FE_Element::shape_function_derivatives(const DENS_MAT eltCoords, const VECTOR &x, DENS_MAT &dNdx) { DENS_VEC xi; local_coordinates(eltCoords, x, xi); feInterpolate_->shape_function_derivatives(eltCoords, xi, dNdx); } void FE_Element::shape_function(const DENS_MAT eltCoords, DENS_MAT &N, vector<DENS_MAT> &dN, DIAG_MAT &weights) { feInterpolate_->shape_function(eltCoords, N, dN, weights); } void FE_Element::face_shape_function(const DENS_MAT &eltCoords, const int faceID, DENS_MAT &N, DENS_MAT &n, DIAG_MAT &weights) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); feInterpolate_->face_shape_function(eltCoords, faceCoords, faceID, N, n, weights); } void FE_Element::face_shape_function(const DENS_MAT &eltCoords, const int faceID, DENS_MAT &N, vector<DENS_MAT> &dN, vector<DENS_MAT> &Nn, DIAG_MAT &weights) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); feInterpolate_->face_shape_function(eltCoords, faceCoords, faceID, N, dN, Nn, weights); } double FE_Element::face_normal(const DENS_MAT &eltCoords, const int faceID, int ip, DENS_VEC &normal) { DENS_MAT faceCoords; face_coordinates(eltCoords, faceID, faceCoords); double J = feInterpolate_->face_normal(faceCoords, ip, normal); return J; } void FE_Element::tangents(const DENS_MAT &eltCoords, const DENS_VEC & localCoords, vector<DENS_VEC> &tangents, const bool normalize) const { feInterpolate_->tangents(eltCoords,localCoords,tangents,normalize); } // ============================================================= // class FE_ElementHex // ============================================================= FE_ElementHex::FE_ElementHex(int numNodes, int numFaceNodes, int numNodes1d) : FE_Element(3, // number of spatial dimensions 6, // number of faces numNodes, numFaceNodes, numNodes1d) { // 3 --- 2 // /| /| // / 0 --/ 1 y // 7 --- 6 / | // | |/ | // 4 --- 5 -----x // / // / // z // Basic properties of element: vol_ = 8.0; faceArea_ = 4.0; // Order-specific information: if (numNodes != 8 && numNodes != 20 && numNodes != 27) { throw ATC_Error("Unrecognized interpolation order specified " "for element class: \n" " element only knows how to construct lin " "and quad elments."); } localCoords_.resize(nSD_,numNodes_); localFaceConn_ = Array2D<int>(numFaces_,numFaceNodes_); // Matrix of local nodal coordinates localCoords_(0,0) = -1; localCoords_(0,4) = -1; localCoords_(1,0) = -1; localCoords_(1,4) = -1; localCoords_(2,0) = -1; localCoords_(2,4) = 1; // localCoords_(0,1) = 1; localCoords_(0,5) = 1; localCoords_(1,1) = -1; localCoords_(1,5) = -1; localCoords_(2,1) = -1; localCoords_(2,5) = 1; // localCoords_(0,2) = 1; localCoords_(0,6) = 1; localCoords_(1,2) = 1; localCoords_(1,6) = 1; localCoords_(2,2) = -1; localCoords_(2,6) = 1; // localCoords_(0,3) = -1; localCoords_(0,7) = -1; localCoords_(1,3) = 1; localCoords_(1,7) = 1; localCoords_(2,3) = -1; localCoords_(2,7) = 1; if (numNodes >= 20) { // only for quads localCoords_(0,8) = 0; localCoords_(0,14) = 1; localCoords_(1,8) = -1; localCoords_(1,14) = 1; localCoords_(2,8) = -1; localCoords_(2,14) = 0; // localCoords_(0,9) = 1; localCoords_(0,15) = -1; localCoords_(1,9) = 0; localCoords_(1,15) = 1; localCoords_(2,9) = -1; localCoords_(2,15) = 0; // localCoords_(0,10) = 0; localCoords_(0,16) = 0; localCoords_(1,10) = 1; localCoords_(1,16) = -1; localCoords_(2,10) = -1; localCoords_(2,16) = 1; // localCoords_(0,11) = -1; localCoords_(0,17) = 1; localCoords_(1,11) = 0; localCoords_(1,17) = 0; localCoords_(2,11) = -1; localCoords_(2,17) = 1; // localCoords_(0,12) = -1; localCoords_(0,18) = 0; localCoords_(1,12) = -1; localCoords_(1,18) = 1; localCoords_(2,12) = 0; localCoords_(2,18) = 1; // localCoords_(0,13) = 1; localCoords_(0,19) = -1; localCoords_(1,13) = -1; localCoords_(1,19) = 0; localCoords_(2,13) = 0; localCoords_(2,19) = 1; if (numNodes >= 27) { // only for quads localCoords_(0,20) = 0; localCoords_(0,24) = 1; localCoords_(1,20) = 0; localCoords_(1,24) = 0; localCoords_(2,20) = 0; localCoords_(2,24) = 0; // localCoords_(0,21) = 0; localCoords_(0,25) = 0; localCoords_(1,21) = 0; localCoords_(1,25) = -1; localCoords_(2,21) = -1; localCoords_(2,25) = 0; // localCoords_(0,22) = 0; localCoords_(0,26) = 0; localCoords_(1,22) = 0; localCoords_(1,26) = 1; localCoords_(2,22) = 1; localCoords_(2,26) = 0; // localCoords_(0,23) = -1; localCoords_(1,23) = 0; localCoords_(2,23) = 0; } } // Matrix of local face connectivity // -x // +x localFaceConn_(0,0) = 0; localFaceConn_(1,0) = 1; localFaceConn_(0,1) = 4; localFaceConn_(1,1) = 2; localFaceConn_(0,2) = 7; localFaceConn_(1,2) = 6; localFaceConn_(0,3) = 3; localFaceConn_(1,3) = 5; if (numNodes >= 20) { localFaceConn_(0,4) = 12; localFaceConn_(1,4) = 9; localFaceConn_(0,5) = 19; localFaceConn_(1,5) = 14; localFaceConn_(0,6) = 15; localFaceConn_(1,6) = 17; localFaceConn_(0,7) = 11; localFaceConn_(1,7) = 13; if (numNodes >= 27) { localFaceConn_(0,8) = 23; localFaceConn_(1,8) = 24; } } // -y // +y localFaceConn_(2,0) = 0; localFaceConn_(3,0) = 3; localFaceConn_(2,1) = 1; localFaceConn_(3,1) = 7; localFaceConn_(2,2) = 5; localFaceConn_(3,2) = 6; localFaceConn_(2,3) = 4; localFaceConn_(3,3) = 2; if (numNodes >= 20) { localFaceConn_(2,4) = 8; localFaceConn_(3,4) = 15; localFaceConn_(2,5) = 13; localFaceConn_(3,5) = 18; localFaceConn_(2,6) = 16; localFaceConn_(3,6) = 14; localFaceConn_(2,7) = 12; localFaceConn_(3,7) = 10; if (numNodes >= 27) { localFaceConn_(2,8) = 25; localFaceConn_(3,8) = 26; } } // -z // +z localFaceConn_(4,0) = 0; localFaceConn_(5,0) = 4; localFaceConn_(4,1) = 3; localFaceConn_(5,1) = 5; localFaceConn_(4,2) = 2; localFaceConn_(5,2) = 6; localFaceConn_(4,3) = 1; localFaceConn_(5,3) = 7; if (numNodes >= 20) { localFaceConn_(4,4) = 8; localFaceConn_(5,4) = 16; localFaceConn_(4,5) = 11; localFaceConn_(5,5) = 17; localFaceConn_(4,6) = 10; localFaceConn_(5,6) = 18; localFaceConn_(4,7) = 9; localFaceConn_(5,7) = 19; if (numNodes >= 27) { localFaceConn_(4,8) = 21; localFaceConn_(5,8) = 22; } } if (numNodes == 8) { feInterpolate_ = new FE_InterpolateCartLin(this); } else if (numNodes == 20) { feInterpolate_ = new FE_InterpolateCartSerendipity(this); } else if (numNodes == 27) { feInterpolate_ = new FE_InterpolateCartLagrange(this); } // determine alignment and skewness to see which guess we should use } FE_ElementHex::~FE_ElementHex() { // Handled by base class } void FE_ElementHex::set_quadrature(FeIntQuadrature type) { feInterpolate_->set_quadrature(HEXA,type); } bool FE_ElementHex::contains_point(const DENS_MAT &eltCoords, const DENS_VEC &x) const { if (! range_check(eltCoords,x) ) return false; DENS_VEC xi; bool converged = local_coordinates(eltCoords,x,xi); if (!converged) return false; for (int i=0; i<nSD_; ++i) { if (!dbl_geq(1.0,abs(xi(i)))) return false; } return true; } // ============================================================= // class FE_ElementRect // ============================================================= FE_ElementRect::FE_ElementRect() : FE_ElementHex(8,4,2) { // Handled by hex class } FE_ElementRect::~FE_ElementRect() { // Handled by base class } bool FE_ElementRect::contains_point(const DENS_MAT &eltCoords, const DENS_VEC &x) const { return range_check(eltCoords,x); } // much faster than the unstructured method bool FE_ElementRect::local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { xi.reset(nSD_); double min = 0.0; double max = 0.0; for (int iSD=0; iSD<nSD_; ++iSD) { min = eltCoords(iSD,0); max = eltCoords(iSD,6); xi(iSD) = 2.0*(x(iSD)-min)/(max-min) - 1.0; } return true; } // ============================================================= // class FE_ElementTet // ============================================================= FE_ElementTet::FE_ElementTet(int numNodes, int numFaceNodes, int numNodes1d) : FE_Element(3, // number of spatial dimensions 4, // number of faces numNodes, numFaceNodes, numNodes1d) { // t // ^ // | // | // s // 3 .7 // |\```--- . // | \ ```--2 . // | \ .| // | \ . | // | . \ | // | . \ | // |.___________\| -------> r // 0 1 // // (This is as dictated by the EXODUSII standard.) // // The face opposite point 1 has r = 0, // 2 has s = 0, // 3 has t = 0, // 0 has u = 0. // Basic properties of element: vol_ = 1.0/6.0; // local volume faceArea_ = 1.0/2.0; // Order-specific information: if (numNodes != 4 && numNodes != 10) { throw ATC_Error("Unrecognized interpolation order specified " "for element class: \n" " element only knows how to construct lin " "and quad elments."); } localCoords_.resize(nSD_+1, numNodes_); localFaceConn_ = Array2D<int>(numFaces_,numFaceNodes_); // Matrix of local nodal coordinates // // Remember, there's actually another coordinate too (u), coming // out from the third non-normal face. But we can deal with it on // the fly; these coordinates are barycentric, such that // r + s + t + u = 1 always. As such we only need to deal with r, // s, and t and the computations become easy. // // The first three axes correspond to x, y, and z (essentially), // for the canonical element. // Everyone gets these nodes... localCoords_(0,0) = 0; localCoords_(0,2) = 0; localCoords_(1,0) = 0; localCoords_(1,2) = 1; localCoords_(2,0) = 0; localCoords_(2,2) = 0; localCoords_(3,0) = 1; localCoords_(3,2) = 0; // localCoords_(0,1) = 1; localCoords_(0,3) = 0; localCoords_(1,1) = 0; localCoords_(1,3) = 0; localCoords_(2,1) = 0; localCoords_(2,3) = 1; localCoords_(3,1) = 0; localCoords_(3,3) = 0; if (numNodes >= 10) { // ...quads get even more! localCoords_(0,4) = 0.5; localCoords_(0,5) = 0.5; localCoords_(1,4) = 0.0; localCoords_(1,5) = 0.5; localCoords_(2,4) = 0.0; localCoords_(2,5) = 0.0; localCoords_(3,4) = 0.5; localCoords_(3,5) = 0.0; // localCoords_(0,6) = 0.0; localCoords_(0,7) = 0.0; localCoords_(1,6) = 0.5; localCoords_(1,7) = 0.0; localCoords_(2,6) = 0.0; localCoords_(2,7) = 0.5; localCoords_(3,6) = 0.5; localCoords_(3,7) = 0.5; // localCoords_(0,8) = 0.5; localCoords_(0,9) = 0.0; localCoords_(1,8) = 0.0; localCoords_(1,9) = 0.5; localCoords_(2,8) = 0.5; localCoords_(2,9) = 0.5; localCoords_(3,8) = 0.0; localCoords_(3,9) = 0.0; } // Matrix of local face connectivity: // ...opposite point 0, ...opposite point 2, localFaceConn_(0,0) = 1; localFaceConn_(2,0) = 0; localFaceConn_(0,1) = 2; localFaceConn_(2,1) = 1; localFaceConn_(0,2) = 3; localFaceConn_(2,2) = 3; // ...opposite point 1, ...opposite point 3. localFaceConn_(1,0) = 2; localFaceConn_(3,0) = 0; localFaceConn_(1,1) = 0; localFaceConn_(3,1) = 2; localFaceConn_(1,2) = 3; localFaceConn_(3,2) = 1; feInterpolate_ = new FE_InterpolateSimpLin(this); } FE_ElementTet::~FE_ElementTet() { // Handled by base class } void FE_ElementTet::set_quadrature(FeIntQuadrature type) { feInterpolate_->set_quadrature(TETRA,type); } bool FE_ElementTet::local_coordinates(const DENS_MAT &eltCoords, const DENS_VEC &x, DENS_VEC &xi) const { DENS_MAT T(nSD_, numNodes_-1); DENS_VEC r(nSD_); for (int iSD=0; iSD<nSD_; ++iSD) { for (int inode=1; inode<numNodes_; ++inode) { T(iSD, inode-1) = eltCoords(iSD, inode) - eltCoords(iSD, 0); } r(iSD) = x(iSD) - eltCoords(iSD, 0); } MultMv(inv(T), r, xi, false, 1.0, 0.0); return true; } bool FE_ElementTet::contains_point(const DENS_MAT &eltCoords, const DENS_VEC &x) const { if (! range_check(eltCoords,x) ) return false; DENS_VEC xi(nSD_); bool converged = local_coordinates(eltCoords, x, xi); if (! converged) return false; double sum = 0.0; bool inside = true; for (int iSD = 0; iSD < nSD_; ++iSD) { if (dbl_geq(xi(iSD),1.0) || dbl_geq(0.0,xi(iSD))) { inside = false; break; } sum += xi(iSD); } if (dbl_geq(sum,1.0)) inside = false; return inside; } }; // namespace ATC diff --git a/lib/atc/FE_Engine.cpp b/lib/atc/FE_Engine.cpp index a87a03032..4b519e336 100644 --- a/lib/atc/FE_Engine.cpp +++ b/lib/atc/FE_Engine.cpp @@ -1,2713 +1,2975 @@ #include "FE_Engine.h" #include "ATC_Transfer.h" #include "FE_Element.h" #include "Function.h" #include "PhysicsModel.h" #include "KernelFunction.h" #include "Utility.h" #include "MPI_Wrappers.h" #include <stdio.h> #include <stdlib.h> #include <map> #include <string> using namespace std; using ATC_Utility::is_numeric; using ATC_Utility::to_string;; using MPI_Wrappers::allsum; using MPI_Wrappers::int_allsum; using MPI_Wrappers::rank_zero; using MPI_Wrappers::print_msg; using MPI_Wrappers::print_msg_once; namespace ATC{ static const double tol_sparse = 1.e-30;//tolerance for compaction from dense //----------------------------------------------------------------- FE_Engine::FE_Engine(MPI_Comm comm) : communicator_(comm), feMesh_(NULL), initialized_(false), outputManager_() { // Nothing to do here } //----------------------------------------------------------------- FE_Engine::~FE_Engine() { if (feMesh_) delete feMesh_; } //----------------------------------------------------------------- void FE_Engine::initialize() { if (!feMesh_) throw ATC_Error("FE_Engine has no mesh"); if (!initialized_) { // set up work spaces nNodesPerElement_ = feMesh_->num_nodes_per_element(); nIPsPerElement_ = feMesh_->num_ips_per_element(); nIPsPerFace_ = feMesh_->num_ips_per_face(); nSD_ = feMesh_->num_spatial_dimensions(); nElems_ = feMesh_->num_elements(); nNodesUnique_ = feMesh_->num_nodes_unique(); nNodes_ = feMesh_->num_nodes(); // arrays & matrices _weights_.reset(nIPsPerElement_,nIPsPerElement_); _N_.reset(nIPsPerElement_,nNodesPerElement_); _dN_.assign(nSD_, DENS_MAT(nIPsPerElement_,nNodesPerElement_)); _Nw_.reset(nIPsPerElement_,nNodesPerElement_); _dNw_.assign(nSD_, DENS_MAT(nIPsPerElement_,nNodesPerElement_)); _Bfluxes_.assign(nSD_, DENS_MAT()); // faces _fweights_.reset(nIPsPerElement_,nIPsPerElement_); _fN_.reset(nIPsPerFace_,nNodesPerElement_); _fdN_.assign(nSD_, DENS_MAT(nIPsPerFace_, nNodesPerElement_)); _nN_.assign(nSD_, DENS_MAT(nIPsPerFace_, nNodesPerElement_)); // remove specified elements if (nullElements_.size() > 0) delete_elements(nullElements_); initialized_ = true; } } void FE_Engine::partition_mesh() { if (is_partitioned()) return; feMesh_->partition_mesh(); // now do all FE_Engine data structure partitioning // partition nullElements_ - /*for (vector<int>::const_iterator elemsIter = feMesh_->processor_elts().begin(); + /*for (vector<int>::iterator elemsIter = feMesh_->processor_elts().begin(); elemsIter != feMesh_->processor_elts().end(); ++elemsIter) { if (nullElements_.find(*elemsIter) != nullElements_.end()) { myNullElements_.insert(map_elem_to_myElem(*elemsIter)); } }*/ } void FE_Engine::departition_mesh() { if (!is_partitioned()) return; feMesh_->departition_mesh(); } void FE_Engine::set_quadrature(FeIntQuadrature type, bool temp) const { if (!feMesh_) throw ATC_Error("FE_Engine has no mesh"); feMesh_->set_quadrature(type); if (!temp) quadrature_ = type; int nIPsPerElement_new = feMesh_->num_ips_per_element(); int nIPsPerFace_new = feMesh_->num_ips_per_face(); if (nIPsPerElement_ != nIPsPerElement_new) { // arrays & matrices nIPsPerElement_ = nIPsPerElement_new; _weights_.resize(nIPsPerElement_,nIPsPerElement_); _N_.resize(nIPsPerElement_,nNodesPerElement_); _dN_.assign(nSD_, DENS_MAT(nIPsPerElement_,nNodesPerElement_)); _Nw_.reset(nIPsPerElement_,nNodesPerElement_); _dNw_.assign(nSD_, DENS_MAT(nIPsPerElement_,nNodesPerElement_)); } if (nIPsPerFace_ != nIPsPerFace_new) { // faces nIPsPerFace_ = nIPsPerFace_new; _fweights_.reset(nIPsPerElement_,nIPsPerElement_); _fN_.reset(nIPsPerFace_,nNodesPerElement_); _fdN_.assign(nSD_, DENS_MAT(nIPsPerFace_, nNodesPerElement_)); _nN_.assign(nSD_, DENS_MAT(nIPsPerFace_, nNodesPerElement_)); } } //----------------------------------------------------------------- bool FE_Engine::modify(int narg, char **arg) { bool match = false; /*! \page man_mesh_create fix_modify AtC mesh create \section syntax fix_modify AtC mesh create <nx> <ny> <nz> <region-id> <f|p> <f|p> <f|p> \n - nx ny nz = number of elements in x, y, z - region-id = id of region that is to be meshed - f p p = periodicity flags for x, y, z \section examples <TT> fix_modify AtC mesh create 10 1 1 feRegion p p p </TT> \n \section description Creates a uniform mesh in a rectangular region \section restrictions Creates only uniform rectangular grids in a rectangular region \section related \ref man_mesh_quadrature \section default When created, mesh defaults to gauss2 (2-point Gaussian) quadrature. Use "mesh quadrature" command to change quadrature style. */ int argIdx = 0; if (strcmp(arg[argIdx],"mesh")==0) { argIdx++; // create mesh if (strcmp(arg[argIdx],"create")==0) { if (feMesh_) throw ATC_Error("FE Engine already has a mesh"); argIdx++; int nx = atoi(arg[argIdx++]); int ny = atoi(arg[argIdx++]); int nz = atoi(arg[argIdx++]); string box = arg[argIdx++]; Array<bool> periodicity(3); periodicity(0) = (strcmp(arg[argIdx++],"p")==0) ? true : false; periodicity(1) = (strcmp(arg[argIdx++],"p")==0) ? true : false; periodicity(2) = (strcmp(arg[argIdx++],"p")==0) ? true : false; if (argIdx < narg ) { Array<double> dx(nx),dy(ny),dz(nz); dx = 0; dy = 0; dz = 0; - double x[3] = {0,0,0}; while (argIdx < narg) { - if (strcmp(arg[argIdx++],"dx")==0) { - // parse relative values for each element - if (is_numeric(arg[argIdx])) { - for (int i = 0; i < nx; ++i) { - if (is_numeric(arg[argIdx])) { dx(i) = atof(arg[argIdx++]); } - else throw ATC_Error("not enough element partitions"); - } - } - // construct relative values from a density function - // evaluate for a domain (0,1) - else { - XT_Function * f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); - for (int i = 0; i < nx; ++i) { - x[0] = (i+0.5)/nx; dx(i) = f->f(x,0.); - } - } - - - break ; + if (strcmp(arg[argIdx],"dx")==0) { + parse_partitions(++argIdx, narg, arg, 0, dx); } - else if (strcmp(arg[argIdx++],"dy")==0) { - for (int i = 0; i < ny; ++i) { dy(i) = atof(arg[argIdx++]); } + else if (strcmp(arg[argIdx],"dy")==0) { + parse_partitions(++argIdx, narg, arg, 1, dy); } - else if (strcmp(arg[argIdx++],"dz")==0) { - for (int i = 0; i < nz; ++i) { dz(i) = atof(arg[argIdx++]); } + else if (strcmp(arg[argIdx],"dz")==0) { + parse_partitions(++argIdx, narg, arg, 2, dz); } } create_mesh(dx, dy, dz, box.c_str(), periodicity); } else { create_mesh(nx, ny, nz, box.c_str(), periodicity); } quadrature_ = GAUSS2; match = true; } /*! \page man_mesh_quadrature fix_modify AtC mesh quadrature \section syntax fix_modify AtC mesh quadrature <quad> - quad = one of <nodal|gauss1|gauss2|gauss3|face> --- when a mesh is created it defaults to gauss2, use this call to change it after the fact \section examples <TT> fix_modify AtC mesh quadrature face </TT> \section description (Re-)assigns the quadrature style for the existing mesh. \section restrictions \section related \ref man_mesh_create \section default none */ else if (strcmp(arg[argIdx],"quadrature")==0) { argIdx++; string quadStr = arg[argIdx]; FeIntQuadrature quadEnum = string_to_FIQ(quadStr); set_quadrature(quadEnum); match = true; } /*! \page man_mesh_read fix_modify AtC mesh read \section syntax fix_modify AtC mesh read <filename> <f|p> <f|p> <f|p> - filename = name of file containing mesh to be read - f p p = periodicity flags for x, y, z \section examples <TT> fix_modify AtC mesh read myComponent.mesh p p p </TT> \n <TT> fix_modify AtC mesh read myOtherComponent.exo </TT> \section description Reads a mesh from a text or exodus file, and assigns periodic boundary conditions if needed. \section restrictions \section related \section default periodicity flags are false by default */ else if (strcmp(arg[argIdx],"read")==0) { argIdx++; string meshFile = arg[argIdx++]; Array<bool> periodicity(3); periodicity = false; if (argIdx < narg) { periodicity(0) = (strcmp(arg[argIdx++],"p")==0) ? true : false; periodicity(1) = (strcmp(arg[argIdx++],"p")==0) ? true : false; periodicity(2) = (strcmp(arg[argIdx++],"p")==0) ? true : false; } read_mesh(meshFile,periodicity); if (periodicity(0) || periodicity(1) || periodicity(2)) { meshFile = "periodic_"+meshFile; stringstream ss; ss << "writing periodicity corrected mesh: " << meshFile; print_msg(communicator_,ss.str()); feMesh_->write_mesh(meshFile); feMesh_->output(meshFile); } match = true; } /*! \page man_mesh_write fix_modify AtC mesh write \section syntax fix_modify AtC mesh write <filename> - filename = name of file to write mesh \section examples <TT> fix_modify AtC mesh write myMesh.mesh </TT> \n \section description Writes a mesh to a text file. \section restrictions \section related \section default */ else if (strcmp(arg[argIdx],"write")==0) { argIdx++; string meshFile = arg[argIdx]; feMesh_->write_mesh(meshFile); match = true; } /*! \page man_mesh_delete_elements fix_modify AtC mesh delete_elements \section syntax fix_modify AtC mesh delete_elements <element_set> - <element_set> = name of an element set \section examples <TT> fix_modify AtC delete_elements gap </TT> \section description Deletes a group of elements from the mesh. \section restrictions \section related \section default none */ else if (strcmp(arg[argIdx],"delete_elements")==0) { argIdx++; string esetName = arg[argIdx]; set<int> elemSet = feMesh_->elementset(esetName); nullElements_.insert(elemSet.begin(), elemSet.end()); match = true; } else if (strcmp(arg[argIdx],"cut")==0) { argIdx++; string fsetName = arg[argIdx++]; set<PAIR> faceSet = feMesh_->faceset(fsetName); cutFaces_.insert(faceSet.begin(), faceSet.end()); if (narg > argIdx && strcmp(arg[argIdx],"edge")==0) { argIdx++; string nsetName = arg[argIdx]; set<int> nodeSet = feMesh_->nodeset(nsetName); cutEdge_.insert(nodeSet.begin(), nodeSet.end()); } // cut mesh if (cutFaces_.size() > 0) cut_mesh(cutFaces_,cutEdge_); match = true; } else if (strcmp(arg[argIdx],"lammps_partition")==0) { feMesh_->set_lammps_partition(true); match = true; } else if (strcmp(arg[argIdx],"data_decomposition")==0) { feMesh_->set_data_decomposition(true); match = true; } else { if ( ! feMesh_ ) throw ATC_Error("need mesh for parsing"); match = feMesh_->modify(narg,arg); } } // FE_Mesh else { if ( ! feMesh_ ) throw ATC_Error("need mesh for parsing"); match = feMesh_->modify(narg,arg); } return match; } + //----------------------------------------------------------------- + void FE_Engine::parse_partitions(int & argIdx, int narg, char ** arg, + int idof, Array<double> & dx ) const + { + double x[3] = {0,0,0}; + int nx = dx.size(); + // parse relative values for each element + if (is_numeric(arg[argIdx])) { + for (int i = 0; i < nx; ++i) { + if (is_numeric(arg[argIdx])) { dx(i) = atof(arg[argIdx++]); } + else throw ATC_Error("not enough element partitions"); + } + } + // each segment of the piecewise funcion is length-normalized separately + else if (strcmp(arg[argIdx],"position-number-density")==0) { + argIdx++; + double y[nx],w[nx]; + int n[nx]; + int nn = 0; + while (argIdx < narg) { + if (! is_numeric(arg[argIdx])) break; + y[nn] = atof(arg[argIdx++]); + n[nn] = atoi(arg[argIdx++]); + w[nn++] = atof(arg[argIdx++]); + } + if (n[nn-1] != nx) throw ATC_Error("total element partitions do not match"); + int k = 0; + for (int i = 1; i < nn; ++i) { + int dn = n[i]-n[i-1]; + double dy = y[i]-y[i-1]; + double w0 = w[i-1]; + double dw = w[i]-w0; + double lx = 0; + double l[dn]; + for (int j = 0; j < dn; ++j) { + double x = (j+0.5)/dn; + double dl = w0+x*dw; + lx += dl; + l[j]= dl; + } + double scale = dy/lx; + for (int j = 0; j < dn; ++j) { + dx(k++) = scale*l[j]; + } + } + } + // construct relative values from a density function + // evaluate for a domain (0,1) + else { + XT_Function * f = XT_Function_Mgr::instance()->function(&(arg[argIdx]),narg-argIdx); + argIdx++; + while (argIdx < narg) { + if (! is_numeric(arg[argIdx++])) break; + } + for (int i = 0; i < nx; ++i) { + x[idof] = (i+0.5)/nx; dx(i) = f->f(x,0.); + } + } + } //----------------------------------------------------------------- void FE_Engine::finish() { // Nothing to do } //----------------------------------------------------------------- // write geometry //----------------------------------------------------------------- void FE_Engine::initialize_output(int rank, string outputPrefix, set<int> otypes) { outputManager_.initialize(outputPrefix, otypes); if (!feMesh_) throw ATC_Error("output needs mesh"); if (!initialized_) initialize(); if (!feMesh_->coordinates() || !feMesh_->connectivity()) throw ATC_Error("output mesh not properly initialized"); if (!feMesh_->coordinates()->nCols() || !feMesh_->connectivity()->nCols()) throw ATC_Error("output mesh is empty"); if (rank == 0) outputManager_.write_geometry(feMesh_->coordinates(), feMesh_->connectivity()); outputManager_.print_custom_names(); } //----------------------------------------------------------------- // write geometry //----------------------------------------------------------------- void FE_Engine::write_geometry(void) { outputManager_.write_geometry(feMesh_->coordinates(), feMesh_->connectivity()); } // ------------------------------------------------------------- // write data // ------------------------------------------------------------- void FE_Engine::write_data(double time, FIELDS &soln, OUTPUT_LIST *data) { outputManager_.write_data( time, &soln, data, (feMesh_->node_map())->data()); } // ------------------------------------------------------------- // write data // ------------------------------------------------------------- void FE_Engine::write_data(double time, OUTPUT_LIST *data) { outputManager_.write_data( time, data, feMesh_->node_map()->data()); } // ------------------------------------------------------------- // amend mesh for deleted elements // ------------------------------------------------------------- void FE_Engine::delete_elements(const set<int> & elementList) { feMesh_->delete_elements(elementList); } // ------------------------------------------------------------- // amend mesh for cut at specified faces // ------------------------------------------------------------- void FE_Engine::cut_mesh(const set<PAIR> &faceSet, const set<int> &nodeSet) { feMesh_->cut_mesh(faceSet,nodeSet); } // ------------------------------------------------------------- // interpolate one value // ------------------------------------------------------------- DENS_VEC FE_Engine::interpolate_field(const DENS_VEC & x, const FIELD & f) const { DENS_VEC N; Array<int> nodelist; feMesh_->shape_functions(x, N, nodelist); const DENS_MAT &vI = f.quantity(); int dof = vI.nCols(); DENS_MAT vIe(nNodesPerElement_, dof); for (int i = 0; i < nNodesPerElement_; i++) for (int j = 0; j < dof; j++) vIe(i,j) = vI(nodelist(i),j); DENS_VEC vP; vP = N*vIe; return vP; } // ------------------------------------------------------------- // interpolate fields and gradients // Currently, this function will break if called with an unowned ielem. // Currently, this function is only called with owned ielems. // ------------------------------------------------------------- void FE_Engine::interpolate_fields( const int ielem, const FIELDS &fields, AliasArray<int> & conn, DENS_MAT & N, DENS_MAT_VEC & dN, DIAG_MAT & _weights_, FIELD_MATS &fieldsAtIPs, GRAD_FIELD_MATS &gradFieldsAtIPs) const { // evaluate shape functions feMesh_->shape_function(ielem, N, dN, _weights_); // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); // compute fields and gradients of fields ips of this element FIELD_MATS localElementFields; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { // field values at all nodes _fieldName_ = _fieldItr_->first; const DENS_MAT &vI = (_fieldItr_->second).quantity(); int dof = vI.nCols(); // field values at integration points -> to be computed DENS_MAT &vP = fieldsAtIPs[_fieldName_]; // gradients of field at integration points -> to be computed DENS_MAT_VEC &dvP = gradFieldsAtIPs[_fieldName_]; if (_fieldName_ == ELECTRON_WAVEFUNCTION_ENERGIES ) { vP = vI; continue; } // field values at element nodes DENS_MAT &vIe = localElementFields[_fieldName_]; // gather local field vIe.reset(nNodesPerElement_, dof); for (int i = 0; i < nNodesPerElement_; i++) for (int j = 0; j < dof; j++) vIe(i,j) = vI(conn(i),j); // interpolate field at integration points vP = N*vIe; // gradients dvP.assign(nSD_, DENS_MAT(nIPsPerElement_, dof)); for (int j = 0; j < nSD_; ++j) dvP[j] = dN[j]*vIe; } } // ------------------------------------------------------------- // interpolate fields // Currently, this function will break if called with an unowned ielem. // Currently, this function is only called with owned ielems. // ------------------------------------------------------------- void FE_Engine::interpolate_fields( const int ielem, const FIELDS &fields, AliasArray<int> & conn, DENS_MAT & N, DIAG_MAT & _weights_, FIELD_MATS &fieldsAtIPs) const { // evaluate shape functions feMesh_->shape_function(ielem, N, _weights_); // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); // compute fields and gradients of fields ips of this element FIELD_MATS localElementFields; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { // field values at all nodes _fieldName_ = _fieldItr_->first; const DENS_MAT &vI = (_fieldItr_->second).quantity(); int dof = vI.nCols(); // field values at integration points -> to be computed DENS_MAT &vP = fieldsAtIPs[_fieldName_]; // field values at element nodes DENS_MAT &vIe = localElementFields[_fieldName_]; if (_fieldName_ == ELECTRON_WAVEFUNCTION_ENERGIES ) { vP = vI; continue; } // gather local field vIe.reset(nNodesPerElement_, dof); for (int i = 0; i < nNodesPerElement_; i++) for (int j = 0; j < dof; j++) vIe(i,j) = vI(conn(i),j); // interpolate field at integration points vP = N*vIe; } } // ------------------------------------------------------------- // compute dimensionless stiffness matrix using native quadrature // ------------------------------------------------------------- void FE_Engine::stiffness_matrix(SPAR_MAT &matrix) const { // assemble consistent mass matrix.reset(nNodesUnique_,nNodesUnique_);// zero since partial fill DENS_MAT elementMassMatrix(nNodesPerElement_,nNodesPerElement_); vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // evaluate shape functions feMesh_->shape_function(ielem, _N_, _dN_, _weights_); // _N_ unused // perform quadrature elementMassMatrix = _dN_[0].transMat(_weights_*_dN_[0]); for (int i = 1; i < nSD_; ++i) { elementMassMatrix += _dN_[i].transMat(_weights_*_dN_[i]); } // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); matrix.add(inode, jnode, elementMassMatrix(i,j)); } } } #ifdef ISOLATE_FE sparse_allsum(communicator_,matrix); #else LammpsInterface::instance()->sparse_allsum(matrix); #endif matrix.compress(); } // ------------------------------------------------------------- // compute tangent using native quadrature for one (field,field) pair // ------------------------------------------------------------- void FE_Engine::compute_tangent_matrix( const Array2D<bool> &rhsMask, const pair<FieldName,FieldName> row_col, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, SPAR_MAT &tangent, const DenseMatrix<bool> *elementMask) const { tangent.reset(nNodesUnique_,nNodesUnique_); FieldName rowField = row_col.first; FieldName colField = row_col.second; bool BB = rhsMask(rowField,FLUX); bool NN = rhsMask(rowField,SOURCE); DENS_MAT elementMatrix(nNodesPerElement_,nNodesPerElement_); DENS_MAT coefsAtIPs; vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // if element is masked, skip it if (elementMask && !(*elementMask)(ielem,0)) continue; // material id int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); // interpolate fields and gradients (nonlinear only) interpolate_fields(ielem,fields,_conn_,_N_,_dN_,_weights_, _fieldsAtIPs_,_gradFieldsAtIPs_); // evaluate Physics model if (! (physicsModel->null(rowField,imat)) ) { if (BB && physicsModel->weak_equation(rowField)-> has_BB_tangent_coefficients() ) { physicsModel->weak_equation(rowField)-> BB_tangent_coefficients(colField, _fieldsAtIPs_, mat, coefsAtIPs); DIAG_MAT D(column(coefsAtIPs,0)); D = _weights_*D; elementMatrix = _dN_[0].transMat(D*_dN_[0]); for (int i = 1; i < nSD_; i++) { elementMatrix += _dN_[i].transMat(D*_dN_[i]); } } else { elementMatrix.reset(nNodesPerElement_,nNodesPerElement_); } if (NN && physicsModel->weak_equation(rowField)-> has_NN_tangent_coefficients() ) { physicsModel->weak_equation(rowField)-> NN_tangent_coefficients(colField, _fieldsAtIPs_, mat, coefsAtIPs); DIAG_MAT D(column(coefsAtIPs,0)); D = _weights_*D; elementMatrix += _N_.transMat(D*_N_); } // assemble for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); tangent.add(inode, jnode, elementMatrix(i,j)); } } } } #ifdef ISOLATE_FE sparse_allsum(communicator_,tangent); #else LammpsInterface::instance()->sparse_allsum(tangent); #endif tangent.compress(); } // ------------------------------------------------------------- // compute tangent using given quadrature for one (field,field) pair // ------------------------------------------------------------- void FE_Engine::compute_tangent_matrix(const Array2D<bool> &rhsMask, const pair<FieldName,FieldName> row_col, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<set<int> > & pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, SPAR_MAT &tangent, const DenseMatrix<bool> *elementMask ) const { int nn = nNodesUnique_; FieldName rowField = row_col.first; FieldName colField = row_col.second; bool BB = rhsMask(rowField,FLUX); bool NN = rhsMask(rowField,SOURCE); DENS_MAT K(nn,nn); DENS_MAT coefsAtIPs; int nips = weights.nCols(); if (nips>0) { // compute fields and gradients of fields at given ips GRAD_FIELD_MATS gradFieldsAtIPs; FIELD_MATS fieldsAtIPs; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); gradFieldsAtIPs[_fieldName_].assign(nSD_,DENS_MAT(nips,dof)); fieldsAtIPs[_fieldName_] = N*field; for (int j = 0; j < nSD_; ++j) { gradFieldsAtIPs[_fieldName_][j] = (*dN[j])*field; } } // treat single material point sets specially int nMatls = pointMaterialGroups.size(); int atomMatls = 0; for (int imat = 0; imat < nMatls; imat++) { const set<int> & indices = pointMaterialGroups(imat); if ( indices.size() > 0) atomMatls++; } bool singleMaterial = ( atomMatls == 1 ); if (! singleMaterial ) throw ATC_Error("FE_Engine::compute_tangent_matrix-given quadrature can not handle multiple atom material currently"); if (singleMaterial) { int imat = 0; const Material * mat = physicsModel->material(imat); // evaluate Physics model if (! (physicsModel->null(rowField,imat)) ) { if (BB && physicsModel->weak_equation(rowField)-> has_BB_tangent_coefficients() ) { physicsModel->weak_equation(rowField)-> BB_tangent_coefficients(colField, fieldsAtIPs, mat, coefsAtIPs); DIAG_MAT D(column(coefsAtIPs,0)); D = weights*D; K = (*dN[0]).transMat(D*(*dN[0])); for (int i = 1; i < nSD_; i++) { K += (*dN[i]).transMat(D*(*dN[i])); } } if (NN && physicsModel->weak_equation(rowField)-> has_NN_tangent_coefficients() ) { physicsModel->weak_equation(rowField)-> NN_tangent_coefficients(colField, fieldsAtIPs, mat, coefsAtIPs); DIAG_MAT D(column(coefsAtIPs,0)); D = weights*D; K += N.transMat(D*N); } } } } // share information between processors int count = nn*nn; DENS_MAT K_sum(nn,nn); allsum(communicator_, K.ptr(), K_sum.ptr(), count); // create sparse from dense tangent.reset(K_sum,tol_sparse); tangent.compress(); } // ------------------------------------------------------------- // compute a consistent mass matrix for native quadrature // ------------------------------------------------------------- void FE_Engine::compute_mass_matrix( const Array<FieldName>& field_mask, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, CON_MASS_MATS & massMatrices, const DenseMatrix<bool> *elementMask) const { int nfields = field_mask.size(); vector<FieldName> usedFields; DENS_MAT elementMassMatrix(nNodesPerElement_,nNodesPerElement_); // (JAT, 04/21/11) FIX THIS DENS_MAT capacity; // zero, use incoming matrix as template if possible for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); SPAR_MAT & M = massMatrices[_fieldName_].set_quantity(); // compresses 2May11 if (M.has_template()) { M = 0; } else { M.reset(nNodesUnique_,nNodesUnique_); } M.reset(nNodesUnique_,nNodesUnique_); } // element wise assembly vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // if element is masked, skip if (elementMask && !(*elementMask)(ielem,0)) continue; // material id int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); // interpolate fields interpolate_fields(ielem,fields,_conn_,_N_,_weights_,_fieldsAtIPs_); for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); SPAR_MAT & M = massMatrices[_fieldName_].set_quantity(); // skip null weakEqns by material if (! (physicsModel->null(_fieldName_,imat)) ) { physicsModel->weak_equation(_fieldName_)-> M_integrand(_fieldsAtIPs_, mat, capacity); DIAG_MAT rho(column(capacity,0)); elementMassMatrix = _N_.transMat(_weights_*rho*_N_); // assemble for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); M.add(inode, jnode, elementMassMatrix(i,j)); } } } } } for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); SPAR_MAT & M = massMatrices[_fieldName_].set_quantity(); #ifdef ISOLATE_FE sparse_allsum(communicator_,M); #else LammpsInterface::instance()->sparse_allsum(M); #endif } // fix zero diagonal entries due to null material elements for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); SPAR_MAT & M = massMatrices[_fieldName_].set_quantity(); for (int inode = 0; inode < nNodesUnique_; ++inode) { if (! M.has_entry(inode,inode)) { M.set(inode,inode,1.0); } } M.compress(); } } // ------------------------------------------------------------- // compute dimensionless consistent mass using native quadrature // ------------------------------------------------------------- void FE_Engine::compute_mass_matrix(SPAR_MAT &massMatrix) const { // assemble nnodes X nnodes matrix massMatrix.reset(nNodesUnique_,nNodesUnique_);// zero since partial fill DENS_MAT elementMassMatrix(nNodesPerElement_,nNodesPerElement_); vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // evaluate shape functions feMesh_->shape_function(ielem, _N_, _weights_); // perform quadrature elementMassMatrix = _N_.transMat(_weights_*_N_); // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); massMatrix.add(inode, jnode, elementMassMatrix(i,j)); } } } // Assemble partial results #ifdef ISOLATE_FE sparse_allsum(communicator_,massMatrix); #else LammpsInterface::instance()->sparse_allsum(massMatrix); #endif } // ------------------------------------------------------------- // compute dimensionless consistent mass using given quadrature // ------------------------------------------------------------- void FE_Engine::compute_mass_matrix(const DIAG_MAT &weights, const SPAR_MAT &N, SPAR_MAT &massMatrix) const { int nn = N.nCols(); int nips = N.nRows(); DENS_MAT tmp_mass_matrix_local(nn,nn), tmp_mass_matrix(nn,nn); if (nips>0) { tmp_mass_matrix_local = N.transMat(weights*N); } // share information between processors int count = nn*nn; allsum(communicator_, tmp_mass_matrix_local.ptr(), tmp_mass_matrix.ptr(), count); // create sparse from dense massMatrix.reset(tmp_mass_matrix,tol_sparse); } // ------------------------------------------------------------- // compute dimensionless lumped mass using native quadrature // ------------------------------------------------------------- void FE_Engine::compute_lumped_mass_matrix(DIAG_MAT & M) const { M.reset(nNodesUnique_,nNodesUnique_); // zero since partial fill // assemble lumped diagonal mass DENS_VEC Nvec(nNodesPerElement_); vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // evaluate shape functions feMesh_->shape_function(ielem, _N_, _weights_); CLON_VEC w(_weights_); // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); Nvec = w*_N_; for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); M(inode,inode) += Nvec(i); } } // Assemble partial results allsum(communicator_,MPI_IN_PLACE, M.ptr(), M.size()); } // ------------------------------------------------------------- // compute physical lumped mass using native quadrature // ------------------------------------------------------------- void FE_Engine::compute_lumped_mass_matrix( const Array<FieldName>& field_mask, const FIELDS & fields, const PhysicsModel * physicsModel, const Array<int> &elementMaterials, MASS_MATS & massMatrices, // mass matrix const DenseMatrix<bool> *elementMask) const { int nfields = field_mask.size(); // zero initialize for assembly for (int j = 0; j < nfields; ++j) { DIAG_MAT & M = massMatrices[field_mask(j)].set_quantity(); M.reset(nNodesUnique_,nNodesUnique_); } // assemble diagonal matrix vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // if element is masked, skip it if (elementMask && !(*elementMask)(ielem,0)) continue; // material id int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); interpolate_fields(ielem,fields,_conn_,_N_,_weights_,_fieldsAtIPs_); // compute densities, integrate & assemble DENS_MAT capacity; for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); if (! physicsModel->null(_fieldName_,imat)) { physicsModel->weak_equation(_fieldName_)-> M_integrand(_fieldsAtIPs_, mat, capacity); _Nmat_ = _N_.transMat(_weights_*capacity); DIAG_MAT & M = massMatrices[_fieldName_].set_quantity(); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); M(inode,inode) += _Nmat_(i,0); } } } } // Assemble partial results for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); DIAG_MAT & M = massMatrices[_fieldName_].set_quantity(); allsum(communicator_,MPI_IN_PLACE, M.ptr(), M.size()); } // fix zero diagonal entries due to null material elements for (int inode = 0; inode < nNodesUnique_; ++inode) { for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); DIAG_MAT & M = massMatrices[_fieldName_].set_quantity(); if (M(inode,inode) == 0.0) { M(inode,inode) = 1.0; } } } } // ------------------------------------------------------------- // compute physical lumped mass using given quadrature // ------------------------------------------------------------- void FE_Engine::compute_lumped_mass_matrix( const Array<FieldName> &field_mask, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<set<int> > & pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, MASS_MATS &M) const // mass matrices { int nips = weights.nCols(); int nfields = field_mask.size(); // initialize map<FieldName, DIAG_MAT> M_local; for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); M_local[_fieldName_].reset(nNodesUnique_,nNodesUnique_); M[_fieldName_].reset(nNodesUnique_,nNodesUnique_); } if (nips>0) { // compute fields at given ips // compute all fields since we don't the capacities dependencies FIELD_MATS fieldsAtIPs; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); fieldsAtIPs[_fieldName_].reset(nips,dof); fieldsAtIPs[_fieldName_] = N*field; } // treat single material point sets specially int nMatls = pointMaterialGroups.size(); int atomMatls = 0; + int iatomMat = 0; for (int imat = 0; imat < nMatls; imat++) { const set<int> & indices = pointMaterialGroups(imat); - if ( indices.size() > 0) atomMatls++; + if ( indices.size() > 0) { + iatomMat = imat; + atomMatls++; + } } if (atomMatls == 0) { throw ATC_Error("no materials in atom region - atoms may have migrated to FE-only region"); } bool singleMaterial = ( atomMatls == 1 ); if (! singleMaterial ) { stringstream ss; ss << " WARNING: multiple materials in atomic region"; print_msg(communicator_,ss.str()); } // setup data structures FIELD_MATS capacities; // evaluate physics model & compute capacity|density for requested fields if (singleMaterial) { - int imat = 0; - const Material * mat = physicsModel->material(imat); + const Material * mat = physicsModel->material(iatomMat); for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); // mass matrix needs to be invertible so null matls have cap=1 - if (physicsModel->null(_fieldName_,imat)) { + if (physicsModel->null(_fieldName_,iatomMat)) { throw ATC_Error("null material not supported for atomic region (single material)"); const FIELD & f = (fields.find(_fieldName_))->second; capacities[_fieldName_].reset(f.nRows(),f.nCols()); capacities[_fieldName_] = 1.; } else { physicsModel->weak_equation(_fieldName_)-> M_integrand(fieldsAtIPs, mat, capacities[_fieldName_]); } } } else { FIELD_MATS groupCapacities, fieldsGroup; for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); capacities[_fieldName_].reset(nips,1); } for ( int imat = 0; imat < pointMaterialGroups.size(); imat++) { const Material * mat = physicsModel->material(imat); const set<int> & indices = pointMaterialGroups(imat); int npts = indices.size(); if (npts > 0) { for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); groupCapacities[_fieldName_].reset(npts,1); const FIELD & f = (fields.find(_fieldName_))->second; int ndof = f.nCols(); fieldsGroup[_fieldName_].reset(npts,ndof); int i = 0; for (set<int>::const_iterator iter=indices.begin(); iter != indices.end(); iter++, i++ ) { for (int dof = 0; dof < ndof; ++dof) { fieldsGroup[_fieldName_](i,dof) = fieldsAtIPs[_fieldName_](*iter,dof); } } } for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); if (physicsModel->null(_fieldName_,imat)) { throw ATC_Error("null material not supported for atomic region (multiple materials)"); const FIELD & f = (fields.find(_fieldName_))->second; groupCapacities[_fieldName_].reset(f.nRows(),f.nCols()); groupCapacities[_fieldName_] = 1.; } else { physicsModel->weak_equation(_fieldName_)-> M_integrand(fieldsGroup, mat, groupCapacities[_fieldName_]); } int i = 0; for (set<int>::const_iterator iter=indices.begin(); iter != indices.end(); iter++, i++ ) { capacities[_fieldName_](*iter,0) = groupCapacities[_fieldName_](i,0); } } } } } // integrate & assemble for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); M_local[_fieldName_].reset( // assume all columns same column(N.transMat(weights*capacities[_fieldName_]),0) ); } } // Share information between processors for (int j = 0; j < nfields; ++j) { _fieldName_ = field_mask(j); DIAG_MAT & myMassMat(M[_fieldName_].set_quantity()); int count = M_local[_fieldName_].size(); allsum(communicator_, M_local[_fieldName_].ptr(), myMassMat.ptr(), count); } } //----------------------------------------------------------------- // compute assembled average gradient evaluated at the nodes //----------------------------------------------------------------- void FE_Engine::compute_gradient_matrix(SPAR_MAT_VEC & grad_matrix) const { // zero DENS_MAT_VEC tmp_grad_matrix(nSD_); for (int i = 0; i < nSD_; i++) { tmp_grad_matrix[i].reset(nNodesUnique_,nNodesUnique_); } // element wise assembly Array<int> count(nNodesUnique_); count = 0; set_quadrature(NODAL); vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // evaluate shape functions feMesh_->shape_function(ielem, _N_, _dN_, _weights_); // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); for (int j = 0; j < nIPsPerElement_; ++j) { int jnode = _conn_(j); count(jnode) += 1; for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int k = 0; k < nSD_; ++k) { tmp_grad_matrix[k](jnode,inode) += _dN_[k](j,i); } } } } // Assemble partial results for (int k = 0; k < nSD_; ++k) { allsum(communicator_,MPI_IN_PLACE, tmp_grad_matrix[k].ptr(), tmp_grad_matrix[k].size()); } int_allsum(communicator_,MPI_IN_PLACE, count.ptr(), count.size()); set_quadrature(quadrature_); //reset to default for (int inode = 0; inode < nNodesUnique_; ++inode) { for (int jnode = 0; jnode < nNodesUnique_; ++jnode) { for (int k = 0; k < nSD_; ++k) { tmp_grad_matrix[k](jnode,inode) /= count(jnode); } } } // compact dense matrices for (int k = 0; k < nSD_; ++k) { grad_matrix[k]->reset(tmp_grad_matrix[k],tol_sparse); } } // ------------------------------------------------------------- // compute energy per node using native quadrature // ------------------------------------------------------------- void FE_Engine::compute_energy(const Array<FieldName> &mask, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, FIELD_MATS &energies, const DenseMatrix<bool> *elementMask, const IntegrationDomainType domain) const { // Zero out all fields for (int n = 0; n < mask.size(); n++) { _fieldName_ = mask(n); _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); energies[_fieldName_].reset(field.nRows(), 1); } DENS_MAT elementEnergy(nNodesPerElement_,1); // workspace vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // if element is masked, skip it if (domain != FULL_DOMAIN && elementMask && !(*elementMask)(ielem,0)) continue; // material id int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); interpolate_fields(ielem,fields,_conn_,_N_,_dN_,_weights_, _fieldsAtIPs_,_gradFieldsAtIPs_); // assemble for (int n = 0; n < mask.size(); n++) { _fieldName_ = mask(n); if (physicsModel->null(_fieldName_,imat)) continue; if( ! (physicsModel->weak_equation(_fieldName_)-> has_E_integrand())) continue; physicsModel->weak_equation(_fieldName_)-> E_integrand(_fieldsAtIPs_, _gradFieldsAtIPs_, mat, elementEnergy); _fieldItr_ = fields.find(_fieldName_); _Nmat_ = _N_.transMat(_weights_*elementEnergy); DENS_MAT & energy = energies[_fieldName_]; for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); energy(inode,0) += _Nmat_(i,0); } } } for (int n = 0; n < mask.size(); n++) { _fieldName_ = mask(n); DENS_MAT& myEnergy(energies[_fieldName_]); allsum(communicator_,MPI_IN_PLACE, myEnergy.ptr(), myEnergy.size()); } } // ------------------------------------------------------------- // compute rhs using native quadrature // ------------------------------------------------------------- void FE_Engine::compute_rhs_vector(const Array2D<bool> &rhsMask, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, FIELDS &rhs, + bool freeOnly, const DenseMatrix<bool> *elementMask) const { vector<FieldName> usedFields; // size and zero output for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_, FLUX) || rhsMask(_fieldName_, SOURCE)) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); rhs[_fieldName_].reset(field.nRows(), field.nCols()); // Save field names for easy lookup later. usedFields.push_back(_fieldName_); } } // Iterate over elements partitioned onto current processor. vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // Skip masked elements if (elementMask && !(*elementMask)(ielem,0)) continue; int imat = elementMaterials(ielem); // material id const Material * mat = physicsModel->material(imat); // interpolate field values to integration points interpolate_fields(ielem,fields,_conn_,_N_,_dN_,_weights_, _fieldsAtIPs_,_gradFieldsAtIPs_); // rescale by _weights_, a diagonal matrix _Nw_ = _weights_*_N_; for (int j = 0; j < nSD_; ++j) _dNw_[j] = _weights_*_dN_[j]; // evaluate physics model and assemble // _Nfluxes is a set of fields for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (!rhsMask(_fieldName_,SOURCE)) continue; if (physicsModel->null(_fieldName_,imat)) continue; _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); int dof = field.nCols(); bool has = physicsModel->weak_equation(_fieldName_)-> N_integrand(_fieldsAtIPs_,_gradFieldsAtIPs_, mat, _Nfluxes_); if (!has) continue; _Nmat_ = _Nw_.transMat(_Nfluxes_); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int k = 0; k < dof; ++k) { myRhs(inode,k) += _Nmat_(i,k); } } } // _Bfluxes_ is a set of field gradients for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (!rhsMask(_fieldName_,FLUX)) continue; if (physicsModel->null(_fieldName_,imat)) continue; _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); int dof = field.nCols(); physicsModel->weak_equation(_fieldName_)-> B_integrand(_fieldsAtIPs_, _gradFieldsAtIPs_, mat, _Bfluxes_); for (int j = 0; j < nSD_; j++) { _Nmat_ = _dNw_[j].transMat(_Bfluxes_[j]); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int k = 0; k < dof; ++k) { myRhs(inode,k) += _Nmat_(i,k); } } } } } - vector<FieldName>::const_iterator _fieldIter_; + vector<FieldName>::iterator _fieldIter_; for (_fieldIter_ = usedFields.begin(); _fieldIter_ != usedFields.end(); ++_fieldIter_) { // myRhs is where we put the global result for this field. _fieldName_ = *_fieldIter_; DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); // Sum matrices in-place across all processors into myRhs. allsum(communicator_,MPI_IN_PLACE, myRhs.ptr(), myRhs.size()); } } // ------------------------------------------------------------- // compute rhs using given quadrature // ------------------------------------------------------------- void FE_Engine::compute_rhs_vector(const Array2D<bool> &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<set<int> >&pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, FIELDS &rhs) const { FIELD_MATS rhs_local; for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_,FLUX) || rhsMask(_fieldName_,SOURCE)) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); int nrows = field.nRows(); int ncols = field.nCols(); rhs [_fieldName_].reset(nrows,ncols); rhs_local[_fieldName_].reset(nrows,ncols); } } int nips = weights.nCols(); if (nips>0) { // compute fields and gradients of fields at given ips GRAD_FIELD_MATS gradFieldsAtIPs; FIELD_MATS fieldsAtIPs; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); gradFieldsAtIPs[_fieldName_].assign(nSD_,DENS_MAT(nips,dof)); fieldsAtIPs[_fieldName_] = N*field; for (int j = 0; j < nSD_; ++j) { gradFieldsAtIPs[_fieldName_][j] = (*dN[j])*field; } } // setup data structures FIELD_MATS Nfluxes; GRAD_FIELD_MATS Bfluxes; for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if ( rhsMask(index,FLUX) ) { Bfluxes[_fieldName_].assign(nSD_, DENS_MAT()); } } // treat single material point sets specially int nMatls = pointMaterialGroups.size(); int atomMatls = 0; for (int imat = 0; imat < nMatls; imat++) { const set<int> & indices = pointMaterialGroups(imat); if ( indices.size() > 0) atomMatls++; } bool singleMaterial = ( atomMatls == 1 ); // evaluate physics model if (singleMaterial) { int imat = 0; const Material * mat = physicsModel->material(imat); for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if (! physicsModel->null(_fieldName_,imat)) { if ( rhsMask(index,SOURCE) ) { physicsModel->weak_equation(_fieldName_)-> N_integrand(fieldsAtIPs, gradFieldsAtIPs, mat, Nfluxes[_fieldName_]); } if ( rhsMask(index,FLUX) ) { physicsModel->weak_equation(_fieldName_)-> B_integrand(fieldsAtIPs, gradFieldsAtIPs, mat, Bfluxes[_fieldName_]); } } } } else { // 1) permanent workspace with per-row mapped clones per matl // from caller/atc // 2) per point calls to N/B_integrand // 3) collect internalToAtom into materials and use mem-cont clones // what about dof for matrices and data storage: clone _matrix_ // for each material group: // set up storage DENS_MAT group_Nfluxes; DENS_MAT_VEC group_Bfluxes; group_Bfluxes.reserve(nSD_); FIELD_MATS fieldsGroup; GRAD_FIELD_MATS gradFieldsGroup; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int ndof = field.nCols(); gradFieldsGroup[_fieldName_].assign(nSD_,DENS_MAT(nips,ndof)); Nfluxes[_fieldName_].reset(nips,ndof); Bfluxes[_fieldName_].assign(nSD_,DENS_MAT(nips,ndof)); //} } // copy fields for ( int imat = 0; imat < pointMaterialGroups.size(); imat++) { const set<int> & indices = pointMaterialGroups(imat); const Material * mat = physicsModel->material(0); int npts = indices.size(); int i = 0; for (set<int>::const_iterator iter=indices.begin(); iter != indices.end(); iter++, i++ ) { for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int ndof = field.nCols(); fieldsGroup[_fieldName_].reset(npts,ndof); for (int j = 0; j < nSD_; ++j) { (gradFieldsGroup[_fieldName_][j]).reset(npts,ndof); } for (int dof = 0; dof < ndof; ++dof) { fieldsGroup[_fieldName_](i,dof) = fieldsAtIPs[_fieldName_](*iter,dof); for (int j = 0; j < nSD_; ++j) { gradFieldsGroup[_fieldName_][j](i,dof) = gradFieldsAtIPs[_fieldName_][j](*iter,dof); } } } } // calculate forces, & assemble for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); _fieldItr_ = fields.find(_fieldName_); int index = (int) _fieldName_; if (physicsModel->null(_fieldName_,imat)) continue; if ( rhsMask(index,SOURCE) ) { const DENS_MAT & field = (_fieldItr_->second).quantity(); int ndof = field.nCols(); bool has = physicsModel->weak_equation(_fieldName_)-> N_integrand(fieldsGroup, gradFieldsGroup, mat, group_Nfluxes); if (! has) throw ATC_Error("atomic source can not be null currently"); int i = 0; for (set<int>::const_iterator iter=indices.begin(); iter != indices.end(); iter++, i++ ) { for (int dof = 0; dof < ndof; ++dof) { Nfluxes[_fieldName_](*iter,dof) += group_Nfluxes(i,dof); } } } } // calculate forces, & assemble for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (physicsModel->null(_fieldName_,imat)) continue; int index = (int) _fieldName_; if ( rhsMask(index,FLUX) ) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); int ndof = field.nCols(); physicsModel->weak_equation(_fieldName_)-> B_integrand(fieldsGroup, gradFieldsGroup, mat, group_Bfluxes); int i = 0; for (set<int>::const_iterator iter=indices.begin(); iter != indices.end(); iter++, i++ ) { for (int dof = 0; dof < ndof; ++dof) { for (int j = 0; j < nSD_; ++j) { Bfluxes[_fieldName_][j](*iter,dof) += group_Bfluxes[j](i,dof); } } } } } } } // endif multiple materials // assemble : N/Bfluxes -> rhs_local for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if ( rhsMask(index,SOURCE) && Nfluxes[_fieldName_].nCols() > 0 ) { rhs_local[_fieldName_] += N.transMat(weights*Nfluxes[_fieldName_]); } if ( rhsMask(index,FLUX) && (Bfluxes[_fieldName_][0]).nCols() > 0 ) { for (int j = 0; j < nSD_; ++j) { rhs_local[_fieldName_] += dN[j]->transMat(weights*Bfluxes[_fieldName_][j]); } } } } // end nips > 0 // Share information between processors: rhs_local -> rhs for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_,FLUX) || rhsMask(_fieldName_,SOURCE)) { DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); int count = rhs_local[_fieldName_].size(); allsum(communicator_, rhs_local[_fieldName_].ptr(), myRhs.ptr(), count); } } } // ------------------------------------------------------------- // compute sources using given quadrature // ------------------------------------------------------------- void FE_Engine::compute_source(const Array2D<bool> &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<set<int> >&pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, FIELD_MATS &sources) const { int nips = weights.nCols(); if (nips>0) { FIELD_MATS Nfluxes; // compute fields and gradients of fields at given ips GRAD_FIELD_MATS gradFieldsAtIPs; FIELD_MATS fieldsAtIPs; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); gradFieldsAtIPs[_fieldName_].assign(nSD_,DENS_MAT(nips,dof)); fieldsAtIPs[_fieldName_] = N*field; for (int j = 0; j < nSD_; ++j) { gradFieldsAtIPs[_fieldName_][j] = (*dN[j])*field; } } // treat single material point sets specially int nMatls = pointMaterialGroups.size(); int atomMatls = 0; for (int imat = 0; imat < nMatls; imat++) { const set<int> & indices = pointMaterialGroups(imat); if ( indices.size() > 0) atomMatls++; } bool singleMaterial = ( atomMatls == 1 ); // evaluate physics model if (singleMaterial) { int imat = 0; const Material * mat = physicsModel->material(imat); for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if (! physicsModel->null(_fieldName_,imat)) { if ( rhsMask(index,SOURCE) ) { bool has = physicsModel->weak_equation(_fieldName_)-> N_integrand(fieldsAtIPs, gradFieldsAtIPs, mat, Nfluxes[_fieldName_]); if (! has) throw ATC_Error("atomic source can not be null currently"); } } } } else { throw ATC_Error("compute source does not handle multiple materials currently"); } // assemble for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if ( rhsMask(index,SOURCE) ) { sources[_fieldName_] =weights*Nfluxes[_fieldName_]; } } } // no need to share information between processors } // ------------------------------------------------------------- // compute flux for post processing // ------------------------------------------------------------- void FE_Engine::compute_flux(const Array2D<bool> &rhsMask, const FIELDS &fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, GRAD_FIELD_MATS &fluxes, const DenseMatrix<bool> *elementMask) const { for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_,FLUX)) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); fluxes[_fieldName_].assign(nSD_, DENS_MAT(field.nRows(),field.nCols())); } } // element wise assembly vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; // if element is masked, skip it if (elementMask && !(*elementMask)(ielem,0)) continue; // material id int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); interpolate_fields(ielem,fields,_conn_,_N_,_dN_,_weights_, _fieldsAtIPs_,_gradFieldsAtIPs_); _Nw_ = _weights_*_N_; // evaluate Physics model & assemble for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (!rhsMask(_fieldName_,FLUX)) continue; if (physicsModel->null(_fieldName_,imat)) continue; _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); physicsModel->weak_equation(_fieldName_)-> B_integrand(_fieldsAtIPs_, _gradFieldsAtIPs_, mat, _Bfluxes_); for (int j = 0; j < nSD_; j++) { _Nmat_ = _Nw_.transMat(_Bfluxes_[j]); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int k = 0; k < dof; ++k) { fluxes[_fieldName_][j](inode,k) += _Nmat_(i,k); } } } } } // Assemble partial results for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (!rhsMask(_fieldName_,FLUX)) continue; for (int j = 0; j < nSD_; j++) { allsum(communicator_,MPI_IN_PLACE, fluxes[_fieldName_][j].ptr(), fluxes[_fieldName_][j].size()); } } } //----------------------------------------------------------------- // boundary flux using native quadrature //----------------------------------------------------------------- void FE_Engine::compute_boundary_flux(const Array2D<bool> & rhsMask, const FIELDS & fields, const PhysicsModel * physicsModel, const Array<int> & elementMaterials, const set< pair <int,int> > & faceSet, FIELDS & rhs) const { for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_,FLUX)) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); rhs[_fieldName_].reset(field.nRows(),field.nCols()); } } FIELD_MATS localElementFields; GRAD_FIELD_MATS gradFieldsAtIPs; FIELD_MATS fieldsAtIPs; for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const FIELD & field = _fieldItr_->second; int dof = field.nCols(); gradFieldsAtIPs[_fieldName_].reserve(nSD_); for (int i = 0; i < nSD_; ++i) { gradFieldsAtIPs[_fieldName_].push_back(DENS_MAT(nIPsPerFace_,dof)); } fieldsAtIPs[_fieldName_].reset(nIPsPerFace_,dof); localElementFields[_fieldName_].reset(nNodesPerElement_,dof); } // element wise assembly - set< PAIR >::const_iterator iter; + set< PAIR >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { // get connectivity int ielem = iter->first; // if this is not our element, do not do calculations if (!feMesh_->is_owned_elt(ielem)) continue; int imat = elementMaterials(ielem); const Material * mat = physicsModel->material(imat); _conn_ = feMesh_->element_connectivity_unique(ielem); // evaluate shape functions at ips feMesh_->face_shape_function(*iter, _fN_, _fdN_, _nN_, _fweights_); // interpolate fields and gradients of fields ips of this element for (_fieldItr_ = fields.begin(); _fieldItr_ != fields.end(); _fieldItr_++) { _fieldName_ = _fieldItr_->first; const DENS_MAT & field = (_fieldItr_->second).quantity(); int dof = field.nCols(); for (int i = 0; i < nNodesPerElement_; i++) { for (int j = 0; j < dof; j++) { localElementFields[_fieldName_](i,j) = field(_conn_(i),j); } } // ips X dof = ips X nodes * nodes X dof fieldsAtIPs[_fieldName_] = _fN_*localElementFields[_fieldName_]; for (int j = 0; j < nSD_; ++j) { gradFieldsAtIPs[_fieldName_][j] = _fdN_[j]*localElementFields[_fieldName_]; } } // Evaluate- physics model // do nothing for N_integrand // nN : precomputed and held by ATC_Transfer // assemble for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if ( rhsMask(index,FLUX) ) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); physicsModel->weak_equation(_fieldName_)-> B_integrand(fieldsAtIPs, gradFieldsAtIPs, mat, _Bfluxes_); int dof = field.nCols(); for (int j = 0; j < nSD_; j++) { _Nmat_ = _nN_[j].transMat(_fweights_*_Bfluxes_[j]); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int k = 0; k < dof; ++k) { myRhs(inode,k) += _Nmat_(i,k); } } } } } } for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); int index = (int) _fieldName_; if ( rhsMask(index,FLUX) ) { DENS_MAT & myRhs(rhs[_fieldName_].set_quantity()); allsum(communicator_,MPI_IN_PLACE, myRhs.ptr(), myRhs.size()); } } } // ------------------------------------------------------------- // compute boundary flux using given quadrature and interpolation // ------------------------------------------------------------- void FE_Engine::compute_boundary_flux(const Array2D<bool> & rhsMask, const FIELDS & fields, const PhysicsModel * physicsModel, const Array< int > & elementMaterials, const Array< set<int> > & pointMaterialGroups, const DIAG_MAT & _weights_, const SPAR_MAT & N, const SPAR_MAT_VEC & dN, const DIAG_MAT & flux_mask, FIELDS & flux, const DenseMatrix<bool> * elementMask, const set<int> * nodeSet) const { FIELDS rhs, rhsSubset; for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if (rhsMask(_fieldName_,FLUX)) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); int nrows = field.nRows(); int ncols = field.nCols(); rhs [_fieldName_].reset(nrows,ncols); rhsSubset[_fieldName_].reset(nrows,ncols); } } // R_I = - int_Omega Delta _N_I .q dV compute_rhs_vector(rhsMask, fields, physicsModel, elementMaterials, rhs, elementMask); + // R_I^md = - int_Omega^md Delta _N_I .q dV compute_rhs_vector(rhsMask, fields, physicsModel, pointMaterialGroups, _weights_, N, dN, rhsSubset); - + // flux_I = 1/Delta V_I V_I^md R_I + R_I^md // where : Delta V_I = int_Omega _N_I dV for (int n = 0; n < rhsMask.nRows(); n++) { _fieldName_ = FieldName(n); if ( rhsMask(_fieldName_,FLUX) ) { if (nodeSet) { _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); int nrows = field.nRows(); int ncols = field.nCols(); DENS_MAT & myFlux(flux[_fieldName_].set_quantity()); const DENS_MAT & myRhsSubset(rhsSubset[_fieldName_].quantity()); const DENS_MAT & myRhs(rhs[_fieldName_].quantity()); myFlux.reset(nrows,ncols); set<int>::const_iterator iset; for (iset = nodeSet->begin(); iset != nodeSet->end(); iset++) { for (int j = 0; j < ncols; j++) { myFlux(*iset,j) = myRhsSubset(*iset,j) - flux_mask(*iset,*iset)*myRhs(*iset,j); } } } else { flux[_fieldName_] = rhsSubset[_fieldName_].quantity() - flux_mask*(rhs[_fieldName_].quantity()); } } } } /** integrate a nodal field over an element set */ DENS_VEC FE_Engine::integrate(const DENS_MAT &field, const ESET & eset) const { int dof = field.nCols(); DENS_MAT eField(nNodesPerElement_, dof); int nips = nIPsPerElement_; DENS_MAT ipField(nips, dof); DENS_VEC integral(dof); integral = 0; for (ESET::const_iterator itr = eset.begin(); itr != eset.end(); ++itr) { int ielem = *itr; // if this is not our element, do not do calculations if (!feMesh_->is_owned_elt(ielem)) continue; feMesh_->shape_function(ielem,_N_, _weights_); _conn_ = feMesh_->element_connectivity_unique(ielem); for (int i = 0; i < nNodesPerElement_; i++) { for (int j = 0; j < dof; j++) { eField(i,j) = field(_conn_(i),j); }} ipField = _N_*eField; for (int i = 0; i < nips; ++i) { for (int j = 0; j < dof; ++j) { integral(j) += ipField(i,j)*_weights_[i]; } } } // assemble partial results allsum(communicator_,MPI_IN_PLACE, integral.ptr(), integral.size()); return integral; } //----------------------------------------------------------------- // Robin boundary flux using native quadrature // integrate \int_delV _N_I s(x,t).n dA //----------------------------------------------------------------- void FE_Engine::add_robin_fluxes(const Array2D<bool> &rhsMask, const FIELDS & fields, const double time, const ROBIN_SURFACE_SOURCE & sourceFunctions, FIELDS &nodalSources) const { // sizing working arrays DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT faceSource; DENS_MAT localField; DENS_MAT xAtIPs(nSD_,nIPsPerFace_); DENS_MAT uAtIPs(nIPsPerFace_,1); - + FIELDS myNodalSources; // element wise assembly ROBIN_SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) - { - _fieldName_ = src_iter->first; - if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } + for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) + { + _fieldName_ = src_iter->first; + if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; typedef map<PAIR,Array<UXT_Function*> > FSET; const FSET *fset = (const FSET *)&(src_iter->second); FSET::const_iterator fset_iter; for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) { const PAIR &face = fset_iter->first; const int elem = face.first; // if this is not our element, do not do calculations if (!feMesh_->is_owned_elt(elem)) continue; const Array <UXT_Function*> &fs = fset_iter->second; _conn_ = feMesh_->element_connectivity_unique(elem); // evaluate location at ips feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); feMesh_->element_coordinates(elem, xCoords); xAtIPs = xCoords*(_fN_.transpose()); // collect field _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); feMesh_->element_field(elem, field, localField); uAtIPs = _fN_*localField; // interpolate prescribed flux at ips of this element FSET::const_iterator face_iter = fset->find(face); int nFieldDOF = (face_iter->second).size(); faceSource.reset(nIPsPerFace_,nFieldDOF); for (int ip = 0; ip < nIPsPerFace_; ++ip) { for (int idof = 0; idof<nFieldDOF; ++idof) { UXT_Function * f = fs(idof); if (!f) continue; faceSource(ip,idof) = f->f(&(uAtIPs(ip,0)), column(xAtIPs,ip).ptr(),time); DENS_MAT coefsAtIPs(nIPsPerFace_,1); coefsAtIPs(ip,idof) = f->dfdu(&(uAtIPs(ip,0)), column(xAtIPs,ip).ptr(),time); faceSource(ip,idof) -= coefsAtIPs(ip,0)*uAtIPs(ip,0); } } // assemble - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); _Nmat_ = _fN_.transMat(_fweights_*faceSource); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } } } } // assemble partial result matrices for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!rhsMask((int) _fieldName_,ROBIN_SOURCE)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; } } //----------------------------------------------------------------- // Robin boundary flux stiffness using native quadrature // integrate \int_delV _N_I ds/du(x,t).n dA //----------------------------------------------------------------- void FE_Engine::add_robin_tangent(const Array2D<bool> &rhsMask, const FIELDS & fields, const double time, const ROBIN_SURFACE_SOURCE & sourceFunctions, SPAR_MAT &tangent) const { // sizing working arrays DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT coefsAtIPs; DENS_MAT localField; DENS_MAT xAtIPs(nSD_,nIPsPerFace_); DENS_MAT uAtIPs(nIPsPerFace_,1); // element wise assembly ROBIN_SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out result (tangent) matrix on all non-main processors - // to avoid multiple-counting of the values already in tangent - tangent.reset(tangent.nRows(), tangent.nCols()); - } + SPAR_MAT K(tangent.nRows(), tangent.nCols()); for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!rhsMask((int)_fieldName_,ROBIN_SOURCE)) continue; typedef map<PAIR,Array<UXT_Function*> > FSET; const FSET *fset = (const FSET *)&(src_iter->second); FSET::const_iterator fset_iter; for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) { const PAIR &face = fset_iter->first; const int elem = face.first; // if this is not our element, do not do calculations if (!feMesh_->is_owned_elt(elem)) continue; const Array <UXT_Function*> &fs = fset_iter->second; _conn_ = feMesh_->element_connectivity_unique(elem); // evaluate location at ips feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); feMesh_->element_coordinates(elem, xCoords); xAtIPs = xCoords*(_fN_.transpose()); // collect field _fieldItr_ = fields.find(_fieldName_); const DENS_MAT & field = (_fieldItr_->second).quantity(); feMesh_->element_field(elem, field, localField); uAtIPs = _fN_*localField; // interpolate prescribed flux at ips of this element FSET::const_iterator face_iter = fset->find(face); int nFieldDOF = (face_iter->second).size(); coefsAtIPs.reset(nIPsPerFace_,nFieldDOF); for (int ip = 0; ip < nIPsPerFace_; ++ip) { for (int idof = 0; idof<nFieldDOF; ++idof) { UXT_Function * f = fs(idof); if (!f) continue; coefsAtIPs(ip,idof) = f->dfdu(&(uAtIPs(ip,0)), column(xAtIPs,ip).ptr(),time); } } // assemble DIAG_MAT D(column(coefsAtIPs,0)); D *= -1; - D *= _fweights_; _Nmat_ = _fN_.transMat(D*_fN_); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = _conn_(j); - tangent.add(inode, jnode, _Nmat_(i,j)); + K.add(inode, jnode, _Nmat_(i,j)); } } } } // assemble partial result matrices #ifdef ISOLATE_FE - sparse_allsum(communicator_,tangent); + sparse_allsum(communicator_,K); #else - LammpsInterface::instance()->sparse_allsum(tangent); + LammpsInterface::instance()->sparse_allsum(K); +#endif + tangent += K; + } + + //----------------------------------------------------------------- + // Robin boundary flux using native quadrature + // integrate \int_delV _N_I s(x,t).n dA + //----------------------------------------------------------------- + void FE_Engine::add_open_fluxes(const Array2D<bool> &rhsMask, + const FIELDS & fields, + const OPEN_SURFACE & openFaces, + FIELDS &nodalSources, + const FieldName Velocity) const + { + + // sizing working arrays + DENS_MAT xCoords(nSD_,nNodesPerElement_); + DENS_MAT faceSource; + DENS_MAT localField; + DENS_MAT xAtIPs(nSD_,nIPsPerFace_); + DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols); + DENS_MAT aAtIPs(nIPsPerFace_,1); + FIELDS myNodalSources; + + // throw error if electron velocity is not defined + + // element wise assembly + OPEN_SURFACE::const_iterator face_iter; + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); + } + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + + typedef set<PAIR> FSET; + const FSET *fset = (const FSET *)&(face_iter->second); + FSET::const_iterator fset_iter; + for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) + { + const PAIR &face = *fset_iter; + const int elem = face.first; + // if this is not our element, do not do calculations + if (!feMesh_->is_owned_elt(elem)) continue; + // get velocity, multiply with field (vector gives tensor) + // dot with face normal + _conn_ = feMesh_->element_connectivity_unique(elem); + // evaluate location at ips + feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); + // collect field at ips + _fieldItr_ = fields.find(_fieldName_); + const DENS_MAT & field = (_fieldItr_->second).quantity(); + feMesh_->element_field(elem, field, localField); + int nFieldDOF = field.nCols(); + // "u" is the quantity being advected + uAtIPs.reset(nIPsPerFace_,nFieldDOF); + uAtIPs = _fN_*localField; + // collect velocity at ips for "advection" = v.n + _fieldItr_ = fields.find(Velocity); + const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity + feMesh_->element_field(elem, advection, localField); + for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int I = 0; I < nNodesPerElement_; ++I) { + aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j); + } + } + } + // construct open flux at ips of this element + // flux = field \otimes advection_vector \dot n + FSET::const_iterator face_iter = fset->find(face); + faceSource.reset(nIPsPerFace_,nFieldDOF); + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int idof = 0; idof<nFieldDOF; ++idof) { + // multiply field DOF value times advection vector + faceSource(ip,idof) = aAtIPs(ip,1)*uAtIPs(ip,idof);//(v.n) u + } + } + // assemble contributions for this direction of the face normal + // _nN_[j](ip,I) nodal shapefunction(I) at ip X face normal(j) + // _fweights_ diagonal nips X nips ==> facesource is nips X ndofs + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + _Nmat_ = _fN_.transMat(_fweights_*faceSource); + // s_Ii = \sum_ip N_I (u_i v.n)_ip wg_ip + for (int i = 0; i < nNodesPerElement_; ++i) { + int inode = _conn_(i); + for (int idof = 0; idof < nFieldDOF; ++idof) { + s(inode,idof) += _Nmat_(i,idof); + } + } + } + } + // assemble partial result matrices + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) { + _fieldName_ = face_iter->first; + if (!rhsMask((int) _fieldName_,OPEN_SOURCE)) continue; + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; + } + } + + //----------------------------------------------------------------- + // Open boundary flux stiffness using native quadrature + // integrate \int_delV _N_I ds/du(x,t).n dA + //----------------------------------------------------------------- + void FE_Engine::add_open_tangent(const Array2D<bool> &rhsMask, + const FIELDS & fields, + const OPEN_SURFACE & openFaces, + SPAR_MAT &tangent, + const FieldName Velocity) const + { + + // sizing working arrays + DENS_MAT xCoords(nSD_,nNodesPerElement_); + DENS_MAT faceSource; + DENS_MAT localField; + DENS_MAT xAtIPs(nSD_,nIPsPerFace_); + DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols); + DENS_MAT aAtIPs(nIPsPerFace_,nSD_); + SPAR_MAT K(tangent.nRows(), tangent.nCols()); + // element wise assembly + OPEN_SURFACE::const_iterator face_iter; + for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) + { + _fieldName_ = face_iter->first; + if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue; + bool convective = false; + if (_fieldName_ == Velocity) convective = true; + + typedef set<PAIR> FSET; + const FSET *fset = (const FSET *)&(face_iter->second); + FSET::const_iterator fset_iter; + for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) + { + const PAIR &face = *fset_iter; + const int elem = face.first; + // if this is not our element, do not do calculations + if (!feMesh_->is_owned_elt(elem)) continue; + _conn_ = feMesh_->element_connectivity_unique(elem); + // evaluate location at ips + feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); + // collect field at ips + _fieldItr_ = fields.find(_fieldName_); + const DENS_MAT & field = (_fieldItr_->second).quantity(); + feMesh_->element_field(elem, field, localField); + int nFieldDOF = field.nCols(); + // "u" is the quantity being advected + uAtIPs.reset(nIPsPerFace_,nFieldDOF); + uAtIPs = _fN_*localField; + // collect velocity at ips for "advection" = v.n + _fieldItr_ = fields.find(Velocity); + const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity + feMesh_->element_field(elem, advection, localField); + for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int I = 0; I < nNodesPerElement_; ++I) { + aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j); + } + } + } + // K_IJ = \sum_ip N_I ( v.n I + u (x) n ) N_J wg_ip + DENS_MAT D(nFieldDOF,nFieldDOF); + DENS_VEC n(nSD_); + for (int ip = 0; ip < nIPsPerFace_; ++ip) { + for (int idof = 0; idof<nFieldDOF; ++idof) { + D(idof,idof) -= aAtIPs(ip,0); + if (convective) { + feMesh_->face_normal(face,ip,n); + for (int jdof = 0; jdof<nFieldDOF; ++jdof) { + D(idof,jdof) -= uAtIPs(ip,idof)*n(jdof); + } + } + } + } + // assemble + _Nmat_ = _fN_.transMat(D*_fN_); + for (int i = 0; i < nNodesPerElement_; ++i) { + int inode = _conn_(i); + for (int j = 0; j < nNodesPerElement_; ++j) { + int jnode = _conn_(j); + K.add(inode, jnode, _Nmat_(i,j)); + } + } + } + } + // assemble partial result matrices +#ifdef ISOLATE_FE + sparse_allsum(communicator_,K); +#else + LammpsInterface::instance()->sparse_allsum(K); #endif + tangent += K; } //----------------------------------------------------------------- // prescribed boundary flux using native quadrature // integrate \int_delV _N_I s(x,t).n dA //----------------------------------------------------------------- void FE_Engine::add_fluxes(const Array<bool> &fieldMask, const double time, const SURFACE_SOURCE & sourceFunctions, FIELDS &nodalSources) const { // sizing working arrays DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT xAtIPs(nSD_,nIPsPerFace_); DENS_MAT faceSource; + FIELDS myNodalSources; // element wise assembly SURFACE_SOURCE::const_iterator src_iter; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) - { - _fieldName_ = src_iter->first; - if (!fieldMask((int)_fieldName_)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } + for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) + { + _fieldName_ = src_iter->first; + if (!fieldMask((int)_fieldName_)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!fieldMask((int)_fieldName_)) continue; typedef map<PAIR,Array<XT_Function*> > FSET; const FSET *fset = (const FSET *)&(src_iter->second); FSET::const_iterator fset_iter; for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) { const PAIR &face = fset_iter->first; const int elem = face.first; // if this is not our element, do not do calculations if (!feMesh_->is_owned_elt(elem)) continue; const Array <XT_Function*> &fs = fset_iter->second; _conn_ = feMesh_->element_connectivity_unique(elem); // evaluate location at ips feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_); feMesh_->element_coordinates(elem, xCoords); MultAB(xCoords,_fN_,xAtIPs,0,1); //xAtIPs = xCoords*(N.transpose()); // interpolate prescribed flux at ips of this element FSET::const_iterator face_iter = fset->find(face); if (face_iter == fset->end()) { stringstream ss; ss << "face not found" << std::endl; print_msg(communicator_,ss.str()); } int nFieldDOF = (face_iter->second).size(); faceSource.reset(nIPsPerFace_,nFieldDOF); for (int ip = 0; ip < nIPsPerFace_; ++ip) { for (int idof = 0; idof<nFieldDOF; ++idof) { XT_Function * f = fs(idof); if (!f) continue; faceSource(ip,idof) = f->f(column(xAtIPs,ip).ptr(),time); } } // assemble - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); _Nmat_ = _fN_.transMat(_fweights_*faceSource); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } // end assemble nFieldDOF } // end assemble nNodesPerElement_ } // end fset loop } // field loop // assemble partial result matrices for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!fieldMask((int)_fieldName_)) continue; - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; } } //----------------------------------------------------------------- // prescribed volume flux using native quadrature // integrate \int_V _N_I s(x,t) dV //----------------------------------------------------------------- void FE_Engine::add_sources(const Array<bool> &fieldMask, const double time, const VOLUME_SOURCE &sourceFunctions, FIELDS &nodalSources) const { // sizing working arrays DENS_MAT elemSource; DENS_MAT xCoords(nSD_,nNodesPerElement_); DENS_MAT xAtIPs(nSD_,nIPsPerElement_); + FIELDS myNodalSources; - if (!(rank_zero(communicator_))) { - // Zero out unmasked nodal sources on all non-main processors. - // This is to avoid counting the previous nodal source values - // multiple times when we aggregate. - for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); - src_iter != sourceFunctions.end(); src_iter++) { - _fieldName_ = src_iter->first; - int index = (int) _fieldName_; - if (fieldMask(index)) { - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - myNodalSources.reset(myNodalSources.nRows(), myNodalSources.nCols()); - } - } + for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); + src_iter != sourceFunctions.end(); src_iter++) { + _fieldName_ = src_iter->first; + int index = (int) _fieldName_; + if (! fieldMask(index)) continue; + DENS_MAT & s(nodalSources[_fieldName_].set_quantity()); + myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols()); } - vector<int> myElems = feMesh_->owned_elts(); - for (vector<int>::const_iterator elemsIter = myElems.begin(); + for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; _conn_ = feMesh_->element_connectivity_unique(ielem); // evaluate location at ips feMesh_->shape_function(ielem, _N_, _weights_); feMesh_->element_coordinates(ielem, xCoords); xAtIPs =xCoords*(_N_.transpose()); for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); src_iter != sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; int index = (int) _fieldName_; if ( fieldMask(index) ) { const Array2D<XT_Function *> * thisSource = (const Array2D<XT_Function *> *) &(src_iter->second); int nFieldDOF = thisSource->nCols(); elemSource.reset(nIPsPerElement_,nFieldDOF); // interpolate prescribed flux at ips of this element for (int ip = 0; ip < nIPsPerElement_; ++ip) { for (int idof = 0; idof < nFieldDOF; ++idof) { XT_Function * f = (*thisSource)(ielem,idof); if (f) { elemSource(ip,idof) = f->f(column(xAtIPs,ip).ptr(),time); } } } // assemble _Nmat_ = _N_.transMat(_weights_*elemSource); - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); for (int i = 0; i < nNodesPerElement_; ++i) { int inode = _conn_(i); for (int idof = 0; idof < nFieldDOF; ++idof) { - myNodalSources(inode,idof) += _Nmat_(i,idof); + s(inode,idof) += _Nmat_(i,idof); } } } } } // Aggregate unmasked nodal sources on all processors. for (VOLUME_SOURCE::const_iterator src_iter = sourceFunctions.begin(); src_iter != sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; int index = (int) _fieldName_; - if (fieldMask(index)) { - DENS_MAT & myNodalSources(nodalSources[_fieldName_].set_quantity()); - allsum(communicator_,MPI_IN_PLACE, myNodalSources.ptr(), myNodalSources.size()); + if (!fieldMask(index)) continue; + DENS_MAT & s(myNodalSources[_fieldName_].set_quantity()); + allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size()); + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + src += s; + } + } + //----------------------------------------------------------------- + // previously computed nodal sources + //----------------------------------------------------------------- + void FE_Engine::add_sources(const Array<bool> &fieldMask, + const double time, + const FIELDS &sources, + FIELDS &nodalSources) const + { + FIELDS::const_iterator itr; + for (itr=sources.begin(); itr!=sources.end(); itr++) { + _fieldName_ = itr->first; + if (!fieldMask((int)_fieldName_)) continue; + DENS_MAT & src(nodalSources[_fieldName_].set_quantity()); + const DENS_MAT & s((sources.find(_fieldName_)->second).quantity()); + for (int inode = 0; inode < nNodesUnique_; ++inode) { + for (int j = 0; j < src.nCols(); ++j) { + src(inode,j) += s(inode,j); + } } } } //----------------------------------------------------------------- // boundary integral of a nodal field //----------------------------------------------------------------- void FE_Engine::field_surface_flux( const DENS_MAT & field, const set< PAIR > & faceSet, DENS_MAT & values, const bool contour, const int axis) const { int dof = field.nCols(); double a[3] = {0,0,0}; a[axis] = 1; // sizing working arrays DENS_MAT n(nSD_,nIPsPerFace_); DENS_MAT localElementFields(nNodesPerElement_,dof); DENS_MAT integrals(dof,nSD_); DENS_MAT fieldsAtIPs; // SJL shouldn't this just be _fieldsAtIPs_ // the data member? // element wise assembly - set< pair <int,int> >::const_iterator iter; + set< pair <int,int> >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { int ielem = iter->first; // if this is not our element, do not do calculations //if (!feMesh_->is_owned_elt(ielem)) continue; // evaluate shape functions at ips feMesh_->face_shape_function(*iter, _N_, n, _fweights_); // cross n with axis to get tangent if (contour) { double t[3]; for (int i = 0; i < nIPsPerFace_; i++) { t[0] = a[1]*n(2,i) - a[2]*n(1,i); t[1] = a[2]*n(0,i) - a[0]*n(2,i); t[2] = a[0]*n(1,i) - a[1]*n(0,i); n(0,i) = t[0]; n(1,i) = t[1]; n(2,i) = t[2]; } } // get connectivity _conn_ = feMesh_->element_connectivity_unique(ielem); // interpolate fields and gradients of fields ips of this element for (int i = 0; i < nNodesPerElement_; i++) { for (int j = 0; j < dof; j++) { localElementFields(i,j) = field(_conn_(i),j); } } // ips X dof = ips X nodes * nodes X dof fieldsAtIPs = _N_*localElementFields; // assemble : integral(k,j) = sum_ip n(j,ip) wg(ip,ip) field(ip,k) _Nmat_ = n*_fweights_*fieldsAtIPs; for (int j = 0; j < nSD_; j++) { for (int k = 0; k < dof; ++k) { integrals(k,j) += _Nmat_(j,k); } } } // assemble partial results //LammpsInterface::instance()->allsum(MPI_IN_PLACE, integrals.ptr(), integrals.size()); // (S.n)_1 = S_1j n_j = S_11 n_1 + S_12 n_2 + S_13 n_3 // (S.n)_2 = S_2j n_j = S_21 n_1 + S_22 n_2 + S_23 n_3 // (S.n)_3 = S_3j n_j = S_31 n_1 + S_32 n_2 + S_33 n_3 if (dof == 9) { // tensor values.reset(nSD_,1); values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2); values(1,0) = integrals(3,0)+integrals(4,1)+integrals(5,2); values(2,0) = integrals(6,0)+integrals(7,1)+integrals(8,2); } else if (dof == 6) { // sym tensor values.reset(nSD_,1); values(0,0) = integrals(0,0)+integrals(3,1)+integrals(4,2); values(1,0) = integrals(3,0)+integrals(1,1)+integrals(5,2); values(2,0) = integrals(4,1)+integrals(5,1)+integrals(2,2); } // (v.n) = v_j n_j else if (dof == 3) { // vector values.reset(1,1); values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2); } // s n = s n_j e_j else if (dof == 1) { // scalar values.reset(nSD_,1); values(0,0) = integrals(0,0); values(1,0) = integrals(0,1); values(2,0) = integrals(0,2); } else { string msg = "FE_Engine::field_surface_flux unsupported field width: "; msg += to_string(dof); throw ATC_Error(msg); } } //----------------------------------------------------------------- // evaluate shape functions at given points //----------------------------------------------------------------- void FE_Engine::evaluate_shape_functions( const MATRIX & pt_coords, SPAR_MAT & N) const { // Get shape function and derivatives at atomic locations int nnodes = feMesh_->num_nodes_unique(); int npts = pt_coords.nRows(); // loop over point (atom) coordinates DENS_VEC x(nSD_); Array<int> node_index(nNodesPerElement_); DENS_VEC shp(nNodesPerElement_); N.reset(npts,nnodes); for (int i = 0; i < npts; ++i) { for (int k = 0; k < nSD_; ++k) { x(k) = pt_coords(i,k); } feMesh_->shape_functions(x,shp,node_index); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = node_index(j); N.add(i,jnode,shp(j)); } } N.compress(); } //----------------------------------------------------------------- // evaluate shape functions and their spatial derivatives at given points //----------------------------------------------------------------- void FE_Engine::evaluate_shape_functions( const MATRIX & pt_coords, SPAR_MAT & N, SPAR_MAT_VEC & dN) const { // Get shape function and derivatives at atomic locations int nnodes = feMesh_->num_nodes_unique(); int npts = pt_coords.nRows(); // loop over point (atom) coordinates DENS_VEC x(nSD_); Array<int> node_index(nNodesPerElement_); DENS_VEC shp(nNodesPerElement_); DENS_MAT dshp(nSD_,nNodesPerElement_); for (int k = 0; k < nSD_; ++k) { dN[k]->reset(npts,nnodes); } N.reset(npts,nnodes); for (int i = 0; i < npts; ++i) { for (int k = 0; k < nSD_; ++k) { x(k) = pt_coords(i,k); } feMesh_->shape_functions(x,shp,dshp,node_index); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = node_index(j); N.add(i,jnode,shp(j)); for (int k = 0; k < nSD_; ++k) { dN[k]->add(i,jnode,dshp(k,j)); } } } N.compress(); for (int k = 0; k < nSD_; ++k) { dN[k]->compress(); } } //----------------------------------------------------------------- // evaluate shape functions at given points //----------------------------------------------------------------- void FE_Engine::evaluate_shape_functions( const MATRIX & pt_coords, const INT_ARRAY & pointToEltMap, SPAR_MAT & N) const { // Get shape function and derivatives at atomic locations int nnodes = feMesh_->num_nodes_unique(); int npts = pt_coords.nRows(); // loop over point (atom) coordinates DENS_VEC x(nSD_); Array<int> node_index(nNodesPerElement_); DENS_VEC shp(nNodesPerElement_); N.reset(npts,nnodes); for (int i = 0; i < npts; ++i) { for (int k = 0; k < nSD_; ++k) { x(k) = pt_coords(i,k); } int eltId = pointToEltMap(i,0); feMesh_->shape_functions(x,eltId,shp,node_index); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = node_index(j); N.add(i,jnode,shp(j)); } } N.compress(); } //----------------------------------------------------------------- // evaluate shape functions and their spatial derivatives at given points //----------------------------------------------------------------- void FE_Engine::evaluate_shape_functions( const MATRIX & pt_coords, const INT_ARRAY & pointToEltMap, SPAR_MAT & N, SPAR_MAT_VEC & dN) const { // Get shape function and derivatives at atomic locations int nnodes = feMesh_->num_nodes_unique(); int npts = pt_coords.nRows(); // loop over point (atom) coordinates DENS_VEC x(nSD_); Array<int> node_index(nNodesPerElement_); DENS_VEC shp(nNodesPerElement_); DENS_MAT dshp(nSD_,nNodesPerElement_); for (int k = 0; k < nSD_; ++k) { dN[k]->reset(npts,nnodes); } N.reset(npts,nnodes); for (int i = 0; i < npts; ++i) { for (int k = 0; k < nSD_; ++k) { x(k) = pt_coords(i,k); } int eltId = pointToEltMap(i,0); feMesh_->shape_functions(x,eltId,shp,dshp,node_index); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = node_index(j); N.add(i,jnode,shp(j)); for (int k = 0; k < nSD_; ++k) { dN[k]->add(i,jnode,dshp(k,j)); } } } N.compress(); for (int k = 0; k < nSD_; ++k) { dN[k]->compress(); } } //----------------------------------------------------------------- // evaluate shape functions spatial derivatives at given points //----------------------------------------------------------------- void FE_Engine::evaluate_shape_function_derivatives( const MATRIX & pt_coords, const INT_ARRAY & pointToEltMap, SPAR_MAT_VEC & dNdx ) const { int nnodes = feMesh_->num_nodes_unique(); int npts = pt_coords.nRows(); for (int k = 0; k < nSD_; ++k) { dNdx[k]->reset(npts,nnodes); } // loop over point (atom) coordinates DENS_VEC x(nSD_); Array<int> node_index(nNodesPerElement_); DENS_MAT dshp(nSD_,nNodesPerElement_); for (int i = 0; i < npts; ++i) { for (int k = 0; k < nSD_; ++k) { x(k) = pt_coords(i,k); } int eltId = pointToEltMap(i,0); feMesh_->shape_function_derivatives(x,eltId,dshp,node_index); for (int j = 0; j < nNodesPerElement_; ++j) { int jnode = node_index(j); for (int k = 0; k < nSD_; ++k) { dNdx[k]->add(i,jnode,dshp(k,j)); } } } for (int k = 0; k < nSD_; ++k) { dNdx[k]->compress(); } } //------------------------------------------------------------------- // set kernels //------------------------------------------------------------------- void FE_Engine::set_kernel(KernelFunction* ptr){kernelFunction_=ptr;} //------------------------------------------------------------------- // kernel_matrix_bandwidth //------------------------------------------------------------------- int FE_Engine::kernel_matrix_bandwidth( const MATRIX & ptCoords) const { if (! kernelFunction_) throw ATC_Error("no kernel function specified"); int npts = ptCoords.nRows(); int bw = 0; if (npts>0) { DENS_VEC xI(nSD_),x(nSD_),dx(nSD_); for (int i = 0; i < nNodes_; i++) { xI = feMesh_->global_coordinates(i); for (int j = 0; j < npts; j++) { for (int k = 0; k < nSD_; ++k) { x(k) = ptCoords(j,k); } dx = x - xI; if (kernelFunction_->in_support(dx)) bw = max(bw,abs(j-map_global_to_unique(i))); } } } return bw; } //------------------------------------------------------------------- // evaluate kernels //------------------------------------------------------------------- void FE_Engine::evaluate_kernel_functions( const MATRIX & ptCoords, SPAR_MAT & N ) const { #ifdef EXTENDED_ERROR_CHECKING print_msg_once(communicator_,"kernel matrix bandwidth " +to_string(kernel_matrix_bandwidth(ptCoords))); #endif if (! kernelFunction_) throw ATC_Error("no kernel function specified"); int npts = ptCoords.nRows(); if (npts>0) { N.reset(npts,nNodesUnique_); // transpose of N_Ia DENS_VEC xI(nSD_),x(nSD_),dx(nSD_); for (int i = 0; i < nNodes_; i++) { xI = feMesh_->global_coordinates(i); for (int j = 0; j < npts; j++) { for (int k = 0; k < nSD_; ++k) { x(k) = ptCoords(j,k); } dx = x - xI; double val = kernelFunction_->value(dx); val *= kernelFunction_->dimensionality_factor(); if (val > 0) N.add(j,map_global_to_unique(i),val); } } N.compress(); } } //----------------------------------------------------------------- // create a non-uniform rectangular mesh on a rectangular region //----------------------------------------------------------------- void FE_Engine::create_mesh(Array<double> & dx, Array<double> & dy, Array<double> & dz, const char * regionName, Array<bool> periodicity) { double xmin, xmax, ymin, ymax, zmin, zmax; double xscale, yscale, zscale; // check to see if region exists and is it a box, and if so get the bounds bool isBox; isBox = ATC::LammpsInterface::instance()->region_bounds( regionName, xmin, xmax, ymin, ymax, zmin, zmax, xscale, yscale, zscale); if (!isBox) throw ATC_Error("Region for FE mesh is not a box"); if (dx(0) == 0.0) dx = (xmax-xmin)/dx.size(); if (dy(0) == 0.0) dy = (ymax-ymin)/dy.size(); if (dz(0) == 0.0) dz = (zmax-zmin)/dz.size(); feMesh_ = new FE_Rectangular3DMesh(dx,dy,dz, xmin, xmax, ymin, ymax, zmin, zmax, periodicity, xscale, yscale, zscale); stringstream ss; ss << "created structured mesh with " << feMesh_->num_nodes() << " nodes, " << feMesh_->num_nodes_unique() << " unique nodes, and " << feMesh_->num_elements() << " elements"; print_msg_once(communicator_,ss.str()); #ifdef ATC_VERBOSE - string msg = "element sizes in x:\n"; + print_partitions(xmin,xmax,dx); + print_partitions(ymin,ymax,dy); + print_partitions(zmin,zmax,dz); +#endif + } + //----------------------------------------------------------------- + void FE_Engine::print_partitions(double xmin, double xmax, Array<double> & dx) const { + stringstream msg; + msg.precision(3); + msg << std::fixed; + msg << "\nindex weight fraction location size[A] size[uc]:\n"; double sum = 0; for (int i = 0; i < dx.size(); ++i) { sum += dx(i); } double xn= 1.0/sum; double xs= xn*(xmax-xmin); double xl= xs/(ATC::LammpsInterface::instance()->xlattice()); double sumn = 0, sums = 0, suml = 0; + double x = xmin; for (int i = 0; i < dx.size(); ++i) { double dxn = dx(i)*xn; sumn += dxn; double dxs = dx(i)*xs; sums += dxs; double dxl = dx(i)*xl; suml += dxl; - msg += " "+to_string(i+1) - +" "+to_string(dx(i)) - +" "+to_string(dxn) - +" "+to_string(dxs) - +" "+to_string(dxl)+"\n"; + msg << std::setw(5) << i+1 + << std::setw(7) << dx(i) + << std::setw(9) << dxn + << std::setw(9) << x + << std::setw(8) << dxs + << std::setw(9) << dxl << "\n"; + x += dxs; } - msg += "sum "+to_string(sum)+" " - +to_string(sumn)+" " - +to_string(sums)+" " - +to_string(suml)+"\n"; - print_msg_once(communicator_,msg); -#endif + msg << "sum " << setw(7) << sum + << setw(9) << sumn + << setw(9) << x + << setw(8) << sums + << setw(9) << suml << "\n"; + print_msg_once(communicator_,msg.str()); } - //----------------------------------------------------------------- // create a uniform rectangular mesh on a rectangular region //----------------------------------------------------------------- void FE_Engine::create_mesh(int nx, int ny, int nz, const char * regionName, Array<bool> periodicity) { double xmin, xmax, ymin, ymax, zmin, zmax; double xscale, yscale, zscale; // check to see if region exists and is it a box, and if so get the bounds bool isBox; isBox = ATC::LammpsInterface::instance()->region_bounds( regionName, xmin, xmax, ymin, ymax, zmin, zmax, xscale, yscale, zscale); if (!isBox) throw ATC_Error("Region for FE mesh is not a box"); feMesh_ = new FE_Uniform3DMesh(nx,ny,nz, xmin, xmax, ymin, ymax, zmin, zmax, periodicity, xscale, yscale, zscale); stringstream ss; ss << "created uniform mesh with " << feMesh_->num_nodes() << " nodes, " << feMesh_->num_nodes_unique() << " unique nodes, and " << feMesh_->num_elements() << " elements"; print_msg_once(communicator_,ss.str()); } //----------------------------------------------------------------- // read a mesh from an input file using MeshReader object //----------------------------------------------------------------- void FE_Engine::read_mesh(string meshFile, Array<bool> & periodicity) { if (feMesh_) throw ATC_Error("FE_Engine already has a mesh"); feMesh_ = MeshReader(meshFile,periodicity).create_mesh(); feMesh_->initialize(); } }; diff --git a/lib/atc/FE_Engine.h b/lib/atc/FE_Engine.h index ab902fe60..bc3fb0e4a 100644 --- a/lib/atc/FE_Engine.h +++ b/lib/atc/FE_Engine.h @@ -1,556 +1,579 @@ #ifndef FE_ENGINE_H #define FE_ENGINE_H #include <vector> #include <map> #include <set> #include <utility> #include <string> #include "ATC_TypeDefs.h" #include "Array.h" #include "Array2D.h" #include "FE_Mesh.h" #include "PhysicsModel.h" #include "OutputManager.h" #include "MeshReader.h" #include "mpi.h" namespace ATC { class ATC_Method; class FE_Element; class XT_Function; class KernelFunction; /** * @class FE_Engine * @brief Base class for computing and assembling mass matrix * and rhs vectors */ class FE_Engine{ public: /** constructor/s */ FE_Engine(MPI_Comm comm); /** destructor */ ~FE_Engine(); /** initialize */ void initialize(); MPI_Comm communicator() {return communicator_;} void partition_mesh(); void departition_mesh(); bool is_partitioned() const { return feMesh_->is_partitioned(); } int map_elem_to_myElem(int elemID) const { return feMesh_->map_elem_to_myElem(elemID); } int map_myElem_to_elem(int myElemID) const { return feMesh_->map_myElem_to_elem(myElemID); } // note: it is misleading to declare the following const // because it touches the nIPsPer* data members, which // are now declared mutable. Why? Well, set_quadrature // has to be called from a const function, and all the // matrices dependent on nIPsPer* are declared mutable // as well (and have been). I think this is because a // const engine needs to be able to deal with various // quadratures and update its data members directly, which // are really convenience-copies of data members that // are more pertinent to other classes (FE_Interpolate, // for the most part) that it uses temporarily for space/ // time speedups while doing it's computations. // // I approve of this usage of mutable, but the const/ // non-const member function declaring in this class is // really all wrong to begin with. /** set quadrature scheme, resize matrices if necessary as per * initialize() */ void set_quadrature(FeIntQuadrature quadType, bool temp=true) const; /** parser/modifier */ bool modify(int narg, char **arg); /** finish up */ void finish(); /** print out the "global connectivity" of all elements */ void print_mesh() const; //---------------------------------------------------------------- /** \name output */ //---------------------------------------------------------------- /*@{*/ /** these assume the caller is handling the parallel collection */ void initialize_output(int rank, std::string outputPrefix, std::set<int> otypes); /** write geometry */ void write_geometry(void); /** write data: data is arrayed over _unique_ nodes and then mapped by the engine */ void write_data(double time, FIELDS &soln, OUTPUT_LIST *data=NULL); void write_data(double time, OUTPUT_LIST *data); void write_restart_file(std::string fileName, RESTART_LIST *data) { outputManager_.write_restart_file(fileName,data); } void read_restart_file(std::string fileName, RESTART_LIST *data) { outputManager_.read_restart_file(fileName,data); } void delete_elements(const std::set<int> &elementList); void cut_mesh(const std::set<PAIR> &cutFaces, const std::set<int> &edgeNodes); void add_global(const std::string name, const double value) { outputManager_.add_global(name,value); } void add_field_names(const std::string field, const std::vector<std::string> & names) { outputManager_.add_field_names(field,names); } void reset_globals() { outputManager_.reset_globals(); } /** pass through to access output manager */ OutputManager *output_manager() { return &outputManager_; } /*@}*/ //---------------------------------------------------------------- /** \name assembled matrices and vectors */ //---------------------------------------------------------------- /*@{*/ DENS_VEC interpolate_field(const DENS_VEC & x, const FIELD & f) const; /** interpolate fields */ void interpolate_fields(const int ielem, const FIELDS &fields, AliasArray<int> &conn, DENS_MAT &N, DIAG_MAT &weights, std::map<FieldName,DENS_MAT> &fieldsAtIPs) const; /** interpolate fields & gradients */ void interpolate_fields(const int ielem, const FIELDS &fields, AliasArray<int> &conn, DENS_MAT &N, DENS_MAT_VEC &dN, DIAG_MAT &weights, FIELD_MATS &fieldsAtIPs, GRAD_FIELD_MATS &grad_fieldsAtIPs) const; /** compute a dimensionless stiffness matrix */ void stiffness_matrix(SPAR_MAT &matrix) const; /** compute tangent matrix for a pair of fields - native quadrature */ void compute_tangent_matrix( const RHS_MASK &rhsMask, const std::pair<FieldName,FieldName> row_col, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, SPAR_MAT &tangent, const DenseMatrix<bool> *elementMask=NULL) const; /** compute tangent matrix for a pair of fields - given quadrature */ void compute_tangent_matrix(const RHS_MASK &rhsMask, const std::pair<FieldName,FieldName> row_col, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<std::set<int> > &pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, SPAR_MAT &tangent, const DenseMatrix<bool> *elementMask=NULL) const; /** compute a consistent mass matrix for a field */ void compute_mass_matrix( const Array<FieldName> &mask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, CON_MASS_MATS &mass_matrix, const DenseMatrix<bool> *elementMask=NULL) const; /** compute a dimensionless mass matrix */ void compute_mass_matrix(SPAR_MAT &mass_matrix) const; /** computes a dimensionless mass matrix for the given-quadrature */ void compute_mass_matrix(const DIAG_MAT &weights, const SPAR_MAT &N, SPAR_MAT &mass_matrix) const; /** compute a single dimensionless mass matrix */ void compute_lumped_mass_matrix(DIAG_MAT &lumped_mass_matrix) const; /** compute lumped mass matrix = diag (\int \rho N_I dV) */ void compute_lumped_mass_matrix( const Array<FieldName> &mask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, MASS_MATS &mass_matrix, const DenseMatrix<bool> *elementMask=NULL) const; /** compute dimensional lumped mass matrix using given quadrature */ void compute_lumped_mass_matrix( const Array<FieldName> &mask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<std::set<int> > &pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, MASS_MATS &mass_matrix) const; /** compute an approximation to a finite difference gradient from mesh */ void compute_gradient_matrix(SPAR_MAT_VEC &grad_matrix) const; /** compute energy */ void compute_energy(const Array<FieldName> &mask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, FIELD_MATS &energy, const DenseMatrix<bool> *elementMask=NULL, const IntegrationDomainType domain=FULL_DOMAIN) const; /** compute residual or RHS of the dynamic weak eqn */ void compute_rhs_vector( const RHS_MASK &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, FIELDS &rhs, + bool freeOnly=false, const DenseMatrix<bool> *elementMask=NULL) const; /** compute RHS for given quadrature */ void compute_rhs_vector(const RHS_MASK &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<std::set<int> > &pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, FIELDS &rhs) const; /** compute pointwise source for given quadrature */ void compute_source(const Array2D<bool> &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<std::set<int> > &pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, FIELD_MATS &sources) const; /** compute flux in domain i.e. N^T B_integrand */ void compute_flux(const RHS_MASK &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, GRAD_FIELD_MATS &flux, const DenseMatrix<bool> *elementMask=NULL) const; /** compute the flux on the MD/FE boundary */ void compute_boundary_flux(const RHS_MASK &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, const std::set<PAIR> &faceSet, FIELDS &rhs) const; /** compute the flux on using an L2 interpolation of the flux */ void compute_boundary_flux(const RHS_MASK &rhsMask, const FIELDS &fields, const PhysicsModel *physicsModel, const Array<int> &elementMaterials, const Array<std::set<int> > &pointMaterialGroups, const DIAG_MAT &weights, const SPAR_MAT &N, const SPAR_MAT_VEC &dN, const DIAG_MAT &flux_mask, FIELDS &rhs, const DenseMatrix<bool> *elementMask=NULL, const std::set<int> *nodeSet=NULL) const; /** compute prescribed flux given an array of functions of x & t */ void add_fluxes(const Array<bool> &fieldMask, const double time, const SURFACE_SOURCE &sourceFunctions, FIELDS &nodalSources) const; void compute_fluxes(const Array<bool> &fieldMask, const double time, const SURFACE_SOURCE &sourceFunctions, FIELDS &nodalSources) const { SURFACE_SOURCE::const_iterator src_iter; for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) { _fieldName_ = src_iter->first; if (!fieldMask((int)_fieldName_)) continue; if (nodalSources[_fieldName_].nRows()==0) { nodalSources[_fieldName_].reset(nNodesUnique_,1); } } add_fluxes(fieldMask, time, sourceFunctions, nodalSources); } - /** compute prescribed flux given an array of functions of u, x & t */ + /** compute robin flux given an array of functions of u, x & t */ void add_robin_fluxes(const Array2D<bool> &rhsMask, const FIELDS &fields, const double time, const ROBIN_SURFACE_SOURCE &sourceFunctions, FIELDS &nodalSources) const; void add_robin_tangent(const Array2D<bool> &rhsMask, const FIELDS &fields, const double time, const ROBIN_SURFACE_SOURCE &sourceFunctions, SPAR_MAT &tangent) const; + /** compute open flux given a face set */ + + void add_open_fluxes(const Array2D<bool> &rhsMask, + const FIELDS &fields, + const OPEN_SURFACE &openFaces, + FIELDS &nodalSources, + const FieldName velocity = ELECTRON_VELOCITY) const; + + void add_open_tangent(const Array2D<bool> &rhsMask, + const FIELDS &fields, + const OPEN_SURFACE &openFaces, + SPAR_MAT &tangent, + const FieldName velocity = ELECTRON_VELOCITY) const; + /** compute nodal vector of volume based sources */ void add_sources(const Array<bool> &fieldMask, const double time, const VOLUME_SOURCE &sourceFunctions, FIELDS &nodalSources) const; + void add_sources(const Array<bool> &fieldMask, + const double time, + const FIELDS &sources, + FIELDS &nodalSources) const; /** compute surface flux of a nodal field */ void field_surface_flux(const DENS_MAT &field, const std::set<PAIR> &faceSet, DENS_MAT &values, const bool contour=false, const int axis=2) const; /** integrate a nodal field over an element set */ DENS_VEC integrate(const DENS_MAT &field, const ESET & eset) const; /** integrate a nodal field over an face set */ DENS_VEC integrate(const DENS_MAT &field, const FSET & fset) const { throw ATC_Error(FILELINE,"unimplemented function"); } /*@}*/ //---------------------------------------------------------------- /** \name shape functions */ //---------------------------------------------------------------- /*@{*/ /** evaluate shape function at a list of points in R^3 */ void evaluate_shape_functions(const MATRIX &coords, SPAR_MAT &N) const; /** evaluate shape function & derivatives at a list of points in R^3 */ void evaluate_shape_functions(const MATRIX &coords, SPAR_MAT &N, SPAR_MAT_VEC &dN) const; /** evaluate shape function at a list of points in R^3 */ void evaluate_shape_functions(const MATRIX &coords, const INT_ARRAY &pointToEltMap, SPAR_MAT &N) const; /** evaluate shape function & derivatives at a list of points in R^3 */ void evaluate_shape_functions(const MATRIX &coords, const INT_ARRAY &pointToEltMap, SPAR_MAT &N, SPAR_MAT_VEC &dN) const; /** evaluate shape derivatives at a list of points in R^3 */ void evaluate_shape_function_derivatives(const MATRIX &coords, const INT_ARRAY &pointToEltMap, SPAR_MAT_VEC &dN) const; void shape_functions(const VECTOR &x, DENS_VEC &shp, Array<int> &node_list) const { feMesh_->shape_functions(x,shp,node_list); } void shape_functions(const VECTOR & x, DENS_VEC& shp, DENS_MAT& dshp, Array<int> &node_list) const { feMesh_->shape_functions(x,shp,dshp,node_list); } void shape_functions(const VECTOR &x, const int eltId, DENS_VEC& shp, Array<int> &node_list) const { feMesh_->shape_functions(x,eltId,shp,node_list); } void shape_functions(const VECTOR &x, DENS_VEC& shp, Array<int> &node_list, int &eltId) const { feMesh_->shape_functions(x,shp,node_list,eltId); } void shape_functions(const VECTOR &x, const int eltId, DENS_VEC &shp, DENS_MAT &dshp, Array<int> &node_list) const { feMesh_->shape_functions(x,eltId,shp,dshp,node_list); } /*@}*/ //---------------------------------------------------------------- /** \name kernel functions */ //---------------------------------------------------------------- /** evaluate kernel function */ void evaluate_kernel_functions(const MATRIX &pt_coords, SPAR_MAT &N) const; /** kernel matrix bandwidth */ int kernel_matrix_bandwidth(const MATRIX &pt_coords) const; //---------------------------------------------------------------- /** \name nodeset */ //---------------------------------------------------------------- /** pass through */ void create_nodeset(const std::string &name, const std::set<int> &nodeset) { feMesh_->create_nodeset(name,nodeset); } //---------------------------------------------------------------- /** \name accessors */ //---------------------------------------------------------------- /*@{*/ /** even though these are pass-throughs there is a necessary * translation */ /** return number of unique nodes */ int num_nodes() const { return feMesh_->num_nodes_unique(); } /** return number of total nodes */ int nNodesTotal() const { return feMesh_->num_nodes(); } /** return number of elements */ int num_elements() const { return feMesh_->num_elements(); } int my_num_elements() const { return feMesh_->my_num_elements(); } /** return number of nodes per element */ int num_nodes_per_element() const { return feMesh_->num_nodes_per_element(); } /** return element connectivity */ void element_connectivity(const int eltID, Array<int> & nodes) const { feMesh_->element_connectivity_unique(eltID, nodes); } /** return face connectivity */ void face_connectivity(const PAIR &faceID, Array<int> &nodes) const { feMesh_->face_connectivity_unique(faceID, nodes); } /** in lieu of pass-throughs const accessors ... */ /** return const ptr to mesh */ const FE_Mesh* fe_mesh() const { return feMesh_; } /** return number of spatial dimensions */ int nsd() const { return feMesh_->num_spatial_dimensions(); } /** return if the FE mesh has been created */ int has_mesh() const { return feMesh_!=NULL; } /** get nodal coordinates for a given element */ void element_coordinates(const int eltIdx, DENS_MAT &coords) { feMesh_->element_coordinates(eltIdx,coords); } /** get nodal coordinates for a given element */ void element_field(const int eltIdx, const DENS_MAT field, DENS_MAT &local_field) { feMesh_->element_field(eltIdx, field, local_field); } /** access list of elements to be deleted */ const std::set<int> &null_elements(void) const { return nullElements_; } /** access to the amended nodal coordinate values */ const DENS_MAT &nodal_coordinates(void) const { return (*feMesh_->coordinates()); } /** map global node numbering to unique node numbering for * amended mesh */ int map_global_to_unique(const int global_id) const { return (*feMesh_->node_map())(global_id); } int number_of_global_nodes(void) const { return nNodes_; } /*@}*/ /** set kernel */ void set_kernel(KernelFunction* ptr); KernelFunction *kernel(int i=0) { return kernelFunction_; } private: //---------------------------------------------------------------- /** mesh setup commands (called from modify) */ //---------------------------------------------------------------- /*@{*/ MPI_Comm communicator_; /** finite element mesh */ FE_Mesh *feMesh_; /** auxillary kernel function */ KernelFunction *kernelFunction_; /** initialized flag */ bool initialized_; + void parse_partitions(int & argIdx, int narg, char ** arg, + int idof, Array<double> & dx ) const; + void print_partitions(double xmin, double xmax, Array<double> &dx) const; + /** create a uniform, structured mesh */ void create_mesh(Array<double> &dx, Array<double> &dy, Array<double> &dz, const char *regionName, Array<bool> periodic); void create_mesh(int nx, int ny, int nz, const char *regionName, Array<bool> periodic); /** read an unstructured mesh from a file */ void read_mesh(std::string meshFile, Array<bool> & periodicity); /*@}*/ /** data that can be used for a subset of original mesh */ std::set<int> nullElements_; /** faces upon which nodes are duplicated */ std::set<PAIR> cutFaces_; std::set<int> cutEdge_; /** workspace */ int nNodesPerElement_; int nSD_; int nElems_; int nNodes_; /** number of global nodes */ int nNodesUnique_; /** number of unique nodes */ mutable int nIPsPerElement_; mutable int nIPsPerFace_; mutable FeIntQuadrature quadrature_; mutable FIELDS::const_iterator _fieldItr_; mutable FieldName _fieldName_; /** sized arrays */ mutable DIAG_MAT _weights_; mutable DENS_MAT _N_, _Nw_; mutable DENS_MAT_VEC _dN_, _dNw_; mutable DIAG_MAT _fweights_; mutable DENS_MAT _fN_; mutable DENS_MAT_VEC _fdN_, _nN_; /** unsized arrays */ mutable DENS_MAT _Nmat_; mutable FIELD_MATS _fieldsAtIPs_; mutable GRAD_FIELD_MATS _gradFieldsAtIPs_; mutable DENS_MAT _Nfluxes_; mutable AliasArray<int> _conn_; mutable DENS_MAT_VEC _Bfluxes_; /** output object */ OutputManager outputManager_; }; }; // end namespace ATC #endif diff --git a/lib/atc/FE_Mesh.cpp b/lib/atc/FE_Mesh.cpp index 6e7036a0c..6225a1b19 100644 --- a/lib/atc/FE_Mesh.cpp +++ b/lib/atc/FE_Mesh.cpp @@ -1,3038 +1,3061 @@ // ATC header files #include "FE_Element.h" #include "FE_Mesh.h" #include "LammpsInterface.h" #include "ATC_Error.h" #include "OutputManager.h" #include "Utility.h" #include <sstream> #include <assert.h> #include <algorithm> #include <cmath> #include <functional> // Other headers #include <iostream> using namespace std; using ATC_Utility::dbl_geq; using ATC_Utility::parse_min; using ATC_Utility::parse_max; using ATC_Utility::parse_minmax; using ATC_Utility::split_values; using ATC_Utility::plane_coords; using ATC_Utility::norm3; using ATC_Utility::to_string; +using ATC_Utility::tolerance; namespace ATC { // constants const static double tangentTolerance = 0.01; const static double tol = 1.0e-10; // ============================================================= // class FE_Mesh // ============================================================= FE_Mesh::FE_Mesh() : decomposition_(false), lammpsPartition_(false), partitioned_(false), nNodes_(0), nNodesUnique_(0), feElement_(NULL), twoDimensional_(false), hasPlanarFaces_(false) { } // ------------------------------------------------------------- FE_Mesh::~FE_Mesh() { if (feElement_) delete feElement_; } // ------------------------------------------------------------- // modify // ------------------------------------------------------------- bool FE_Mesh::modify(int narg, char **arg) { bool match = false; if (strcmp(arg[0],"mesh")==0) { /*! \page man_mesh_create_faceset_box fix_modify AtC mesh create_faceset box \section syntax fix_modify AtC mesh create_faceset <id> box <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> <in|out> [units] - <id> = id to assign to the collection of FE faces - <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> = coordinates of the bounding box that is coincident with the desired FE faces - <in|out> = "in" gives inner faces to the box, "out" gives the outer faces to the box - units = option to specify real as opposed to lattice units \section examples <TT> fix_modify AtC mesh create_faceset obndy box -4.0 4.0 -12 12 -12 12 out </TT> \section description Command to assign an id to a set of FE faces. \section restrictions Only viable for rectangular grids. \section related \section default The default options are units = lattice and the use of outer faces */ /*! \page man_mesh_create_faceset_plane fix_modify AtC mesh create_faceset plane \section syntax fix_modify AtC mesh create_faceset <id> plane <x|y|z> <val1> <x|y|z> <lval2> <uval2> [units] - <id> = id to assign to the collection of FE faces - <x|y|z> = coordinate directions that define plane on which faceset lies - <val1>,<lval2>,<uval2> = plane is specified as the x|y|z=val1 plane bounded by the segments x|y|z = [lval2,uval2] - units = option to specify real as opposed to lattice units \section examples <TT> fix_modify AtC mesh create_faceset xyplane plane y 0 x -4 0 </TT> \section description Command to assign an id to a set of FE faces. \section restrictions Only viable for rectangular grids. \section related \section default The default option is units = lattice. */ if (strcmp(arg[1],"create_faceset")==0) { int argIdx = 2; string tag = arg[argIdx++]; if (strcmp(arg[argIdx],"plane")==0) { argIdx++; - string n(arg[argIdx++]); - int idir = -1, isgn = 0; - int i2dir = -1, i3dir = -1; - double x2min = 0.0, x2max = 0.0, x3min = 0.0, x3max = 0.0; - string_to_index(n, idir, isgn); - double x = parse_minmax(arg[argIdx++]); - if (narg > argIdx ) { - if (string_to_index(arg[argIdx],i2dir)) { - argIdx++; - x2min = parse_min(arg[argIdx++]); - x2max = parse_max(arg[argIdx++]); - } - } - if (narg > argIdx ) { - if (string_to_index(arg[argIdx],i3dir)) { - argIdx++; - x3min = parse_min(arg[argIdx++]); - x3max = parse_max(arg[argIdx++]); - } - } - if (i2dir >= 0 && - ((i2dir == idir) || (i2dir == idir) || (i2dir == idir) ) ) - { - throw ATC_Error( "inconsistent directions in create_faceset plane"); - } - if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) - {} - else - { - if (idir == 0) { x *= xscale_; } - else if (idir == 1) { x *= yscale_; } - else if (idir == 2) { x *= zscale_; } - if (i2dir >=0) { - if (i2dir == 0) { x2min *= xscale_; x2max *= xscale_; } - else if (i2dir == 1) { x2min *= yscale_; x2max *= yscale_; } - else if (i2dir == 2) { x2min *= zscale_; x2max *= zscale_; } - } - if (i3dir >=0) { - if (i3dir == 0) { x3min *= xscale_; x2max *= xscale_; } - else if (i3dir == 1) { x3min *= yscale_; x3max *= yscale_; } - else if (i3dir == 2) { x3min *= zscale_; x3max *= zscale_; } - } - } - if (i2dir >=0) { - create_faceset(tag, x, idir, isgn, i2dir, x2min, x2max, - i3dir, x3min, x3max); + int ndir, idir[3], isgn; + double xlimits[3][2]; + parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); + if (xlimits[idir[1]][0] == xlimits[idir[1]][1]) + split_values(xlimits[idir[1]][0],xlimits[idir[1]][1]); + if (xlimits[idir[2]][0] == xlimits[idir[2]][1]) + split_values(xlimits[idir[2]][0],xlimits[idir[2]][1]); + parse_units(argIdx,narg,arg, + xlimits[0][0],xlimits[0][1], + xlimits[1][0],xlimits[1][1], + xlimits[2][0],xlimits[2][1]); + if (ndir > 1) { + create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn, + idir[1], xlimits[idir[1]][0], xlimits[idir[1]][1], + idir[2], xlimits[idir[2]][0], xlimits[idir[2]][1]); } else { - create_faceset(tag, x, idir, isgn); + create_faceset(tag, xlimits[idir[0]][0], idir[0], isgn); } match = true; } // bounding_box else { if (strcmp(arg[argIdx],"box")==0) argIdx++; double xmin = parse_min(arg[argIdx++]); double xmax = parse_max(arg[argIdx++]); double ymin = parse_min(arg[argIdx++]); double ymax = parse_max(arg[argIdx++]); double zmin = parse_min(arg[argIdx++]); double zmax = parse_max(arg[argIdx++]); bool outward = true; if (narg > argIdx && (strcmp(arg[argIdx++],"in") == 0)) outward = false; - - if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) {} - else - { // scale from lattice units to physical units - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - } - + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_faceset(tag, xmin, xmax, ymin, ymax, zmin, zmax, outward); match = true; } } /*! \page man_mesh_create_nodeset fix_modify AtC mesh create_nodeset \section syntax fix_modify AtC mesh create_nodeset <id> <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> - <id> = id to assign to the collection of FE nodes - <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> = coordinates of the bounding box that contains only the desired nodes \section examples <TT> fix_modify AtC mesh create_nodeset lbc -12.1 -11.9 -12 12 -12 12 </TT> \section description Command to assign an id to a set of FE nodes to be used subsequently in defining boundary conditions. \section restrictions \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_nodeset")==0) { - string tag = arg[2]; - double xmin = parse_min(arg[3]); - double xmax = parse_max(arg[4]); - double ymin = parse_min(arg[5]); - double ymax = parse_max(arg[6]); - double zmin = parse_min(arg[7]); - double zmax = parse_max(arg[8]); + int argIdx = 2; + string tag = arg[argIdx++]; + double xmin, xmax, ymin, ymax, zmin, zmax; + if (strcmp(arg[argIdx],"plane")==0) { + argIdx++; + int ndir, idir[3], isgn; + double xlimits[3][2]; + parse_plane(argIdx, narg, arg, ndir, idir, isgn, xlimits); + xmin = xlimits[0][0]; + xmax = xlimits[0][1]; + ymin = xlimits[1][0]; + ymax = xlimits[1][1]; + zmin = xlimits[2][0]; + zmax = xlimits[2][1]; + } + else { + xmin = parse_min(arg[argIdx++]); + xmax = parse_max(arg[argIdx++]); + ymin = parse_min(arg[argIdx++]); + ymax = parse_max(arg[argIdx++]); + zmin = parse_min(arg[argIdx++]); + zmax = parse_max(arg[argIdx++]); + } + if (xmin == xmax) split_values(xmin,xmax); + if (ymin == ymax) split_values(ymin,ymax); + if (zmin == zmax) split_values(zmin,zmax); + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); create_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_add_to_nodeset fix_modify AtC mesh add_to_nodeset \section syntax fix_modify AtC mesh add_to_nodeset <id> <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> - <id> = id of FE nodeset to be added to - <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> = coordinates of the bounding box that contains the desired nodes to be added \section examples <TT> fix_modify AtC mesh add_to_nodeset lbc -11.9 -11 -12 12 -12 12 </TT> \section description Command to add nodes to an already existing FE nodeset. \section restrictions \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"add_to_nodeset")==0) { string tag = arg[2]; double xmin = parse_min(arg[3]); double xmax = parse_max(arg[4]); double ymin = parse_min(arg[5]); double ymax = parse_max(arg[6]); double zmin = parse_min(arg[7]); double zmax = parse_max(arg[8]); add_to_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_create_elementset fix_modify AtC mesh create_elementset \section syntax fix_modify AtC create_elementset <id> <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> - <id> = id to assign to the collection of FE element - <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> = coordinates of the bounding box that contains only the desired elements \section examples <TT> fix_modify AtC mesh create_elementset middle -4.1 4.1 -100 100 -100 1100 </TT> \section description Command to assign an id to a set of FE elements to be used subsequently in defining material and mesh-based operations. \section restrictions Only viable for rectangular grids. \section related \section default Coordinates are assumed to be in lattice units. */ else if (strcmp(arg[1],"create_elementset")==0) { - string tag = arg[2]; + int argIdx = 2; + string tag = arg[argIdx++]; double xmin = 0; double xmax = 0; double ymin = 0; double ymax = 0; double zmin = 0; double zmax = 0; if (narg > 4) { - xmin = parse_min(arg[3]); - xmax = parse_max(arg[4]); - ymin = parse_min(arg[5]); - ymax = parse_max(arg[6]); - zmin = parse_min(arg[7]); - zmax = parse_max(arg[8]); + xmin = parse_min(arg[argIdx++]); + xmax = parse_max(arg[argIdx++]); + ymin = parse_min(arg[argIdx++]); + ymax = parse_max(arg[argIdx++]); + zmin = parse_min(arg[argIdx++]); + zmax = parse_max(arg[argIdx++]); + if (xmin == xmax) split_values(xmin,xmax); + if (ymin == ymax) split_values(ymin,ymax); + if (zmin == zmax) split_values(zmin,zmax); + parse_units(argIdx,narg,arg, xmin,xmax,ymin,ymax,zmin,zmax); } else { - string regionName = arg[3]; + string regionName = arg[argIdx++]; LammpsInterface::instance()-> region_bounds(regionName.c_str(),xmin,xmax,ymin,ymax,zmin,zmax); } create_elementset(tag, xmin, xmax, ymin, ymax, zmin, zmax); match = true; } /*! \page man_mesh_nodeset_to_elementset fix_modify AtC mesh nodeset_to_elementset \section syntax fix_modify AtC nodeset_to_elementset <nodeset_id> <elementset_id> <max/min> - <nodeset_id> = id of desired nodeset from which to create elementset - <elementset_id> = id to assign to the collection of FE element - <max/min> = flag to choose either the maximal or minimal elementset \section examples <TT> fix_modify AtC mesh nodeset_to_elementset myNodeset myElementset min </TT> \section description Command to create an elementset from an existing nodeset. Either the minimal element set of elements with all nodes in the set, or maximal element set with all elements with at least one node in the set, can be created \section restrictions None. \section related \section default Unless specified, the maximal element set is created */ else if (strcmp(arg[1],"nodeset_to_elementset")==0) { string nodesetId = arg[2]; string elementsetId = arg[3]; set<int> elementSet; if (narg < 5) { this->nodeset_to_maximal_elementset(nodesetId,elementSet); } else { if (strcmp(arg[4],"max")==0) { this->nodeset_to_maximal_elementset(nodesetId,elementSet); } else if (strcmp(arg[4],"min")==0) { this->nodeset_to_minimal_elementset(nodesetId,elementSet); } else { return false; } } elementSetMap_[elementsetId] = elementSet; match = true; } /*! \page man_mesh_output fix_modify AtC mesh output \section syntax fix_modify AtC mesh output <file_prefix> \section examples <TT> fix_modify AtC mesh output meshData </TT> \n \section description Command to output mesh and associated data: nodesets, facesets, and elementsets. This data is only output once upon initialization since currently the mesh is static. Creates (binary, "gold" format) Ensight output of mesh data. \section restrictions none \section related \section default none */ else if (strcmp(arg[1],"output")==0) { string outputPrefix = arg[2]; output(outputPrefix); match = true; } } return match; } // ------------------------------------------------------------- + void FE_Mesh::parse_units(int & argIdx, int narg, char ** arg, + double & xmin, double & xmax, double & ymin, double & ymax, + double & zmin, double & zmax) + { + if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) {} + else + { // scale from lattice units to physical units + xmin *= xscale_; xmax *= xscale_; + ymin *= yscale_; ymax *= yscale_; + zmin *= zscale_; zmax *= zscale_; + } + } + // ------------------------------------------------------------- + // parse plane + // ------------------------------------------------------------- + void FE_Mesh::parse_plane(int & argIdx, int narg, char ** arg, + int & ndir, int * idir, int & isgn, double xlimits[][2]) + { + ndir = 0; + xlimits[0][0] = parse_min("-INF"); + xlimits[0][1] = parse_max("INF"); + xlimits[1][0] = parse_min("-INF"); + xlimits[1][1] = parse_max("INF"); + xlimits[2][0] = parse_min("-INF"); + xlimits[2][1] = parse_max("INF"); + string n(arg[argIdx++]); + int i1dir = -1, i2dir = -1, i3dir = -1; + string_to_index(n, i1dir, isgn); + idir[ndir++] = i1dir; + idir[ndir] = (i1dir+1) % 3; + idir[ndir+1] = (i1dir+2) % 3; + xlimits[i1dir][0] = parse_minmax(arg[argIdx++]); + xlimits[i1dir][1] = xlimits[i1dir][0]; + if (narg > argIdx ) { + if (string_to_index(arg[argIdx],i2dir)) { + argIdx++; + xlimits[i2dir][0] = parse_min(arg[argIdx++]); + xlimits[i2dir][1] = parse_max(arg[argIdx++]); + idir[ndir++] = i2dir; + } + if (narg > argIdx ) { + if (string_to_index(arg[argIdx],i3dir)) { + argIdx++; + xlimits[i3dir][0] = parse_min(arg[argIdx++]); + xlimits[i3dir][1] = parse_max(arg[argIdx++]); + } + } + else { + i3dir = 0; + if ((i1dir == 0 && i2dir == 1) || (i1dir == 1 && i2dir == 0) ) { + i3dir = 2; + } + else if ((i1dir == 0 && i2dir == 2) || (i1dir == 2 && i2dir == 0) ) { + i3dir = 1; + } + } + idir[ndir++] = i3dir; + } + if ((idir[0]==idir[1]) || (idir[0]==idir[2]) || (idir[1]==idir[2]) ) { + throw ATC_Error( "inconsistent directions in plane:"+to_string(idir[0]+1)+" "+to_string(idir[1]+1)+" "+to_string(idir[2]+1)); + } + } + // ------------------------------------------------------------- // initialize // ------------------------------------------------------------- void FE_Mesh::initialize(void) { + bool aligned = is_aligned(); if (!aligned) { feElement_->set_projection_guess(CENTROID_LINEARIZED); ATC::LammpsInterface::instance()->print_msg_once("WARNING: mesh is not aligned with the coordinate directions atom-to-element mapping will be expensive"); // if HEX8 -> orient(); } + bool twoD = is_two_dimensional(); + if (twoD) { + feElement_->set_projection_guess(TWOD_ANALYTIC); + if (feElement_->order()< 3) hasPlanarFaces_ = true; + ATC::LammpsInterface::instance()->print_msg_once(" mesh is two dimensional"); + } } //----------------------------------------------------------------- //----------------------------------------------------------------- void FE_Mesh::write_mesh(string meshFile) { ofstream out; out.open(meshFile.c_str()); DENS_MAT & x = *(coordinates()); Array2D<int> & conn = *(connectivity()); int nNodes = x.nCols(); // transpose int ndm = x.nRows(); int nElems = conn.nCols(); int nodesPerElem = conn.nRows(); // transpose out << "Coordinates " << nNodes << "\n"; for (int n = 0; n < nNodes; n++) { for (int i = 0; i < ndm; i++) { out << " " << std::setprecision(16) << x(i,n); } out << "\n"; } out << "\n"; string type = element_type(); out << "Elements " << nElems << " " << type << "\n"; for (int n = 0; n < nElems; n++) { for (int i = 0; i < nodesPerElem; i++) { out << 1+conn(i,n) << " "; } out << "\n"; } out << "\n"; if (nodeSetMap_.size()) { out << "Nodesets " << nodeSetMap_.size() <<"\n"; NODE_SET_MAP::const_iterator niter; map<string,DENS_MAT> nodesets; for (niter = nodeSetMap_.begin(); niter != nodeSetMap_.end(); niter++) { string name = niter->first; const set<int> & nset = niter->second; out << name << " " << nset.size() << "\n"; set<int>::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { out << *iter << " " ; } out << "\n"; } } } // ------------------------------------------------------------- // test whether almost structured // ------------------------------------------------------------- bool FE_Mesh::is_aligned(void) const { vector<bool> foundBestMatch(nSD_,false); vector<DENS_VEC> tangents(nSD_); DENS_VEC xi0(nSD_); xi0 = 0; DENS_MAT eltCoords; for (int ielem = 0; ielem < nElts_; ielem++) { element_coordinates(ielem,eltCoords); feElement_->tangents(eltCoords,xi0,tangents,true); for (unsigned i = 0; i < tangents.size(); i++) { // find maximum value for which global axis its closest to int maxIndex = 0; double maxValue = abs(tangents[i](0)); for (int j = 1; j < nSD_; j++) { if (abs(tangents[i](j)) > maxValue) { maxValue = abs(tangents[i](j)); maxIndex = j; } } // make sure no other tangent is associated with this direction if (foundBestMatch[maxIndex]) { return false; } else { foundBestMatch[maxIndex] = true; } // compute deviation from a perfectly aligned vector double error = 0.; for (int j = 1; j < nSD_; j++) { if (j != maxIndex) { error += abs(tangents[i](j)); } } error /= maxValue; if (error > tangentTolerance) { return false; } } } return true; } // ------------------------------------------------------------- // element_type // ------------------------------------------------------------- string FE_Mesh::element_type(void) const { int npe = feElement_->num_elt_nodes(); if (npe == 4) { return "TET4"; } else if (npe == 8) { return "HEX8"; } else if (npe == 20) { return "HEX20"; } else if (npe == 27) { return "HEX27"; } return "UNKNOWN"; } // ------------------------------------------------------------- // element_coordinates // ------------------------------------------------------------- void FE_Mesh::element_coordinates(const int eltID, DENS_MAT & xCoords) const { const int nne = num_nodes_per_element(); xCoords.reset(nSD_, nne, false); for (int inode=0; inode<nne; inode++) { const int id = element_connectivity_global(eltID, inode); for (int isd=0; isd<nSD_; isd++) { xCoords(isd,inode) = nodalCoords_(isd,id); } } } // ------------------------------------------------------------- // position // ------------------------------------------------------------- void FE_Mesh::position(const int eltID, const VECTOR & xi, DENS_VEC & x) const { const int nne = num_nodes_per_element(); DENS_VEC N; feElement_->shape_function(xi,N); x.reset(nSD_); for (int inode=0; inode<nne; inode++) { const int id = element_connectivity_global(eltID, inode); for (int isd=0; isd<nSD_; isd++) { x(isd) += nodalCoords_(isd,id)*N(inode); } } } // ------------------------------------------------------------- // element size in each direction // ------------------------------------------------------------- void FE_Mesh::bounding_box(const int ielem, DENS_VEC & xmin, DENS_VEC & xmax) { xmin.reset(nSD_); xmax.reset(nSD_); int nne = num_nodes_per_element(); for (int isd=0; isd<nSD_; isd++) { int id = element_connectivity_global(ielem, 0); double x = nodalCoords_(isd,id); xmin(isd) = x; xmax(isd) = x; for (int inode=1; inode<nne; inode++) { id = element_connectivity_global(ielem, inode); x = nodalCoords_(isd,id); xmin(isd) = min(xmin(isd), x ); xmax(isd) = max(xmax(isd), x ); } } } // ------------------------------------------------------------- // element size in each direction // ------------------------------------------------------------- void FE_Mesh::element_size(const int ielem, double & hx, double & hy, double & hz) { DENS_VEC xmin(nSD_), xmax(nSD_); bounding_box(ielem,xmin,xmax); hx = xmax(0)-xmin(0); hy = xmax(1)-xmin(1); hz = xmax(2)-xmin(2); } // ------------------------------------------------------------- // face_coordinates // ------------------------------------------------------------- void FE_Mesh::face_coordinates(const PAIR face, DENS_MAT & xCoords) const { const int eltID=face.first, faceID=face.second; const int nnf = num_nodes_per_face(); const Array2D <int> & local_conn = local_face_connectivity(); xCoords.reset(nSD_, nnf, false); for (int inode=0; inode < nnf; inode++) { int id = element_connectivity_global(eltID, local_conn(faceID,inode)); for (int isd=0; isd<nSD_; isd++) xCoords(isd,inode) = nodalCoords_(isd,id); } } // ------------------------------------------------------------- // nodal_coordinates // ------------------------------------------------------------- DENS_VEC FE_Mesh::nodal_coordinates(const int nodeID) const { DENS_VEC xCoords(nSD_, false); const int id = uniqueToGlobalMap_(nodeID); for (int isd=0; isd<nSD_; isd++) xCoords(isd) = nodalCoords_(isd, id); return xCoords; } DENS_VEC FE_Mesh::global_coordinates(const int nodeID) const { DENS_VEC xCoords(nSD_, false); for (int isd=0; isd<nSD_; isd++) xCoords(isd) = nodalCoords_(isd, nodeID); return xCoords; } // ------------------------------------------------------------- // query_nodeset // ------------------------------------------------------------- bool FE_Mesh::query_nodeset(const string & name) const { if (name == "all") return true; if (nodeSetMap_.find(name) == nodeSetMap_.end()) return false; return true; } // ------------------------------------------------------------- // get_nodeset // ------------------------------------------------------------- const set<int> & FE_Mesh::nodeset(const string & name) const { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (name == "all") return nodeSetAll_; else if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); else return iter->second; } // ------------------------------------------------------------- // get_elementset // ------------------------------------------------------------- const set<int> & FE_Mesh::elementset(const string & name) const { NODE_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (name == "all") return elementSetAll_; else if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); else return iter->second; } // ------------------------------------------------------------- // nodeset_to_minimal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_minimal_elementset (const string & name, set<int> & elemSet) const { if (name == "all") { for (int ielem = 0; ielem < nElts_; ielem++) { elemSet.insert(ielem); } } else { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); nodeset_to_minimal_elementset(iter->second,elemSet); if (elemSet.size()==0) { throw ATC_Error("No elements found in minimal condensation of nodeset " + name); } } } // ------------------------------------------------------------- // nodeset_to_minimal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_minimal_elementset (const set<int> & nodeSet, set<int> & elemSet) const { int npe = num_nodes_per_element(); for (int ielem=0; ielem < nElts_; ielem++) { int inode = 0; bool in = true; while (in && inode < npe) { int node = element_connectivity_unique(ielem, inode); set<int>::const_iterator iter = nodeSet.find(node); if (iter == nodeSet.end()) { in=false; } inode++; } if (in) elemSet.insert(ielem); } } // ------------------------------------------------------------- // nodeset_to_maximal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_maximal_elementset(const string &name, set<int> &elemSet) const { if (name == "all") { for (int ielem = 0; ielem < nElts_; ielem++) { elemSet.insert(ielem); } } else { NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) throw ATC_Error( "No nodeset with name " + name + " found."); nodeset_to_maximal_elementset(iter->second,elemSet); if (elemSet.size()==0) { throw ATC_Error("No elements found in maximal condensation of nodeset " + name); } } } // ------------------------------------------------------------- // nodeset_to_maximal_elementset // ------------------------------------------------------------- void FE_Mesh::nodeset_to_maximal_elementset(const set<int> &nodeSet, set<int> &elemSet) const { int npe = num_nodes_per_element(); for (int ielem = 0; ielem < nElts_; ielem++) { int inode = 0; bool in = false; while (!in && inode < npe) { int node = element_connectivity_unique(ielem, inode); set<int>::const_iterator iter = nodeSet.find(node); if (iter != nodeSet.end()) { in = true; } inode++; } if (in) elemSet.insert(ielem); } } // ------------------------------------------------------------- // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset - (const set<int> & elemSet, set<int> & nodeSet) const + (const set<int> & elemSet, set<int> nodeSet) const { int npe = num_nodes_per_element(); set<int>::const_iterator itr; for (itr = elemSet.begin(); itr != elemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_global(ielem, inode); nodeSet.insert(node); } } } // ------------------------------------------------------------- // elementset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_nodeset - (const string & name, set<int> & nodeSet) const + (const string & name, set<int> nodeSet) const { if (name == "all") for (int ielem = 0; ielem < nElts_; ielem++) nodeSet.insert(ielem); else { ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); int npe = num_nodes_per_element(); const set<int> &elemSet = iter->second; set<int>::const_iterator itr; for (itr = elemSet.begin(); itr != elemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_unique(ielem, inode); nodeSet.insert(node); inode++; } } } } + set<int> FE_Mesh::elementset_to_nodeset + (const string & name) const + { + set<int> nset; + elementset_to_nodeset(name,nset); + return nset; + } // ------------------------------------------------------------- // elementset_to_minimal_nodeset // ------------------------------------------------------------- void FE_Mesh::elementset_to_minimal_nodeset (const string & name, set<int> & nodeSet) const { // return: set - complement_of_set if (name == "all") { return;} else { elementset_to_nodeset(name,nodeSet); set<int> compElemSet; elementset_complement(name,compElemSet); int npe = num_nodes_per_element(); set<int>::const_iterator itr; for (itr = compElemSet.begin(); itr != compElemSet.end(); itr++) { int ielem = *itr; for (int inode=0; inode < npe; inode++) { int node = element_connectivity_unique(ielem, inode); nodeSet.erase(node); inode++; } } } } // ------------------------------------------------------------- // elementset_complement // ------------------------------------------------------------- void FE_Mesh::elementset_complement (const string & name, set<int> & cElemSet) const { // return: set - complement_of_set if (name == "all") { return;} else { ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); if (iter == elementSetMap_.end()) throw ATC_Error( "No elementset with name " + name + " found."); const set<int> &elemSet = iter->second; for (int ielem = 0; ielem < nElts_; ielem++) { if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); } } } // ------------------------------------------------------------- // elementset_complement // ------------------------------------------------------------- void FE_Mesh::elementset_complement (const set<int> & elemSet, set<int> & cElemSet) const { for (int ielem = 0; ielem < nElts_; ielem++) { if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); } } // ------------------------------------------------------------- // faceset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::faceset_to_nodeset(const string &name, set<int> &nodeSet) const { if (name == "all") { for (int inode = 0; inode < nNodesUnique_; inode++) nodeSet.insert(inode); } else { FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); if (faceset == faceSetMap_.end()) throw ATC_Error( "No faceset with name " + name + " found."); const set<PAIR> & faceSet = faceset->second; set<PAIR>::const_iterator iter; Array <int> conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity_unique(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } } void FE_Mesh::faceset_to_nodeset(const set<PAIR> &faceSet, set<int> &nodeSet) const { set<PAIR>::const_iterator iter; Array <int> conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity_unique(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } // ------------------------------------------------------------- // faceset_to_nodeset // ------------------------------------------------------------- void FE_Mesh::faceset_to_nodeset_global(const string &name, set<int> &nodeSet) const { if (name == "all") { for (int inode = 0; inode < nNodes_; inode++) nodeSet.insert(inode); } else { FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); if (faceset == faceSetMap_.end()) throw ATC_Error( "No faceset with name " + name + " found."); const set<PAIR> & faceSet = faceset->second; set<PAIR>::const_iterator iter; Array <int> conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } } void FE_Mesh::faceset_to_nodeset_global(const set<PAIR> &faceSet, set<int> &nodeSet) const { set<PAIR>::const_iterator iter; Array <int> conn; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; face_connectivity(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); nodeSet.insert(inode); } } } // ------------------------------------------------------------- // get_faceset // ------------------------------------------------------------- const set<PAIR> &FE_Mesh::faceset(const string & name) const { FACE_SET_MAP::const_iterator iter = faceSetMap_.find(name); if (iter == faceSetMap_.end()) { throw ATC_Error( "No faceset with name " + name + " found."); } return iter->second; } // ------------------------------------------------------------- // create_nodeset // ------------------------------------------------------------- void FE_Mesh::create_nodeset(const string & name, const set<int> & nodeSet) { // Make sure we don't already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter != nodeSetMap_.end()) { string message("A nodeset with name " + name + " is already defined."); throw ATC_Error( message); } nodeSetMap_[name] = nodeSet; if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "created nodeset " << name << " with " << nodeSet.size() << " nodes"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } void FE_Mesh::create_nodeset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we don't already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter != nodeSetMap_.end()) { string message("A nodeset with name " + name + " is already defined."); throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set<int> nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("nodeset " + name + " has zero size."); throw ATC_Error( message); } nodeSetMap_[name] = nodeSet; if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "created nodeset " << name << " with " << nodeSet.size() << " nodes"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // add_to_nodeset // ------------------------------------------------------------- void FE_Mesh::add_to_nodeset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we already have a nodeset with this name NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); if (iter == nodeSetMap_.end()) { string message("A nodeset with name " +name + " is not already defined."); throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set<int> nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("nodeset " + name + " has zero size."); throw ATC_Error( message); } nodeSetMap_[name].insert(nodeSet.begin(),nodeSet.end()); if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << "added " << nodeSet.size() << " nodes to nodeset " << name ; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // create_faceset // ------------------------------------------------------------- void FE_Mesh::create_faceset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, bool outward) { // Make sure we don't already have a nodeset with this name FACE_SET_MAP::iterator iter = faceSetMap_.find(name); if (iter != faceSetMap_.end()) throw ATC_Error( "A faceset with name " + name + " is already defined."); - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - set<PAIR> faceSet; // Loop over face and add their unique id's to the set if they concide // with region const int nf = num_faces_per_element(); const int npf = num_nodes_per_face(); const Array2D<int> & face_conn = local_face_connectivity(); for (int ielem = 0; ielem < nElts_; ielem++) { for (int iface = 0; iface < nf; iface++) { bool in = true; bool on_xmin = true, on_xmax = true; bool on_ymin = true, on_ymax = true; bool on_zmin = true, on_zmax = true; bool x_neg = false, x_pos = false; bool y_neg = false, y_pos = false; bool z_neg = false, z_pos = false; double x,y,z; for (int inode = 0; inode < npf; inode++) { x = nodalCoords_(0,connectivity_(face_conn(iface,inode),ielem)); y = nodalCoords_(1,connectivity_(face_conn(iface,inode),ielem)); z = nodalCoords_(2,connectivity_(face_conn(iface,inode),ielem)); if ( x + tol < xmin) { in = false; break; } if ( x - tol > xmax) { in = false; break; } if ( y + tol < ymin) { in = false; break; } if ( y - tol > ymax) { in = false; break; } if ( z + tol < zmin) { in = false; break; } if ( z - tol > zmax) { in = false; break; } on_xmin = on_xmin && fabs(x-xmin) <= tol; on_xmax = on_xmax && fabs(x-xmax) <= tol; on_ymin = on_ymin && fabs(y-ymin) <= tol; on_ymax = on_ymax && fabs(y-ymax) <= tol; on_zmin = on_zmin && fabs(z-zmin) <= tol; on_zmax = on_zmax && fabs(z-zmax) <= tol; } if (in) { // note based on structured grid if (outward) { if (on_xmin && iface==0) { x_neg = true;} if (on_xmax && iface==1) { x_pos = true;} if (on_ymin && iface==2) { y_neg = true;} if (on_ymax && iface==3) { y_pos = true;} if (on_zmin && iface==4) { z_neg = true;} if (on_zmax && iface==5) { z_pos = true;} } else { if (on_xmin && iface==1) { x_pos = true;} if (on_xmax && iface==0) { x_neg = true;} if (on_ymin && iface==3) { y_pos = true;} if (on_ymax && iface==2) { y_neg = true;} if (on_zmin && iface==5) { z_pos = true;} if (on_zmax && iface==4) { z_neg = true;} } if ( (x_neg || x_pos) || (y_neg || y_pos) || (z_neg || z_pos) ) { PAIR face(ielem,iface); faceSet.insert(face); } } } } if (faceSet.empty()) throw ATC_Error( "faceset "+name+" is empty."); faceSetMap_[name] = faceSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created faceset " << name << " with " << faceSet.size() << " faces"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } void FE_Mesh::create_faceset(const string & name, double xRef, int nIdx, int nSgn, int nIdx2, double x2lo, double x2hi, int nIdx3, double x3lo, double x3hi) { - // note scaling done by caller and split_values is tricky with orientation + double xtol = tolerance(xRef); // Make sure we don't already have a faceset with this name FACE_SET_MAP::iterator iter = faceSetMap_.find(name); if (iter != faceSetMap_.end()) throw ATC_Error( "A faceset with name "+name+" is already defined."); bool finite2 = (nIdx2 >= 0); bool finite3 = (nIdx3 >= 0); set<PAIR> faceSet; // Loop over faces i& add unique id's to the set if concide w/ plane int nf = num_faces_per_element(); int npf = num_nodes_per_face(); const Array2D<int> & face_conn = local_face_connectivity(); for (int ielem = 0; ielem < nElts_; ielem++) { for (int iface = 0; iface < nf; iface++) { bool in = true; // all nodes must be on the plane for (int inode = 0; inode < npf; inode++) { int node = connectivity_(face_conn(iface,inode),ielem); double x = nodalCoords_(nIdx,node); - if ( fabs(x-xRef) > tol){ in = false; break;} + if ( fabs(x-xRef) > xtol){ in = false; break;} if (finite2) { double y = nodalCoords_(nIdx2,node); if ( y < x2lo || y > x2hi){ in = false; break;} } if (finite3) { double y = nodalCoords_(nIdx3,node); if ( y < x3lo || y > x3hi){ in = false; break;} } } // check correct orientation if (in) { if ( (nIdx == 0 && iface==0 && nSgn == -1) || (nIdx == 0 && iface==1 && nSgn == 1) || (nIdx == 1 && iface==2 && nSgn == -1) || (nIdx == 1 && iface==3 && nSgn == 1) || (nIdx == 2 && iface==4 && nSgn == -1) || (nIdx == 3 && iface==5 && nSgn == 1) ) { PAIR face(ielem,iface); faceSet.insert(face); } } } } if (faceSet.empty()) throw ATC_Error( "faceset "+name+" is empty."); faceSetMap_[name] = faceSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created faceset " << name << " with " << faceSet.size() << " faces"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // create_elementset // ------------------------------------------------------------- void FE_Mesh::create_elementset(const string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) { // Make sure we don't already have a elementset with this name ELEMENT_SET_MAP::iterator iter = elementSetMap_.find(name); if (iter != elementSetMap_.end()) { string message("An elementset with name "+name+" is already defined."); throw ATC_Error( message); } - if (xmin == xmax) split_values(xmin,xmax); - if (ymin == ymax) split_values(ymin,ymax); - if (zmin == zmax) split_values(zmin,zmax); - - xmin *= xscale_; - xmax *= xscale_; - ymin *= yscale_; - ymax *= yscale_; - zmin *= zscale_; - zmax *= zscale_; - set<int> nodeSet; // Loop over nodes and add their unique id's to the set if they're // in the correct range for (int inode = 0; inode < nNodes_; inode++) { double x = nodalCoords_(0,inode); double y = nodalCoords_(1,inode); double z = nodalCoords_(2,inode); if ( (xmin <= x) && (x <= xmax) && (ymin <= y) && (y <= ymax) && (zmin <= z) && (z <= zmax) ) { int uid = globalToUniqueMap_(inode); nodeSet.insert(uid); } } if (nodeSet.size() == 0) { string message("elementset " + name + " has zero size."); throw ATC_Error( message); } // create a minimal element set from all the nodes included in the region set<int> elemSet; int npe = num_nodes_per_element(); for (int ielem=0; ielem < nElts_; ielem++) { int inode = 0; bool in = true; while (in && inode < npe) { int node = connectivityUnique_(inode, ielem); set<int>::const_iterator iter = nodeSet.find(node); if (iter == nodeSet.end()) { in=false; } inode++; } if (in) elemSet.insert(ielem); } if (elemSet.size() == 0) { string message("element set " + name + " has zero size."); throw ATC_Error( message); } elementSetMap_[name] = elemSet; if (ATC::LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "created elementset " << name << " with " << elemSet.size() << " elements"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } // ------------------------------------------------------------- // get_numIPsPerElement() // ------------------------------------------------------------- int FE_Mesh::num_ips_per_element() const { return feElement_->num_ips(); } // ------------------------------------------------------------- // get_numNodesPerElement() // ------------------------------------------------------------- int FE_Mesh::num_nodes_per_element() const { return feElement_->num_elt_nodes(); } // ------------------------------------------------------------- // get_numFacesPerElement() // ------------------------------------------------------------- int FE_Mesh::num_faces_per_element() const { return feElement_->num_faces(); } // ------------------------------------------------------------- // get_num_ips_per_face() // ------------------------------------------------------------- int FE_Mesh::num_ips_per_face() const { return feElement_->num_face_ips(); } // ------------------------------------------------------------- // get_num_nodes_per_face() // ------------------------------------------------------------- int FE_Mesh::num_nodes_per_face() const { return feElement_->num_face_nodes(); } // ------------------------------------------------------------- // mappings from element id to associated nodes // ------------------------------------------------------------- void FE_Mesh::element_connectivity_global(const int eltID, Array<int> & nodes) const { const int npe = num_nodes_per_element(); nodes.reset(npe); // use connectivity arrays if (decomposition_ && partitioned_) { for (int inode = 0; inode < npe; inode++) { nodes(inode) = myConnectivity_(inode, map_elem_to_myElem(eltID)); } } else { for (int inode = 0; inode < npe; inode++) { nodes(inode) = connectivity_(inode, eltID); } } } // ------------------------------------------------------------- // // ------------------------------------------------------------- void FE_Mesh::element_connectivity_unique(const int eltID, Array<int> & nodes) const { const int npe = num_nodes_per_element(); nodes.reset(npe); // use connectivity arrays if (decomposition_ && partitioned_) { for (int inode = 0; inode < npe; inode++) { nodes(inode) = myConnectivityUnique_(inode, map_elem_to_myElem(eltID)); } } else { for (int inode = 0; inode < npe; inode++) { nodes(inode) = connectivityUnique_(inode, eltID); } } } // ------------------------------------------------------------- // // ------------------------------------------------------------- int FE_Mesh::element_connectivity_global(const int eltID, const int inode) const { if (decomposition_ && partitioned_) { return myConnectivity_(inode, map_elem_to_myElem(eltID)); } else { return connectivity_(inode, eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- int FE_Mesh::element_connectivity_unique(const int eltID, const int inode) const { if (decomposition_ && partitioned_) { return myConnectivityUnique_(inode, map_elem_to_myElem(eltID)); } else { return connectivityUnique_(inode, eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- AliasArray<int> FE_Mesh::element_connectivity_global(const int eltID) const { if (decomposition_ && partitioned_) { return myConnectivity_.column(map_elem_to_myElem(eltID)); } else { return connectivity_.column(eltID); } } // ------------------------------------------------------------- // // ------------------------------------------------------------- AliasArray<int> FE_Mesh::element_connectivity_unique(const int eltID) const { if (decomposition_ && partitioned_) { return myConnectivityUnique_.column(map_elem_to_myElem(eltID)); } else { return connectivityUnique_.column(eltID); } } // ------------------------------------------------------------- // local_face_connectivity() // ------------------------------------------------------------- const Array2D<int> &FE_Mesh::local_face_connectivity() const { return feElement_->local_face_conn(); } // ------------------------------------------------------------- // maps to/from partitioned element data // ------------------------------------------------------------- int FE_Mesh::map_elem_to_myElem(int elemID) const { return elemToMyElemMap_.find(elemID)->second; } int FE_Mesh::map_myElem_to_elem(int myElemID) const { return myElts_[myElemID]; } // ------------------------------------------------------------- // shape function evaluation // ------------------------------------------------------------- // set quadrature scheme pass-through void FE_Mesh::set_quadrature(FeIntQuadrature type) { feElement_->set_quadrature(type); } // shape function evaluation void FE_Mesh::shape_functions(const VECTOR &x, DENS_VEC &N, Array<int> &nodeList) const { // get element id from global coordinates int eltID = map_to_element(x); // call appropriate function below, with eltID shape_functions(x,eltID,N,nodeList); } void FE_Mesh::shape_functions(const DENS_VEC &x, DENS_VEC &N, DENS_MAT &dNdx, Array<int> &nodeList) const { // get element id from global coordinates int eltID = map_to_element(x); // call appropriate function below, with eltID shape_functions(x,eltID,N,dNdx,nodeList); } void FE_Mesh::shape_functions(const VECTOR &x, const int eltID, DENS_VEC &N, Array<int> &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID, eltCoords); // pass through call feElement_->shape_function(eltCoords,x,N); // determine nodes which correspond to shape function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_functions(const DENS_VEC &x, const int eltID, DENS_VEC &N, DENS_MAT &dNdx, Array<int> &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function(eltCoords,x,N,dNdx); // determine nodes which correspond to shp function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_function_derivatives(const DENS_VEC &x, const int eltID, DENS_MAT &dNdx, Array<int> &nodeList) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function_derivatives(eltCoords,x,dNdx); // determine nodes which correspond to shp function indices element_connectivity_unique(eltID,nodeList); } void FE_Mesh::shape_function(const int eltID, DENS_MAT &N, DIAG_MAT &weights) const { // unused data (but required to calc weights) vector<DENS_MAT> dN; // call below function with dN to avoid duplicated code shape_function(eltID,N,dN,weights); } void FE_Mesh::shape_function(int eltID, DENS_MAT &N, vector<DENS_MAT> &dN, DIAG_MAT &weights) const { // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->shape_function(eltCoords,N,dN,weights); } void FE_Mesh::face_shape_function(const PAIR &face, DENS_MAT &N, DENS_MAT &n, DIAG_MAT &weights) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->face_shape_function(eltCoords,faceID,N,n,weights); } void FE_Mesh::face_shape_function(const PAIR &face, DENS_MAT &N, vector<DENS_MAT> &dN, vector<DENS_MAT> &Nn, DIAG_MAT &weights) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call feElement_->face_shape_function(eltCoords,faceID,N,dN,Nn,weights); } double FE_Mesh::face_normal(const PAIR &face, int ip, DENS_VEC &normal) const { int eltID = face.first; int faceID = face.second; // Get element node coordinates from mesh DENS_MAT eltCoords; element_coordinates(eltID,eltCoords); // pass through call double J = feElement_->face_normal(eltCoords,faceID,ip,normal); return J; } //----------------------------------------------------------------------- void FE_Mesh::output(string prefix) const { set<int> otypes; otypes.insert(ENSIGHT); otypes.insert(FULL_GNUPLOT); OutputManager meshSets(prefix,otypes); meshSets.write_geometry(&nodalCoords_, & connectivity_); OUTPUT_LIST subsetData; int size = nNodesUnique_; // material // DENS_MAT material(nNodes_,1); // material = 1; // subsetData["material"] = &material; //string name = "global_to_unique_map"; // nodesets NODE_SET_MAP::const_iterator niter; map<string,DENS_MAT> nodesets; for (niter = nodeSetMap_.begin(); niter != nodeSetMap_.end(); niter++) { string name = niter->first; const set<int> & nset = niter->second; string nodeset = "nodeset_"+name; nodesets[nodeset].reset(size,1); set<int>::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { (nodesets[nodeset])(*iter,0) = 1; } subsetData[nodeset] = & nodesets[nodeset]; } // facesets FACE_SET_MAP::const_iterator fiter; map<string,DENS_MAT> facesets; for (fiter = faceSetMap_.begin(); fiter != faceSetMap_.end(); fiter++) { string name = fiter->first; string faceset = "faceset_"+name; facesets[faceset].reset(size,1); set<int> nset; faceset_to_nodeset(name,nset); set<int>::const_iterator iter; for (iter = nset.begin(); iter != nset.end(); iter++) { (facesets[faceset])(*iter,0) = 1; } subsetData[faceset] = & facesets[faceset]; } // elementsets ELEMENT_SET_MAP::const_iterator eiter; map<string,DENS_MAT> elemsets; for (eiter = elementSetMap_.begin();eiter != elementSetMap_.end();eiter++) { string name = eiter->first; const set<int> & eset = eiter->second; string elemset = "elementset_"+name; elemsets[elemset].reset(size,1); set<int>::const_iterator iter; for (iter = eset.begin(); iter != eset.end(); iter++) { Array<int> nodes; element_connectivity_unique(*iter, nodes); for(int i = 0; i < nodes.size(); ++i) { (elemsets[elemset])(nodes(i),0) = 1; } } subsetData[elemset] = & elemsets[elemset]; } // output const int * map = globalToUniqueMap_.data(); if (subsetData.size() > 0 ) meshSets.write_data(0.0,&subsetData,map); else if (LammpsInterface::instance()->comm_rank() == 0) { stringstream ss; ss << "Warning mesh output requested without any mesh entities, output suppressed"; ATC::LammpsInterface::instance()->print_msg(ss.str()); } } bool FE_Mesh::is_owned_elt(int elt) const { return (find(myElts_.begin(), myElts_.end(), elt) != myElts_.end()); } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_3DMesh::FE_3DMesh(const string elementType, const int nNodes, const int nElements, const Array2D<int> *connectivity, const DENS_MAT *nodalCoordinates, const Array<bool> periodicity, const Array< pair< string, set<int> > > *nodeSets): FE_Mesh(), minEltSize_(0), tree_(NULL) { // Pick which element class to make if (elementType == "HEX8") { feElement_ = new FE_ElementHex(8,4,2); } else if (elementType == "HEX20") { feElement_ = new FE_ElementHex(20,8,3); } else if (elementType == "HEX27") { feElement_ = new FE_ElementHex(27,9,3); } else if (elementType == "TET4") { feElement_ = new FE_ElementTet(4,3,2); hasPlanarFaces_ = true; } else { throw ATC_Error("Unrecognized element type specified."); } nSD_ = 3; nNodes_ = nNodes; nNodesUnique_ = nNodes; nElts_ = nElements; xscale_ = yscale_ = zscale_ = 1; periodicity_ = periodicity; // Connectivity and coordinates describe the mesh geometry. connectivity_.reset(connectivity->nRows(),connectivity->nCols()); connectivity_ = (*connectivity); nodalCoords_.reset(nodalCoordinates->nRows(),nodalCoordinates->nCols()); nodalCoords_ = (*nodalCoordinates); // set minimum element size minEltSize_ = 1.e20; for (int i=0; i< connectivity_.nCols(); ++i) { int n1 = connectivity_(0,i); int n2 = connectivity_(1,i); double dx[3] = {fabs(nodalCoords_(0,n1)-nodalCoords_(0,n2)), fabs(nodalCoords_(1,n1)-nodalCoords_(1,n2)), fabs(nodalCoords_(2,n1)-nodalCoords_(2,n2))}; minEltSize_ = min(minEltSize_,norm3(dx)); } // create global-unique maps coordTol_ = this->coordinate_tolerance(); setup_periodicity(); // Create the "all" elementset, the "all" nodeset, and the read-in nodesets. for (int elem = 0; elem < nElts_; elem++) elementSetAll_.insert(elem); for (int node = 0; node < nNodesUnique_; node++) nodeSetAll_.insert(node); const Array<pair<string,set<int> > > & sets = *nodeSets; for (int nodeSet = 0; nodeSet < sets.size(); ++nodeSet) { const set<int> & nset = sets(nodeSet).second; set<int> copy; if (compactRemap_.size() > 0) { for (set<int>::iterator itr = nset.begin(); itr != nset.end(); itr++) { copy.insert(globalToUniqueMap_(compactRemap_(*itr))); } } else { for (set<int>::iterator itr = nset.begin(); itr != nset.end(); itr++) { copy.insert(globalToUniqueMap_(*itr)); } } create_nodeset(sets(nodeSet).first, copy); } // Insert nodes and elements into KD-tree for PIE search. if (tree_ == NULL) { tree_ = KD_Tree::create_KD_tree(feElement_->num_elt_nodes(), nNodes_, &nodalCoords_, nElts_, connectivity_); } } FE_3DMesh::~FE_3DMesh() { - //if (tree_) delete tree_; - - // not sure why, commenting out for now - JZ 2/28/13 + if (tree_) delete tree_; } // ------------------------------------------------------------- // setup_periodicity // ------------------------------------------------------------- void FE_3DMesh::setup_periodicity(double tol) { // unique <-> global id maps globalToUniqueMap_.reset(nNodes_); uniqueToGlobalMap_.reset(nNodes_); for (int node = 0; node < nNodes_; node++) { globalToUniqueMap_(node) = node; } // recursive fix and setup global to unique map if (periodicity_(2)) { fix_periodicity(3); } if (periodicity_(1)) { fix_periodicity(2); } if (periodicity_(0)) { fix_periodicity(1); } // renumber to compact unique numbering // unique nodes map to the same id with global to unique if (nNodesUnique_ < nNodes_) { int n = 0; int m = nNodesUnique_; compactRemap_.reset(nNodes_); // old to new global numbering compactRemap_ = -1; for (int node = 0; node < nNodes_; node++) { if (globalToUniqueMap_(node) == node) { // unique nodes compactRemap_(node) = n++; } else { // periodic nodes compactRemap_(node) = m++; } } if (n != nNodesUnique_) throw ATC_Error("didn't compact numbering"); int npe = num_nodes_per_element(); // temporary copy DENS_MAT coor = DENS_MAT(nodalCoords_); Array2D<int> conn(connectivity_); Array<int> oldGlobalToUniqueMap = globalToUniqueMap_; for (int node = 0; node < nNodes_; node++) { // unique = remap * global2unique global globalToUniqueMap_(compactRemap_(node)) = compactRemap_(oldGlobalToUniqueMap(node)); if (compactRemap_(node) < 0) throw ATC_Error("mis-map to compact numbering"); // swap coordinates for (int i = 0; i < nSD_; i++) { nodalCoords_(i,compactRemap_(node)) = coor(i,node); } } for (int elem = 0; elem < nElts_; elem++) { for (int i = 0; i < npe; i++) { connectivity_(i,elem) = compactRemap_(conn(i,elem)); } } } for (int node = 0; node < nNodes_; node++) { uniqueToGlobalMap_(globalToUniqueMap_(node)) = node; } set_unique_connectivity(); //amended_ = true; // would always be true since setup_always called } void FE_3DMesh::fix_periodicity(int idir) { set<int> topNodes,botNodes; int ntop = find_boundary_nodes( idir,topNodes); int nbot = find_boundary_nodes(-idir,botNodes); if (ntop != nbot) throw ATC_Error("can't fix periodicity, number of top and bottom nodes are different "); bool match = match_nodes(idir,topNodes,botNodes,globalToUniqueMap_); if (!match) { stringstream ss; ss << "can't match periodic nodes with tolerance " << coordTol_; throw ATC_Error(ss.str()); } } int FE_3DMesh::find_boundary_nodes(int idir, set<int> & nodes) { nodes.clear(); double limit = 0; int idm = abs(idir)-1; DENS_MAT & x = nodalCoords_; if (idir > 0) limit = x.row_max(idm); else limit = x.row_min(idm); for (int i=0; i < x.nCols(); ++i) { double xi = x(idm,i); if (fabs(xi-limit) < coordTol_) nodes.insert(i); } // stringstream ss; // ss << "found " << nodes.size() << " nodes at x_" << abs(idir) << "= "<< limit ; // ATC::LammpsInterface::instance()->print_msg_once(ss.str()); return nodes.size(); } bool FE_3DMesh::match_nodes(int idir, set<int> & nodes1, set<int> & nodes2, Array<int> & map) { int i1=0,i2=1; plane_coords(idir-1,i1,i2); vector<bool> found(nNodes_,false); DENS_MAT & x = nodalCoords_; for (set<int>::iterator it1=nodes1.begin(); it1 != nodes1.end(); it1++) { int n1 = *it1; double x1 = x(i1,n1); double x2 = x(i2,n1); for (set<int>::iterator it2=nodes2.begin(); it2 != nodes2.end(); it2++) { int n2 = *it2; if (!found[n2]) { double y1 = x(i1,n2); double y2 = x(i2,n2); if (fabs(x1-y1) < coordTol_ && fabs(x2-y2) < coordTol_) { map(n1) = n2; found[n2] = true; // coincidence x(i1,n2) = x1; x(i2,n2) = x2; } } } if (map(n1) == n1) return false; } nNodesUnique_ -= nodes1.size(); stringstream ss; ss << "condensed " << nodes1.size() << " periodic nodes in the " << abs(idir) << " direction"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); return true; } void FE_3DMesh::set_unique_connectivity(void) { int numEltNodes = feElement_->num_elt_nodes(); connectivityUnique_.reset(numEltNodes, nElts_); uniqueNodeToElementMap_.reset(nNodes_); for (int node = 0; node < nNodes_; node++) { uniqueNodeToElementMap_(node) = vector<int>(); } for (int elem = 0; elem < nElts_; elem++) { for (int node = 0; node < numEltNodes; node++) { int global_node = connectivity_(node, elem); int unique_node = globalToUniqueMap_(global_node); connectivityUnique_(node,elem) = unique_node; uniqueNodeToElementMap_(unique_node).push_back(elem); } } } // orient the local coordinate with the global one bool FE_3DMesh::orient(int idm) { int numEltNodes = feElement_->num_elt_nodes(); if (numEltNodes != 8) throw ATC_Error("can't currently orient non HEX8 elements"); DENS_MAT x; for (int elem = 0; elem < nElts_; elem++) { element_coordinates(elem,x); double xmax = x.row_max(idm); double xmin = x.row_min(idm); set<int> top,bot; for (int node = 0; node < numEltNodes; node++) { // find top nodes if ((x(idm,node) - xmax) < coordTol_) top.insert(node); // find bottom nodes else if ((x(idm,node) - xmin) < coordTol_) bot.insert(node); else return false; } // order by rh rule // start with one } throw ATC_Error("not completely implemented function: FE_3DMesh::orient"); return true; } // ------------------------------------------------------------- // amend mesh for cut at specified faces // ------------------------------------------------------------- void FE_3DMesh::cut_mesh(const set<PAIR> & faceSet, const set<int> & nodeSet) { int nsd = nSD_; // get all nodes to create an old to new number map set<int> dupNodes; map<int,int> oldToNewMap; faceset_to_nodeset_global(faceSet,dupNodes); // remove edge nodes Array<int> & node_map = globalToUniqueMap_; set<int>::const_iterator itr; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++) { int gnode = *itr; int unode = node_map(gnode); if (nodeSet.find(unode) != nodeSet.end()) { dupNodes.erase(gnode); } } int nNodesAdded = dupNodes.size(); // copy coordinates and nodemap DENS_MAT &coordinates = nodalCoords_; int nNodesNew = coordinates.nCols() + nNodesAdded; coordinates.resize(nsd,nNodesNew,true); node_map.resize(nNodesNew,true); // add duplicate coordinates int iNew = nNodes_; int iNewUnique = nNodesUnique_; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++,iNew++) { int iOld = *itr; oldToNewMap[iOld] = iNew; // global ids if (iOld == node_map(iOld)) { // non-image atom node_map(iNew) = iNewUnique++; } else { node_map(iNew) = -1; } for(int j = 0; j < nsd; j++) { coordinates(j,iNew) = coordinates(j,iOld); } } nNodes_ = iNew; nNodesUnique_ = iNewUnique; for (itr = dupNodes.begin(); itr != dupNodes.end(); itr++,iNew++) { int iOld = *itr; iNew = oldToNewMap[iOld]; // global ids int iOldImage = node_map(iOld); if (iOld != iOldImage) { // image atom int iNewImage = oldToNewMap[iOldImage]; node_map(iNew) = node_map(iNewImage); } } // update element connectivities const int nnf = num_nodes_per_face(); const Array2D <int> & local_conn = feElement_->local_face_conn(); set< PAIR >::iterator iter; for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { PAIR face = *iter; int eltID=face.first, faceID=face.second; for (int inode=0; inode < nnf; inode++) { int lid = local_conn(faceID,inode); int id = connectivity_(lid, eltID); if (oldToNewMap.find(id) != oldToNewMap.end() ) { int new_id = (*oldToNewMap.find(id)).second; connectivity_(lid,eltID) = new_id; connectivityUnique_(lid, eltID) = node_map(new_id); } } } } // ------------------------------------------------------------- // amend mesh for deleted elements // ------------------------------------------------------------- void FE_3DMesh::delete_elements(const set<int> &elementList) { int nPE = num_nodes_per_element(); set<int> elementsNew; elementset_complement(elementList,elementsNew); int nElementsNew = elementsNew.size(); set<int> newToOld; map<int,int> oldToNewMap; elementset_to_nodeset(elementsNew,newToOld); int nNodesNew = newToOld.size(); set<int>::const_iterator itr; // coordinates & node map (from nodes to data) const DENS_MAT &coordinates = nodal_coordinates(); const Array<int> & node_map = global_to_unique_map(); DENS_MAT *newCoords = new DENS_MAT(nSD_,nNodesNew); Array<int> *newNodeMap = new Array<int> (nNodesNew); Array2D<int> * newConnectivity = new Array2D<int>(nPE,nElementsNew); int k = 0, i = 0; for (itr = newToOld.begin(); itr != newToOld.end(); itr++) { int node = *itr; oldToNewMap[node] = i++; (*newNodeMap)(k) = node_map(node); for(int j = 0; j < nSD_; j++) { (*newCoords)(j,k) = coordinates(j,node); } k++; } // nNodes_ = nNodesNew; ??? // connectivity k = 0; for (itr = elementsNew.begin(); itr != elementsNew.end(); itr++) { int ielem = *itr; for(int j = 0; j < nPE; j++) { int old_node = connectivity_(j,ielem); map<int,int>::iterator map_itr = oldToNewMap.find(old_node); if (map_itr == oldToNewMap.end()) { stringstream ss; ss << "map failure " << old_node << "\n"; ATC::LammpsInterface::instance()->print_msg(ss.str()); } int node = map_itr->second; (*newConnectivity)(j,k) = node; } k++; } delete newCoords; delete newNodeMap; delete newConnectivity; } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; int nProcs, myrank; MPI_Comm_size(MPI_COMM_WORLD, &nProcs); // get the number of processors MPI_Comm_rank(MPI_COMM_WORLD, &myrank); // get the current processor's rank // use the KD tree for partitioning, getting more blocks than // processors if (tree_ == NULL) { tree_ = KD_Tree::create_KD_tree(feElement_->num_elt_nodes(), nNodes_, &nodalCoords_, nElts_, connectivity_); } // Get the divisions of the elements from the KD-tree. int depth = ceil(log(nProcs)/log(2)); // In order to make sure there are enough divisions to evenly // divide between all processors, we get the next-highest // power of 2. vector<vector<int> > procEltLists = tree_->getElemIDs(depth); int numEltLists = procEltLists.size(); // Make sure the KD tree is behaving as expected. assert(numEltLists >= nProcs); // If the KD-tree was not able to return enough divisions, // duplicate the largest list. // elements, then the division would be more even. vector<vector<int> >::iterator it; if (numNonempty(procEltLists) < nProcs) { // Find the list of maximum size and assign it to empty processors vector<int> maxSizeList (0); for (it = procEltLists.begin(); it != procEltLists.end(); ++it) { if (it->size() > maxSizeList.size()) maxSizeList.assign(it->begin(), it->end()); } for (it = procEltLists.begin(); it != procEltLists.end(); ++it) { if (it->empty()) { if (numNonempty(procEltLists) >= nProcs) break; it->assign(maxSizeList.begin(), maxSizeList.end()); } } } // We will store the owning processor for each element. int * eltToOwners = new int[nElts_]; for (int i = 0; i < nElts_; ++i) { eltToOwners[i] = -1; // -1 here means the element is unowned. } // Prune elements that appear on more than one processor. // Simultaneously fill in the ownership array. prune_duplicate_elements( procEltLists, eltToOwners); // If we have more lists than processors, get rid of the // extras and redistribute their elements. if (numNonempty(procEltLists) > nProcs) { redistribute_extra_proclists(procEltLists, eltToOwners, nProcs); } // Sort the lists so that the fuller ones are in the front. sort(procEltLists.begin(), procEltLists.end(), vectorCompSize); // Assign each processor a list of elements. myElts_ = procEltLists[myrank]; //mesh_distribute(eltStartIDs); delete[] eltToOwners; // We should do nodes later. if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } void FE_3DMesh::prune_duplicate_elements(vector<vector<int> > & procEltLists, int * eltToOwners) { int procID = 0; vector<vector<int> >::iterator topIt; vector<int> * conflictingProc; vector<int>::iterator it, toErase; for (topIt = procEltLists.begin(); topIt != procEltLists.end(); ++topIt) { // Simultaneously fill in eltToOwners and prune, if an element // appears on multiple processors. for (it = topIt->begin(); it != topIt->end(); ++it) { // If the element has no corresponding processor in eltToOwners, // record it as belonging to processor *it. if (eltToOwners[*it] == -1) { eltToOwners[*it] = procID; } else { // If it does have a processor in eltToOwners, then we need // to remove it from either processor *topIt or from the processor // listed in eltToOwners. We discriminate based on size. conflictingProc = &(procEltLists[eltToOwners[*it]]); if (conflictingProc->size() <= procEltLists[procID].size()) { // Delete element from processor *topIt, if it has more elements. it = topIt->erase(it); --it; } else { // Delete element from conflicting processor otherwise. toErase = find(conflictingProc->begin(), conflictingProc->end(), *it); conflictingProc->erase(toErase); eltToOwners[*it] = procID; } } } ++procID; } } // ------------------------------------------------------------- // lammps_partition_mesh // ------------------------------------------------------------- void FE_3DMesh::lammps_partition_mesh() { if (LammpsInterface::instance()->domain_triclinic()) { LammpsInterface::instance()->print_msg_once("Cannot use lammps partitioning, domain is triclinic"); return; } LammpsInterface::instance()->print_msg_once("Warning: Using native lammps partitioning"); double xlo, xhi, ylo, yhi, zlo, zhi; double boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi; LammpsInterface::instance()->sub_bounds(xlo, xhi, ylo, yhi, zlo, zhi); LammpsInterface::instance()->box_bounds(boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi); myElts_.clear(); double xCent, yCent, zCent; // Assign elements to processors based on the centroid of the element. int numNodes = num_nodes_per_element(); for (int i = 0; i < nElts_; ++i) { xCent = 0.0; yCent = 0.0; zCent = 0.0; for (int j = 0; j < numNodes; ++ j) { xCent += nodalCoords_(0, connectivity_(j,i)); yCent += nodalCoords_(1, connectivity_(j,i)); zCent += nodalCoords_(2, connectivity_(j,i)); } xCent /= numNodes; yCent /= numNodes; zCent /= numNodes; if (xCent < boxxlo) xCent = boxxlo; if (xCent < boxxhi) xCent = boxxhi; if (yCent < boxylo) yCent = boxylo; if (yCent < boxyhi) yCent = boxyhi; if (zCent < boxzlo) zCent = boxzlo; if (zCent < boxzhi) zCent = boxzhi; if ( dbl_geq(xCent, xlo) && ((xhi == boxxhi) || !dbl_geq(xCent, xhi)) && dbl_geq(yCent, ylo) && ((yhi == boxyhi) || !dbl_geq(yCent, yhi)) && dbl_geq(zCent, zlo) && ((zhi == boxzhi) || !dbl_geq(zCent, zhi))) { myElts_.push_back(i); } } // if decomposing add in myAtomElts list based on nodal locations, i.e., all elements with a local node // myElts: for FE assembly // myAndGhost : for atom ops like restrict if (decomposition_) { set<int> elms; vector<int>::const_iterator itr; for (itr=myElts_.begin(); itr!=myElts_.end(); itr++) {elms.insert(*itr); } set<int> nodes; elementset_to_nodeset(elms,nodes); elms.clear(); nodeset_to_maximal_elementset(nodes,elms); myAndGhostElts_.clear(); set<int>::const_iterator iter; for (iter=elms.begin(); iter!=elms.end(); iter++) {myAndGhostElts_.push_back(*iter);} distribute_mesh_data(); } partitioned_ = true; return; } void FE_3DMesh::redistribute_extra_proclists(vector<vector<int> > &procEltLists, int *eltToOwners, int nProcs) { DENS_MAT faceAdjacencies(nElts_, num_faces_per_element()); faceAdjacencies = -1; // Set all values to -1, indicating uninitialized/uncalculated int currentElt, adjacentElt, procID; // Put all of the hobos onto one master list, allHomelessElts. list<int> allHomelessElts; vector<int> oneHomelessList; vector<vector<int> >::iterator current; int nHoboLists = numNonempty(procEltLists) - nProcs; for (int i = 0; i < nHoboLists; ++i) { current = min_element(procEltLists.begin(), procEltLists.end(), vectorCompSize); oneHomelessList = *current; allHomelessElts.insert(allHomelessElts.end(), oneHomelessList.begin(), oneHomelessList.end()); current->clear(); } // Make sure the hobos lose their association with their old processor. list<int>::iterator it; for (it = allHomelessElts.begin(); it != allHomelessElts.end(); it++){ eltToOwners[*it] = -1; } // Figure out which elements the hobos are adjacent to. That way, they // will know what processors they can be redistributed to. compute_face_adjacencies(allHomelessElts, faceAdjacencies); // Place homeless elements onto lists that correspond to actual processors. while (!allHomelessElts.empty()) { currentElt = allHomelessElts.back(); // This will store the ID of the processor with the fewest elements // so far that has an element adjacent to currentElt. PAIR smallestProc(-1, INT_MAX); // Iterate over the faces, check the processors of adjacent elements, // and slate the element to go on the adjacent processor with the fewest // elements. for (int localFaceID = 0; localFaceID < num_faces_per_element(); ++localFaceID) { adjacentElt = faceAdjacencies(currentElt, localFaceID); // This means that there is no adjacency through this face. if (adjacentElt >= nElts_) continue; procID = eltToOwners[adjacentElt]; // The procID > -1 check makes sure we're not adjacent to another // homeless element by this face, in which case it won't have a // processor to put currentElt onto yet. if (procID > -1 && ((int) procEltLists[procID].size()) < smallestProc.second) { smallestProc = PAIR(procID, procEltLists[procID].size()); } } allHomelessElts.pop_back(); // If we couldn't find an adjacent element that had a processor, // skip for now and come back to it later. if (smallestProc.first == -1) { allHomelessElts.push_front(currentElt); } // Otherwise, put it onto the processor with the fewest elements that // we found. else { procEltLists[smallestProc.first].push_back(currentElt); eltToOwners[currentElt] = smallestProc.first; } } } void FE_3DMesh::compute_face_adjacencies(const list<int> &elts, DENS_MAT &faceAdjacencies) { list<int>::const_iterator cit; for (cit = elts.begin(); cit != elts.end(); ++cit) { // For each element, look at ever face, get the nodes on that face, // and find out which elements are in the intersection of elements // containing that node. Those two elements are adjacent, and we // mark it as such in the faceAdjacencies array. for (int localFaceID = 0; localFaceID < num_faces_per_element(); ++localFaceID) { Array<int> faceNodes; face_connectivity(PAIR(*(cit), localFaceID), faceNodes); // Put the first node's elements into the accumulator to start. vector<int> vIntersect = uniqueNodeToElementMap_(faceNodes(0)); vector<int> vCurrent; // set_intersect requires a vector large enough to contain the // max possible intersect, which cannot be larger than the entirety // of the first vector involved. vector<int> vTemp(vIntersect.size(), -1); // Find the intersection of each of the nodes' element vectors. for (int ithOnFace = 1; ithOnFace < num_nodes_per_face(); ++ithOnFace) { vCurrent = uniqueNodeToElementMap_(faceNodes(ithOnFace)); // Vector of elements for this node set_intersection(vCurrent.begin(), vCurrent.end(), vIntersect.begin(), vIntersect.end(), vTemp.begin()); vIntersect = vTemp; // Because we initialized the vector to be larger than necessary, maybe, // we remove all of the meaningless values used in initialization. while (vIntersect.back() == -1) vIntersect.pop_back(); vTemp.clear(); vTemp.resize(vIntersect.size(),-1); } // This means there is an adjacent face, since there // are two elements sharing all of the nodes on the face. if (vIntersect.size() == 2) { // We want to choose the element id of NOT the current // element to be listed as the adjacency. // well, but that requires more complicated memoization and // this doesn't take much extra time. if (*cit == vIntersect[0]) { faceAdjacencies(*cit, localFaceID) = vIntersect[1]; } else { faceAdjacencies(*cit, localFaceID) = vIntersect[0]; } } // This means the element is on the border. else if (vIntersect.size() == 1) { faceAdjacencies(*cit, localFaceID) = INT_MAX; } else { // This should never ever happen! The nodes should at least // share one element, since they are all on one face! // There should also never be more than two elements on the // same face... that would defy mathematics and physics in // every way. } } } } // Sometimes we want to count the number of vectors that actually // have stuff in them. We use this. int FE_3DMesh::numNonempty(vector<vector<int> > & v) { int result = 0; vector<vector<int> >::iterator it; for (it = v.begin(); it != v.end(); ++it){ if (!(it->empty())){ result++; } } return result; } int FE_3DMesh::map_to_element(const DENS_VEC &query) const { DENS_MAT eltCoords; vector<int> candidates = tree_->find_nearest(query); vector<int> matches = vector<int>(); // Search through each of the nearest elements for (vector<int>::iterator elem = candidates.begin(); elem < candidates.end(); elem++) { if (contains_point(*elem, query)) { matches.push_back(*elem); // Keep track of the elements // which contain it } } // Return the index of the parent element which does contain the point if (matches.size() == 1) { // x is conclusively in a single element return matches[0]; } else if (matches.size() == 0) { // not so much throw ATC_Error("FE_3DMesh::map_to_element could not find an element"); } else { // definitely not so much throw ATC_Error("FE_3DMesh::map_to_element found multiple elements"); } } bool FE_3DMesh::contains_point(const int eltID, const DENS_VEC &x) const { DENS_MAT eltCoords; element_coordinates(eltID, eltCoords); return feElement_->contains_point(eltCoords, x); } //----------------------------------------------------------------------- void FE_3DMesh::distribute_mesh_data() { myNElts_ = myElts_.size(); //create elemToMyElemMap_ elemToMyElemMap_.clear(); for (int myID = 0; myID < myNElts_; ++myID) { int baseID = myElts_[myID]; elemToMyElemMap_[baseID] = myID; } // create myConnectivity_, myConnectivityUnique_ int numEltNodes = feElement_->num_elt_nodes(); myConnectivity_.reset(numEltNodes, myNElts_); myConnectivityUnique_.reset(numEltNodes, myNElts_); for (int elem = 0; elem < myNElts_; ++elem) { for (int node = 0; node < numEltNodes; ++node) { myConnectivity_(node,elem) = connectivity_(node,map_myElem_to_elem(elem)); myConnectivityUnique_(node,elem) = connectivityUnique_(node,map_myElem_to_elem(elem)); } } } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_Rectangular3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_Rectangular3DMesh::FE_Rectangular3DMesh( const Array<double> & hx, const Array<double> & hy, const Array<double> & hz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array<bool> periodicity, const double xscale, const double yscale, const double zscale) : hx_(hx), hy_(hy), hz_(hz) { + tree_ = NULL; hasPlanarFaces_ = true; xscale_ = xscale; yscale_ = yscale; zscale_ = zscale; borders_[0][0] = xmin; borders_[0][1] = ymin; borders_[0][2] = zmin; borders_[1][0] = xmax; borders_[1][1] = ymax; borders_[1][2] = zmax; L_[0] = xmax-xmin; L_[1] = ymax-ymin; L_[2] = zmax-zmin; n_[0] = hx_.size(); n_[1] = hy_.size(); n_[2] = hz_.size(); // Compute region size and element size double Lx = 0; for (int i = 0; i < n_[0]; ++i) { Lx += hx_(i); } double Ly = 0; for (int i = 0; i < n_[1]; ++i) { Ly += hy_(i); } double Lz = 0; for (int i = 0; i < n_[2]; ++i) { Lz += hz_(i); } // rescale to fit box double ax = L_[0]/Lx; for (int i = 0; i < n_[0]; ++i) { hx_(i) *= ax; } double ay = L_[1]/Ly; for (int i = 0; i < n_[1]; ++i) { hy_(i) *= ay; } double az = L_[2]/Lz; for (int i = 0; i < n_[2]; ++i) { hz_(i) *= az; } // fill node locations nSD_ = 3; x_.reserve(nSD_); for (int i = 0; i < nSD_; ++i) {x_.push_back(Array<double>(n_[i]+1)); } Array<double> & xI = x_[0]; xI(0) = xmin; for (int i = 0; i < n_[0]; ++i) { xI(i+1) = xI(i)+hx_(i); } Array<double> & yI = x_[1]; yI(0) = ymin; for (int i = 0; i < n_[1]; ++i) { yI(i+1) = yI(i)+hy_(i); } Array<double> & zI = x_[2]; zI(0) = zmin; for (int i = 0; i < n_[2]; ++i) { zI(i+1) = zI(i)+hz_(i); } // Member data setup nElts_ = n_[0] * n_[1] * n_[2]; nNodes_ = (n_[0]+1) * (n_[1]+1) * (n_[2]+1); periodicity_ = Array<bool>(periodicity); feElement_ = new FE_ElementRect(); nodalCoords_.reset(3, nNodes_); connectivity_.reset(feElement_->num_elt_nodes(), nElts_); // Fill nodal coordinates double x[3] = {xmin,ymin,zmin}; int inode = 0; for (int k = 0; k <= n_[2]; ++k) { for (int j = 0; j <= n_[1]; ++j) { for (int i = 0; i <= n_[0]; ++i) { for (int m = 0; m < 3; ++m) { nodalCoords_(m,inode) = x[m]; } ++inode; if (i < n_[0]) x[0] += hx_(i); } if (j < n_[1]) x[1] += hy_(j); x[0] = xmin; } if (k < n_[2]) x[2] += hz_(k); x[1] = ymin; } // Compute element connectivities int ielt = 0; int noffx = 1; int noffy = n_[0] + 1; int noffz = (n_[0]+1) * (n_[1]+1); for (int k = 0; k < n_[2]; ++k) { for (int j = 0; j < n_[1]; ++j) { for (int i = 0; i < n_[0]; ++i) { int i1 = i + j*noffy + k*noffz; connectivity_(0,ielt) = i1; connectivity_(1,ielt) = i1 + noffx; connectivity_(2,ielt) = i1 + noffx + noffy; connectivity_(3,ielt) = i1 + noffy; connectivity_(4,ielt) = i1 + noffz; connectivity_(5,ielt) = i1 + noffx + noffz; connectivity_(6,ielt) = i1 + noffx + noffy + noffz; connectivity_(7,ielt) = i1 + noffy + noffz; ++ielt; } } } setup_periodicity(); } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_Rectangular3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; // Currently this code has been rather naively copied from // FE_Uniform3DMesh::partition_mesh() // Determine dimensions of mesh in order to partition according to largest dimension. double xmin = borders_[0][0]; double xmax = borders_[1][0]; double ymin = borders_[0][1]; double ymax = borders_[1][1]; double zmin = borders_[0][2]; double zmax = borders_[1][2]; int numProcs; MPI_Comm_size(MPI_COMM_WORLD, &numProcs); int processorRank; MPI_Comm_rank(MPI_COMM_WORLD, &processorRank); // Spatially partition along the largest dimension. procs_.clear(); if (max(max(L_[0], L_[1]), L_[2]) == L_[0]) { partitionAxis_ = 0; for (int i = 0; i < numProcs; ++i) { procs_.push_back(xmin + (L_[0]*i)/numProcs); } procs_.push_back(xmax); } else if (max(max(L_[0], L_[1]), L_[2]) == L_[1]) { partitionAxis_ = 1; for (int i = 0; i < numProcs; ++i) { procs_.push_back(ymin + (L_[1]*i)/numProcs); } procs_.push_back(ymax); } else { partitionAxis_ = 2; for (int i = 0; i < numProcs; ++i) { procs_.push_back(zmin + (L_[2]*i)/numProcs); } procs_.push_back(zmax); } // Distribute each node to the correct processor myNodes_.clear(); for (int i = 0; i < nNodes_; ++i) { // Allocate node to this processor if it lies between processor's left and right boundaries. if ( dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank]) && !dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } // Also allocate nodes on the right boundary to the last processor. if ((processorRank == numProcs - 1) && dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } } // Distribute each element to the correct processor - assign it to the processor // which owns its node of lowest index. (this partitioning scheme is unambiguous) myElts_.clear(); for (int i = 0; i < nElts_; ++i) { int min = INT_MAX; for (int j = 0; j < connectivity_.nRows(); j++) { if (connectivity_(j, i) < min) min = connectivity_(j, i); } if (find(myNodes_.begin(), myNodes_.end(), min) != myNodes_.end()) { myElts_.push_back(i); } } /* Commented out because ghost nodes are never used and dx_ is not a member of FE_Rectangular3DMesh. // Compute the facesets that describes the left and right boundaries // in order to determine ghost nodes. int leftMult = 0; while ((leftMult+1)*dx_[partitionAxis_] < procs_[processorRank]) { ++leftMult; } int rightMult = 0; while ((rightMult)*dx_[partitionAxis_] < procs_[processorRank+1]) { ++rightMult; } // Compute our ghost nodes - nodes that we need that belong to adjacent processors, // and our shared nodes - our nodes that are ghosted on the adjacent processors. for (int i = 0; i < nNodes_; ++i) { if (nodalCoords_(partitionAxis_, i) == leftMult*dx_[partitionAxis_]) ghostNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == rightMult*dx_[partitionAxis_]) ghostNodesR_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (leftMult+1)*dx_[partitionAxis_]) shareNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (rightMult-1)*dx_[partitionAxis_]) shareNodesR_.push_back(i); }*/ if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_Rectangular3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } // ------------------------------------------------------------- // setup_periodicity // ------------------------------------------------------------- void FE_Rectangular3DMesh::setup_periodicity() { nNodesUnique_ = 1; for (int i = 0; i < 3; i++) { nNodesUnique_ *= (n_[i] + 1 - periodicity_(i)); } // form maximal nodeset for (int i = 0; i < nNodesUnique_; i++) { nodeSetAll_.insert(i); } // Create global-to-unique map: globalToUniqueMap_(ig) = iu globalToUniqueMap_.reset(nNodes_); uniqueToGlobalMap_.reset(nNodesUnique_); for (int k = 0; k <= n_[2]; ++k) { int kper = (k == n_[2] && periodicity_(2)) ? 0 : k; for (int j = 0; j <= n_[1]; ++j) { int jper = (j == n_[1] && periodicity_(1)) ? 0 : j; for (int i = 0; i <= n_[0]; ++i) { int iper = (i == n_[0] && periodicity_(0)) ? 0 : i; int id = i + j*(n_[0]+1) + k*(n_[0]+1)*(n_[1]+1); int uid = iper + jper*(n_[0]+1-periodicity_(0)) + kper*(n_[0]+1-periodicity_(0))*(n_[1]+1-periodicity_(1)); globalToUniqueMap_(id) = uid; uniqueToGlobalMap_(uid) = id; } } } set_unique_connectivity(); // form maximal elementset for (int i = 0; i < nElts_; i++) { elementSetAll_.insert(i); } } int FE_Rectangular3DMesh::map_to_element(const DENS_VEC &x) const { int ix[3]; // ix[i] is the element in the ith direction for (int i = 0; i < 3; i++) { // map to box double y = x(i); if (periodicity_(i)) { double diff = y-borders_[0][i]; int shift = int(diff/L_[i]); if (diff < 0.) shift--; y -= shift*L_[i]; } // project into element ix[i] = x_[i].index(y); if (fabs(y-borders_[0][i]) < tol) { ix[i] = 0; } // on the lower boundary if (ix[i] < 0 || ix[i] >= n_[i]) { string msg = "FE_Rectangular3DMesh:: point maps outside of mesh, coordinate " + index_to_string(i) + "=" + to_string(x(i)) + " image=" + to_string(y) + " not in " + to_string(borders_[0][i]) + ":" + to_string(borders_[1][i]); throw ATC_Error(msg); } } int elt = ix[2]*(n_[0]*n_[1]) + ix[1]*n_[0] + ix[0]; return elt; } // ------------------------------------------------------------- // ------------------------------------------------------------- // class FE_Uniform3DMesh // ------------------------------------------------------------- // ------------------------------------------------------------- FE_Uniform3DMesh::FE_Uniform3DMesh(const int nx, const int ny, const int nz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array<bool> periodicity, const double xscale, const double yscale, const double zscale) { hasPlanarFaces_ = true; tree_ = NULL; xscale_ = xscale; yscale_ = yscale; zscale_ = zscale; n_[0] = nx; n_[1] = ny; n_[2] = nz; borders_[0][0] = xmin; borders_[1][0] = xmax; borders_[0][1] = ymin; borders_[1][1] = ymax; borders_[0][2] = zmin; borders_[1][2] = zmax; periodicity_ = Array<bool>(periodicity); // Compute region size and element size for (int i = 0; i < 3; i++) { L_[i] = borders_[1][i] - borders_[0][i]; dx_[i] = L_[i]/n_[i]; } // Member data setup nSD_ = 3; nElts_ = n_[0] * n_[1] * n_[2]; nNodes_ = (n_[0]+1) * (n_[1]+1) * (n_[2]+1); feElement_ = new FE_ElementRect(); nodalCoords_.reset(3, nNodes_); connectivity_.reset(feElement_->num_elt_nodes(), nElts_); // Fill nodal coordinates double ix[3]; int inode = 0; for (int k = 0; k <= n_[2]; ++k) { ix[2] = borders_[0][2] + k*dx_[2]; for (int j = 0; j <= n_[1]; ++j) { ix[1] = borders_[0][1] + j*dx_[1]; for (int i = 0; i <= n_[0]; ++i) { ix[0] = borders_[0][0] + i*dx_[0]; for (int m = 0; m < 3; ++m) { nodalCoords_(m,inode) = ix[m]; } ++inode; } } } // Compute element connectivities int ielt = 0; int noffx = 1; int noffy = n_[0] + 1; int noffz = (n_[0]+1) * (n_[1]+1); for (int k = 0; k < n_[2]; ++k) { for (int j = 0; j < n_[1]; ++j) { for (int i = 0; i < n_[0]; ++i) { int i1 = i + j*noffy + k*noffz; connectivity_(0,ielt) = i1; connectivity_(1,ielt) = i1 + noffx; connectivity_(2,ielt) = i1 + noffx + noffy; connectivity_(3,ielt) = i1 + noffy; connectivity_(4,ielt) = i1 + noffz; connectivity_(5,ielt) = i1 + noffx + noffz; connectivity_(6,ielt) = i1 + noffx + noffy + noffz; connectivity_(7,ielt) = i1 + noffy + noffz; ++ielt; } } } setup_periodicity(); } // ------------------------------------------------------------- // destructor // ------------------------------------------------------------- FE_Uniform3DMesh::~FE_Uniform3DMesh() { // Clean up is currently unimplemented } // ------------------------------------------------------------- // partition_mesh // ------------------------------------------------------------- void FE_Uniform3DMesh::partition_mesh() { if (lammpsPartition_) { lammps_partition_mesh(); } if (partitioned_) return; // Determine dimensions of mesh in order to partition according to largest dimension. double xmin = borders_[0][0]; double xmax = borders_[1][0]; double ymin = borders_[0][1]; double ymax = borders_[1][1]; double zmin = borders_[0][2]; double zmax = borders_[1][2]; int numProcs; MPI_Comm_size(MPI_COMM_WORLD, &numProcs); int processorRank; MPI_Comm_rank(MPI_COMM_WORLD, &processorRank); // Spatially partition along the largest dimension. procs_.clear(); if (max(max(L_[0], L_[1]), L_[2]) == L_[0]) { partitionAxis_ = 0; for (int i = 0; i < numProcs; ++i) { procs_.push_back(xmin + (L_[0]*i)/numProcs); } procs_.push_back(xmax); } else if (max(max(L_[0], L_[1]), L_[2]) == L_[1]) { partitionAxis_ = 1; for (int i = 0; i < numProcs; ++i) { procs_.push_back(ymin + (L_[1]*i)/numProcs); } procs_.push_back(ymax); } else { partitionAxis_ = 2; for (int i = 0; i < numProcs; ++i) { procs_.push_back(zmin + (L_[2]*i)/numProcs); } procs_.push_back(zmax); } // Distribute each node to the correct processor myNodes_.clear(); for (int i = 0; i < nNodes_; ++i) { // Allocate node to this processor if it lies between processor's left and right boundaries. if ( dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank]) && !dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } // Also allocate nodes on the right boundary to the last processor. if ((processorRank == numProcs - 1) && dbl_geq(nodalCoords_(partitionAxis_, i), procs_[processorRank + 1])) { myNodes_.push_back(i); } } // Distribute each element to the correct processor - assign it to the processor // which owns its node of lowest index. (this partitioning scheme is unambiguous) myElts_.clear(); for (int i = 0; i < nElts_; ++i) { int min = INT_MAX; for (int j = 0; j < connectivity_.nRows(); j++) { if (connectivity_(j, i) < min) min = connectivity_(j, i); } if (find(myNodes_.begin(), myNodes_.end(), min) != myNodes_.end()) { myElts_.push_back(i); } } // Compute the facesets that describes the left and right boundaries // in order to determine ghost nodes. int leftMult = 0; while ((leftMult+1)*dx_[partitionAxis_] < procs_[processorRank]) { ++leftMult; } int rightMult = 0; while ((rightMult)*dx_[partitionAxis_] < procs_[processorRank+1]) { ++rightMult; } // Compute our ghost nodes - nodes that we need that belong to adjacent processors, // and our shared nodes - our nodes that are ghosted on the adjacent processors. for (int i = 0; i < nNodes_; ++i) { if (nodalCoords_(partitionAxis_, i) == leftMult*dx_[partitionAxis_]) ghostNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == rightMult*dx_[partitionAxis_]) ghostNodesR_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (leftMult+1)*dx_[partitionAxis_]) shareNodesL_.push_back(i); else if (nodalCoords_(partitionAxis_, i) == (rightMult-1)*dx_[partitionAxis_]) shareNodesR_.push_back(i); } if (decomposition_) distribute_mesh_data(); partitioned_ = true; } void FE_Uniform3DMesh::departition_mesh() { if (!partitioned_) return; partitioned_ = false; } // ------------------------------------------------------------- // map_to_element // ------------------------------------------------------------- int FE_Uniform3DMesh::map_to_element(const DENS_VEC &x) const { // countx[i] is the number of the element, where 1 is the // element adjacent to the lower border, in the ith direction int countx[3]; for (int i = 0; i < 3; ++i) { // handle points on upper boundary; not sure why this is // hard-coded in, though... if (fabs(x(i)-borders_[1][i]) < tol) { countx[i] = n_[i] - 1; } else { // find the x, y, and z bins for the point in the mesh countx[i] = (int)floor((x(i)-borders_[0][i])/dx_[i]); } // handle points out-of-range [0:nx-1] w/ periodicity if (countx[i] < 0 || countx[i] >= n_[i]) { if (periodicity_(i)) { countx[i] = countx[i] % n_[i]; // handles c++ ambiguous mod problems if (countx[i] < 0) countx[i] += n_[i]; } else { string msg = " point maps outside " "of mesh, coordinate " + index_to_string(i) + " " + to_string(x(i)) + " not in " + to_string(borders_[0][i]) + " : " + to_string(borders_[1][i]); throw ATC_Error(FILELINE,msg); } } } int elt = countx[2]*(n_[0]*n_[1]) + countx[1]*n_[0] + countx[0]; return elt; } } // namespace ATC diff --git a/lib/atc/FE_Mesh.h b/lib/atc/FE_Mesh.h index b832a5b70..c7709168d 100644 --- a/lib/atc/FE_Mesh.h +++ b/lib/atc/FE_Mesh.h @@ -1,704 +1,711 @@ #ifndef FE_MESH_H #define FE_MESH_H #include "Array.h" #include "Array2D.h" #include "MatrixLibrary.h" #include "ATC_TypeDefs.h" #include "KD_Tree.h" #include <vector> #include <deque> #include <list> #include <map> #include <set> #include <utility> #include <float.h> #include <string> #include <vector> #include "mpi.h" namespace ATC { class FE_Element; /** * @class FE_Mesh * @brief Base class for a finite element mesh */ class FE_Mesh { public: /** constructor */ FE_Mesh(); /** destructor */ virtual ~FE_Mesh(); /** parser/modifier */ bool modify(int narg, char **arg); /** initialization */ void initialize(void); /** write an unstructured mesh */ void write_mesh(std::string meshFile); // SJL why? will they ever be overridden by derived classes? in what // situation would that be required, or desirable? virtual functions // are slightly less efficient because they cannot be hard-linked at // compile time. bool is_partitioned() const { return partitioned_; } virtual void partition_mesh() = 0; virtual void departition_mesh() = 0; int map_elem_to_myElem(int elemID) const; int map_myElem_to_elem(int myElemID) const; void set_lammps_partition(bool t) {lammpsPartition_ = t;} void set_data_decomposition(bool t) {decomposition_ = t;} /** set quadrature on element from within interpolate class */ void set_quadrature(FeIntQuadrature type); /** evaluate shape function at real coordinates */ void position(const int elem, const VECTOR &xi, DENS_VEC &x) const; /** evaluate shape function at real coordinates */ void shape_functions(const VECTOR &x, DENS_VEC &N, Array<int> &nodeList) const; /** evaluate shape function at real coordinates */ void shape_functions(const VECTOR &x, DENS_VEC &N, Array<int> &nodeList, const Array<bool> &) const; /** evaluate shape function at real coordinates */ void shape_functions(const DENS_VEC &x, DENS_VEC &N, DENS_MAT &dNdx, Array<int> &nodeList) const; /** evaluate shape function at real coordinates */ void shape_functions(const VECTOR &x, const int eltID, DENS_VEC &N, Array<int> &nodeList) const; /** evaluate shape function at real coordinates */ void shape_functions(const DENS_VEC &x, const int eltID, DENS_VEC &N, DENS_MAT &dNdx, Array<int> &nodeList) const; /** evaluate shape function at real coordinates */ void shape_function_derivatives(const DENS_VEC &x, const int eltID, DENS_MAT &dNdx, Array<int> &nodeList) const; /** evaluate shape functions for all ip's on an element */ // N is numIPsInElement X numNodesInElement void shape_function(const int eltID, DENS_MAT &N, DIAG_MAT &weights) const; /** evaluate shape functions for all ip's on an element */ // N is numIPsInElement X numNodesInElement void shape_function(const int eltID, DENS_MAT &N, std::vector<DENS_MAT> &dN, DIAG_MAT &weights) const; /** evaluate shape functions for all ip's on a face */ // N is numIPsInFace X numNodesInElement void face_shape_function(const PAIR &face, DENS_MAT &N, DENS_MAT &n, DIAG_MAT &weights) const; void face_shape_function(const PAIR &face, DENS_MAT &N, std::vector<DENS_MAT> &dN, std::vector<DENS_MAT> &Nn, DIAG_MAT &weights) const; /** compute normal vector from the specified face */ double face_normal(const PAIR &face, const int ip, DENS_VEC &normal) const; /** return connectivity (global ID numbers) for element eltID */ void element_connectivity_global(const int eltID, Array<int> & nodes) const; void element_connectivity_unique(const int eltID, Array<int> & nodes) const; int element_connectivity_global(const int eltID, const int inode) const; int element_connectivity_unique(const int eltID, const int inode) const; AliasArray<int> element_connectivity_global(const int eltID) const; AliasArray<int> element_connectivity_unique(const int eltID) const; void face_connectivity(const PAIR & faceID, - Array<int> & nodes) const + Array<int> & nodes) const { int nNodesPerFace = num_nodes_per_face(); nodes.reset(nNodesPerFace); int eltID = faceID.first; int localFace = faceID.second; const Array2D<int> & localFaceConn = local_face_connectivity(); for(int i = 0; i < nNodesPerFace; ++i) { nodes(i) = element_connectivity_global(eltID, localFaceConn(localFace,i)); } } void face_connectivity_unique(const PAIR & faceID, Array<int> & nodes) const { int nNodesPerFace = num_nodes_per_face(); nodes.reset(nNodesPerFace); int eltID = faceID.first; int localFace = faceID.second; const Array2D<int> & localFaceConn = local_face_connectivity(); for(int i = 0; i < nNodesPerFace; ++i) { nodes(i) = element_connectivity_unique(eltID, localFaceConn(localFace,i)); } } /** * return spatial coordinates for element nodes on eltID, * indexed xCoords(isd,inode) */ void element_coordinates(const int eltID, DENS_MAT & xCoords) const; void face_coordinates(const PAIR face, DENS_MAT & xCoords) const; /** access to the nodal coordinate values */ const DENS_MAT & nodal_coordinates(void) const {return nodalCoords_ ;} /** access to nodal coordinates of a unique node */ DENS_VEC nodal_coordinates(const int nodeID) const; /** access to nodal coordinates of a node */ DENS_VEC global_coordinates(const int nodeID) const; /** map spatial location to element */ virtual int map_to_element(const DENS_VEC &x) const = 0; /** map global node numbering to unique node numbering */ int map_global_to_unique(const int global_id) const { return globalToUniqueMap_(global_id); } inline const Array<int>& global_to_unique_map(void) const { return globalToUniqueMap_; } /** map unique node numbering a global node numbering */ int map_unique_to_global(const int unique_id) { return uniqueToGlobalMap_(unique_id); } inline const Array<int>& unique_to_global_map(void) const { return uniqueToGlobalMap_; } /** query whether a nodeset with the given name exists */ bool query_nodeset(const std::string & name) const; /** get node set (unique ID's) from the string name assigned to the set */ const std::set<int> & nodeset(const std::string & name) const; /** create node set with tag "name" from nodes in given spatial range */ void create_nodeset(const std::string & name, const std::set<int> & nodeset); void create_nodeset(const std::string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax); /** add to node set with tag "name" from nodes in given spatial range */ void add_to_nodeset(const std::string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax); /** get element set from the string name assigned to the set */ const std::set<int> & elementset(const std::string & name) const; /** create element set with tag "name" from nodes in given spatial range */ void create_elementset(const std::string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax); /** get the minimal element set from a nodeset by name */ void nodeset_to_minimal_elementset(const std::string &name, std::set<int> &elemSet) const; /** get the minimal element set from a set of nodes */ void nodeset_to_minimal_elementset(const std::set<int> &nodeSet, std::set<int> &elemSet) const; /** get the maximal element set from a nodeset by name */ void nodeset_to_maximal_elementset(const std::string &name, std::set<int> &elemSet) const; /** get the maximal element set from a set of nodes */ void nodeset_to_maximal_elementset(const std::set<int> &nodeSet, std::set<int> &elemSet) const; /** get complement of element set by name */ void elementset_complement(const std::string &name, std::set<int> &elemSet) const; void elementset_complement(const std::set<int> &elemSet, std::set<int> &elemSetComplement) const; /** get the node set from an element set by name */ void elementset_to_minimal_nodeset(const std::string &name, std::set<int> &nodeSet) const; void elementset_to_nodeset(const std::string &name, - std::set<int> &nodeSet) const; + std::set<int> nodeSet) const; void elementset_to_nodeset(const std::set<int> &elemSet, - std::set<int> &nodeSet) const; + std::set<int> nodeSet) const; + std::set<int> elementset_to_nodeset(const std::string &name) const; /** convert faceset to nodeset in _unique_ node numbering */ void faceset_to_nodeset(const std::string &name, std::set<int> &nodeSet) const; void faceset_to_nodeset(const std::set<PAIR> &faceSet, std::set<int> &nodeSet) const; void faceset_to_nodeset_global(const std::string &name, std::set<int> &nodeSet) const; void faceset_to_nodeset_global(const std::set<PAIR> &faceSet, std::set<int> &nodeSet) const; /** get face set from the string name assigned to the set */ const std::set< std::pair<int,int> > & faceset(const std::string & name) const; /** create face set with tag "name" from faces aligned with box */ void create_faceset(const std::string & name, double xmin, double xmax, double ymin, double ymax, double zmin, double zmax, bool outward); /** create face set with tag "name" from faces aligned with plane */ void create_faceset(const std::string & name, double x, int idir, int isgn, int nIdx2=-1, double x2lo=0.0, double x2hi=0.0, int nIdx3=-1, double x3lo=0.0, double x3hi=0.0); /** cut mesh */ virtual void cut_mesh(const std::set<PAIR> & faceSet, const std::set<int> & nodeSet) = 0; /** delete elements */ virtual void delete_elements(const std::set<int> & elementList) = 0; /** return number of spatial dimensions */ int num_spatial_dimensions() const { return nSD_; } /** return total number of nodes */ int num_nodes() const { return nNodes_; } /** return number of unique nodes */ int num_nodes_unique() const { return nNodesUnique_; } /** return number of elements */ int num_elements() const { return nElts_; } /** return number of elements partitioned to my processor */ int my_num_elements() const { return myNElts_; } /** return number of integration points per element */ int num_ips_per_element() const; /** return number of nodes per element */ int num_nodes_per_element() const; /** return number of faces per element */ int num_faces_per_element() const; /** return number of nodes per face */ int num_nodes_per_face() const; /** return number of integration points per face */ int num_ips_per_face() const; /** return a pointer to the connectivity. This function will only work when mesh is not partitioned. */ Array2D<int> * connectivity(void) { return &connectivity_; } /** return a pointer to the connectivity */ DENS_MAT * coordinates(void) { return &nodalCoords_;} /** Engine nodeMap stuff */ Array<int> *node_map(void) { return &globalToUniqueMap_;} /** return scale in x,y,z */ double xscale() const { return xscale_; } double yscale() const { return yscale_; } double zscale() const { return zscale_; } /** local face connectivity */ const Array2D<int> & local_face_connectivity() const; /** element size in each direction */ virtual void bounding_box(const int ielem, DENS_VEC & xmin, DENS_VEC & xmax); /** element size in each direction */ virtual void element_size(const int ielem, double & hx, double & hy, double & hz); /** element size in each direction */ virtual double min_element_size(void) const {return 0.0 ;} /** get nodal coordinates for a given element */ void element_field(const int eltIdx, const DENS_MAT f, DENS_MAT &local_field) { int dof = f.nCols(); Array<int> nodes; element_connectivity_unique(eltIdx,nodes); local_field.reset(num_nodes_per_element(), dof); for (int i = 0; i < nodes.size(); i++) { for (int j = 0; j < dof; j++) local_field(i,j) = f(nodes(i),j); } } /** almost structured */ bool is_aligned(void) const; /** extruded */ bool is_two_dimensional(void) const {return twoDimensional_;} virtual double coordinate_tolerance(void) const {return 1.e-8;} /** element type */ std::string element_type(void) const ; /** output mesh subsets */ void output(std::string prefix) const; /* Parallelization data members */ /** return element vector for this processor */ const std::vector<int> & owned_elts() const { return myElts_; } const std::vector<int> & owned_and_ghost_elts() const { return (decomposition_) ? myAndGhostElts_: myElts_; } bool is_owned_elt(int elt) const; protected: + void parse_plane(int & argIdx, int narg, char ** arg, + int & ndir, int * idir, int & isgn, double xlimits[][2]); + + void parse_units(int & argIdx, int narg, char ** arg, + double & xmin, double & xmax, double & ymin, double & ymax, double & zmin, double & zmax); + /** will this mesh use data decomposition? */ bool decomposition_; /** should the mesh use the native lammps partitioning? */ bool lammpsPartition_; /** is the element/node data currently partitioned among processors? */ bool partitioned_; /** number of spatial dimensions */ int nSD_; /** number of elements */ int nElts_; /** number of elements partitioned to this processor */ int myNElts_; /** number of nodes */ int nNodes_; int nNodesUnique_; /** periodicity flags */ Array<bool> periodicity_; /** element type for this mesh */ FE_Element *feElement_; /** Nodal coordinates: nodalCoords_(nsd, numnode) */ DENS_MAT nodalCoords_; /** Element connectivity: connectivity_(neltnode, nelt) */ Array2D<int> connectivity_; Array2D<int> myConnectivity_; Array2D<int> connectivityUnique_; Array2D<int> myConnectivityUnique_; /** map from unique node id to associated elements for periodic meshes */ /** this data structure is only ever accessed from an unpartitioned context */ Array<std::vector<int> > uniqueNodeToElementMap_; /** map of global to unique node ID's */ Array<int> globalToUniqueMap_; Array<int> compactRemap_; // for condensing unique numbering /** map of unique to global node ID's */ Array<int> uniqueToGlobalMap_; /** map of string names to node sets */ NODE_SET_MAP nodeSetMap_; /** maximal nodeset */ std::set<int> nodeSetAll_; /** map of string names to node sets */ FACE_SET_MAP faceSetMap_; /** map of string names to element sets */ ELEMENT_SET_MAP elementSetMap_; /** maximal elementset */ std::set<int> elementSetAll_; /** length scaling used by lammps */ double xscale_, yscale_, zscale_; /** Processor demarcations */ std::vector<double> procs_; /** Dimension (x=0, y=1, or z=2) */ int partitionAxis_; /** List of nodes for this processor */ std::vector<int> myNodes_; /** List of elements for this processor */ std::vector<int> myElts_; std::vector<int> myAndGhostElts_; /** maps between my IDs and the total IDs */ std::map<int,int> elemToMyElemMap_; /** Lists of ghost nodes/neighbor ghost nodes */ std::vector<int> ghostNodesL_; std::vector<int> ghostNodesR_; std::vector<int> shareNodesL_; std::vector<int> shareNodesR_; /** extruded */ bool twoDimensional_; bool hasPlanarFaces_; double coordTol_; }; /** * @class FE_3DMesh * @brief Derived class for an unstructured 3D mesh */ class FE_3DMesh : public FE_Mesh { public: /** constructor */ FE_3DMesh(){}; /** constructor for read-in mesh **/ // can later be extended to take nodesets, elementsets, etc. FE_3DMesh(const std::string elementType, const int nNodes, const int nElements, const Array2D<int> *connectivity, const DENS_MAT *nodalCoordinates, const Array<bool> periodicity, const Array<std::pair<std::string,std::set<int> > > *nodeSets); /** destructor */ virtual ~FE_3DMesh(); void partition_mesh(void); void departition_mesh(void); void lammps_partition_mesh(void); /** Removes duplicate elements that appear in more than one vector within procEltLists. **/ void prune_duplicate_elements(std::vector<std::vector<int> > &procEltLists, int *eltToOwners); /** Takes procEltLists, and if there are more than nProcs of them it takes the extra elements and distributes them to other vectors in procEltLists. */ // processors if during pruning processors end up // elementless. This is slightly complicated because of // ghost nodes. void redistribute_extra_proclists(std::vector<std::vector<int> > &procEltLists, int *eltToOwners, int nProcs); /** This takes in a dense matrix and a list of elements and fills in a standard adjacency list (within the matrix) for those elements. **/ // the set intersection, which does redundant computations // right now, and filling in the adjacencies for both elements // simultaneously when two elements share a face. void compute_face_adjacencies(const std::list<int> &elts, DENS_MAT &faceAdjacencies); /** Counts the number of nonempty vectors in a vector of vectors. **/ int numNonempty(std::vector<std::vector<int> > &v); /** In the partitioning, we want to sort vectors of integers by size, and furthermore we want empty vectors to count as the "largest" possible vector because they dont want to count in the minimum. **/ struct vectorComparer { bool operator() (std::vector<int> l, std::vector<int> r) { if (l.empty()) return false; if (r.empty()) return true; return (l.size() < r.size()); } } vectorCompSize; virtual double min_element_size(void) const {return minEltSize_; } virtual double coordinate_tolerance(void) const { return 0.25*(this->min_element_size()); // loose //return 0.5; } virtual void cut_mesh(const std::set<PAIR> &faceSet, const std::set<int> &nodeSet); virtual void delete_elements(const std::set<int> &elementSet); /** map spatial location to element */ virtual int map_to_element(const DENS_VEC &x) const; /** sends out data to processors during partitioning */ void distribute_mesh_data(); protected: /** create global-to-unique node mapping */ virtual void setup_periodicity(double tol = 1.e-8); void fix_periodicity (int idim); int find_boundary_nodes(int idim, std::set<int> & nodes); bool match_nodes(int idim, std::set<int> & top, std::set<int> & bot, Array<int> & map); void set_unique_connectivity(void); bool orient(int idir); double minEltSize_; KD_Tree *tree_; /** test if a specified element actually contains the given point */ bool contains_point(const int eltID, const DENS_VEC & x) const; private: Array<std::vector<int> > nodeToParentElements_; }; /** * @class FE_Rectangular3DMesh * @brief Derived class for a structured mesh with * variable element sizes in x, y, and z directions */ class FE_Rectangular3DMesh : public FE_3DMesh { public: /** constructor */ FE_Rectangular3DMesh(){}; FE_Rectangular3DMesh( const Array<double> & hx, const Array<double> & hy, const Array<double> & hz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array<bool> periodicity, const double xscale=1, const double yscale=1, const double zscale=1); /** destructor */ virtual ~FE_Rectangular3DMesh() {}; void partition_mesh(void); void departition_mesh(void); /** map spatial location to element */ virtual int map_to_element(const DENS_VEC &x) const; protected: /** Number of elements in each spatial direction */ int n_[3]; /** Bounds of region on which mesh is defined */ double borders_[2][3]; /** Region size in each direction */ double L_[3]; /** create global-to-unique node mapping */ virtual void setup_periodicity(); // note no "tol" private: // only used by this class /** partitions in x,y,z */ Array<double> hx_, hy_, hz_; /** nodal locations */ std::vector< Array<double> > x_; }; /** * @class FE_Uniform3DMesh * @brief Derived class for a uniform structured mesh with * fixed element sizes in x, y, and z directions */ class FE_Uniform3DMesh : public FE_Rectangular3DMesh { public: /** constructor */ FE_Uniform3DMesh(const int nx, const int ny, const int nz, const double xmin, const double xmax, const double ymin, const double ymax, const double zmin, const double zmax, const Array<bool> periodicity, const double xscale=1, const double yscale=1, const double zscale=1); /** destructor */ virtual ~FE_Uniform3DMesh(); void partition_mesh(void); void departition_mesh(void); virtual void element_size(const int ielem, double hx, double hy, double hz) { hx = L_[0]/n_[0]; hy = L_[1]/n_[1]; hz = L_[2]/n_[2]; } virtual double min_element_size(void) const { return std::min(L_[0]/n_[0], std::min(L_[1]/n_[1], L_[2]/n_[2])); } /** map spatial location to element */ virtual int map_to_element(const DENS_VEC &x) const; private: // only used by this class /** Element size in each direction */ double dx_[3]; }; }; // namespace ATC_Transfer #endif // FE_MESH_H diff --git a/lib/atc/FieldEulerIntegrator.cpp b/lib/atc/FieldEulerIntegrator.cpp index 2375a33ff..eeea0cd4e 100644 --- a/lib/atc/FieldEulerIntegrator.cpp +++ b/lib/atc/FieldEulerIntegrator.cpp @@ -1,102 +1,153 @@ #include "FieldEulerIntegrator.h" #include "ATC_Coupling.h" #include "FE_Engine.h" #include "PhysicsModel.h" -#include "GMRES.h" -#include "CG.h" +#include "PrescribedDataManager.h" +//#include "GMRES.h" +//#include "CG.h" #include "ImplicitSolveOperator.h" +#include "MatrixDef.h" +#include "LinearSolver.h" namespace ATC { // ==================================================================== // FieldEulerIntegrator // ==================================================================== FieldEulerIntegrator::FieldEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask // copy ) : atc_(atc), feEngine_(feEngine), physicsModel_(physicsModel), fieldName_(fieldName), rhsMask_(rhsMask) { nNodes_ = feEngine->num_nodes(); } // ==================================================================== // FieldImplicitIntegrator // ==================================================================== FieldExplicitEulerIntegrator::FieldExplicitEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask // copy ) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atc,rhsMask) { } // -------------------------------------------------------------------- // update // -------------------------------------------------------------------- void FieldExplicitEulerIntegrator::update(const double dt, double time, FIELDS & fields, FIELDS & rhs) { // write and add update mass matrix to handled time variation // update mass matrix to be consistent/lumped, and handle this in apply_inverse_mass_matrix atc_->compute_rhs_vector(rhsMask_, fields, rhs, FULL_DOMAIN, physicsModel_); DENS_MAT & myRhs(rhs[fieldName_].set_quantity()); atc_->apply_inverse_mass_matrix(myRhs,fieldName_); fields[fieldName_] += dt*myRhs; } // ==================================================================== // FieldImplicitEulerIntegrator // ==================================================================== FieldImplicitEulerIntegrator::FieldImplicitEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask, // copy const double alpha ) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atc,rhsMask), alpha_(alpha), dT_(1.0e-6), maxRestarts_(50), maxIterations_(1000), tol_(1.0e-8) { } // -------------------------------------------------------------------- // update // -------------------------------------------------------------------- void FieldImplicitEulerIntegrator::update(const double dt, double time, FIELDS & fields, FIELDS & rhs) { // solver handles bcs - FieldImplicitSolveOperator solver(atc_, feEngine_, + FieldImplicitSolveOperator solver(atc_, fields, fieldName_, rhsMask_, physicsModel_, time, dt, alpha_); - DiagonalMatrix<double> preconditioner = solver.preconditioner(fields); - DENS_VEC myRhs = solver.rhs(); + DiagonalMatrix<double> preconditioner = solver.preconditioner(); + DENS_VEC rT = solver.r(); DENS_VEC dT(nNodes_); dT = dT_; DENS_MAT H(maxRestarts_+1, maxRestarts_); double tol = tol_; // tol returns the residual int iterations = maxIterations_; // iterations returns number of iterations int restarts = maxRestarts_; int convergence = GMRES(solver, - dT, myRhs, preconditioner, H, restarts, iterations, tol); + dT, rT, preconditioner, H, restarts, iterations, tol); if (convergence != 0) { throw ATC_Error(field_to_string(fieldName_) + " evolution did not converge"); } - fields[fieldName_] += dT; - rhs[fieldName_] = myRhs; + solver.solution(dT,fields[fieldName_].set_quantity()); } +// ==================================================================== +// FieldImplicitDirectEulerIntegrator +// ==================================================================== +FieldImplicitDirectEulerIntegrator::FieldImplicitDirectEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Coupling * atc, + const Array2D< bool > & rhsMask, // copy + const double alpha +) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atc,rhsMask), + alpha_(alpha),solver_(NULL) +{ + rhsMask_(fieldName_,FLUX) = false; // handle laplacian term with stiffness + const BC_SET & bcs = (atc_->prescribed_data_manager()->bcs(fieldName_))[0]; + solver_ = new LinearSolver(_lhsMK_,bcs); + solver_->allow_reinitialization(); +} +FieldImplicitDirectEulerIntegrator::~FieldImplicitDirectEulerIntegrator() +{ + if (solver_) delete solver_; +} + +// -------------------------------------------------------------------- +// initialize +// -------------------------------------------------------------------- +void FieldImplicitDirectEulerIntegrator::initialize(const double dt, double time, + FIELDS & fields) +{ + std::pair<FieldName,FieldName> p(fieldName_,fieldName_); + Array2D <bool> rmask = atc_->rhs_mask(); + rmask(fieldName_,FLUX) = true; + atc_->tangent_matrix(p,rmask,physicsModel_,_K_); + _lhsMK_ = (1./dt)*(_M_)- alpha_*(_K_); + _rhsMK_ = (1./dt)*(_M_)+(1.+alpha_)*(_K_); +} +// -------------------------------------------------------------------- +// update +// -------------------------------------------------------------------- +void FieldImplicitDirectEulerIntegrator::update(const double dt, double time, + FIELDS & fields, FIELDS & rhs) +{ + atc_->compute_rhs_vector(rhsMask_, fields, rhs, + FULL_DOMAIN, physicsModel_); + CLON_VEC myRhs = column( rhs[fieldName_].set_quantity(),0); + CLON_VEC myField = column(fields[fieldName_].set_quantity(),0); + myRhs += _rhsMK_*myField; // f = (1/dt M + (1+alpha) K) T + f + solver_->solve(myField,myRhs); // (1/dt M -alpha K)^-1 f +} } // namespace ATC diff --git a/lib/atc/FieldEulerIntegrator.h b/lib/atc/FieldEulerIntegrator.h index 174f37435..f349ada5d 100644 --- a/lib/atc/FieldEulerIntegrator.h +++ b/lib/atc/FieldEulerIntegrator.h @@ -1,138 +1,183 @@ #ifndef FIELD_EULER_INTEGRATOR_H #define FIELD_EULER_INTEGRATOR_H // ATC includes #include "Array2D.h" #include "MatrixLibrary.h" #include "PhysicsModel.h" #include "TimeIntegrator.h" #include "ImplicitSolveOperator.h" // other includes #include <vector> #include <map> namespace ATC { // Forward class declarations class ATC_Coupling; class FE_Engine; /** * @class FieldEulerIntegrator * @brief method for integrating fast fields */ class FieldEulerIntegrator { public: /** Constructor */ FieldEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask // copy ); /** Destructor */ virtual ~FieldEulerIntegrator() {}; + /** initialize */ + virtual void initialize(const double dt, const double time, + FIELDS & fields) {}; + /** update */ virtual void update(const double dt, const double time, FIELDS & fields, FIELDS & rhs) = 0; protected: /** Pointer to ATC_Tranfer */ ATC_Coupling * atc_; /** Pointer to FE_Engine */ /*const*/ FE_Engine * feEngine_; /** Pointer to PhysicsModel */ const PhysicsModel * physicsModel_; /** field name */ FieldName fieldName_; /** rhs mask */ Array2D <bool> rhsMask_; /** number of nodes */ int nNodes_; }; /** * @class FieldExplicitEulerIntegrator * @brief explicit Euler method for integrating fast electron fields */ class FieldExplicitEulerIntegrator : public FieldEulerIntegrator { public: /** Constructor */ FieldExplicitEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask // copy ); /** Destructor */ virtual ~FieldExplicitEulerIntegrator() {}; /** update */ void update(const double dt, const double time, FIELDS & fields, FIELDS & rhs); }; /** * @class FieldImplicitEulerIntegrator * @brief explicit Euler method for integrating fast electron fields */ class FieldImplicitEulerIntegrator : public FieldEulerIntegrator { public: /** Constructor */ FieldImplicitEulerIntegrator( const FieldName fieldName, const PhysicsModel * physicsModel, /*const*/ FE_Engine * feEngine, /*const*/ ATC_Coupling * atc, const Array2D< bool > & rhsMask, // copy const double alpha = 0.5 // default to trap/midpt ); /** Destructor */ virtual ~FieldImplicitEulerIntegrator() {}; /** update */ void update(const double dt, const double time, FIELDS & fields, FIELDS & rhs); protected: /** euler update factor */ double alpha_; /** perturbation */ double dT_; /** max number of restarts = size of basis */ int maxRestarts_; /** max number of iterations */ int maxIterations_; /** convergence tolerance */ double tol_; }; +/** + * @class FieldImplicitDirectEulerIntegrator + * @brief implicit Euler method with direct solve + */ +class FieldImplicitDirectEulerIntegrator : public FieldEulerIntegrator { + + public: + + /** Constructor */ + FieldImplicitDirectEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Coupling * atc, + const Array2D< bool > & rhsMask, // copy + const double alpha = 0.5 // default to trap/midpt + ); + + /** Destructor */ + virtual ~FieldImplicitDirectEulerIntegrator(); + + /** initalize - init the matrices and inverses */ + void initialize(const double dt, const double time, + FIELDS & fields); + + /** update */ + void update(const double dt, const double time, + FIELDS & fields, FIELDS & rhs); + + protected: + /** euler update factor */ + double alpha_; + + /** matrices */ + SPAR_MAT _M_; + SPAR_MAT _K_; + SPAR_MAT _lhsMK_; + SPAR_MAT _rhsMK_; + class LinearSolver * solver_; +}; + } // namespace ATC #endif diff --git a/lib/atc/FieldManager.cpp b/lib/atc/FieldManager.cpp index 9a74fc22c..ce9f85953 100644 --- a/lib/atc/FieldManager.cpp +++ b/lib/atc/FieldManager.cpp @@ -1,548 +1,549 @@ #include "FieldManager.h" #include "ATC_Method.h" #include "LammpsInterface.h" #include "PerAtomQuantity.h" #include "TransferOperator.h" using std::string; namespace ATC { typedef PerAtomQuantity<double> PAQ; //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- FieldManager::FieldManager(ATC_Method * atc): atc_(atc), interscaleManager_(atc->interscale_manager()) {}; //----------------------------------------------------------------------------- //* restricted_atom_quantity //----------------------------------------------------------------------------- DENS_MAN * FieldManager::restricted_atom_quantity(FieldName field, string name, PAQ * atomicQuantity) { if (name == "default") { name = field_to_restriction_name(field); } DENS_MAN * quantity = interscaleManager_.dense_matrix(name); if (!quantity){ if (field == CHARGE_DENSITY) { atomicQuantity = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE); } else if (field == MASS_DENSITY) { atomicQuantity = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); } else { if (!atomicQuantity) { throw ATC_Error("FieldManager::restricted_atom_quantity - need to supply PAQ if restricted quantity does not already exist"); } } quantity = new AtfShapeFunctionRestriction(atc_,atomicQuantity,atc_->accumulant()); interscaleManager_.add_dense_matrix(quantity,name); } return quantity; } //----------------------------------------------------------------------------- //* restricted_atom_quantity //----------------------------------------------------------------------------- DENS_MAN * FieldManager::projected_atom_quantity(FieldName field,string name, PAQ * atomic, DIAG_MAN * normalization) { if (atc_->use_md_mass_normalization()) { if (name == "default") { name = field_to_intrinsic_name(field); } DENS_MAN * quantity = interscaleManager_.dense_matrix(name); if (!quantity) { DENS_MAN * restricted = restricted_atom_quantity(field,field_to_restriction_name(field),atomic); quantity = new AtfShapeFunctionMdProjection(atc_,restricted,use_mass_matrix(field)); interscaleManager_.add_dense_matrix(quantity,name); } return quantity; } else { if (name == "default") { name = field_to_string(field); } DENS_MAN * quantity = interscaleManager_.dense_matrix(name); if (quantity) return quantity; if (atc_->kernel_on_the_fly()) { if (atc_->kernel_based()) { quantity = new OnTheFlyKernelAccumulationNormalized(atc_, atomic, atc_->kernel_function(), atc_->atom_coarsegraining_positions(), normalization); } else { quantity = new OnTheFlyMeshAccumulationNormalized(atc_, atomic, atc_->atom_coarsegraining_positions(), normalization); } } else { quantity = new AtfProjection(atc_, atomic, atc_->accumulant(), normalization); } interscaleManager_.add_dense_matrix(quantity,name); return quantity; } } //----------------------------------------------------------------------------- //* referenced_projected_atom_quantity //----------------------------------------------------------------------------- DENS_MAN * FieldManager::referenced_projected_atom_quantity(FieldName field,string name, PAQ * atomic, DENS_MAN * reference, DIAG_MAN * normalization) { if (name == "default") { name = field_to_string(field); } DENS_MAN * quantity = interscaleManager_.dense_matrix(name); if (quantity) return quantity; if (atc_->use_md_mass_normalization()) { DENS_MAN * restricted = restricted_atom_quantity(field,field_to_restriction_name(field),atomic); quantity = new AtfShapeFunctionMdProjectionReferenced(atc_,restricted,reference,use_mass_matrix(field)); } else if (atc_->kernel_on_the_fly()) { if (atc_->kernel_based()) { quantity = new OnTheFlyKernelAccumulationNormalizedReferenced(atc_, atomic, atc_->kernel_function(), atc_->atom_coarsegraining_positions(), normalization, reference); } else { quantity = new OnTheFlyMeshAccumulationNormalizedReferenced(atc_, atomic, atc_->atom_coarsegraining_positions(), normalization, reference); } } else { quantity = new AtfProjectionReferenced(atc_, atomic, atc_->accumulant(), reference, normalization); } interscaleManager_.add_dense_matrix(quantity,name); return quantity; } //----------------------------------------------------------------------------- //* scaled_projected_atom_quantity //----------------------------------------------------------------------------- DENS_MAN * FieldManager::scaled_projected_atom_quantity(FieldName field,string name, PAQ * atomic, double scale, DIAG_MAN * normalization) { if (name == "default") { name = field_to_string(field); } DENS_MAN * quantity = interscaleManager_.dense_matrix(name); if (quantity) return quantity; if (atc_->use_md_mass_normalization()) { DENS_MAN * restricted = restricted_atom_quantity(field,field_to_restriction_name(field),atomic); quantity = new AtfShapeFunctionMdProjectionScaled(atc_,restricted,scale,use_mass_matrix(field)); } else if (atc_->kernel_on_the_fly()) { if (atc_->kernel_based()) { quantity = new OnTheFlyKernelAccumulationNormalizedScaled(atc_, atomic, atc_->kernel_function(), atc_->atom_coarsegraining_positions(), normalization, scale); } else { quantity = new OnTheFlyMeshAccumulationNormalizedScaled(atc_, atomic, atc_->atom_coarsegraining_positions(), normalization, scale); } } else { quantity = new AtfProjectionScaled(atc_, atomic, atc_->accumulant(), scale, normalization); } interscaleManager_.add_dense_matrix(quantity,name); return quantity; } //----------------------------------------------------------------------------- //* CHARGE_DENSITY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::charge_density(string name) { FundamentalAtomQuantity * atomic = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE); return projected_atom_quantity(CHARGE_DENSITY,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* MASS_DENSITY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::mass_density(string name) { FundamentalAtomQuantity * atomic = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); return projected_atom_quantity(MASS_DENSITY,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* SPECIES_CONCENTRATION //----------------------------------------------------------------------------- DENS_MAN * FieldManager::species_concentration(string name) { PerAtomQuantity<double> * atomTypeVector = interscaleManager_.per_atom_quantity("atom_species_vector"); if (!atomTypeVector) { atomTypeVector = new AtomTypeVector(atc_,atc_->type_list(),atc_->group_list()); interscaleManager_.add_per_atom_quantity(atomTypeVector,"atom_species_vector"); } return projected_atom_quantity(SPECIES_CONCENTRATION,name,atomTypeVector,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* NUMBER_DENSITY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::number_density(string name) { PAQ * atomic = interscaleManager_.per_atom_quantity("atomNumber"); if (!atomic) { atomic = new AtomNumber(atc_); interscaleManager_.add_per_atom_quantity(atomic, "atomNumber"); } return projected_atom_quantity(NUMBER_DENSITY,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* MOMENTUM //----------------------------------------------------------------------------- DENS_MAN * FieldManager::momentum(string name) { PAQ * atomic = interscaleManager_.per_atom_quantity("atomMomentum"); if (!atomic) { atomic = new AtomicMomentum(atc_); interscaleManager_.add_per_atom_quantity(atomic, "atomMomentum"); } return projected_atom_quantity(MOMENTUM,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* VELOCITY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::velocity(string name) { if (name == "default") { name = field_to_string(VELOCITY); } DENS_MAN * v = interscaleManager_.dense_matrix(name); if (v) return v; if (atc_->use_md_mass_normalization()) { PAQ * atomic = interscaleManager_.per_atom_quantity("atomMomentum"); if (!atomic) { atomic = new AtomicMomentum(atc_); interscaleManager_.add_per_atom_quantity(atomic, "atomMomentum"); } DENS_MAN * restricted = restricted_atom_quantity(VELOCITY,field_to_restriction_name(VELOCITY),atomic); v = new AtfShapeFunctionMdProjection(atc_,restricted,VELOCITY); } else { DENS_MAN* p = interscaleManager_.dense_matrix(field_to_string(MOMENTUM)); if (!p) p = nodal_atomic_field(MOMENTUM); DENS_MAN* m = interscaleManager_.dense_matrix(field_to_string(MASS_DENSITY)); if (!m) m = nodal_atomic_field(MASS_DENSITY); v = new DenseMatrixQuotient(p,m); } interscaleManager_.add_dense_matrix(v,field_to_string(VELOCITY)); return v; } //----------------------------------------------------------------------------- //* PROJECTED_VELOCITY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::projected_velocity(string name) { FundamentalAtomQuantity * atomic = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); return projected_atom_quantity(PROJECTED_VELOCITY,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* DISPLACEMENT //----------------------------------------------------------------------------- DENS_MAN * FieldManager::displacement(string name) { if (name == "default") { name = field_to_string(DISPLACEMENT); } DENS_MAN * u = interscaleManager_.dense_matrix(name); if (u) return u; PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicMassWeightedDisplacement"); if (!atomic) { FundamentalAtomQuantity * atomMasses = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); FundamentalAtomQuantity * atomPositions = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION); atomic = new AtomicMassWeightedDisplacement(atc_,atomPositions, atomMasses, atc_->atom_reference_positions(), INTERNAL); interscaleManager_.add_per_atom_quantity(atomic,"AtomicMassWeightedDisplacement"); } if (atc_->use_md_mass_normalization()) { DENS_MAN * restricted = restricted_atom_quantity(DISPLACEMENT,field_to_restriction_name(DISPLACEMENT),atomic); u = new AtfShapeFunctionMdProjection(atc_,restricted,VELOCITY); } else { DENS_MAN * q = NULL; if (atc_->kernel_on_the_fly()) { if (atc_->kernel_based()) { q = new OnTheFlyKernelAccumulationNormalized(atc_, atomic, atc_->kernel_function(), atc_->atom_coarsegraining_positions(), atc_->accumulant_inverse_volumes()); } else { q = new OnTheFlyMeshAccumulationNormalized(atc_, atomic, atc_->atom_coarsegraining_positions(), atc_->accumulant_inverse_volumes()); } } else { q = new AtfProjection(atc_, atomic, atc_->accumulant(), atc_->accumulant_inverse_volumes()); } interscaleManager_.add_dense_matrix(q,"CoarseGrainedAMWD"); DENS_MAN* m = interscaleManager_.dense_matrix(field_to_string(MASS_DENSITY)); u = new DenseMatrixQuotient(q,m); } interscaleManager_.add_dense_matrix(u,name); return u; } //----------------------------------------------------------------------------- //* REFERENCE_POTENTIAL_ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::reference_potential_energy(string name) { DENS_MAN * rpe = interscaleManager_.dense_matrix(field_to_string(REFERENCE_POTENTIAL_ENERGY)); if (! rpe ) { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicReferencePotential"); if (!atomic) { atomic = new AtcAtomQuantity<double>(atc_); interscaleManager_.add_per_atom_quantity(atomic, "AtomicReferencePotential"); atomic->set_memory_type(PERSISTENT); } SPAR_MAN * referenceAccumulant = interscaleManager_.sparse_matrix("ReferenceAccumulant"); if (!referenceAccumulant) { referenceAccumulant = new SPAR_MAN(); referenceAccumulant->set_memory_type(PERSISTENT); interscaleManager_.add_sparse_matrix(referenceAccumulant, "ReferenceAccumulant"); } DIAG_MAN * referenceAccumulantInverseVolumes = interscaleManager_.diagonal_matrix("ReferenceAccumulantInverseVolumes"); if (!referenceAccumulantInverseVolumes) { referenceAccumulantInverseVolumes = new DIAG_MAN(); referenceAccumulantInverseVolumes->set_memory_type(PERSISTENT); interscaleManager_.add_diagonal_matrix(referenceAccumulantInverseVolumes, "ReferenceAccumulantInverseVolumes"); } rpe = new AtfProjection(atc_, atomic, referenceAccumulant, referenceAccumulantInverseVolumes); interscaleManager_.add_dense_matrix(rpe,field_to_string(REFERENCE_POTENTIAL_ENERGY)); rpe->set_memory_type(PERSISTENT); } return rpe; } //----------------------------------------------------------------------------- //* POTENTIAL_ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::potential_energy(string name) { PerAtomQuantity<double> * atomic = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy"); if (!atomic) { atomic = new ComputedAtomQuantity(atc_,(atc_->lammps_interface())->compute_pe_name(), atc_->pe_scale()); interscaleManager_.add_per_atom_quantity(atomic,"AtomicPotentialEnergy"); } DENS_MAN * reference = interscaleManager_.dense_matrix(field_to_string(REFERENCE_POTENTIAL_ENERGY)); if (reference) { return referenced_projected_atom_quantity(POTENTIAL_ENERGY,name,atomic,reference,atc_->accumulant_inverse_volumes()); } else { return projected_atom_quantity(POTENTIAL_ENERGY,name,atomic,atc_->accumulant_inverse_volumes()); } } //----------------------------------------------------------------------------- //* TEMPERATURE //----------------------------------------------------------------------------- DENS_MAN * FieldManager::temperature(string name) { double Tcoef = 1./((atc_->nsd())*(atc_->lammps_interface())->kBoltzmann()); PAQ * atomic = per_atom_quantity("AtomicTwiceFluctuatingKineticEnergy"); return scaled_projected_atom_quantity(TEMPERATURE,name,atomic,Tcoef,atc_->accumulant_weights()); } //----------------------------------------------------------------------------- //* KINETIC_TEMPERATURE //----------------------------------------------------------------------------- DENS_MAN * FieldManager::kinetic_temperature(string name) { double Tcoef = 1./((atc_->nsd())*(atc_->lammps_interface())->kBoltzmann()); PAQ * atomic = per_atom_quantity("AtomicTwiceKineticEnergy"); return scaled_projected_atom_quantity(KINETIC_TEMPERATURE,name,atomic,Tcoef,atc_->accumulant_weights()); } //----------------------------------------------------------------------------- //* THERMAL_ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::thermal_energy(string name) { double Ecoef = 0.5*atc_->ke_scale(); PAQ * atomic = per_atom_quantity("AtomicTwiceFluctuatingKineticEnergy"); return scaled_projected_atom_quantity(THERMAL_ENERGY,name,atomic,Ecoef,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* KINETIC_ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::kinetic_energy(string name) { double Ecoef = 0.5*atc_->ke_scale(); PAQ * atomic = per_atom_quantity("AtomicTwiceKineticEnergy"); return scaled_projected_atom_quantity(KINETIC_ENERGY,name,atomic,Ecoef,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* CHARGE_FLUX //----------------------------------------------------------------------------- DENS_MAN * FieldManager::charge_flux(string name) { PAQ * atomic = per_atom_quantity("AtomicChargeVelocity"); return projected_atom_quantity(CHARGE_FLUX,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* SPECIES_FLUX //----------------------------------------------------------------------------- DENS_MAN * FieldManager::species_flux(string name) { PAQ * atomic = per_atom_quantity("AtomicSpeciesVelocity"); return projected_atom_quantity(SPECIES_FLUX,name,atomic,atc_->accumulant_inverse_volumes()); } //----------------------------------------------------------------------------- //* INTERNAL_ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::internal_energy(string name) { if (name == "default") { name = field_to_string(INTERNAL_ENERGY); } DENS_MAN * te = thermal_energy(field_to_string(THERMAL_ENERGY)); DENS_MAN * pe = potential_energy(field_to_string(POTENTIAL_ENERGY)); DenseMatrixSum * ie = new DenseMatrixSum(te,pe); interscaleManager_.add_dense_matrix(ie,name); return ie; } //----------------------------------------------------------------------------- //* ENERGY //----------------------------------------------------------------------------- DENS_MAN * FieldManager::energy(string name) { if (name == "default") { name = field_to_string(ENERGY); } DENS_MAN * ke = kinetic_energy(field_to_string(KINETIC_ENERGY)); DENS_MAN * pe = potential_energy(field_to_string(POTENTIAL_ENERGY)); DenseMatrixSum * e = new DenseMatrixSum(ke,pe); interscaleManager_.add_dense_matrix(e,name); return e; } //============================================================================= //* PER ATOM QUANTITIES //============================================================================= //----------------------------------------------------------------------------- //* 2 KE ' //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_twice_fluctuating_kinetic_energy() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicTwiceFluctuatingKineticEnergy"); if (!atomic) { FundamentalAtomQuantity * atomMass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); FundamentalAtomQuantity * atomVelocity = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); PAQ * vbar = per_atom_quantity(field_to_prolongation_name(VELOCITY)); atomic = new TwiceFluctuatingKineticEnergy(atc_,atomVelocity,atomMass,vbar); interscaleManager_.add_per_atom_quantity(atomic, "AtomicTwiceFluctuatingKineticEnergy"); } return atomic; } //----------------------------------------------------------------------------- //* 2 KE //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_twice_kinetic_energy() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicTwiceKineticEnergy"); if (!atomic) { FundamentalAtomQuantity * atomMass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); FundamentalAtomQuantity * atomVelocity = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); atomic = new TwiceKineticEnergy(atc_,atomVelocity,atomMass); interscaleManager_.add_per_atom_quantity(atomic, "AtomicTwiceKineticEnergy"); } return atomic; } //----------------------------------------------------------------------------- //* v' //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_fluctuating_velocity() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicFluctuatingVelocity"); if (!atomic) { FundamentalAtomQuantity * atomVelocity = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); PAQ * atomMeanVelocity = per_atom_quantity(field_to_prolongation_name(VELOCITY)); atomic = new FluctuatingVelocity(atc_,atomVelocity,atomMeanVelocity); interscaleManager_.add_per_atom_quantity(atomic, "AtomicFluctuatingVelocity"); } return atomic; } //----------------------------------------------------------------------------- //* q v' //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_charge_velocity() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicChargeVelocity"); if (!atomic) { PAQ * atomVelocity = atomic_fluctuating_velocity(); FundamentalAtomQuantity * atomCharge = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE); atomic = new ChargeVelocity(atc_,atomVelocity,atomCharge); interscaleManager_.add_per_atom_quantity(atomic, "AtomicChargeVelocity"); } return atomic; } //----------------------------------------------------------------------------- //* m^a v' //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_species_velocity() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicSpeciesVelocity"); if (!atomic) { PAQ * atomVelocity = atomic_fluctuating_velocity(); PAQ * atomSpecies = atomic_species_vector(); atomic = new SpeciesVelocity(atc_,atomVelocity,atomSpecies); interscaleManager_.add_per_atom_quantity(atomic, "AtomicSpeciesVelocity"); } return atomic; } //----------------------------------------------------------------------------- //* [0 1 0 0 ] for type 2 atom //----------------------------------------------------------------------------- PAQ * FieldManager::atomic_species_vector() { PAQ * atomic = interscaleManager_.per_atom_quantity("AtomicSpeciesVector"); if (!atomic) { atomic = new AtomTypeVector(atc_,atc_->type_list(),atc_->group_list()); interscaleManager_.add_per_atom_quantity(atomic,"AtomicSpeciesVector"); } return atomic; } //----------------------------------------------------------------------------- //* Prolonged coarse scale field //----------------------------------------------------------------------------- PAQ * FieldManager::prolonged_field(FieldName field) { PAQ * quantity = interscaleManager_.per_atom_quantity(field_to_prolongation_name(field)); if (!quantity) { + DENS_MAN * coarseQuantity = interscaleManager_.dense_matrix(field_to_string(field)); if (!coarseQuantity) coarseQuantity = nodal_atomic_field(field); if (!coarseQuantity) throw ATC_Error("can not prolong quantity: " + field_to_string(field) + " no field registered"); if (atc_->kernel_on_the_fly()) { quantity = new OnTheFlyShapeFunctionProlongation(atc_, coarseQuantity,atc_->atom_coarsegraining_positions()); } else { quantity = new FtaShapeFunctionProlongation(atc_, coarseQuantity,atc_->interpolant()); } interscaleManager_.add_per_atom_quantity(quantity, field_to_prolongation_name(field)); } return quantity; } } diff --git a/lib/atc/Function.cpp b/lib/atc/Function.cpp index 36d3e9a09..0bd759695 100644 --- a/lib/atc/Function.cpp +++ b/lib/atc/Function.cpp @@ -1,548 +1,554 @@ #include "Function.h" #include "ATC_Error.h" #include "LammpsInterface.h" #include <sstream> using std::stringstream; using std::string; using std::set; using std::fstream; namespace ATC { //==================================================================== // UXT_Function //=================================================================== UXT_Function::UXT_Function(int narg, double* args) { } //==================================================================== // UXT_Function_Mgr //==================================================================== UXT_Function_Mgr * UXT_Function_Mgr::myInstance_ = NULL; // ----------------------------------------------------------------- // instance() // ----------------------------------------------------------------- UXT_Function_Mgr * UXT_Function_Mgr::instance() { if (myInstance_ == NULL) { myInstance_ = new UXT_Function_Mgr(); } return myInstance_; } // Destructor UXT_Function_Mgr::~UXT_Function_Mgr() { // Delete all functions created using "new" set<UXT_Function * >::iterator it; for (it = pointerSet_.begin(); it != pointerSet_.end(); it++) if (*it) delete *it; } // add user function into the if statement and assign returnFunction to it UXT_Function* UXT_Function_Mgr::function(string & type, int nargs, double * args) { UXT_Function * returnFunction; if (type=="linear") { returnFunction = new ScalarLinearFunction(nargs,args); } else throw ATC_Error("Bad user function name"); pointerSet_.insert(returnFunction); return returnFunction; } // add user function into the if statement and assign returnFunction to it UXT_Function* UXT_Function_Mgr::function(char ** args, int nargs) { string type = args[0]; int narg = nargs -1; double dargs[narg]; for (int i = 0; i < narg; ++i) dargs[i] = atof(args[i+1]); return function(type, narg, dargs); } // add constant function UXT_Function* UXT_Function_Mgr::linear_function(double c0, double c1) { double args[2] = {c0,c1}; UXT_Function * returnFunction = new ScalarLinearFunction(2,args); pointerSet_.insert(returnFunction); return (returnFunction); } UXT_Function* UXT_Function_Mgr::copy_UXT_function(UXT_Function* other) { string tag = other->tag(); UXT_Function * returnFunction = NULL; if (tag=="linear") { ScalarLinearFunction * other_cast = (ScalarLinearFunction*) other; returnFunction = new ScalarLinearFunction(*other_cast); } pointerSet_.insert(returnFunction); return returnFunction; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // ScalarLinearFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- ScalarLinearFunction::ScalarLinearFunction(int narg, double* args) : UXT_Function(narg,args) { tag_ = "linear"; c0_ = args[0]; c1_ = args[1]; stringstream ss; ss << "created function : " << c0_ << " + " << c1_ << "*u"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // XT_Function //-------------------------------------------------------------------- //-------------------------------------------------------------------- XT_Function::XT_Function(int narg, double* args) { if (narg > 5 ) { x0[0] = args[0]; x0[1] = args[1]; x0[2] = args[2]; mask[0] = args[3]; mask[1] = args[4]; mask[2] = args[5]; } else { x0[0] = 0.0; x0[1] = 0.0; x0[2] = 0.0; mask[0] = 0.0; mask[1] = 0.0; mask[2] = 0.0; } } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // XT_Function_Mgr //-------------------------------------------------------------------- //-------------------------------------------------------------------- XT_Function_Mgr * XT_Function_Mgr::myInstance_ = NULL; // ----------------------------------------------------------------- // instance() // ----------------------------------------------------------------- XT_Function_Mgr * XT_Function_Mgr::instance() { if (myInstance_ == NULL) { myInstance_ = new XT_Function_Mgr(); } return myInstance_; } // Destructor XT_Function_Mgr::~XT_Function_Mgr() { // Delete all functions created using "new" set<XT_Function * >::iterator it; for (it = pointerSet_.begin(); it != pointerSet_.end(); it++) if (*it) delete *it; } // add user function into the if statement and assign returnFunction to it XT_Function* XT_Function_Mgr::function(string & type, int nargs, double * args) { XT_Function * returnFunction; if (type=="constant") { returnFunction = new ConstantFunction(nargs,args); } else if (type=="temporal_ramp") { returnFunction = new TemporalRamp(nargs,args); } else if (type=="linear") returnFunction = new LinearFunction(nargs,args); else if (type=="piecewise_linear") returnFunction = new PiecewiseLinearFunction(nargs,args); else if (type=="linear_temporal_ramp") returnFunction = new LinearTemporalRamp(nargs,args); else if (type=="quadratic") returnFunction = new QuadraticFunction(nargs,args); else if (type=="sine") returnFunction = new SineFunction(nargs,args); else if (type=="gaussian") returnFunction = new GaussianFunction(nargs,args); else if (type=="gaussian_temporal_ramp") returnFunction = new GaussianTemporalRamp(nargs,args); else if (type=="radial_power") returnFunction = new RadialPower(nargs,args); else throw ATC_Error("Bad user function name"); pointerSet_.insert(returnFunction); return returnFunction; } // add user function into the if statement and assign returnFunction to it XT_Function* XT_Function_Mgr::function(char ** args, int nargs) { string type = args[0]; int narg = nargs -1; double dargs[narg]; for (int i = 0; i < narg; ++i) dargs[i] = atof(args[i+1]); return function(type, narg, dargs); } // add constant function XT_Function* XT_Function_Mgr::constant_function(double c) { XT_Function * returnFunction = new ConstantFunction(c); pointerSet_.insert(returnFunction); return (returnFunction); } XT_Function* XT_Function_Mgr::copy_XT_function(XT_Function* other) { string tag = other->tag(); XT_Function * returnFunction = NULL; if (tag=="linear") { LinearFunction * other_cast = (LinearFunction*) other; returnFunction = new LinearFunction(*other_cast); } else if (tag=="piecewise_linear") { PiecewiseLinearFunction * other_cast = (PiecewiseLinearFunction*) other; returnFunction = new PiecewiseLinearFunction(*other_cast); } else if (tag=="quadratic") { QuadraticFunction * other_cast = (QuadraticFunction*) other; returnFunction = new QuadraticFunction(*other_cast); } else if (tag=="sine") { SineFunction * other_cast = (SineFunction*) other; returnFunction = new SineFunction(*other_cast); } else if (tag=="gaussian") { GaussianFunction * other_cast = (GaussianFunction*) other; returnFunction = new GaussianFunction(*other_cast); } else if (tag=="gaussian_temporal_ramp") { GaussianTemporalRamp * other_cast = (GaussianTemporalRamp*) other; returnFunction = new GaussianTemporalRamp(*other_cast); } else if (tag=="temporal_ramp") { TemporalRamp * other_cast = (TemporalRamp*) other; returnFunction = new TemporalRamp(*other_cast); } else if (tag=="radial_power") { RadialPower * other_cast = (RadialPower*) other; returnFunction = new RadialPower(*other_cast); } pointerSet_.insert(returnFunction); return returnFunction; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // ConstantFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- ConstantFunction::ConstantFunction(int narg, double* args) : XT_Function(narg,args), C0(args[0]) { tag_ = "constant"; } //-------------------------------------------------------------------- ConstantFunction::ConstantFunction(double arg) : XT_Function(1,&arg), C0(arg) { tag_ = "constant"; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // LinearFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- LinearFunction::LinearFunction(int narg, double* args) : XT_Function(narg,args) { C0 = args[6]; tag_ = "linear"; stringstream ss; ss << "created function : " << C0 << " + " << mask[0] << "(x-"<< x0[0] << ")+"<< mask[1] << "(y-"<<x0[1]<<")+"<<mask[2]<<"(z-"<<x0[2] << ")"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // PiecewiseLinearFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- PiecewiseLinearFunction::PiecewiseLinearFunction(int narg, double* args) : XT_Function(narg,args) { int i=0, idx = 6, n = (narg-idx)/2; xi.reset(n); fi.reset(n); while (idx < narg) { xi(i) = args[idx++]; fi(i++) = args[idx++]; } tag_ = "piecewise_linear"; + stringstream ss; + ss << "created piecewise function : " << n << " \n"; + for (i = 0; i < n; i++) { + ss << xi(i) << " " << fi(i) << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } double PiecewiseLinearFunction::f(double * x, double t) { double s = mask[0]*(x[0]-x0[0])+mask[1]*(x[1]-x0[1])+mask[2]*(x[2]-x0[2]); int index = xi.index(s); if (index < 0) return fi(0); else if (index >= xi.size()-1 ) return fi(xi.size()-1); else { double f = fi(index) + (fi(index+1)-fi(index))*(s-xi(index))/(xi(index+1)-xi(index)); return f; } } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // LinearTemporalRamp //-------------------------------------------------------------------- //-------------------------------------------------------------------- LinearTemporalRamp::LinearTemporalRamp(int narg, double* args) : XT_Function(narg,args) { double mask_final[3]; mask_final[0] = args[6]; mask_final[1] = args[7]; mask_final[2] = args[8]; C0_initial = args[9]; double C0_final = args[10]; double delta_t = args[11]; for (int i = 0; i < 3; i++) mask_slope[i] = (mask_final[i] - mask[i])/delta_t; C0_slope = (C0_initial - C0_final)/delta_t; } double LinearTemporalRamp::f(double* x, double t) { double slope[3]; for (int i = 0; i < 3; i++) slope[i] = mask[i] + mask_slope[i]*t; double C0 = C0_initial + C0_slope*t; return slope[0]*(x[0]-x0[0])+slope[1]*(x[1]-x0[1])+slope[2]*(x[2]-x0[2]) + C0; } double LinearTemporalRamp::dfdt(double* x, double t) { return mask_slope[0]*(x[0]-x0[0])+mask_slope[1]*(x[1]-x0[1])+mask_slope[2]*(x[2]-x0[2]) + C0_slope; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // QuadraticFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- QuadraticFunction::QuadraticFunction(int narg, double* args) : XT_Function(narg,args) { C0 = args[6]; C2[0] = args[7]; C2[1] = args[8]; C2[2] = args[9]; C2[3] = args[10]; C2[4] = args[11]; C2[5] = args[12]; tag_ = "quadratic"; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // SineFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- SineFunction::SineFunction(int narg, double* args) : XT_Function(narg,args) { C = args[6]; w = args[7]; C0 = args[8]; tag_ = "sine"; stringstream ss; ss << "created function : " << C << " sin( " << mask[0] << "(x-"<< x0[0] << ")+"<< mask[1] << "(y-"<<x0[1]<<")+"<<mask[2]<<"(z-"<<x0[2] << ") - " << w << "t ) + " << C0; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // GaussianFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- GaussianFunction::GaussianFunction(int narg, double* args) : XT_Function(narg,args) { tau = args[6]; C = args[7]; C0 = args[8]; tag_ = "gaussian"; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // GaussianTemporalRamp //-------------------------------------------------------------------- //-------------------------------------------------------------------- GaussianTemporalRamp::GaussianTemporalRamp(int narg, double* args) : GaussianFunction(narg,args) { tau_initial = args[9]; C_initial = args[10]; C0_initial = args[11]; double delta_t = args[12]; tau_slope = (tau - tau_initial)/delta_t; C_slope = (C - C_initial)/delta_t; C0_slope = (C0 - C0_initial)/delta_t; tag_ = "gaussian_temporal_ramp"; } double GaussianTemporalRamp::f(double* x, double t) { tau = tau_initial + tau_slope*t; C = C_initial + C_slope*t; C0 = C0_initial + C0_slope*t; return GaussianFunction::f(x,t); } double GaussianTemporalRamp::dfdt(double* x, double t) { tau = tau_initial + tau_slope*t; C = C_initial + C_slope*t; C0 = C0_initial + C0_slope*t; double dfdt = 0.; dfdt += C_slope*exp(-(mask[0]*(x[0]-x0[0])*(x[0]-x0[0]) +mask[1]*(x[1]-x0[1])*(x[1]-x0[1]) +mask[2]*(x[2]-x0[2])*(x[2]-x0[2])) /tau/tau); dfdt += C*exp(2.*tau_slope*(mask[0]*(x[0]-x0[0])*(x[0]-x0[0]) +mask[1]*(x[1]-x0[1])*(x[1]-x0[1]) +mask[2]*(x[2]-x0[2])*(x[2]-x0[2])) /tau/tau/tau); dfdt += C0_slope; return dfdt; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // TemporalRamp //-------------------------------------------------------------------- //-------------------------------------------------------------------- TemporalRamp::TemporalRamp(int narg, double* args) : XT_Function(narg,args) { f_initial = args[0]; double f_final = args[1]; double delta_t = args[2]; slope = (f_final - f_initial)/delta_t; tag_ = "temporal_ramp"; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // RadialPower //-------------------------------------------------------------------- //-------------------------------------------------------------------- RadialPower::RadialPower(int narg, double* args) : XT_Function(narg,args) { C0 = args[6]; n = args[7]; tag_ = "radial_power"; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- // InterpolationFunction //-------------------------------------------------------------------- //-------------------------------------------------------------------- void InterpolationFunction::initialize(int npts, fstream &fileId, double coef) { // read data npts_ = npts; xs_.reset(npts); fs_.reset(npts); fps_.reset(npts); double x,f,fp; int i = 0; while(fileId.good() && i < npts) { fileId >> x >> f >> fp; xs_(i)=x; fs_(i)=coef*f; fps_(i)=coef*fp; i++; } // scale tangents double dx, dx0 = xs_(1)-xs_(0); for (int i = 0; i < npts_ ; i++) { if (i == 0) { dx = xs_(1)-xs_(0); } else if (i+1 == npts_) { dx = xs_(npts_-1)-xs_(npts_-2); } else { dx= 0.5*(xs_(i+1)-xs_(i-1)); } if (abs(dx-dx0) > 1.e-8) throw ATC_Error("InterpolationFunction::initialize non-uniform data spacing not handled currently"); fps_(i) *= dx; } // options: calculate / adjust tangents for monotonicity } double InterpolationFunction::coordinate(double x, double & f0, double & fp0, double & f1, double & fp1, double & inv_dx ) const { int i0 = xs_.index(x); int i1 = i0+1; if (i0 < 0) { double x0 = xs_(0), x1 = xs_(1); inv_dx = 1./(x1-x0); fp0 = fp1 = fps_(0); f1 = fs_(0); f0 = fp0*(x-xs_(0))+f1; return 0; } else if (i1 >= npts_) { double x0 = xs_(npts_-2), x1 = xs_(npts_-1); inv_dx = 1./(x1-x0); fp0 = fp1 = fps_(i0); f0 = fs_(i0); f1 = fp0*(x-xs_(i0))+f0; return 1; } else { double x0 = xs_(i0), x1 = xs_(i1); inv_dx = 1./(x1-x0); f0 = fs_ (i0); f1 = fs_ (i1); fp0 = fps_(i0); fp1 = fps_(i1); double t = (x-x0)*inv_dx; return t; } } double InterpolationFunction::f(const double x) const { double f0,fp0,f1,fp1,inv_dx; double t = coordinate(x,f0,fp0,f1,fp1,inv_dx); double t2 = t*t; double t3 = t*t2; double h00 = 2*t3 - 3*t2 + 1; double h10 = t3 - 2*t2 + t; double h01 =-2*t3 + 3*t2; double h11 = t3 - t2; double f = h00 * f0 + h10 * fp0 + h01 * f1 + h11 * fp1; return f; } double InterpolationFunction::dfdt(const double x) const { double f0,fp0,f1,fp1,inv_dx; double t = coordinate(x,f0,fp0,f1,fp1,inv_dx); double t2 = t*t; double d00 = 6*t2 - 6*t; double d10 = 3*t2 - 4*t + 1; double d01 =-6*t2 + 6*t; double d11 = 3*t2 - 2*t; double fp = d00 * f0 + d10 * fp0 + d01 * f1 + d11 * fp1; fp *= inv_dx; return fp; } } diff --git a/lib/atc/GhostManager.cpp b/lib/atc/GhostManager.cpp index 1f7fc8c83..3c9e54b4f 100644 --- a/lib/atc/GhostManager.cpp +++ b/lib/atc/GhostManager.cpp @@ -1,504 +1,785 @@ // ATC transfer headers #include "GhostManager.h" #include "ATC_Method.h" #include "LammpsInterface.h" #include "ATC_Error.h" +using std::vector; +using std::set; +using std::pair; +using std::map; +using std::stringstream; +using ATC_Utility::to_string; + namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostManager //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostManager::GhostManager(ATC_Method * atc) : ghostModifier_(NULL), atc_(atc), boundaryDynamics_(NO_BOUNDARY_DYNAMICS), - needReset_(true), - kappa_(0.), - gamma_(0.), - mu_(0.) + needReset_(true) { // do nothing } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- GhostManager::~GhostManager() { if (ghostModifier_) delete ghostModifier_; } //-------------------------------------------------------- // modify // parses inputs and modifies state of the integrator //-------------------------------------------------------- bool GhostManager::modify(int narg, char **arg) { int argIndex = 0; /*! \page man_boundary_dynamics fix_modify AtC boundary_dynamics \section syntax fix_modify AtC boundary_dynamics < on | damped_harmonic | prescribed | coupled | none > [args] \n \section description Sets different schemes for controlling boundary atoms. On will integrate the boundary atoms using the velocity-verlet algorithm. Damped harmonic uses a mass/spring/dashpot for the boundary atoms with added arguments of the damping and spring constants followed by the ratio of the boundary type mass to the desired mass. Prescribed forces the boundary atoms to follow the finite element displacement. Coupled does the same. \section restrictions Boundary atoms must be specified. When using swaps between internal and boundary atoms, the initial configuration must have already correctly partitioned the two. \section related \man_boundary \section default prescribed on */ if (strcmp(arg[argIndex],"none")==0) { boundaryDynamics_ = NO_BOUNDARY_DYNAMICS; needReset_ = true; return true; } else if (strcmp(arg[argIndex],"on")==0) { boundaryDynamics_ = VERLET; needReset_ = true; return true; } else if (strcmp(arg[argIndex],"prescribed")==0) { boundaryDynamics_ = PRESCRIBED; needReset_ = true; return true; } else if (strcmp(arg[argIndex],"damped_harmonic")==0) { argIndex++; - kappa_ = atof(arg[argIndex++]); - gamma_ = atof(arg[argIndex++]); - mu_ = atof(arg[argIndex]); + kappa_.push_back(atof(arg[argIndex++])); + gamma_.push_back(atof(arg[argIndex++])); + mu_.push_back(atof(arg[argIndex])); boundaryDynamics_ = DAMPED_HARMONIC; needReset_ = true; return true; } + else if (strcmp(arg[argIndex],"damped_layers")==0) { + argIndex++; + while (argIndex < narg) { + kappa_.push_back(atof(arg[argIndex++])); + gamma_.push_back(atof(arg[argIndex++])); + mu_.push_back(atof(arg[argIndex++])); + } + + boundaryDynamics_ = DAMPED_LAYERS; + needReset_ = true; + return true; + } else if (strcmp(arg[argIndex],"coupled")==0) { boundaryDynamics_ = COUPLED; - kappa_ = 0.; - gamma_ = 0.; - mu_ = 0.; + kappa_.push_back(0.); + gamma_.push_back(0.); + mu_.push_back(0.); needReset_ = true; return true; } return false; } //-------------------------------------------------------- // construct_methods // constructs the specific method to modify the ghosts //-------------------------------------------------------- void GhostManager::construct_methods() { if (ghostModifier_) { delete ghostModifier_; ghostModifier_ = NULL; } if (!atc_->groupbit_ghost()) { ghostModifier_ = new GhostModifier(this); return; } switch (boundaryDynamics_) { case VERLET: { ghostModifier_ = new GhostModifier(this); ghostModifier_->set_integrate_atoms(true); break; } case PRESCRIBED: { ghostModifier_ = new GhostModifierPrescribed(this); break; } case COUPLED: case DAMPED_HARMONIC: { ghostModifier_ = new GhostModifierDampedHarmonic(this,kappa_,gamma_,mu_); ghostModifier_->set_integrate_atoms(true); break; } + case DAMPED_LAYERS: { + ghostModifier_ = new GhostModifierDampedHarmonicLayers(this,kappa_,gamma_,mu_); + ghostModifier_->set_integrate_atoms(true); + break; + } case SWAP: { // if regions based on element sets, use verlet on ghosts and swap ghosts and internal when they move between regions const std::string & internalElementSet(atc_->internal_element_set()); if (internalElementSet.size() && (atc_->atom_to_element_map_type()==EULERIAN)) { LammpsInterface * lammpsInterface = LammpsInterface::instance(); if (atc_->atom_to_element_map_frequency() % lammpsInterface->reneighbor_frequency() != 0) { throw ATC_Error("GhostManager::construct_methods - eulerian frequency and lammsp reneighbor frequency must be consistent to swap boundary and internal atoms"); } ghostModifier_ = new GhostIntegratorSwap(this); } break; } case SWAP_VERLET: { // if regions based on element sets, use verlet on ghosts and swap ghosts and internal when they move between regions const std::string & internalElementSet(atc_->internal_element_set()); if (internalElementSet.size() && (atc_->atom_to_element_map_type()==EULERIAN)) { LammpsInterface * lammpsInterface = LammpsInterface::instance(); if (atc_->atom_to_element_map_frequency() % lammpsInterface->reneighbor_frequency() != 0) { throw ATC_Error("GhostManager::construct_methods - eulerian frequency and lammsp reneighbor frequency must be consistent to swap boundary and internal atoms"); } ghostModifier_ = new GhostIntegratorSwap(this); ghostModifier_->set_integrate_atoms(true); } break; } default: { ghostModifier_ = new GhostModifier(this); } } } //-------------------------------------------------------- // construct_transfers // sets/constructs all required dependency managed data //-------------------------------------------------------- void GhostManager::construct_transfers() { ghostModifier_->construct_transfers(); } //-------------------------------------------------------- // initialize // initialize all data and variables before a run //-------------------------------------------------------- void GhostManager::initialize() { ghostModifier_->initialize(); needReset_ = false; } //-------------------------------------------------------- // pre_exchange // makes any updates required before lammps exchanges // atoms //-------------------------------------------------------- void GhostManager::pre_exchange() { ghostModifier_->pre_exchange(); } //-------------------------------------------------------- // initial_integrate_velocity // velocity update in first part of velocity-verlet //-------------------------------------------------------- void GhostManager::init_integrate_velocity(double dt) { ghostModifier_->init_integrate_velocity(dt); } //-------------------------------------------------------- // initial_integrate_position // position update in first part of velocity-verlet //-------------------------------------------------------- void GhostManager::init_integrate_position(double dt) { ghostModifier_->init_integrate_position(dt); } //-------------------------------------------------------- // post_init_integrate // makes any updates required after first integration //-------------------------------------------------------- void GhostManager::post_init_integrate() { ghostModifier_->post_init_integrate(); } //-------------------------------------------------------- // final_integrate // velocity update in second part of velocity-verlet //-------------------------------------------------------- void GhostManager::final_integrate(double dt) { ghostModifier_->final_integrate(dt); } //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostModifier //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostModifier::GhostModifier(GhostManager * ghostManager) : ghostManager_(ghostManager), atomTimeIntegrator_(NULL), integrateAtoms_(false) { // do nothing } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- GhostModifier::~GhostModifier() { if (atomTimeIntegrator_) delete atomTimeIntegrator_; } //-------------------------------------------------------- // construct_transfers // sets/constructs all required dependency managed data //-------------------------------------------------------- void GhostModifier::construct_transfers() { if (atomTimeIntegrator_) delete atomTimeIntegrator_; if (integrateAtoms_) { atomTimeIntegrator_ = new AtomTimeIntegratorType(ghostManager_->atc(),GHOST); atomTimeIntegrator_->construct_transfers(); } else { atomTimeIntegrator_ = new AtomTimeIntegrator(); } } //-------------------------------------------------------- // initial_integrate_velocity // velocity update in first part of velocity-verlet //-------------------------------------------------------- void GhostModifier::init_integrate_velocity(double dt) { atomTimeIntegrator_->init_integrate_velocity(dt); } //-------------------------------------------------------- // initial_integrate_position // position update in first part of velocity-verlet //-------------------------------------------------------- void GhostModifier::init_integrate_position(double dt) { atomTimeIntegrator_->init_integrate_position(dt); } //-------------------------------------------------------- // final_integrate // velocity update in second part of velocity-verlet //-------------------------------------------------------- void GhostModifier::final_integrate(double dt) { atomTimeIntegrator_->final_integrate(dt); } //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostModifierPrescribed //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostModifierPrescribed::GhostModifierPrescribed(GhostManager * ghostManager) : GhostModifier(ghostManager), atomPositions_(NULL), atomFeDisplacement_(NULL), atomRefPositions_(NULL) { // do nothing } //-------------------------------------------------------- // construct_transfers // sets/constructs all required dependency managed data //-------------------------------------------------------- void GhostModifierPrescribed::construct_transfers() { GhostModifier::construct_transfers(); InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); atomPositions_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION,GHOST); // prolongation from displacement field to atoms PerAtomSparseMatrix<double> * atomShapeFunctions = interscaleManager.per_atom_sparse_matrix("InterpolantGhost"); if (!atomShapeFunctions) { atomShapeFunctions = new PerAtomShapeFunction(ghostManager_->atc(), interscaleManager.per_atom_quantity("AtomicGhostCoarseGrainingPositions"), interscaleManager.per_atom_int_quantity("AtomGhostElement"), GHOST); interscaleManager.add_per_atom_sparse_matrix(atomShapeFunctions,"InterpolantGhost"); } atomFeDisplacement_ = new FtaShapeFunctionProlongation(ghostManager_->atc(), &(ghostManager_->atc())->field(DISPLACEMENT), atomShapeFunctions, GHOST); interscaleManager.add_per_atom_quantity(atomFeDisplacement_,field_to_prolongation_name(DISPLACEMENT)+"Ghost"); atomRefPositions_ = interscaleManager.per_atom_quantity("AtomicGhostCoarseGrainingPositions"); } //-------------------------------------------------------- // post_init_integrate // after integration, fix ghost atoms' positions //-------------------------------------------------------- void GhostModifierPrescribed::post_init_integrate() { const DENS_MAT & atomFeDisplacement(atomFeDisplacement_->quantity()); const DENS_MAT & atomRefPositions(atomRefPositions_->quantity()); *atomPositions_ = atomFeDisplacement + atomRefPositions; } //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostModifierDampedHarmonic //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostModifierDampedHarmonic::GhostModifierDampedHarmonic(GhostManager * ghostManager, - double kappa, - double gamma, double mu) : + const vector<double> & kappa, + const vector<double> & gamma, + const vector<double> & mu) : GhostModifierPrescribed(ghostManager), atomVelocities_(NULL), atomFeVelocity_(NULL), atomForces_(NULL), kappa_(kappa), gamma_(gamma), mu_(mu) { // do nothing } //-------------------------------------------------------- // construct_transfers // sets/constructs all required dependency managed data //-------------------------------------------------------- void GhostModifierDampedHarmonic::construct_transfers() { GhostModifierPrescribed::construct_transfers(); InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY,GHOST); atomForces_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE,GHOST); // prolongation from displacement field to atoms PerAtomSparseMatrix<double> * atomShapeFunctions = interscaleManager.per_atom_sparse_matrix("InterpolantGhost"); atomFeVelocity_ = new FtaShapeFunctionProlongation(ghostManager_->atc(), &(ghostManager_->atc())->field(VELOCITY), atomShapeFunctions, GHOST); interscaleManager.add_per_atom_quantity(atomFeVelocity_,field_to_prolongation_name(VELOCITY)+"Ghost"); // calculate nominal bond stiffness int i = 0, j = 1;// HACk should be an atom and its neighbor in the boundary double rsq = 0.0; //k0_ = LammpsInterface_->bond_stiffness(i,j,rsq); k0_ = LammpsInterface::instance()->bond_stiffness(i,j,rsq); } +#if true //-------------------------------------------------------- // initial_integrate_velocity // velocity update in first part of velocity-verlet //-------------------------------------------------------- void GhostModifierDampedHarmonic::init_integrate_velocity(double dt) { - atomTimeIntegrator_->init_integrate_velocity(mu_*dt); +#if true + atomTimeIntegrator_->init_integrate_velocity(mu_[0]*dt); +#else + atomTimeIntegrator_->init_integrate_velocity(dt); +#endif } //-------------------------------------------------------- // initial_integrate_position // position update in first part of velocity-verlet //-------------------------------------------------------- void GhostModifierDampedHarmonic::init_integrate_position(double dt) { atomTimeIntegrator_->init_integrate_position(dt); } - +#endif //-------------------------------------------------------- // final_integrate // velocity update in second part of velocity-verlet //-------------------------------------------------------- void GhostModifierDampedHarmonic::final_integrate(double dt) { const DENS_MAT & atomPositions(atomPositions_->quantity()); const DENS_MAT & atomVelocities(atomVelocities_->quantity()); const DENS_MAT & atomFeDisplacement(atomFeDisplacement_->quantity()); const DENS_MAT & atomFeVelocity(atomFeVelocity_->quantity()); const DENS_MAT & atomRefPositions(atomRefPositions_->quantity()); _forces_ = atomFeDisplacement; _forces_ += atomRefPositions; _forces_ -= atomPositions; - _forces_ *= kappa_; - _forces_ += gamma_*(atomFeVelocity - atomVelocities); + _forces_ *= kappa_[0]; + _forces_ += gamma_[0]*(atomFeVelocity - atomVelocities); +#if true +#else + _forces_ *= 1./mu_[0]; +#endif *atomForces_ = _forces_; - atomTimeIntegrator_->final_integrate(mu_*dt); +#if true + atomTimeIntegrator_->final_integrate(mu_[0]*dt); +#else + atomTimeIntegrator_->final_integrate(dt); +#endif + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class GhostModifierDampedHarmonicLayers + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + GhostModifierDampedHarmonicLayers::GhostModifierDampedHarmonicLayers(GhostManager * ghostManager, + const vector<double> & kappa, + const vector<double> & gamma, + const vector<double> & mu) : + GhostModifierDampedHarmonic(ghostManager,kappa,gamma,mu), + ghostToBoundaryDistance_(NULL), + layerId_(NULL) + { + + // do nothing + } + + //-------------------------------------------------------- + // construct_transfers + // sets/constructs all required dependency managed data + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::construct_transfers() + { + GhostModifierDampedHarmonic::construct_transfers(); + + InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); + + // transfer for distance to boundary + ghostToBoundaryDistance_ = new AtcAtomQuantity<double>(ghostManager_->atc(), + 1,GHOST); + interscaleManager.add_per_atom_quantity(ghostToBoundaryDistance_, + "GhostToBoundaryDistance"); + + // transfer from ghost atom to its layer id + layerId_ = new AtcAtomQuantity<int>(ghostManager_->atc(), + 1,GHOST); + interscaleManager.add_per_atom_int_quantity(layerId_,"GhostLayerId"); + } + + //-------------------------------------------------------- + // initialize + // initialize all data and variables before a run + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::initialize() + { + compute_distances(); + int nlayers = find_layers(); + if (nlayers > ((int)gamma_.size())) throw ATC_Error("GhostModifierDampedHarmonicLayers::initialize not enough damping factors specfied " + to_string(gamma_.size())); + } + + //-------------------------------------------------------- + // find atomic mononlayers + //-------------------------------------------------------- + bool compare( pair<int,double> a, pair<int,double> b) { + return (a.second < b.second); + } + int GhostModifierDampedHarmonicLayers::find_layers() + { + DENS_MAT & d(ghostToBoundaryDistance_->set_quantity()); + DenseMatrix<int> & ids = layerId_->set_quantity(); + + // get distances for every ghost atom for sorting + // size arrays of length number of processors + int commSize = LammpsInterface::instance()->comm_size(); + int * procCounts = new int[commSize]; + int * procOffsets = new int[commSize]; + + // get size information from all processors + int localGhosts = d.nRows(); + LammpsInterface::instance()->int_allgather(localGhosts,procCounts); + procOffsets[0] = 0; + int totalGhosts = 0; + for (int i = 0; i < commSize-1; ++i) { + totalGhosts += procCounts[i]; + procOffsets[i+1] = totalGhosts; + } + totalGhosts += procCounts[commSize-1]; + double * globalDistances = new double[totalGhosts]; + + // allgather distances + LammpsInterface::instance()->allgatherv(d.ptr(), localGhosts, + globalDistances, procCounts, procOffsets); + + // add to distances vector with -1 ids + // convert to STL for sort + vector< pair <int,double> > distances; + distances.resize(totalGhosts); + int myRank = LammpsInterface::instance()->comm_rank(); + int j = 0; + for (int i = 0; i < totalGhosts; ++i) { + if (i >= procOffsets[myRank] && i < procOffsets[myRank] + procCounts[myRank]) { + distances[i] = pair <int,double> (j++,globalDistances[i]); + } + else { + distances[i] = pair <int,double> (-1,globalDistances[i]); + } + } + delete [] globalDistances; + delete [] procCounts; + delete [] procOffsets; + + std::sort(distances.begin(),distances.end(),compare); + double min = (distances[0]).second; + double a = LammpsInterface::instance()->max_lattice_constant(); + double tol = a/4; + double xlayer = min; // nominal position of layer + int ilayer =0; + vector<int> counts(1); + counts[0] = 0; + for (vector<pair<int,double> >::const_iterator itr = distances.begin(); + itr != distances.end(); itr++) { + int id = (*itr).first; + double d = (*itr).second; + if (fabs(d-xlayer) > tol) { + counts.push_back(0); + ilayer++; + xlayer = d; + } + if (id > -1) { + ids(id,0) = ilayer; + } + counts[ilayer]++; + } + int nlayers = ilayer; + stringstream msg; + msg << nlayers << " boundary layers:\n"; + for (int i = 0; i < nlayers; ++i) { + msg << i+1 << ": " << counts[i] << "\n"; + } + ATC::LammpsInterface::instance()->print_msg_once(msg.str()); + return nlayers; + } + + //-------------------------------------------------------- + // compute distances to boundary faces + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::compute_distances() + { + // get fe mesh + const FE_Mesh * feMesh = ((ghostManager_->atc())->fe_engine())->fe_mesh(); + InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); + + // get elements in which ghosts reside + const DenseMatrix<int> & elementHasGhost((interscaleManager.dense_matrix_int("ElementHasGhost"))->quantity()); + // get type of each node + const DenseMatrix<int> & nodeType((interscaleManager.dense_matrix_int("NodalGeometryType"))->quantity()); + + // create map from those elements to pair<DENS_VEC,DENS_VEC> (centroid, normal) + map<int, pair<DENS_VEC,DENS_VEC> > elementToFace; + int nsd = (ghostManager_->atc())->nsd(); + int nfe = feMesh->num_faces_per_element(); + DENS_MAT nodalCoords(nsd,nfe); + DENS_VEC centroid(nsd), normal(nsd); + Array<int> faceNodes; + bool isBoundaryFace; + for (int elt = 0; elt < elementHasGhost.nRows(); ++elt) { + if (elementHasGhost(elt,0)) { + // loop over all faces + int face; + for (face = 0; face < nfe; ++face) { + // get the nodes in a face + feMesh->face_connectivity_unique(PAIR(elt,face),faceNodes); + + // identify the boundary face by the face which contains only boundary nodes + isBoundaryFace = true; + for (int i = 0; i < faceNodes.size(); ++i) { + if (nodeType(faceNodes(i),0) != BOUNDARY) { + isBoundaryFace = false; + break; + } + } + + if (isBoundaryFace) { + break; + } + } + if (face == nfe) { + throw ATC_Error("GhostModifierDampedHarmonicLayers::initialize - Could not find boundary face for element " + to_string(elt)); + } + + // for each boundary face get the centroid by the average position of the nodes comprising it + feMesh->face_coordinates(PAIR(elt,face),nodalCoords); + centroid = 0.; + for (int i = 0; i < nodalCoords.nRows(); ++i) { + for (int j = 0; j < nodalCoords.nCols(); ++j) { + centroid(i) += nodalCoords(i,j); + } + } + centroid *= -1./double(nfe); // -1 gets outward normal from ATC region => all distances should be > 0 + + // for each boundary face get the normal + // ASSUMES all faces are planar + feMesh->face_normal(PAIR(elt,face),0,normal); + + elementToFace[elt] = pair<DENS_VEC,DENS_VEC>(centroid,normal); + } + } + + // for each atom compute (atom_pos - element->face_centroid) dot element_->face_normal + // get atom to element map for ghosts + PerAtomQuantity<int> * ghostToElementMap = interscaleManager.per_atom_int_quantity("AtomGhostElement"); + const DenseMatrix<int> & ghostToElement(ghostToElementMap->quantity()); + DENS_MAT & distance(ghostToBoundaryDistance_->set_quantity()); + const DENS_MAT & atomPositions(atomPositions_->quantity()); + DENS_VEC diff(nsd); + for (int i = 0; i < distance.nRows(); ++i) { + int elt = ghostToElement(i,0); + const DENS_VEC & c(elementToFace[elt].first); + const DENS_VEC & n(elementToFace[elt].second); + for (int j = 0; j < nsd; ++j) { + diff(j) = atomPositions(i,j) - c(j); + } + distance(i,0) = diff.dot(n); // should always be positive + } + } + + + //-------------------------------------------------------- + // final_integrate + // velocity update in second part of velocity-verlet + //-------------------------------------------------------- + void GhostModifierDampedHarmonicLayers::final_integrate(double dt) + { + + const DENS_MAT & atomPositions(atomPositions_->quantity()); + const DENS_MAT & atomVelocities(atomVelocities_->quantity()); + const DENS_MAT & atomFeDisplacement(atomFeDisplacement_->quantity()); + const DENS_MAT & atomFeVelocity(atomFeVelocity_->quantity()); + const DENS_MAT & atomRefPositions(atomRefPositions_->quantity()); + + _forces_ = atomFeDisplacement; + _forces_ += atomRefPositions; + _forces_ -= atomPositions; + + _forces_ *= kappa_[0]; + _forces_ += gamma_[0]*(atomFeVelocity - atomVelocities); + _forces_ *= 1./mu_[0]; + *atomForces_ = _forces_; + + atomTimeIntegrator_->final_integrate(dt); } //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostIntegratorVerletSwap //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostIntegratorSwap::GhostIntegratorSwap(GhostManager * ghostManager) : GhostModifier(ghostManager), lammpsInterface_(LammpsInterface::instance()), elementSet_((((ghostManager_->atc())->fe_engine())->fe_mesh())->elementset((ghostManager_->atc())->internal_element_set())), atomElement_(NULL), atomGhostElement_(NULL), internalToAtom_((ghostManager_->atc())->internal_to_atom_map()), ghostToAtom_((ghostManager_->atc())->ghost_to_atom_map()), groupbit_((ghostManager_->atc())->groupbit()), groupbitGhost_((ghostManager_->atc())->groupbit_ghost()) { // do nothing } //-------------------------------------------------------- // construct_transfers // sets/constructs all required dependency managed data //-------------------------------------------------------- void GhostIntegratorSwap::construct_transfers() { GhostModifier::construct_transfers(); InterscaleManager & interscaleManager((ghostManager_->atc())->interscale_manager()); atomElement_ = interscaleManager.per_atom_int_quantity("AtomElement"); atomGhostElement_ = interscaleManager.per_atom_int_quantity("AtomGhostElement"); } //-------------------------------------------------------- // initialize // initialize all data and variables before a run //-------------------------------------------------------- void GhostIntegratorSwap::initialize() { if ((ghostManager_->atc())->atom_to_element_map_frequency() % lammpsInterface_->reneighbor_frequency() != 0) { throw ATC_Error("GhostIntegratorSwap::initialize - AtC Eulerian reset frequency must be a multiple of the Lammps reneighbor frequency when using internal/boundary atom swapping"); } } //-------------------------------------------------------- // pre_exchange // swaps atoms between types depending on region //-------------------------------------------------------- void GhostIntegratorSwap::pre_exchange() { // lammps mask to change type int * mask = lammpsInterface_->atom_mask(); const DenseMatrix<int> & atomElement(atomElement_->quantity()); for (int i = 0; i < atomElement.nRows(); ++i) { if (elementSet_.find(atomElement(i,0)) == elementSet_.end()) { mask[internalToAtom_(i)] |= groupbitGhost_; // remove from internal } } } }; diff --git a/lib/atc/GhostManager.h b/lib/atc/GhostManager.h index c146644db..9c3bbbe93 100644 --- a/lib/atc/GhostManager.h +++ b/lib/atc/GhostManager.h @@ -1,327 +1,383 @@ #ifndef GHOST_MANAGER_H #define GHOST_MANAGER_H // ATC headers #include "MatrixLibrary.h" #include "PerAtomQuantityLibrary.h" #include "TimeIntegrator.h" #include "ATC_TypeDefs.h" namespace ATC { // forward declarations class ATC_Method; class GhostModifier; class LammpsInterface; /** * @class GhostManager * @brief Manages methods for modifying ghost atoms */ class GhostManager { public: /** types of ghost boundary conditions in momentum */ enum BoundaryDynamicsType { NO_BOUNDARY_DYNAMICS=0, - VERLET, - PRESCRIBED, - DAMPED_HARMONIC, - COUPLED, - SWAP, - SWAP_VERLET + VERLET, // performs velocity-verlet + PRESCRIBED, // forces ghost locations to conform to interpolated finite element locations + DAMPED_HARMONIC, // turns ghost atoms into spring-mass-dashpot systems + DAMPED_LAYERS, // oer layer DAMPED_HARMONIC + COUPLED, // applies a spring-dashpot force to the ghosts + SWAP, // exchanges ghost and real atoms when they cross AtC boundaries + SWAP_VERLET // like above, but integrates the ghosts using velocity verlet }; // constructor GhostManager(ATC_Method * atc); // destructor virtual ~GhostManager(); /** parser/modifier */ virtual bool modify(int narg, char **arg); /** create objects to implement requested numerical method */ virtual void construct_methods(); /** create and get necessary transfer operators */ virtual void construct_transfers(); /** pre time integration initialization of data */ virtual void initialize(); /** prior to lammps exchange */ virtual void pre_exchange(); /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt); /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt); /** set positions after integration */ virtual void post_init_integrate(); /** Corrector phase, Verlet second step for velocity */ virtual void final_integrate(double dt); /** sets the boundary dynamics flag as desired */ void set_boundary_dynamics(BoundaryDynamicsType boundaryDynamics) {boundaryDynamics_ = boundaryDynamics;} /** flag for reset */ bool need_reset() const {return needReset_;}; /** access to ATC method object */ ATC_Method * atc() {return atc_;}; protected: /** pointer to routines that modify ghosts */ GhostModifier* ghostModifier_; /** pointer to access ATC methods */ ATC_Method * atc_; /** boundary dynamics method type */ BoundaryDynamicsType boundaryDynamics_; /** flag for reset */ bool needReset_; /** spring constant for some models */ - double kappa_; + std::vector<double> kappa_; /** damping constant for some models */ - double gamma_; + std::vector<double> gamma_; /** ratio between mass of ghost types and desired mass for some models */ - double mu_; + std::vector<double> mu_; private: // DO NOT define this GhostManager(); }; /** * @class GhostModifier * @brief Base class for objects which modify the ghost atoms, integrates ghost atoms using velocity-verlet if requested */ class GhostModifier { public: // constructor GhostModifier(GhostManager * ghostManager); // destructor virtual ~GhostModifier(); /** create and get necessary transfer operators */ virtual void construct_transfers(); /** pre time integration initialization of data */ virtual void initialize(){}; /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt); /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt); /** set positions after integration */ virtual void post_init_integrate(){}; /** prior to lammps exchange */ virtual void pre_exchange(){}; /** Corrector phase, Verlet second step for velocity */ virtual void final_integrate(double dt); /** sets the verlet integration flag as desired */ void set_integrate_atoms(bool integrateAtoms) {integrateAtoms_ = integrateAtoms;} protected: /** owning ghost manager */ GhostManager * ghostManager_; /** object which integrates atoms */ AtomTimeIntegrator * atomTimeIntegrator_; /** flag to perform velocity-verlet integration of ghosts */ bool integrateAtoms_; private: // DO NOT define this GhostModifier(); }; /** * @class GhostModifierPrescribed * @brief sets ghost atom positions based on FE displacement */ class GhostModifierPrescribed : public GhostModifier { public: // constructor GhostModifierPrescribed(GhostManager * ghostManager); // destructor virtual ~GhostModifierPrescribed(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(); /** set positions after integration */ virtual void post_init_integrate(); protected: /** positions of atoms */ PerAtomQuantity<double> * atomPositions_; /** FE displacement at ghost locations */ PerAtomQuantity<double> * atomFeDisplacement_; /** atom reference positions */ PerAtomQuantity<double> * atomRefPositions_; private: // DO NOT define this GhostModifierPrescribed(); }; /** * @class GhostModifierDampedHarmonic * @brief Integrates ghost atoms using velocity-verlet with a damped harmonic force */ class GhostModifierDampedHarmonic : public GhostModifierPrescribed { public: // constructor GhostModifierDampedHarmonic(GhostManager * ghostManager, - double kappa_, double gamma, double mu); + const std::vector<double> & kappa, + const std::vector<double> & gamma, + const std::vector<double> & mu); // destructor virtual ~GhostModifierDampedHarmonic(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(); - +#if true /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt); /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt); - +#endif /** set positions after integration */ virtual void post_init_integrate(){}; /** Corrector phase, Verlet second step for velocity */ virtual void final_integrate(double dt); protected: /** velocities of atoms */ PerAtomQuantity<double> * atomVelocities_; /** FE velocity at ghost locations */ PerAtomQuantity<double> * atomFeVelocity_; /** atom forces */ PerAtomQuantity<double> * atomForces_; + /** effective spring constant for potential */ + double k0_; + /** spring constant */ - double kappa_, k0_; + const std::vector<double> & kappa_; /** damping constant */ - double gamma_; + const std::vector<double> & gamma_; /** ratio between mass of ghost types and desired mass */ - double mu_; + const std::vector<double> & mu_; // workspace DENS_MAT _forces_; private: // DO NOT define this GhostModifierDampedHarmonic(); }; + /** + * @class GhostModifierDampedHarmonicLayers + * @brief Integrates ghost atoms using velocity-verlet with a damped harmonic force based on which layer the atom resides in + */ + + class GhostModifierDampedHarmonicLayers : public GhostModifierDampedHarmonic { + + public: + + // constructor + GhostModifierDampedHarmonicLayers(GhostManager * ghostManager, + const std::vector<double> & kappa, + const std::vector<double> & gamma, + const std::vector<double> & mu); + + // destructor + virtual ~GhostModifierDampedHarmonicLayers(){}; + + /** create and get necessary transfer operators */ + virtual void construct_transfers(); + + /** pre time integration initialization of data */ + virtual void initialize(); + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(double dt); + + protected: + + // methods + /** compute distance of ghost atom to boundary */ + void compute_distances(); + /** sorting heuristics to identify layers */ + int find_layers(); + + // data + /** distance from all ghost atoms to boundary, i.e. boundary face of containing element */ + PerAtomQuantity<double> * ghostToBoundaryDistance_; + + /** layer id for ghost atoms */ + PerAtomQuantity<int> * layerId_; + + private: + + // DO NOT define this + GhostModifierDampedHarmonicLayers(); + + }; + + /** * @class GhostIntegratorSwap * @brief Integrates ghost atoms using velocity-verlet, and swaps atoms between ghost * and internal depending on what element they are in */ class GhostIntegratorSwap : public GhostModifier { public: // constructor GhostIntegratorSwap(GhostManager * ghostManager); // destructor virtual ~GhostIntegratorSwap(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(); /** pre time integration initialization of data */ virtual void initialize(); /** prior to lammps exchange */ virtual void pre_exchange(); protected: /** pointer to lammps interface */ LammpsInterface * lammpsInterface_; /** internal element set */ const std::set<int> & elementSet_; /** internal to element map */ PerAtomQuantity<int> * atomElement_; /** ghost to element map */ PerAtomQuantity<int> * atomGhostElement_; /** internal to atom map */ const Array<int> & internalToAtom_; /** ghost to atom map */ const Array<int> & ghostToAtom_; /** group bit for internal */ int groupbit_; /** group bit for ghost */ int groupbitGhost_; private: // DO NOT define this GhostIntegratorSwap(); }; }; #endif diff --git a/lib/atc/ImplicitSolveOperator.cpp b/lib/atc/ImplicitSolveOperator.cpp index 93ee362e5..d66da8a0e 100644 --- a/lib/atc/ImplicitSolveOperator.cpp +++ b/lib/atc/ImplicitSolveOperator.cpp @@ -1,160 +1,200 @@ // Header file for this class #include "ImplicitSolveOperator.h" // Other ATC includes #include "ATC_Coupling.h" #include "FE_Engine.h" #include "PhysicsModel.h" #include "PrescribedDataManager.h" + +using std::set; +using std::vector; + namespace ATC { // -------------------------------------------------------------------- // -------------------------------------------------------------------- // ImplicitSolveOperator // -------------------------------------------------------------------- // -------------------------------------------------------------------- ImplicitSolveOperator:: -ImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, - const PhysicsModel * physicsModel) - : atc_(atc), - feEngine_(feEngine), - physicsModel_(physicsModel) -{ - // Nothing else to do here -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// FieldImplicitSolveOperator -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -FieldImplicitSolveOperator:: -FieldImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, - FIELDS & fields, - const FieldName fieldName, - const Array2D< bool > & rhsMask, - const PhysicsModel * physicsModel, - double simTime, - double dt, - double alpha) - : ImplicitSolveOperator(atc, feEngine, physicsModel), - fieldName_(fieldName), - fields_(fields), // ref to fields - time_(simTime), +ImplicitSolveOperator(double alpha, double dt) + : n_(0), + dof_(0), dt_(dt), alpha_(alpha), epsilon0_(1.0e-8) { - // find field associated with ODE - rhsMask_.reset(NUM_FIELDS,NUM_FLUX); - rhsMask_ = false; - for (int i = 0; i < rhsMask.nCols(); i++) { - rhsMask_(fieldName_,i) = rhsMask(fieldName_,i); - } - massMask_.reset(1); - massMask_(0) = fieldName_; - - // Save off current field - TnVect_ = column(fields_[fieldName_].quantity(),0); - - // Allocate vectors for fields and rhs - int nNodes = atc_->num_nodes(); - // copy fields - fieldsNp1_ = fields_; - // size rhs - int dof = fields_[fieldName_].nCols(); - RnMap_ [fieldName_].reset(nNodes,dof); - RnpMap_[fieldName_].reset(nNodes,dof); - - // Compute the RHS vector R(T^n) - // Set BCs on Rn, multiply by inverse mass and then extract its vector - atc_->compute_rhs_vector(rhsMask_, fields_, RnMap_, - FULL_DOMAIN, physicsModel_); - DENS_MAT & Rn = RnMap_[fieldName_].set_quantity(); - atc_->prescribed_data_manager() - ->set_fixed_dfield(time_, fieldName_, Rn); - atc_->apply_inverse_mass_matrix(Rn,fieldName_); - RnVect_ = column(Rn,0); + // Nothing else to do here } - // -------------------------------------------------------------------- -// operator *(Vector) +// operator * // -------------------------------------------------------------------- DENS_VEC -FieldImplicitSolveOperator::operator * (DENS_VEC x) const +ImplicitSolveOperator::operator * (const DENS_VEC & x) const { // This method uses a matrix-free approach to approximate the // multiplication by matrix A in the matrix equation Ax=b, where the // matrix equation results from an implicit treatment of the - // fast field solve for the Two Temperature Model. In - // brief, if the ODE for the fast field can be written: + // fast field. In brief, if the ODE for the fast field can be written: // - // dT/dt = R(T) + // dx/dt = R(x) // // A generalized discretization can be written: // - // 1/dt * (T^n+1 - T^n) = alpha * R(T^n+1) + (1-alpha) * R(T^n) + // 1/dt * (x^n+1 - x^n) = alpha * R(x^n+1) + (1-alpha) * R(x^n) // - // Taylor expanding the R(T^n+1) term and rearranging gives the - // equation to be solved for dT at each timestep: + // Taylor expanding the R(x^n+1) term and rearranging gives the + // equation to be solved for dx at each timestep: // - // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) + // [1 - dt * alpha * dR/dx] * dx = dt * R(x^n) // // The operator defined in this method computes the left-hand side, - // given a vector dT. It uses a finite difference, matrix-free - // approximation of dR/dT * dT, giving: + // given a vector dx. It uses a finite difference, matrix-free + // approximation of dR/dx * dx, giving: // - // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) - // ~= dT - dt*alpha/epsilon * ( R(T^n + epsilon*dT) - R(T^n) ) + // [1 - dt * alpha * dR/dx] * dx = dt * R(x^n) + // ~= dx - dt*alpha/epsilon * ( R(x^n + epsilon*dx) - R(x^n) ) // - - // Compute epsilon - double epsilon = (x.norm() > 0.0) ? epsilon0_ * TnVect_.norm()/x.norm() : epsilon0_; - - // Compute incremented vector = T + epsilon*dT - fieldsNp1_[fieldName_] = TnVect_ + epsilon * x; - - // Evaluate R(b) - atc_->compute_rhs_vector(rhsMask_, fieldsNp1_, RnpMap_, - FULL_DOMAIN, physicsModel_); - DENS_MAT & Rnp = RnpMap_[fieldName_].set_quantity(); - atc_->prescribed_data_manager() - ->set_fixed_dfield(time_, fieldName_, Rnp); - atc_->apply_inverse_mass_matrix(Rnp,fieldName_); - RnpVect_ = column(Rnp,0); - + double epsilon = (x.norm()>0.0) ? epsilon0_*x0_.norm()/x.norm():epsilon0_; + // Compute incremented vector x^n+1 = x^n + epsilon*dx + x_ = x0_ + epsilon * x; + // Evaluate R(x) + this->R(x_,R_); // Compute full left hand side and return it - DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (RnpVect_ - RnVect_); + DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (R_ - R0_); return Ax; } // -------------------------------------------------------------------- -// rhs +// rhs of Ax = r // -------------------------------------------------------------------- DENS_VEC -FieldImplicitSolveOperator::rhs() +ImplicitSolveOperator::r() const { - // Return dt * R(T^n) - return dt_ * RnVect_; + return dt_ * R0_; // dt * R(T^n) } // -------------------------------------------------------------------- // preconditioner // -------------------------------------------------------------------- DIAG_MAT -FieldImplicitSolveOperator::preconditioner(FIELDS & fields) +ImplicitSolveOperator::preconditioner() const +{ + DENS_VEC diag(n_); + diag = 1.0; + DIAG_MAT preconditioner(diag); + return preconditioner; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// FieldImplicitSolveOperator +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +FieldImplicitSolveOperator:: +FieldImplicitSolveOperator(ATC_Coupling * atc, + FIELDS & fields, + const FieldName fieldName, + const Array2D< bool > & rhsMask, + const PhysicsModel * physicsModel, + double simTime, + double dt, + double alpha) + : ImplicitSolveOperator(alpha, dt), + fieldName_(fieldName), + atc_(atc), + physicsModel_(physicsModel), + fields0_(fields), // ref to fields + fields_ (fields), // copy of fields + rhsMask_(rhsMask), + time_(simTime) { - // Just create and return identity matrix - int nNodes = atc_->num_nodes(); - DENS_VEC ones(nNodes); - ones = 1.0; - DIAG_MAT identity(ones); - return identity; + const DENS_MAT & f = fields0_[fieldName_].quantity(); + dof_ = f.nCols(); + if (dof_ > 1) throw ATC_Error("Implicit solver operator can only handle scalar fields"); + // create all to free map + int nNodes = f.nRows(); + set<int> fixedNodes_ = atc_->prescribed_data_manager()->fixed_nodes(fieldName_); + n_ = nNodes; + vector<bool> tag(nNodes); + set<int>::const_iterator it; int i = 0; + for (i = 0; i < nNodes; ++i) { tag[i] = true; } + for (it=fixedNodes_.begin();it!=fixedNodes_.end();++it) {tag[*it]=false;} + int m = 0; + for (i = 0; i < nNodes; ++i) { if (tag[i]) freeNodes_[i]= m++; } +//std::cout << " nodes " << n_ << " " << nNodes << "\n"; + + // Save current field + x0_.reset(n_); + to_free(f,x0_); + x_ = x0_; // initialize + + // righthand side/forcing vector + rhsMask_.reset(NUM_FIELDS,NUM_FLUX); + rhsMask_ = false; + for (int i = 0; i < rhsMask.nCols(); i++) { + rhsMask_(fieldName_,i) = rhsMask(fieldName_,i); + } +//std::cout << print_mask(rhsMask_) << "\n"; + massMask_.reset(1); + massMask_(0) = fieldName_; + rhs_[fieldName_].reset(nNodes,dof_); + // Compute the RHS vector R(T^n) + R0_.reset(n_); + R_ .reset(n_); + R(x0_, R0_); +} + +void FieldImplicitSolveOperator::to_all(const VECTOR &x, MATRIX &f) const +{ + f.reset(x.nRows(),1); + for (int i = 0; i < x.nRows(); ++i) { + f(i,0) = x(i); + } +} +void FieldImplicitSolveOperator::to_free(const MATRIX &r, VECTOR &v) const +{ + v.reset(r.nRows()); + for (int i = 0; i < r.nRows(); ++i) { + v(i) = r(i,0); + } +} +void +FieldImplicitSolveOperator::R(const DENS_VEC &x, DENS_VEC &v ) const +{ + DENS_MAT & f = fields_[fieldName_].set_quantity(); + atc_->prescribed_data_manager()->set_fixed_field(time_, fieldName_, f); + to_all(x,f); + atc_->compute_rhs_vector(rhsMask_,fields_,rhs_,FULL_DOMAIN,physicsModel_); + DENS_MAT & r = rhs_[fieldName_].set_quantity(); + atc_->prescribed_data_manager()->set_fixed_dfield(time_, fieldName_, r); + atc_->apply_inverse_mass_matrix(r,fieldName_); + to_free(r,v); +#if 0 +int n = 6; +//std::cout << "# x "; for (int i = 0; i < n_; ++i) std::cout << x(i) << " "; std::cout << "\n"; +//std::cout << "# f "; for (int i = 0; i < n; ++i) std::cout << f(i,0) << " "; std::cout << "\n"; +std::cout << "# r "; for (int i = 0; i < n; ++i) std::cout << r(i,0) << " "; std::cout << "\n"; +//std::cout << "# v "; for (int i = 0; i < n; ++i) std::cout << v(i) << " "; std::cout << "\n"; +#endif +} + +void FieldImplicitSolveOperator::solution(const DENS_MAT & dx, DENS_MAT &f) const +{ + DENS_MAT & df = fields_[fieldName_].set_quantity(); + to_all(column(dx,0),df); + atc_->prescribed_data_manager()->set_fixed_dfield(time_, fieldName_, df); + f += df; } +void FieldImplicitSolveOperator::rhs(const DENS_MAT & r, DENS_MAT &rhs) const +{ + + to_all(column(r,0),rhs); +} + } // namespace ATC diff --git a/lib/atc/ImplicitSolveOperator.h b/lib/atc/ImplicitSolveOperator.h index 670f20d2c..8b9f28dc5 100644 --- a/lib/atc/ImplicitSolveOperator.h +++ b/lib/atc/ImplicitSolveOperator.h @@ -1,129 +1,91 @@ #ifndef IMPLICIT_SOLVE_OPERATOR_H #define IMPLICIT_SOLVE_OPERATOR_H // ATC includes #include "Array2D.h" #include "MatrixLibrary.h" #include "PhysicsModel.h" // other includes #include <vector> #include <map> namespace ATC { // Forward class declarations class ATC_Coupling; class FE_Engine; /** * @class ImplicitSolveOperator * @brief Helper class to compute matrix-free product for use with IML++ solvers */ class ImplicitSolveOperator { - public: - /** Constructor */ - ImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * feEngine, - const PhysicsModel * physicsModel); - + ImplicitSolveOperator(double alpha, double dt); /** Destructor */ virtual ~ImplicitSolveOperator() {}; - /** pure virtual operator to compute Ax, for equation Ax=b */ - virtual DENS_VEC operator * (DENS_VEC x) const = 0; - + virtual DENS_VEC operator * (const DENS_VEC &x) const; /** pure virtual method to return the rhs vector b */ - virtual DENS_VEC rhs() = 0; - + virtual void R(const DENS_VEC &f, DENS_VEC &v) const = 0; + /** pure virtual method to return the rhs vector b */ + virtual DENS_VEC r() const; /** pure virtual method to return preconditioner */ - virtual DiagonalMatrix<double> preconditioner(FIELDS & fields) = 0; - + virtual DiagonalMatrix<double> preconditioner() const; + /** finalize */ + virtual void solution(const DENS_MAT & dx, DENS_MAT &x) const = 0; + virtual void rhs(const DENS_MAT & r, DENS_MAT &rhs) const = 0; protected: - - /** Pointer to atc */ - ATC_Coupling * atc_; - - /** Pointer to FE_Engine */ - /*const*/ FE_Engine * feEngine_; - - /** Pointer to PhysicsModel */ - const PhysicsModel * physicsModel_; - + int n_,dof_; + DENS_VEC x0_; // condensed previous + mutable DENS_VEC x_; // condensed current + DENS_VEC R0_; // condensed previous + mutable DENS_VEC R_; // condensed current + double dt_; // timestep + double alpha_; // implicit/explicit parameter (0 -> explicit, else implicit) + double epsilon0_; // small parameter to compute increment }; /** * @class FieldImplicitSolveOperator * @brief Class to perform A*x operation for electron temperature solution */ class FieldImplicitSolveOperator : public ImplicitSolveOperator { - public: - /** Constructor */ FieldImplicitSolveOperator(ATC_Coupling * atc, - /*const*/ FE_Engine * fe_Engine, FIELDS & fields, - const FieldName electronField, + const FieldName f, const Array2D< bool > & rhsMask, const PhysicsModel * physicsModel, - double simTime, - double dt, - double alpha); - + double simTime, double dt, double alpha = 0.5); /** Destructor */ virtual ~FieldImplicitSolveOperator() {}; - - /** operator to compute A*x for the electron temperature equation */ - virtual DENS_VEC operator * (DENS_VEC x) const; - - /** method to return the rhs vector b */ - virtual DENS_VEC rhs(); - - /** method to return preconditioner (identity matrix) */ - virtual DIAG_MAT preconditioner(FIELDS & fields); - + virtual void R(const DENS_VEC &f, DENS_VEC &v) const; + virtual void solution(const DENS_MAT & dx, DENS_MAT &x) const; + virtual void rhs(const DENS_MAT & r, DENS_MAT &rhs) const; protected: - // field name of ODE to solve - FieldName fieldName_; - - // Reference to current fields (passed in constructor) - FIELDS & fields_; - + void to_all(const VECTOR& free, MATRIX& all) const; + void to_free(const MATRIX& all, VECTOR& free) const; + FieldName fieldName_; // field name of ODE to solve + ATC_Coupling * atc_; /** Pointer to atc */ + const PhysicsModel * physicsModel_; /** Pointer to PhysicsModel */ + FIELDS & fields0_; // Reference to current fields (passed in constructor) // Local fields - mutable FIELDS fieldsNp1_; - - // Vector to hold current temperature - DENS_VEC TnVect_; - - // Old and new RHS maps (not including inverse mass) - FIELDS RnMap_; - mutable FIELDS RnpMap_; - - // Matrices/vectors to hold electron temperature components of RHS - // vectors (including inverse mass) - DENS_VEC RnVect_; - mutable DENS_VEC RnpVect_; - + mutable FIELDS fields_; // full + // set of fixed nodes + std::map<int,int> freeNodes_; + // masks Array2D<bool> rhsMask_; Array<FieldName> massMask_; - + // right hand side of dot x = R(x) + mutable FIELDS rhs_; // full // simulation time double time_; - - // timestep - double dt_; - - // implicit/explicit parameter (0 -> explicit, else implicit) - double alpha_; - - // small parameter to compute increment - double epsilon0_; - }; } // namespace ATC #endif diff --git a/lib/atc/KD_Tree.cpp b/lib/atc/KD_Tree.cpp index a28e54b7a..362bd70d3 100644 --- a/lib/atc/KD_Tree.cpp +++ b/lib/atc/KD_Tree.cpp @@ -1,164 +1,169 @@ #include "KD_Tree.h" #include <assert.h> using std::vector; KD_Tree *KD_Tree::create_KD_tree(const int nNodesPerElem, const int nNodes, const DENS_MAT *nodalCoords, const int nElems, const Array2D<int> &conn) { vector<Node> *points = new vector<Node>(); // Initialize an empty list of Nodes for (int node = 0; node < nNodes; node++) { // Insert all nodes into list points->push_back(Node(node, (*nodalCoords)(0, node), (*nodalCoords)(1, node), (*nodalCoords)(2, node))); } vector<Elem> *elements = new vector<Elem>(); for (int elem = 0; elem < nElems; elem++) { vector<Node> nodes = vector<Node>(); for (int node = 0; node < nNodesPerElem; node++) { nodes.push_back((*points)[conn(node, elem)]); } elements->push_back(Elem(elem, nodes)); } return new KD_Tree(points, elements); } KD_Tree::~KD_Tree() { delete sortedPts_; + delete candElems_; delete leftChild_; delete rightChild_; } KD_Tree::KD_Tree(vector<Node> *points, vector<Elem> *elements, int dimension) : candElems_(elements) { // Set up comparison functions bool (*compare)(Node, Node); if (dimension == 0) { compare = Node::compareX; } else if (dimension == 1) { compare = Node::compareY; } else { compare = Node::compareZ; } // Sort points by their coordinate in the current dimension sort(points->begin(), points->end(), compare); sortedPts_ = points; // Pick the median point as the root of the tree size_t nNodes = points->size(); size_t med = nNodes/2; value_ = (*sortedPts_)[med]; // Recursively construct the left sub-tree vector<Node> *leftPts = new vector<Node>; vector<Elem> *leftElems = new vector<Elem>; // Recursively construct the right sub-tree vector<Node> *rightPts = new vector<Node>; vector<Elem> *rightElems = new vector<Elem>; for (vector<Elem>::iterator elit = candElems_->begin(); elit != candElems_->end(); elit++) { // Identify elements that should be kept on either side bool foundElemLeft = false; bool foundElemRight = false; for (vector<Node>::iterator ndit = elit->second.begin(); ndit != elit->second.end(); ndit++) { // Search this node if (compare(*ndit, value_)) { if (find(leftPts->begin(), leftPts->end(), *ndit) == leftPts->end()) { leftPts->push_back(*ndit); } foundElemLeft = true; } if (compare(value_, *ndit)) { if (find(rightPts->begin(), rightPts->end(), *ndit) == rightPts->end()) { rightPts->push_back(*ndit); } foundElemRight = true; } } if (foundElemLeft) leftElems->push_back(*elit); if (foundElemRight) rightElems->push_back(*elit); } // Create child tree, or NULL if there's nothing to create if (candElems_->size() - leftElems->size() < 4 || leftElems->size() == 0) { leftChild_ = NULL; + delete leftPts; + delete leftElems; } else { leftChild_ = new KD_Tree(leftPts, leftElems, (dimension+1) % 3); } // Create child tree, or NULL if there's nothing to create if (candElems_->size() - rightElems->size() < 4 || rightElems->size() == 0) { rightChild_ = NULL; + delete rightPts; + delete rightElems; } else { rightChild_ = new KD_Tree(rightPts, rightElems, (dimension+1) % 3); } } vector<int> KD_Tree::find_nearest_elements(Node query, int dimension) { // if the root coordinate is less than the query coordinate // If the query point is less that the value (split) point of this // tree, either recurse to the left or return this node's elements // if there is no left child. if (query.lessThanInDimension(value_, dimension)) { if (leftChild_ == NULL) { vector<int> result = vector<int>(); for (vector<Elem>::iterator elem = candElems_->begin(); elem != candElems_->end(); elem++) { result.push_back(elem->first); } return result; } return leftChild_->find_nearest_elements(query, (dimension+1) % 3); } else { if (rightChild_ == NULL) { vector<int> result = vector<int>(); for (vector<Elem>::iterator elem = candElems_->begin(); elem != candElems_->end(); elem++) { result.push_back(elem->first); } return result; } return rightChild_->find_nearest_elements(query, (dimension+1) % 3); } } vector<vector<int> > KD_Tree::getElemIDs(int depth) { vector<vector<int> > result; vector<vector<int> > temp; assert(depth >= 0 ); if (depth == 0) { vector<int> candElemIDs; vector<Elem>::iterator it; for(it = candElems_->begin(); it != candElems_->end(); ++it) { candElemIDs.push_back((*it).first); } sort(candElemIDs.begin(), candElemIDs.end()); result.push_back(candElemIDs); } else if (leftChild_ == NULL || rightChild_ == NULL) { // Insert all nodes at this level once, // then insert a bunch of empty vectors. temp = this->getElemIDs(0); result.insert(result.end(), temp.begin(), temp.end()); int numRequested = floor(pow(2,depth)); for (int i = 0; i < numRequested - 1; ++i) { vector<int> emptyVec; result.push_back(emptyVec); } } else { --depth; temp = leftChild_->getElemIDs(depth); result.insert(result.end(), temp.begin(), temp.end()); temp = rightChild_->getElemIDs(depth); result.insert(result.end(), temp.begin(), temp.end()); } return result; } diff --git a/lib/atc/KernelFunction.cpp b/lib/atc/KernelFunction.cpp index 214d81f5d..91f99a614 100644 --- a/lib/atc/KernelFunction.cpp +++ b/lib/atc/KernelFunction.cpp @@ -1,726 +1,726 @@ #include "KernelFunction.h" #include "math.h" #include <vector> #include "ATC_Error.h" #include "Quadrature.h" #include "Utility.h" using namespace std; using namespace ATC_Utility; static const double tol = 1.0e-8; static const int line_ngauss = 10; static double line_xg[line_ngauss], line_wg[line_ngauss]; namespace ATC { //======================================================================== // KernelFunctionMgr //======================================================================== KernelFunctionMgr * KernelFunctionMgr::myInstance_ = NULL; //------------------------------------------------------------------------ // instance //------------------------------------------------------------------------ KernelFunctionMgr * KernelFunctionMgr::instance() { if (myInstance_ == NULL) { myInstance_ = new KernelFunctionMgr(); } return myInstance_; } //------------------------------------------------------------------------ // get function from args //------------------------------------------------------------------------ KernelFunction* KernelFunctionMgr::function(char ** arg, int narg) { /*! \page man_kernel_function fix_modify AtC kernel \section syntax fix_modify AtC kernel <type> <parameters> - 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 */ int argIdx = 0; KernelFunction * ptr = NULL; char* type = arg[argIdx++]; if (strcmp(type,"step")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionStep(1,parameters); } else if (strcmp(type,"cell")==0) { double parameters[3]; parameters[0] = parameters[1] = parameters[2] = atof(arg[argIdx++]); if (narg > argIdx) { // L_x, L_y, L_z for (int i = 1; i < 3; i++) { parameters[i] = atof(arg[argIdx++]); } } ptr = new KernelFunctionCell(2,parameters); } else if (strcmp(type,"cubic_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionCubicBar(1,parameters); } else if (strcmp(type,"linear_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionLinearBar(1,parameters); } else if (strcmp(type,"cubic_cylinder")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionCubicCyl(1,parameters); } else if (strcmp(type,"cubic_sphere")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionCubicSphere(1,parameters); } else if (strcmp(type,"quartic_bar")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff half-length ptr = new KernelFunctionQuarticBar(1,parameters); } else if (strcmp(type,"quartic_cylinder")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionQuarticCyl(1,parameters); } else if (strcmp(type,"quartic_sphere")==0) { double parameters[1] = {atof(arg[argIdx])}; // cutoff radius ptr = new KernelFunctionQuarticSphere(1,parameters); } pointerSet_.insert(ptr); return ptr; } // Destructor KernelFunctionMgr::~KernelFunctionMgr() { set<KernelFunction * >::iterator it; for (it = pointerSet_.begin(); it != pointerSet_.end(); it++) if (*it) delete *it; } //------------------------------------------------------------------------ // KernelFunction //------------------------------------------------------------------------ // constructor KernelFunction::KernelFunction(int nparameters, double* parameters): Rc_(0),invRc_(0),nsd_(3), lammpsInterface_(LammpsInterface::instance()) { Rc_ = parameters[0]; invRc_ = 1.0/Rc_; Rc_ = parameters[0]; invRc_ = 1.0/Rc_; - invVol_ = 1.0/(4.0/3.0*Pi*pow(Rc_,3)); + invVol_ = 1.0/(4.0/3.0*Pi_*pow(Rc_,3)); ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg); // get periodicity and box bounds/lengths 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 < 3; k++) { box_length[k] = box_bounds[1][k] - box_bounds[0][k]; } } // does an input node's kernel intersect bonds on this processor bool KernelFunction::node_contributes(DENS_VEC node) const { DENS_VEC ghostNode = node; bool contributes = true; bool ghostContributes = lammpsInterface_->nperiodic(); double kernel_bounds[2][3]; lammpsInterface_->sub_bounds(kernel_bounds[0][0],kernel_bounds[1][0], kernel_bounds[0][1],kernel_bounds[1][1], kernel_bounds[0][2],kernel_bounds[1][2]); for (int i=0; i<3; ++i) { if (i < nsd_) { kernel_bounds[0][i] -= (Rc_+lammpsInterface_->pair_cutoff()); kernel_bounds[1][i] += (Rc_+lammpsInterface_->pair_cutoff()); contributes = contributes && (node(i) >= kernel_bounds[0][i]) && (node(i) < kernel_bounds[1][i]); if (periodicity[i]) { if (node[i] <= box_bounds[0][i] + box_length[i]/2) { ghostNode[i] += box_length[i]; } else { ghostNode[i] -= box_length[i]; } ghostContributes = ghostContributes && ((ghostNode(i) >= kernel_bounds[0][i]) || (node(i) >= kernel_bounds[0][i])) && ((ghostNode(i) < kernel_bounds[1][i]) || (node(i) < kernel_bounds[1][i])); } } if (!(contributes || ghostContributes)) break; } return true; } bool KernelFunction::in_support(DENS_VEC dx) const { if (dx.norm() > Rc_) return false; return true; } // bond function value via quadrature double KernelFunction::bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) const { DENS_VEC xab(nsd_), q(nsd_); double lamg; double bhsum=0.0; xab = xa - xb; for (int i = 0; i < line_ngauss; i++) { lamg=0.5*((lam2-lam1)*line_xg[i]+(lam2+lam1)); q = lamg*xab + xb; double locg_value=this->value(q); bhsum+=locg_value*line_wg[i]; } return 0.5*(lam2-lam1)*bhsum; } // localization-volume intercepts for bond calculation // bond intercept values assuming spherical support void KernelFunction::bond_intercepts(DENS_VEC& xa, DENS_VEC& xb, double &lam1, double &lam2) const { if (nsd_ == 2) {// for cylinders, axis is always z! const int iaxis = 2; xa[iaxis] = 0.0; xb[iaxis] = 0.0; } else if (nsd_ == 1) {// for bars, analysis is 1D in x xa[1] = 0.0; xa[2] = 0.0; xb[1] = 0.0; xb[2] = 0.0; } lam1 = lam2 = -1; double ra_n = invRc_*xa.norm(); // lambda = 1 double rb_n = invRc_*xb.norm(); // lambda = 0 bool a_in = (ra_n <= 1.0); bool b_in = (rb_n <= 1.0); if (a_in && b_in) { lam1 = 0.0; lam2 = 1.0; return; } DENS_VEC xab = xa - xb; double rab_n = invRc_*xab.norm(); double a = rab_n*rab_n; // always at least an interatomic distance double b = 2.0*invRc_*invRc_*xab.dot(xb); double c = rb_n*rb_n - 1.0; double discrim = b*b - 4.0*a*c; // discriminant if (discrim < 0) return; // no intersection // num recipes: double s1, s2; if (b < 0.0) { double aux = -0.5*(b-sqrt(discrim)); s1 = c/aux; s2 = aux/a; } else { double aux = -0.5*(b+sqrt(discrim)); s1 = aux/a; s2 = c/aux; } if (a_in && !b_in) { lam1 = s1; lam2 = 1.0; } else if (!a_in && b_in) { lam1 = 0.0; lam2 = s2; } else { if (s1 >= 0.0 && s2 <= 1.0) { lam1 = s1; lam2 = s2; } } } //------------------------------------------------------------------------ // constructor KernelFunctionStep::KernelFunctionStep (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); } } } } // function value double KernelFunctionStep::value(DENS_VEC& x_atom) const { double rn=invRc_*x_atom.norm(); if (rn <= 1.0) { return 1.0; } else { return 0.0; } } // function derivative value void KernelFunctionStep::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ /** a step with rectangular support suitable for a rectangular grid */ // constructor KernelFunctionCell::KernelFunctionCell (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { hx = parameters[0]; hy = parameters[1]; hz = parameters[2]; invVol_ = 1.0/8.0/(hx*hy*hz); cellBounds_.reset(6); cellBounds_(0) = -hx; cellBounds_(1) = hx; cellBounds_(2) = -hy; cellBounds_(3) = hy; cellBounds_(4) = -hz; cellBounds_(5) = hz; for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (parameters[k] > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); } } } } // does an input node's kernel intersect bonds on this processor bool KernelFunctionCell::node_contributes(DENS_VEC node) const { DENS_VEC ghostNode = node; bool contributes = true; bool ghostContributes = lammpsInterface_->nperiodic(); double kernel_bounds[2][3]; lammpsInterface_->sub_bounds(kernel_bounds[0][0],kernel_bounds[1][0], kernel_bounds[0][1],kernel_bounds[1][1], kernel_bounds[0][2],kernel_bounds[1][2]); for (int i=0; i<3; ++i) { kernel_bounds[0][i] -= (cellBounds_(i*2+1) + lammpsInterface_->pair_cutoff()); kernel_bounds[1][i] += (cellBounds_(i*2+1) + lammpsInterface_->pair_cutoff()); contributes = contributes && (node(i) >= kernel_bounds[0][i]) && (node(i) < kernel_bounds[1][i]); if (periodicity[i]) { if (node[i] <= box_bounds[0][i] + box_length[i]/2) { ghostNode[i] += box_length[i]; } else { ghostNode[i] -= box_length[i]; } ghostContributes = ghostContributes && ((ghostNode(i) >= kernel_bounds[0][i]) || (node(i) >= kernel_bounds[0][i])) && ((ghostNode(i) < kernel_bounds[1][i]) || (node(i) < kernel_bounds[1][i])); } if (!(contributes || ghostContributes)) break; } return true; } bool KernelFunctionCell::in_support(DENS_VEC dx) const { if (dx(0) < cellBounds_(0) || dx(0) > cellBounds_(1) || dx(1) < cellBounds_(2) || dx(1) > cellBounds_(3) || dx(2) < cellBounds_(4) || dx(2) > cellBounds_(5) ) return false; return true; } // function value double KernelFunctionCell::value(DENS_VEC& x_atom) const { if ((cellBounds_(0) <= x_atom(0)) && (x_atom(0) < cellBounds_(1)) && (cellBounds_(2) <= x_atom(1)) && (x_atom(1) < cellBounds_(3)) && (cellBounds_(4) <= x_atom(2)) && (x_atom(2) < cellBounds_(5))) { return 1.0; } else { return 0.0; } } // function derivative value void KernelFunctionCell::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } // bond intercept values for rectangular region : origin is the node position void KernelFunctionCell::bond_intercepts(DENS_VEC& xa, DENS_VEC& xb, double &lam1, double &lam2) const { lam1 = 0.0; // start lam2 = 1.0; // end bool a_in = (value(xa) > 0.0); bool b_in = (value(xb) > 0.0); // (1) both in, no intersection needed if (a_in && b_in) { return; } // (2) for one in & one out -> one plane intersection // determine the points of intersection between the line joining // atoms a and b and the bounding planes of the localization volume else if (a_in || b_in) { DENS_VEC xab = xa - xb; for (int i = 0; i < nsd_; i++) { // check if segment is parallel to face if (fabs(xab(i)) > tol) { for (int j = 0; j < 2; j++) { double s = (cellBounds_(2*i+j) - xb(i))/xab(i); // check if between a & b if (s >= 0 && s <= 1) { bool in_bounds = false; DENS_VEC x = xb + s*xab; if (i == 0) { if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { in_bounds = true; } } else if (i == 1) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { in_bounds = true; } } else if (i == 2) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { in_bounds = true; } } if (in_bounds) { if (a_in) { lam1 = s;} else { lam2 = s;} return; } } } } } throw ATC_Error("logic failure in HardyKernel Cell for single intersection\n"); } // (3) both out -> corner intersection else { lam2 = lam1; // default to no intersection DENS_VEC xab = xa - xb; double ss[6] = {-1,-1,-1,-1,-1,-1}; int is = 0; for (int i = 0; i < nsd_; i++) { // check if segment is parallel to face if (fabs(xab(i)) > tol) { for (int j = 0; j < 2; j++) { double s = (cellBounds_(2*i+j) - xb(i))/xab(i); // check if between a & b if (s >= 0 && s <= 1) { // check if in face DENS_VEC x = xb + s*xab; if (i == 0) { if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { ss[is++] = s; } } else if (i == 1) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { ss[is++] = s; } } else if (i == 2) { if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { ss[is++] = s; } } } } } } if (is == 1) { // intersection occurs at a box edge - leave lam1 = lam2 } else if (is == 2) { lam1 = min(ss[0],ss[1]); lam2 = max(ss[0],ss[1]); } else if (is == 3) { // intersection occurs at a box vertex - leave lam1 = lam2 } else { if (is != 0) throw ATC_Error("logic failure in HardyKernel Cell for corner intersection\n"); } } } //------------------------------------------------------------------------ // constructor KernelFunctionCubicSphere::KernelFunctionCubicSphere (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionCubicSphere::value(DENS_VEC& x_atom) const { double r=x_atom.norm(); double rn=r/Rc_; if (rn < 1.0) { return 5.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicSphere::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticSphere::KernelFunctionQuarticSphere (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionQuarticSphere::value(DENS_VEC& x_atom) const { double r=x_atom.norm(); double rn=r/Rc_; if (rn < 1.0) { return 35.0/8.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticSphere::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionCubicCyl::KernelFunctionCubicCyl (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { nsd_ = 2; double Lz = box_length[2]; - invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionCubicCyl::value(DENS_VEC& x_atom) const { double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); double rn=r/Rc_; if (rn < 1.0) { return 10.0/3.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicCyl::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticCyl::KernelFunctionQuarticCyl (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { nsd_ = 2; double Lz = box_length[2]; - invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + invVol_ = 1.0/(Pi_*pow(Rc_,2)*Lz); for (int k = 0; k < nsd_; k++ ) { if ((bool) periodicity[k]) { if (Rc_ > 0.5*box_length[k]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; }; } // function value double KernelFunctionQuarticCyl::value(DENS_VEC& x_atom) const { double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); double rn=r/Rc_; if (rn < 1.0) { return 3.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticCyl::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionCubicBar::KernelFunctionCubicBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the x(0) direction nsd_ = 1; double Ly = box_length[1]; double Lz = box_length[2]; invVol_ = 1.0/(2*Rc_*Ly*Lz); if ((bool) periodicity[0]) { if (Rc_ > 0.5*box_length[0]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionCubicBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(0)); double rn=r/Rc_; if (rn < 1.0) { return 2.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } else { return 0.0; } } // function derivative value void KernelFunctionCubicBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } //------------------------------------------------------------------------ // constructor KernelFunctionLinearBar::KernelFunctionLinearBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the z(0) direction double Lx = box_length[0]; double Ly = box_length[1]; invVol_ = 1.0/(Lx*Ly*Rc_); if ((bool) periodicity[2]) { if (Rc_ > 0.5*box_length[2]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionLinearBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(2)); double rn=r/Rc_; if (rn < 1.0) { return 1.0-rn; } else { return 0.0; } } // function derivative value void KernelFunctionLinearBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); deriv(0) = 0.0; deriv(1) = 0.0; double r=abs(x_atom(2)); double rn=r/Rc_; if (rn < 1.0 && x_atom(2) <= 0.0) { deriv(2) = 1.0/Rc_; } else if (rn < 1.0 && x_atom(2) > 0.0) { deriv(2) = -1.0/Rc_; } else { deriv(2) = 0.0; } } //------------------------------------------------------------------------ // constructor KernelFunctionQuarticBar::KernelFunctionQuarticBar (int nparameters, double* parameters): KernelFunction(nparameters, parameters) { // Note: Bar is assumed to be oriented in the x(0) direction nsd_ = 1; double Ly = box_length[1]; double Lz = box_length[2]; invVol_ = 1.0/(2*Rc_*Ly*Lz); if ((bool) periodicity[0]) { if (Rc_ > 0.5*box_length[0]) { throw ATC_Error("Size of localization volume is too large for periodic boundary condition"); }; }; } // function value double KernelFunctionQuarticBar::value(DENS_VEC& x_atom) const { double r=abs(x_atom(0)); double rn=r/Rc_; // if (rn < 1.0) { return 5.0/2.0*(1.0-6*rn*rn+8*rn*rn*rn-3*rn*rn*rn*rn); } - alternative quartic if (rn < 1.0) { return 15.0/8.0*pow((1.0-rn*rn),2); } else { return 0.0; } } // function derivative value void KernelFunctionQuarticBar::derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const { deriv.reset(nsd_); } }; diff --git a/lib/atc/KinetoThermostat.cpp b/lib/atc/KinetoThermostat.cpp index cf3b9c29c..bdc11238a 100644 --- a/lib/atc/KinetoThermostat.cpp +++ b/lib/atc/KinetoThermostat.cpp @@ -1,436 +1,703 @@ #include "KinetoThermostat.h" #include "ATC_Coupling.h" #include "ATC_Error.h" #include "PrescribedDataManager.h" #include "ElasticTimeIntegrator.h" #include "ThermalTimeIntegrator.h" #include "TransferOperator.h" using namespace std; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetoThermostat //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- KinetoThermostat::KinetoThermostat(ATC_Coupling * atc, const string & regulatorPrefix) : AtomicRegulator(atc,regulatorPrefix), - kinetostat_(atc,"Momentum"), - thermostat_(atc,"Energy") + couplingMaxIterations_(myCouplingMaxIterations) { // nothing to do } //-------------------------------------------------------- // modify: // parses and adjusts thermostat state based on // user input, in the style of LAMMPS user input //-------------------------------------------------------- bool KinetoThermostat::modify(int narg, char **arg) { bool foundMatch = false; return foundMatch; } //-------------------------------------------------------- // reset_lambda_contribution: // resets the thermostat generated power to a // prescribed value //-------------------------------------------------------- void KinetoThermostat::reset_lambda_contribution(const DENS_MAT & target, const FieldName field) { - //TEMP_JAT if (field==VELOCITY) { - kinetostat_.reset_lambda_contribution(target); + DENS_MAN * lambdaForceFiltered = regulator_data("LambdaForceFiltered",atc_->nsd()); + *lambdaForceFiltered = target; } else if (field == TEMPERATURE) { - thermostat_.reset_lambda_contribution(target); + DENS_MAN * lambdaPowerFiltered = regulator_data("LambdaPowerFiltered",1); + *lambdaPowerFiltered = target; } else { throw ATC_Error("KinetoThermostat::reset_lambda_contribution - invalid field given"); } } //-------------------------------------------------------- // construct_methods: // instantiations desired regulator method(s) // dependence, but in general there is also a // time integrator dependence. In general the // precedence order is: // time filter -> time integrator -> thermostat // In the future this may need to be added if // different types of time integrators can be // specified. //-------------------------------------------------------- void KinetoThermostat::construct_methods() { -// #ifdef WIP_JAT -// // get data associated with stages 1 & 2 of ATC_Method::initialize -// AtomicRegulator::construct_methods(); - -// if (atc_->reset_methods()) { -// // eliminate existing methods -// delete_method(); - -// // update time filter -// TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); -// TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); -// if (timeFilterManager->need_reset() ) { -// if (myIntegrationType == TimeIntegrator::GEAR) -// timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); -// else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) -// timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); -// } + // get data associated with stages 1 & 2 of ATC_Method::initialize + AtomicRegulator::construct_methods(); + + if (atc_->reset_methods()) { + // eliminate existing methods + delete_method(); + + // error check time integration methods + TimeIntegrator::TimeIntegrationType myEnergyIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); + TimeIntegrator::TimeIntegrationType myMomentumIntegrationType = (atc_->time_integrator(VELOCITY))->time_integration_type(); + if (myEnergyIntegrationType != TimeIntegrator::FRACTIONAL_STEP || myMomentumIntegrationType != TimeIntegrator::FRACTIONAL_STEP) { + throw ATC_Error("KinetoThermostat::construct_methods - this scheme only valid with fractional step integration"); + } + + // update time filter + TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); + if (timeFilterManager->need_reset() ) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } -// if (timeFilterManager->filter_dynamics()) { -// switch (regulatorTarget_) { -// case NONE: { -// regulatorMethod_ = new RegulatorMethod(this); -// break; -// } -// case FIELD: { // error check, rescale and filtering not supported together -// throw ATC_Error("Cannot use rescaling thermostat with time filtering"); -// break; -// } -// case DYNAMICS: { -// switch (couplingMode_) { -// case FIXED: { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); -// } -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (md_flux_nodes(TEMPERATURE)) { -// if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fluxes but no fixed or coupled nodes -// regulatorMethod_ = new ThermostatFluxFiltered(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixedFiltered(this); -// } -// } -// else { -// // there are only fixed nodes -// regulatorMethod_ = new ThermostatFixedFiltered(this); -// } -// } -// else { -// regulatorMethod_ = new ThermostatHooverVerletFiltered(this); -// } -// break; -// } -// case FLUX: { -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); -// } -// if (md_fixed_nodes(TEMPERATURE)) { -// if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fixed nodes but no fluxes -// regulatorMethod_ = new ThermostatFixedFiltered(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixedFiltered(this); -// } -// } -// else { -// // there are only flux nodes -// regulatorMethod_ = new ThermostatFluxFiltered(this); -// } -// } -// else { -// if (use_localized_lambda()) { -// if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && -// atc_->boundary_integration_type() != NO_QUADRATURE) { -// throw ATC_Error("Cannot use flux coupling with localized lambda"); -// } -// } -// regulatorMethod_ = new ThermostatPowerVerletFiltered(this); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown thermostat type in Thermostat::initialize"); -// } -// } -// else { -// switch (regulatorTarget_) { -// case NONE: { -// regulatorMethod_ = new RegulatorMethod(this); -// break; -// } -// case FIELD: { -// if (atc_->temperature_def()==KINETIC) -// regulatorMethod_ = new ThermostatRescale(this); -// else if (atc_->temperature_def()==TOTAL) -// regulatorMethod_ = new ThermostatRescaleMixedKePe(this); -// else -// throw ATC_Error("Unknown temperature definition"); -// break; -// } -// case DYNAMICS: { -// switch (couplingMode_) { -// case FIXED: { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); -// } -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (md_flux_nodes(TEMPERATURE)) { -// if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fluxes but no fixed or coupled nodes -// regulatorMethod_ = new ThermostatFlux(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixed(this); -// } -// } -// else { -// // there are only fixed nodes -// regulatorMethod_ = new ThermostatFixed(this); -// } -// } -// else { -// regulatorMethod_ = new ThermostatHooverVerlet(this); -// } -// break; -// } -// case FLUX: { -// if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { -// if (use_lumped_lambda_solve()) { -// throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); -// } -// if (md_fixed_nodes(TEMPERATURE)) { -// if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { -// // there are fixed nodes but no fluxes -// regulatorMethod_ = new ThermostatFixed(this); -// } -// else { -// // there are both fixed and flux nodes -// regulatorMethod_ = new ThermostatFluxFixed(this); -// } -// } -// else { -// // there are only flux nodes -// regulatorMethod_ = new ThermostatFlux(this); -// } -// } -// else { -// if (use_localized_lambda()) { -// if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && -// atc_->boundary_integration_type() != NO_QUADRATURE) { -// throw ATC_Error("Cannot use flux coupling with localized lambda"); -// } -// } -// regulatorMethod_ = new ThermostatPowerVerlet(this); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); -// } -// break; -// } -// default: -// throw ATC_Error("Unknown thermostat target in Thermostat::initialize"); -// } -// } + if (timeFilterManager->filter_dynamics()) { + switch (regulatorTarget_) { + case NONE: { + regulatorMethod_ = new RegulatorMethod(this); + break; + } + case FIELD: { // error check, rescale and filtering not supported together + throw ATC_Error("KinetoThermostat::construct_methods - Cannot use rescaling thermostat with time filtering"); + break; + } + case DYNAMICS: { + } + default: + throw ATC_Error("Unknown thermostat type in Thermostat::initialize"); + } + } + else { + switch (regulatorTarget_) { + case NONE: { + regulatorMethod_ = new RegulatorMethod(this); + break; + } + case FIELD: { + if (atc_->temperature_def()==KINETIC) { + regulatorMethod_ = new KinetoThermostatRescale(this,couplingMaxIterations_); + } + else if (atc_->temperature_def()==TOTAL) { + regulatorMethod_ = new KinetoThermostatRescaleMixedKePe(this,couplingMaxIterations_); + } + else + throw ATC_Error("Unknown temperature definition"); + break; + } + default: + throw ATC_Error("Unknown thermostat target in Thermostat::initialize"); + } + } -// AtomicRegulator::reset_method(); -// } -// else { -// set_all_data_to_used(); -// } -// #endif - //TEMP_JAT - kinetostat_.construct_methods(); - thermostat_.construct_methods(); + AtomicRegulator::reset_method(); + } + else { + set_all_data_to_used(); + } } - //TEMP_JAT all functions which have explicit thermo and kinetostats need to be removed - //-------------------------------------------------------- - // reset_nlocal: - // resizes lambda force if necessary //-------------------------------------------------------- - void KinetoThermostat::reset_nlocal() + //-------------------------------------------------------- + // Class VelocityRescaleCombined + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + VelocityRescaleCombined::VelocityRescaleCombined(AtomicRegulator * kinetostat) : + VelocityGlc(kinetostat), + velocity_(atc_->field(VELOCITY)), + thermostatCorrection_(NULL) { - kinetostat_.reset_nlocal(); - thermostat_.reset_nlocal(); + // do nothing } + //-------------------------------------------------------- - // reset_atom_materials: - // resets the localized atom to material map + // initialize + // initializes all data //-------------------------------------------------------- - void KinetoThermostat::reset_atom_materials(const Array<int> & elementToMaterialMap, - const MatrixDependencyManager<DenseMatrix, int> * atomElement) + void VelocityRescaleCombined::initialize() { - kinetostat_.reset_atom_materials(elementToMaterialMap, - atomElement); - thermostat_.reset_atom_materials(elementToMaterialMap, - atomElement); + VelocityGlc::initialize(); + thermostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicFluctuatingMomentumRescaled"); } + //-------------------------------------------------------- - // construct_transfers: - // pass through to appropriate transfer constuctors + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations //-------------------------------------------------------- - void KinetoThermostat::construct_transfers() + void VelocityRescaleCombined::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { - kinetostat_.construct_transfers(); - thermostat_.construct_transfers(); + rhs = ((atc_->mass_mat_md(VELOCITY)).quantity())*(velocity_.quantity()); + rhs -= thermostatCorrection_->quantity(); } + + //-------------------------------------------------------- //-------------------------------------------------------- - // initialize: - // sets up methods before a run + // Class ThermostatRescaleCombined //-------------------------------------------------------- - void KinetoThermostat::initialize() + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatRescaleCombined::ThermostatRescaleCombined(AtomicRegulator * thermostat) : + ThermostatRescale(thermostat), + kinetostatCorrection_(NULL) { - kinetostat_.initialize(); - thermostat_.initialize(); - needReset_ = false; + // do nothing } + //-------------------------------------------------------- - // output: - // pass through to appropriate output methods + // initialize + // initializes all data //-------------------------------------------------------- - void KinetoThermostat::output(OUTPUT_LIST & outputData) const + void ThermostatRescaleCombined::initialize() { - kinetostat_.output(outputData); - thermostat_.output(outputData); + ThermostatRescale::initialize(); + kinetostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicCombinedRescaleThermostatError"); } + //-------------------------------------------------------- - // finish: - // pass through to appropriate end-of-run methods + // set_rhs: + // constructs the RHS vector with the target + // temperature //-------------------------------------------------------- - void KinetoThermostat::finish() + void ThermostatRescaleCombined::set_rhs(DENS_MAT & rhs) { - kinetostat_.finish(); - thermostat_.finish(); + ThermostatRescale::set_rhs(rhs); + rhs -= kinetostatCorrection_->quantity(); } - //-------------------------------------------------- - // pack_fields - // bundle all allocated field matrices into a list - // for output needs - //-------------------------------------------------- - void KinetoThermostat::pack_fields(RESTART_LIST & data) + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatRescale + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatRescale::KinetoThermostatRescale(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations) : + KinetoThermostatShapeFunction(kinetoThermostat,couplingMaxIterations), + atomVelocities_(NULL), + nodalVelocities_(atc_->field(VELOCITY)), + lambdaMomentum_(NULL), + lambdaEnergy_(NULL), + atomicFluctuatingVelocityRescaled_(NULL), + atomicStreamingVelocity_(NULL), + thermostat_(NULL), + kinetostat_(NULL) { - kinetostat_.pack_fields(data); - thermostat_.pack_fields(data); + thermostat_ = this->construct_rescale_thermostat(); + kinetostat_ = new VelocityRescaleCombined(kinetoThermostat); + // data associated with stage 3 in ATC_Method::initialize + lambdaMomentum_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaMomentum",atc_->nsd()); + lambdaEnergy_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); } + //-------------------------------------------------------- - // compute_boundary_flux: - // computes the boundary flux to be consistent with - // the controller + // Destructor //-------------------------------------------------------- - void KinetoThermostat::compute_boundary_flux(FIELDS & fields) + KinetoThermostatRescale::~KinetoThermostatRescale() { - kinetostat_.compute_boundary_flux(fields); - thermostat_.compute_boundary_flux(fields); + if (thermostat_) delete thermostat_; + if (kinetostat_) delete kinetostat_; } + //-------------------------------------------------------- - // add_to_rhs: - // adds any controller contributions to the FE rhs + // constructor_transfers + // instantiates or obtains all dependency managed data //-------------------------------------------------------- - void KinetoThermostat::add_to_rhs(FIELDS & rhs) + void KinetoThermostatRescale::construct_transfers() { - thermostat_.add_to_rhs(rhs); - kinetostat_.add_to_rhs(rhs); + // construct independent transfers first + thermostat_->construct_transfers(); + kinetostat_->construct_transfers(); + + InterscaleManager & interscaleManager(atc_->interscale_manager()); + + // get atom velocity data from manager + atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); + + // transfers requiring terms from both regulators + // rescaled velocity fluctuations + atomicFluctuatingVelocityRescaled_ = new AtomicFluctuatingVelocityRescaled(atc_); + interscaleManager.add_per_atom_quantity(atomicFluctuatingVelocityRescaled_, + "AtomFluctuatingVelocityRescaled"); + + // streaming velocity component + atomicStreamingVelocity_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); + + // rescaled momentum fluctuations, error term for kinetostat rhs + PerAtomQuantity<double> * tempAtom = new AtomicMomentum(atc_, + atomicFluctuatingVelocityRescaled_); + interscaleManager.add_per_atom_quantity(tempAtom,"AtomFluctuatingMomentumRescaled"); + DENS_MAN * tempNodes = new AtfShapeFunctionRestriction(atc_, + tempAtom, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(tempNodes, + "NodalAtomicFluctuatingMomentumRescaled"); + + // error term for thermostat rhs + tempAtom = new AtomicCombinedRescaleThermostatError(atc_); + interscaleManager.add_per_atom_quantity(tempAtom,"AtomCombinedRescaleThermostatError"); + tempNodes = new AtfShapeFunctionRestriction(atc_, + tempAtom, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(tempNodes, + "NodalAtomicCombinedRescaleThermostatError"); } + //-------------------------------------------------------- - // apply_pre_predictor: - // applies the controller in the pre-predictor - // phase of the time integrator + // construct_rescale_thermostat + // constructs the appropriate rescaling thermostat + // varied through inheritance //-------------------------------------------------------- - void KinetoThermostat::apply_pre_predictor(double dt, int timeStep) + ThermostatRescale * KinetoThermostatRescale::construct_rescale_thermostat() { - thermostat_.apply_pre_predictor(dt,timeStep); - kinetostat_.apply_pre_predictor(dt,timeStep); + return new ThermostatRescaleCombined(atomicRegulator_); } + //-------------------------------------------------------- - // apply_mid_predictor: - // applies the controller in the mid-predictor - // phase of the time integrator + // initialize + // initializes all data //-------------------------------------------------------- - void KinetoThermostat::apply_mid_predictor(double dt, int timeStep) + void KinetoThermostatRescale::initialize() { - thermostat_.apply_mid_predictor(dt,timeStep); - kinetostat_.apply_mid_predictor(dt,timeStep); + KinetoThermostatShapeFunction::initialize(); + thermostat_->initialize(); + kinetostat_->initialize(); } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat in the post corrector phase + //-------------------------------------------------------- + void KinetoThermostatRescale::apply_post_corrector(double dt) + { + // initial guesses + lambdaMomentum_->set_quantity() = nodalVelocities_.quantity(); + lambdaEnergy_->set_quantity() = 1.; + + int iteration = 0; + double eErr, pErr; + while (iteration < couplingMaxIterations_) { + _lambdaMomentumOld_ = lambdaMomentum_->quantity(); + _lambdaEnergyOld_ = lambdaEnergy_->quantity(); + + // update thermostat + thermostat_->compute_thermostat(dt); + + // update kinetostat + kinetostat_->compute_kinetostat(dt); + + // check convergence + _diff_ = lambdaEnergy_->quantity() - _lambdaEnergyOld_; + eErr = _diff_.col_norm()/_lambdaEnergyOld_.col_norm(); + _diff_ = lambdaMomentum_->quantity() - _lambdaMomentumOld_; + pErr = _diff_.col_norm()/_lambdaMomentumOld_.col_norm(); + if (eErr < tolerance_ && pErr < tolerance_) { + break; + } + iteration++; + } + if (iteration == couplingMaxIterations_) { + stringstream message; + message << "WARNING: Iterative solve for lambda failed to converge after " << couplingMaxIterations_ << " iterations, final tolerance was " << std::max(eErr,pErr) << "\n"; + ATC::LammpsInterface::instance()->print_msg(message.str()); + } + + // application of rescaling lambda due + apply_to_atoms(atomVelocities_); + } + //-------------------------------------------------------- - // apply_post_predictor: - // applies the controller in the post-predictor - // phase of the time integrator + // apply_lambda_to_atoms: + // applies the velocity rescale with an existing lambda + // note oldAtomicQuantity and dt are not used //-------------------------------------------------------- - void KinetoThermostat::apply_post_predictor(double dt, int timeStep) + void KinetoThermostatRescale::apply_to_atoms(PerAtomQuantity<double> * atomVelocities) { - thermostat_.apply_post_predictor(dt,timeStep); - kinetostat_.apply_post_predictor(dt,timeStep); + *atomVelocities = atomicFluctuatingVelocityRescaled_->quantity() + atomicStreamingVelocity_->quantity(); } + //-------------------------------------------------------- - // apply_pre_corrector: - // applies the controller in the pre-corrector phase - // of the time integrator + // output: + // adds all relevant output to outputData //-------------------------------------------------------- - void KinetoThermostat::apply_pre_corrector(double dt, int timeStep) + void KinetoThermostatRescale::output(OUTPUT_LIST & outputData) { - thermostat_.apply_pre_corrector(dt,timeStep); - kinetostat_.apply_pre_corrector(dt,timeStep); + thermostat_->output(outputData); + kinetostat_->output(outputData); } + //-------------------------------------------------------- - // apply_post_corrector: - // applies the controller in the post-corrector phase - // of the time integrator //-------------------------------------------------------- - void KinetoThermostat::apply_post_corrector(double dt, int timeStep) + // Class ThermostatRescaleMixedKePeCombined + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatRescaleMixedKePeCombined::ThermostatRescaleMixedKePeCombined(AtomicRegulator * thermostat) : + ThermostatRescaleMixedKePe(thermostat), + kinetostatCorrection_(NULL) { - thermostat_.apply_post_corrector(dt,timeStep); - kinetostat_.apply_post_corrector(dt,timeStep); + // do nothing } + //-------------------------------------------------------- - // pre_exchange + // initialize + // initializes all data //-------------------------------------------------------- - void KinetoThermostat::pre_exchange() + void ThermostatRescaleMixedKePeCombined::initialize() { - thermostat_.pre_exchange(); - kinetostat_.pre_exchange(); + ThermostatRescaleMixedKePe::initialize(); + kinetostatCorrection_ = (atc_->interscale_manager()).dense_matrix("NodalAtomicCombinedRescaleThermostatError"); } + //-------------------------------------------------------- - // pre_force + // set_rhs: + // constructs the RHS vector with the target + // temperature //-------------------------------------------------------- - void KinetoThermostat::pre_force() + void ThermostatRescaleMixedKePeCombined::set_rhs(DENS_MAT & rhs) { - thermostat_.pre_force(); - kinetostat_.pre_force(); + ThermostatRescaleMixedKePe::set_rhs(rhs); + rhs -= kinetostatCorrection_->quantity(); } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatRescaleMixedKePe + //-------------------------------------------------------- //-------------------------------------------------------- - // coupling_mode - //------------------------------------------------------- - AtomicRegulator::RegulatorCouplingType KinetoThermostat::coupling_mode(const FieldName field) const + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatRescaleMixedKePe::KinetoThermostatRescaleMixedKePe(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations) : + KinetoThermostatRescale(kinetoThermostat,couplingMaxIterations) { - //TEMP_JAT - if (field==VELOCITY) { - return kinetostat_.coupling_mode(); - } - else if (field == TEMPERATURE) { - return thermostat_.coupling_mode(); - } - else { - throw ATC_Error("KinetoThermostat::coupling_mode - invalid field given"); + // do nothing + } + + //-------------------------------------------------------- + // construct_rescale_thermostat + // constructs the appropriate rescaling thermostat + // varied through inheritance + //-------------------------------------------------------- + ThermostatRescale * KinetoThermostatRescaleMixedKePe::construct_rescale_thermostat() + { + return new ThermostatRescaleMixedKePeCombined(atomicRegulator_); + } + + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class KinetoThermostatGlcFs + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetoThermostatGlcFs::KinetoThermostatGlcFs(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const string & regulatorPrefix) : + KinetoThermostatShapeFunction(kinetoThermostat,couplingMaxIterations,regulatorPrefix), + velocity_(atc_->field(VELOCITY)), + temperature_(atc_->field(TEMPERATURE)), + timeFilter_(atomicRegulator_->time_filter()), + nodalAtomicLambdaForce_(NULL), + lambdaForceFiltered_(NULL), + nodalAtomicLambdaPower_(NULL), + lambdaPowerFiltered_(NULL), + atomRegulatorForces_(NULL), + atomThermostatForces_(NULL), + atomMasses_(NULL), + atomVelocities_(NULL), + isFirstTimestep_(true), + nodalAtomicMomentum_(NULL), + nodalAtomicEnergy_(NULL), + atomPredictedVelocities_(NULL), + nodalAtomicPredictedMomentum_(NULL), + nodalAtomicPredictedEnergy_(NULL), + firstHalfAtomForces_(NULL), + dtFactor_(0.) + { + // construct/obtain data corresponding to stage 3 of ATC_Method::initialize + //nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1); + //lambdaPowerFiltered_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void KinetoThermostatGlcFs::construct_transfers() + { + // BASES INITIALIZE + // GRAB ANY COPIES FROM BASES + + // TOTAL REGULATOR FORCE + // TOTAL FIRST HALF FORCE, IF NECESSARY + } + + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void KinetoThermostatGlcFs::initialize() + { + RegulatorMethod::initialize(); + + // INITIALIZE BASES + + // MAKE SURE ANY NEEDED POINTERS FROM BASES ARE COPIED BY HERE + } + + //-------------------------------------------------------- + // apply_to_atoms: + // determines what if any contributions to the + // atomic moition is needed for + // consistency with the thermostat + // and computes the instantaneous induced power + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, + const DENS_MAN * nodalAtomicMomentum, + const DENS_MAN * nodalAtomicEnergy, + const DENS_MAT & lambdaForce, + DENS_MAT & nodalAtomicLambdaForce, + DENS_MAT & nodalAtomicLambdaPower, + double dt) + { + // compute initial contributions to lambda force and power + nodalAtomicLambdaPower = nodalAtomicEnergy->quantity(); + nodalAtomicLambdaPower *= -1.; + nodalAtomicLambdaForce = nodalAtomicMomentum->quantity(); + nodalAtomicLambdaForce *= -1.; + + // apply lambda force to atoms + _velocityDelta_ = lambdaForce; + _velocityDelta_ /= atomMasses_->quantity(); + _velocityDelta_ *= dt; + (*atomicVelocity) += _velocityDelta_; + + // finalize lambda force and power + nodalAtomicLambdaForce += nodalAtomicMomentum->quantity(); + nodalAtomicLambdaPower += nodalAtomicEnergy->quantity(); + } + + //-------------------------------------------------------- + // full_prediction: + // flag to perform a full prediction calcalation + // for lambda rather than using the old value + //-------------------------------------------------------- + bool KinetoThermostatGlcFs::full_prediction() + { + // CHECK BOTH BASES + return false; + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_pre_predictor(double dt) + { + DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myLambdaPowerFiltered(lambdaPowerFiltered_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + + // update filtered forces power, equivalent to measuring changes in momentum and energy + timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + timeFilter_->apply_pre_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); + + // apply lambda force to atoms and compute instantaneous lambda power for first half of time step + this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,nodalAtomicEnergy_, + firstHalfAtomForces_->quantity(), + nodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + // update nodal variables for first half of time step + // velocity + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy1_,0.5*dt); + + // start update of filtered lambda force and power using temporary (i.e., 0 valued) quantities for first part of update + nodalAtomicLambdaForce = 0.; + timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + myNodalAtomicLambdaPower = 0.; + timeFilter_->apply_post_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_pre_corrector(double dt) + { + // CHECK WHEN CREATING PREDICTED VELOCITIES IN BASE REGULATORS, ONLY NEED ONE + (*atomPredictedVelocities_) = atomVelocities_->quantity(); + + // do full prediction if we just redid the shape functions + if (full_prediction()) { + this->compute_lambda(dt); + + atomThermostatForces_->unfix_quantity(); // allow update of atomic force applied by lambda } + + // apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step + DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + apply_to_atoms(atomPredictedVelocities_, + nodalAtomicPredictedMomentum_,nodalAtomicPredictedEnergy_, + firstHalfAtomForces_->quantity(), + myNodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + if (full_prediction()) + atomThermostatForces_->fix_quantity(); + + // SPLIT OUT FUNCTION TO CREATE DELTA VARIABLES IN BASES, ONLY NEED THESE + // update predicted nodal variables for second half of time step + // velocity + this->add_to_momentum(myNodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); + // following manipulations performed this way for efficiency + deltaEnergy1_ += deltaEnergy2_; + atc_->apply_inverse_mass_matrix(deltaEnergy1_,TEMPERATURE); + temperature_ += deltaEnergy1_; + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetoThermostatGlcFs::apply_post_corrector(double dt) + { + // remove predicted power effects + // velocity + DENS_MAT & myVelocity(velocity_.set_quantity()); + myVelocity -= deltaMomentum_; + // temperature + DENS_MAT & myTemperature(temperature_.set_quantity()); + atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); + myTemperature -= deltaEnergy2_; + + // set up equation and update lambda + this->compute_lambda(dt); + + // apply lambda force to atoms and compute instantaneous lambda power for second half of time step + DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); + // allow computation of force applied by lambda using current velocities + atomThermostatForces_->unfix_quantity(); + atomThermostatForces_->quantity(); + atomThermostatForces_->fix_quantity(); + apply_to_atoms(atomVelocities_,nodalAtomicMomentum_,nodalAtomicEnergy_, + atomRegulatorForces_->quantity(), + nodalAtomicLambdaForce,myNodalAtomicLambdaPower,0.5*dt); + + // finalize filtered lambda force and power by adding latest contribution + timeFilter_->apply_post_step2(lambdaForceFiltered_->set_quantity(), + nodalAtomicLambdaForce,dt); + timeFilter_->apply_post_step2(lambdaPowerFiltered_->set_quantity(), + myNodalAtomicLambdaPower,dt); + + // update nodal variables for second half of time step + // velocity + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + // temperature + this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); + myTemperature += deltaEnergy2_; + + + isFirstTimestep_ = false; + } + + //-------------------------------------------------------- + // compute_lambda: + // sets up and solves linear system for lambda, if the + // bool is true it iterators to a non-linear solution + //-------------------------------------------------------- + void KinetoThermostatGlcFs::compute_lambda(double dt, + bool iterateSolution) + { + // ITERATIVE SOLUTION + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void KinetoThermostatGlcFs::output(OUTPUT_LIST & outputData) + { + // DO NOT CALL INDIVIDUAL REGULATORS + // OUTPUT TOTAL FORCE AND TOTAL POWER + // OUTPUT EACH LAMBDA } + }; diff --git a/lib/atc/KinetoThermostat.h b/lib/atc/KinetoThermostat.h index 00c43f666..465da6b49 100644 --- a/lib/atc/KinetoThermostat.h +++ b/lib/atc/KinetoThermostat.h @@ -1,689 +1,798 @@ #ifndef KINETOTHERMOSTAT_H #define KINETOTHERMOSTAT_H #include "AtomicRegulator.h" #include "PerAtomQuantityLibrary.h" -//TEMP_JAT - transitional headers until we have a new method #include "Kinetostat.h" #include "Thermostat.h" #include <map> #include <set> #include <string> namespace ATC { -/* #ifdef WIP_JAT */ -/* static const int myLambdaMaxIterations = 50; */ -/* #endif */ + + static const int myCouplingMaxIterations = 50; + // forward declarations class MomentumTimeIntegrator; class ThermalTimeIntegrator; class AtfShapeFunctionRestriction; class FundamentalAtomQuantity; class PrescribedDataManager; /** * @class KinetoThermostat * @brief Manager class for atom-continuum simulataneous control of momentum and thermal energy */ class KinetoThermostat : public AtomicRegulator { public: // constructor KinetoThermostat(ATC_Coupling * atc, const std::string & regulatorPrefix = ""); // destructor virtual ~KinetoThermostat(){}; /** parser/modifier */ virtual bool modify(int narg, char **arg); /** instantiate up the desired method(s) */ virtual void construct_methods(); - //TEMP_JAT - required temporarily while we have two regulators - - /** initialization of method data */ - virtual void initialize(); - /** method(s) create all necessary transfer operators */ - virtual void construct_transfers(); - /** reset number of local atoms, as well as atomic data */ - virtual void reset_nlocal(); - /** set up atom to material identification */ - virtual void reset_atom_materials(const Array<int> & elementToMaterialMap, - const MatrixDependencyManager<DenseMatrix, int> * atomElement); - /** apply the regulator in the pre-predictor phase */ - virtual void apply_pre_predictor(double dt, int timeStep); - /** apply the regulator in the mid-predictor phase */ - virtual void apply_mid_predictor(double dt, int timeStep); - /** apply the regulator in the post-predictor phase */ - virtual void apply_post_predictor(double dt, int timeStep); - /** apply the regulator in the pre-correction phase */ - virtual void apply_pre_corrector(double dt, int timeStep); - /** apply the regulator in the post-correction phase */ - virtual void apply_post_corrector(double dt, int timeStep); - /** prior to exchanges */ - virtual void pre_exchange(); - /** prior to force calculation */ - virtual void pre_force(); - /** force a reset to occur */ - void force_reset() {kinetostat_.force_reset();thermostat_.force_reset();}; - /** compute the thermal boundary flux, must be consistent with regulator */ - virtual void compute_boundary_flux(FIELDS & fields); - /** type of boundary coupling */ - virtual RegulatorCouplingType coupling_mode(const FieldName) const; - virtual void output(OUTPUT_LIST & outputData) const; - /** final work at the end of a run */ - virtual void finish(); - /** add contributions (if any) to the finite element right-hand side */ - virtual void add_to_rhs(FIELDS & rhs); - - /** pack fields for restart */ - virtual void pack_fields(RESTART_LIST & data); // data access, intended for method objects /** reset the nodal power to a prescribed value */ virtual void reset_lambda_contribution(const DENS_MAT & target, const FieldName field); - //TEMP_JAT - will use real accessor - /** return value for the correction maximum number of iterations */ - int lambda_max_iterators() {return thermostat_.lambda_max_iterations();}; + + /** return value for the max number of mechanical/thermal coupling iterations */ + int coupling_max_iterations() const {return couplingMaxIterations_;}; protected: - //TEMP_JAT - individual controllers for now - Kinetostat kinetostat_; - Thermostat thermostat_; + /** maximum number of iterations used to solved coupled thermo/mechanical problem */ + int couplingMaxIterations_; + private: // DO NOT define this KinetoThermostat(); }; -/* #ifdef WIP_JAT */ -/* /\** */ -/* * @class ThermostatShapeFunction */ -/* * @brief Class for thermostat algorithms using the shape function matrices */ -/* * (thermostats have general for of N^T w N lambda = rhs) */ -/* *\/ */ + + /** + * @class KinetoThermostatShapeFunction + * @brief Class for kinetostat/thermostat algorithms using the shape function matrices + * (thermostats have general for of N^T w N lambda = rhs) + */ -/* class ThermostatShapeFunction : public RegulatorShapeFunction { */ + class KinetoThermostatShapeFunction : public RegulatorMethod { -/* public: */ + public: -/* ThermostatShapeFunction(Thermostat * thermostat, */ -/* const std::string & regulatorPrefix = ""); */ + KinetoThermostatShapeFunction(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const std::string & regulatorPrefix = "") : RegulatorMethod(kinetoThermostat), + couplingMaxIterations_(couplingMaxIterations) {}; -/* virtual ~ThermostatShapeFunction() {}; */ + virtual ~KinetoThermostatShapeFunction() {}; -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ + /** instantiate all needed data */ + virtual void construct_transfers() = 0; -/* protected: */ + /** initialize all data */ + virtual void initialize() {tolerance_ = atomicRegulator_->tolerance();}; -/* // methods */ -/* /\** set weighting factor for in matrix Nhat^T * weights * Nhat *\/ */ -/* virtual void set_weights(); */ + protected: -/* // member data */ -/* /\** pointer to thermostat object for data *\/ */ -/* Thermostat * thermostat_; */ + /** maximum number of iterations between energy and momentum regulators */ + int couplingMaxIterations_; -/* -/* /\** MD mass matrix *\/ */ -/* DIAG_MAN & mdMassMatrix_; + /** tolerance */ + double tolerance_; -/* /\** pointer to atom velocities *\/ */ -/* FundamentalAtomQuantity * atomVelocities_; */ + private: -/* /\** workspace variables *\/ */ -/* DENS_VEC _weightVector_, _maskedWeightVector_; */ + // DO NOT define this + KinetoThermostatShapeFunction(); -/* private: */ + }; -/* // DO NOT define this */ -/* ThermostatShapeFunction(); */ + /** + * @class VelocityRescaleCombined + * @brief Enforces constraints on atomic velocity based on FE temperature and velocity + */ + + class VelocityRescaleCombined : public VelocityGlc { + + public: -/* }; */ + friend class KinetoThermostatRescale; // since this is basically a set of member functions for friend + + VelocityRescaleCombined(AtomicRegulator * kinetostat); + + virtual ~VelocityRescaleCombined(){}; -/* /\** */ -/* * @class ThermostatRescale */ -/* * @brief Enforces constraint on atomic kinetic energy based on FE temperature */ -/* *\/ */ + /** pre-run initialization of method data */ + virtual void initialize(); + + /** applies kinetostat to atoms */ + virtual void apply_mid_predictor(double dt){}; + /** applies kinetostat to atoms */ + virtual void apply_post_corrector(double dt){}; + + /** local shape function matrices are incompatible with this mode */ + virtual bool use_local_shape_functions() const {return false;}; + + protected: + + // data + /** reference to AtC FE velocity */ + DENS_MAN & velocity_; + + /** RHS correct based on thermostat */ + DENS_MAN * thermostatCorrection_; + + // methods + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + + // disable un-needed functionality + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt){}; + /** applies kinetostat correction to atoms */ + virtual void apply_kinetostat(double dt) {}; + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt){}; + /** apply any required corrections for localized kinetostats */ + virtual void apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight = 1.){}; + + private: + + // DO NOT define this + VelocityRescaleCombined(); + + }; + + /** + * @class ThermostatRescaleCombined + * @brief Enforces constraint on atomic kinetic energy based on FE temperature and velocity + */ -/* class ThermostatRescale : public ThermostatShapeFunction { */ + class ThermostatRescaleCombined : public ThermostatRescale { -/* public: */ + public: -/* ThermostatRescale(Thermostat * thermostat); */ + ThermostatRescaleCombined(AtomicRegulator * thermostat); -/* virtual ~ThermostatRescale() {}; */ + virtual ~ThermostatRescaleCombined() {}; -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ + /** pre-run initialization of method data */ + virtual void initialize(); -/* /\** applies thermostat to atoms in the post-corrector phase *\/ */ -/* virtual void apply_post_corrector(double dt); */ -/* #ifdef WIP_JAT */ -/* /\** compute boundary flux, requires thermostat input since it is part of the coupling scheme *\/ */ -/* virtual void compute_boundary_flux(FIELDS & fields) */ -/* {boundaryFlux_[TEMPERATURE] = 0.;}; */ -/* #endif */ + // deactivate un-needed methods + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + protected: -/* /\** get data for output *\/ */ -/* virtual void output(OUTPUT_LIST & outputData); */ + // data + /** RHS correct based on kinetostat */ + DENS_MAN * kinetostatCorrection_; + + // deactivate un-needed methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity<double> * atomVelocities){}; + + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); + + private: + + // DO NOT define this + ThermostatRescaleCombined(); + + }; + + /** + * @class KinetoThermostatRescale + * @brief Enforces constraints on atomic kinetic energy and velocity based on FE temperature and velocity + */ + + class KinetoThermostatRescale : public KinetoThermostatShapeFunction { + + public: + + KinetoThermostatRescale(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations); -/* protected: */ + virtual ~KinetoThermostatRescale(); -/* /\** apply solution to atomic quantities *\/ */ -/* void apply_to_atoms(PerAtomQuantity<double> * atomVelocities); */ + /** instantiate all needed data */ + virtual void construct_transfers(); -/* /\** correct the RHS for complex temperature definitions *\/ */ -/* virtual void correct_rhs(DENS_MAT & rhs) {}; // base class does no correction, assuming kinetic definition */ + /** pre-run initialization of method data */ + virtual void initialize(); -/* /\** FE temperature field *\/ */ -/* DENS_MAN & nodalTemperature_; */ + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); -/* /\** construction for prolongation of lambda to atoms *\/ */ -/* AtomicVelocityRescaleFactor * atomVelocityRescalings_; */ + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[TEMPERATURE] = 0.; boundaryFlux_[VELOCITY] = 0.;}; -/* /\** workspace variables *\/ */ -/* DENS_MAT _rhs_; */ + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); + + protected: -/* private: */ + // methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity<double> * atomVelocities); -/* // DO NOT define this */ -/* ThermostatRescale(); */ + /** creates the appropriate rescaling thermostat */ + virtual ThermostatRescale * construct_rescale_thermostat(); + + // data + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; + + /** clone of FE velocity field */ + DENS_MAN & nodalVelocities_; + + /** lambda coupling parameter for momentum */ + DENS_MAN * lambdaMomentum_; + + /** lambda coupling parameter for energy */ + DENS_MAN * lambdaEnergy_; + + /** pointer to rescaled velocity fluctuations */ + PerAtomQuantity<double> * atomicFluctuatingVelocityRescaled_; + + /** pointer to streaming velocity */ + PerAtomQuantity<double> * atomicStreamingVelocity_; + + /** rescaling thermostat */ + ThermostatRescale * thermostat_; + + /** velocity regulating kinetostat */ + VelocityRescaleCombined * kinetostat_; + + // workspace + DENS_MAT _lambdaEnergyOld_, _lambdaMomentumOld_, _diff_; + + private: + + // DO NOT define this + KinetoThermostatRescale(); -/* }; */ + }; -/* /\** */ -/* * @class ThermostatRescaleMixedKePe */ -/* * @brief Enforces constraint on atomic kinetic energy based on FE temperature */ -/* * when the temperature is a mix of the KE and PE */ -/* *\/ */ + /** + * @class ThermostatRescaleMixedKePeCombined + * @brief Enforces constraint on atomic kinetic energy based on FE temperature and velocity when the temperature is comprised of both KE and PE contributions + */ -/* class ThermostatRescaleMixedKePe : public ThermostatRescale { */ + class ThermostatRescaleMixedKePeCombined : public ThermostatRescaleMixedKePe { -/* public: */ + public: -/* ThermostatRescaleMixedKePe(Thermostat * thermostat); */ + ThermostatRescaleMixedKePeCombined(AtomicRegulator * thermostat); -/* virtual ~ThermostatRescaleMixedKePe() {}; */ - -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ + virtual ~ThermostatRescaleMixedKePeCombined() {}; -/* /\** pre-run initialization of method data *\/ */ -/* virtual void initialize(); */ + /** pre-run initialization of method data */ + virtual void initialize(); -/* protected: */ + // deactivate un-needed methods + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + protected: -/* /\** correct the RHS for inclusion of the PE *\/ */ -/* virtual void correct_rhs(DENS_MAT & rhs); */ + // data + /** RHS correct based on kinetostat */ + DENS_MAN * kinetostatCorrection_; -/* /\** nodal fluctuating potential energy *\/ */ -/* DENS_MAN * nodalAtomicFluctuatingPotentialEnergy_; */ + // deactivate un-needed methods + /** apply solution to atomic quantities */ + virtual void apply_to_atoms(PerAtomQuantity<double> * atomVelocities){}; -/* /\** fraction of temperature from KE *\/ */ -/* double keMultiplier_; */ + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); -/* /\** fraction of temperature from PE *\/ */ -/* double peMultiplier_; */ + private: -/* private: */ + // DO NOT define this + ThermostatRescaleMixedKePeCombined(); + + }; -/* // DO NOT define this */ -/* ThermostatRescaleMixedKePe(); */ + /** + * @class KinetoThermostatRescaleMixedKePe + * @brief Enforces constraint on atomic kinetic energy based on FE temperature + * when the temperature is a mix of the KE and PE + */ -/* }; */ + class KinetoThermostatRescaleMixedKePe : public KinetoThermostatRescale { + + public: + + KinetoThermostatRescaleMixedKePe(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations); -/* /\** */ -/* * @class ThermostatGlcFs */ -/* * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm */ -/* *\/ */ + virtual ~KinetoThermostatRescaleMixedKePe() {}; + + protected: + + /** creates the appropriate rescaling thermostat */ + virtual ThermostatRescale * construct_rescale_thermostat(); + + private: + + // DO NOT define this + KinetoThermostatRescaleMixedKePe(); + + }; + + /** + * @class KinetoThermostatGlcFs + * @brief Class for regulation algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm + */ -/* class ThermostatGlcFs : public ThermostatShapeFunction { */ + class KinetoThermostatGlcFs : public KinetoThermostatShapeFunction { -/* public: */ + public: -/* ThermostatGlcFs(Thermostat * thermostat, */ -/* const std::string & regulatorPrefix = ""); */ + KinetoThermostatGlcFs(AtomicRegulator * kinetoThermostat, + int couplingMaxIterations, + const std::string & regulatorPrefix = ""); -/* virtual ~ThermostatGlcFs() {}; */ + virtual ~KinetoThermostatGlcFs() {}; -/* /\** instantiate all needed data *\/ */ -/* virtual void construct_transfers(); */ + /** instantiate all needed data */ + virtual void construct_transfers(); -/* /\** pre-run initialization of method data *\/ */ -/* virtual void initialize(); */ + /** pre-run initialization of method data */ + virtual void initialize(); -/* /\** applies thermostat to atoms in the predictor phase *\/ */ -/* virtual void apply_pre_predictor(double dt); */ + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); -/* /\** applies thermostat to atoms in the pre-corrector phase *\/ */ -/* virtual void apply_pre_corrector(double dt); */ + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); -/* /\** applies thermostat to atoms in the post-corrector phase *\/ */ -/* virtual void apply_post_corrector(double dt); */ + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); -/* /\** get data for output *\/ */ -/* virtual void output(OUTPUT_LIST & outputData); */ + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); -/* /\* flag for performing the full lambda prediction calculation *\/ */ -/* bool full_prediction(); */ + /* flag for performing the full lambda prediction calculation */ + bool full_prediction(); -/* protected: */ + protected: -/* // methods */ -/* /\** determine mapping from all nodes to those to which the thermostat applies *\/ */ -/* void compute_rhs_map(); */ + // methods -/* /\** sets up appropriate rhs for thermostat equations *\/ */ -/* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ -/* double dt) = 0; */ + /** apply forces to atoms */ + virtual void apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, + const DENS_MAN * nodalAtomicMomentum, + const DENS_MAN * nodalAtomicEnergy, + const DENS_MAT & lambdaForce, + DENS_MAT & nodalAtomicLambdaForce, + DENS_MAT & nodalAtomicLambdaPower, + double dt); -/* /\** apply forces to atoms *\/ */ -/* virtual void apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, */ -/* const DENS_MAN * nodalAtomicEnergy, */ -/* const DENS_MAT & lambdaForce, */ -/* DENS_MAT & nodalAtomicLambdaPower, */ -/* double dt); */ + // USE BASE CLASSES FOR THESE + /** add contributions from regulator to FE momentum */ + virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, + DENS_MAT & deltaForce, + double dt) = 0; -/* /\** add contributions from thermostat to FE energy *\/ */ -/* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ -/* DENS_MAT & deltaEnergy, */ -/* double dt) = 0; */ + /** add contributions from regulator to FE energy */ + virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) = 0; -/* /\* sets up and solves the linear system for lambda *\/ */ -/* virtual void compute_lambda(double dt, */ -/* bool iterateSolution = true); */ + /* sets up and solves the linear system for lambda */ + virtual void compute_lambda(double dt, + bool iterateSolution = true); -/* /\** solves the non-linear equation for lambda iteratively *\/ */ -/* void iterate_lambda(const MATRIX & rhs); */ + // member data + /** reference to AtC FE velocity */ + DENS_MAN & velocity_; -/* // member data */ -/* /\** reference to AtC FE temperature *\/ */ -/* DENS_MAN & temperature_; */ + /** reference to AtC FE temperature */ + DENS_MAN & temperature_; -/* /\** pointer to a time filtering object *\/ */ -/* TimeFilter * timeFilter_; */ + /** pointer to a time filtering object */ + TimeFilter * timeFilter_; -/* /\** power induced by lambda *\/ */ -/* DENS_MAN * nodalAtomicLambdaPower_; */ + /** force induced by lambda */ + DENS_MAN * nodalAtomicLambdaForce_; -/* /\** filtered lambda power *\/ */ -/* DENS_MAN * lambdaPowerFiltered_; */ + /** filtered lambda force */ + DENS_MAN * lambdaForceFiltered_; -/* /\** atomic force induced by lambda *\/ */ -/* AtomicThermostatForce * atomThermostatForces_; */ + /** power induced by lambda */ + DENS_MAN * nodalAtomicLambdaPower_; -/* /\** pointer to atom masses *\/ */ -/* FundamentalAtomQuantity * atomMasses_; */ + /** filtered lambda power */ + DENS_MAN * lambdaPowerFiltered_; -/* /\** pointer to the values of lambda interpolated to atoms *\/ */ -/* DENS_MAN * rhsLambdaSquared_; */ + /** atomic force induced by lambda */ + AtomicThermostatForce * atomRegulatorForces_; -/* /\** hack to determine if first timestep has been passed *\/ */ -/* bool isFirstTimestep_; */ + /** atomic force induced by thermostat lambda */ + AtomicThermostatForce * atomThermostatForces_; -/* /\** maximum number of iterations used in iterative solve for lambda *\/ */ -/* int lambdaMaxIterations_; */ + /** pointer to atom masses */ + FundamentalAtomQuantity * atomMasses_; -/* /\** nodal atomic energy *\/ */ -/* DENS_MAN * nodalAtomicEnergy_; */ + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; -/* /\** local version of velocity used as predicted final veloctiy *\/ */ -/* PerAtomQuantity<double> * atomPredictedVelocities_; */ + /** hack to determine if first timestep has been passed */ + bool isFirstTimestep_; -/* /\** predicted nodal atomic energy *\/ */ -/* AtfShapeFunctionRestriction * nodalAtomicPredictedEnergy_; */ + /** nodal atomic momentum */ + DENS_MAN * nodalAtomicMomentum_; -/* /\** pointer for force applied in first time step *\/ */ -/* DENS_MAN * firstHalfAtomForces_; */ + /** nodal atomic energy */ + DENS_MAN * nodalAtomicEnergy_; -/* /\** FE temperature change from thermostat during predictor phase in second half of timestep *\/ */ -/* DENS_MAT deltaEnergy1_; */ + /** local version of velocity used as predicted final veloctiy */ + PerAtomQuantity<double> * atomPredictedVelocities_; -/* /\** FE temperature change from thermostat during corrector phase in second half of timestep *\/ */ -/* DENS_MAT deltaEnergy2_; */ + /** predicted nodal atomic momentum */ + AtfShapeFunctionRestriction * nodalAtomicPredictedMomentum_; -/* /\** right-hand side data for thermostat equation *\/ */ -/* DENS_MAT rhs_; */ + /** predicted nodal atomic energy */ + AtfShapeFunctionRestriction * nodalAtomicPredictedEnergy_; -/* /\** mapping from all to regulated nodes *\/ */ -/* DENS_MAT rhsMap_; */ + /** pointer for force applied in first time step */ + DENS_MAN * firstHalfAtomForces_; -/* /\** fraction of timestep over which constraint is exactly enforced *\/ */ -/* double dtFactor_; */ + /** FE momentum change from regulator during predictor phase in second half of timestep */ + DENS_MAT deltaMomentum_; -/* // workspace */ -/* DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format */ -/* DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied */ -/* DENS_VEC _lambdaOverlap_; // lambda in MD overlapping FE nodes */ -/* DENS_MAT _lambdaOld_; // lambda from previous iteration */ -/* DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes */ -/* DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop */ + /** FE temperature change from regulator during predictor phase in second half of timestep */ + DENS_MAT deltaEnergy1_; -/* private: */ + /** FE temperature change from regulator during corrector phase in second half of timestep */ + DENS_MAT deltaEnergy2_; -/* // DO NOT define this */ -/* ThermostatGlcFs(); */ + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; -/* }; */ + // workspace + DENS_MAT _lambdaForceOutput_; // force applied by lambda in output format + DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format + DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied + private: + + // DO NOT define this + KinetoThermostatGlcFs(); + + }; +/* #ifdef WIP_JAT */ /* /\** */ /* * @class ThermostatFlux */ /* * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ /* *\/ */ /* class ThermostatFlux : public ThermostatGlcFs { */ /* public: */ /* ThermostatFlux(Thermostat * thermostat, */ /* const std::string & regulatorPrefix = ""); */ /* virtual ~ThermostatFlux() {}; */ /* /\** instantiate all needed data *\/ */ /* virtual void construct_transfers(); */ /* /\** pre-run initialization of method data *\/ */ /* virtual void initialize(); */ /* protected: */ /* /\** sets up appropriate rhs for thermostat equations *\/ */ /* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ /* double dt); */ /* /\** add contributions from thermostat to FE energy *\/ */ /* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ /* DENS_MAT & deltaEnergy, */ /* double dt); */ /* /\** sets up the transfer which is the set of nodes being regulated *\/ */ /* virtual void construct_regulated_nodes(); */ /* // data */ /* /\** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling *\/ */ /* DENS_MAN & heatSource_; */ /* private: */ /* // DO NOT define this */ /* ThermostatFlux(); */ /* }; */ /* /\** */ /* * @class ThermostatFixed */ /* * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ /* *\/ */ /* class ThermostatFixed : public ThermostatGlcFs { */ /* public: */ /* ThermostatFixed(Thermostat * thermostat, */ /* const std::string & regulatorPrefix = ""); */ /* virtual ~ThermostatFixed() {}; */ /* /\** instantiate all needed data *\/ */ /* virtual void construct_transfers(); */ /* /\** pre-run initialization of method data *\/ */ /* virtual void initialize(); */ /* /\** applies thermostat to atoms in the predictor phase *\/ */ /* virtual void apply_pre_predictor(double dt); */ /* /\** applies thermostat to atoms in the pre-corrector phase *\/ */ /* virtual void apply_pre_corrector(double dt); */ /* /\** applies thermostat to atoms in the post-corrector phase *\/ */ /* virtual void apply_post_corrector(double dt); */ /* /\** compute boundary flux, requires thermostat input since it is part of the coupling scheme *\/ */ /* virtual void compute_boundary_flux(FIELDS & fields) */ /* {boundaryFlux_[TEMPERATURE] = 0.;}; */ /* /\** determine if local shape function matrices are needed *\/ */ /* virtual bool use_local_shape_functions() const {return atomicRegulator_->use_localized_lambda();}; */ /* protected: */ /* // methods */ /* /\** initialize data for tracking the change in nodal atomic temperature *\/ */ /* virtual void initialize_delta_nodal_atomic_energy(double dt); */ /* /\** compute the change in nodal atomic temperature *\/ */ /* virtual void compute_delta_nodal_atomic_energy(double dt); */ /* /\** sets up appropriate rhs for thermostat equations *\/ */ /* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ /* double dt); */ /* /\** add contributions from thermostat to FE energy *\/ */ /* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ /* DENS_MAT & deltaEnergy, */ /* double dt); */ /* /\* sets up and solves the linear system for lambda *\/ */ /* virtual void compute_lambda(double dt, */ /* bool iterateSolution = true); */ /* /\** flag for halving the applied force to mitigate numerical errors *\/ */ /* bool halve_force(); */ /* /\** sets up the transfer which is the set of nodes being regulated *\/ */ /* virtual void construct_regulated_nodes(); */ /* // data */ /* /\** change in FE energy over a timestep *\/ */ /* DENS_MAT deltaFeEnergy_; */ /* /\** initial FE energy used to compute change *\/ */ /* DENS_MAT initialFeEnergy_; */ /* /\** change in restricted atomic FE energy over a timestep *\/ */ /* DENS_MAT deltaNodalAtomicEnergy_; */ /* /\** intial restricted atomic FE energy used to compute change *\/ */ /* DENS_MAT initialNodalAtomicEnergy_; */ /* /\** filtered nodal atomic energy *\/ */ /* DENS_MAN nodalAtomicEnergyFiltered_; */ /* /\** forces depending on predicted velocities for correct updating with fixed nodes *\/ */ /* AtomicThermostatForce * atomThermostatForcesPredVel_; */ /* /\** coefficient to account for effect of time filtering on rhs terms *\/ */ /* double filterCoefficient_; */ /* /\** kinetic energy multiplier in total energy (used for temperature expression) *\/ */ /* double keMultiplier_; */ /* // workspace */ /* DENS_MAT _tempNodalAtomicEnergyFiltered_; // stores filtered energy change in atoms for persistence during predictor */ /* private: */ /* // DO NOT define this */ /* ThermostatFixed(); */ /* }; */ /* /\** */ /* * @class ThermostatFluxFiltered */ /* * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ /* * in conjunction with time filtering */ /* *\/ */ /* class ThermostatFluxFiltered : public ThermostatFlux { */ /* public: */ /* ThermostatFluxFiltered(Thermostat * thermostat, */ /* const std::string & regulatorPrefix = ""); */ /* virtual ~ThermostatFluxFiltered() {}; */ /* /\** pre-run initialization of method data *\/ */ /* virtual void initialize(); */ /* /\** applies thermostat to atoms in the post-corrector phase *\/ */ /* virtual void apply_post_corrector(double dt); */ /* /\** get data for output *\/ */ /* virtual void output(OUTPUT_LIST & outputData); */ /* protected: */ /* /\** sets up appropriate rhs for thermostat equations *\/ */ /* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ /* double dt); */ /* /\** add contributions from thermostat to FE energy *\/ */ /* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ /* DENS_MAT & deltaEnergy, */ /* double dt); */ /* // data */ /* /\** heat source time history required to get instantaneous heat sources *\/ */ /* DENS_MAT heatSourceOld_; */ /* DENS_MAT instantHeatSource_; */ /* DENS_MAT timeStepSource_; */ /* private: */ /* // DO NOT define this */ /* ThermostatFluxFiltered(); */ /* }; */ /* /\** */ /* * @class ThermostatFixedFiltered */ /* * @brief Class for thermostatting using the temperature matching constraint and is compatible with */ /* the fractional step time-integration with time filtering */ /* *\/ */ /* class ThermostatFixedFiltered : public ThermostatFixed { */ /* public: */ /* ThermostatFixedFiltered(Thermostat * thermostat, */ /* const std::string & regulatorPrefix = ""); */ /* virtual ~ThermostatFixedFiltered() {}; */ /* /\** get data for output *\/ */ /* virtual void output(OUTPUT_LIST & outputData); */ /* protected: */ /* // methods */ /* /\** initialize data for tracking the change in nodal atomic temperature *\/ */ /* virtual void initialize_delta_nodal_atomic_energy(double dt); */ /* /\** compute the change in nodal atomic temperature *\/ */ /* virtual void compute_delta_nodal_atomic_energy(double dt); */ /* /\** sets up appropriate rhs for thermostat equations *\/ */ /* virtual void set_thermostat_rhs(DENS_MAT & rhs, */ /* double dt); */ /* /\** add contributions from thermostat to temperature for uncoupled nodes *\/ */ /* virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, */ /* DENS_MAT & deltaEnergy, */ /* double dt); */ /* private: */ /* // DO NOT define this */ /* ThermostatFixedFiltered(); */ /* }; */ /* /\** */ /* * @class ThermostatFluxFixed */ /* * @brief Class for thermostatting using the temperature matching constraint one one set of nodes and the flux matching constraint on another */ /* *\/ */ /* class ThermostatFluxFixed : public RegulatorMethod { */ /* public: */ /* ThermostatFluxFixed(Thermostat * thermostat, */ /* bool constructThermostats = true); */ /* virtual ~ThermostatFluxFixed(); */ /* /\** instantiate all needed data *\/ */ /* virtual void construct_transfers(); */ /* /\** pre-run initialization of method data *\/ */ /* virtual void initialize(); */ /* /\** applies thermostat to atoms in the predictor phase *\/ */ /* virtual void apply_pre_predictor(double dt); */ /* /\** applies thermostat to atoms in the pre-corrector phase *\/ */ /* virtual void apply_pre_corrector(double dt); */ /* /\** applies thermostat to atoms in the post-corrector phase *\/ */ /* virtual void apply_post_corrector(double dt); */ /* /\** get data for output *\/ */ /* virtual void output(OUTPUT_LIST & outputData); */ /* /\** compute boundary flux, requires thermostat input since it is part of the coupling scheme *\/ */ /* virtual void compute_boundary_flux(FIELDS & fields) */ /* {thermostatBcs_->compute_boundary_flux(fields);}; */ /* protected: */ /* // data */ /* /\** thermostat for imposing the fluxes *\/ */ /* ThermostatFlux * thermostatFlux_; */ /* /\** thermostat for imposing fixed nodes *\/ */ /* ThermostatFixed * thermostatFixed_; */ /* /\** pointer to whichever thermostat should compute the flux, based on coupling method *\/ */ /* ThermostatGlcFs * thermostatBcs_; */ /* private: */ /* // DO NOT define this */ /* ThermostatFluxFixed(); */ /* }; */ /* /\** */ /* * @class ThermostatFluxFixedFiltered */ /* * @brief Class for thermostatting using the temperature matching constraint one one set of nodes and the flux matching constraint on another with time filtering */ /* *\/ */ /* class ThermostatFluxFixedFiltered : public ThermostatFluxFixed { */ /* public: */ /* ThermostatFluxFixedFiltered(Thermostat * thermostat); */ /* virtual ~ThermostatFluxFixedFiltered(){}; */ /* private: */ /* // DO NOT define this */ /* ThermostatFluxFixedFiltered(); */ /* }; */ /* #endif */ }; #endif diff --git a/lib/atc/Kinetostat.cpp b/lib/atc/Kinetostat.cpp index c104efc72..17c5b6caf 100644 --- a/lib/atc/Kinetostat.cpp +++ b/lib/atc/Kinetostat.cpp @@ -1,2246 +1,2508 @@ #include "Kinetostat.h" #include "ATC_Error.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PerAtomQuantityLibrary.h" #include "PrescribedDataManager.h" #include "ElasticTimeIntegrator.h" #include "TransferOperator.h" using std::set; using std::pair; using std::string; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class Kinetostat //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- Kinetostat::Kinetostat(ATC_Coupling * atc, const string & regulatorPrefix) : AtomicRegulator(atc,regulatorPrefix) { // do nothing } //-------------------------------------------------------- // modify: // parses and adjusts kinetostat state based on // user input, in the style of LAMMPS user input //-------------------------------------------------------- bool Kinetostat::modify(int narg, char **arg) { bool foundMatch = false; int argIndex = 0; if (strcmp(arg[argIndex],"momentum")==0) { argIndex++; // fluxstat type /*! \page man_control_momentum fix_modify AtC control momentum \section syntax fix_modify AtC control momentum none \n fix_modify AtC control momentum rescale <frequency>\n - frequency (int) = time step frequency for applying displacement and velocity rescaling \n fix_modify AtC control momentum glc_displacement \n fix_modify AtC control momentum glc_velocity \n fix_modify AtC control momentum hoover \n fix_modify AtC control momentum flux [faceset face_set_id, interpolate] - face_set_id (string) = id of boundary face set, if not specified (or not possible when the atomic domain does not line up with mesh boundaries) defaults to an atomic-quadrature approximate evaulation\n \section examples fix_modify AtC control momentum glc_velocity \n fix_modify AtC control momentum flux faceset bndy_faces \n \section description \section restrictions only to be used with specific transfers : elastic \n rescale not valid with time filtering activated \section related \section default none */ boundaryIntegrationType_ = NO_QUADRATURE; howOften_ = 1; if (strcmp(arg[argIndex],"none")==0) { // restore defaults regulatorTarget_ = NONE; couplingMode_ = UNCOUPLED; foundMatch = true; } else if (strcmp(arg[argIndex],"glc_displacement")==0) { regulatorTarget_ = FIELD; couplingMode_ = FIXED; foundMatch = true; } else if (strcmp(arg[argIndex],"glc_velocity")==0) { regulatorTarget_ = DERIVATIVE; couplingMode_ = FIXED; foundMatch = true; } else if (strcmp(arg[argIndex],"hoover")==0) { regulatorTarget_ = DYNAMICS; couplingMode_ = FIXED; foundMatch = true; } else if (strcmp(arg[argIndex],"flux")==0) { regulatorTarget_ = DYNAMICS; couplingMode_ = FLUX; argIndex++; boundaryIntegrationType_ = atc_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_); foundMatch = true; } else if (strcmp(arg[argIndex],"ghost_flux")==0) { regulatorTarget_ = DYNAMICS; couplingMode_ = GHOST_FLUX; foundMatch = true; } } if (!foundMatch) foundMatch = AtomicRegulator::modify(narg,arg); if (foundMatch) needReset_ = true; return foundMatch; } //-------------------------------------------------------- // reset_lambda_contribution // resets the kinetostat generated force to a // prescribed value //-------------------------------------------------------- void Kinetostat::reset_lambda_contribution(const DENS_MAT & target) { DENS_MAN * lambdaForceFiltered = regulator_data("LambdaForceFiltered",nsd_); lambdaForceFiltered->set_quantity() = target; } //-------------------------------------------------------- // initialize: // sets up methods before a run // dependence, but in general there is also a // time integrator dependence. In general the // precedence order is: // time filter -> time integrator -> kinetostat // In the future this may need to be added if // different types of time integrators can be // specified. //-------------------------------------------------------- void Kinetostat::construct_methods() { // get data associated with stages 1 & 2 of ATC_Method::initialize AtomicRegulator::construct_methods(); if (atc_->reset_methods()) { // eliminate existing methods delete_method(); DENS_MAT nodalGhostForceFiltered; TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(VELOCITY))->time_integration_type(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (timeFilterManager->end_equilibrate() && regulatorTarget_==AtomicRegulator::DYNAMICS) { StressFlux * myMethod; myMethod = dynamic_cast<StressFlux *>(regulatorMethod_); nodalGhostForceFiltered = (myMethod->filtered_ghost_force()).quantity(); } // update time filter if (timeFilterManager->need_reset()) { - - timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE); + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } + else { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE); + } } if (timeFilterManager->filter_dynamics()) { switch (regulatorTarget_) { case NONE: { regulatorMethod_ = new RegulatorMethod(this); break; } case FIELD: { regulatorMethod_ = new DisplacementGlcFiltered(this); break; } case DERIVATIVE: { regulatorMethod_ = new VelocityGlcFiltered(this); break; } case DYNAMICS: { throw ATC_Error("Kinetostat::initialize - force based kinetostats not yet implemented with time filtering"); regulatorMethod_ = new StressFluxFiltered(this); if (timeFilterManager->end_equilibrate()) { StressFlux * myMethod; myMethod = dynamic_cast<StressFlux *>(regulatorMethod_); myMethod->reset_filtered_ghost_force(nodalGhostForceFiltered); } break; } default: - throw ATC_Error("Unknown kinetostat type in Kinetostat::initialize"); + throw ATC_Error("Kinetostat::construct_methods - Unknown filtered kinetostat type"); } } else { switch (regulatorTarget_) { case NONE: { regulatorMethod_ = new RegulatorMethod(this); break; } case FIELD: { regulatorMethod_ = new DisplacementGlc(this); break; } case DERIVATIVE: { regulatorMethod_ = new VelocityGlc(this); break; } case DYNAMICS: { if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (couplingMode_ == GHOST_FLUX) { - regulatorMethod_ = new KinetostatFluxGhost(this); + if (md_fixed_nodes(VELOCITY)) { + if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fixed nodes but no fluxes + regulatorMethod_ = new KinetostatFixed(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only flux nodes + regulatorMethod_ = new KinetostatFluxGhost(this); + } } else if (couplingMode_ == FIXED) { - regulatorMethod_ = new KinetostatFixed(this); + if (md_flux_nodes(VELOCITY)) { + if (!md_fixed_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fluxes but no fixed or coupled nodes + regulatorMethod_ = new KinetostatFlux(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only fixed nodes + regulatorMethod_ = new KinetostatFixed(this); + } } else if (couplingMode_ == FLUX) { - regulatorMethod_ = new KinetostatFlux(this); + if (md_fixed_nodes(VELOCITY)) { + if (!md_flux_nodes(VELOCITY) && (boundaryIntegrationType_ == NO_QUADRATURE)) { + // there are fixed nodes but no fluxes + regulatorMethod_ = new KinetostatFixed(this); + } + else { + // there are both fixed and flux nodes + regulatorMethod_ = new KinetostatFluxFixed(this); + } + } + else { + // there are only flux nodes + regulatorMethod_ = new KinetostatFlux(this); + } } break; } if (myIntegrationType == TimeIntegrator::GEAR) { if (couplingMode_ == FIXED) { regulatorMethod_ = new KinetostatFixed(this); } else if (couplingMode_ == FLUX) { regulatorMethod_ = new KinetostatFlux(this); } break; } else { if (couplingMode_ == GHOST_FLUX) { regulatorMethod_ = new StressFluxGhost(this); } else { regulatorMethod_ = new StressFlux(this); } break; } } default: - throw ATC_Error("Unknown kinetostat type in Kinetostat::initialize"); + throw ATC_Error("Kinetostat::construct_methods - Unknown kinetostat type"); } AtomicRegulator::reset_method(); } } else { set_all_data_to_used(); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetostatShapeFunction //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatShapeFunction::KinetostatShapeFunction(Kinetostat *kinetostat, + KinetostatShapeFunction::KinetostatShapeFunction(AtomicRegulator *kinetostat, const string & regulatorPrefix) : RegulatorShapeFunction(kinetostat,regulatorPrefix), - kinetostat_(kinetostat), + mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), timeFilter_(atomicRegulator_->time_filter()), nodalAtomicLambdaForce_(NULL), lambdaForceFiltered_(NULL), atomKinetostatForce_(NULL), atomVelocities_(NULL), atomMasses_(NULL) { // data associated with stage 3 in ATC_Method::initialize - lambda_ = kinetostat->regulator_data(regulatorPrefix_+"LambdaMomentum",nsd_); - lambdaForceFiltered_ = kinetostat_->regulator_data("LambdaForceFiltered",nsd_); + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaMomentum",nsd_); + lambdaForceFiltered_ = atomicRegulator_->regulator_data("LambdaForceFiltered",nsd_); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void KinetostatShapeFunction::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // needed fundamental quantities atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); // base class transfers RegulatorShapeFunction::construct_transfers(); // lambda interpolated to the atomic coordinates atomLambdas_ = new FtaShapeFunctionProlongation(atc_, lambda_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_per_atom_quantity(atomLambdas_, regulatorPrefix_+"AtomLambdaMomentum"); } //-------------------------------------------------------- // set_weights // sets diagonal weighting matrix used in // solve_for_lambda //-------------------------------------------------------- void KinetostatShapeFunction::set_weights() { if (this->use_local_shape_functions()) { ConstantQuantityMapped<double> * myWeights = new ConstantQuantityMapped<double>(atc_,1.,lambdaAtomMap_); weights_ = myWeights; (atc_->interscale_manager()).add_per_atom_quantity(myWeights, "AtomOnesMapped"); } else { weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicOnes"); if (!weights_) { weights_ = new ConstantQuantity<double>(atc_,1.); (atc_->interscale_manager()).add_per_atom_quantity(weights_, "AtomicOnes"); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class GlcKinetostat //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - GlcKinetostat::GlcKinetostat(Kinetostat *kinetostat) : + GlcKinetostat::GlcKinetostat(AtomicRegulator *kinetostat) : KinetostatShapeFunction(kinetostat), - mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), atomPositions_(NULL) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void GlcKinetostat::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // needed fundamental quantities atomPositions_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION); // base class transfers KinetostatShapeFunction::construct_transfers(); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void GlcKinetostat::initialize() { KinetostatShapeFunction::initialize(); // set up list of nodes using Hoover coupling // (a) nodes with prescribed values PrescribedDataManager * prescribedDataMgr(atc_->prescribed_data_manager()); for (int i = 0; i < nNodes_; ++i) for (int j = 0; j < nsd_; ++j) if (prescribedDataMgr->is_fixed(i,VELOCITY,j)) hooverNodes_.insert(pair<int,int>(i,j)); // (b) AtC coupling nodes if (atomicRegulator_->coupling_mode()==AtomicRegulator::FIXED) { InterscaleManager & interscaleManager(atc_->interscale_manager()); const INT_ARRAY & nodeType((interscaleManager.dense_matrix_int("NodalGeometryType"))->quantity()); if (atomicRegulator_->use_localized_lambda()) { for (int i = 0; i < nNodes_; ++i) { if (nodeType(i,0)==BOUNDARY) { for (int j = 0; j < nsd_; ++j) { hooverNodes_.insert(pair<int,int>(i,j)); } } } } else { for (int i = 0; i < nNodes_; ++i) { if (nodeType(i,0)==BOUNDARY || nodeType(i,0)==MD_ONLY) { for (int j = 0; j < nsd_; ++j) { hooverNodes_.insert(pair<int,int>(i,j)); } } } } } } //-------------------------------------------------------- // apply_lambda_to_atoms // uses existing lambda to modify given // atomic quantity //-------------------------------------------------------- void GlcKinetostat::apply_to_atoms(PerAtomQuantity<double> * quantity, const DENS_MAT & lambdaAtom, double dt) { *quantity -= lambdaAtom; } //-------------------------------------------------------- //-------------------------------------------------------- // Class DisplacementGlc //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - DisplacementGlc::DisplacementGlc(Kinetostat * kinetostat) : + DisplacementGlc::DisplacementGlc(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalAtomicMassWeightedDisplacement_(NULL), nodalDisplacements_(atc_->field(DISPLACEMENT)) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void DisplacementGlc::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // set up shape function matrix if (this->use_local_shape_functions()) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, lambdaAtomMap_, nodeToOverlapMap_); } else { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); } interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixMomentum"); // set linear solver strategy if (atomicRegulator_->use_lumped_lambda_solve()) { linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { linearSolverType_ = AtomicRegulator::CG_SOLVE; } // base class transfers GlcKinetostat::construct_transfers(); // atomic force induced by kinetostat atomKinetostatForce_ = new AtomicKinetostatForceDisplacement(atc_); interscaleManager.add_per_atom_quantity(atomKinetostatForce_, regulatorPrefix_+"AtomKinetostatForce"); // restricted force due to kinetostat nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_, atomKinetostatForce_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_, regulatorPrefix_+"NodalAtomicLambdaForce"); // nodal displacement restricted from atoms nodalAtomicMassWeightedDisplacement_ = interscaleManager.dense_matrix("NodalAtomicMassWeightedDisplacement"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void DisplacementGlc::initialize() { GlcKinetostat::initialize(); // sets up time filter for cases where variables temporally filtered TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { *lambdaForceFiltered_ = 0.; timeFilter_->initialize(lambdaForceFiltered_->quantity()); } } //-------------------------------------------------------- // apply: // apply the kinetostat to the atoms //-------------------------------------------------------- void DisplacementGlc::apply_post_predictor(double dt) { compute_kinetostat(dt); } //-------------------------------------------------------- // compute_kinetostat // manages the solution and application of the // kinetostat equations and variables //-------------------------------------------------------- void DisplacementGlc::compute_kinetostat(double dt) { // initial filtering update apply_pre_filtering(dt); // set up rhs DENS_MAT rhs(nNodes_,nsd_); set_kinetostat_rhs(rhs,dt); // solve linear system for lambda solve_for_lambda(rhs,lambda_->set_quantity()); // compute nodal atomic power compute_nodal_lambda_force(dt); // apply kinetostat to atoms apply_to_atoms(atomPositions_,atomLambdas_->quantity()); } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the right-hand side of the // kinetostat equations //-------------------------------------------------------- void DisplacementGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii rhs = nodalAtomicMassWeightedDisplacement_->quantity(); - rhs -= ((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalDisplacements_.quantity()); + rhs -= (mdMassMatrix_.quantity())*(nodalDisplacements_.quantity()); } //-------------------------------------------------------- // compute_nodal_lambda_force // compute the effective FE force applied // by the kinetostat //-------------------------------------------------------- void DisplacementGlc::compute_nodal_lambda_force(double dt) { const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity()); timeFilter_->apply_post_step1(lambdaForceFiltered_->set_quantity(), myNodalAtomicLambdaForce,dt); // update FE displacements for localized thermostats apply_localization_correction(myNodalAtomicLambdaForce, nodalDisplacements_.set_quantity(), dt*dt); } //-------------------------------------------------------- // apply_pre_filtering // applies first step of filtering to // relevant variables //-------------------------------------------------------- void DisplacementGlc::apply_pre_filtering(double dt) { // apply time filtered lambda force DENS_MAT lambdaZero(nNodes_,nsd_); timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),(-1./dt/dt)*lambdaZero,dt); } //-------------------------------------------------------- // set_weights // sets diagonal weighting matrix used in // solve_for_lambda //-------------------------------------------------------- void DisplacementGlc::set_weights() { if (lambdaAtomMap_) { MappedAtomQuantity * myWeights = new MappedAtomQuantity(atc_,atomMasses_,lambdaAtomMap_); weights_ = myWeights; (atc_->interscale_manager()).add_per_atom_quantity(myWeights, "AtomMassesMapped"); } else { weights_ = atomMasses_; } } //-------------------------------------------------------- // apply_localization_correction // corrects for localized kinetostats only // solving kinetostat equations on a subset // of the MD region //-------------------------------------------------------- void DisplacementGlc::apply_localization_correction(const DENS_MAT & source, DENS_MAT & nodalField, double weight) { DENS_MAT nodalLambdaRoc(nNodes_,nsd_); atc_->apply_inverse_mass_matrix(source, nodalLambdaRoc, VELOCITY); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { nodalLambdaRoc(iter->first,iter->second) = 0.; } nodalField += weight*nodalLambdaRoc; } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void DisplacementGlc::output(OUTPUT_LIST & outputData) { _nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity(); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class DisplacementGlcFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - DisplacementGlcFiltered::DisplacementGlcFiltered(Kinetostat * kinetostat) : + DisplacementGlcFiltered::DisplacementGlcFiltered(AtomicRegulator * kinetostat) : DisplacementGlc(kinetostat), nodalAtomicDisplacements_(atc_->nodal_atomic_field(DISPLACEMENT)) { // do nothing } //-------------------------------------------------------- // apply_pre_filtering // applies first step of filtering to // relevant variables //-------------------------------------------------------- void DisplacementGlcFiltered::apply_pre_filtering(double dt) { // apply time filtered lambda to atomic fields DisplacementGlc::apply_pre_filtering(dt); DENS_MAT nodalAcceleration(nNodes_,nsd_); atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->set_quantity(), nodalAcceleration, VELOCITY); nodalAtomicDisplacements_ += dt*dt*nodalAcceleration; } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the right-hand side of the // kinetostat equations //-------------------------------------------------------- void DisplacementGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt)); - rhs = coef*((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalAtomicDisplacements_.quantity() - nodalDisplacements_.quantity()); + rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicDisplacements_.quantity() - nodalDisplacements_.quantity()); } //-------------------------------------------------------- // compute_nodal_lambda_force // compute the effective FE force applied // by the kinetostat //-------------------------------------------------------- void DisplacementGlcFiltered::compute_nodal_lambda_force(double dt) { const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity()); DENS_MAT & myLambdaForceFiltered(lambdaForceFiltered_->set_quantity()); timeFilter_->apply_post_step1(myLambdaForceFiltered, myNodalAtomicLambdaForce,dt); // update filtered atomic displacements DENS_MAT nodalLambdaRoc(myNodalAtomicLambdaForce.nRows(),myNodalAtomicLambdaForce.nCols()); atc_->apply_inverse_md_mass_matrix(myNodalAtomicLambdaForce, nodalLambdaRoc, VELOCITY); timeFilter_->apply_post_step1(nodalAtomicDisplacements_.set_quantity(),dt*dt*nodalLambdaRoc,dt); // update FE displacements for localized thermostats apply_localization_correction(myLambdaForceFiltered, nodalDisplacements_.set_quantity(), dt*dt); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void DisplacementGlcFiltered::output(OUTPUT_LIST & outputData) { DENS_MAT & lambda(lambda_->set_quantity()); DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &lambdaForceFiltered; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocityGlc //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - VelocityGlc::VelocityGlc(Kinetostat * kinetostat) : + VelocityGlc::VelocityGlc(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalAtomicMomentum_(NULL), nodalVelocities_(atc_->field(VELOCITY)) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void VelocityGlc::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // set up shape function matrix - if (this->use_local_shape_functions()) { - lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); - interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, - regulatorPrefix_+"LambdaAtomMap"); - shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, - lambdaAtomMap_, - nodeToOverlapMap_); - } - else { - shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); + shapeFunctionMatrix_ = interscaleManager.per_atom_sparse_matrix(regulatorPrefix_+"LambdaCouplingMatrixMomentum"); + if (!shapeFunctionMatrix_) { + if (this->use_local_shape_functions()) { + lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); + interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, + regulatorPrefix_+"LambdaAtomMap"); + shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, + lambdaAtomMap_, + nodeToOverlapMap_); + } + else { + shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); + } + interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, + regulatorPrefix_+"LambdaCouplingMatrixMomentum"); } - interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, - regulatorPrefix_+"LambdaCouplingMatrixMomentum"); // set linear solver strategy if (atomicRegulator_->use_lumped_lambda_solve()) { linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { linearSolverType_ = AtomicRegulator::CG_SOLVE; } // base class transfers GlcKinetostat::construct_transfers(); // atomic force induced by kinetostat atomKinetostatForce_ = new AtomicKinetostatForceVelocity(atc_); interscaleManager.add_per_atom_quantity(atomKinetostatForce_, regulatorPrefix_+"AtomKinetostatForce"); // restricted force due to kinetostat nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_, atomKinetostatForce_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_, - regulatorPrefix_+"NodalAtomicLambdaForce"); + regulatorPrefix_+"NodalAtomicLambdaForce"); // nodal momentum restricted from atoms nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void VelocityGlc::initialize() { GlcKinetostat::initialize(); // sets up time filter for cases where variables temporally filtered TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { lambdaForceFiltered_->set_quantity() = 0.; timeFilter_->initialize(lambdaForceFiltered_->quantity()); } } //-------------------------------------------------------- // apply_mid_corrector: // apply the kinetostat during the middle of the // predictor phase //-------------------------------------------------------- void VelocityGlc::apply_mid_predictor(double dt) { double dtLambda = 0.5*dt; compute_kinetostat(dtLambda); + apply_kinetostat(dtLambda); } //-------------------------------------------------------- // apply_post_corrector: // apply the kinetostat after the corrector phase //-------------------------------------------------------- void VelocityGlc::apply_post_corrector(double dt) { double dtLambda = 0.5*dt; compute_kinetostat(dtLambda); + apply_kinetostat(dtLambda); } //-------------------------------------------------------- // apply_pre_filtering // applies first step of filtering to // relevant variables //-------------------------------------------------------- void VelocityGlc::apply_pre_filtering(double dt) { // apply time filtered lambda to atomic fields DENS_MAT lambdaZero(nNodes_,nsd_); timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),(-1./dt)*lambdaZero,dt); } //-------------------------------------------------------- // compute_kinetostat // manages the solution and application of the // kinetostat equations and variables //-------------------------------------------------------- void VelocityGlc::compute_kinetostat(double dt) { // initial filtering update apply_pre_filtering(dt); // set up rhs DENS_MAT rhs(nNodes_,nsd_); - set_kinetostat_rhs(rhs,dt); + this->set_kinetostat_rhs(rhs,dt); // solve linear system for lambda solve_for_lambda(rhs,lambda_->set_quantity()); +#ifdef OBSOLETE + // compute nodal atomic power + compute_nodal_lambda_force(dt); + + // apply kinetostat to atoms + apply_to_atoms(atomVelocities_,atomLambdas_->quantity()); +#endif + } + //-------------------------------------------------------- + // apply_kinetostat + // manages the application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void VelocityGlc::apply_kinetostat(double dt) + { // compute nodal atomic power compute_nodal_lambda_force(dt); // apply kinetostat to atoms apply_to_atoms(atomVelocities_,atomLambdas_->quantity()); } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the right-hand side of the // kinetostat equations //-------------------------------------------------------- void VelocityGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // form rhs : sum_a (hatN_Ia * x_ai) - (\dot{Upsilon})_Ii rhs = nodalAtomicMomentum_->quantity(); - rhs -= ((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalVelocities_.quantity()); + rhs -= (mdMassMatrix_.quantity())*(nodalVelocities_.quantity()); } //-------------------------------------------------------- // compute_nodal_lambda_force // compute the effective FE force applied // by the kinetostat //-------------------------------------------------------- void VelocityGlc::compute_nodal_lambda_force(double dt) { const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity()); timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(), myNodalAtomicLambdaForce,dt); // update FE displacements for localized thermostats apply_localization_correction(myNodalAtomicLambdaForce, nodalVelocities_.set_quantity(), dt); } //-------------------------------------------------------- // set_weights // sets diagonal weighting matrix used in // solve_for_lambda //-------------------------------------------------------- void VelocityGlc::set_weights() { if (lambdaAtomMap_) { MappedAtomQuantity * myWeights = new MappedAtomQuantity(atc_,atomMasses_,lambdaAtomMap_); weights_ = myWeights; (atc_->interscale_manager()).add_per_atom_quantity(myWeights, "AtomMassesMapped"); } else { weights_ = atomMasses_; } } //-------------------------------------------------------- // apply_localization_correction // corrects for localized kinetostats only // solving kinetostat equations on a subset // of the MD region //-------------------------------------------------------- void VelocityGlc::apply_localization_correction(const DENS_MAT & source, DENS_MAT & nodalField, double weight) { DENS_MAT nodalLambdaRoc(nNodes_,nsd_); atc_->apply_inverse_mass_matrix(source, nodalLambdaRoc, VELOCITY); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { nodalLambdaRoc(iter->first,iter->second) = 0.; } nodalField += weight*nodalLambdaRoc; } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void VelocityGlc::output(OUTPUT_LIST & outputData) { _nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity(); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = &(lambda_->set_quantity()); + outputData[regulatorPrefix_+"LambdaMomentum"] = &(lambda_->set_quantity()); outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocityGlcFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - VelocityGlcFiltered::VelocityGlcFiltered(Kinetostat *kinetostat) + VelocityGlcFiltered::VelocityGlcFiltered(AtomicRegulator *kinetostat) : VelocityGlc(kinetostat), nodalAtomicVelocities_(atc_->nodal_atomic_field(VELOCITY)) { // do nothing } //-------------------------------------------------------- // apply_pre_filtering // applies first step of filtering to // relevant variables //-------------------------------------------------------- void VelocityGlcFiltered::apply_pre_filtering(double dt) { // apply time filtered lambda to atomic fields VelocityGlc::apply_pre_filtering(dt); DENS_MAT nodalAcceleration(nNodes_,nsd_); atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->quantity(), nodalAcceleration, VELOCITY); nodalAtomicVelocities_ += dt*nodalAcceleration; } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the right-hand side of the // kinetostat equations //-------------------------------------------------------- void VelocityGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii double coef = 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt)); - rhs = coef*((atc_->mass_mat_md(VELOCITY)).quantity())*(nodalAtomicVelocities_.quantity() - nodalVelocities_.quantity()); + rhs = coef*(mdMassMatrix_.quantity())*(nodalAtomicVelocities_.quantity() - nodalVelocities_.quantity()); } //-------------------------------------------------------- // compute_nodal_lambda_force // compute the effective FE force applied // by the kinetostat //-------------------------------------------------------- void VelocityGlcFiltered::compute_nodal_lambda_force(double dt) { const DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->quantity()); DENS_MAT & myLambdaForceFiltered(lambdaForceFiltered_->set_quantity()); timeFilter_->apply_post_step1(myLambdaForceFiltered,myNodalAtomicLambdaForce,dt); // update filtered atomic displacements DENS_MAT nodalLambdaRoc(myNodalAtomicLambdaForce.nRows(),myNodalAtomicLambdaForce.nCols()); atc_->apply_inverse_md_mass_matrix(myNodalAtomicLambdaForce, nodalLambdaRoc, VELOCITY); timeFilter_->apply_post_step1(nodalAtomicVelocities_.set_quantity(),dt*nodalLambdaRoc,dt); // update FE displacements for localized thermostats apply_localization_correction(myLambdaForceFiltered, nodalVelocities_.set_quantity(), dt); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void VelocityGlcFiltered::output(OUTPUT_LIST & outputData) { if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = &(lambda_->set_quantity()); outputData[regulatorPrefix_+"NodalLambdaForce"] = &(lambdaForceFiltered_->set_quantity()); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFlux //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - StressFlux::StressFlux(Kinetostat * kinetostat) : + StressFlux::StressFlux(AtomicRegulator * kinetostat) : GlcKinetostat(kinetostat), nodalForce_(atc_->field_rhs(VELOCITY)), nodalAtomicForce_(NULL), nodalGhostForce_(NULL), momentumSource_(atc_->atomic_source(VELOCITY)) { // flag for performing boundary flux calculation fieldMask_(VELOCITY,FLUX) = true; } StressFlux::~StressFlux() { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void StressFlux::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // set up shape function matrix if (this->use_local_shape_functions()) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, lambdaAtomMap_, nodeToOverlapMap_); } else { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); } interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixMomentum"); // set linear solver strategy if (atomicRegulator_->use_lumped_lambda_solve()) { linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { linearSolverType_ = AtomicRegulator::CG_SOLVE; } // base class transfers GlcKinetostat::construct_transfers(); // force at nodes due to atoms nodalAtomicForce_ = interscaleManager.dense_matrix("NodalAtomicForce"); // atomic force induced by kinetostat atomKinetostatForce_ = new AtomicKinetostatForceStress(atc_,atomLambdas_); interscaleManager.add_per_atom_quantity(atomKinetostatForce_, regulatorPrefix_+"AtomKinetostatForce"); // restricted force due to kinetostat nodalAtomicLambdaForce_ = new AtfShapeFunctionRestriction(atc_, atomKinetostatForce_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicLambdaForce_, regulatorPrefix_+"NodalAtomicLambdaForce"); // sets up space for ghost force related variables if (atc_->groupbit_ghost()) { GhostCouplingMatrix * shapeFunctionGhost = new GhostCouplingMatrix(atc_,interscaleManager.per_atom_sparse_matrix("InterpolantGhost"), regulatedNodes_,nodeToOverlapMap_); interscaleManager.add_sparse_matrix(shapeFunctionGhost, regulatorPrefix_+"GhostCouplingMatrix"); FundamentalAtomQuantity * atomGhostForce = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE, GHOST); nodalGhostForce_ = new AtfShapeFunctionRestriction(atc_,atomGhostForce, shapeFunctionGhost); interscaleManager.add_dense_matrix(nodalGhostForce_, regulatorPrefix_+"NodalGhostForce"); nodalGhostForceFiltered_.reset(nNodes_,nsd_); } } //-------------------------------------------------------- // compute_boundary_flux: // computes the boundary flux to be consistent with // the controller //-------------------------------------------------------- void StressFlux::compute_boundary_flux(FIELDS & fields) { GlcKinetostat::compute_boundary_flux(fields); } //-------------------------------------------------------- // apply_pre_predictor: // apply the kinetostat to the atoms in the // mid-predictor integration phase //-------------------------------------------------------- void StressFlux::apply_pre_predictor(double dt) { double dtLambda = 0.5*dt; // apply lambda force to atoms apply_to_atoms(atomVelocities_,atomKinetostatForce_->quantity(),dtLambda); } //-------------------------------------------------------- // apply_post_corrector: // apply the kinetostat to the atoms in the // post-corrector integration phase //-------------------------------------------------------- void StressFlux::apply_post_corrector(double dt) { double dtLambda = 0.5*dt; // apply lambda force to atoms apply_to_atoms(atomVelocities_,atomKinetostatForce_->quantity(),dtLambda); } //-------------------------------------------------------- // compute_kinetostat // manages the solution and application of the // kinetostat equations and variables //-------------------------------------------------------- void StressFlux::compute_kinetostat(double dt) { // initial filtering update apply_pre_filtering(dt); // set up rhs DENS_MAT rhs(nNodes_,nsd_); set_kinetostat_rhs(rhs,dt); // solve linear system for lambda solve_for_lambda(rhs,lambda_->set_quantity()); // compute nodal atomic power compute_nodal_lambda_force(dt); } //-------------------------------------------------------- // apply_pre_filtering // applies first step of filtering to // relevant variables //-------------------------------------------------------- void StressFlux::apply_pre_filtering(double dt) { // apply time filtered lambda force DENS_MAT lambdaZero(nNodes_,nsd_); timeFilter_->apply_pre_step1(lambdaForceFiltered_->set_quantity(),lambdaZero,dt); if (nodalGhostForce_) { timeFilter_->apply_pre_step1(nodalGhostForceFiltered_.set_quantity(), nodalGhostForce_->quantity(),dt); } } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void StressFlux::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // (a) for flux based : // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g // sources are set in ATC transfer rhs.reset(nNodes_,nsd_); rhs = momentumSource_.quantity(); if (nodalGhostForce_) { rhs -= nodalGhostForce_->quantity(); } // (b) for ess. bcs // form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I} DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity(); atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY); rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed; rhsPrescribed += nodalAtomicForce_->quantity(); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second); } } //-------------------------------------------------------- // compute_nodal_lambda_force // computes the force induced on the FE // by applying lambdaForce on the atoms //-------------------------------------------------------- void StressFlux::compute_nodal_lambda_force(double dt) { DENS_MAT myNodalAtomicLambdaForce = nodalAtomicLambdaForce_->quantity(); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { myNodalAtomicLambdaForce(iter->first,iter->second) = 0.; } timeFilter_->apply_post_step1(lambdaForceFiltered_->set_quantity(), myNodalAtomicLambdaForce,dt); } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the kinetostat //-------------------------------------------------------- void StressFlux::add_to_rhs(FIELDS & rhs) { // compute the kinetostat force compute_kinetostat(atc_->dt()); rhs[VELOCITY] += nodalAtomicLambdaForce_->quantity() + boundaryFlux_[VELOCITY].quantity(); } //-------------------------------------------------------- // apply_lambda_to_atoms // uses existing lambda to modify given // atomic quantity //-------------------------------------------------------- void StressFlux::apply_to_atoms(PerAtomQuantity<double> * atomVelocities, const DENS_MAT & lambdaForce, double dt) { _deltaVelocity_ = lambdaForce; _deltaVelocity_ /= atomMasses_->quantity(); _deltaVelocity_ *= dt; *atomVelocities += _deltaVelocity_; } //-------------------------------------------------------- // reset_filtered_ghost_force: // resets the kinetostat generated ghost force to a // prescribed value //-------------------------------------------------------- void StressFlux::reset_filtered_ghost_force(DENS_MAT & target) { nodalGhostForceFiltered_ = target; } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void StressFlux::output(OUTPUT_LIST & outputData) { _nodalAtomicLambdaForceOut_ = nodalAtomicLambdaForce_->quantity(); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_nodalAtomicLambdaForceOut_); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFluxGhost //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - StressFluxGhost::StressFluxGhost(Kinetostat * kinetostat) : + StressFluxGhost::StressFluxGhost(AtomicRegulator * kinetostat) : StressFlux(kinetostat) { // flag for performing boundary flux calculation fieldMask_(VELOCITY,FLUX) = false; } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void StressFluxGhost::construct_transfers() { StressFlux::construct_transfers(); if (!nodalGhostForce_) { throw ATC_Error("StressFluxGhost::StressFluxGhost - ghost atoms must be specified"); } } //-------------------------------------------------------- // compute_boundary_flux: // computes the boundary flux to be consistent with // the controller //-------------------------------------------------------- void StressFluxGhost::compute_boundary_flux(FIELDS & fields) { // This is only used in computation of atomic sources boundaryFlux_[VELOCITY] = 0.; } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the kinetostat //-------------------------------------------------------- void StressFluxGhost::add_to_rhs(FIELDS & rhs) { // compute the kinetostat force compute_kinetostat(atc_->dt()); // uses ghost force as the boundary flux to add to the RHS rhs[VELOCITY] += nodalAtomicLambdaForce_->quantity() + nodalGhostForce_->quantity(); } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void StressFluxGhost::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // (a) for flux based : // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g // sources are set in ATC transfer rhs.reset(nNodes_,nsd_); rhs = momentumSource_.quantity(); // (b) for ess. bcs // form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I} DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity(); atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY); rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed; rhsPrescribed += nodalAtomicForce_->quantity(); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFluxFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - StressFluxFiltered::StressFluxFiltered(Kinetostat * kinetostat) : + StressFluxFiltered::StressFluxFiltered(AtomicRegulator * kinetostat) : StressFlux(kinetostat), nodalAtomicVelocity_(atc_->nodal_atomic_field(VELOCITY)) { // do nothing } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void StressFluxFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // set basic terms // (a) for flux based : // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g // sources are set in ATC transfer rhs.reset(nNodes_,nsd_); rhs = momentumSource_.quantity() - nodalGhostForceFiltered_.quantity(); // (b) for ess. bcs // form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I} DENS_MAT rhsPrescribed = -1.*nodalForce_.quantity(); atc_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY); rhsPrescribed = (mdMassMatrix_.quantity())*rhsPrescribed; rhsPrescribed += nodalAtomicForce_->quantity(); set<pair<int,int> >::const_iterator iter; for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second); } // adjust for application of current lambda force rhs += lambdaForceFiltered_->quantity(); // correct for time filtering rhs *= 1./(timeFilter_->unfiltered_coefficient_pre_s1(dt)); } //-------------------------------------------------------- // apply_lambda_to_atoms // uses existing lambda to modify given // atomic quantity //-------------------------------------------------------- void StressFluxFiltered::apply_to_atoms(PerAtomQuantity<double> * atomVelocities, const DENS_MAT & lambdaForce, double dt) { StressFlux::apply_to_atoms(atomVelocities,lambdaForce,dt); // add in corrections to filtered nodal atomice velocity DENS_MAT velocityRoc(nNodes_,nsd_); atc_->apply_inverse_md_mass_matrix(lambdaForceFiltered_->quantity(), velocityRoc, VELOCITY); nodalAtomicVelocity_ += dt*velocityRoc; } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the kinetostat //-------------------------------------------------------- void StressFluxFiltered::add_to_rhs(FIELDS & rhs) { // compute kinetostat forces compute_kinetostat(atc_->dt()); rhs[VELOCITY] += lambdaForceFiltered_->quantity() + boundaryFlux_[VELOCITY].quantity(); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void StressFluxFiltered::output(OUTPUT_LIST & outputData) { DENS_MAT & lambda(lambda_->set_quantity()); DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &lambdaForceFiltered; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetostatGlcFs //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatGlcFs::KinetostatGlcFs(Kinetostat * kinetostat, + KinetostatGlcFs::KinetostatGlcFs(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatShapeFunction(kinetostat,regulatorPrefix), - velocity_(atc_->field(VELOCITY)) + velocity_(atc_->field(VELOCITY)), + //timeFilter_(atomicRegulator_->time_filter()), + //nodalAtomicLambdaForce_(NULL), + //lambdaPowerFiltered_(NULL), + //atomKinetostatForces_(NULL), + //atomMasses_(NULL), + nodalAtomicMomentum_(NULL), + isFirstTimestep_(true), + atomPredictedVelocities_(NULL), + nodalAtomicPredictedMomentum_(NULL), + dtFactor_(0.) { // constuct/obtain data corresponding to stage 3 of ATC_Method::initialize - nodalAtomicLambdaForce_ = kinetostat_->regulator_data(regulatorPrefix_+"NodalAtomicLambdaForce",nsd_); + nodalAtomicLambdaForce_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalAtomicLambdaForce",nsd_); + lambdaForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaForceFiltered",1); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void KinetostatGlcFs::construct_transfers() { - InterscaleManager & interscaleManager(atc_->interscale_manager()); - // base class transfers KinetostatShapeFunction::construct_transfers(); + InterscaleManager & interscaleManager(atc_->interscale_manager()); // get data from manager nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum"); // atomic force induced by kinetostat PerAtomQuantity<double> * atomLambdas = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaMomentum"); atomKinetostatForce_ = new AtomicKinetostatForceStress(atc_,atomLambdas); interscaleManager.add_per_atom_quantity(atomKinetostatForce_, regulatorPrefix_+"AtomKinetostatForce"); + + // predicted momentum quantities: atom velocities, atom momenta, and restricted atom momenta + // MAKE THINGS WORK WITH ONLY ONE PREDICTED VELOCITY AND DERIVED QUANTITIES, CHECK IT EXISTS + atomPredictedVelocities_ = new AtcAtomQuantity<double>(atc_,nsd_); + interscaleManager.add_per_atom_quantity(atomPredictedVelocities_, + regulatorPrefix_+"AtomicPredictedVelocities"); + AtomicMomentum * atomPredictedMomentum = new AtomicMomentum(atc_, + atomPredictedVelocities_); + interscaleManager.add_per_atom_quantity(atomPredictedMomentum, + regulatorPrefix_+"AtomicPredictedMomentum"); + nodalAtomicPredictedMomentum_ = new AtfShapeFunctionRestriction(atc_, + atomPredictedMomentum, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_dense_matrix(nodalAtomicPredictedMomentum_, + regulatorPrefix_+"NodalAtomicPredictedMomentum"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void KinetostatGlcFs::initialize() { KinetostatShapeFunction::initialize(); - - // set up workspaces - _deltaMomentum_.reset(nNodes_,nsd_); - _lambdaForceOutput_.reset(nNodes_,nsd_); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { // we should reset lambda and lambdaForce to zero in this case // implies an initial condition of 0 for the filtered nodal lambda power // initial conditions will always be needed when using time filtering // however, the fractional step scheme must assume the instantaneous // nodal lambda power is 0 initially because all quantities are in delta form *lambda_ = 0.; // ensures initial lambda force is zero *nodalAtomicLambdaForce_ = 0.; // momentum change due to kinetostat *lambdaForceFiltered_ = 0.; // filtered momentum change due to kinetostats } else { // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration } // sets up time filter for cases where variables temporally filtered if (timeFilterManager->need_reset()) { // the form of this integrator implies no time filters that require history data can be used timeFilter_->initialize(nodalAtomicLambdaForce_->quantity()); } - compute_rhs_map(); + atomKinetostatForce_->quantity(); // initialize } //-------------------------------------------------------- - // compute_rhs_map - // creates mapping from all nodes to those to which - // the kinetostat applies + // apply_lambda_to_atoms + // uses existing lambda to modify given + // atomic quantity //-------------------------------------------------------- - void KinetostatGlcFs::compute_rhs_map() + void KinetostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomVelocity, + const DENS_MAN * nodalAtomicMomentum, + const DENS_MAT & lambdaForce, + DENS_MAT & nodalAtomicLambdaForce, + double dt) { - rhsMap_.resize(overlapToNodeMap_->nRows(),1); - DENS_MAT rhsMapGlobal(nNodes_,1); - const set<int> & applicationNodes(applicationNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (applicationNodes.find(i) != applicationNodes.end()) { - rhsMapGlobal(i,0) = 1.; - } - else { - rhsMapGlobal(i,0) = 0.; - } + // compute initial contributions to lambda force + nodalAtomicLambdaForce = nodalAtomicMomentum->quantity(); + nodalAtomicLambdaForce *= -1.; + + // apply lambda force to atoms + _velocityDelta_ = lambdaForce; + _velocityDelta_ /= atomMasses_->quantity(); + _velocityDelta_ *= dt; + (*atomVelocity) += _velocityDelta_; + + // finalize lambda force + nodalAtomicLambdaForce += nodalAtomicMomentum->quantity(); + } + + //-------------------------------------------------------- + // full_prediction: + // flag to perform a full prediction calcalation + // for lambda rather than using the old value + //-------------------------------------------------------- + bool KinetostatGlcFs::full_prediction() + { + if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) + && (atc_->atom_to_element_map_frequency() > 1) + && (atc_->step() % atc_->atom_to_element_map_frequency() == 0 ))) { + return true; } - map_unique_to_overlap(rhsMapGlobal,rhsMap_); + return false; } //-------------------------------------------------------- // apply_pre_predictor: // apply the kinetostat to the atoms in the // pre-predictor integration phase //-------------------------------------------------------- void KinetostatGlcFs::apply_pre_predictor(double dt) { DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); // update filtered forces timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); // apply lambda force to atoms and compute instantaneous lambda force this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, atomKinetostatForce_->quantity(), nodalAtomicLambdaForce,0.5*dt); // update nodal variables for first half of timestep - this->add_to_momentum(nodalAtomicLambdaForce,_deltaMomentum_,0.5*dt); - atc_->apply_inverse_mass_matrix(_deltaMomentum_,VELOCITY); - velocity_ += _deltaMomentum_; + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; // start update of filtered lambda force nodalAtomicLambdaForce = 0.; timeFilter_->apply_post_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatGlcFs::apply_pre_corrector(double dt) + { + (*atomPredictedVelocities_) = atomVelocities_->quantity(); + + // do full prediction if we just redid the shape functions + if (full_prediction()) { + this->compute_lambda(dt); + } + + // apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step + DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); + apply_to_atoms(atomPredictedVelocities_, + nodalAtomicPredictedMomentum_, + atomKinetostatForce_->quantity(), + myNodalAtomicLambdaForce,0.5*dt); + + // update predicted nodal variables for second half of time step + this->add_to_momentum(myNodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; + } //-------------------------------------------------------- // apply_post_corrector: // apply the kinetostat to the atoms in the // post-corrector integration phase //-------------------------------------------------------- void KinetostatGlcFs::apply_post_corrector(double dt) { + // remove predicted force effects + DENS_MAT & myVelocity(velocity_.set_quantity()); + myVelocity -= deltaMomentum_; + // compute the kinetostat equation and update lambda this->compute_lambda(dt); - DENS_MAT & lambdaForceFiltered(lambdaForceFiltered_->set_quantity()); + // apply lambda force to atoms and compute instantaneous lambda force for second half of time step DENS_MAT & nodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); - - // update filtered force - timeFilter_->apply_pre_step1(lambdaForceFiltered,nodalAtomicLambdaForce,dt); - - // apply lambda force to atoms and compute instantaneous lambda force this->apply_to_atoms(atomVelocities_,nodalAtomicMomentum_, atomKinetostatForce_->quantity(), nodalAtomicLambdaForce,0.5*dt); + // start update of filtered lambda force + timeFilter_->apply_post_step2(lambdaForceFiltered_->set_quantity(), + nodalAtomicLambdaForce,dt); + // update nodal variables for first half of timestep - this->add_to_momentum(nodalAtomicLambdaForce,_deltaMomentum_,0.5*dt); - nodalAtomicLambdaForce *= 2./dt; - atc_->apply_inverse_mass_matrix(_deltaMomentum_,VELOCITY); - velocity_ += _deltaMomentum_; + this->add_to_momentum(nodalAtomicLambdaForce,deltaMomentum_,0.5*dt); + atc_->apply_inverse_mass_matrix(deltaMomentum_,VELOCITY); + velocity_ += deltaMomentum_; - // start update of filtered lambda force - timeFilter_->apply_post_step2(lambdaForceFiltered,nodalAtomicLambdaForce,dt); + + isFirstTimestep_ = false; } //-------------------------------------------------------- // compute_kinetostat // manages the solution and application of the // kinetostat equations and variables //-------------------------------------------------------- void KinetostatGlcFs::compute_lambda(double dt) { // set up rhs for lambda equation this->set_kinetostat_rhs(rhs_,0.5*dt); // solve linear system for lambda DENS_MAT & lambda(lambda_->set_quantity()); solve_for_lambda(rhs_,lambda); } - //-------------------------------------------------------- - // apply_lambda_to_atoms - // uses existing lambda to modify given - // atomic quantity - //-------------------------------------------------------- - void KinetostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomVelocity, - const DENS_MAN * nodalAtomicMomentum, - const DENS_MAT & lambdaForce, - DENS_MAT & nodalAtomicLambdaForce, - double dt) - { - // compute initial contributions to lambda force - nodalAtomicLambdaForce = nodalAtomicMomentum->quantity(); - nodalAtomicLambdaForce *= -1.; - - // apply lambda force to atoms - _velocityDelta_ = lambdaForce; - _velocityDelta_ /= atomMasses_->quantity(); - _velocityDelta_ *= dt; - (*atomVelocity) += _velocityDelta_; - - // finalize lambda force - nodalAtomicLambdaForce += nodalAtomicMomentum->quantity(); - } - //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void KinetostatGlcFs::output(OUTPUT_LIST & outputData) { _lambdaForceOutput_ = nodalAtomicLambdaForce_->quantity(); // approximate value for lambda force double dt = LammpsInterface::instance()->dt(); _lambdaForceOutput_ *= (2./dt); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaMomentum"] = λ outputData[regulatorPrefix_+"NodalLambdaForce"] = &(_lambdaForceOutput_); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetostatFlux //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFlux::KinetostatFlux(Kinetostat * kinetostat, + KinetostatFlux::KinetostatFlux(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatGlcFs(kinetostat,regulatorPrefix), momentumSource_(atc_->atomic_source(VELOCITY)), nodalGhostForce_(NULL), nodalGhostForceFiltered_(NULL) { // flag for performing boundary flux calculation fieldMask_(VELOCITY,FLUX) = true; // constuct/obtain data corresponding to stage 3 of ATC_Method::initialize - nodalGhostForceFiltered_ = kinetostat_->regulator_data(regulatorPrefix_+"NodalGhostForceFiltered",nsd_); + nodalGhostForceFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"NodalGhostForceFiltered",nsd_); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void KinetostatFlux::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); - - // set up linear solver // set up data for linear solver shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixMomentum"); if (elementMask_) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); } if (atomicRegulator_->use_localized_lambda()) { linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { linearSolverType_ = AtomicRegulator::CG_SOLVE; } // base class transfers KinetostatGlcFs::construct_transfers(); // sets up space for ghost force related variables if (atc_->groupbit_ghost()) { MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap = interscaleManager.dense_matrix_int(regulatorPrefix_+"NodeToOverlapMap"); GhostCouplingMatrix * shapeFunctionGhost = new GhostCouplingMatrix(atc_,interscaleManager.per_atom_sparse_matrix("InterpolantGhost"), regulatedNodes_, nodeToOverlapMap); interscaleManager.add_sparse_matrix(shapeFunctionGhost, regulatorPrefix_+"GhostCouplingMatrix"); FundamentalAtomQuantity * atomGhostForce = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE, GHOST); nodalGhostForce_ = new AtfShapeFunctionRestriction(atc_,atomGhostForce, shapeFunctionGhost); interscaleManager.add_dense_matrix(nodalGhostForce_, regulatorPrefix_+"NodalGhostForce"); } } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void KinetostatFlux::initialize() { KinetostatGlcFs::initialize(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { // we should reset lambda and lambdaForce to zero in this case // implies an initial condition of 0 for the filtered nodal lambda power // initial conditions will always be needed when using time filtering // however, the fractional step scheme must assume the instantaneous // nodal lambda power is 0 initially because all quantities are in delta form *nodalGhostForceFiltered_ = 0.; // filtered force from ghost atoms } else { // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration } + + // timestep factor + dtFactor_ = 1.; } //-------------------------------------------------------- // construct_regulated_nodes: // constructs the set of nodes being regulated //-------------------------------------------------------- void KinetostatFlux::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // matrix requires all entries even if localized for correct lumping - regulatedNodes_ = new RegulatedNodes(atc_); - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"KinetostatRegulatedNodes"); - + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"KinetostatRegulatedNodes"); + if (!regulatedNodes_) { + regulatedNodes_ = new RegulatedNodes(atc_); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"KinetostatRegulatedNodes"); + } + // if localized monitor nodes with applied fluxes if (atomicRegulator_->use_localized_lambda()) { - if ((kinetostat_->coupling_mode() == Kinetostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { + if ((atomicRegulator_->coupling_mode() == Kinetostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { // include boundary nodes applicationNodes_ = new FluxBoundaryNodes(atc_); boundaryNodes_ = new BoundaryNodes(atc_); interscaleManager.add_set_int(boundaryNodes_, regulatorPrefix_+"KinetostatBoundaryNodes"); } else { // fluxed nodes only applicationNodes_ = new FluxNodes(atc_); } interscaleManager.add_set_int(applicationNodes_, regulatorPrefix_+"KinetostatApplicationNodes"); } else { applicationNodes_ = regulatedNodes_; } // special set of boundary elements for boundary flux quadrature if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION) && (atomicRegulator_->use_localized_lambda())) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } //-------------------------------------------------------- // apply_pre_predictor: // apply the kinetostat to the atoms in the // pre-predictor integration phase //-------------------------------------------------------- void KinetostatFlux::apply_pre_predictor(double dt) { // update filtered forces if (nodalGhostForce_) { timeFilter_->apply_pre_step1(nodalGhostForceFiltered_->set_quantity(), nodalGhostForce_->quantity(),dt); } KinetostatGlcFs::apply_pre_predictor(dt); } //-------------------------------------------------------- // apply_post_corrector: // apply the kinetostat to the atoms in the // post-corrector integration phase //-------------------------------------------------------- void KinetostatFlux::apply_post_corrector(double dt) { // update filtered ghost force if (nodalGhostForce_) { timeFilter_->apply_post_step1(nodalGhostForceFiltered_->set_quantity(), nodalGhostForce_->quantity(),dt); } // compute the kinetostat equation and update lambda KinetostatGlcFs::apply_post_corrector(dt); } + //-------------------------------------------------------- + // add_to_momentum: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the kinetostat + //-------------------------------------------------------- + void KinetostatFlux::add_to_momentum(const DENS_MAT & nodalLambdaForce, + DENS_MAT & deltaMomentum, + double dt) + { + deltaMomentum.resize(nNodes_,nsd_); + const DENS_MAT & boundaryFlux(boundaryFlux_[VELOCITY].quantity()); + for (int i = 0; i < nNodes_; i++) { + for (int j = 0; j < nsd_; j++) { + deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j); + } + } + } + //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void KinetostatFlux::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // (a) for flux based : // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g // sources are set in ATC transfer rhs.reset(nNodes_,nsd_); const DENS_MAT & momentumSource(momentumSource_.quantity()); const set<int> & applicationNodes(applicationNodes_->quantity()); set<int>::const_iterator iNode; for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) { for (int j = 0; j < nsd_; j++) { rhs(*iNode,j) = momentumSource(*iNode,j); } } // add ghost forces, if needed if (nodalGhostForce_) { const DENS_MAT & nodalGhostForce(nodalGhostForce_->quantity()); for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) { for (int j = 0; j < nsd_; j++) { rhs(*iNode,j) -= nodalGhostForce(*iNode,j); } } } } - //-------------------------------------------------------- - // add_to_momentum: - // determines what if any contributions to the - // finite element equations are needed for - // consistency with the kinetostat - //-------------------------------------------------------- - void KinetostatFlux::add_to_momentum(const DENS_MAT & nodalLambdaForce, - DENS_MAT & deltaMomentum, - double dt) - { - deltaMomentum.resize(nNodes_,nsd_); - const DENS_MAT & boundaryFlux(boundaryFlux_[VELOCITY].quantity()); - for (int i = 0; i < nNodes_; i++) { - for (int j = 0; j < nsd_; j++) { - deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j); - } - } - } - //-------------------------------------------------------- // reset_filtered_ghost_force: // resets the kinetostat generated ghost force to a // prescribed value //-------------------------------------------------------- void KinetostatFlux::reset_filtered_ghost_force(DENS_MAT & target) { (*nodalGhostForceFiltered_) = target; } //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetostatFluxGhost //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFluxGhost::KinetostatFluxGhost(Kinetostat * kinetostat, + KinetostatFluxGhost::KinetostatFluxGhost(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatFlux(kinetostat,regulatorPrefix) { // flag for performing boundary flux calculation fieldMask_(VELOCITY,FLUX) = false; } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void KinetostatFluxGhost::construct_transfers() { KinetostatFlux::construct_transfers(); if (!nodalGhostForce_) { - throw ATC_Error("StressFluxGhost::StressFluxGhost - ghost atoms must be specified"); + throw ATC_Error("KinetostatFluxGhost::KinetostatFluxGhost - ghost atoms must be specified"); } } //-------------------------------------------------------- // compute_boundary_flux: // computes the boundary flux to be consistent with // the controller //-------------------------------------------------------- void KinetostatFluxGhost::compute_boundary_flux(FIELDS & fields) { // This is only used in computation of atomic sources boundaryFlux_[VELOCITY] = 0.; } //-------------------------------------------------------- // add_to_momentum: // determines what if any contributions to the // finite element equations are needed for // consistency with the kinetostat //-------------------------------------------------------- void KinetostatFluxGhost::add_to_momentum(const DENS_MAT & nodalLambdaForce, DENS_MAT & deltaMomentum, double dt) { deltaMomentum.resize(nNodes_,nsd_); const DENS_MAT & boundaryFlux(nodalGhostForce_->quantity()); for (int i = 0; i < nNodes_; i++) { for (int j = 0; j < nsd_; j++) { deltaMomentum(i,j) = nodalLambdaForce(i,j) + dt*boundaryFlux(i,j); } } } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void KinetostatFluxGhost::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // (a) for flux based : // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g // sources are set in ATC transfer rhs.reset(nNodes_,nsd_); const DENS_MAT & momentumSource(momentumSource_.quantity()); const set<int> & applicationNodes(applicationNodes_->quantity()); set<int>::const_iterator iNode; for (iNode = applicationNodes.begin(); iNode != applicationNodes.end(); iNode++) { for (int j = 0; j < nsd_; j++) { rhs(*iNode,j) = momentumSource(*iNode,j); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KinetostatFixed //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and kinetostat data //-------------------------------------------------------- - KinetostatFixed::KinetostatFixed(Kinetostat * kinetostat, + KinetostatFixed::KinetostatFixed(AtomicRegulator * kinetostat, const string & regulatorPrefix) : KinetostatGlcFs(kinetostat,regulatorPrefix), - mdMassMatrix_(atc_->set_mass_mat_md(VELOCITY)), - isFirstTimestep_(true) + filterCoefficient_(1.) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void KinetostatFixed::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // determine if map is needed and set up if so if (this->use_local_shape_functions()) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, lambdaAtomMap_, nodeToOverlapMap_); } else { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); } interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixMomentum"); linearSolverType_ = AtomicRegulator::CG_SOLVE; // base class transfers KinetostatGlcFs::construct_transfers(); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void KinetostatFixed::initialize() { KinetostatGlcFs::initialize(); // reset data to zero deltaFeMomentum_.reset(nNodes_,nsd_); deltaNodalAtomicMomentum_.reset(nNodes_,nsd_); + + // initialize filtered energy + TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + nodalAtomicMomentumFiltered_ = nodalAtomicMomentum_->quantity(); + } + + // timestep factor + dtFactor_ = 0.5; } //-------------------------------------------------------- // halve_force: // flag to halve the lambda force for improved // accuracy //-------------------------------------------------------- bool KinetostatFixed::halve_force() { if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) && (atc_->atom_to_element_map_frequency() > 1) && (atc_->step() % atc_->atom_to_element_map_frequency() == 1))) { return true; } return false; } //-------------------------------------------------------- // construct_regulated_nodes: // constructs the set of nodes being regulated //-------------------------------------------------------- void KinetostatFixed::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"RegulatedNodes"); - if (!atomicRegulator_->use_localized_lambda()) { - regulatedNodes_ = new RegulatedNodes(atc_); - } - else if (kinetostat_->coupling_mode() == Kinetostat::FLUX) { - regulatedNodes_ = new FixedNodes(atc_); - } - else if (kinetostat_->coupling_mode() == Kinetostat::FIXED) { - // include boundary nodes - regulatedNodes_ = new FixedBoundaryNodes(atc_); - } - else { - throw ATC_Error("ThermostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); - } + if (!regulatedNodes_) { + if (!atomicRegulator_->use_localized_lambda()) { + regulatedNodes_ = new RegulatedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == Kinetostat::FLUX) { + regulatedNodes_ = new FixedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == Kinetostat::FIXED) { + // include boundary nodes + regulatedNodes_ = new FixedBoundaryNodes(atc_); + } + else { + throw ATC_Error("KinetostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); + } - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"RegulatedNodes"); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"RegulatedNodes"); + } applicationNodes_ = regulatedNodes_; // special set of boundary elements for defining regulated atoms if (atomicRegulator_->use_localized_lambda()) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } //-------------------------------------------------------- // initialize_delta_nodal_atomic_momentum: // initializes storage for the variable tracking // the change in the nodal atomic momentum // that has occured over the past timestep //-------------------------------------------------------- void KinetostatFixed::initialize_delta_nodal_atomic_momentum(double dt) { // initialize delta energy const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity()); initialNodalAtomicMomentum_ = myNodalAtomicMomentum; initialNodalAtomicMomentum_ *= -1.; // initially stored as negative for efficiency timeFilter_->apply_pre_step1(nodalAtomicMomentumFiltered_.set_quantity(), myNodalAtomicMomentum,dt); } //-------------------------------------------------------- // compute_delta_nodal_atomic_momentum: // computes the change in the nodal atomic momentum // that has occured over the past timestep //-------------------------------------------------------- void KinetostatFixed::compute_delta_nodal_atomic_momentum(double dt) { // set delta energy based on predicted atomic velocities const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity()); timeFilter_->apply_post_step1(nodalAtomicMomentumFiltered_.set_quantity(), myNodalAtomicMomentum,dt); deltaNodalAtomicMomentum_ = initialNodalAtomicMomentum_; deltaNodalAtomicMomentum_ += myNodalAtomicMomentum; } + //-------------------------------------------------------- + // compute_lambda + // sets up and solves linear system for lambda + //-------------------------------------------------------- + void KinetostatFixed::compute_lambda(double dt) + { + // compute predicted changes in nodal atomic momentum + compute_delta_nodal_atomic_momentum(dt); + + // change in finite element momentum + deltaFeMomentum_ = initialFeMomentum_; + deltaFeMomentum_ += (mdMassMatrix_.quantity())*(velocity_.quantity()); + + // set up rhs for lambda equation + KinetostatGlcFs::compute_lambda(dt); + } + //-------------------------------------------------------- // apply_pre_predictor: // apply the kinetostat to the atoms in the // pre-predictor integration phase //-------------------------------------------------------- void KinetostatFixed::apply_pre_predictor(double dt) { // initialize values to be track change in finite element energy over the timestep initialize_delta_nodal_atomic_momentum(dt); initialFeMomentum_ = -1.*((mdMassMatrix_.quantity())*(velocity_.quantity())); // initially stored as negative for efficiency KinetostatGlcFs::apply_pre_predictor(dt); } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the kinetostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFixed::apply_pre_corrector(double dt) + { + // do full prediction if we just redid the shape functions + if (full_prediction()) { + _tempNodalAtomicMomentumFiltered_ = nodalAtomicMomentumFiltered_.quantity(); + } + + KinetostatGlcFs::apply_pre_corrector(dt); + + if (full_prediction()) { + // reset temporary variables + nodalAtomicMomentumFiltered_ = _tempNodalAtomicMomentumFiltered_; + } + } //-------------------------------------------------------- // apply_post_corrector: // apply the kinetostat to the atoms in the // post-corrector integration phase //-------------------------------------------------------- void KinetostatFixed::apply_post_corrector(double dt) { + + bool halveForce = halve_force(); + KinetostatGlcFs::apply_post_corrector(dt); // update filtered momentum with lambda force DENS_MAT & myNodalAtomicLambdaForce(nodalAtomicLambdaForce_->set_quantity()); timeFilter_->apply_post_step2(nodalAtomicMomentumFiltered_.set_quantity(), myNodalAtomicLambdaForce,dt); - if (halve_force()) { + if (halveForce) { // Halve lambda force due to fixed temperature constraints // 1) makes up for poor initial condition // 2) accounts for possibly large value of lambda when atomic shape function values change // from eulerian mapping after more than 1 timestep // avoids unstable oscillations arising from // thermostat having to correct for error introduced in lambda changing the // shape function matrices *lambda_ *= 0.5; } - - isFirstTimestep_ = false; } //-------------------------------------------------------- - // compute_kinetostat - // manages the solution and application of the - // kinetostat equations and variables + // add_to_momentum: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the kinetostat //-------------------------------------------------------- - void KinetostatFixed::compute_lambda(double dt) + void KinetostatFixed::add_to_momentum(const DENS_MAT & nodalLambdaForce, + DENS_MAT & deltaMomentum, + double dt) { - // compute predicted changes in nodal atomic momentum - compute_delta_nodal_atomic_momentum(dt); - - // change in finite element momentum - deltaFeMomentum_ = initialFeMomentum_; - deltaFeMomentum_ += (mdMassMatrix_.quantity())*(velocity_.quantity()); - - // set up rhs for lambda equation - KinetostatGlcFs::compute_lambda(dt); + deltaMomentum.resize(nNodes_,nsd_); + const set<int> & regulatedNodes(regulatedNodes_->quantity()); + for (int i = 0; i < nNodes_; i++) { + if (regulatedNodes.find(i) != regulatedNodes.end()) { + for (int j = 0; j < nsd_; j++) { + deltaMomentum(i,j) = 0.; + } + } + else { + for (int j = 0; j < nsd_; j++) { + deltaMomentum(i,j) = nodalLambdaForce(i,j); + } + } + } } //-------------------------------------------------------- // set_kinetostat_rhs // sets up the RHS of the kinetostat equations // for the coupling parameter lambda //-------------------------------------------------------- void KinetostatFixed::set_kinetostat_rhs(DENS_MAT & rhs, double dt) { // for essential bcs (fixed nodes) : // form rhs : (delUpsV - delUps)/dt const set<int> & regulatedNodes(regulatedNodes_->quantity()); double factor = (1./dt); for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { for (int j = 0; j < nsd_; j++) { rhs(i,j) = factor*(deltaNodalAtomicMomentum_(i,j) - deltaFeMomentum_(i,j)); } } else { for (int j = 0; j < nsd_; j++) { rhs(i,j) = 0.; } } } } //-------------------------------------------------------- - // add_to_momentum: - // determines what if any contributions to the - // finite element equations are needed for - // consistency with the kinetostat //-------------------------------------------------------- - void KinetostatFixed::add_to_momentum(const DENS_MAT & nodalLambdaForce, - DENS_MAT & deltaMomentum, - double dt) + // Class KinetostatFluxFixed + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + KinetostatFluxFixed::KinetostatFluxFixed(AtomicRegulator * kinetostat, + bool constructKinetostats) : + RegulatorMethod(kinetostat), + kinetostatFlux_(NULL), + kinetostatFixed_(NULL), + kinetostatBcs_(NULL) { - deltaMomentum.resize(nNodes_,nsd_); - const set<int> & regulatedNodes(regulatedNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (regulatedNodes.find(i) != regulatedNodes.end()) { - for (int j = 0; j < nsd_; j++) { - deltaMomentum(i,j) = 0.; - } + if (constructKinetostats) { + if (kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) { + kinetostatFlux_ = new KinetostatFluxGhost(kinetostat,regulatorPrefix_+"Flux"); } else { - for (int j = 0; j < nsd_; j++) { - deltaMomentum(i,j) = nodalLambdaForce(i,j); - } + kinetostatFlux_ = new KinetostatFlux(kinetostat,regulatorPrefix_+"Flux"); + } + kinetostatFixed_ = new KinetostatFixed(kinetostat,regulatorPrefix_+"Fixed"); + + // need to choose BC type based on coupling mode + if (kinetostat->coupling_mode() == AtomicRegulator::FLUX || kinetostat->coupling_mode(VELOCITY) == AtomicRegulator::GHOST_FLUX) { + kinetostatBcs_ = kinetostatFlux_; + } + else if (kinetostat->coupling_mode() == AtomicRegulator::FIXED) { + kinetostatBcs_ = kinetostatFixed_; + } + else { + throw ATC_Error("KinetostatFluxFixed::constructor - invalid kinetostat type provided"); } } } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + KinetostatFluxFixed::~KinetostatFluxFixed() + { + if (kinetostatFlux_) delete kinetostatFlux_; + if (kinetostatFixed_) delete kinetostatFixed_; + } + + //-------------------------------------------------------- + // constructor_transfers + // instantiates or obtains all dependency managed data + //-------------------------------------------------------- + void KinetostatFluxFixed::construct_transfers() + { + kinetostatFlux_->construct_transfers(); + kinetostatFixed_->construct_transfers(); + } + + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void KinetostatFluxFixed::initialize() + { + kinetostatFlux_->initialize(); + kinetostatFixed_->initialize(); + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_pre_predictor(double dt) + { + kinetostatFlux_->apply_pre_predictor(dt); + kinetostatFixed_->apply_pre_predictor(dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_pre_corrector(double dt) + { + kinetostatFlux_->apply_pre_corrector(dt); + if (kinetostatFixed_->full_prediction()) { + atc_->set_fixed_nodes(); + } + kinetostatFixed_->apply_pre_corrector(dt); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void KinetostatFluxFixed::apply_post_corrector(double dt) + { + kinetostatFlux_->apply_post_corrector(dt); + atc_->set_fixed_nodes(); + kinetostatFixed_->apply_post_corrector(dt); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void KinetostatFluxFixed::output(OUTPUT_LIST & outputData) + { + kinetostatFlux_->output(outputData); + kinetostatFixed_->output(outputData); + } + }; diff --git a/lib/atc/Kinetostat.h b/lib/atc/Kinetostat.h index 184003429..3d9b8cd5d 100644 --- a/lib/atc/Kinetostat.h +++ b/lib/atc/Kinetostat.h @@ -1,809 +1,882 @@ #ifndef KINETOSTAT_H #define KINETOSTAT_H #include "AtomicRegulator.h" #include "PerAtomQuantityLibrary.h" #include <map> #include <set> #include <utility> #include <string> namespace ATC { // forward declarations class FundamentalAtomQuantity; class AtfShapeFunctionRestriction; template <typename T> class ProtectedAtomQuantity; /** * @class Kinetostat * @brief Manager class for atom-continuum control of momentum and position */ class Kinetostat : public AtomicRegulator { public: // constructor Kinetostat(ATC_Coupling *atc, const std::string & regulatorPrefix = ""); // destructor virtual ~Kinetostat(){}; /** parser/modifier */ virtual bool modify(int narg, char **arg); /** instantiate up the desired method(s) */ virtual void construct_methods(); // data access, intended for method objects /** reset the nodal force to a prescribed value */ virtual void reset_lambda_contribution(const DENS_MAT & target); private: // DO NOT define this Kinetostat(); }; /** * @class KinetostatShapeFunction * @brief Base class for implementation of kinetostat algorithms based on FE shape functions */ class KinetostatShapeFunction : public RegulatorShapeFunction { public: - KinetostatShapeFunction(Kinetostat *kinetostat, + KinetostatShapeFunction(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatShapeFunction(){}; /** instantiate all needed data */ virtual void construct_transfers(); protected: // methods /** set weighting factor for in matrix Nhat^T * weights * Nhat */ virtual void set_weights(); // member data - /** pointer to thermostat object for data */ - Kinetostat * kinetostat_; + /** MD mass matrix */ + DIAG_MAN & mdMassMatrix_; /** pointer to a time filtering object */ TimeFilter * timeFilter_; /** stress induced by lambda */ DENS_MAN * nodalAtomicLambdaForce_; /** filtered lambda force */ DENS_MAN * lambdaForceFiltered_; /** atomic force induced by lambda */ ProtectedAtomQuantity<double> * atomKinetostatForce_; /** lambda prolonged to the atoms */ ProtectedAtomQuantity<double> * atomLambda_; /** pointer to atom velocities */ FundamentalAtomQuantity * atomVelocities_; /** pointer to atom velocities */ FundamentalAtomQuantity * atomMasses_; // workspace DENS_MAT _nodalAtomicLambdaForceOut_; // matrix for output only private: // DO NOT define this KinetostatShapeFunction(); }; /** * @class GlcKinetostat * @brief Base class for implementation of kinetostat algorithms based on Gaussian least constraints (GLC) */ class GlcKinetostat : public KinetostatShapeFunction { public: - GlcKinetostat(Kinetostat *kinetostat); + GlcKinetostat(AtomicRegulator *kinetostat); virtual ~GlcKinetostat(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); protected: // methods /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * quantity, const DENS_MAT & lambdaAtom, double dt=0.); /** apply any required corrections for localized kinetostats */ virtual void apply_localization_correction(const DENS_MAT & source, DENS_MAT & nodalField, double weight = 1.){}; // member data - - /** MD mass matrix */ - DIAG_MAN & mdMassMatrix_; - /** nodeset corresponding to Hoover coupling */ std::set<std::pair<int,int> > hooverNodes_; /** pointer to atom positions */ FundamentalAtomQuantity * atomPositions_; private: // DO NOT define this GlcKinetostat(); }; /** * @class DisplacementGlc * @brief Enforces GLC on atomic position based on FE displacement */ class DisplacementGlc : public GlcKinetostat { public: - DisplacementGlc(Kinetostat * kinetostat); + DisplacementGlc(AtomicRegulator * kinetostat); virtual ~DisplacementGlc(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies kinetostat to atoms */ virtual void apply_post_predictor(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return (!atomicRegulator_->use_lumped_lambda_solve()) && atomicRegulator_->use_localized_lambda();}; protected: // methods /** set weighting factor for in matrix Nhat^T * weights * Nhat */ virtual void set_weights(); /** does initial filtering operations before main computation */ virtual void apply_pre_filtering(double dt); /** sets up and solves kinetostat equations */ virtual void compute_kinetostat(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ virtual void compute_nodal_lambda_force(double dt); /** apply any required corrections for localized kinetostats */ virtual void apply_localization_correction(const DENS_MAT & source, DENS_MAT & nodalField, double weight = 1.); // data /** restricted atomic displacements at the nodes */ DENS_MAN * nodalAtomicMassWeightedDisplacement_; /** clone of FE displacement field */ DENS_MAN & nodalDisplacements_; private: // DO NOT define this DisplacementGlc(); }; /** * @class DisplacementGlcFiltered * @brief Enforces GLC on time filtered atomic position based on FE displacement */ //-------------------------------------------------------- //-------------------------------------------------------- // Class DisplacementGlcFiltered //-------------------------------------------------------- //-------------------------------------------------------- class DisplacementGlcFiltered : public DisplacementGlc { public: - DisplacementGlcFiltered(Kinetostat * kinetostat); + DisplacementGlcFiltered(AtomicRegulator * kinetostat); virtual ~DisplacementGlcFiltered(){}; /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: // methods /** does initial filtering operations before main computation */ virtual void apply_pre_filtering(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ virtual void compute_nodal_lambda_force(double dt); // data /** clone of FE nodal atomic displacement field */ DENS_MAN & nodalAtomicDisplacements_; private: // DO NOT define this DisplacementGlcFiltered(); }; /** * @class VelocityGlc * @brief Enforces GLC on atomic velocity based on FE velocity */ //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocityGlc //-------------------------------------------------------- //-------------------------------------------------------- class VelocityGlc : public GlcKinetostat { public: - VelocityGlc(Kinetostat * kinetostat); + VelocityGlc(AtomicRegulator * kinetostat); virtual ~VelocityGlc(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies kinetostat to atoms */ virtual void apply_mid_predictor(double dt); /** applies kinetostat to atoms */ virtual void apply_post_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return (!atomicRegulator_->use_lumped_lambda_solve()) && atomicRegulator_->use_localized_lambda();}; protected: // methods /** set weighting factor for in matrix Nhat^T * weights * Nhat */ virtual void set_weights(); /** does initial filtering operations before main computation */ virtual void apply_pre_filtering(double dt); /** sets up and solves kinetostat equations */ virtual void compute_kinetostat(double dt); + /** applies kinetostat correction to atoms */ + virtual void apply_kinetostat(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ virtual void compute_nodal_lambda_force(double dt); /** apply any required corrections for localized kinetostats */ virtual void apply_localization_correction(const DENS_MAT & source, DENS_MAT & nodalField, double weight = 1.); // data /** restricted atomic displacements at the nodes */ DENS_MAN * nodalAtomicMomentum_; /** clone of FE velocity field */ DENS_MAN & nodalVelocities_; private: // DO NOT define this VelocityGlc(); }; /** * @class VelocityGlcFiltered * @brief Enforces GLC on time filtered atomic velocity based on FE velocity */ //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocityGlcFiltered //-------------------------------------------------------- //-------------------------------------------------------- class VelocityGlcFiltered : public VelocityGlc { public: - VelocityGlcFiltered(Kinetostat * kinetostat); + VelocityGlcFiltered(AtomicRegulator * kinetostat); virtual ~VelocityGlcFiltered(){}; /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: // methods /** does initial filtering operations before main computation */ virtual void apply_pre_filtering(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ virtual void compute_nodal_lambda_force(double dt); // data /** clone of FE nodal atomic velocity field */ DENS_MAN & nodalAtomicVelocities_; private: // DO NOT define this VelocityGlcFiltered(); }; /** * @class StressFlux * @brief Enforces GLC on atomic forces based on FE stresses or accelerations */ //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFlux //-------------------------------------------------------- //-------------------------------------------------------- class StressFlux : public GlcKinetostat { public: - StressFlux(Kinetostat * kinetostat); + StressFlux(AtomicRegulator * kinetostat); virtual ~StressFlux(); /** instantiate all needed data */ virtual void construct_transfers(); /** applies kinetostat to atoms in the pre-predictor phase */ virtual void apply_pre_predictor(double dt); /** applies kinetostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** sets filtered ghost force to prescribed value */ void reset_filtered_ghost_force(DENS_MAT & targetForce); /** returns reference to filtered ghost force */ DENS_MAN & filtered_ghost_force() {return nodalGhostForceFiltered_;}; /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return ((!atomicRegulator_->use_lumped_lambda_solve()) && atomicRegulator_->use_localized_lambda());}; protected: // data /** nodal force */ DENS_MAN & nodalForce_; /** nodal force due to atoms */ DENS_MAN * nodalAtomicForce_; /** nodal ghost force */ AtfShapeFunctionRestriction * nodalGhostForce_; /** filtered ghost force */ DENS_MAN nodalGhostForceFiltered_; /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN & momentumSource_; // methods /** does initial filtering operations before main computation */ virtual void apply_pre_filtering(double dt); /** sets up and solves kinetostat equations */ virtual void compute_kinetostat(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** computes the nodal FE force applied by the kinetostat */ virtual void compute_nodal_lambda_force(double dt); /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * atomVelocities, const DENS_MAT & lambdaForce, double dt); /** adds in finite element rhs contributions */ virtual void add_to_rhs(FIELDS & rhs); // workspace DENS_MAT _deltaVelocity_; // change in velocity during time integration private: // DO NOT define this StressFlux(); }; /** * @class StressFluxGhost * @brief Enforces GLC on atomic forces based on FE stresses or accelerations, using * the ghost forces to prescribe the FE boundary stress */ //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFluxGhost //-------------------------------------------------------- //-------------------------------------------------------- class StressFluxGhost : public StressFlux { public: - StressFluxGhost(Kinetostat * kinetostat); + StressFluxGhost(AtomicRegulator * kinetostat); virtual ~StressFluxGhost() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** compute boundary flux, requires kinetostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields); protected: // methods /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** adds in finite element rhs contributions */ virtual void add_to_rhs(FIELDS & rhs); private: // DO NOT define this StressFluxGhost(); }; /** * @class StressFluxFiltered * @brief Enforces GLC on time filtered atomic forces based on FE stresses or accelerations */ //-------------------------------------------------------- //-------------------------------------------------------- // Class StressFluxFiltered //-------------------------------------------------------- //-------------------------------------------------------- class StressFluxFiltered : public StressFlux { public: - StressFluxFiltered(Kinetostat * kinetostat); + StressFluxFiltered(AtomicRegulator * kinetostat); virtual ~StressFluxFiltered(){}; /** adds in finite element rhs contributions */ virtual void add_to_rhs(FIELDS & rhs); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: // data DENS_MAN & nodalAtomicVelocity_; // methods /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * quantity, const DENS_MAT & lambdaAtom, double dt); private: // DO NOT define this StressFluxFiltered(); }; /** * @class KinetostatGlcFs * @brief Base class for implementation of kinetostat algorithms based on Gaussian least constraints (GLC) * when fractional step time integration is used */ class KinetostatGlcFs : public KinetostatShapeFunction { public: - KinetostatGlcFs(Kinetostat *kinetostat, + KinetostatGlcFs(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatGlcFs(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); + /* flag for performing the full lambda prediction calculation */ + bool full_prediction(); + protected: // methods /** determine mapping from all nodes to those to which the kinetostat applies */ void compute_rhs_map(); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt) = 0; /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, const DENS_MAN * nodalAtomicEnergy, const DENS_MAT & lambdaForce, DENS_MAT & nodalAtomicLambdaPower, double dt); /** add contributions from kinetostat to FE energy */ virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, DENS_MAT & deltaMomemtum, double dt) = 0; /* sets up and solves the linear system for lambda */ virtual void compute_lambda(double dt); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes() = 0; - // member data /** reference to AtC FE velocity */ DENS_MAN & velocity_; /** nodal atomic momentum */ DENS_MAN * nodalAtomicMomentum_; + /** hack to determine if first timestep has been passed */ + bool isFirstTimestep_; + + /** local version of velocity used as predicted final veloctiy */ + PerAtomQuantity<double> * atomPredictedVelocities_; + + /** predicted nodal atomic momentum */ + AtfShapeFunctionRestriction * nodalAtomicPredictedMomentum_; + + /** FE momentum change from kinetostat forces */ + DENS_MAT deltaMomentum_; + /** right-hand side data for thermostat equation */ DENS_MAT rhs_; - /** mapping from all to regulated nodes */ - DENS_MAT rhsMap_; + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; // workspace DENS_MAT _lambdaForceOutput_; // force applied by lambda in output format DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied - DENS_MAT _deltaMomentum_; // FE velocity change from kinetostat private: // DO NOT define this KinetostatGlcFs(); }; /** * @class KinetostatFlux * @brief Implementation of kinetostat algorithms based on Gaussian least constraints (GLC) * which apply stresses when fractional step time integration is used */ class KinetostatFlux : public KinetostatGlcFs { public: - KinetostatFlux(Kinetostat *kinetostat, + KinetostatFlux(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFlux(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** enables resetting of filtered ghost force */ void reset_filtered_ghost_force(DENS_MAT & target); protected: // methods /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from kinetostat to FE energy */ virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, DENS_MAT & deltaMomemtum, double dt); /** sets up the transfer which is the set of nodes being regulated */ virtual void construct_regulated_nodes(); // member data /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN & momentumSource_; /** force from ghost atoms restricted to nodes */ DENS_MAN * nodalGhostForce_; /** filtered nodal ghost force */ DENS_MAN * nodalGhostForceFiltered_; private: // DO NOT define this KinetostatFlux(); }; /** * @class KinetostatFluxGhost * @brief Implements ghost-atom boundary flux and other loads for fractional-step based kinetostats */ class KinetostatFluxGhost : public KinetostatFlux { public: - KinetostatFluxGhost(Kinetostat *kinetostat, + KinetostatFluxGhost(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFluxGhost(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** compute boundary flux */ virtual void compute_boundary_flux(FIELDS & fields); protected: /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from kinetostat to FE energy */ virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, DENS_MAT & deltaMomemtum, double dt); private: // DO NOT define this KinetostatFluxGhost(); }; /** * @class KinetostatFixed * @brief Implementation of kinetostat algorithms based on Gaussian least constraints (GLC) * which perform Hoover coupling when fractional step time integration is used */ class KinetostatFixed : public KinetostatGlcFs { public: - KinetostatFixed(Kinetostat *kinetostat, + KinetostatFixed(AtomicRegulator *kinetostat, const std::string & regulatorPrefix = ""); virtual ~KinetostatFixed(){}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[VELOCITY] = 0.;}; + /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return atomicRegulator_->use_localized_lambda();}; protected: // methods /** initialize data for tracking the change in nodal atomic velocity */ virtual void initialize_delta_nodal_atomic_momentum(double dt); /** compute the change in nodal atomic velocity */ virtual void compute_delta_nodal_atomic_momentum(double dt); /** sets up appropriate rhs for kinetostat equations */ virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from kinetostat to FE energy */ virtual void add_to_momentum(const DENS_MAT & nodalLambdaForce, DENS_MAT & deltaMomemtum, double dt); /* sets up and solves the linear system for lambda */ virtual void compute_lambda(double dt); /** flag for halving the applied force to mitigate numerical errors */ bool halve_force(); /** sets up the transfer which is the set of nodes being regulated */ virtual void construct_regulated_nodes(); // member data - /** MD mass matrix */ - DIAG_MAN & mdMassMatrix_; - /** change in FE momentum over a timestep */ DENS_MAT deltaFeMomentum_; /** initial FE momentum used to compute change */ DENS_MAT initialFeMomentum_; /** change in restricted atomic FE momentum over a timestep */ DENS_MAT deltaNodalAtomicMomentum_; /** intial restricted atomic FE momentum used to compute change */ DENS_MAT initialNodalAtomicMomentum_; /** filtered nodal atomic momentum */ DENS_MAN nodalAtomicMomentumFiltered_; - /** hack to determine if first timestep has been passed */ - bool isFirstTimestep_; + /** coefficient to account for effect of time filtering on rhs terms */ + double filterCoefficient_; + + // workspace + DENS_MAT _tempNodalAtomicMomentumFiltered_; // stores filtered momentum change in atoms for persistence during predictor private: // DO NOT define this KinetostatFixed(); }; + /** + * @class KinetostatFluxFixed + * @brief Class for kinetostatting using the velocity matching constraint one one set of nodes and the flux matching constraint on another + */ + + class KinetostatFluxFixed : public RegulatorMethod { + + public: + + KinetostatFluxFixed(AtomicRegulator * kinetostat, + bool constructThermostats = true); + + virtual ~KinetostatFluxFixed(); + + /** instantiate all needed data */ + virtual void construct_transfers(); + + /** pre-run initialization of method data */ + virtual void initialize(); + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(OUTPUT_LIST & outputData); + + /** compute boundary flux, requires kinetostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {kinetostatBcs_->compute_boundary_flux(fields);}; + + protected: + + // data + /** kinetostat for imposing the fluxes */ + KinetostatFlux * kinetostatFlux_; + + /** kinetostat for imposing fixed nodes */ + KinetostatFixed * kinetostatFixed_; + + /** pointer to whichever kinetostat should compute the flux, based on coupling method */ + KinetostatGlcFs * kinetostatBcs_; + + private: + + // DO NOT define this + KinetostatFluxFixed(); + }; + } #endif diff --git a/lib/atc/LammpsInterface.cpp b/lib/atc/LammpsInterface.cpp index 565a3c987..78859aec6 100644 --- a/lib/atc/LammpsInterface.cpp +++ b/lib/atc/LammpsInterface.cpp @@ -1,1534 +1,1555 @@ // Header file for this class #include "LammpsInterface.h" // LAMMPS includes #include "lammps.h" #include "atom.h" // x, v, f #include "atom_vec.h" //for insertion #include "domain.h" // for basing locations on regions #include "region.h" // region bounding box and style #include "force.h" // boltzman constant #include "group.h" // atom masks #include "memory.h" // grow atom information #include "compute.h" // computes #include "compute_pe_atom.h" // computes potential energy per atom #include "compute_stress_atom.h" // computes stress per atom #include "compute_centro_atom.h" // computes centrosymmetry per atom #include "compute_cna_atom.h" // computes common-neighbor-analysis per atom #include "compute_coord_atom.h" // computes coordination number per atom #include "compute_ke_atom.h" // computes kinetic energy per atom #include "modify.h" // #include "neighbor.h" // neighbors #include "neigh_list.h" // neighbor list #include "update.h" // timestepping information #include "pair.h" // pair potentials #include "MANYBODY/pair_eam.h" // pair potentials #include "lattice.h" // lattice parameters #include "bond.h" // bond potentials #include "comm.h" // #include "fix.h" // ATC includes #include "ATC_Error.h" #include "MatrixLibrary.h" #include "Utility.h" using ATC_Utility::to_string; // Other include files #include "mpi.h" #include <cstring> #include <map> #include <typeinfo> using std::max; using std::stringstream; using std::copy; using std::map; using std::pair; using std::string; using std::set; using LAMMPS_NS::bigint; namespace ATC { const static double PI = 3.141592653589793238; const static int seed_ = 3141592; const static int MAX_GROUP_BIT = 2147483647; //4294967295; // pow(2,31)-1; double norm(double * v) {return sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); } LammpsInterface * LammpsInterface::myInstance_ = NULL; // ----------------------------------------------------------------- // instance() // ----------------------------------------------------------------- LammpsInterface * LammpsInterface::instance() { if (myInstance_ == NULL) { myInstance_ = new LammpsInterface(); } return myInstance_; } // ----------------------------------------------------------------- // Destroy() // ----------------------------------------------------------------- void LammpsInterface::Destroy() { if (myInstance_) delete myInstance_; myInstance_ = NULL; } // ----------------------------------------------------------------- // constructor // ----------------------------------------------------------------- LammpsInterface::LammpsInterface() : lammps_(NULL), fixPointer_(NULL), commRank_(0), atomPE_(NULL), refBoxIsSet_(false), random_(NULL), globalrandom_(NULL) { } // ----------------------------------------------------------------- // general interface methods // ----------------------------------------------------------------- MPI_Comm LammpsInterface::world() const { return lammps_->world; } void LammpsInterface::set_fix_pointer(LAMMPS_NS::Fix * thisFix) { fixPointer_ = thisFix; } void LammpsInterface::forward_comm_fix() const { lammps_->comm->forward_comm_fix(fixPointer_); } void LammpsInterface::comm_borders() const { lammps_->comm->borders(); } #ifndef ISOLATE_FE void LammpsInterface::sparse_allsum(SparseMatrix<double> &toShare) const { toShare.compress(); // initialize MPI information int nProcs; int myRank; MPI_Comm_size(MPI_COMM_WORLD, &nProcs); MPI_Comm_rank(MPI_COMM_WORLD, &myRank); int error; // get numbers of rows, columns, rowsCRS, and // sizes (number of nonzero elements in matrix) SparseMatInfo *recInfo = new SparseMatInfo[nProcs]; SparseMatInfo myInfo; myInfo.rows = toShare.nRows(); myInfo.cols = toShare.nCols(); myInfo.rowsCRS = toShare.nRowsCRS(); myInfo.size = toShare.size(); error = MPI_Allgather(&myInfo, 4, MPI_INT, recInfo, 4, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_numrows "+to_string(error)); // adjust row sendcounts because recRowsCRS is off by one int rowCounts[nProcs]; int sizeCounts[nProcs]; // set up total size of receive buffers for Allgatherv calls int totalRowsCRS = 0; int totalSize = 0; // set up array of displacements for Allgatherv calls int rowOffsets[nProcs]; rowOffsets[0] = 0; int sizeOffsets[nProcs]; sizeOffsets[0] = 0; for (int i = 0; i < nProcs; i++) { // find the total number of entries to share in the mpi calls below rowCounts[i] = recInfo[i].rowsCRS + 1; sizeCounts[i] = recInfo[i].size; totalRowsCRS += rowCounts[i]; totalSize += recInfo[i].size; // these already have their 0th slot filled in if (i == 0) continue; rowOffsets[i] = rowOffsets[i-1] + rowCounts[i-1]; sizeOffsets[i] = sizeOffsets[i-1] + sizeCounts[i-1]; } // get actual rows INDEX *rec_ia = new INDEX[totalRowsCRS]; if (toShare.size() == 0) { double dummy[0]; error = MPI_Allgatherv(dummy, 0, MPI_INT, rec_ia, rowCounts, rowOffsets, MPI_INT, lammps_->world); } else error = MPI_Allgatherv(toShare.rows(), rowCounts[myRank], MPI_INT, rec_ia, rowCounts, rowOffsets, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_rowarray "+to_string(error)); // get actual cols INDEX *rec_ja = new INDEX[totalSize]; error = MPI_Allgatherv(toShare.cols(), sizeCounts[myRank], MPI_INT, rec_ja, sizeCounts, sizeOffsets, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_colarray "+to_string(error)); // get the array of values double *rec_vals = new double[totalSize]; error = MPI_Allgatherv(toShare.ptr(), sizeCounts[myRank], MPI_DOUBLE, rec_vals, sizeCounts, sizeOffsets, MPI_DOUBLE, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_valarray "+to_string(error)); INDEX *rec_ia_proc; INDEX *rec_ja_proc; double *rec_vals_proc; for (int i = 0; i < nProcs; i++) { if (myRank != i) { // deallocated when tempMat is deleted since it wraps them rec_ia_proc = new INDEX[rowCounts[i]]; rec_ja_proc = new INDEX[sizeCounts[i]]; rec_vals_proc = new double[sizeCounts[i]]; // copy the data passed with MPI into the new spots copy(rec_ia + rowOffsets[i], rec_ia + rowOffsets[i] + rowCounts[i], rec_ia_proc); copy(rec_ja + sizeOffsets[i], rec_ja + sizeOffsets[i] + sizeCounts[i], rec_ja_proc); copy(rec_vals + sizeOffsets[i], rec_vals + sizeOffsets[i] + sizeCounts[i], rec_vals_proc); // Does anyone know why we have to declare tempMat here (as well as set it equal to // something) to avoid segfaults? there are still segfaults, but they happen at a much // later stage of the game now (and for less benchmarks overall). SparseMatrix<double> tempMat = SparseMatrix<double>(rec_ia_proc, rec_ja_proc, rec_vals_proc, recInfo[i].size, recInfo[i].rows, recInfo[i].cols, recInfo[i].rowsCRS); toShare += tempMat; } } delete[] recInfo; delete[] rec_ia; delete[] rec_ja; delete[] rec_vals; } #endif +std::string LammpsInterface::read_file(std::string filename) const +{ + FILE *fp = NULL; + if (! comm_rank()) { + fp = fopen(filename.c_str(),"r"); + if (!fp) throw ATC_Error("can't open file: "+filename); + } + const int MAXLINE = 256; + const int CHUNK = 1024; + char * buffer = new char[CHUNK*MAXLINE]; + std::stringstream s; + bool eof = false; + while ( ! eof) { + eof = lammps_->comm->read_lines_from_file(fp,1,MAXLINE,buffer); + s << buffer; + } + delete [] buffer; + return s.str(); // can't copy the stream itself +} + + // ----------------------------------------------------------------- // atom interface methods // ----------------------------------------------------------------- string LammpsInterface::fix_id() const { return string(fixPointer_->id); } int LammpsInterface::nlocal() const { return lammps_->atom->nlocal; } int LammpsInterface::nghost() const { return lammps_->atom->nghost; } bool LammpsInterface::atoms_sorted() const { int sortfreq = lammps_->atom->sortfreq; if (sortfreq > 0) { return true; } else { return false; } } bigint LammpsInterface::natoms() const { return lammps_->atom->natoms; } int LammpsInterface::nmax() const { return lammps_->atom->nmax; } int LammpsInterface::ntypes() const { return lammps_->atom->ntypes; } double ** LammpsInterface::xatom() const { return lammps_->atom->x; } int LammpsInterface::type_to_charge(int atype) const { double *q = lammps_->atom->q; if (! q) return 0; int nlocal = lammps_->atom->nlocal; int *type = lammps_->atom->type; double aq = 0.0; for (int i = 0; i < nlocal; i++) { if (type[i] == atype) { aq = q[i]; break; } } double pcharge; MPI_Allreduce(&aq,&pcharge,1,MPI_DOUBLE,MPI_MAX,world()); double ncharge; MPI_Allreduce(&aq,&ncharge,1,MPI_DOUBLE,MPI_MIN,world()); double charge = (pcharge == 0.0) ? ncharge : pcharge; return charge; } //const double ** LammpsInterface::xatom() const { return (const double**)(lammps_->atom->x); } double ** LammpsInterface::vatom() const { return lammps_->atom->v; } double ** LammpsInterface::fatom() const { return lammps_->atom->f; } const int * LammpsInterface::atom_mask() const { return (const int*)lammps_->atom->mask; } int * LammpsInterface::atom_mask() { return lammps_->atom->mask; } int * LammpsInterface::atom_type() const { return lammps_->atom->type; } int * LammpsInterface::atom_tag() const { return lammps_->atom->tag; } int * LammpsInterface::atom_to_molecule() const { return lammps_->atom->molecule; } int * LammpsInterface::num_bond() const { return lammps_->atom->num_bond; } int ** LammpsInterface::bond_atom() const { return lammps_->atom->bond_atom; } int * LammpsInterface::image() const { return lammps_->atom->image; } int LammpsInterface::bond_per_atom() const { return lammps_->atom->bond_per_atom; } int LammpsInterface::newton_bond() const { return lammps_->force->newton_bond; } int LammpsInterface::local_to_global_map(int global) const { return lammps_->atom->map(global); } double * LammpsInterface::atom_mass() const { return lammps_->atom->mass; } double LammpsInterface::atom_mass(int iType) const { return lammps_->atom->mass[iType]; } double * LammpsInterface::atom_rmass() const { return lammps_->atom->rmass; } double * LammpsInterface::atom_charge() const { return lammps_->atom->q; } double * LammpsInterface::atom_scalar(FundamentalAtomQuantity quantityType) const { if (quantityType==ATOM_MASS) { if (atom_mass()) throw ATC_Error("Atom mass array requested but not defined"); return atom_rmass(); } else if (quantityType==ATOM_CHARGE) { double * atomCharge = atom_charge(); if (!atomCharge) throw ATC_Error("Atom charge array requested but not defined"); return atomCharge; } else throw ATC_Error("BAD type requested in atom_scalar"); return NULL; } double ** LammpsInterface::atom_vector(FundamentalAtomQuantity quantityType) const { if (quantityType==ATOM_POSITION) return xatom(); else if (quantityType==ATOM_VELOCITY) return vatom(); else if (quantityType==ATOM_FORCE) return fatom(); else throw ATC_Error("BAD type requested in atom_vector"); return NULL; } int LammpsInterface::atom_quantity_ndof(FundamentalAtomQuantity quantityType) const { if (quantityType==ATOM_MASS || quantityType==ATOM_CHARGE) return 1; else if (quantityType==ATOM_POSITION || quantityType==ATOM_VELOCITY || quantityType==ATOM_FORCE) return dimension(); else throw ATC_Error("BAD type requested in atom_quantity_ndof"); } double LammpsInterface::atom_quantity_conversion(FundamentalAtomQuantity quantityType) const { if (quantityType==ATOM_MASS || quantityType==ATOM_CHARGE || quantityType==ATOM_POSITION || quantityType==ATOM_VELOCITY) return 1; else if ( quantityType==ATOM_FORCE) return ftm2v(); else throw ATC_Error("BAD type requested in atom_quantity_conversion"); } // ----------------------------------------------------------------- // domain interface methods // ----------------------------------------------------------------- int LammpsInterface::dimension() const { return lammps_->domain->dimension; } int LammpsInterface::nregion() const { return lammps_->domain->nregion; } void LammpsInterface::box_bounds(double & boxxlo, double & boxxhi, double & boxylo, double & boxyhi, double & boxzlo, double &boxzhi) const { if (lammps_->domain->triclinic == 0) { boxxlo = lammps_->domain->boxlo[0]; boxxhi = lammps_->domain->boxhi[0]; boxylo = lammps_->domain->boxlo[1]; boxyhi = lammps_->domain->boxhi[1]; boxzlo = lammps_->domain->boxlo[2]; boxzhi = lammps_->domain->boxhi[2]; } else { boxxlo = lammps_->domain->boxlo_bound[0]; boxxhi = lammps_->domain->boxhi_bound[0]; boxylo = lammps_->domain->boxlo_bound[1]; boxyhi = lammps_->domain->boxhi_bound[1]; boxzlo = lammps_->domain->boxlo_bound[2]; boxzhi = lammps_->domain->boxhi_bound[2]; } } bool LammpsInterface::in_box(double * x) const { double xlo,xhi,ylo,yhi,zlo,zhi; box_bounds(xlo,xhi,ylo,yhi,zlo,zhi); if (x[0] >= xlo && x[0] < xhi && x[1] >= ylo && x[1] < yhi && x[2] >= zlo && x[2] < zhi) return true; return false; } bool LammpsInterface::in_my_processor_box(double * x) const { if (x[0] >= lammps_->domain->sublo[0] && x[0] < lammps_->domain->subhi[0] && x[1] >= lammps_->domain->sublo[1] && x[1] < lammps_->domain->subhi[1] && x[2] >= lammps_->domain->sublo[2] && x[2] < lammps_->domain->subhi[2]) return true; if (! in_box(x)) throw ATC_Error("point is in no processors box"); return false; } void LammpsInterface::sub_bounds(double & subxlo, double & subxhi, double & subylo, double & subyhi, double & subzlo, double & subzhi) const { if (lammps_->domain->triclinic == 0) { subxlo = lammps_->domain->sublo[0]; subxhi = lammps_->domain->subhi[0]; subylo = lammps_->domain->sublo[1]; subyhi = lammps_->domain->subhi[1]; subzlo = lammps_->domain->sublo[2]; subzhi = lammps_->domain->subhi[2]; } else { ATC_Error("Subboxes not accurate when triclinic != 0."); } } int LammpsInterface::xperiodic() const { return lammps_->domain->xperiodic; } int LammpsInterface::yperiodic() const { return lammps_->domain->yperiodic; } int LammpsInterface::zperiodic() const { return lammps_->domain->zperiodic; } int LammpsInterface::nperiodic() const { int nprd = 0; if ( lammps_->domain->xperiodic > 0 ) { nprd++ ; } if ( lammps_->domain->yperiodic > 0 ) { nprd++ ; } if ( lammps_->domain->zperiodic > 0 ) { nprd++ ; } return nprd; } // correct posistions for periodic box void LammpsInterface::periodicity_correction(double * x) const { int* periodicity = lammps_->domain->periodicity; if (!refBoxIsSet_) set_reference_box(); for (int m = 0; m < 3; m++) { if ((bool) periodicity[m]) { if (x[m] < lower_[m] || x[m] > upper_[m]) { x[m] -= length_[m]*floor((x[m]-lower_[m])/length_[m]); } if (x[m] < lower_[m] || x[m] > upper_[m]) { throw ATC_Error("periodicity_correction: still out of box bounds"); } } } } void LammpsInterface::set_reference_box(void) const { double * hi = lammps_->domain->boxhi; double * lo = lammps_->domain->boxlo; double * len = lammps_->domain->prd; for (int i = 0; i < 3; i++) { upper_[i] = hi[i]; lower_[i] = lo[i]; length_[i] = len[i]; } refBoxIsSet_ = true; } double LammpsInterface::domain_xprd() const { return lammps_->domain->xprd; } double LammpsInterface::domain_yprd() const { return lammps_->domain->yprd; } double LammpsInterface::domain_zprd() const { return lammps_->domain->zprd; } double LammpsInterface::domain_volume() const { return (lammps_->domain->xprd)* (lammps_->domain->yprd)* (lammps_->domain->zprd); } double LammpsInterface::domain_xy() const { return lammps_->domain->xy; } double LammpsInterface::domain_xz() const { return lammps_->domain->xz; } double LammpsInterface::domain_yz() const { return lammps_->domain->yz; } int LammpsInterface::domain_triclinic() const { return lammps_->domain->triclinic; } void LammpsInterface::box_periodicity(int & xperiodic, int & yperiodic, int & zperiodic) const { xperiodic = lammps_->domain->xperiodic; yperiodic = lammps_->domain->yperiodic; zperiodic = lammps_->domain->zperiodic; } int LammpsInterface::region_id(const char * regionName) const { int nregion = this->nregion(); for (int iregion = 0; iregion < nregion; iregion++) { if (strcmp(regionName, region_name(iregion)) == 0) { return iregion; } } throw ATC_Error("Region has not been defined"); return -1; } bool LammpsInterface::region_bounds(const char * regionName, double & xmin, double & xmax, double & ymin, double & ymax, double & zmin, double & zmax, double & xscale, double & yscale, double & zscale) const { int iRegion = region_id(regionName); xscale = region_xscale(iRegion); yscale = region_yscale(iRegion); zscale = region_zscale(iRegion); xmin = region_xlo(iRegion); xmax = region_xhi(iRegion); ymin = region_ylo(iRegion); ymax = region_yhi(iRegion); zmin = region_zlo(iRegion); zmax = region_zhi(iRegion); if (strcmp(region_style(iRegion),"block")==0) { return true; } else { return false; } } void LammpsInterface::minimum_image(double & dx, double & dy, double & dz) const { lammps_->domain->minimum_image(dx,dy,dz); } void LammpsInterface::closest_image(const double * const xi, const double * const xj, double * const xjImage) const { lammps_->domain->closest_image(xi,xj,xjImage); } // ----------------------------------------------------------------- // update interface methods // ----------------------------------------------------------------- LammpsInterface::UnitsType LammpsInterface::units_style(void) const { if (strcmp(lammps_->update->unit_style,"lj") == 0) return LJ; else if (strcmp(lammps_->update->unit_style,"real") == 0) return REAL; else if (strcmp(lammps_->update->unit_style,"metal") == 0) return METAL; else return UNKNOWN; } double LammpsInterface::convert_units(double value, UnitsType in, UnitsType out, int massExp, int lenExp, int timeExp, int engExp) const { double ps2fs = 1.e3; double eV2kcal = 23.069; if (in==REAL) { if (out==METAL) { return value*pow(ps2fs,-timeExp)*pow(eV2kcal,-engExp); } else if (out==ATC) { if (units_style()==REAL) { return value; } else if (units_style()==METAL) { return convert_units(value, METAL, out, massExp, lenExp, timeExp)*1.0; } } else throw ATC_Error("can't convert"); } else if (in==METAL) { if (out==REAL) { return value*pow(ps2fs,timeExp)*pow(eV2kcal,engExp); } else if (out==ATC) { if (units_style()==REAL) { return convert_units(value, REAL, out, massExp, lenExp, timeExp)*1.0; } else if (units_style()==METAL) { return value; } } else throw ATC_Error("can't convert"); } else throw ATC_Error("can't convert"); return value; } // ----------------------------------------------------------------- // lattice interface methods // ----------------------------------------------------------------- double LammpsInterface::xlattice() const { return lammps_->domain->lattice->xlattice; } double LammpsInterface::ylattice() const { return lammps_->domain->lattice->ylattice; } double LammpsInterface::zlattice() const { return lammps_->domain->lattice->zlattice; } LammpsInterface::LatticeType LammpsInterface::lattice_style() const { if (lammps_->domain->lattice) return (LammpsInterface::LatticeType)lammps_->domain->lattice->style; else throw ATC_Error("Lattice has not been defined"); } //* retuns the number of basis vectors int LammpsInterface::n_basis() const { return lammps_->domain->lattice->nbasis; } //* returns the basis vectors, transformed to the box coords void LammpsInterface::basis_vectors(double **basis) const { LAMMPS_NS::Lattice *lattice = lammps_->domain->lattice; int i,j; double origin[3] = {0.0, 0.0, 0.0}; lattice->lattice2box(origin[0], origin[1], origin[2]); for (i=0; i<n_basis(); i++) { memcpy(basis[i],lattice->basis[i],3*sizeof(double)); lattice->lattice2box(basis[i][0], basis[i][1], basis[i][2]); for (j=0; j<3; j++) basis[i][j] -= origin[j]; } } //* gets the (max) lattice constant double LammpsInterface::max_lattice_constant(void) const { double a1[3], a2[3], a3[3]; unit_cell(a1,a2,a3); double a = norm(a1); a = max(a,norm(a2)); a = max(a,norm(a3)); return a; } //* computes a cutoff distance halfway between 1st and 2nd nearest neighbors double LammpsInterface::near_neighbor_cutoff(void) const { double cutoff; double alat = LammpsInterface::max_lattice_constant(); LatticeType type = lattice_style(); if (type == LammpsInterface::SC) { cutoff = 0.5*(1.0+sqrt(2.0))*alat; } else if (type == LammpsInterface::BCC) { cutoff = 0.5*(0.5*sqrt(3.0)+1.0)*alat; } else if (type == LammpsInterface::FCC) { cutoff = 0.5*(1.0/sqrt(2.0)+1.0)*alat; } else if (type == LammpsInterface::HCP) { cutoff = 0.5*(1.0/sqrt(2.0)+1.0)*alat; } else if (type == LammpsInterface::DIAMOND) { cutoff = 0.5*(0.25*sqrt(3.0)+1.0/sqrt(2.0))*alat; } else if (type == LammpsInterface::SQ) { cutoff = 0.5*(1.0+sqrt(2.0))*alat; } else if (type == LammpsInterface::SQ2) { cutoff = 0.5*(1.0/sqrt(2.0)+1.0)*alat; } else if (type == LammpsInterface::HEX) { cutoff = 0.5*(1.0/sqrt(3.0)+1.0)*alat; } else { throw ATC_Error("Unknown lattice type"); } return cutoff; } //* gets the unit cell vectors void LammpsInterface::unit_cell(double *a1, double *a2, double *a3) const { int i, j; double *a[3] = {a1,a2,a3}; double origin[3] = {0.0,0.0,0.0}; LAMMPS_NS::Lattice *lattice = lammps_->domain->lattice; // transform origin lattice->lattice2box(origin[0], origin[1], origin[2]); // copy reference lattice vectors memcpy(a[0], lattice->a1, 3*sizeof(double)); memcpy(a[1], lattice->a2, 3*sizeof(double)); memcpy(a[2], lattice->a3, 3*sizeof(double)); for (i=0; i<3; i++) { lattice->lattice2box(a[i][0], a[i][1], a[i][2]); for (j=0; j<3; j++) a[i][j] -= origin[j]; } } //* gets number of atoms in a unit cell int LammpsInterface::num_atoms_per_cell(void) const { int naCell = 0; LatticeType type = lattice_style(); if (type == LammpsInterface::SC) naCell = 1; else if (type == LammpsInterface::BCC) naCell = 2; else if (type == LammpsInterface::FCC) naCell = 4; else if (type == LammpsInterface::DIAMOND) naCell = 8; else if (comm_rank()==0) { //{throw ATC_Error("lattice style not currently supported by ATC");} print_msg_once("WARNING: Cannot get number of atoms per cell from lattice"); naCell = 1; } return naCell; } //* gets tributary volume for an atom double LammpsInterface::volume_per_atom(void) const { double naCell = num_atoms_per_cell(); double volPerAtom = xlattice() * ylattice() * zlattice() / naCell; return volPerAtom; } //* gets lattice basis void LammpsInterface::lattice(MATRIX &N, MATRIX &B) const { int nbasis = n_basis(); double **basis = new double*[nbasis]; N.reset(3,3); B.reset(3,nbasis); for (int i=0; i<nbasis; i++) basis[i] = column(B,i).ptr(); basis_vectors(basis); unit_cell(column(N,0).ptr(), column(N,1).ptr(), column(N,2).ptr()); delete [] basis; } // ----------------------------------------------------------------- // force interface methods // ----------------------------------------------------------------- double LammpsInterface::boltz() const{ return lammps_->force->boltz; } double LammpsInterface::mvv2e() const{ return lammps_->force->mvv2e; } double LammpsInterface::ftm2v()const { return lammps_->force->ftm2v; } double LammpsInterface::nktv2p()const{ return lammps_->force->nktv2p; } double LammpsInterface::qqr2e() const{ return lammps_->force->qqr2e; } double LammpsInterface::qe2f() const{ return lammps_->force->qe2f; } double LammpsInterface::dielectric()const{return lammps_->force->dielectric; } double LammpsInterface::qqrd2e()const{ return lammps_->force->qqrd2e; } double LammpsInterface::qv2e() const{ return qe2f()*ftm2v(); } double LammpsInterface::pair_force(int i, int j, double rsq, double & fmag_over_rmag) const { int itype = (lammps_->atom->type)[i]; int jtype = (lammps_->atom->type)[j]; // return value is the energy if (rsq < (lammps_->force->pair->cutsq)[itype][jtype]) { return lammps_->force->pair->single(i,j,itype,jtype, rsq,1.0,1.0,fmag_over_rmag); } return 0.0; } double LammpsInterface::pair_force(int n, double rsq, double & fmag_over_rmag) const { int i = bond_list_i(n); int j = bond_list_j(n); int type = bond_list_type(n); // return value is the energy return lammps_->force->bond->single(type,rsq,i,j,fmag_over_rmag); } double LammpsInterface::pair_force( map< std::pair< int,int >,int >::const_iterator itr, double rsq, double & fmag_over_rmag, int nbonds) const { int n = itr->second; if (n < nbonds) { return pair_force(n, rsq,fmag_over_rmag); } else { std::pair <int,int> ij = itr->first; int i = ij.first; int j = ij.second; return pair_force(i,j, rsq,fmag_over_rmag); } } double LammpsInterface::pair_force( std::pair< std::pair< int,int >,int > apair, double rsq, double & fmag_over_rmag, int nbonds) const { int n = apair.second; if (n < nbonds) { return pair_force(n, rsq,fmag_over_rmag); } else { std::pair <int,int> ij = apair.first; int i = ij.first; int j = ij.second; return pair_force(i,j, rsq,fmag_over_rmag); } } double LammpsInterface::bond_stiffness(int i, int j, double rsq0) const { const double perturbation = 1.e-8; double rsq1 = sqrt(rsq0)+perturbation; rsq1 *= rsq1; double f0,f1; pair_force(i,j,rsq0,f0); pair_force(i,j,rsq1,f1); double k = (f1-f0)/perturbation; return k; } double LammpsInterface::pair_cutoff() const { return lammps_->force->pair->cutforce; } void LammpsInterface::pair_reinit() const { lammps_->force->pair->reinit(); } int LammpsInterface::single_enable() const { return lammps_->force->pair->single_enable; // all bonds have a single } //* insertion/deletion functions : see FixGCMC //* delete atom int LammpsInterface::delete_atom(int id) const { LAMMPS_NS::Atom * atom = lammps_->atom; atom->avec->copy(atom->nlocal-1,id,1); atom->nlocal--; return atom->nlocal; } //* insert atom int LammpsInterface::insert_atom(int atype, int amask, double *ax, double *av, double aq) const { LAMMPS_NS::Atom * atom = lammps_->atom; atom->avec->create_atom(atype,ax); int m = atom->nlocal - 1; atom->mask[m] = amask; atom->v[m][0] = av[0]; atom->v[m][1] = av[1]; atom->v[m][2] = av[2]; if (aq != 0) atom->q[m] = aq; int nfix = lammps_->modify->nfix; LAMMPS_NS::Fix **fix = lammps_->modify->fix; for (int j = 0; j < nfix; j++) { if (fix[j]->create_attribute) fix[j]->set_arrays(m); } return m; } int LammpsInterface::reset_ghosts(int deln) const { LAMMPS_NS::Atom * atom = lammps_->atom; //ATC::LammpsInterface::instance()->print_msg("reset_ghosts del n "+to_string(deln)); if (atom->tag_enable) { atom->natoms += deln; //ATC::LammpsInterface::instance()->print_msg("reset_ghosts natoms "+to_string(atom->natoms)); if (deln > 0) { atom->tag_extend(); } if (atom->map_style) atom->map_init(); } atom->nghost = 0; lammps_->comm->borders(); //ATC::LammpsInterface::instance()->print_msg("reset_ghosts nghosts "+to_string(atom->nghost)); return atom->nghost; } //* energy for interactions within the shortrange cutoff double LammpsInterface::shortrange_energy(double *coord, int itype, int id, double max) const { LAMMPS_NS::Atom * atom = lammps_->atom; double **x = atom->x; int *type = atom->type; int nall = atom->nlocal+ atom->nghost; LAMMPS_NS::Pair *pair = lammps_->force->pair; double **cutsq = lammps_->force->pair->cutsq; double fpair = 0.0; // an output of single double factor_coul = 1.0; double factor_lj = 1.0; double total_energy = 0.0; for (int j = 0; j < nall; j++) { if (id == j) continue; // factor_lj = special_lj[sbmask(j)]; // factor_coul = special_coul[sbmask(j)]; //j &= NEIGHMASK; double delx = coord[0] - x[j][0]; double dely = coord[1] - x[j][1]; double delz = coord[2] - x[j][2]; double rsq = delx*delx + dely*dely + delz*delz; int jtype = type[j]; double cut2 = cutsq[itype][jtype]; if (rsq < cut2) { total_energy += pair->single(id,j,itype,jtype,rsq,factor_coul,factor_lj,fpair); //id is for charge lookup } } return total_energy; } double LammpsInterface::shortrange_energy(int id, double max) const { double *x = (lammps_->atom->x)[id]; int type = (lammps_->atom->type)[id]; return shortrange_energy(x,type,id,max); } POTENTIAL LammpsInterface::potential() const { // find pair style - FRAGILE const int nStyles = 4; string pairStyles[nStyles] = {"lj/cut", "lj/cut/coul/long", "lj/cut/coul/cut", "lj/charmm/coul/long"}; LAMMPS_NS::Pair *pair = NULL; for (int i = 0; i < nStyles; i++){ pair = lammps_->force->pair_match(pairStyles[i].c_str(),1); if (pair != NULL) break; } return pair; } int LammpsInterface::type_to_groupbit(int itype) const { LAMMPS_NS::Atom * atom = lammps_->atom; int groupbit = -1; int *type = atom->type; int *mask = atom->mask; for (int i = 0; i < nlocal(); i++) { if (type[i] == itype) { groupbit = mask[i]; break; } } return int_allmax(groupbit); } bool LammpsInterface::epsilons(int itype, POTENTIAL pair, double * epsilon0) const { // grab energy parameters char * pair_parameter = new char[8]; strcpy(pair_parameter,"epsilon"); int dim = 2; // a return value for extract double ** epsilons = (double**) ( pair->extract(pair_parameter,dim) ); delete [] pair_parameter; if (epsilons == NULL) return false; //if (epsilons == NULL) error->all(FLERR,"Fix concentration adapted pair style parameter not supported"); int i1,i2; for (int i=1; i < ntypes()+1; i++) { if (i < itype) { i1 = i; i2 = itype; } else { i2 = i; i1 = itype; } epsilon0[i-1] = epsilons[i1][i2]; } return true; } bool LammpsInterface::set_epsilons(int itype, POTENTIAL pair, double * epsilon) const { // grab energy parameters char * pair_parameter = new char[8]; strcpy(pair_parameter,"epsilon"); int dim = 2; // a return value for extract double ** epsilons = (double**) ( pair->extract(pair_parameter,dim) ); delete [] pair_parameter; if (epsilons == NULL) return false; //if (epsilons == NULL) error->all(FLERR,"Fix concentration adapted pair style parameter not supported"); // scale interactions int i1,i2; for (int i = 1; i < ntypes()+1; i++) { if (i < itype) { i1 = i; i2 = itype; } else { i2 = i; i1 = itype; } epsilons[i1][i2] = epsilon[i-1]; } return true; } int LammpsInterface::set_charge(int itype, double charge) const { int nlocal = lammps_->atom->nlocal; int *type = lammps_->atom->type; double *q = lammps_->atom->q; int count = 0; for (int i = 0; i < nlocal; i++) { if (type[i] == itype) { q[i] = charge; count++; } } return count; } int LammpsInterface::change_type(int itype, int jtype) const { int nlocal = lammps_->atom->nlocal; int *type = lammps_->atom->type; int count = 0; for (int i = 0; i < nlocal; i++) { if (type[i] == itype) { type[i] = jtype; count++; } } return count; } int LammpsInterface::count_type(int itype) const { int nlocal = lammps_->atom->nlocal; int *type = lammps_->atom->type; int count = 0; for (int i = 0; i < nlocal; i++) { if (type[i] == itype) { count++; } } return int_allsum(count); } // random number generators RNG_POINTER LammpsInterface::random_number_generator() const { RNG_POINTER p = new LAMMPS_NS::RanPark(lammps_,seed_); return p; } double LammpsInterface::random_uniform(RNG_POINTER p) const { return p->uniform(); } double LammpsInterface::random_normal (RNG_POINTER p) const { return p->gaussian(); } int LammpsInterface::random_state (RNG_POINTER p) const { return p->state(); } void LammpsInterface::set_random_state (RNG_POINTER p, int seed) const { return p->reset(seed); } void LammpsInterface::advance_random_generator (RNG_POINTER p, int n) const { advance_random_uniform(p,n); } void LammpsInterface::advance_random_uniform (RNG_POINTER p, int n) const { for (int i = 0; i < n; i++) p->uniform(); } void LammpsInterface::advance_random_normal (RNG_POINTER p, int n) const { for (int i = 0; i < n; i++) p->gaussian(); } //* Boltzmann's constant in M,L,T,t units double LammpsInterface::kBoltzmann() const { return (lammps_->force->boltz)/(lammps_->force->mvv2e); } //* Planck's constant double LammpsInterface::hbar() const { const int UNITS_STYLE = (int) units_style(); double hbar = 1.0; // LJ: Dimensionless if (UNITS_STYLE == 2) hbar = 15.1685792814; // Real: KCal/mol-fs else if (UNITS_STYLE == 3) hbar = 0.000658212202469; // Metal: eV-ps return hbar; } //* Dulong-Petit heat capacity double LammpsInterface::heat_capacity() const { double rhoCp = dimension()*kBoltzmann()/volume_per_atom(); return rhoCp; } //* reference mass density for a *unit cell* // all that is needed is a unit cell: volume, types, mass per type double LammpsInterface::mass_density(int* numPerType) const { const double *mass = lammps_->atom->mass; if (!mass) throw ATC_Error("cannot compute a mass density: no mass"); const int ntypes = lammps_->atom->ntypes; const int *mass_setflag = lammps_->atom->mass_setflag; const int *type = lammps_->atom->type; double naCell = num_atoms_per_cell(); double vol = volume_per_atom(); if (numPerType) { double m = 0.0; double n = 0; for (int i = 0; i < ntypes; i++) { m += numPerType[i]*mass[i+1]; n += numPerType[i]; } if (n>naCell) throw ATC_Error("cannot compute a mass density: too many basis atoms"); return m/n/vol; } // since basis->type map not stored only monatomic lattices are automatic // if not given a basis try to guess else { if (ntypes == 1) { if ((this->natoms()==0) && mass_setflag[1]) { return mass[1]/vol; } else { if (type) return mass[type[0]]/vol; else if (mass_setflag[1]) return mass[1]/vol; } } } throw ATC_Error("cannot compute a mass density"); return 0.0; } //* permittivity of free space double LammpsInterface::epsilon0() const { return qe2f()/(4.*PI*qqr2e()); } //* Coulomb's constant double LammpsInterface::coulomb_constant() const { return qqr2e()/qe2f(); } //* special coulombic interactions double * LammpsInterface::special_coul() const { return lammps_->force->special_coul; } //* flag for newton int LammpsInterface::newton_pair() const { return lammps_->force->newton_pair; } // ----------------------------------------------------------------- // group interface methods // ----------------------------------------------------------------- int LammpsInterface::ngroup() const { return lammps_->group->ngroup; } int LammpsInterface::group_bit(string name) const { return group_bit(group_index(name)); } int LammpsInterface::group_bit(int iGroup) const { int mybit = 0; mybit |= lammps_->group->bitmask[iGroup]; if (mybit < 0 || mybit > MAX_GROUP_BIT) { string msg("LammpsInterface::group_bit() lammps group bit "+to_string(mybit)+" is out of range 0:"+to_string(MAX_GROUP_BIT)); throw ATC_Error(msg); } return mybit; } int LammpsInterface::group_index(string name) const { int igroup = lammps_->group->find(name.c_str()); if (igroup == -1) { string msg("LammpsInterface::group_index() lammps group "+name+" does not exist"); throw ATC_Error(msg); } return igroup; } int LammpsInterface::group_inverse_mask(int iGroup) const { return lammps_->group->inversemask[iGroup]; } char * LammpsInterface::group_name(int iGroup) const { return lammps_->group->names[iGroup]; } void LammpsInterface::group_bounds(int iGroup, double * b) const { lammps_->group->bounds(iGroup, b); } // ----------------------------------------------------------------- // memory interface methods // ----------------------------------------------------------------- double * LammpsInterface::create_1d_double_array(int length, const char *name) const { double * myArray; return lammps_->memory->create(myArray, length, name); } double *LammpsInterface::grow_1d_double_array(double *array, int length, const char *name) const { return lammps_->memory->grow(array, length, name); } void LammpsInterface::destroy_1d_double_array(double * d) const { lammps_->memory->destroy(d); } double ** LammpsInterface::create_2d_double_array(int n1, int n2, const char *name) const { double ** myArray; return lammps_->memory->create(myArray, n1, n2, name); } void LammpsInterface::destroy_2d_double_array(double **d) const { lammps_->memory->destroy(d); } double **LammpsInterface::grow_2d_double_array(double **array, int n1, int n2, const char *name) const { return lammps_->memory->grow(array, n1, n2, name); } int * LammpsInterface::create_1d_int_array(int length, const char *name) const { int * myArray; return lammps_->memory->create(myArray, length, name); } int *LammpsInterface::grow_1d_int_array(int *array, int length, const char *name) const { return lammps_->memory->grow(array, length, name); } void LammpsInterface::destroy_1d_int_array(int * d) const { lammps_->memory->destroy(d); } int ** LammpsInterface::create_2d_int_array(int n1, int n2, const char *name) const { int ** myArray; return lammps_->memory->create(myArray, n1, n2, name); } void LammpsInterface::destroy_2d_int_array(int **i) const { lammps_->memory->destroy(i); } int ** LammpsInterface::grow_2d_int_array(int **array, int n1, int n2, const char *name) const { return lammps_->memory->grow(array, n1, n2, name); } // ----------------------------------------------------------------- // update interface methods // ----------------------------------------------------------------- double LammpsInterface::dt() const { return lammps_->update->dt; } bigint LammpsInterface::ntimestep() const { return lammps_->update->ntimestep; } int LammpsInterface::nsteps() const { return lammps_->update->nsteps; } // ----------------------------------------------------------------- // neighbor list interface methods // ----------------------------------------------------------------- int LammpsInterface::sbmask(int j) const {return j >> SBBITS & 3; } void LammpsInterface::set_list(int id, LAMMPS_NS::NeighList *ptr) { list_ = ptr; } int LammpsInterface::neighbor_list_inum() const { return list_->inum; } int * LammpsInterface::neighbor_list_numneigh() const { return list_->numneigh; } int * LammpsInterface::neighbor_list_ilist() const { return list_->ilist; } int ** LammpsInterface::neighbor_list_firstneigh() const { return list_->firstneigh; } int LammpsInterface::neighbor_ago() const { return lammps_->neighbor->ago; } int LammpsInterface::reneighbor_frequency() const {return lammps_->neighbor->every; } // ----------------------------------------------------------------- // bond list interface methods // ----------------------------------------------------------------- int LammpsInterface::bond_list_length() const { return lammps_->neighbor->nbondlist; } int** LammpsInterface::bond_list() const { return lammps_->neighbor->bondlist; } // ----------------------------------------------------------------- // region interface methods // ----------------------------------------------------------------- char * LammpsInterface::region_name(int iRegion) const { return lammps_->domain->regions[iRegion]->id; } char * LammpsInterface::region_style(int iRegion) const { return lammps_->domain->regions[iRegion]->style; } double LammpsInterface::region_xlo(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_xlo; } double LammpsInterface::region_xhi(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_xhi; } double LammpsInterface::region_ylo(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_ylo; } double LammpsInterface::region_yhi(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_yhi; } double LammpsInterface::region_zlo(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_zlo; } double LammpsInterface::region_zhi(int iRegion) const { return lammps_->domain->regions[iRegion]->extent_zhi; } double LammpsInterface::region_xscale(int iRegion) const { return lammps_->domain->regions[iRegion]->xscale; } double LammpsInterface::region_yscale(int iRegion) const { return lammps_->domain->regions[iRegion]->yscale; } double LammpsInterface::region_zscale(int iRegion) const { return lammps_->domain->regions[iRegion]->zscale; } int LammpsInterface::region_match(int iRegion, double x, double y, double z) const { return lammps_->domain->regions[iRegion]->match(x,y,z); } // ----------------------------------------------------------------- // compute methods // ----------------------------------------------------------------- COMPUTE_POINTER LammpsInterface::compute_pointer(string tag) const { // get the compute id char * name = const_cast <char*> (tag.c_str()); int id = lammps_->modify->find_compute(name); if (id < 0) { string msg("Could not find compute "+tag); msg += tag; throw ATC_Error(msg); } // get the compute LAMMPS_NS::Compute* cmpt = lammps_->modify->compute[id]; // insert it into our set, recall it won't be added if it already exists computePointers_.insert(cmpt); return cmpt; } void LammpsInterface::computes_addstep(int step) const { set<LAMMPS_NS::Compute * >::iterator iter; for (iter = computePointers_.begin(); iter != computePointers_.end(); iter++) { (*iter)->addstep(step); } } void LammpsInterface::compute_addstep(COMPUTE_POINTER computePointer, int step) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); cmpt->addstep(step); } int LammpsInterface::compute_matchstep(COMPUTE_POINTER computePointer, int step) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); return cmpt->matchstep(step); } void LammpsInterface::reset_invoked_flag(COMPUTE_POINTER computePointer) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); cmpt->invoked_flag = 0; } int LammpsInterface::compute_ncols_peratom(COMPUTE_POINTER computePointer) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); int ndof = cmpt->size_peratom_cols; if (ndof == 0 ) ndof = 1; return ndof; } double* LammpsInterface::compute_vector_peratom(COMPUTE_POINTER computePointer) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); if (!(cmpt->invoked_flag & INVOKED_PERATOM)) { cmpt->compute_peratom(); cmpt->invoked_flag |= INVOKED_PERATOM; } return cmpt->vector_atom; } double** LammpsInterface::compute_array_peratom(COMPUTE_POINTER computePointer) const { LAMMPS_NS::Compute* cmpt = const_to_active(computePointer); if (!(cmpt->invoked_flag & INVOKED_PERATOM)) { cmpt->compute_peratom(); cmpt->invoked_flag |= INVOKED_PERATOM; } return cmpt->array_atom; } LAMMPS_NS::Compute * LammpsInterface::const_to_active(COMPUTE_POINTER computePointer) const { LAMMPS_NS::Compute* cmpt = const_cast<LAMMPS_NS::Compute* >(computePointer); set<LAMMPS_NS::Compute * >::iterator cmptPtr; cmptPtr = computePointers_.find(cmpt); if (cmptPtr != computePointers_.end()) return *cmptPtr; else throw ATC_Error("Requested bad computer pointer"); } // ----------------------------------------------------------------- // compute pe/atom interface methods // - the only compute "owned" by ATC // ----------------------------------------------------------------- int LammpsInterface::create_compute_pe_peratom(void) const { char **list = new char*[4]; string atomPeName = compute_pe_name(); list[0] = (char *) atomPeName.c_str(); list[1] = (char *) "all"; list[2] = (char *) "pe/atom"; list[3] = (char *) "pair"; int icompute = lammps_->modify->find_compute(list[0]); if (icompute < 0) { lammps_->modify->add_compute(4,list); icompute = lammps_->modify->find_compute(list[0]); } delete [] list; if (! atomPE_ ) { atomPE_ = lammps_->modify->compute[icompute]; } computePointers_.insert(atomPE_); stringstream ss; ss << "peratom PE compute created with ID: " << icompute; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); return icompute; } double * LammpsInterface::compute_pe_peratom(void) const { if (atomPE_) { atomPE_->compute_peratom(); return atomPE_->vector_atom; } else { return NULL; } } /* ---------------------------------------------------------------------- */ void LammpsInterface::unwrap_coordinates(int iatom, double* xatom) const { double **x = lammps_->atom->x; int *image = lammps_->atom->image; double *h = lammps_->domain->h; double xprd = lammps_->domain->xprd; double yprd = lammps_->domain->yprd; double zprd = lammps_->domain->zprd; int xbox,ybox,zbox; // for triclinic, need to unwrap current atom coord via h matrix if (lammps_->domain->triclinic == 0) { xbox = (image[iatom] & 1023) - 512; ybox = (image[iatom] >> 10 & 1023) - 512; zbox = (image[iatom] >> 20) - 512; xatom[0] = x[iatom][0] + xbox*xprd; xatom[1] = x[iatom][1] + ybox*yprd; xatom[2] = x[iatom][2] + zbox*zprd; } else { xbox = (image[iatom] & 1023) - 512; ybox = (image[iatom] >> 10 & 1023) - 512; zbox = (image[iatom] >> 20) - 512; xatom[0] = x[iatom][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox; xatom[1] = x[iatom][1] + h[1]*ybox + h[3]*zbox; xatom[2] = x[iatom][2] + h[2]*zbox; } } /* ---------------------------------------------------------------------- */ LAMMPS_NS::PairEAM* LammpsInterface::pair_eam() const { //if (typeid(lammps_->force->pair) == typeid(LAMMPS_NS::PairEAM)) { // return lammps_->force->pair; //} LAMMPS_NS::PairEAM* pair_eam = dynamic_cast<LAMMPS_NS::PairEAM*> (lammps_->force->pair); if (pair_eam != NULL) { return pair_eam; } else { throw ATC_Error("LAMMPS Pair object is not of the derived class type PairEAM"); } } // ----------------------------------------------------------------- // other methods // ----------------------------------------------------------------- /** Return lammps pointer -- only as a last resort! */ LAMMPS_NS::LAMMPS * LammpsInterface::lammps_pointer() const { return lammps_; } } diff --git a/lib/atc/LammpsInterface.h b/lib/atc/LammpsInterface.h index e306a37aa..238e6cff3 100644 --- a/lib/atc/LammpsInterface.h +++ b/lib/atc/LammpsInterface.h @@ -1,711 +1,735 @@ #ifndef LAMMPS_INTERFACE_H #define LAMMPS_INTERFACE_H #include <iostream> #include <stdlib.h> #include <map> #include <iostream> #include <string> #include <sstream> +#include <fstream> #include <utility> #include "mpi.h" #include "lammps.h" +#include "comm.h" #include "modify.h" #include "memory.h" #include "random_park.h" typedef LAMMPS_NS::RanPark* RNG_POINTER; #include "lmptype.h" #include "compute.h" typedef const LAMMPS_NS::Compute* COMPUTE_POINTER; #include "update.h" #include "min.h" #include "ATC_Error.h" #include "ATC_TypeDefs.h" #include "MatrixDef.h" #include "MPI_Wrappers.h" typedef LAMMPS_NS::Pair* POTENTIAL; // Forward class declarations for LAMMPS_NS namespace namespace LAMMPS_NS { class LAMMPS; class NeighList; class Compute; class ComputePEAtom; class ComputeStressAtom; class ComputeCentroAtom; class ComputeCNAAtom; class ComputeCoordAtom; class ComputeKEAtom; class Pair; class PairEAM; class Fix; class RanPark; } namespace ATC { static const std::string atomPeNameBase_ = "atcPE"; static const double big_ = 1.e20; /** * @class LammpsInterface * @brief Singleton class that handles all interfacing with the lammps code */ class LammpsInterface { public: // Enumeration of fundamental per-atom quantities, i.e. those defined directly by Lammps enum FundamentalAtomQuantity { ATOM_MASS = 0, ATOM_CHARGE, ATOM_POSITION, ATOM_VELOCITY, ATOM_FORCE, NUM_FUNDAMENTAL_ATOM_QUANTITIES }; // Enumeration for lattice type. this MUST match the enum in src/lattice.cpp enum LatticeType { NONE=0, SC, BCC, FCC, HCP, DIAMOND, SQ, SQ2, HEX, CUSTOM }; // Enumeration for units type. this is internal to ATC enum UnitsType { UNKNOWN=0, ATC, LJ, REAL, METAL, SI }; + // Enumeration for atom data style + enum AtomStyle { + UNKNOWN_STYLE=0, + ATOMIC_STYLE, + CHARGE_STYLE, + FULL_STYLE + }; + // Provides a struct for easily passing/recovering data about SparseMats struct SparseMatInfo { INDEX rows; INDEX cols; INDEX rowsCRS; INDEX size; }; /** Static instance of this class */ static LammpsInterface * instance(); /** Destroy */ static void Destroy(); /** Set lammps pointer */ void set_lammps(LAMMPS_NS::LAMMPS * lammps) { lammps_ = lammps; MPI_Comm_rank(lammps_->world, & commRank_); MPI_Comm_size(lammps_->world, & commSize_); } /** \name Methods that interface with lammps base class */ /*@{*/ // begin MPI -------------------------------------------------------------------- MPI_Comm world() const; void broadcast(double * buf, int count = 1) const { MPI_Wrappers::broadcast(lammps_->world, buf, count); } void int_broadcast(int * buf, int count = 1) const { MPI_Wrappers::int_broadcast(lammps_->world, buf, count); } // send_buf is frequently a void* so MPI_IN_PLACE can be passed in void allsum(void * send_buf, double * rec_buf, int count = 1) const { MPI_Wrappers::allsum(lammps_->world, send_buf, rec_buf, count); } void int_allsum(void * send_buf, int * rec_buf, int count = 1) const { MPI_Wrappers::int_allsum(lammps_->world, send_buf, rec_buf, count); } int int_allsum(int & i) const { int j = 0; MPI_Wrappers::int_allsum(lammps_->world, &i, &j, 1); return j; } int int_scansum(int & i) const { int j = 0; MPI_Wrappers::int_scansum(lammps_->world, &i, &j, 1); return j; } int int_allmax(int & i) const { int j = 0; MPI_Wrappers::int_allmax(lammps_->world, &i, &j, 1); return j; } int int_allmin(int & i) const { int j = 0; MPI_Wrappers::int_allmin(lammps_->world, &i, &j, 1); return j; } double allmin(double & i) const { double j = 0; MPI_Wrappers::allmin(lammps_->world, &i, &j, 1); return j; } void sparse_allsum(SparseMatrix<double> &toShare) const #ifdef ISOLATE_FE { MPI_Wrappers::sparse_allsum(lammps_->world, toShare); } #else ; #endif void allmax(double * send_buf, double * rec_buf, int count = 1) { MPI_Wrappers::allmax(lammps_->world, send_buf, rec_buf, count); } void int_allmax(int * send_buf, int * rec_buf, int count = 1) const { MPI_Wrappers::int_allmax(lammps_->world, send_buf, rec_buf, count); } void allmin(double * send_buf, double * rec_buf, int count = 1) const { MPI_Wrappers::allmin(lammps_->world, send_buf, rec_buf, count); } void int_allmin(int * send_buf, int * rec_buf, int count = 1) const { MPI_Wrappers::int_allmin(lammps_->world, send_buf, rec_buf, count); } int rank_min(double * send_buf, double * rec_buf, int count = 1) const { return MPI_Wrappers::rank_min(lammps_->world, send_buf, rec_buf, count); } void int_recv(int * recv_buf, int max_size, int iproc) const { MPI_Wrappers::int_recv(lammps_->world, recv_buf, max_size, iproc); } void recv(double * recv_buf, int max_size, int iproc) const { MPI_Wrappers::recv(lammps_->world, recv_buf, max_size, iproc); } void int_send(int * send_buf, int send_size) const { MPI_Wrappers::int_send(lammps_->world, send_buf, send_size); } void send(double * send_buf, int send_size) const { MPI_Wrappers::send(lammps_->world, send_buf, send_size); } void allgatherv(double * send_buf, int send_count, - double * rec_buf, int * rec_counts, int * displacements) const + double * rec_buf, int * rec_counts, int * displacements) const { MPI_Wrappers::allgatherv(lammps_->world, send_buf, send_count, rec_buf, rec_counts, displacements); } + void int_allgather(int send, int* recv) + { + MPI_Wrappers::int_allgather(lammps_->world,send,recv); + } void int_scatter(int * send_buf, int * rec_buf, int count = 1) { MPI_Wrappers::int_scatter(lammps_->world, send_buf, rec_buf, count); } void logical_or(void * send_buf, int * rec_buf, int count = 1) const { MPI_Wrappers::logical_or(lammps_->world, send_buf, rec_buf, count); } void barrier(void) const { MPI_Wrappers::barrier(lammps_->world); } void stop(std::string msg="") const { MPI_Wrappers::stop(lammps_->world, msg); } + std::string read_file(std::string filename) const; + void write_file(std::string filename, std::string contents, + std::ofstream::openmode mode = std::ofstream::out) const { + if (! comm_rank()) { + std::ofstream f(filename.c_str(),mode); + f << contents; + f.close(); + } + // ignore other ranks and assume they are consistent + } // end MPI -------------------------------------------------------------------- void print_debug(std::string msg="") const { std::cout << "rank " << comm_rank() << " " << msg << "\n" << std::flush; barrier(); } int comm_rank(void) const { return commRank_;} int comm_size(void) const { return commSize_;} bool rank_zero(void) const { return (commRank_==0);} bool serial(void) const { int size = 1; MPI_Comm_size(lammps_->world,&size); return (size==1); } void print_msg(std::string msg) const { int me; MPI_Comm_rank(lammps_->world,&me); std::stringstream full_msg; if (serial()) { full_msg << " ATC: " << msg << "\n"; } else { full_msg << " ATC: P" << me << ", " << msg << "\n"; } std::string mesg = full_msg.str(); if (lammps_->screen) fprintf(lammps_->screen, "%s",mesg.c_str()); if (lammps_->logfile) fprintf(lammps_->logfile,"%s",mesg.c_str()); } void print_msg_once(std::string msg,bool prefix=true, bool endline=true) const { int me; MPI_Comm_rank(lammps_->world,&me); if (me==0) { std::stringstream full_msg; if (prefix) full_msg << " ATC: "; full_msg << msg; if (endline) full_msg << "\n"; std::string mesg = full_msg.str(); if (lammps_->screen) fprintf(lammps_->screen, "%s",mesg.c_str()); if (lammps_->logfile) fprintf(lammps_->logfile,"%s",mesg.c_str()); } } void all_print(double data, std::string tag ="") const { int me; MPI_Comm_rank(lammps_->world,&me); std::stringstream full_msg; if (serial()) { full_msg << " ATC: " << tag << data << "\n"; } else { int commSize = comm_size(); double recv[commSize]; MPI_Wrappers::gather(lammps_->world,data,recv); if (rank_zero()) { full_msg << " ATC:" << tag; for (int i = 0; i < commSize; i++) { full_msg << " P" << i << ": " << recv[i] ; } full_msg << "\n"; } } if (rank_zero()) { std::string mesg = full_msg.str(); if (lammps_->screen) fprintf(lammps_->screen, "%s",mesg.c_str()); if (lammps_->logfile) fprintf(lammps_->logfile,"%s",mesg.c_str()); } } void stream_msg_once(std::string msg,bool prefix=true, bool endline=true) const { int me; MPI_Comm_rank(lammps_->world,&me); if (me==0) { if (prefix) std::cout << " ATC: "; std::cout << msg; if (endline) std::cout << "\n"; std::cout << std::flush; } } void forward_comm_fix() const; void comm_borders() const; /*@}*/ /** \name Methods that interface with Atom class */ /*@{*/ void set_fix_pointer(LAMMPS_NS::Fix * thisFix); std::string fix_id() const; bool atoms_sorted() const; LAMMPS_NS::bigint natoms() const; int nlocal() const; int nghost() const; int nmax() const; int ntypes() const; double ** xatom() const; double ** vatom() const; double ** fatom() const; const int * atom_mask() const; int * atom_mask(); int * atom_type() const; int * atom_tag() const; int * atom_to_molecule() const; int * num_bond() const; int ** bond_atom() const; int * image() const; int bond_per_atom() const; int newton_bond() const; int local_to_global_map(int global) const; int type_to_charge(int t) const; //* Returns a pointer to the atom masses (NOT SAFE). double * atom_mass() const; //* Indexes an atomic mass by atom type (NO BOUNDS CHECK). double atom_mass(int iType) const; double * atom_rmass() const; double * atom_charge() const; double * atom_scalar(FundamentalAtomQuantity quantityType) const; double ** atom_vector(FundamentalAtomQuantity quantityType) const; int atom_quantity_ndof(FundamentalAtomQuantity quantityType) const; double atom_quantity_conversion(FundamentalAtomQuantity quantityType) const; void unwrap_coordinates(int iatom, double* xatom) const; /*@}*/ /** \name Methods that interface with Domain class */ /*@{*/ int dimension() const; int nregion() const; void box_bounds(double & boxxlo, double & boxxhi, double & boxylo, double & boxyhi, double & boxzlo, double & boxzhi) const; bool in_box(double * x) const; bool in_my_processor_box(double * x) const; void sub_bounds(double & subxlo, double & subxhi, double & subylo, double & subyhi, double & subzlo, double & subzhi) const; int xperiodic() const; int yperiodic() const; int zperiodic() const; int nperiodic() const; void box_periodicity(int & xperiodic, int & yperiodic, int & zperiodic) const; void periodicity_correction(double * x) const; void set_reference_box(void) const; // const since called by perd_corr int region_id(const char * regionName) const; double domain_xprd() const; double domain_yprd() const; double domain_zprd() const; double domain_volume() const; double domain_xy() const; double domain_xz() const; double domain_yz() const; int domain_triclinic() const; bool region_bounds(const char * regionName, double &xmin, double &xmax, double &ymin, double & ymax, double &zmin, double &zmax, double &xscale, double &yscale, double &zscale) const; bool region_bounds(const char * regionName, double &xmin, double &xmax, double &ymin, double & ymax, double &zmin, double &zmax) const { double xs,ys,zs; bool ifBlock = region_bounds(regionName, xmin,xmax,ymin,ymax,zmin,zmax,xs,ys,zs); xmin *= xs; xmax *= xs; ymin *= ys; ymax *= ys; zmin *= zs; zmax *= zs; return ifBlock; } /*@}*/ void minimum_image(double & dx, double & dy, double & dz) const; void closest_image(const double * const xi, const double * const xj, double * const xjImage) const; /** \name Methods that interface with Update class */ UnitsType units_style() const; double convert_units(double value, UnitsType in, UnitsType out, int massExp, int lenExp, int timeExp, int engExp=0) const; //double minimize_energy() { return lammps_->update->minimize->ecurrent; } double minimize_energy() const { return lammps_->update->minimize->eprevious; } /*@}*/ /** \name Methods that interface with Lattice class */ /*@{*/ double xlattice() const; double ylattice() const; double zlattice() const; LatticeType lattice_style() const; int n_basis() const; void basis_vectors(double **basis) const; double max_lattice_constant(void) const; double near_neighbor_cutoff(void) const; void unit_cell(double *a1, double *a2, double *a3) const; /** these functions are more than just simple pass throughs */ int num_atoms_per_cell(void) const; double volume_per_atom(void) const; void lattice(Matrix<double> &N, Matrix<double> &B) const; /*@}*/ /** \name Methods that interface with Force class */ /*@{*/ double boltz() const; double mvv2e() const; double ftm2v() const; double nktv2p() const; double qqr2e() const; double qe2f() const; double dielectric() const; double qqrd2e() const; double qv2e() const; // converts charge * voltage --> mass length^2 / time^2 /*@}*/ /** \name Methods that interface with pair class */ /*@{*/ // interface to "single" double pair_force(int i, int j, double rsq, double& fmag_over_rmag) const; // pair class double pair_force(int n, double rsq, double& fmag_over_rmag) const; // bond class double pair_force(std::map< std::pair< int,int >,int >::const_iterator itr, double rsq, double& fmag_over_rmag, int nbonds = 0) const; double pair_force(std::pair< std::pair< int,int >,int > apair, double rsq, double& fmag_over_rmag, int nbonds = 0) const; double pair_cutoff() const; void pair_reinit() const; int single_enable() const; LAMMPS_NS::PairEAM * pair_eam(void) const; double bond_stiffness(int i, int j, double rsq) const; /*@}*/ /** \name Methods for addition/deletion of atoms*/ /*@{*/ int delete_atom(int id) const; int insert_atom(int type, int mask, double* x, double* v, double q = 0) const; double shortrange_energy(double *x, int type, int id = -1, double max = big_) const; int reset_ghosts(int dn) const; double shortrange_energy(int id, double max = big_) const; POTENTIAL potential(void) const; int type_to_groupbit(int itype) const; int change_type(int itype, int jtype) const; int count_type(int itype) const; bool epsilons(int type, POTENTIAL p, double * epsilons) const; bool set_epsilons(int type, POTENTIAL p, double * epsilons) const; int set_charge(int type, double charge) const; /*@}*/ /** \name interface to random number generator */ /*@{*/ RNG_POINTER random_number_generator() const; double random_uniform(RNG_POINTER p) const; double random_normal (RNG_POINTER p) const; int random_state (RNG_POINTER p) const; void set_random_state (RNG_POINTER p, int seed) const; void advance_random_generator (RNG_POINTER p, int n = 1) const; void advance_random_uniform (RNG_POINTER p, int n = 1) const; void advance_random_normal (RNG_POINTER p, int n = 1) const; /*@}*/ /** these functions are more than just simple pass throughs */ /*@{*/ /** Boltzmann's constant in M,L,T,t units */ double kBoltzmann(void) const; /** Planck's constant (energy-time units) */ double hbar(void) const; /** Dulong-Petit heat capacity per volume in M,L,T,t units */ double heat_capacity(void) const; /** mass per volume in reference configuraturation in M,L units */ double mass_density(int* numPerType=NULL) const; /** permittivity of free space, converts from LAMMPS potential units implied by the electric field units to LAMMPS charge units/LAMMPS length units (e.g., V to elemental charge/A) */ double epsilon0(void) const; double coulomb_constant(void) const; double * special_coul() const; int newton_pair() const; double coulomb_factor(int & j) const { int n = nlocal() + nghost(); double * sc = special_coul(); double factor_coul = 1.; if (j >= n) { factor_coul = sc[j/n]; j %= n; } return factor_coul; } /*@}*/ /** \name Methods that interface with Group class */ /*@{*/ int ngroup() const; int group_bit(std::string name) const; int group_bit(int iGroup) const; int group_index(std::string name) const; int group_inverse_mask(int iGroup) const; char * group_name(int iGroup) const; void group_bounds(int iGroup, double * b) const; /*@}*/ /** \name Methods that interface with Memory class */ /*@{*/ double * create_1d_double_array(int length, const char *name) const; double * grow_1d_double_array(double *array, int length, const char *name) const; void destroy_1d_double_array(double * d) const; double ** create_2d_double_array(int n1, int n2, const char *name) const; void destroy_2d_double_array(double **d) const; double **grow_2d_double_array(double **array, int n1, int n2, const char *name) const; int * create_1d_int_array(int length, const char *name) const; int * grow_1d_int_array(int *array, int length, const char *name) const; void destroy_1d_int_array(int * d) const; int ** create_2d_int_array(int n1, int n2, const char *name) const; void destroy_2d_int_array(int **i) const; int ** grow_2d_int_array(int **array, int n1, int n2, const char *name) const; template <typename T> T * grow_array(T *&array, int n, const char *name) const {return lammps_->memory->grow(array,n,name);}; template <typename T> void destroy_array(T * array) {lammps_->memory->destroy(array);}; template <typename T> T ** grow_array(T **&array, int n1, int n2, const char *name) const {return lammps_->memory->grow(array,n1,n2,name);}; template <typename T> void destroy_array(T ** array) const {lammps_->memory->destroy(array);}; /*@}*/ /** \name Methods that interface with Update class */ /*@{*/ double dt() const; LAMMPS_NS::bigint ntimestep() const; int nsteps() const; bool now(LAMMPS_NS::bigint f) { return (ntimestep() % f == 0); } /*@}*/ /** \name Methods that interface with neighbor list */ /*@{*/ void neighbor_remap(int & j) const { j &= NEIGHMASK; } int sbmask(int j) const; void set_list(int id, LAMMPS_NS::NeighList *ptr) ; int neighbor_list_inum() const; int * neighbor_list_numneigh() const; int * neighbor_list_ilist() const; int ** neighbor_list_firstneigh() const; int neighbor_ago() const; int reneighbor_frequency() const; LAMMPS_NS::NeighList * neighbor_list(void) const { return list_;} /*@}*/ /** \name Methods that interface with bond list */ /*@{*/ int bond_list_length() const; int ** bond_list() const; // direct access int * bond_list(int n) const { return bond_list()[n];} int bond_list_i(int n) const { return bond_list(n)[0];} int bond_list_j(int n) const { return bond_list(n)[1];} int bond_list_type(int n) const { return bond_list(n)[2];} /*@}*/ /** \name Methods that interface with Region class */ /*@{*/ char * region_name(int iRegion) const; char * region_style(int iRegion) const; double region_xlo(int iRegion) const; double region_xhi(int iRegion) const; double region_ylo(int iRegion) const; double region_yhi(int iRegion) const; double region_zlo(int iRegion) const; double region_zhi(int iRegion) const; double region_xscale(int iRegion) const; double region_yscale(int iRegion) const; double region_zscale(int iRegion) const; int region_match(int iRegion, double x, double y, double z) const; /*@}*/ /** \name Methods that interface with compute class */ enum COMPUTE_INVOKED {INVOKED_SCALAR=1,INVOKED_VECTOR=2,INVOKED_ARRAY=4,INVOKED_PERATOM=8}; enum PER_ATOM_COMPUTE {PE_ATOM, STRESS_ATOM, CENTRO_ATOM, CNA_ATOM, COORD_ATOM, KE_ATOM, NUM_PER_ATOM_COMPUTES}; // computes owned by LAMMPS COMPUTE_POINTER compute_pointer(std::string tag) const; int compute_ncols_peratom(COMPUTE_POINTER computePointer) const; double* compute_vector_peratom(COMPUTE_POINTER computePointer) const; double** compute_array_peratom(COMPUTE_POINTER computePointer) const; void computes_addstep(int step) const; void compute_addstep(COMPUTE_POINTER computePointer, int step) const; int compute_matchstep(COMPUTE_POINTER computePointer, int step) const; void reset_invoked_flag(COMPUTE_POINTER computePointer) const; // computes owned by ATC int create_compute_pe_peratom(void) const; double * compute_pe_peratom(void) const; std::string compute_pe_name(void) const {return atomPeNameBase_;};// +fix_id();}; enables unique names, if desired void computes_clearstep(void) const {lammps_->modify->clearstep_compute();}; /*@}*/ /** Return lammps pointer -- only as a last resort! */ LAMMPS_NS::LAMMPS * lammps_pointer() const; protected: /** transfer a const compute pointer to a non-const computer pointer */ LAMMPS_NS::Compute * const_to_active(const LAMMPS_NS::Compute* computePointer) const; LAMMPS_NS::LAMMPS * lammps_; LAMMPS_NS::Fix * fixPointer_; /** access to neighbor list */ mutable LAMMPS_NS::NeighList *list_; /** constructor */ LammpsInterface(); /** comm rank */ int commRank_; /** number of processes */ int commSize_; /** compute pe/atom */ mutable LAMMPS_NS::Compute * atomPE_; /** box info */ mutable bool refBoxIsSet_; mutable double upper_[3],lower_[3],length_[3]; /** registry of computer pointers */ mutable std::set<LAMMPS_NS::Compute * > computePointers_; /** a random number generator from lammps */ mutable LAMMPS_NS::RanPark * random_; mutable LAMMPS_NS::RanPark * globalrandom_; private: static LammpsInterface * myInstance_; }; class HeartBeat { public: HeartBeat(std::string name, int freq) : name_(name), freq_(freq), counter_(0) {}; ~HeartBeat(){}; void start() const { ATC::LammpsInterface::instance()->stream_msg_once(name_,true,false);} void next() const { if (counter_++ % freq_ == 0 ) ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);} void finish() const { ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);} protected: std::string name_; int freq_; mutable int counter_; private: HeartBeat(); }; } // end namespace ATC #endif diff --git a/lib/atc/LinearSolver.cpp b/lib/atc/LinearSolver.cpp index c6e1e9cf7..ea31a64a7 100644 --- a/lib/atc/LinearSolver.cpp +++ b/lib/atc/LinearSolver.cpp @@ -1,563 +1,564 @@ // Header file for this class #include "LinearSolver.h" #include <sstream> using std::stringstream; using std::set; namespace ATC { const double kPenalty = 1.0e4; const double kTol = 1.0e-8; const int kMaxDirect = 1000; // ==================================================================== // LinearSolver // ==================================================================== LinearSolver::LinearSolver( const SPAR_MAT & A, const BC_SET & bcs, const int solverType, const int constraintHandlerType, bool parallel ) : solverType_(solverType), constraintHandlerType_(constraintHandlerType), nVariables_(0), initialized_(false), initializedMatrix_(false), initializedInverse_(false), matrixModified_(false), allowReinitialization_(false), homogeneousBCs_(false), bcs_(&bcs), rhs_(NULL), rhsDense_(), b_(NULL), matrix_(A), matrixDense_(), matrixFreeFree_(), matrixFreeFixed_(),matrixInverse_(), - penalty_(0),maxIterations_(0), maxRestarts_(0), tol_(0), + penalty_(1), + maxIterations_(0), maxRestarts_(0), tol_(0), parallel_(parallel) { // deep copy matrixCopy_ = A; matrixSparse_ = &matrixCopy_; setup(); } LinearSolver::LinearSolver( const SPAR_MAT & A, const int solverType, bool parallel ) : solverType_(solverType), constraintHandlerType_(NO_CONSTRAINTS), nVariables_(0), initialized_(false), initializedMatrix_(true), initializedInverse_(false), matrixModified_(false), allowReinitialization_(false), homogeneousBCs_(false), bcs_(NULL), // null implies no contraints will be added later rhs_(NULL), rhsDense_(), b_(NULL), matrix_(A), matrixDense_(), matrixFreeFree_(), matrixFreeFixed_(),matrixInverse_(), - penalty_(0),maxIterations_(0), maxRestarts_(0), tol_(0), + penalty_(1), + maxIterations_(0), maxRestarts_(0), tol_(0), parallel_(parallel) { // shallow copy matrixSparse_ = &A; setup(); } // -------------------------------------------------------------------- // Setup // -------------------------------------------------------------------- void LinearSolver::setup(void) { - penalty_ = kPenalty; // relative to matrix diagonal tol_ = kTol; nVariables_ = matrix_.nRows(); maxIterations_=2*nVariables_; maxRestarts_=nVariables_; // switch method based on size if (solverType_ < 0) { if (nVariables_ > kMaxDirect ) { solverType_ = ITERATIVE_SOLVE_SYMMETRIC; constraintHandlerType_ = PENALIZE_CONSTRAINTS; } else { solverType_ = DIRECT_SOLVE; } } if (constraintHandlerType_ < 0) { constraintHandlerType_ = PENALIZE_CONSTRAINTS; if (solverType_ == DIRECT_SOLVE) constraintHandlerType_ = CONDENSE_CONSTRAINTS; } if ( solverType_ == DIRECT_SOLVE && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) allowReinitialization_ = true; - if ( solverType_ == ITERATIVE_SOLVE_SYMMETRIC && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) - throw ATC_Error("LinearSolver::unimplemented method"); + if ( solverType_ == ITERATIVE_SOLVE_SYMMETRIC && constraintHandlerType_ == CONDENSE_CONSTRAINTS ) { throw ATC_Error("LinearSolver::unimplemented method"); } } // -------------------------------------------------------------------- // Initialize // -------------------------------------------------------------------- void LinearSolver::allow_reinitialization(void) { if (constraintHandlerType_ == PENALIZE_CONSTRAINTS) { if (matrixModified_ ) throw ATC_Error("LinearSolver: can't allow reinitialization after matrix has been modified"); matrixOriginal_ = *matrixSparse_; } allowReinitialization_ = true; } void LinearSolver::initialize(const BC_SET * bcs) { if (bcs) { if (! allowReinitialization_ ) throw ATC_Error("LinearSolver: reinitialization not allowed"); //if (! bcs_ ) throw ATC_Error("LinearSolver: adding constraints after constructing without constraints is not allowed"); // shallow --> deep copy if (! bcs_ ) { // constraintHandlerType_ == NO_CONSTRAINTS if (matrixModified_) { throw ATC_Error("LinearSolver: adding constraints after constructing without constraints is not allowed if matrix has been modified"); } else { matrixCopy_ = *matrixSparse_; matrixSparse_ = &matrixCopy_; constraintHandlerType_ = -1; setup(); } } bcs_ = bcs; initializedMatrix_ = false; initializedInverse_ = false; if (matrixModified_) { matrixCopy_ = matrixOriginal_; matrixSparse_ = &matrixCopy_; } } initialize_matrix(); initialize_inverse(); initialize_rhs(); initialized_ = true; } // -------------------------------------------------------------------- // initialize_matrix // -------------------------------------------------------------------- void LinearSolver::initialize_matrix(void) { if ( initializedMatrix_ ) return; if (constraintHandlerType_ == PENALIZE_CONSTRAINTS) { add_matrix_penalty(); } else if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { partition_matrix(); } initializedMatrix_ = true; } // -------------------------------------------------------------------- // initialize_inverse // -------------------------------------------------------------------- void LinearSolver::initialize_inverse(void) { if ( initializedInverse_ ) return; if (solverType_ == ITERATIVE_SOLVE_SYMMETRIC || solverType_ == ITERATIVE_SOLVE ) { matrixDiagonal_ = matrixSparse_->diag(); // preconditioner } else { // DIRECT_SOLVE if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { if( num_unknowns() > 0 ) { matrixInverse_ = inv(matrixFreeFree_); } } else { // NO_CONSTRAINTS || PENALIZE_CONSTRAINTS matrixDense_ = matrixSparse_->dense_copy(); // need dense for lapack matrixInverse_ = inv(matrixDense_); } } initializedInverse_ = true; } // -------------------------------------------------------------------- // initialize_rhs // -------------------------------------------------------------------- void LinearSolver::initialize_rhs(void) { if (! rhs_ ) return; if (! bcs_ ) { b_ = rhs_; return; } if (constraintHandlerType_ == PENALIZE_CONSTRAINTS) { add_rhs_penalty(); } else if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { add_rhs_influence(); } } // -------------------------------------------------------------------- // add matrix penalty // - change matrix for Dirichlet conditions: add penalty // -------------------------------------------------------------------- void LinearSolver::add_matrix_penalty(void) { + penalty_ = kPenalty; // relative to matrix diagonal SPAR_MAT & A = matrixCopy_; penalty_ *= (A.diag()).maxabs(); BC_SET::const_iterator itr; for (itr = bcs_->begin(); itr != bcs_->end(); itr++) { int i = itr->first; A.add(i,i,penalty_); // modifies matrix } A.compress(); matrixModified_ = true; } // -------------------------------------------------------------------- // partition matrix // - partition matrix based on Dirichlet constraints // -------------------------------------------------------------------- void LinearSolver::partition_matrix(void) { fixedSet_.clear(); BC_SET::const_iterator itr; for (itr = bcs_->begin(); itr != bcs_->end(); itr++) { int i = itr->first; fixedSet_.insert(i); } freeSet_.clear(); freeGlobalToCondensedMap_.clear(); int j = 0; // local index for (int i = 0; i < nVariables_; i++) { if (fixedSet_.find(i) == fixedSet_.end() ) { freeSet_.insert(i); freeGlobalToCondensedMap_[i] = j++; } } if (matrixDense_.nRows() == 0) matrixDense_ =matrixSparse_->dense_copy(); DENS_MAT & K = matrixDense_; K.row_partition(freeSet_,matrixFreeFree_,matrixFreeFixed_); } // -------------------------------------------------------------------- // add_rhs_penalty // -------------------------------------------------------------------- void LinearSolver::add_rhs_penalty() { // deep copy VECTOR & b = rhsDense_; const VECTOR & r = *rhs_; int size = r.nRows(); b.reset(size); for (int i = 0; i < size; i++) { b(i) = r(i); } if ( ! homogeneousBCs_ ){ BC_SET::const_iterator itr; for (itr = bcs_->begin(); itr != bcs_->end(); itr++) { int i = itr->first; double v = itr->second; b(i) += penalty_ * v; } } b_ = &rhsDense_; } // -------------------------------------------------------------------- // add_rhs_influence // -------------------------------------------------------------------- void LinearSolver::add_rhs_influence() { if (! initializedMatrix_ ) partition_matrix(); // rhs = rhs + K_free,fixed * x_fixed int nbcs = bcs_->size(); if (nbcs == 0) { // no bcs to handle b_ = rhs_; } else { DENS_VEC & b = rhsDense_; if ( ! homogeneousBCs_ ){ DENS_VEC xFixed(nbcs); BC_SET::const_iterator itr; int i = 0; for (itr = bcs_->begin(); itr != bcs_->end(); itr++,i++) { double v = itr->second; xFixed(i,0) = -v; } b = matrixFreeFixed_*xFixed; // matrix and bcs have same ordering } else { b.reset(matrixFreeFixed_.nRows()); } const VECTOR & r = *rhs_; set<int>::const_iterator iter; int i = 0; for (iter = freeSet_.begin(); iter != freeSet_.end(); iter++,i++) { b(i) += r(*iter); } b_ = &rhsDense_; } } // -------------------------------------------------------------------- // set fixed values // - {x_i = y_i} // -------------------------------------------------------------------- void LinearSolver::set_fixed_values(VECTOR & X) { BC_SET::const_iterator itr; for (itr = bcs_->begin(); itr != bcs_->end(); itr++) { int i = itr->first; double v = 0; if ( ! homogeneousBCs_ ) v = itr->second; X(i) = v; } } // -------------------------------------------------------------------- // Eigensystem // -------------------------------------------------------------------- // calls lapack void LinearSolver::eigen_system( DENS_MAT & eigenvalues, DENS_MAT & eigenvectors, const DENS_MAT * M) /* const */ { initialize_matrix(); // no inverse needed const DENS_MAT * Kp = NULL; const DENS_MAT * Mp =M; DENS_MAT MM; DENS_MAT KM; if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { Kp = &matrixFreeFree_; if (M) { DENS_MAT MfreeFixed; // not used M->row_partition(freeSet_,MM,MfreeFixed); Mp = &MM; } } else { if (matrixDense_.nRows() == 0) matrixDense_ =matrixSparse_->dense_copy(); Kp = &matrixDense_; } if (!M) { MM.identity(Kp->nRows()); Mp = &MM; } DENS_MAT eVecs, eVals; eVecs = eigensystem(*Kp,*Mp,eVals); eigenvalues.reset(nVariables_,1); eigenvectors.reset(nVariables_,nVariables_); set<int>::const_iterator itr; for (int i = 0; i < Kp->nRows(); i++) { // ordering is by energy not node eigenvalues(i,0) = eVals(i,0); int j = 0; for (itr = freeSet_.begin(); itr != freeSet_.end(); itr++,j++) { int jj = *itr; eigenvectors(jj,i) = eVecs(j,i); // transpose } } } // -------------------------------------------------------------------- // solve // - solves A x = b // - if a "b" is provided it is used as the new rhs // -------------------------------------------------------------------- bool LinearSolver::solve(VECTOR & x, const VECTOR & b) { SPAR_MAT * A = NULL; rhs_ = &b; initialized_ = false; initialize(); if (num_unknowns() == 0) { set_fixed_values(x); return true; } const VECTOR & r = *b_; if (solverType_ == ITERATIVE_SOLVE_SYMMETRIC) { if (parallel_) { A = new PAR_SPAR_MAT(LammpsInterface::instance()->world(), *matrixSparse_); } else { A = new SPAR_MAT(*matrixSparse_); } DIAG_MAT & PC = matrixDiagonal_; int niter = maxIterations_; double tol = tol_; int convergence = CG(*A, x, r, PC, niter, tol);// CG changes niter, tol if (convergence>0) { stringstream ss; ss << "CG solve did not converge,"; ss << " iterations: " << niter; ss << " residual: " << tol; throw ATC_Error(ss.str()); } } else if (solverType_ == ITERATIVE_SOLVE) { if (parallel_) { A = new PAR_SPAR_MAT(LammpsInterface::instance()->world(), *matrixSparse_); } else { A = new SPAR_MAT(*matrixSparse_); } const DIAG_MAT & PC = matrixDiagonal_; int iterations = maxIterations_; int restarts = maxRestarts_; double tol = tol_; DENS_MAT H(maxRestarts_+1, maxRestarts_); DENS_VEC xx(nVariables_); DENS_VEC bb; bb = b; int convergence = GMRES(*A, xx, bb, PC, H, restarts, iterations, tol); if (convergence>0) { stringstream ss; ss << "GMRES greens_function solve did not converge,"; ss << " iterations: " << iterations; ss << " residual: " << tol; throw ATC_Error(ss.str()); } x.copy(xx.ptr(),xx.nRows()); } else { // DIRECT_SOLVE const DENS_MAT & invA = matrixInverse_; if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { DENS_MAT xx = invA*r; int i = 0; set<int>::const_iterator itr; for (itr = freeSet_.begin(); itr != freeSet_.end(); itr++,i++) { int ii = *itr; x(ii) = xx(i,0); } set_fixed_values(x); } else { DENS_VEC xx = invA*r; for (int i = 0; i < xx.nRows(); i++) { x(i) = xx(i); } } } delete A; return true; } // -------------------------------------------------------------------- // greens function // - returns the solution to a Kronecker delta rhs b = {0 0 .. 1 .. 0 0} // and with homogeneous constraints {x_i = 0} // -------------------------------------------------------------------- void LinearSolver::greens_function(int I, VECTOR & G_I) { SPAR_MAT * A = NULL; initialize_matrix(); initialize_inverse(); G_I.reset(nVariables_); VECTOR & x = G_I; if (solverType_ == ITERATIVE_SOLVE_SYMMETRIC) { DENS_VEC b(nVariables_); b = 0.0; b(I) = 1.0; if (parallel_) { A = new PAR_SPAR_MAT(LammpsInterface::instance()->world(), *matrixSparse_); } else { A = new SPAR_MAT(*matrixSparse_); } const DIAG_MAT & PC = matrixDiagonal_; int niter = maxIterations_; double tol = tol_; int convergence = CG(*A, x, b, PC, niter, tol); if (convergence>0) { stringstream ss; ss << "CG greens_function solve did not converge,"; ss << " iterations: " << niter; ss << " residual: " << tol; throw ATC_Error(ss.str()); } } else if (solverType_ == ITERATIVE_SOLVE) { DENS_VEC b(nVariables_); b = 0.0; b(I) = 1.0; // VECTOR & bb = b; if (parallel_) { A = new PAR_SPAR_MAT(LammpsInterface::instance()->world(), *matrixSparse_); } else { A = new SPAR_MAT(*matrixSparse_); } // const DENS_MAT A = matrixSparse_->dense_copy(); const DIAG_MAT & PC = matrixDiagonal_; int iterations = maxIterations_; int restarts = maxRestarts_; double tol = tol_; DENS_MAT H(maxRestarts_+1, maxRestarts_); DENS_VEC xx(nVariables_); int convergence = GMRES(*A, xx, b, PC, H, restarts, iterations, tol); if (convergence>0) { stringstream ss; ss << "GMRES greens_function solve did not converge,"; ss << " iterations: " << iterations; ss << " residual: " << tol; throw ATC_Error(ss.str()); } x.copy(xx.ptr(),xx.nRows()); } else { const DENS_MAT & invA = matrixInverse_; if (constraintHandlerType_ == CONDENSE_CONSTRAINTS) { set<int>::const_iterator itr; for (itr = fixedSet_.begin(); itr != fixedSet_.end(); itr++) { int ii = *itr; x(ii) = 0; } itr = freeSet_.find(I); if (itr !=freeSet_.end() ) { int j = freeGlobalToCondensedMap_[I]; int i = 0; for (itr = freeSet_.begin(); itr != freeSet_.end(); itr++,i++) { int ii = *itr; x(ii) = invA(j,i); } } } else { for (int i = 0; i < nVariables_; ++i) x(i) = invA(I,i); } } delete A; } } // namespace ATC diff --git a/lib/atc/LinearSolver.h b/lib/atc/LinearSolver.h index eb84976c3..5f456b9a1 100644 --- a/lib/atc/LinearSolver.h +++ b/lib/atc/LinearSolver.h @@ -1,182 +1,183 @@ #ifndef LINEAR_SOLVER_H #define LINEAR_SOLVER_H // ATC includes #include "ATC_TypeDefs.h" #include "MatrixLibrary.h" #include "ATC_Error.h" // other includes #include <set> #include <map> #include "LammpsInterface.h" #include "CG.h" #include "GMRES.h" namespace ATC { /** * @class LinearSolver * @brief a class to solve a system of linear equations * A x = b subject to a set of constraints { x_i = y_i } */ class LinearSolver { public: enum LinearSolveType { AUTO_SOLVE=-1, DIRECT_SOLVE=0, ITERATIVE_SOLVE, ITERATIVE_SOLVE_SYMMETRIC }; enum LinearSolveConstraintHandlingType { + AUTO_HANDLE_CONSTRAINTS=-1, NO_CONSTRAINTS=0, CONDENSE_CONSTRAINTS, PENALIZE_CONSTRAINTS }; /** Constructor */ LinearSolver( // does not assume that A is persistent const SPAR_MAT & A, // lhs matrix "deep" copy const BC_SET & bcs, // constraints const int solverType = AUTO_SOLVE, const int bcHandlerType = -1, bool parallel = false ); LinearSolver( // assumes A is persistent const SPAR_MAT & A, // lhs matrix "shallow" copy const int solverType = AUTO_SOLVE, bool parallel = false ); /** Destructor */ virtual ~LinearSolver() {}; /** (re)initialize - if bcs are provided the lhs matrix is re-configured for the new constraints - if the class is to be reused with new constraints allow_reinitialization must be called before first solve, etc */ void allow_reinitialization(void); // depending on method save a copy of A void set_homogeneous_bcs(void) { homogeneousBCs_ = true;} // for nonlinear solver, solve for increment void initialize(const BC_SET * bcs = NULL); /** solve - solves A x = b - if a "b" is provided it is used as the new rhs */ bool solve(VECTOR & x, const VECTOR & b); /** greens function - returns the solution to a Kronecker delta rhs b = {0 0 .. 1 .. 0 0} and with homogeneous constraints {x_i = 0} */ void greens_function(int I, VECTOR & G_I); /** eigensystem - returns the e-values & e-vectors for constrained system Ax + v x = 0 - if M is provided the eval problem : ( A + v M ) x = 0 is solved*/ void eigen_system(DENS_MAT & eigenvalues, DENS_MAT & eigenvectors, const DENS_MAT * M = NULL); /** access to penalty coefficient - if a penalty method is not being used this returns zero */ double penalty_coefficient(void) const {return penalty_;}; /** change iterative solver parameters */ void set_max_iterations(const int maxIter) { if (solverType_ != ITERATIVE_SOLVE && solverType_ != ITERATIVE_SOLVE_SYMMETRIC ) throw ATC_Error("inappropriate parameter set in LinearSolver"); maxIterations_=maxIter; } void set_tolerance(const double tol) { tol_=tol;} /* access to number of unknowns */ int num_unknowns(void) const { int nUnknowns = nVariables_; if (bcs_) { nUnknowns -= bcs_->size(); } return nUnknowns; } protected: /** flavors */ int solverType_; int constraintHandlerType_ ; /** number of variables = number of rows of matrix */ int nVariables_; /** initialize methods */ bool initialized_,initializedMatrix_,initializedInverse_; bool matrixModified_,allowReinitialization_; bool homogeneousBCs_; void setup(void); void initialize_matrix(void); void initialize_inverse(void); void initialize_rhs(void); /** constraint handling methods to modify the RHS */ void add_matrix_penalty(void); /** penalty */ void partition_matrix(void); /** condense */ /** constraint handling methods to modify the RHS */ void add_rhs_penalty(void); /** penalty */ void add_rhs_influence(void); /** condense */ /** set fixed values */ void set_fixed_values(VECTOR & x); /** constraints container */ const BC_SET * bcs_; /** rhs vector/s */ const VECTOR * rhs_; DENS_VEC rhsDense_; // modified const VECTOR * b_; // points to appropriate rhs /** lhs matrix */ const SPAR_MAT & matrix_; SPAR_MAT matrixCopy_; // a copy that will be modified by penalty methods SPAR_MAT matrixOriginal_; // a copy that is used for re-initialization const SPAR_MAT * matrixSparse_; // points to matrix_ or matrixCopy_ DENS_MAT matrixDense_; // a dense copy for lapack /** partitioned matrix - condense constraints */ DENS_MAT matrixFreeFree_, matrixFreeFixed_; /** maps for free and fixed variables for partitioned matrix - condense */ std::set<int> freeSet_, fixedSet_; std::map<int,int> freeGlobalToCondensedMap_; /** inverse matrix matrix - direct solve */ DENS_MAT matrixInverse_; /** pre-conditioner diagonal of the matrix - iterative solve */ DIAG_MAT matrixDiagonal_; /** penalty coefficient - penalty constraints */ double penalty_; /** max iterations - iterative solve */ int maxIterations_; /** max restarts - GMRES solve */ int maxRestarts_; /** tolerance - iterative solve */ double tol_; /** run solve in parallel */ bool parallel_; }; } // namespace ATC #endif diff --git a/lib/atc/MPI_Wrappers.cpp b/lib/atc/MPI_Wrappers.cpp index d69620ecb..9468644c5 100644 --- a/lib/atc/MPI_Wrappers.cpp +++ b/lib/atc/MPI_Wrappers.cpp @@ -1,333 +1,342 @@ #include "MPI_Wrappers.h" #include "Utility.h" using ATC_Utility::to_string; #include "ATC_Error.h" using ATC::ATC_Error; using std::cout; using std::string; #ifdef ISOLATE_FE #include "Matrix.h" using ATC_Matrix::SparseMatrix; #endif namespace MPI_Wrappers { int rank(MPI_Comm comm) { int rank; MPI_Comm_rank(comm, &rank); return rank; } bool rank_zero(MPI_Comm comm) { return rank(comm)==0;} int size(MPI_Comm comm) { int size; MPI_Comm_size(comm, &size); return size; } bool serial(MPI_Comm comm) { return size(comm) == 0; } void broadcast(MPI_Comm comm, double *buf, int count) { int error = MPI_Bcast(buf, count, MPI_DOUBLE, 0, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in broadcast "+to_string(error)); } void int_broadcast(MPI_Comm comm, int *buf, int count) { int error = MPI_Bcast(buf, count, MPI_INT, 0, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in broadcast "+to_string(error)); } void allsum(MPI_Comm comm, void *send_buf, double *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_DOUBLE, MPI_SUM, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in allsum "+to_string(error)); } void int_allsum(MPI_Comm comm, void *send_buf, int *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_SUM, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_allsum "+to_string(error)); } void int_scansum(MPI_Comm comm, int *send_buf, int *rec_buf, int count) { int error = MPI_Scan(send_buf, rec_buf, count, MPI_INT, MPI_SUM, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_scansum "+to_string(error)); } void allmax(MPI_Comm comm, double *send_buf, double *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_DOUBLE, MPI_MAX, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in allmax "+to_string(error)); } void int_allmax(MPI_Comm comm, int *send_buf, int *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_MAX, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_allmax "+to_string(error)); } void allmin(MPI_Comm comm, double *send_buf, double *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_DOUBLE, MPI_MIN, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in allmax "+to_string(error)); } void int_allmin(MPI_Comm comm, int *send_buf, int *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_MIN, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_allmax "+to_string(error)); } int rank_min(MPI_Comm comm, double *send_buf, double *rec_buf, int count) { int myRank; MPI_Comm_rank(MPI_COMM_WORLD, &myRank); DOUBLE_RANK in[count],out[count]; for (int i = 0; i < count; i++) { in[i].val = send_buf[i]; in[i].rank = myRank; } int error = MPI_Allreduce(in, out, count, MPI_DOUBLE_INT, MPI_MINLOC, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in rank_min "+to_string(error)); for (int i = 0; i < count; i++) { rec_buf[i] = out[i].val; } return out[0].rank; } void int_recv(MPI_Comm comm, int *recv_buf, int max_size, int iproc) { MPI_Status status; MPI_Request request; int tmp, error, recv_size; error = MPI_Irecv(recv_buf,max_size,MPI_INT,iproc,0,comm,&request); error = error && MPI_Send(&tmp,0,MPI_INT,iproc,0,comm); error = error && MPI_Wait(&request,&status); error = error && MPI_Get_count(&status,MPI_DOUBLE,&recv_size); if (error != MPI_SUCCESS) throw ATC_Error("error in int_recv "+to_string(error)); } void recv(MPI_Comm comm, double *recv_buf, int max_size,int iproc) { MPI_Status status; MPI_Request request; int tmp, error, recv_size; error = MPI_Irecv(recv_buf,max_size,MPI_DOUBLE,iproc,0,comm,&request); error = error && MPI_Send(&tmp,0,MPI_INT,iproc,0,comm); error = error && MPI_Wait(&request,&status); error = error && MPI_Get_count(&status,MPI_DOUBLE,&recv_size); if (error != MPI_SUCCESS) throw ATC_Error("error in recv "+to_string(error)); } void int_send(MPI_Comm comm, int *send_buf,int send_size) { MPI_Status status; int tmp, error; error = MPI_Recv(&tmp,0,MPI_INT,0,0,comm,&status); error = error && MPI_Rsend(send_buf,send_size,MPI_INT,0,0,comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_send "+to_string(error)); } void send(MPI_Comm comm, double *send_buf,int send_size) { MPI_Status status; int tmp, error; error = MPI_Recv(&tmp,0,MPI_INT,0,0,comm,&status); error = error && MPI_Rsend(send_buf,send_size,MPI_DOUBLE,0,0,comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_send "+to_string(error)); } void int_scatter(MPI_Comm comm, int *send_buf, int *rec_buf, int count) { int error; int numprocs = size(comm); int sizes[numprocs]; int displacements[numprocs]; for (int i = 0; i < numprocs; ++i) { sizes[i] = 1; displacements[i] = i; } error = MPI_Scatterv(send_buf, sizes, displacements, MPI_INT, rec_buf, count, MPI_INT, 0, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in int_scatter "+to_string(error)); } void allgatherv(MPI_Comm comm, double *send_buf, int send_count, double *rec_buf, int *rec_counts, int *displacements) { int error = MPI_Allgatherv(send_buf, send_count, MPI_DOUBLE, rec_buf, rec_counts, displacements, MPI_DOUBLE, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in allgatherv "+to_string(error)); } void gather(MPI_Comm comm, double send, double* recv) { int send_count = 1; int recv_count = 1; int root = 0; int error = MPI_Gather(&send, send_count, MPI_DOUBLE, recv, recv_count, MPI_DOUBLE, root, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in allgatherv "+to_string(error)); } + void int_allgather(MPI_Comm comm, int send, int* recv) + { + int send_count = 1; + int recv_count = 1; + int error = MPI_Allgather(&send, send_count, MPI_INT, + recv, recv_count, MPI_INT, comm); + if (error != MPI_SUCCESS) throw ATC_Error("error in allgatherv "+to_string(error)); + } + void logical_or(MPI_Comm comm, void *send_buf, int *rec_buf, int count) { int error = MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_LOR, comm); if (error != MPI_SUCCESS) throw ATC_Error("error in logical_or "+to_string(error)); } void barrier(MPI_Comm comm) { int error = MPI_Barrier(comm); if (error != MPI_SUCCESS) throw ATC_Error("error in barrier "+to_string(error)); } void stop(MPI_Comm comm, string msg) { int error = MPI_Barrier(comm); if (error != MPI_SUCCESS) throw ATC_Error("error in barrier "+to_string(error)); throw ATC_Error("...stopping "+msg); } #ifdef ISOLATE_FE void sparse_allsum(MPI_Comm comm,SparseMatrix<double> &toShare) const { toShare.compress(); // initialize MPI information int nProcs = size(comm); int myRank = rank(comm);; int error; // get numbers of rows, columns, rowsCRS, and // sizes (number of nonzero elements in matrix) SparseMatInfo *recInfo = new SparseMatInfo[nProcs]; SparseMatInfo myInfo; myInfo.rows = toShare.nRows(); myInfo.cols = toShare.nCols(); myInfo.rowsCRS = toShare.nRowsCRS(); myInfo.size = toShare.size(); error = MPI_Allgather(&myInfo, 4, MPI_INT, recInfo, 4, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_numrows "+to_string(error)); // adjust row sendcounts because recRowsCRS is off by one int rowCounts[nProcs]; int sizeCounts[nProcs]; // set up total size of receive buffers for Allgatherv calls int totalRowsCRS = 0; int totalSize = 0; // set up array of displacements for Allgatherv calls int rowOffsets[nProcs]; rowOffsets[0] = 0; int sizeOffsets[nProcs]; sizeOffsets[0] = 0; for (int i = 0; i < nProcs; i++) { // find the total number of entries to share in the mpi calls below rowCounts[i] = recInfo[i].rowsCRS + 1; sizeCounts[i] = recInfo[i].size; totalRowsCRS += rowCounts[i]; totalSize += recInfo[i].size; // these already have their 0th slot filled in if (i == 0) continue; rowOffsets[i] = rowOffsets[i-1] + rowCounts[i-1]; sizeOffsets[i] = sizeOffsets[i-1] + sizeCounts[i-1]; } // get actual rows INDEX *rec_ia = new INDEX[totalRowsCRS]; if (toShare.size() == 0) { double dummy[0]; error = MPI_Allgatherv(dummy, 0, MPI_INT, rec_ia, rowCounts, rowOffsets, MPI_INT, lammps_->world); } else error = MPI_Allgatherv(toShare.rows(), rowCounts[myRank], MPI_INT, rec_ia, rowCounts, rowOffsets, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_rowarray "+to_string(error)); // get actual cols INDEX *rec_ja = new INDEX[totalSize]; error = MPI_Allgatherv(toShare.cols(), sizeCounts[myRank], MPI_INT, rec_ja, sizeCounts, sizeOffsets, MPI_INT, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_colarray "+to_string(error)); // get the array of values double *rec_vals = new double[totalSize]; error = MPI_Allgatherv(toShare.ptr(), sizeCounts[myRank], MPI_DOUBLE, rec_vals, sizeCounts, sizeOffsets, MPI_DOUBLE, lammps_->world); if (error != MPI_SUCCESS) throw ATC_Error("error in sparse_allsum_valarray "+to_string(error)); INDEX *rec_ia_proc; INDEX *rec_ja_proc; double *rec_vals_proc; for (int i = 0; i < nProcs; i++) { if (myRank != i) { // deallocated when tempMat is deleted since it wraps them rec_ia_proc = new INDEX[rowCounts[i]]; rec_ja_proc = new INDEX[sizeCounts[i]]; rec_vals_proc = new double[sizeCounts[i]]; // copy the data passed with MPI into the new spots copy(rec_ia + rowOffsets[i], rec_ia + rowOffsets[i] + rowCounts[i], rec_ia_proc); copy(rec_ja + sizeOffsets[i], rec_ja + sizeOffsets[i] + sizeCounts[i], rec_ja_proc); copy(rec_vals + sizeOffsets[i], rec_vals + sizeOffsets[i] + sizeCounts[i], rec_vals_proc); // Does anyone know why we have to declare tempMat here (as well as set it equal to // something) to avoid segfaults? there are still segfaults, but they happen at a much // later stage of the game now (and for less benchmarks overall). SparseMatrix<double> tempMat = SparseMatrix<double>(rec_ia_proc, rec_ja_proc, rec_vals_proc, recInfo[i].size, recInfo[i].rows, recInfo[i].cols, recInfo[i].rowsCRS); toShare += tempMat; } } delete[] recInfo; delete[] rec_ia; delete[] rec_ja; delete[] rec_vals; } #endif void print_msg(MPI_Comm comm, string msg) { if (serial(comm)) { cout << " ATC: " << msg << "\n"; } else { cout << " ATC: P" << rank(comm) << ", " << msg << "\n"; } } void print_msg_once(MPI_Comm comm, string msg, bool prefix, bool endline) { if (rank_zero(comm)) { if (prefix) cout << " ATC: "; cout << msg; if (endline) cout << "\n"; } } } diff --git a/lib/atc/MPI_Wrappers.h b/lib/atc/MPI_Wrappers.h index 691c9d525..37f484c6a 100644 --- a/lib/atc/MPI_Wrappers.h +++ b/lib/atc/MPI_Wrappers.h @@ -1,45 +1,46 @@ #ifndef MPI_WRAPPERS_H #define MPI_WRAPPERS_H #include <iostream> #include <string> #include "mpi.h" namespace MPI_Wrappers { typedef struct {double val; int rank; } DOUBLE_RANK; int rank(MPI_Comm comm); bool rank_zero(MPI_Comm comm); int size(MPI_Comm comm); bool serial(MPI_Comm comm); void broadcast(MPI_Comm comm, double *buf, int count = 1); void int_broadcast(MPI_Comm comm, int *buf, int count = 1); void allsum(MPI_Comm comm, void *send_buf, double *rec_buf, int count = 1); void int_allsum(MPI_Comm comm, void *send_buf, int *rec_buf, int count = 1); void int_scansum(MPI_Comm comm, int *send_buf, int *rec_buf, int count = 1); void allmax(MPI_Comm comm, double *send_buf, double *rec_buf, int count = 1); void int_allmax(MPI_Comm comm, int *send_buf, int *rec_buf, int count = 1); void allmin(MPI_Comm comm, double *send_buf, double *rec_buf, int count = 1); void int_allmin(MPI_Comm comm, int *send_buf, int *rec_buf, int count = 1); int rank_min(MPI_Comm comm, double *send_buf, double *rec_buf, int count); void int_recv(MPI_Comm comm, int *recv_buf, int max_size,int iproc); void recv(MPI_Comm comm, double *recv_buf, int max_size,int iproc); void int_send(MPI_Comm comm, int *send_buf,int send_size); void send(MPI_Comm comm, double *send_buf,int send_size); void allgatherv(MPI_Comm comm, double *send_buf, int send_count, double *rec_buf, int *rec_counts, int *displacements); void gather(MPI_Comm comm, double send, double * recv); + void int_allgather(MPI_Comm comm, int send, int* recv); void logical_or(MPI_Comm comm, void *send_buf, int *rec_buf, int count = 1); void barrier(MPI_Comm comm); void stop(MPI_Comm comm, std::string msg=""); void int_scatter(MPI_Comm comm, int *send_buf, int *rec_buf, int count = 1); // void sparse_allsum(MPI_Comm comm, SparseMatrix<double> &toShare); void print_msg(MPI_Comm comm, std::string msg); void print_msg_once(MPI_Comm comm,std::string msg,bool prefix=true,bool endline=true); } #endif diff --git a/lib/atc/Material.cpp b/lib/atc/Material.cpp index eaeb7c975..22273de2e 100644 --- a/lib/atc/Material.cpp +++ b/lib/atc/Material.cpp @@ -1,776 +1,964 @@ #include "Material.h" #include "ATC_Transfer.h" #include "LammpsInterface.h" #include "ElectronChargeDensity.h" #include "ElectronHeatCapacity.h" #include "ElectronHeatFlux.h" #include "ElectronPhononExchange.h" #include "ElectronDragPower.h" #include "Stress.h" #include "ViscousStress.h" #include "BodyForce.h" #include "ElectronFlux.h" #include <sstream> #include <fstream> #include <vector> using ATC_Utility::command_line; using ATC_Utility::str2dbl; using ATC_Utility::str2int; using std::stringstream; using std::set; using std::fstream; using std::string; using std::vector; namespace ATC { Material::Material() : rhoCp_(0), heatCapacity_(0), electronHeatCapacity_(NULL), massDensity_(0), heatConductivity_(0), electronHeatFlux_(NULL), stress_(NULL), viscousStress_(NULL), bodyForce_(NULL), electronPhononExchange_(NULL), electronDragPower_(NULL), electronFlux_(NULL), permittivity_(1.), invEffectiveMass_(1.), electronEquilibriumDensity_(0), electronRecombinationInvTau_(0), electronChargeDensity_(NULL) { } //-------------------------------------------------------------- // Constructor (parser) //-------------------------------------------------------------- // Example: // material Cu // heat_capacity constant // capacity 1.0 // end // heat_flux linear // conductivity 1.0 // end // electron_heat_flux linear // conductivity 1.0 // end // electron_heat_capacity linear // capacity 1.0 // end // electron_phonon_exchange linear // coefficient 0.1 // end // end Material::Material(string & tag, fstream &fileId) : tag_(tag), rhoCp_(0), heatCapacity_(0), electronHeatCapacity_(NULL), massDensity_(0), heatConductivity_(0), electronHeatFlux_(NULL), stress_(NULL), viscousStress_(NULL), bodyForce_(NULL), electronPhononExchange_(NULL), electronDragPower_(NULL), electronFlux_(NULL), permittivity_(1.), invEffectiveMass_(1.), electronEquilibriumDensity_(0), electronRecombinationInvTau_(0), electronChargeDensity_(NULL) { + /*! \page man_material material + \section syntax + material <tag> <units> \n + <commands> \n + end \n + tag - a unique identifier for the material type which can be referenced in input decks. Multiple materials are specified using different tag regions, terminated with an 'end', in the material file. + units - the LAMMPS units system the material is based on, used as a check against the actual LAMMPS units. AtC units are consistent units using the LAMMPS length, mass, time, charge, and volts. The only units conversion occuring within AtC are LAMMPS to AtC units and charge to volts units. + \section examples + material Argon real + ------- + end + \section description + Starts a section in which material properties can be specified. Materials are organized by material, identified by a tag, and all associated material models are specified within its scope. Unspecified material properties use defaults as indicated or are considered as null. Null material properties contribute no value to integrals using them. Material properties defined which are not part of the physics model are ignored. Functions which are specified correspond to those implemented in the code and there is no mechanism for user-specified material models unless they are added to the main code.\n + \section restrictions + Material models are only used for evaluating finite element integrals with for physics models they are associated with. + \section related + \section default + Default for all material properties is null. The null material using the tag 'null' is the only material defined by default. \n + */ linearFlux_.reset(NUM_FIELDS); linearFlux_ = false; linearSource_.reset(NUM_FIELDS); linearSource_ = true; constantDensity_.reset(NUM_FIELDS); constantDensity_ = false; rhoCp_ = ATC::LammpsInterface::instance()->heat_capacity(); parameters_["heat_capacity"] = rhoCp_; heatCapacity_ = rhoCp_; registry_.insert("heat_capacity"); registry_.insert("thermal_energy"); constantDensity_(TEMPERATURE) = true; constantDensity_(DISPLACEMENT) = true; constantDensity_(VELOCITY) = true; electronDragPower_ = new ElectronDragPower(); vector<string> line; while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line.size() == 1) { if (line[0] == "end") { return; } } + /*! \page man_mat_heat_capacity material heat_capcity + \section syntax + heat_capacity constant\n + capacity <value> \n + end \n + \section description + Overrides use of lattice heat capacity using Dulong-Petit law for continuum regions. \n + \section restrictions + Only valid with AtC models incorporating a phonon temperature: thermal, two-temperature, drift-diffusion + \section related + material + \section default + If no value is given, the Dulong-Petit value for the lattice is used. \n + */ if (line[0] == "heat_capacity") { // over-ride default registry_. insert("heat_capacity"); registry_. insert("thermal_energy"); if (line[1] == "constant") { while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; double value = str2dbl(line[1]); if (line[0] == "capacity") { heatCapacity_ = value; parameters_["heat_capacity"] = heatCapacity_; } } } } + /*! \page man_mat_heat_flux material heat_flux + \section syntax + heat_flux linear\n + conductivity <value> \n + end \n + \section description + Specifies a heat flux proportional to the temperature gradient. \n + \section restrictions + Only valid with AtC models incorporating a phonon temperature: thermal, two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "heat_flux") { registry_. insert("heat_flux"); if (line[1] == "linear") { linearFlux_(TEMPERATURE) = true; while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; double value = str2dbl(line[1]); if (line[0] == "conductivity") { heatConductivity_ = value; } } } } + /*! \page man_mat_electron_heat_flux material electron_heat_flux + \section syntax + electron_heat_flux <null|linear|power_law|thermopower>\n + <parameter> <value> \n + end \n + null - no electron heat flux contributions \n + linear - a heat flux proportional to the temperature gradient, parameter is 'conductivity'\n + power_law - a heat flux proportional to the temperature gradient and ratio of electron to phonon temperatures, parameter is 'conductivity'\n + thermopower - same as power_law but with an addition proportional to the electron current, parameters are 'conductivity' but it also uses the Seebeck coefficient defined elsewhere + \section description + Specifies the form for the electron heat flux. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_heat_flux") { registry_. insert("electron_heat_flux"); if (line[1] == "null") { linearFlux_(ELECTRON_TEMPERATURE) = true; if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFlux(); } else if (line[1] == "linear") { linearFlux_(ELECTRON_TEMPERATURE) = true; if (electronHeatCapacity_) { if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFluxLinear(fileId, parameters_,electronHeatCapacity_); } else { if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFluxLinear(fileId, parameters_); } } else if (line[1] == "power_law") { if (electronHeatCapacity_) { if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFluxPowerLaw(fileId, parameters_,electronHeatCapacity_); } else { if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFluxPowerLaw(fileId, parameters_); } } else if (line[1] == "thermopower") { if (! electronFlux_) { throw ATC_Error( "for thermopower please define electron_flux before electron_heat_flux"); } if (electronHeatFlux_) delete electronHeatFlux_; electronHeatFlux_ = new ElectronHeatFluxThermopower(fileId, parameters_, electronFlux_); } } + /*! \page man_mat_electron_heat_capacity material electron_heat_capacity + \section syntax + electron_heat_capacity <constant|linear> <no_density>\n + capacity <value> \n + end \n + no_density - if this keyword is present, the electron density does not multiply the capacity\n + constant - a constant electron heat flux \n + linear - a heat flux proportional to the electron temperature\n + \section description + Specifies the form for the electron heat capacity. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_heat_capacity") { registry_. insert("electron_heat_capacity"); registry_. insert("electron_thermal_energy"); if (line[1] == "constant") { if ((line.size() == 3) && (line[2] == "no_density")) { if (electronHeatCapacity_) delete electronHeatCapacity_; electronHeatCapacity_ = new ElectronHeatCapacityConstantAddDensity(fileId, parameters_, this); } else { constantDensity_(ELECTRON_TEMPERATURE) = true; if (electronHeatCapacity_) delete electronHeatCapacity_; electronHeatCapacity_ = new ElectronHeatCapacityConstant(fileId, parameters_); } } else if (line[1] == "linear") { if ((line.size() == 3) && line[2] == "no_density") { if (electronHeatCapacity_) delete electronHeatCapacity_; electronHeatCapacity_ = new ElectronHeatCapacityLinearAddDensity(fileId, parameters_, this); } else { if (electronHeatCapacity_) delete electronHeatCapacity_; electronHeatCapacity_ = new ElectronHeatCapacityLinear(fileId, parameters_); } } } + /*! \page man_mat_electron_phonon_exchange material electron_phonon_exchange + \section syntax + electron_phonon_exchange <null|linear|power_law|hertel>\n + <parameter> <value> \n + end \n + null - no electron heat flux contributions \n + linear - an energy exchange proportional to the temperature difference between the electron and phonon temperatures, parameter is 'coefficient'\n + power_law - same as linear, but the temperature difference is raised to a specified power, parameters are 'coefficient' and 'exponent'\n + hertel - exchange proportional to temperature difference to the 5th divided by the electron temperature, the coefficient is a function of the mass enhancement and Debeye temperature, parameters are 'debeye_temperature' and 'mass_enhancement' + \section description + Specifies the form for the electron/phonon heat exchange. \n + \section restrictions + Only valid with AtC models incorporating an electron temperature: two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_phonon_exchange") { registry_. insert("electron_phonon_exchange"); if (line[1] == "null") { if (electronPhononExchange_) delete electronPhononExchange_; electronPhononExchange_ = new ElectronPhononExchange(); } else if (line[1] == "linear") { if (electronPhononExchange_) delete electronPhononExchange_; electronPhononExchange_ = new ElectronPhononExchangeLinear(fileId, parameters_); } else if (line[1] == "power_law") { linearSource_(TEMPERATURE) = false; linearSource_(ELECTRON_TEMPERATURE) = false; if (electronPhononExchange_) delete electronPhononExchange_; electronPhononExchange_ = new ElectronPhononExchangePowerLaw(fileId, parameters_); } else if (line[1] == "hertel") { linearSource_(TEMPERATURE) = false; linearSource_(ELECTRON_TEMPERATURE) = false; if (electronPhononExchange_) delete electronPhononExchange_; electronPhononExchange_ = new ElectronPhononExchangeHertel(fileId,parameters_,this); } } + /*! \page man_mass_density material mass_density + \section syntax + mass_density <no entry|basis|constant>\n + <keyword> <values> \n + end \n + no entry - compute mass density from the lattice using the mass of the first type, no keyword or values\n + basis - compute mass density for the given number of atoms of each type in the lattice, no keyword, values are one integer per type specifying the number of atoms of that type in the lattice\n + constant - prescribed mass density, keyword = density, value = desired mass density + \section description + Specifies the mass density of the system. \n + \section restrictions + Valid for all AtC physics models. + \section related + material + \section default + Compute from the basis. \n + */ else if (line[0] == "mass_density") { // over-ride default registry_. insert("mass_density"); registry_. insert("kinetic_energy"); if (line.size() == 1 ) { // try to calculate from lattice massDensity_ = LammpsInterface::instance()->mass_density(); parameters_["mass_density"] = massDensity_; stringstream ss; ss << "computed mass density : " << massDensity_ ; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } else if (line[1] == "basis") { while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; int n = line.size(); int* numPerType = new int[n]; for (int i = 0; i < n; i++) { numPerType[i] = str2int(line[i]); } massDensity_ = LammpsInterface::instance()->mass_density(numPerType); delete [] numPerType; parameters_["mass_density"] = massDensity_; stringstream ss; ss << "computed mass density (from given basis) : " << massDensity_ ; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } else if (line[1] == "constant") { while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; double value = str2dbl(line[1]); if (line[0] == "density") { massDensity_ = value; parameters_["mass_density"] = massDensity_; } } } } + /*! \page man_mat_stress material stress + \section syntax + stress <linear|cubic|damped_cubic|cauchy_born>\n + <keyword> <values> \n + end \n + null - no electron heat flux contributions \n + linear - a stress tensor proportional to the displacements, keywords are 'modulus' and 'poissons_ratio'\n + cubic - an anisotropic linear stress tensor, keywords are 'c11', 'c12', and 'c44'\n + damped_cubic - same as cubic, with a damping term proportional to the velocity vector, keywords are 'c11', 'c12', 'c44', and the damping parameter 'gamma'\n + cauchy_born - stress tensor is computed using the Cauchy-Born formalism from the lattice and given potential, keywords are 'pairstyle', 'linear' (linearizes the Cauchy-Born relationship), or 'temperature' (the temperature used to determine the Cauchy-Born stress). The 'pairstyle' lines are followed by values of 'lj/cut', 'lj/smooth/linear', and 'eam', the latter two of which are followed on the line by the value for the cut-off radius. The 'lj/cut' and 'lj/smooth/linear' pairstyles are followed on the next line using the keyword 'pair_coeff' followed by value of the pair-coefficients \sigma and \epsilon. + \section description + Specifies the form for the mechanical stress tensor. \n + \section restrictions + Only valid with AtC models incorporating a mechanical stress: elastic + \section related + material + \section default + Null. \n + */ else if (line[0] == "stress") { registry_. insert("stress"); registry_. insert("elastic_energy"); if (line[1] == "linear") { linearFlux_(VELOCITY) = true; linearFlux_(DISPLACEMENT) = true; if (stress_) delete stress_; stress_ = new StressLinearElastic(fileId); } else if (line[1] == "cubic") { linearFlux_(VELOCITY) = true; linearFlux_(DISPLACEMENT) = true; if (stress_) delete stress_; stress_ = new StressCubicElastic(fileId); } else if (line[1] == "damped_cubic") { linearFlux_(VELOCITY) = true; linearFlux_(DISPLACEMENT) = true; if (stress_) delete stress_; stress_ = new StressCubicElasticDamped(fileId); } else if (line[1] == "cauchy-born") { 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.boltzmann = lmp->boltz(); cb.atom_mass = lmp->atom_mass(1); if (stress_) delete stress_; stress_ = new StressCauchyBorn(fileId, cb); } } else if (line[0] == "viscous_stress") { registry_.insert("viscous_stress"); if (line[1] == "constant") { linearFlux_(VELOCITY) = true; if (viscousStress_) delete viscousStress_; viscousStress_ = new ViscousStressConstant(fileId); } } + /*! \page man_body_force material body_force + \section syntax + body_force <electric_field|viscous>\n + <keyword> <values> \n + end \n + electric_field - adds body force proportional to the electric field and charge density, no keywords or values\n + viscous - adds a body force proportional to the velocity vector, keyword = gamma (damping parameter) followed by its value\n + \section description + Specifies body forces acting on the system. \n + \section restrictions + Valid for all AtC mechanical models: elastic + \section related + material + \section default + Null. \n + */ else if (line[0] == "body_force") { registry_. insert("body_force"); if (line.size() > 1) { if (line[1] == "electric_field") { if (bodyForce_) delete bodyForce_; bodyForce_ = new BodyForceElectricField(fileId,parameters_); } else if (line[1] == "viscous") { if (bodyForce_) delete bodyForce_; bodyForce_ = new BodyForceViscous(fileId,parameters_); } else { if (bodyForce_) delete bodyForce_; bodyForce_ = new BodyForce(); } } else { if (bodyForce_) delete bodyForce_; bodyForce_ = new BodyForce(); } } else if (line[0] == "electron_flux") { registry_. insert("electron_flux"); if (line[1] == "null") { linearFlux_(ELECTRON_DENSITY) = true; if (electronFlux_) delete electronFlux_; electronFlux_ = new ElectronFlux(); } else if (line[1] == "linear") { linearFlux_(ELECTRON_DENSITY) = true; if (electronFlux_) delete electronFlux_; electronFlux_ = new ElectronFluxLinear(fileId, parameters_); } else if (line[1] == "thermopower") { if (electronFlux_) delete electronFlux_; electronFlux_ = new ElectronFluxThermopower(fileId, parameters_); } else if (line[1] == "convection") { if (electronFlux_) delete electronFlux_; electronFlux_ = new ElectronFluxConvection(fileId, parameters_); } } + /*! \page man_electric_field material electric_field + \section syntax + electric_field linear\n + permittivity <value> \n + end \n + Provide a value for the permittivity or use LAMMPS' value if no value is given.\n + \section description + Specifies the electric displacement vector to be proportional to the electric field. \n + \section restrictions + Valid for AtC physics models using electric fields: fem_efield, drift-diffusion + \section related + material + \section default + Use LAMMPS' permittivity. \n + */ else if (line[0] == "electric_field") { registry_. insert("electric_field"); registry_. insert("electric_displacement"); if (line[1] == "linear") { linearFlux_(ELECTRIC_POTENTIAL) = true; while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; if (line[0] == "permittivity") { // if no value given use lammps dielectric constant if (line.size() == 1 ) { permittivity_ = LammpsInterface::instance()->dielectric(); } else { double value = str2dbl(line[1]); permittivity_ = value; } // convert relative permitivity (dielectric) to abs internal units stringstream ss; - ss << "permittivity : " << permittivity_ ; + ss << "permittivity: relative= " << permittivity_ ; permittivity_ *= LammpsInterface::instance()->epsilon0(); - ss << " -> " << permittivity_ ; + ss << ", absolute= " << permittivity_ ; + ss << ", lattice constant= " << LammpsInterface::instance()->max_lattice_constant() ; + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); LammpsInterface::UnitsType utype = LammpsInterface::instance()->units_style(); if ( utype != LammpsInterface::REAL && utype != LammpsInterface::METAL) { ATC::LammpsInterface::instance()->print_msg_once("WARNING: must use a unit system where: [Energy/force] = [Length] and [charge] = e"); // note this is so that: perm0/e = 1 / (Epotential_units * Length) // our charge densities are multiples of the elemental charge } parameters_["permittivity"] = permittivity_; } } } } else if (line[0] == "effective_mass") { registry_. insert("inv_effective_mass"); if (line[1] == "constant") { linearFlux_(ELECTRON_WAVEFUNCTION) = true; while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; if (line[0] == "inv_effective_mass") { double value = str2dbl(line[1]); invEffectiveMass_ = value; // 1/m* = inv_eff_mass/m_e // convert to hbar^2 / 2 / m* / e //double hbar = LammpsInterface::instance()->hbar(); //invEffectiveMass_ *= 0.5*hbar*hbar; // m_e in units [eV-ps^2/A^2] : 5.68562958414706e-32 double scale = 3.80998192145007; // units [V A^2] invEffectiveMass_ *= scale; parameters_["inverse_effective_mass"] = invEffectiveMass_; } } } } else if (line[0] == "electron_drag") { registry_.insert("electron_drag_power"); registry_.insert("electron_drag_coefficient"); if (line[1] == "null") { if (electronDragPower_) delete electronDragPower_; electronDragPower_ = new ElectronDragPower(); } else if (line[1] == "linear") { if (electronDragPower_) delete electronDragPower_; electronDragPower_ = new ElectronDragPowerLinear(fileId, parameters_, this); } } else if (line[0] == "electron_recombination") { registry_. insert("electron_recombination"); if (line[1] == "linear") { while(fileId.good()) { command_line(fileId, line); if (line.size() == 0) continue; if (line[0] == "end") break; double value = str2dbl(line[1]); if (line[0] == "inv_relaxation_time") { electronRecombinationInvTau_ = value; parameters_["inv_relaxation_time"] = electronRecombinationInvTau_; } else if (line[0] == "equilibrium_carrier_density") { electronEquilibriumDensity_ = value; parameters_["equilibrium_carrier_density"] = electronEquilibriumDensity_; } } } } + /*! \page man_mat_electron_density material electron_density + \section syntax + electron_density <null|linear|interpolation|exponential|fermi_dirac>\n + <keyword> <values> \n + end \n + null - no electron density constitutive model, uses the state variable \n + linear - density is proportional to the electric field, keyword is 'coefficient' followed by its value\n + interpolation - interpolates in a look-up table contained in a file provided after the 'interpolation' word, keywords are 'scale' and 'number_of_points', followed by their values \n + exponential - density is based on Boltzmann statistics for the electric potential above an activation energy, keywords are 'intrinsic_concentration', 'intrinsic_energy', and reference_temperature', followed by their values\n + fermi_dirac - density is based on Fermi-Dirac statistics for the electric potential relative to an activation energy, keywords are 'fermi_energy', 'reference_temperature', 'band_edge', 'donor_ionization_energy', and 'donor_concentration' + \section description + Specifies the form for the electron density. \n + \section restrictions + Only valid with AtC models incorporating an electrons: electrostatic, two-temperature, drift-diffusion + \section related + material + \section default + Null. \n + */ else if (line[0] == "electron_density") { // density is converted to charge registry_. insert("electron_charge_density"); if (line[1] == "null") { if (electronChargeDensity_) delete electronChargeDensity_; electronChargeDensity_ = new ElectronChargeDensity(); } else if (line[1] == "linear") { linearSource_(ELECTRIC_POTENTIAL) = false; if (electronChargeDensity_) delete electronChargeDensity_; electronChargeDensity_ = new ElectronChargeDensityLinear(fileId, parameters_); } else if (line[1] == "interpolation") { linearSource_(ELECTRIC_POTENTIAL) = false; if (electronChargeDensity_) delete electronChargeDensity_; electronChargeDensity_ = new ElectronChargeDensityInterpolation(fileId, parameters_); } else if (line[1] == "exponential") { linearSource_(ELECTRIC_POTENTIAL) = false; if (electronChargeDensity_) delete electronChargeDensity_; electronChargeDensity_ = new ElectronChargeDensityExponential(fileId, parameters_); } else if (line[1] == "fermi_dirac") { registry_. insert("band_edge_potential"); //linearSource_(ELECTRIC_POTENTIAL) = false; // treated as constant if (electronChargeDensity_) delete electronChargeDensity_; electronChargeDensity_ = new ElectronChargeDensityFermiDirac(fileId, parameters_); } else { throw ATC_Error("unrecognized material function type: "+line[0]+" - "+line[1]); } } else { throw ATC_Error( "unrecognized material function: "+line[0]); } } } //-------------------------------------------------------------------- Material::~Material() { if (electronDragPower_) delete electronDragPower_; if (electronChargeDensity_) delete electronChargeDensity_; if (electronHeatCapacity_) delete electronHeatCapacity_; if (electronHeatFlux_) delete electronHeatFlux_; if (electronFlux_) delete electronFlux_; if (stress_) delete stress_; if (viscousStress_) delete viscousStress_; if (bodyForce_) delete bodyForce_; if (electronPhononExchange_) delete electronPhononExchange_; } //--------------------------------------------------------------------- void Material::initialize(){if (stress_) stress_->initialize();} void Material::heat_capacity( const FIELD_MATS & fields, DENS_MAT & capacity) const { const DENS_MAT & T = (fields.find(TEMPERATURE))->second; int nNodes = T.nRows(); capacity.reset(nNodes,1); capacity = heatCapacity_; }; //--------------------------------------------------------------------- void Material::thermal_energy( const FIELD_MATS &fields, DENS_MAT &energy) const { const DENS_MAT & T = (fields.find(TEMPERATURE))->second; energy = heatCapacity_ * T; }; //--------------------------------------------------------------------- void Material::electron_heat_capacity( const FIELD_MATS & fields, DENS_MAT & capacity) const { electronHeatCapacity_->electron_heat_capacity(fields,capacity); }; //--------------------------------------------------------------------- void Material::D_electron_heat_capacity( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT_VEC & Dcapacity) const { electronHeatCapacity_->D_electron_heat_capacity(fields,gradFields,Dcapacity); }; //--------------------------------------------------------------------- void Material::electron_thermal_energy( const FIELD_MATS &fields, DENS_MAT &energy) const { electronHeatCapacity_->electron_thermal_energy(fields,energy); }; //--------------------------------------------------------------------- void Material::mass_density( const FIELD_MATS &fields, DENS_MAT &density) const { int nNodes = 0; FIELD_MATS::const_iterator field = fields.find(MASS_DENSITY); if (field != fields.end()) { const DENS_MAT & d = field->second; nNodes = d.nRows(); } else { FIELD_MATS::const_iterator field = fields.find(VELOCITY); if (field != fields.end()) { const DENS_MAT & v = field->second; nNodes = v.nRows(); } } density.reset(nNodes,1); density = massDensity_; }; //--------------------------------------------------------------------- void Material::electron_mass_density( const FIELD_MATS &fields, DENS_MAT &density) const { int nNodes = 0; FIELD_MATS::const_iterator field = fields.find(ELECTRON_DENSITY); //if (field != fields.end()) { const DENS_MAT & d = field->second; nNodes = d.nRows(); //} density.reset(nNodes,1); inv_effective_mass(fields,density); density = d.div_by_element(density); }; //--------------------------------------------------------------------- void Material::kinetic_energy( const FIELD_MATS &fields, DENS_MAT &energy) const { FIELD_MATS::const_iterator field = fields.find(VELOCITY); if (field != fields.end()) { const DENS_MAT & v = field->second; energy = 0.5*massDensity_*v; energy *= v; } else { energy = 0.; } }; //--------------------------------------------------------------------- void Material::permittivity( const FIELD_MATS &fields, DENS_MAT &density) const { const DENS_MAT & phi = (fields.find(ELECTRIC_POTENTIAL))->second; int nNodes = phi.nRows(); density.reset(nNodes,1); density = permittivity_; }; //--------------------------------------------------------------------- void Material::band_edge_potential( const FIELD_MATS &fields, DENS_MAT &density) const { electronChargeDensity_->band_edge_potential(fields,density); }; //--------------------------------------------------------------------- void Material::inv_effective_mass( const FIELD_MATS &fields, DENS_MAT &density) const { const DENS_MAT & phi = (fields.find(ELECTRON_DENSITY))->second; int nNodes = phi.nRows(); density.reset(nNodes,1); density = invEffectiveMass_; }; //--------------------------------------------------------------------- void Material::heat_flux( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT_VEC & flux) const { const DENS_MAT_VEC & dT = (gradFields.find(TEMPERATURE))->second; flux[0] = -heatConductivity_* dT[0]; flux[1] = -heatConductivity_* dT[1]; flux[2] = -heatConductivity_* dT[2]; } //--------------------------------------------------------------------- void Material::electron_heat_flux( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT_VEC & flux) const { electronHeatFlux_->electron_heat_flux(fields,gradFields,flux); } //--------------------------------------------------------------------- void Material::electron_heat_convection( const FIELD_MATS & fields, DENS_MAT_VEC & flux) const { electronHeatFlux_->electron_heat_convection(fields,flux); } //--------------------------------------------------------------------- bool Material::electron_phonon_exchange( const FIELD_MATS & fields, DENS_MAT & flux) const { return electronPhononExchange_->electron_phonon_exchange(fields,flux); } //--------------------------------------------------------------------- void Material::electron_drag_velocity_coefficient( const FIELD_MATS &fields, DENS_MAT & dragCoef) const { electronDragPower_->electron_drag_velocity_coefficient(fields,dragCoef); } //--------------------------------------------------------------------- bool Material::electron_drag_power( const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT & power) const { return electronDragPower_->electron_drag_power(fields,gradFields,power); } //--------------------------------------------------------------------- bool Material::electron_recombination( const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT & recombination) const { // 1/tau (n - n0) const DENS_MAT & n = (fields.find(ELECTRON_DENSITY))->second; recombination = n; recombination -= electronEquilibriumDensity_; recombination *= -electronRecombinationInvTau_; return true; } //--------------------------------------------------------------------- bool Material::electron_charge_density( const FIELD_MATS &fields, DENS_MAT & density) const { return electronChargeDensity_->electron_charge_density(fields,density); }; //--------------------------------------------------------------------- void Material::D_electron_charge_density(const FieldName fieldName, const FIELD_MATS &fields, DENS_MAT & D_density) const { electronChargeDensity_->D_electron_charge_density(fieldName,fields,D_density); }; //--------------------------------------------------------------------- bool Material::body_force( const FIELD_MATS &fields, DENS_MAT & density) const { return bodyForce_->body_force(fields,density); }; //--------------------------------------------------------------------- void Material::stress( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT_VEC & stress) const { stress_->stress(fields,gradFields,stress); } //--------------------------------------------------------------------- void Material::elastic_energy( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT & energy) const { stress_->elastic_energy(fields,gradFields,energy); } //--------------------------------------------------------------------- void Material::viscous_stress( const FIELD_MATS & fields, const GRAD_FIELD_MATS & gradFields, DENS_MAT_VEC & stress) const { viscousStress_->viscous_stress(fields,gradFields,stress); } //--------------------------------------------------------------------- void Material::viscosity( const FIELD_MATS &fields, DENS_MAT &coefs) const { viscousStress_->viscosity(fields,coefs); } //--------------------------------------------------------------------- void Material::electron_flux( const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) const { electronFlux_->electron_flux(fields,gradFields,flux); } //--------------------------------------------------------------------- void Material::electric_field( const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) const { // E = - grad \phi const DENS_MAT_VEC & dphi = (gradFields.find(ELECTRIC_POTENTIAL))->second; flux[0] = -1.0* dphi[0]; flux[1] = -1.0* dphi[1]; flux[2] = -1.0* dphi[2]; } //--------------------------------------------------------------------- void Material::electric_displacement( const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, DENS_MAT_VEC &flux) const { // D = - permitivity grad \phi const DENS_MAT_VEC & dphi = (gradFields.find(ELECTRIC_POTENTIAL))->second; flux[0] = -permittivity_* dphi[0]; flux[1] = -permittivity_* dphi[1]; flux[2] = -permittivity_* dphi[2]; } //--------------------------------------------------------------------- } // end namespace diff --git a/lib/atc/Matrix.h b/lib/atc/Matrix.h index 179bea52b..076ed1867 100644 --- a/lib/atc/Matrix.h +++ b/lib/atc/Matrix.h @@ -1,1019 +1,999 @@ #ifndef MATRIX_H #define MATRIX_H #include "MatrixDef.h" namespace ATC_matrix { static const int myPrecision = 15; /** * @class Matrix * @brief Base class for linear algebra subsystem */ template<typename T> class Matrix { protected: Matrix(const Matrix &c); public: Matrix() {} virtual ~Matrix() {} //* stream output functions - void print(std::ostream &o) const { o << to_string(); } - void print(std::ostream &o, const std::string &name) const; + void print(std::ostream &o, int p=myPrecision) const { o << this->to_string(p); } + void print(std::ostream &o, const std::string &name, int p=myPrecision) const; friend std::ostream& operator<<(std::ostream &o, const Matrix<T> &m){m.print(o); return o;} void print() const; - virtual void print(const std::string &name) const; - virtual std::string to_string() const; + virtual void print(const std::string &name, int p = myPrecision) const; + virtual std::string to_string(int p = myPrecision) const; // element by element operations DenseMatrix<T> operator/(const Matrix<T>& B) const; DenseMatrix<T> pow(int n) const; DenseMatrix<T> pow(double n) const; // functions that return a copy DenseMatrix<T> transpose() const; void row_partition(const std::set<int> & rowsIn, std::set<int> & rows, std::set<int> & colsC, DenseMatrix<T> & A1, DenseMatrix<T> & A2, bool complement=true) const; std::set<int> row_partition(const std::set<int> & rows, DenseMatrix<T> & A1, DenseMatrix<T> & A2) const; void map(const std::set<int>& rows, const std::set<int>& cols, DenseMatrix<T> & A) const; void insert(const std::set<int>& rows, const std::set<int>& cols, const DenseMatrix<T> & A); void assemble(const std::set<int>& rows, const std::set<int>& cols, const DenseMatrix<T> & A); // matrix to scalar functions T sum() const; T stdev() const; T max() const; T min() const; T maxabs() const; T minabs() const; T norm() const; T norm_sq() const; T mean() const; T dot(const Matrix<T> &r) const; T trace() const; // row and column operations T row_sum (INDEX i=0) const { return row(*this,i).sum(); } T row_mean (INDEX i=0) const { return row(*this,i).mean(); } T row_norm (INDEX i=0) const { return row(*this,i).norm(); } T row_min (INDEX i=0) const { return row(*this,i).min(); } T row_max (INDEX i=0) const { return row(*this,i).max(); } T row_stdev(INDEX i=0) const { return row(*this,i).stdev(); } T col_sum (INDEX i=0) const { return column(*this,i).sum(); } T col_mean (INDEX i=0) const { return column(*this,i).mean(); } T col_norm (INDEX i=0) const { return column(*this,i).norm(); } T col_min (INDEX i=0) const { return column(*this,i).min(); } T col_max (INDEX i=0) const { return column(*this,i).max(); } T col_stdev(INDEX i=0) const { return column(*this,i).stdev(); } // pure virtual functions (required to implement these) --------------------- //* reference index operator virtual T& operator()(INDEX i, INDEX j)=0; //* value index operator virtual T operator()(INDEX i, INDEX j)const=0; //* value flat index operator virtual T& operator [](INDEX i)=0; //* reference flat index operator virtual T operator [](INDEX i) const=0; //* returns the # of rows virtual INDEX nRows() const=0; //* returns the # of columns virtual INDEX nCols() const=0; //* returns a pointer to the data (dangerous) virtual T * ptr() const=0; //* resizes the matrix, copy what fits default to OFF virtual void resize(INDEX nRows, INDEX nCols=1, bool copy=false)=0; //* resizes the matrix, zero it out default to ON virtual void reset(INDEX nRows, INDEX nCols=1, bool zero=true)=0; //* resizes and copies data virtual void copy(const T * ptr, INDEX nRows, INDEX nCols=1)=0; //* create restart file virtual void write_restart(FILE *f) const=0; //* writes a matlab command to recreate this in a variable named s virtual void matlab(std::ostream &o, const std::string &s="M") const; //* writes a mathematica command to recreate this in a variable named s virtual void mathematica(std::ostream &o, const std::string &s="M") const; // output to matlab, with variable name s void matlab(const std::string &s="M") const; // output to mathematica, with variable name s void mathematica(const std::string &s="M") const; Matrix<T>& operator+=(const Matrix &r); Matrix<T>& operator-=(const Matrix &r); Matrix<T>& operator*=(const Matrix<T>& R); Matrix<T>& operator/=(const Matrix<T>& R); Matrix<T>& operator+=(const T v); Matrix<T>& operator-=(const T v); Matrix<T>& operator*=(const T v); Matrix<T>& operator/=(T v); Matrix<T>& divide_zero_safe(const Matrix<T>& B); Matrix<T>& operator=(const T &v); Matrix<T>& operator=(const Matrix<T> &c); virtual void set_all_elements_to(const T &v); //* adds a matrix scaled by factor s to this one. void add_scaled(const Matrix<T> &A, const T& s); //* sets all elements to zero Matrix& zero(); //* sets matrix to the identity Matrix& identity(int nrows=0); //* returns the total number of elements virtual INDEX size() const; //* returns true if (i,j) is within the range of the matrix bool in_range(INDEX i, INDEX j) const; //* returns true if the matrix size is rs x cs bool is_size(INDEX rs, INDEX cs) const; //* returns true if the matrix is square and not empty bool is_square() const; //* returns true if Matrix, m, is the same size as this bool same_size(const Matrix &m) const; //* returns true if Matrix a and Matrix b are the same size static bool same_size(const Matrix<T> &a, const Matrix<T> &b); //* returns true if Matrix a rows are equal to Matrix b cols static bool cols_equals_rows(const Matrix<T> &a, const Matrix<T> &b); //* checks if memory is contiguous, only can be false for clone vector virtual bool memory_contiguous() const { return true; } //* checks if all values are within the prescribed range virtual bool check_range(T min, T max) const; protected: - virtual void _set_equal(const Matrix<T> &r); + virtual void _set_equal(const Matrix<T> &r) = 0; }; //* Matrix operations //@{ //* Sets C as b*C + a*A[tranpose?]*B[transpose?] template<typename T> void MultAB(const Matrix<T> &A, const Matrix<T> &B, DenseMatrix<T> &C, bool At=0, bool Bt=0, T a=1, T b=0); //* performs a matrix-vector multiply template<typename T> void MultMv(const Matrix<T> &A, const Vector<T> &v, DenseVector<T> &c, const bool At, T a, T b); // returns the inverse of a double precision matrix DenseMatrix<double> inv(const Matrix<double>& A); // returns the eigensystem of a pair of double precision matrices DenseMatrix<double> eigensystem(const Matrix<double>& A, const Matrix<double>& B, DenseMatrix<double> & eVals, bool normalize = true); // returns the polar decomposition of a double precision matrix DenseMatrix<double> polar_decomposition(const Matrix<double>& A, DenseMatrix<double> & rotation, DenseMatrix<double> & stretch, bool leftRotation = true); //* returns the trace of a matrix template<typename T> T trace(const Matrix<T>& A) { return A.trace(); } //* computes the determinant of a square matrix double det(const Matrix<double>& A); //* Returns the maximum eigenvalue of a matrix. double max_eigenvalue(const Matrix<double>& A); //@} //----------------------------------------------------------------------------- // computes the sum of the difference squared of each element. //----------------------------------------------------------------------------- template<typename T> double sum_difference_squared(const Matrix<T>& A, const Matrix<T> &B) { SSCK(A, B, "sum_difference_squared"); double v=0.0; for (INDEX i=0; i<A.size(); i++) { double d = A[i]-B[i]; v += d*d; } return v; } //----------------------------------------------------------------------------- //* Operator for Matrix-matrix product //----------------------------------------------------------------------------- template<typename T> DenseMatrix<T> operator*(const Matrix<T> &A, const Matrix<T> &B) { DenseMatrix<T> C(0,0,false); MultAB(A,B,C); return C; } //----------------------------------------------------------------------------- //* Multiply a Matrix by a scalar //----------------------------------------------------------------------------- template<typename T> DenseMatrix<T> operator*(const Matrix<T> &M, const T s) { DenseMatrix<T> R(M); return R*=s; } //----------------------------------------------------------------------------- //* Multiply a Matrix by a scalar template<typename T> DenseMatrix<T> operator*(const T s, const Matrix<T> &M) { DenseMatrix<T> R(M); return R*=s; } //----------------------------------------------------------------------------- //* inverse scaling operator - must always create memory template<typename T> DenseMatrix<T> operator/(const Matrix<T> &M, const T s) { DenseMatrix<T> R(M); return R*=(1.0/s); // for integer types this may be worthless } //----------------------------------------------------------------------------- //* Operator for Matrix-matrix sum template<typename T> DenseMatrix<T> operator+(const Matrix<T> &A, const Matrix<T> &B) { DenseMatrix<T> C(A); return C+=B; } //----------------------------------------------------------------------------- //* Operator for Matrix-matrix subtraction template<typename T> DenseMatrix<T> operator-(const Matrix<T> &A, const Matrix<T> &B) { DenseMatrix<T> C(A); return C-=B; } /****************************************************************************** * Template definitions for class Matrix ******************************************************************************/ //----------------------------------------------------------------------------- //* performs a matrix-matrix multiply with general type implementation template<typename T> void MultAB(const Matrix<T> &A, const Matrix<T> &B, DenseMatrix<T> &C, const bool At, const bool Bt, T a, T b) { const INDEX sA[2] = {A.nRows(), A.nCols()}; // m is sA[At] k is sA[!At] const INDEX sB[2] = {B.nRows(), B.nCols()}; // k is sB[Bt] n is sB[!Bt] const INDEX M=sA[At], K=sB[Bt], N=sB[!Bt]; // M is the number of rows in A or Atrans (sA[At]), // K is the number of rows in B or Btrans (sB[Bt], sA[!At]), // N is the number of columns in B or Btrans (sB[!Bt]). GCK(A, B, sA[!At]!=K, "MultAB<T> shared index not equal size"); if (!C.is_size(M,N)) { C.resize(M,N); // set size of C C.zero(); } else C *= b; // Zero C for (INDEX p=0; p<M; p++) { INDEX p_times_At = p*At; INDEX p_times_notAt = p*!At; for (INDEX q=0; q<N; q++) { INDEX q_times_Bt = q*Bt; INDEX q_times_notBt = q*!Bt; for (INDEX r=0; r<K; r++) { INDEX ai = p_times_notAt+r*At; INDEX aj = p_times_At+r*!At; INDEX bi = r*!Bt+q_times_Bt; INDEX bj = r*Bt+q_times_notBt; T a_entry = A(ai, aj); T b_entry = B(bi, bj); T mult = a_entry * b_entry; C(p,q) += mult; } } } } //----------------------------------------------------------------------------- //* output operator template<typename T> -std::string Matrix<T>::to_string() const +std::string Matrix<T>::to_string(int p) const { std::string s; - for (INDEX i=0; i<nRows(); i++) - { + for (INDEX i=0; i<nRows(); i++) { if (i) s += '\n'; - for (INDEX j=0; j<nCols(); j++) - { - if (j) s+= '\t'; - s += ATC_Utility::to_string((*this)(i,j),myPrecision)+" "; + for (INDEX j=0; j<nCols(); j++) { + //if (j) s+= '\t'; + s += ATC_Utility::to_string((*this)(i,j),p)+" "; } } return s; } //----------------------------------------------------------------------------- //* output operator that wraps the matrix in a nice labeled box template<typename T> -void Matrix<T>::print(std::ostream &o, const std::string &name) const +void Matrix<T>::print(std::ostream &o, const std::string &name, int p) const { o << "------- Begin "<<name<<" -----------------\n"; - this->print(o); + this->print(o,p); o << "\n------- End "<<name<<" -------------------\n"; } //----------------------------------------------------------------------------- //* print operator, use cout by default template<typename T> void Matrix<T>::print() const { print(std::cout); } //----------------------------------------------------------------------------- //* named print operator, use cout by default template<typename T> -void Matrix<T>::print(const std::string &name) const +void Matrix<T>::print(const std::string &name, int p) const { - print(std::cout, name); + print(std::cout, name, p); } //----------------------------------------------------------------------------- //* element by element division template<typename T> DenseMatrix<T> Matrix<T>::operator/ (const Matrix<T>& B) const { SSCK(*this, B, "Matrix<T>::Operator/"); DenseMatrix<T> R(*this); R /= B; return R; } //----------------------------------------------------------------------------- //* element-wise raise to a power template<typename T> DenseMatrix<T> Matrix<T>::pow(int n) const { DenseMatrix<T> R(*this); int sz=this->size(); for(INDEX i=0; i<sz; i++) { double val = R[i]; for (int k=1; k<n; k++) val *= R[i]; for (int k=n; k<1; k++) val /= R[i]; R[i] = val; } return R; } //----------------------------------------------------------------------------- //* element-wise raise to a power template<typename T> DenseMatrix<T> Matrix<T>::pow(double n) const { DenseMatrix<T> R(*this); int sz=this->size(); for(INDEX i=0; i<sz; i++) { double val = R[i]; R[i] = pow(val,n); } return R; } //----------------------------------------------------------------------------- //* returns the transpose of this matrix (makes a copy) template <typename T> DenseMatrix<T> Matrix<T>::transpose() const { DenseMatrix<T> t(this->nCols(), this->nRows()); int szi = this->nRows(); int szj = this->nCols(); for (INDEX i = 0; i < szi; i++) for (INDEX j = 0; j < szj; j++) t(j,i) = (*this)(i,j); return t; } //----------------------------------------------------------------------------- //* returns the transpose of a matrix (makes a copy) template <typename T> DenseMatrix<T> transpose(const Matrix<T> &A) { return A.transpose(); } //----------------------------------------------------------------------------- //* Returns the sum of all matrix elements template<typename T> T Matrix<T>::sum() const { if (!size()) return T(0); T v = (*this)[0]; for (INDEX i=1; i<this->size(); i++) v += (*this)[i]; return v; } //----------------------------------------------------------------------------- //* Returns the standard deviation of the matrix template<typename T> T Matrix<T>::stdev() const { GCHK(this->size()<2, "Matrix::stdev() size must be > 1"); T mean = this->mean(); T diff = (*this)[0]-mean; T stdev = diff*diff; for (INDEX i=1; i<this->size(); i++) { diff = (*this)[i]-mean; stdev += diff*diff; } return sqrt(stdev/T(this->size()-1)); } //----------------------------------------------------------------------------- //* Returns the maximum of the matrix template<typename T> T Matrix<T>::max() const { GCHK(!this->size(), "Matrix::max() size must be > 0"); T v = (*this)[0]; for (INDEX i=1; i<this->size(); i++) v = std::max(v, (*this)[i]); return v; } //----------------------------------------------------------------------------- //* Returns the minimum of the matrix template<typename T> T Matrix<T>::min() const { GCHK(!this->size(), "Matrix::min() size must be > 0"); T v = (*this)[0]; for (INDEX i=1; i<this->size(); i++) v = std::min(v, (*this)[i]); return v; } //----------------------------------------------------------------------------- //* Returns the maximum absolute value of the matrix template<typename T> T Matrix<T>::maxabs() const { GCHK(!this->size(), "Matrix::maxabs() size must be > 0"); T v = (*this)[0]; for (INDEX i=1; i<this->size(); i++) v = ATC_Utility::max_abs(v, (*this)[i]); return v; } //----------------------------------------------------------------------------- //* Returns the minimum absoute value of the matrix template<typename T> T Matrix<T>::minabs() const { GCHK(!this->size(), "Matrix::minabs() size must be > 0"); T v = (*this)[0]; for (INDEX i=1; i<this->size(); i++) v = ATC_Utility::min_abs(v, (*this)[i]); return v; } //----------------------------------------------------------------------------- //* returns the L2 norm of the matrix template<typename T> T Matrix<T>::norm() const { GCHK(!this->size(), "Matrix::norm() size must be > 0"); return sqrt(dot(*this)); } //----------------------------------------------------------------------------- //* returns the L2 norm of the matrix template<typename T> T Matrix<T>::norm_sq() const { GCHK(!this->size(), "Matrix::norm() size must be > 0"); return dot(*this); } //----------------------------------------------------------------------------- //* returns the average of the matrix template<typename T> T Matrix<T>::mean() const { GCHK(!this->size(), "Matrix::mean() size must be > 0"); return sum()/T(this->size()); } //----------------------------------------------------------------------------- //* Returns the dot product of two vectors template<typename T> T Matrix<T>::dot(const Matrix<T>& r) const { SSCK(*this, r, "Matrix<T>::dot"); if (!this->size()) return T(0); T v = r[0]*(*this)[0]; for (INDEX i=1; i<this->size(); i++) v += r[i]*(*this)[i]; return v; } //----------------------------------------------------------------------------- // returns the sum of the matrix diagonal //----------------------------------------------------------------------------- template<typename T> T Matrix<T>::trace() const { const INDEX N = std::min(nRows(),nCols()); if (!N) return T(0); T r = (*this)(0,0); for (INDEX i=0; i<N; i++) r += (*this)(i,i); return r; } //----------------------------------------------------------------------------- //* Adds a matrix to this one template<typename T> Matrix<T>& Matrix<T>::operator+=(const Matrix &r) { SSCK(*this, r, "operator+= or operator +"); int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i]+=r[i]; return *this; } //----------------------------------------------------------------------------- // subtracts a matrix from this one //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator-=(const Matrix &r) { SSCK(*this, r, "operator-= or operator -"); int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i]-=r[i]; return *this; } //----------------------------------------------------------------------------- // multiplies each element in this by the corresponding element in R //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator*=(const Matrix<T>& R) { if ((R.nCols()==1) && (this->nCols()>1)) { // multiply every entry in a row by the same value 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) *= R[i]; } } else if (((R.nCols()==R.size()) && (R.nRows()==R.size())) && !((this->nCols()==this->size()) && (this->nRows()==this->size()))){ 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) *= R[i]; } } else { // multiply each entry by a different value int sz = this->size(); for (INDEX i = 0; i < sz; i++) { (*this)[i] *= R[i]; } } return *this; } //----------------------------------------------------------------------------- // divides each element in this by the corresponding element in R //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator/=(const Matrix<T>& R) { if ((R.nCols()==1) && (this->nCols()>1)) { // divide every entry in a row by the same value 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) /= R[i]; } } else { // divide each entry by a different value SSCK(*this, R, "operator/= or operator/"); int sz = this->size(); for(INDEX i = 0; i < sz; i++) { GCHK(fabs(R[i])==0,"Operator/: division by zero"); (*this)[i] /= R[i]; } } return *this; } //----------------------------------------------------------------------------- // divides each element in this by the corresponding element in R unless zero //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::divide_zero_safe(const Matrix<T>& R) { if ((R.nCols()==1) && (this->nCols()>1)) { // divide every entry in a row by the same value int szi = this->nRows(); int szj = this->nCols(); for (INDEX i = 0; i < szi; i++) for (INDEX j = 0; j < szj; j++) { if(fabs(R[i])!=0) { (*this)(i,j) /= R[i]; } } } else { // divide each entry by a different value SSCK(*this, R, "operator/= or operator/"); int sz = this->size(); for(INDEX i = 0; i < sz; i++) { if(fabs(R[i])!=0) { (*this)[i] /= R[i]; } } } return *this; } //----------------------------------------------------------------------------- // scales this matrix by a constant //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator*=(const T v) { int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i]*=v; return *this; } //----------------------------------------------------------------------------- // adds a constant to this matrix //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator+=(const T v) { int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i]+=v; return *this; } //----------------------------------------------------------------------------- // substracts a constant to this matrix //----------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator-=(const T v) { int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i]-=v; return *this; } //----------------------------------------------------------------------------- //* scales this matrix by the inverse of a constant template<typename T> Matrix<T>& Matrix<T>::operator/=(T v) { return (*this)*=(1.0/v); } //---------------------------------------------------------------------------- // Assigns one matrix to another //---------------------------------------------------------------------------- template<typename T> Matrix<T>& Matrix<T>::operator=(const Matrix<T> &r) { this->_set_equal(r); return *this; } -//---------------------------------------------------------------------------- -// general matrix assignment (for densely packed matrices) -//---------------------------------------------------------------------------- -template<typename T> -void Matrix<T>::_set_equal(const Matrix<T> &r) -{ - this->resize(r.nRows(), r.nCols()); - const Matrix<T> *pr = &r; - if (const SparseMatrix<T> *ps = sparse_cast(pr)) - copy_sparse_to_matrix(ps, *this); - - else if (diag_cast(pr)) // r is Diagonal? - { - this->zero(); - for (INDEX i=0; i<r.size(); i++) (*this)(i,i) = r[i]; - } - else memcpy(this->ptr(), r.ptr(), r.size()*sizeof(T)); -} //----------------------------------------------------------------------------- //* sets all elements to a constant template<typename T> inline Matrix<T>& Matrix<T>::operator=(const T &v) { set_all_elements_to(v); return *this; } //----------------------------------------------------------------------------- //* sets all elements to a constant template<typename T> void Matrix<T>::set_all_elements_to(const T &v) { int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i] = v; } //---------------------------------------------------------------------------- // adds a matrix scaled by factor s to this one. //---------------------------------------------------------------------------- template <typename T> void Matrix<T>::add_scaled(const Matrix<T> &A, const T& s) { SSCK(A, *this, "Matrix::add_scaled"); int sz=this->size(); for(INDEX i=0; i<sz; i++) (*this)[i] += A[i]*s; } //----------------------------------------------------------------------------- //* writes a matlab command to the console template<typename T> void Matrix<T>::matlab(const std::string &s) const { this->matlab(std::cout, s); } //----------------------------------------------------------------------------- //* Writes a matlab script defining the vector to the stream template<typename T> void Matrix<T>::matlab(std::ostream &o, const std::string &s) const { o << s <<"=zeros(" << nRows() << ","<<nCols()<<");\n"; int szi = this->nRows(); int szj = this->nCols(); for (INDEX i = 0; i < szi; i++) for (INDEX j = 0; j < szj; j++) o << s << "("<<i+1<<","<<j+1<<")=" << (*this)(i,j) << ";\n"; } //----------------------------------------------------------------------------- //* writes a mathematica command to the console template<typename T> void Matrix<T>::mathematica(const std::string &s) const { this->mathematica(std::cout, s); } //----------------------------------------------------------------------------- //* Writes a mathematica script defining the vector to the stream template<typename T> void Matrix<T>::mathematica(std::ostream &o, const std::string &s) const { o << s <<" = { \n"; o.precision(15); o << std::fixed; for(INDEX i=0; i< nRows(); i++) { o <<" { " << (*this)(i,0); for(INDEX j=1; j< nCols(); j++) o << ", " << (*this)(i,j); if (i+1 == nRows()) { o <<" } \n"; } else { o <<" }, \n"; } } o << "};\n"; o << std::scientific; } //----------------------------------------------------------------------------- //* sets all matrix elements to zero template<typename T> inline Matrix<T>& Matrix<T>::zero() { set_all_elements_to(T(0)); return *this; } //----------------------------------------------------------------------------- //* sets to identity template<typename T> inline Matrix<T>& Matrix<T>::identity(int nrows) { if (nrows == 0) { SQCK(*this, "DenseMatrix::inv(), matrix not square"); // check matrix is square nrows = nRows(); } reset(nrows,nrows); for(INDEX i=0; i< nRows(); i++) (*this)(i,i) = 1; return *this; } //----------------------------------------------------------------------------- //* returns the total number of elements template<typename T> inline INDEX Matrix<T>::size() const { return nRows()*nCols(); } //----------------------------------------------------------------------------- //* returns true if (i,j) is within the range of the matrix template<typename T> inline bool Matrix<T>::in_range(INDEX i, INDEX j) const { return i<nRows() && j<nCols(); } //----------------------------------------------------------------------------- //* returns true if the matrix size is rs x cs template<typename T> inline bool Matrix<T>::is_size(INDEX rs, INDEX cs) const { return nRows()==rs && nCols()==cs; } //----------------------------------------------------------------------------- //* returns true if the matrix is square and not empty template<typename T> inline bool Matrix<T>::is_square() const { return nRows()==nCols() && nRows(); } //----------------------------------------------------------------------------- //* returns true if Matrix, m, is the same size as this template<typename T> inline bool Matrix<T>::same_size(const Matrix<T> &m) const { return is_size(m.nRows(), m.nCols()); } //----------------------------------------------------------------------------- //* returns true if Matrix a and Matrix b are the same size template<typename T> inline bool Matrix<T>::same_size(const Matrix<T> &a, const Matrix<T> &b) { return a.same_size(b); } //----------------------------------------------------------------------------- //* returns true if Matrix a rows = Matrix b cols template<typename T> inline bool Matrix<T>::cols_equals_rows(const Matrix<T> &a, const Matrix<T> &b) { return a.nCols() == b.nRows(); } //----------------------------------------------------------------------------- //* returns true if no value is outside of the range template<typename T> inline bool Matrix<T>::check_range(T min, T max) const { for (INDEX i = 0; i < this->nRows(); i++) { for (INDEX j = 0; j < this->nCols(); j++) { T val = (*this)(i,j); if ( (val > max) || (val < min) ) return false; } } return true; } //----------------------------------------------------------------------------- //* Displays indexing error message and quits template<typename T> void ierror(const Matrix<T> &a, const char *FILE, int LINE, INDEX i, INDEX j) { std::cout << "Error: Matrix indexing failure "; std::cout << "in file: " << FILE << ", line: "<< LINE <<"\n"; std::cout << "Tried accessing index (" << i << ", " << j <<")\n"; std::cout << "Matrix size was "<< a.nRows() << "x" << a.nCols() << "\n"; ERROR_FOR_BACKTRACE exit(EXIT_FAILURE); } //----------------------------------------------------------------------------- //* Displays custom message and indexing error and quits template<typename T> void ierror(const Matrix<T> &a, INDEX i, INDEX j, const std::string m) { std::cout << m << "\n"; std::cout << "Tried accessing index (" << i << ", " << j <<")\n"; std::cout << "Matrix size was "<< a.nRows() << "x" << a.nCols() << "\n"; ERROR_FOR_BACKTRACE exit(EXIT_FAILURE); } //----------------------------------------------------------------------------- //* Displays matrix compatibility error message template<typename T> void merror(const Matrix<T> &a, const Matrix<T> &b, const std::string m) { std::cout << "Error: " << m << "\n"; std::cout << "Matrix sizes were " << a.nRows() << "x" << a.nCols(); if (&a != &b) std::cout << ", and "<< b.nRows() << "x" << b.nCols(); std::cout << "\n"; if (a.size() < 100) a.print("Matrix"); ERROR_FOR_BACKTRACE exit(EXIT_FAILURE); } //----------------------------------------------------------------------------- //* returns upper or lower half of a partitioned matrix //* A1 is the on-diagonal square matrix, A2 is the off-diagonal matrix //* rowsIn is the rows to be placed in A1 //* rows is the map for A1, (rows,colsC) is the map for A2 template <typename T> void Matrix<T>::row_partition(const std::set<int> & rowsIn, std::set<int> & rows, std::set<int> & colsC, DenseMatrix<T> & A1, DenseMatrix<T> & A2, bool complement) const { if (complement) { for (INDEX i = 0; i < this->nRows(); i++) { if (rowsIn.find(i) == rowsIn.end() ) rows.insert(i); } } else rows = rowsIn; // complement of set "rows" in set of this.cols is "cols" for (INDEX i = 0; i < this->nCols(); i++) { if (rows.find(i) == rows.end() ) colsC.insert(i); } // degenerate cases if (int(rows.size()) == this->nCols()) { A1 = (*this); A2.reset(0,0); return; } else if (rows.size() == 0) { A1.reset(0,0); A2 = (*this); return; } // non-degenerate case int nrows = rows.size(); int ncolsC = colsC.size(); A1.reset(nrows,nrows); A2.reset(nrows,ncolsC); std::set<int>::const_iterator itrI, itrJ; INDEX i =0; for (itrI = rows.begin(); itrI != rows.end(); itrI++) { INDEX j = 0; for (itrJ = rows.begin(); itrJ != rows.end(); itrJ++) { A1(i,j) = (*this)(*itrI,*itrJ); j++; } j = 0; for (itrJ = colsC.begin(); itrJ != colsC.end(); itrJ++) { A2(i,j) = (*this)(*itrI,*itrJ); j++; } i++; } } template <typename T> std::set<int> Matrix<T>::row_partition(const std::set<int> & rows, DenseMatrix<T> & A1, DenseMatrix<T> & A2) const { // complement of set "rows" in set of this.cols is "cols" std::set<int> colsC; for (INDEX i = 0; i < this->nCols(); i++) { if (rows.find(i) == rows.end() ) colsC.insert(i); } // degenerate cases if (int(rows.size()) == this->nCols()) { A1 = (*this); A2.reset(0,0); return colsC; } else if (rows.size() == 0) { A1.reset(0,0); A2 = (*this); return colsC; } // non-degenerate case int nrows = rows.size(); int ncolsC = colsC.size(); A1.reset(nrows,nrows); A2.reset(nrows,ncolsC); std::set<int>::const_iterator itrI, itrJ; INDEX i =0; for (itrI = rows.begin(); itrI != rows.end(); itrI++) { INDEX j = 0; for (itrJ = rows.begin(); itrJ != rows.end(); itrJ++) { A1(i,j) = (*this)(*itrI,*itrJ); j++; } j = 0; for (itrJ = colsC.begin(); itrJ != colsC.end(); itrJ++) { A2(i,j) = (*this)(*itrI,*itrJ); j++; } i++; } return colsC; } //----------------------------------------------------------------------------- //* returns row & column mapped matrix template <typename T> void Matrix<T>::map(const std::set<int> & rows, const std::set<int> & cols, DenseMatrix<T> & A ) const { if (rows.size() == 0 || cols.size() == 0 ) { A.reset(0,0); return; } int nrows = rows.size(); int ncols = cols.size(); A.reset(nrows,ncols); std::set<int>::const_iterator itrI, itrJ; INDEX i =0; for (itrI = rows.begin(); itrI != rows.end(); itrI++) { INDEX j = 0; for (itrJ = cols.begin(); itrJ != cols.end(); itrJ++) { A(i,j) = (*this)(*itrI,*itrJ); j++; } i++; } } //----------------------------------------------------------------------------- //* inserts elements from a smaller matrix template <typename T> void Matrix<T>::insert(const std::set<int> & rows, const std::set<int> & cols, const DenseMatrix<T> & A ) { if (rows.size() == 0 || cols.size() == 0 ) return; std::set<int>::const_iterator itrI, itrJ; int i =0; for (itrI = rows.begin(); itrI != rows.end(); itrI++) { int j = 0; for (itrJ = cols.begin(); itrJ != cols.end(); itrJ++) { (*this)(*itrI,*itrJ) = A(i,j); //std::cout << *itrI << " " << *itrJ << " : " << (*this)(*itrI,*itrJ) << "\n"; j++; } i++; } } //----------------------------------------------------------------------------- //* assemble elements from a smaller matrix template <typename T> void Matrix<T>::assemble(const std::set<int> & rows, const std::set<int> & cols, const DenseMatrix<T> & A ) { if (rows.size() == 0 || cols.size() == 0 ) return; std::set<int>::const_iterator itrI, itrJ; int i =0; for (itrI = rows.begin(); itrI != rows.end(); itrI++) { int j = 0; for (itrJ = cols.begin(); itrJ != cols.end(); itrJ++) { (*this)(*itrI,*itrJ) += A(i,j); j++; } i++; } } //----------------------------------------------------------------------------- } // end namespace #endif diff --git a/lib/atc/MeshReader.cpp b/lib/atc/MeshReader.cpp index 0ecb8f625..a50a19584 100644 --- a/lib/atc/MeshReader.cpp +++ b/lib/atc/MeshReader.cpp @@ -1,223 +1,226 @@ #include "MeshReader.h" #include "LammpsInterface.h" #include "Utility.h" #ifdef HAS_EXODUS //#include <stdio.h> //#include "netcdf.h" #include "exodusII.h" #endif using ATC_Utility::to_string; using std::ifstream; using std::istringstream; using std::stringstream; using std::map; using std::pair; using std::set; using std::string; namespace ATC { /** constructor, takes a filename */ MeshReader::MeshReader(string filename, Array<bool> periodicity, double tol) : meshfile_(filename), periodicity_(periodicity), nNodes_(0), nElements_(0), coordTol_(tol) { conn_ = new Array2D<int>(); nodeCoords_ = new DENS_MAT; nodeSets_ = new Array< std::pair< string,set<int> > >(); size_t idx = filename.rfind('.'); if (idx == string::npos) { throw ATC_Error("Given mesh file is of unknown type."); } string ext = filename.substr(idx+1); if (ext == "mesh"){ read_mesh_file(); } else { throw ATC_Error("mesh file is of unknown type."); } } /** destructor */ MeshReader::~MeshReader() { + if (conn_) delete conn_; + if (nodeCoords_) delete nodeCoords_; + if (nodeSets_) delete nodeSets_; } /** creates handle to new mesh object */ FE_Mesh* MeshReader::create_mesh() { return new FE_3DMesh(elementType_, nNodes_, nElements_, conn_, nodeCoords_, periodicity_, nodeSets_); } int MeshReader::number_of_vertices(string str) { string temp; int number=0; for (unsigned int i=0; i < str.size(); i++) { if (isdigit(str[i])) { for (unsigned int a=i; a<str.size(); a++) { temp += str[a]; } break; } } istringstream(temp) >> number; return number; } /** reads .mesh format file */ void MeshReader::read_mesh_file() { ifstream in; in.open(meshfile_.c_str(), ifstream::in); string header; while (getline(in,header)) { istringstream words(header); string section; words >> section; if (section == "Coordinates") { words >> nNodes_; nodeCoords_->reset(3, nNodes_, false); string line; for (int i = 0; i < nNodes_; ++i) { getline(in,line); istringstream coords(line); coords >> (*nodeCoords_)(0, i); coords >> (*nodeCoords_)(1, i); coords >> (*nodeCoords_)(2, i); } stringstream ss; ss << "read " << nNodes_ << " nodes"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } else if (section == "Elements") { words >> nElements_; words >> elementType_; int nVerts = number_of_vertices(elementType_); conn_->reset(nVerts, nElements_); string line; for (int i = 0; i < nElements_; ++i) { getline(in,line); istringstream verts(line); for (int j = 0; j < nVerts; ++j ) { int node; verts >> node; (*conn_)(j,i) = node-1; } } stringstream ss; ss << "read " << nElements_ << " " << elementType_ << " elements"; ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } else if (section == "Nodesets") { words >> nNodeSets_; nodeSets_->reset(nNodeSets_); string infoline; string setName; int nNodes; string line; for (int i = 0; i < nNodeSets_; ++i) { getline(in,infoline); istringstream info(infoline); info >> setName; info >> nNodes; (*nodeSets_)(i).first = setName; getline(in,line); istringstream nodes(line); for (int j = 0; j < nNodes; ++j ) { int node; nodes >> node; (*nodeSets_)(i).second.insert(node-1); } } } } in.close(); if (nodeCoords_->size() == 0) { throw ATC_Error("Could not find mesh file, or mesh file empty."); } } /** reads .exo format file */ void MeshReader::read_exo_file() { #ifndef HAS_EXODUS throw ATC_Error("Reading ExodusII .exo files not supported."); #else int CPU_word_size=0,IO_word_size=0; float version; int exoid = ex_open (meshfile_.c_str(), EX_READ, &CPU_word_size, &IO_word_size, &version); if (exoid < 0) { throw ATC_Error("couldn't open "+meshfile_); } int nsd,nelemblk,nfsets; char title[MAX_LINE_LENGTH+1]; int error = ex_get_init (exoid, title, &nsd, &nNodes_, &nElements_, &nelemblk, &nNodeSets_, &nfsets); if (error > 0) { throw ATC_Error("problem with init "+meshfile_+" "+title); } // coordinates float x[nNodes_], y[nNodes_], z[nNodes_]; error = ex_get_coord (exoid, x, y, z); if (error > 0) { throw ATC_Error("problem with getting coordinates "+meshfile_); } nodeCoords_->reset(nsd,nNodes_); DENS_MAT & nodes = *nodeCoords_; for (int i = 0; i < nNodes_; ++i) { nodes(0,i) = x[i]; // this is a float to double conversion nodes(1,i) = y[i]; // this is a float to double conversion nodes(2,i) = z[i]; // this is a float to double conversion } ATC::LammpsInterface::instance()->print_msg_once("read "+to_string(nNodes_)+ " nodes"); // elements int blkIds[nelemblk],nblkelem[nelemblk],nnpe[nelemblk],na[nelemblk]; error = ex_get_elem_blk_ids(exoid, blkIds); char etype[MAX_STR_LENGTH+1]; string lastType; for (int i=0; i<nelemblk; i++){ error = ex_get_elem_block (exoid, blkIds[i], etype, &(nblkelem[i]), &(nnpe[i]), &(na[i])); elementType_ = etype; if (i > 0 && elementType_ != lastType ) { throw ATC_Error(meshfile_+" is composed of multiple types"); } lastType = etype; } int nVerts = number_of_vertices(elementType_); conn_->reset(nVerts, nElements_); int n = 0; for (int i=0; i<nelemblk; i++) { int bconn[nnpe[i]*nblkelem[i]]; error = ex_get_elem_conn (exoid, blkIds[i], &bconn); for (int j=0; j<nblkelem[i]; j++) { for (int k=0; k<nnpe[i]; k++) { (*conn_)(k,n) = bconn[k+j*nnpe[i]]-1; } n++; } ATC::LammpsInterface::instance()->print_msg_once("read "+to_string(n)+" "+elementType_+" elements, block "+to_string(i+1)+"/"+to_string(nelemblk)); } // nodesets int nsetIds[nNodeSets_]; error = ex_get_node_set_ids (exoid, nsetIds); int nnodes,ndist; //nodeSets_ = new Array< pair< string,set<int> > >(); nodeSets_->reset(nNodeSets_); for (int i=0; i<nNodeSets_; i++) { (*nodeSets_)(i).first = to_string(nsetIds[i]); error = ex_get_node_set_param (exoid, nsetIds[i], &nnodes, &ndist); int nodes[nnodes]; error = ex_get_node_set (exoid, nsetIds[i], nodes); for (int j=0; j<nnodes; j++) { (*nodeSets_)(i).second.insert(nodes[j]-1); } } error = ex_close(exoid); if (error > 0) { throw ATC_Error("problem with closing "+meshfile_); } #endif } }; // end namespace ATC diff --git a/lib/atc/OutputManager.cpp b/lib/atc/OutputManager.cpp index 586b4294f..414c5173e 100644 --- a/lib/atc/OutputManager.cpp +++ b/lib/atc/OutputManager.cpp @@ -1,889 +1,890 @@ #include <string> #include <fstream> #include <stdio.h> #include <sstream> #include "OutputManager.h" #include "ATC_Error.h" #include "LammpsInterface.h" using std::ofstream; using std::stringstream; using std::ios_base; using std::setw; using std::string; using std::map; using std::vector; using std::set; namespace ATC { static const int kFieldPrecison = 12; static const int kFieldWidth = kFieldPrecison + 6; static const int kFileNameSize = 26; // HERE <<<< static string tensor_component_names[9] = {"11","12","13", "21","22","23", "31","32","33"}; static string sym_tensor_component_names[6] = {"11","22","33","12","13","23"}; static string vector_component_names[3] = {"_X","_Y","_Z"}; static string list_component_names[26] = {"_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z"}; string* get_component_names(int type) { // HERE <<<< string* componentNames = list_component_names; if (type==VECTOR_OUTPUT) componentNames = vector_component_names; else if (type == SYM_TENSOR_OUTPUT) componentNames = sym_tensor_component_names; else if (type == TENSOR_OUTPUT) componentNames = tensor_component_names; return componentNames; } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- OutputManager::OutputManager(string outputPrefix, set<int> & otypes) : initialized_(false), firstStep_(true), firstGlobalsWrite_(true), writeGlobalsHeader_(true), coordinates_(NULL), connectivities_(NULL), dataType_(POINT), outputPrefix_(outputPrefix), ensightOutput_(otypes.count(ENSIGHT)), textOutput_(otypes.count(GNUPLOT)), fullTextOutput_(otypes.count(FULL_GNUPLOT)), vtkOutput_(otypes.count(VTK)), tensorToComponents_(false), // paraview does not support tensors - vectorToComponents_(false) - + vectorToComponents_(false), + warnTooManyCols_(true) {} //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- OutputManager::OutputManager() : initialized_(false), firstStep_(true), firstGlobalsWrite_(true), writeGlobalsHeader_(true), coordinates_(NULL), connectivities_(NULL), dataType_(POINT), outputPrefix_("NULL"), ensightOutput_(true), textOutput_(false), fullTextOutput_(false), vtkOutput_(false), tensorToComponents_(false), // paraview does not support tensors vectorToComponents_(false) {} //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- OutputManager::~OutputManager() {} //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::set_option(OutputOption option, bool value) { if (option == OUTPUT_VECTOR_COMPONENTS) vectorToComponents_ = value; else if (option == OUTPUT_TENSOR_COMPONENTS) tensorToComponents_ = value; else throw ATC_Error("unsupported output option"); }; //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::initialize(string outputPrefix, set<int> & otypes) { if (outputPrefix_ != outputPrefix ) { // new stream with existing object outputPrefix_ = outputPrefix; initialized_ = false; } outputTimes_.clear(); if (otypes.count(ENSIGHT) > 0) ensightOutput_ = true; else ensightOutput_ = false; if (otypes.count(GNUPLOT) > 0) textOutput_ = true; if (otypes.count(FULL_GNUPLOT) > 0) fullTextOutput_ = true; if (otypes.count(VTK) > 0) vtkOutput_ = true; firstStep_ = true; firstGlobalsWrite_ = true; writeGlobalsHeader_ = true; } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::print_custom_names() { map<string,vector<string> >::const_iterator itr; string msg = "output custom names:\n"; for (itr = fieldNames_.begin(); itr != fieldNames_.end(); itr++) { string stem = itr->first; vector<string> names = itr->second; for (unsigned int i = 0; i < names.size(); i++) { msg+= stem+" : "+names[i]+"\n"; } } ATC::LammpsInterface::instance()->print_msg_once(msg); } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- // Dump text-based fields to disk for later restart void OutputManager::write_restart_file(string fileName, RESTART_LIST *data) { FILE * fp=NULL; fp=fopen(fileName.c_str(),"wb"); // open RESTART_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { const MATRIX* field_data = iter->second; for (int i = 0; i < field_data->nRows(); ++i) { for (int j = 0; j < field_data->nCols(); ++j) { double x = (*field_data)(i,j); fwrite(&x,sizeof(double),1,fp); } } } fclose(fp); } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- // Read a file corresponding to a write by write_restart_file void OutputManager::read_restart_file(string fileName, RESTART_LIST *data) { FILE * fp=NULL; fp=fopen(fileName.c_str(),"rb"); // open RESTART_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { MATRIX* field_data = iter->second; for (int i = 0; i < field_data->nRows(); ++i) { for (int j = 0; j < field_data->nCols(); ++j) { double myVal; fread(&myVal,sizeof(double),1,fp); (*field_data)(i,j) = myVal; } } } fclose(fp); } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::write_globals(void) { if ( outputPrefix_ == "NULL") return; string file = outputPrefix_ + ".GLOBALS"; ofstream text; if ( firstGlobalsWrite_ ) text.open(file.c_str(),ios_base::out); else text.open(file.c_str(),ios_base::app); firstGlobalsWrite_ = false; map<string, double>::iterator iter; // header if ( firstStep_ || writeGlobalsHeader_) { text << "# time:1 "; int index = 2; for (iter = globalData_.begin(); iter != globalData_.end(); iter++) { string name = iter->first; string str; stringstream out; out << ":" << index++; str = out.str(); name.append(str); text.width(kFieldWidth); text << name << " "; } text << '\n'; } writeGlobalsHeader_ = false; // data text.width(kFieldWidth); text << outputTimes_[outputTimes_.size()-1] << " "; for (iter = globalData_.begin(); iter != globalData_.end(); iter++) { double value = iter->second; text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << value << " "; } text << "\n"; } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::write_geometry(const MATRIX *coordinates, const Array2D<int> *connectivities) { if ( outputPrefix_ == "NULL") throw ATC_Error( "No outputPrefix given."); number_of_nodes_ = coordinates->nCols(); coordinates_ = coordinates; connectivities_ = connectivities; if (ensightOutput_) write_geometry_ensight(); if (textOutput_) write_geometry_text(); initialized_ = true; } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::write_geometry_ensight(void) { // geometry based on a reference configuration string geom_file_name = outputPrefix_ + ".geo"; // open file FILE * fp=NULL; char buffer[80]; if ( ! initialized_ ) { fp=fopen(geom_file_name.c_str(),"wb"); // open strcpy(buffer,"C Binary"); fwrite(buffer,sizeof(char),80,fp); } else { fp=fopen(geom_file_name.c_str(),"ab"); // append } if (fp == NULL) { throw ATC_Error("can not create Ensight geometry file"); } // write preamble strcpy(buffer,"BEGIN TIME STEP"); fwrite(buffer,sizeof(char),80,fp); strcpy(buffer,"Ensight geometry file"); fwrite(buffer,sizeof(char),80,fp); strcpy(buffer,"description"); fwrite(buffer,sizeof(char),80,fp); strcpy(buffer,"node id assign"); fwrite(buffer,sizeof(char),80,fp); strcpy(buffer,"element id assign"); fwrite(buffer,sizeof(char),80,fp); // per part strcpy(buffer,"part"); fwrite(buffer,sizeof(char),80,fp); int part_number=1; fwrite(&part_number,sizeof(int),1,fp); strcpy(buffer,"description"); fwrite(buffer,sizeof(char),80,fp); const MATRIX & coordinates = *coordinates_; // write coordinates strcpy(buffer,"coordinates"); fwrite(buffer,sizeof(char),80,fp); fwrite(&number_of_nodes_,sizeof(int),1,fp); int number_of_spatial_dimensions = coordinates.nRows(); if (number_of_spatial_dimensions != 3) throw ATC_Error("Ensight writer needs a 3D geometry"); for (int i = 0; i < number_of_spatial_dimensions; ++i) { for (int j = 0; j < number_of_nodes_; ++j) { float x = (float) coordinates(i,j); fwrite(&x,sizeof(float),1,fp); } } // write mesh connectivities or point "connectivities" if (connectivities_) { dataType_ = MESH; int nodes_per_element = connectivities_->nRows(); if (nodes_per_element == 4) { strcpy(buffer,"tetra4"); } else if (nodes_per_element == 8) { strcpy(buffer,"hexa8"); } else if (nodes_per_element == 20) { strcpy(buffer,"hexa20"); } else if (nodes_per_element == 27) { strcpy(buffer,"hexa27"); } else throw ATC_Error("Ensight writer does not recoginize element type"); fwrite(buffer,sizeof(char),80,fp); int number_of_elements = connectivities_->nCols(); fwrite(&number_of_elements,sizeof(int),1,fp); int number_of_nodes_per_element = connectivities_->nRows(); for (int j = 0; j < number_of_elements; ++j) { for (int i = 0; i < number_of_nodes_per_element; ++i) { int inode = (*connectivities_)(i,j) +1; // 1 based numbering fwrite(&inode,sizeof(int),1,fp); } } } else { strcpy(buffer,"point"); fwrite(buffer,sizeof(char),80,fp); int number_of_elements = number_of_nodes_; fwrite(&number_of_elements,sizeof(int),1,fp); for (int j = 0; j < number_of_elements; ++j) { int inode = j +1; // 1 based numbering fwrite(&inode,sizeof(int),1,fp); } } // end per part strcpy(buffer,"END TIME STEP"); fwrite(buffer,sizeof(char),80,fp); fclose(fp); } //----------------------------------------------------------------------------- //* //----------------------------------------------------------------------------- void OutputManager::write_geometry_text(void) { if ( outputPrefix_ == "NULL") return; // geometry based on a reference configuration string geom_file_text = outputPrefix_ + ".XYZ"; // open file ofstream text; text.open(geom_file_text.c_str(),ios_base::out); if (connectivities_) { int number_of_elements = connectivities_->nCols(); int number_of_nodes_per_element = connectivities_->nRows(); for (int j = 0; j < number_of_elements; ++j) { text << "#"; for (int i = 0; i < number_of_nodes_per_element; ++i) { int inode = (*connectivities_)(i,j) +1; // 1 based numbering text << setw(6) << inode; } text << "\n"; } } const MATRIX & coordinates = *coordinates_; int number_of_spatial_dimensions = coordinates.nRows(); for (int j = 0; j < number_of_nodes_; ++j) { text << setw(6) << j+1 << " "; for (int i = 0; i < number_of_spatial_dimensions; ++i) { text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << coordinates(i,j) << " "; } text << "\n"; } text << "\n"; } //----------------------------------------------------------------------------- /** pack "soln" into data */ //----------------------------------------------------------------------------- void OutputManager::write_data(double time, FIELDS *soln, OUTPUT_LIST *data, const int *node_map) { // pack OUTPUT_LIST combined_data; if (soln) { FIELDS::iterator iter; for (iter = soln->begin(); iter != soln->end(); iter++) { FieldName field_index = iter->first; MATRIX* field_data = &((iter->second).set_quantity()); string field_name = field_to_string(field_index); combined_data[field_name] = field_data; } } if (data) { OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { string field_name = iter->first; const MATRIX* field_data = iter->second; combined_data[field_name] = field_data; } } write_data(time, &(combined_data), node_map); }; //----------------------------------------------------------------------------- /** write data */ //----------------------------------------------------------------------------- void OutputManager::write_data(double time, OUTPUT_LIST *data, const int *node_map) { if (! initialized_) { throw ATC_Error("must write geometry before data"); } // store the time step value outputTimes_.push_back(time); if (ensightOutput_) { // write data OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { string field_name = iter->first; const MATRIX* field_data = iter->second; write_data_ensight(field_name, field_data, node_map); } // write dictionary write_dictionary(time,data); } // write text dump if (textOutput_) { write_data_text(data); if (firstStep_ && node_map) { string map_file_text = outputPrefix_ + ".MAP"; ofstream text; text.open(map_file_text.c_str(),ios_base::out); for (int i=0; i< number_of_nodes_ ; i++) { text << node_map[i] << "\n"; } text.close(); } } else if (fullTextOutput_) { write_data_text(data,node_map); } if (vtkOutput_) { write_data_vtk(data); } // global variables if (! globalData_.empty()) write_globals(); if (firstStep_) firstStep_ = false; } //----------------------------------------------------------------------------- /** write (ensight gold format "C" binary) data */ // use "ens_checker" to check binary format //----------------------------------------------------------------------------- void OutputManager::write_data_ensight(string field_name, const MATRIX *field_data, const int *node_map) { int ndof = field_data->nCols(); int col_start = 0; int col_end = ndof; string filenames[kFileNameSize]; int nfiles = 1; filenames[0] = outputPrefix_ + "." + field_name; int type = data_type(ndof); if (use_component_names(type)){ nfiles = ndof; if (nfiles > kFileNameSize) { - if (ATC::LammpsInterface::instance()->rank_zero()) { + if (warnTooManyCols_ && ATC::LammpsInterface::instance()->rank_zero()) { + warnTooManyCols_ = false; stringstream ss; - ss << " only writing " << kFileNameSize + ss << "WARNING: only writing " << kFileNameSize << " components of " << field_name << " which has " << ndof; ATC::LammpsInterface::instance()->print_msg(ss.str()); } nfiles = kFileNameSize; } string* component_names = get_component_names(type); for (int ifile = 0; ifile < nfiles; ++ifile) { string comp_name; if (! custom_name(field_name,ifile,comp_name)) comp_name = field_name + component_names[ifile]; filenames[ifile] = outputPrefix_ + "." + comp_name; } } for (int ifile = 0; ifile < nfiles; ++ifile) { // for vector/tensor to components if ( nfiles > 1 ) { col_start = ifile; col_end = ifile+1; } // open or append data file string data_file_name = filenames[ifile]; FILE * fp=NULL; if ( outputTimes_.size() == 1 ) { fp=fopen(data_file_name.c_str(),"wb"); // open } else { fp=fopen(data_file_name.c_str(),"ab"); // append } if (fp == NULL) { throw ATC_Error("can not create Ensight data file: "+data_file_name); } // write data char buffer[80]; strcpy(buffer,"BEGIN TIME STEP"); fwrite(buffer,sizeof(char),80,fp); strcpy(buffer,"field name"); fwrite(buffer,sizeof(char),80,fp); // per part strcpy(buffer,"part"); fwrite(buffer,sizeof(char),80,fp); int part_number = 1; fwrite(&part_number,sizeof(int),1,fp); strcpy(buffer,"coordinates"); fwrite(buffer,sizeof(char),80,fp); if (node_map) { for (int j = col_start; j < col_end; ++j) { for (int i = 0; i < number_of_nodes_; ++i) { int inode = node_map[i]; float x = (float) (*field_data)(inode,j); fwrite(&x,sizeof(float),1,fp); } } } else { for (int j = col_start; j < col_end; ++j) { for (int i = 0; i < field_data->nRows(); ++i) { float x = (float) (*field_data)(i,j); fwrite(&x,sizeof(float),1,fp); } } } // end per part strcpy(buffer,"END TIME STEP"); fwrite(buffer,sizeof(char),80,fp); fclose(fp); } } //----------------------------------------------------------------------------- /** write data dict for both text & full_text */ //----------------------------------------------------------------------------- void OutputManager::write_text_data_header(OUTPUT_LIST *data, ofstream & text, int k) { if (data) { OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { string field_name = iter->first; int nrows = iter->second->nRows(); if (!(nrows>0)) { string msg = field_name + " does not have data for output"; throw ATC_Error(msg); } int ncols = iter->second->nCols(); if (ncols > kFileNameSize) { if (ATC::LammpsInterface::instance()->rank_zero()) { stringstream ss; ss << " only writing " << kFileNameSize << " components of " << field_name << " which has " << ncols; ATC::LammpsInterface::instance()->print_msg(ss.str()); } ncols = kFileNameSize; } if (ncols == 1) { string name = field_name; custom_name(field_name,0,name); string str; stringstream out; out <<":"<<k; str = out.str(); name.append(str); text.width(kFieldWidth); text << name << " "; k++; } else { for (int i = 1; i <= ncols; i++) { string name = field_name; string str; stringstream out; if (! custom_name(field_name,i-1,name)) { out <<"_"<<i; } out <<":"<<k; str = out.str(); name.append(str); text.width(kFieldWidth); text << name << " "; k++; } } } } else { throw ATC_Error(" data missing from output");} text << "\n"; } //----------------------------------------------------------------------------- /** write data in text format */ //----------------------------------------------------------------------------- void OutputManager::write_data_text(OUTPUT_LIST *data) { string data_file_text = outputPrefix_ + ".DATA"; ofstream text; if (firstStep_) text.open(data_file_text.c_str(),ios_base::out); else text.open(data_file_text.c_str(),ios_base::app); // write data label header if (firstStep_) { text.width(6); text << "# index:1" << " "; // give an ordinate for gnuplot text.width(10); text << " step:2" << " "; write_text_data_header(data,text,3); } text << "# timestep " << outputTimes_.size() << " : " << outputTimes_[outputTimes_.size()-1] << "\n"; int nrows = 0; OUTPUT_LIST::iterator iter; iter = data->begin(); if (iter == data->end()) { throw ATC_Error(" no data in output");} const MATRIX* field_data = iter->second; nrows = field_data->nRows(); for (int i = 0; i < nrows; ++i) { text.width(6); text << i << " "; // give an ordinate for gnuplot text.width(10); text << outputTimes_.size() << " "; OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { const MATRIX* field_data = iter->second; int ncols = field_data->nCols(); if (ncols > kFileNameSize) { ncols = kFileNameSize;} for (int j = 0; j < ncols; ++j) { text.width(kFieldWidth); text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*field_data)(i,j) << " "; } } text <<"\n"; } text <<"\n"; } //----------------------------------------------------------------------------- /** write data in full text format */ //----------------------------------------------------------------------------- void OutputManager::write_data_text(OUTPUT_LIST *data, const int *node_map) { string data_file_text = outputPrefix_ + ".DATA"; ofstream text; if (firstStep_) text.open(data_file_text.c_str(),ios_base::out); else text.open(data_file_text.c_str(),ios_base::app); // write data label header if (firstStep_) { text.width(6); text << "# index:1" << " "; text.width(6); text << " id:2" << " "; text.width(10); text << " step:3" << " "; text.width(4); text << " x:4" << " "; text.width(4); text << " y:5" << " "; text.width(4); text << " z:6" << " "; write_text_data_header(data,text,7); if (connectivities_) { int number_of_elements = connectivities_->nCols(); int number_of_nodes_per_element = connectivities_->nRows(); text << "# connectivities number_of_elements: " << number_of_elements << " nodes_per_element: " << number_of_nodes_per_element << "\n"; for (int j = 0; j < number_of_elements; ++j) { text << "#"; for (int i = 0; i < number_of_nodes_per_element; ++i) { int inode = (*connectivities_)(i,j) +1; // 1 based numbering text << setw(6) << inode; } text << "\n"; } } } text << "# timestep " << outputTimes_.size() << " : " << outputTimes_[outputTimes_.size()-1] << "\n"; OUTPUT_LIST::iterator iter; iter = data->begin(); if (iter == data->end()) { throw ATC_Error(" no data in output");} int nnodes = coordinates_->nCols(); for (int i = 0; i < nnodes; ++i) { int unode = i; if (node_map) unode = node_map[i]; text.width(6); text << i << " "; text.width(6); text << unode << " "; text.width(10); text << outputTimes_.size() << " "; // coordinates for (int j = 0; j < coordinates_->nRows(); ++j) { text.width(kFieldWidth); text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*coordinates_)(j,i) << " "; } // data OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { const MATRIX* field_data = iter->second; int ncols = field_data->nCols(); if (ncols > kFileNameSize) { ncols = kFileNameSize; } for (int j = 0; j < ncols; ++j) { text.width(kFieldWidth); text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*field_data)(unode,j) << " "; } } text <<"\n"; } text <<"\n"; } //----------------------------------------------------------------------------- /** write data in vtk text format */ //----------------------------------------------------------------------------- void OutputManager::write_data_vtk(OUTPUT_LIST *data) { string data_file_text = outputPrefix_ + ".vtk"; ofstream text; if (firstStep_) text.open(data_file_text.c_str(),ios_base::out); else throw ATC_Error(" vtk format can not handle multiple steps"); text << "# vtk DataFile Version 3.0\n"; text << "# " << outputPrefix_ << "\n"; text << "ASCII\n"; text << "DATASET UNSTRUCTURED_GRID\n"; // geometry int nnodes = coordinates_->nCols(); text << "POINTS " << nnodes << " float\n"; for (int i = 0; i < nnodes; ++i) { for (int j = 0; j < coordinates_->nRows(); ++j) { text.width(kFieldWidth); text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*coordinates_)(j,i) << " "; } text << "\n"; } text << "\n"; int nelems = connectivities_->nCols(); int nodes_per_element = connectivities_->nRows(); text << "CELLS " << nelems << " " << nelems*(nodes_per_element+1) << "\n"; for (int j = 0; j < nelems; ++j) { text << setw(6) << nodes_per_element; for (int i = 0; i < nodes_per_element; ++i) { int inode = (*connectivities_)(i,j); // 0 based numbering text << setw(6) << inode; } text << "\n"; } text << "\n"; int cell_type = 4 ; text << "CELL_TYPES " << nelems << "\n"; for (int j = 0; j < nelems; ++j) { text << cell_type << "\n"; } text << "\n"; // data text << "POINT_DATA " << nnodes << "\n"; text << "\n"; OUTPUT_LIST::iterator iter; for (iter = data->begin(); iter != data->end(); iter++) { string field_name = iter->first; const MATRIX* field_data = iter->second; int ncols = field_data->nCols(); if (ncols == 1) { text << "SCALARS " << field_name << " float\n"; text << "LOOKUP_TABLE default\n"; } else { text << "VECTORS " << field_name << " float\n"; } for (int i = 0; i < nnodes; ++i) { for (int j = 0; j < ncols; ++j) { text.width(kFieldWidth); text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*field_data)(i,j) << " "; } text <<"\n"; } } text <<"\n"; } /** write (ensight gold : ASCII "C" format) dictionary */ void OutputManager::write_dictionary(double time, OUTPUT_LIST *data) { // file names string dict_file_name = outputPrefix_ + ".case"; string geom_file_name = outputPrefix_ + ".geo"; // open file FILE * fp=NULL; if ((fp=fopen(dict_file_name.c_str(),"w")) == NULL) { throw ATC_Error("can not create Ensight case file"); } // write file fprintf(fp,"FORMAT\n"); fprintf(fp,"type: ensight gold\n"); fprintf(fp,"GEOMETRY\n"); if ( dataType_ == POINT) { fprintf(fp,"model: 1 1 %s change_coords_only\n", geom_file_name.c_str()); } else { fprintf(fp,"model: %s\n", geom_file_name.c_str()); } fprintf(fp,"VARIABLE\n"); // data types if (!data) throw ATC_Error("no data for output"); OUTPUT_LIST::iterator iter; int ncols = 0; for (iter = data->begin(); iter != data->end(); iter++) { string field_name = iter->first; string field_file = outputPrefix_ + "." + field_name; const MATRIX* field_data = iter->second; int fieldCols = field_data->nCols(); ncols += fieldCols; int type = data_type(fieldCols); if (use_component_names(type)){ string* component_names = get_component_names(type); int ndof = fieldCols; if (ndof > kFileNameSize) ndof = kFileNameSize; for (int j = 0; j < ndof; ++j) { string comp_name; if (! custom_name(field_name,j,comp_name)) comp_name = field_name + component_names[j]; string comp_file = outputPrefix_ + "." + comp_name; fprintf(fp,"scalar per node: 1 1 %s %s\n", comp_name.c_str(),comp_file.c_str()); } } else if (type == VECTOR_OUTPUT) { fprintf(fp,"vector per node: 1 1 %s %s\n", field_name.c_str(),field_file.c_str()); } else if (type == SYM_TENSOR_OUTPUT) { fprintf(fp,"tensor symm per node: 1 1 %s %s\n", field_name.c_str(),field_file.c_str()); } else if (type == TENSOR_OUTPUT) { fprintf(fp,"tensor asymm per node: 1 1 %s %s\n", field_name.c_str(),field_file.c_str()); } else { fprintf(fp,"scalar per node: 1 1 %s %s\n", field_name.c_str(),field_file.c_str()); } } if (!firstStep_ && ncols != nDataCols_) { throw ATC_Error("number of columns of data has changed: start new output"); } nDataCols_ = ncols; int nsteps = outputTimes_.size(); fprintf(fp,"TIME\n"); fprintf(fp,"time set: 1\n"); fprintf(fp,"number of steps: %10d\n",nsteps); if ( dataType_ == POINT) { fprintf(fp,"filename start number: 0\n"); fprintf(fp,"filename increment: 1\n"); } fprintf(fp,"time values:\n"); for (int j = 0; j < nsteps; ++j) { double t = outputTimes_[j]; fprintf(fp,"%12.5e",t); if ((j+1)%6 == 0) fprintf(fp,"\n"); } fprintf(fp,"\n"); fprintf(fp,"FILE\n"); fprintf(fp,"file set: 1\n"); fprintf(fp,"number of steps: %10d\n",nsteps); fclose(fp); }; } // end ATC namespace diff --git a/lib/atc/OutputManager.h b/lib/atc/OutputManager.h index f63780b77..c5a901e9f 100644 --- a/lib/atc/OutputManager.h +++ b/lib/atc/OutputManager.h @@ -1,145 +1,147 @@ #ifndef OUTPUT_MANAGER_H #define OUTPUT_MANAGER_H #include "ATC_TypeDefs.h" #include <map> #include <vector> #include <string> #include <set> // 1 -> scalar // 3 -> vector x,y,z // NOT 6 -> tensor xx,xy,xz,yy,yz,zz // 6 -> tensor xx,yy,zz,xy,zx,yz // 9 -> tensor xx,xy,xz,yx,yy,yz,zx,zy,zz namespace ATC { enum OutputType { ENSIGHT=0, GNUPLOT, FULL_GNUPLOT, VTK }; enum OutputDataType { POINT=0, MESH }; enum OutputDataCardinality { SCALAR_OUTPUT=0, VECTOR_OUTPUT, TENSOR_OUTPUT, SYM_TENSOR_OUTPUT, LIST_OUTPUT }; enum OutputOption { OUTPUT_VECTOR_COMPONENTS=0, OUTPUT_TENSOR_COMPONENTS}; /** * @class OutputManager * @brief Base class for handling output desired from an AtC computation */ class OutputManager{ public: OutputManager(void); OutputManager(std::string outputPrefix, std::set<int> &otypes); ~OutputManager(void); /** initialize output */ void initialize(std::string outputPrefix, std::set<int> &otypes); /** set output options */ void set_option(OutputOption option, bool value); // Dump text-based field info to disk for later restart void write_restart_file(std::string fileName, RESTART_LIST *data); // Read text-based field file written from write_restart_file void read_restart_file(std::string fileName, RESTART_LIST *data); /** write initial/reference geometry default is to write point data, if connectivities are given then mesh data will be output coordinates : num _total_ points/nodes X num spatial dim connectivities : num elements X num nodes per element*/ void write_geometry(const MATRIX *coordinates, const Array2D<int> *connectivity=NULL); /** write data from a time step specify node_map to handle periodic soln & data */ void write_data(double time, OUTPUT_LIST *data, const int *node_map=NULL); void write_data(double time, FIELDS *soln, OUTPUT_LIST *data, const int *node_map=NULL); /** add custom names for any field */ void add_field_names(const std::string& name, const std::vector<std::string>& list) { fieldNames_[name] = list; } /** add a scalar to a text output file */ void add_global(const std::string& name, const double& value) { globalData_[name] = value; } /** delete a scalar from the output */ void delete_global(const std::string& name) { globalData_.erase(name); } /** reset the stored output scalars */ void reset_globals() { globalData_.clear(); writeGlobalsHeader_=true; } /** return data type: scalar, vector, tensor, list */ int data_type(const DENS_MAT & data) const { return data_type(data.nCols()); } int data_type(int cols) const { if (cols == 1) return SCALAR_OUTPUT; else if (cols == 3) return VECTOR_OUTPUT; else if (cols == 6) return SYM_TENSOR_OUTPUT; else if (cols == 9) return TENSOR_OUTPUT; else return LIST_OUTPUT; } bool use_component_names(int type) const { if ( (type==LIST_OUTPUT) || ((type==SYM_TENSOR_OUTPUT || type==TENSOR_OUTPUT) && tensorToComponents_) || (type==VECTOR_OUTPUT && vectorToComponents_) ) return true; else return false; } bool custom_name(const std::string field, const int index, std::string & name) const { std::map<std::string,std::vector<std::string> >::const_iterator itr = fieldNames_.find(field); if (itr == fieldNames_.end()) return false; std::vector<std::string> names = itr->second; name = names[index]; return true; } void print_custom_names(); private: void write_geometry_ensight(void); void write_geometry_text(void); void write_data_ensight(std::string name, const MATRIX *data, const int *node_map); void write_text_data_header(OUTPUT_LIST *data, std::ofstream & text, int k); void write_data_text(OUTPUT_LIST *data); void write_data_text(OUTPUT_LIST *data, const int *node_map); void write_data_vtk(OUTPUT_LIST *data); void write_dictionary(double time, OUTPUT_LIST *data); void write_globals(); /** status flags */ bool initialized_, firstStep_, firstGlobalsWrite_, writeGlobalsHeader_; /** custom field names */ std::map<std::string,std::vector<std::string> > fieldNames_; /** pointers to mesh data */ const MATRIX * coordinates_; const Array2D<int> * connectivities_; /** number of columns of data */ int nDataCols_; /** number of nodes */ int number_of_nodes_; /** data type */ int dataType_; /** base name for output files */ std::string outputPrefix_; /** list of output timesteps */ std::vector<double> outputTimes_; /** output type flags */ bool ensightOutput_,textOutput_,fullTextOutput_,vtkOutput_; /** output tensor as its components */ bool tensorToComponents_; /** output vector as its components */ bool vectorToComponents_; + /** warn once flags */ + bool warnTooManyCols_; /** global variables */ std::map<std::string,double> globalData_; }; } #endif diff --git a/lib/atc/POEMSChain.h b/lib/atc/POEMSChain.h new file mode 100644 index 000000000..7dc143a9d --- /dev/null +++ b/lib/atc/POEMSChain.h @@ -0,0 +1,73 @@ +/* + *_________________________________________________________________________* + * POEMS: PARALLELIZABLE OPEN SOURCE EFFICIENT MULTIBODY SOFTWARE * + * DESCRIPTION: SEE READ-ME * + * FILE NAME: PoemsChain.h * + * AUTHORS: See Author List * + * GRANTS: See Grants List * + * COPYRIGHT: (C) 2005 by Authors as listed in Author's List * + * LICENSE: Please see License Agreement * + * DOWNLOAD: Free at www.rpi.edu/~anderk5 * + * ADMINISTRATOR: Prof. Kurt Anderson * + * Computational Dynamics Lab * + * Rensselaer Polytechnic Institute * + * 110 8th St. Troy NY 12180 * + * CONTACT: anderk5@rpi.edu * + *_________________________________________________________________________*/ + +#ifndef POEMSCHAIN_H_ +#define POEMSCHAIN_H_ + +#include "poemslist.h" + +struct ChildRingData { + List<int> * childRing; + int entranceNodeId; +}; + +struct POEMSChain{ + ~POEMSChain(){ + for(int i = 0; i < childChains.GetNumElements(); i++) + { + delete childChains(i); + } + } + //void printTreeStructure(int tabs); + //void getTreeAsList(List<int> * temp); + List<int> listOfNodes; + List<POEMSChain> childChains; + POEMSChain * parentChain; + List<ChildRingData> childRings; + + + void printTreeStructure(int tabs){ + for(int i = 0; i < tabs; i++) + { + cout << "\t"; + } + cout << "Chain: "; + for(int i = 0; i < listOfNodes.GetNumElements(); i++) + { + cout << *(listOfNodes(i)) << " "; + } + cout << endl; + for(int i = 0; i < childChains.GetNumElements(); i++) + { + childChains(i)->printTreeStructure(tabs + 1); + } + } + void getTreeAsList(List<int> * temp) + { + for(int i = 0; i < listOfNodes.GetNumElements(); i++) + { + int * integer = new int; + *integer = *(listOfNodes(i)); + temp->Append(integer); + } + for(int i = 0; i < childChains.GetNumElements(); i++) + { + childChains(i)->getTreeAsList(temp); + } + } +}; +#endif diff --git a/lib/atc/PerAtomQuantityLibrary.cpp b/lib/atc/PerAtomQuantityLibrary.cpp index c7f57fd72..36f925245 100644 --- a/lib/atc/PerAtomQuantityLibrary.cpp +++ b/lib/atc/PerAtomQuantityLibrary.cpp @@ -1,2383 +1,2510 @@ // ATC transfer headers #include "PerAtomQuantityLibrary.h" #include "ATC_Transfer.h" #include "FE_Engine.h" #include "LammpsInterface.h" #include <typeinfo> #include <sstream> #include <iostream> using std::map; using std::ifstream; using std::stringstream; using std::set; using std::string; using std::vector; using ATC_Utility::to_string; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomToElementMap //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomToElementMap::AtomToElementMap(ATC_Method * atc, PerAtomQuantity<double> * atomPositions, AtomType atomType) : ProtectedAtomQuantity<int>(atc,1,atomType), atomPositions_(atomPositions) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomPositions_) atomPositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); if (!atomPositions_) throw ATC_Error("AtomToElementMap::AtomToElementMap - atom position quantity is undefined"); atomPositions_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomToElementMap::~AtomToElementMap() { atomPositions_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomToElementMap::reset() const { if (need_reset()) { PerAtomQuantity<int>::reset(); const DENS_MAT & position(atomPositions_->quantity()); const FE_Mesh * feMesh = atc_.fe_engine()->fe_mesh(); int nsd = atc_.nsd(); DENS_VEC coords(nsd); for (int i = 0; i < quantity_.nRows(); i++) { for (int j = 0; j < nsd; j++) { coords(j) = position(i,j); } quantity_(i,0) = feMesh->map_to_element(coords); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomInElementSet //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomInElementSet::AtomInElementSet(ATC_Method * atc, PerAtomQuantity<int> * map, ESET eset, int type): atc_(atc,INTERNAL), map_(map),eset_(eset),type_(type), quantityToLammps_(atc_.atc_to_lammps_map()) { map_->register_dependence(this); needReset_ = true; } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomInElementSet::~AtomInElementSet() { map_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomInElementSet::reset() { if (map_->need_reset() || needReset_) { list_.clear(); INT_ARRAY map = map_->quantity(); int * type = ATC::LammpsInterface::instance()->atom_type(); ESET::const_iterator itr; const Array<int> & quantityToLammps = atc_.atc_to_lammps_map(); for (int i = 0; i < map.size(); i++) { for (itr=eset_.begin(); itr != eset_.end(); itr++) { if (map(i,0) == *itr) { int a = quantityToLammps(i); if (type[a] == type_) { list_.push_back(ID_PAIR(i,a)); break; } } } } needReset_ = false; } } //-------------------------------------------------------- // qauntity //-------------------------------------------------------- const ID_LIST & AtomInElementSet::quantity() { reset(); return list_; } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeUser //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeUser::AtomVolumeUser(ATC_Method * atc, map<int,double> & atomGroupVolume, AtomType atomType) : ProtectedAtomDiagonalMatrix<double>(atc,atomType), atomGroupVolume_(atomGroupVolume), lammpsInterface_(atc->lammps_interface()), atcToLammps_(atc->internal_to_atom_map()) { // do nothing } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomVolumeUser::reset() const { if (need_reset()) { PerAtomDiagonalMatrix<double>::reset(); const int *mask = lammpsInterface_->atom_mask(); quantity_ = 0.; map<int, double>::const_iterator igroup; for (igroup = atomGroupVolume_.begin(); igroup != atomGroupVolume_.end(); igroup++) { int gid = igroup->first; double weight = igroup->second; for (int i = 0; i < quantity_.nRows(); ++i) { if (mask[atcToLammps_(i)] & gid) { quantity_(i,i) = weight; } } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeGroup //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeGroup::AtomVolumeGroup(ATC_Method * atc, map<int,double> & atomGroupVolume, AtomType atomType) : AtomVolumeUser(atc,atomGroupVolume,atomType), atomGroupVolume_(atomGroupVolume), lammpsInterface_(atc->lammps_interface()), atcToLammps_(atc->internal_to_atom_map()) { // Uses group bounding box as total volume // ASSUME will *only* work if atoms exactly fill the box int ngroup = lammpsInterface_->ngroup(); const int *mask = lammpsInterface_->atom_mask(); double * bounds; bounds = new double[6]; for (int i = 0; i < ngroup; ++i) { lammpsInterface_->group_bounds(i, bounds); atomGroupVolume_[lammpsInterface_->group_bit(i)] = (bounds[1]-bounds[0])*(bounds[3]-bounds[2])*(bounds[5]-bounds[4]); } delete [] bounds; INT_VECTOR localCount(ngroup); INT_VECTOR globalCount(ngroup); // loop over atoms localCount = 0; for (int i = 0; i < atcToLammps_.size(); ++i) { for (int j = 0; j < ngroup; ++j) { if (mask[atcToLammps_(i)] & lammpsInterface_->group_bit(j)) localCount(j)++; } } // communication to get total atom counts per group lammpsInterface_->int_allsum(localCount.ptr(), globalCount.ptr(),ngroup); for (int i = 0; i < ngroup; ++i) { int iGroupBit = lammpsInterface_->group_bit(i); if (globalCount(i) > 0) atomGroupVolume_[iGroupBit] /= globalCount(i); else atomGroupVolume_[iGroupBit] = 0; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeLattice //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeLattice::AtomVolumeLattice(ATC_Method * atc, AtomType atomType) : ProtectedAtomDiagonalMatrix<double>(atc,atomType), lammpsInterface_(atc->lammps_interface()) { // do nothing } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomVolumeLattice::reset() const { if (need_reset()) { PerAtomDiagonalMatrix<double>::reset(); quantity_ = lammpsInterface_->volume_per_atom(); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeElement //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeElement::AtomVolumeElement(ATC_Method * atc, PerAtomQuantity<int> * atomElement, AtomType atomType) : ProtectedAtomDiagonalMatrix<double>(atc,atomType), atomElement_(atomElement), lammpsInterface_(atc->lammps_interface()), feMesh_((atc->fe_engine())->fe_mesh()) { if (!atomElement_) { InterscaleManager & interscaleManager(atc->interscale_manager()); atomElement_ = interscaleManager.per_atom_int_quantity("AtomElement"); } atomElement_->register_dependence(this); } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- AtomVolumeElement::~AtomVolumeElement() { atomElement_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomVolumeElement::reset() const { if (need_reset()) { PerAtomDiagonalMatrix<double>::reset(); int nElts = feMesh_->num_elements(); int nLocal = quantity_.nRows(); _elementAtomCountLocal_.reset(nElts); _elementAtomCount_.resize(nElts); const INT_ARRAY & atomElement(atomElement_->quantity()); _elementAtomVolume_.resize(nElts); // determine number of atoms in each element, partial sum for (int i = 0; i < nLocal; ++i) { _elementAtomCountLocal_(atomElement(i,0)) += 1; } // mpi to determine total atoms lammpsInterface_->int_allsum(_elementAtomCountLocal_.ptr(),_elementAtomCount_.ptr(),nElts); // divide element volume by total atoms to get atomic volume if (nLocal>0) { for (int i = 0; i < nElts; ++i) { double minx, maxx, miny, maxy, minz, maxz; feMesh_->element_coordinates(i,_nodalCoordinates_); minx = _nodalCoordinates_(0,0); maxx = _nodalCoordinates_(0,0); miny = _nodalCoordinates_(1,0); maxy = _nodalCoordinates_(1,0); minz = _nodalCoordinates_(2,0); maxz = _nodalCoordinates_(2,0); for (int j = 1; j < _nodalCoordinates_.nCols(); ++j) { if (_nodalCoordinates_(0,j)<minx) minx = _nodalCoordinates_(0,j); if (_nodalCoordinates_(0,j)>maxx) maxx = _nodalCoordinates_(0,j); if (_nodalCoordinates_(1,j)<miny) miny = _nodalCoordinates_(1,j); if (_nodalCoordinates_(1,j)>maxy) maxy = _nodalCoordinates_(1,j); if (_nodalCoordinates_(2,j)<minz) minz = _nodalCoordinates_(2,j); if (_nodalCoordinates_(2,j)>maxz) maxz = _nodalCoordinates_(2,j); } double eltVol = (maxx-minx)*(maxy-miny)*(maxz-minz); if (eltVol<0) eltVol *= -1.; _elementAtomVolume_(i) = eltVol/_elementAtomCount_(i); } for (int i = 0; i < nLocal; ++i) quantity_(i,i) = _elementAtomVolume_(atomElement(i,0)); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeRegion //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeRegion::AtomVolumeRegion(ATC_Method * atc, DENS_MAN * atomCoarseGrainingPositions, AtomType atomType) : ProtectedAtomDiagonalMatrix<double>(atc,atomType), lammpsInterface_(atc->lammps_interface()) { if (!atomCoarseGrainingPositions) { InterscaleManager & interscaleManager(atc->interscale_manager()); atomCoarseGrainingPositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); } atomCoarseGrainingPositions_->register_dependence(this); // compute volumes and atom counts in each region int nregion = lammpsInterface_->nregion(); regionalAtomVolume_.resize(nregion); for (int i = 0; i < nregion; ++i) { regionalAtomVolume_(i) = (lammpsInterface_->region_xhi(i)-lammpsInterface_->region_xlo(i)) *(lammpsInterface_->region_yhi(i)-lammpsInterface_->region_ylo(i)) *(lammpsInterface_->region_zhi(i)-lammpsInterface_->region_zlo(i)); } INT_VECTOR localCount(nregion); INT_VECTOR globalCount(nregion); // loop over atoms localCount = 0; const DENS_MAT atomicCoordinates(atomCoarseGrainingPositions->quantity()); for (int i = 0; i < quantity_.nRows(); ++i) { for (int j = 0; j < nregion; ++j) { if (lammpsInterface_->region_match(j, atomicCoordinates(i,0), atomicCoordinates(i,1), atomicCoordinates(i,2))) { localCount(j)++; } } } // communication to get total atom counts per group lammpsInterface_->int_allsum(localCount.ptr(), globalCount.ptr(),nregion); for (int i = 0; i < nregion; ++i) { if (globalCount(i) > 0) regionalAtomVolume_(i) /= globalCount(i); else regionalAtomVolume_(i) = 0; } } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- AtomVolumeRegion::~AtomVolumeRegion() { atomCoarseGrainingPositions_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomVolumeRegion::reset() const { if (need_reset()) { PerAtomDiagonalMatrix<double>::reset(); const DENS_MAT & atomicCoordinates(atomCoarseGrainingPositions_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { for (int iregion = 0; iregion < lammpsInterface_->nregion(); iregion++) { if (lammpsInterface_->region_match(iregion, atomicCoordinates(i,0), atomicCoordinates(i,0), atomicCoordinates(i,2))) quantity_(i,i) = regionalAtomVolume_(iregion); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomVolumeFile //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomVolumeFile::AtomVolumeFile(ATC_Method * atc, const string & atomVolumeFile, AtomType atomType) : ProtectedAtomDiagonalMatrix<double>(atc,atomType), atomVolumeFile_(atomVolumeFile), lammpsInterface_(atc->lammps_interface()) { // do nothing } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomVolumeFile::reset() const { if (need_reset()) { PerAtomDiagonalMatrix<double>::reset(); int nlocal = lammpsInterface_->nlocal(); ifstream in; const int lineSize = 256; char line[lineSize]; const char* fname = &atomVolumeFile_[0]; // create tag to local id map for this processor map <int,int> tag2id; map <int,int>::const_iterator itr; const 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 (ATC::LammpsInterface::instance()->rank_zero()) { in.open(fname); string msg; string name = atomVolumeFile_; msg = "no "+name+" atomic weights file found"; if (! in.good()) throw ATC_Error(msg); in.getline(line,lineSize); // header in.getline(line,lineSize); // blank line in.getline(line,lineSize); // number of atoms stringstream inss (line,stringstream::in | stringstream::out); inss >> natoms; // number of atoms stringstream ss; ss << " found " << natoms << " atoms in atomic weights file"; lammpsInterface_->print_msg(ss.str()); if (natoms != lammpsInterface_->natoms()) { throw ATC_Error("Incorrect number of atomic weights read-in!"); } in.getline(line,lineSize); // blank line } // read and assign atomic weights int nread = 0, tag = -1, count = 0; double atomic_weight; while (nread < natoms) { if (ATC::LammpsInterface::instance()->rank_zero()) { in.getline(line,lineSize); stringstream ss (line,stringstream::in | stringstream::out); ss >> tag >> atomic_weight; nread++; } lammpsInterface_->int_broadcast(&nread); lammpsInterface_->int_broadcast(&tag); lammpsInterface_->broadcast(&atomic_weight); itr = tag2id.find(tag); if (itr != tag2id.end()) { int iatom = itr->second; quantity_(iatom,0) = atomic_weight; count++; } } if (lammpsInterface_->rank_zero()) { in.close(); stringstream ss; ss << " read " << nread << " atomic weights"; lammpsInterface_->print_msg(ss.str()); } if (count != nlocal) throw ATC_Error("reset "+to_string(count)+" atoms vs "+to_string(nlocal)); } } // need to add capability to take in group bit (JAT, 04/02/11) //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomNumber //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomNumber::AtomNumber(ATC_Method * atc, AtomType atomType) : ProtectedAtomQuantity<double>(atc,1,atomType), atc_(atc) { } void AtomNumber::reset() const { int nlocal = atc_->nlocal(); quantity_.reset(nlocal,1); quantity_ = 1; } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomTypeVector //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomTypeVector::AtomTypeVector(ATC_Method * atc, vector<int> typeList, AtomType atomType) : ProtectedAtomQuantity<double>(atc, typeList.size(), atomType), atc_(atc), ntypes_(ATC::LammpsInterface::instance()->ntypes()), typeList_(typeList) { if (typeList_.size() == 0) throw ATC_Error("type list is empty"); index_.resize(ntypes_,-1); for (unsigned int i = 0; i < typeList_.size(); i++) { index_[typeList_[i]] = i; } } AtomTypeVector::AtomTypeVector(ATC_Method * atc, vector<int> typeList, vector<int> groupList, AtomType atomType) : ProtectedAtomQuantity<double>(atc, typeList.size()+groupList.size(), atomType), atc_(atc), ntypes_(ATC::LammpsInterface::instance()->ntypes()), typeList_(typeList), groupList_(groupList) { if ((typeList_.size() == 0) && (groupList_.size() == 0)) throw ATC_Error("type/group lists are empty"); // reverse map index_.resize(ntypes_,-1); for (unsigned int i = 0; i < typeList_.size(); i++) { index_[typeList_[i]-1] = i; } } void AtomTypeVector::reset() const { if (need_reset()) { //PerAtomQuantity<double>::reset(); int nlocal = atc_->nlocal(); quantity_.reset(nlocal,typeList_.size()+groupList_.size()); const Array<int> & quantityToLammps = (PerAtomQuantity<double>::atc_).atc_to_lammps_map(); if (typeList_.size()) { int * type = ATC::LammpsInterface::instance()->atom_type(); for (int i = 0; i < nlocal; i++) { int a = quantityToLammps(i); int index = index_[type[a]-1]; if (index > -1) quantity_(i,index) = 1; } } int index = typeList_.size(); if (groupList_.size()) { const int * mask = ATC::LammpsInterface::instance()->atom_mask(); for (unsigned int j = 0; j < groupList_.size(); j++) { int group = groupList_[j]; for (int i = 0; i < nlocal; i++) { int a = quantityToLammps(i); if (mask[a] & group) quantity_(i,index) = 1; } index++; } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class XrefWrapper //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- XrefWrapper::XrefWrapper(ATC_Method * atc, AtomType atomType) : ProtectedClonedAtomQuantity<double>(atc,atc->nsd(),atomType), atc_(atc) { // do nothing } //-------------------------------------------------------- // lammps_vector //-------------------------------------------------------- double ** XrefWrapper::lammps_vector() const { return atc_->xref(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicMassWeightedDisplacement //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicMassWeightedDisplacement::AtomicMassWeightedDisplacement(ATC_Method * atc, PerAtomQuantity<double> * atomPositions, PerAtomQuantity<double> * atomMasses, PerAtomQuantity<double> * atomReferencePositions, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), atomPositions_(atomPositions), atomMasses_(atomMasses), atomReferencePositions_(atomReferencePositions) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomPositions_) atomPositions_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_POSITION, atomType); if (!atomMasses_) atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); if (!atomReferencePositions_) atomReferencePositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); atomPositions_->register_dependence(this); atomMasses_->register_dependence(this); atomReferencePositions_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicMassWeightedDisplacement::~AtomicMassWeightedDisplacement() { atomPositions_->remove_dependence(this); atomMasses_->remove_dependence(this); atomReferencePositions_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicMassWeightedDisplacement::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & position(atomPositions_->quantity()); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & refPosition(atomReferencePositions_->quantity()); // q = m * (x - xref) quantity_ = position; quantity_ -= refPosition; quantity_ *= mass; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicMomentum //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicMomentum::AtomicMomentum(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMasses, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), atomVelocities_(atomVelocities), atomMasses_(atomMasses) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, - atomType); + atomType); if (!atomMasses_) atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); atomVelocities_->register_dependence(this); atomMasses_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicMomentum::~AtomicMomentum() { atomVelocities_->remove_dependence(this); atomMasses_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicMomentum::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocity(atomVelocities_->quantity()); // q = m * v quantity_ = velocity; quantity_ *= mass; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class TwiceKineticEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- TwiceKineticEnergy::TwiceKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMasses, AtomType atomType) : AtomicEnergyForTemperature(atc,atomType), atomVelocities_(atomVelocities), atomMasses_(atomMasses) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); if (!atomMasses_) atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); atomVelocities_->register_dependence(this); atomMasses_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- TwiceKineticEnergy::~TwiceKineticEnergy() { atomVelocities_->remove_dependence(this); atomMasses_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void TwiceKineticEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocity(atomVelocities_->quantity()); // q = m * (v dot v) for (int i = 0; i < quantity_.nRows(); i++) { quantity_(i,0) = velocity(i,0)*velocity(i,0); for (int j = 1; j < velocity.nCols(); j++) { quantity_(i,0) += velocity(i,j)*velocity(i,j); } quantity_(i,0) *= mass(i,0); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class FluctuatingVelocity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- FluctuatingVelocity::FluctuatingVelocity(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMeanVelocities, AtomType atomType) : ProtectedAtomQuantity<double>(atc,3,atomType), atomVelocities_(atomVelocities), atomMeanVelocities_(atomMeanVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) { atomVelocities_ = interscaleManager.fundamental_atom_quantity( LammpsInterface::ATOM_VELOCITY, atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); atomMeanVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- FluctuatingVelocity::~FluctuatingVelocity() { atomVelocities_->remove_dependence(this); atomMeanVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void FluctuatingVelocity::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & velocity(atomVelocities_->quantity()); const DENS_MAT & meanVelocity(atomMeanVelocities_->quantity()); // q = m * (v dot v) for (int i = 0; i < quantity_.nRows(); i++) { for (int j = 0; j < velocity.nCols(); j++) { quantity_(i,j) = velocity(i,j) - meanVelocity(i,j); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class ChargeVelocity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- ChargeVelocity::ChargeVelocity(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, FundamentalAtomQuantity * atomCharge, AtomType atomType) : ProtectedAtomQuantity<double>(atc,3,atomType), fluctuatingVelocities_(atomVelocities), atomCharge_(atomCharge) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!fluctuatingVelocities_) { fluctuatingVelocities_ = interscaleManager.per_atom_quantity("AtomicFluctuatingVelocity"); } if (!atomCharge_) { atomCharge_ = interscaleManager.fundamental_atom_quantity( LammpsInterface::ATOM_CHARGE, atomType); } fluctuatingVelocities_->register_dependence(this); atomCharge_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- ChargeVelocity::~ChargeVelocity() { fluctuatingVelocities_->remove_dependence(this); atomCharge_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void ChargeVelocity::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & velocity(fluctuatingVelocities_->quantity()); const DENS_MAT & charge(atomCharge_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { for (int j = 0; j < velocity.nCols(); j++) { quantity_(i,j) = charge(i,0)*velocity(i,j); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class SpeciesVelocity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- SpeciesVelocity::SpeciesVelocity(ATC_Method * atc, PerAtomQuantity<double> * fluctuatingVelocities, PerAtomQuantity<double> * atomTypeVector, AtomType atomType) : ProtectedAtomQuantity<double>(atc,3*(atomTypeVector->nCols()),atomType), fluctuatingVelocities_(fluctuatingVelocities), atomTypeVector_(atomTypeVector) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!fluctuatingVelocities_) { fluctuatingVelocities_ = interscaleManager.per_atom_quantity("AtomicFluctuatingVelocity"); } if (!atomTypeVector_) { atomTypeVector_ = interscaleManager.per_atom_quantity("AtomicTypeVector"); } fluctuatingVelocities_->register_dependence(this); atomTypeVector_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- SpeciesVelocity::~SpeciesVelocity() { fluctuatingVelocities_->remove_dependence(this); atomTypeVector_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void SpeciesVelocity::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & velocity(fluctuatingVelocities_->quantity()); const DENS_MAT & tv(atomTypeVector_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { int m = 0; for (int j = 0; j < velocity.nCols(); j++) { for (int k = 0; j < tv.nCols(); j++) { quantity_(i,m++) = tv(i,k)*velocity(i,j); } } } } } + //-------------------------------------------------------- //-------------------------------------------------------- // Class TwiceFluctuatingKineticEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- TwiceFluctuatingKineticEnergy::TwiceFluctuatingKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMasses, PerAtomQuantity<double> * atomMeanVelocities, AtomType atomType) : AtomicEnergyForTemperature(atc,atomType), atomVelocities_(atomVelocities), atomMasses_(atomMasses), atomMeanVelocities_(atomMeanVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) { atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); } if (!atomMasses_) { atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); atomMasses_->register_dependence(this); atomMeanVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- TwiceFluctuatingKineticEnergy::~TwiceFluctuatingKineticEnergy() { atomVelocities_->remove_dependence(this); atomMasses_->remove_dependence(this); atomMeanVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void TwiceFluctuatingKineticEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocity(atomVelocities_->quantity()); const DENS_MAT & meanVelocity(atomMeanVelocities_->quantity()); // q = m * (v dot v) double vRel; for (int i = 0; i < quantity_.nRows(); i++) { vRel = velocity(i,0) - meanVelocity(i,0); quantity_(i,0) = vRel*vRel; for (int j = 1; j < velocity.nCols(); j++) { vRel = velocity(i,j) - meanVelocity(i,j); quantity_(i,0) += vRel*vRel;; } quantity_(i,0) *= mass(i,0); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KineticTensor //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- KineticTensor::KineticTensor(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMasses, AtomType atomType) : ProtectedAtomQuantity<double>(atc,6,atomType), atomVelocities_(atomVelocities), atomMasses_(atomMasses) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) { atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); } if (!atomMasses_) { atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } atomVelocities_->register_dependence(this); atomMasses_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- KineticTensor::~KineticTensor() { atomVelocities_->remove_dependence(this); atomMasses_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void KineticTensor::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocity(atomVelocities_->quantity()); // K = m * (v \otimes v) for (int i = 0; i < quantity_.nRows(); i++) { double m = mass(i,0); double v[3] = {velocity(i,0),velocity(i,1),velocity(i,2)}; quantity_(i,0) -= m*v[0]*v[0]; quantity_(i,1) -= m*v[1]*v[1]; quantity_(i,2) -= m*v[2]*v[2]; quantity_(i,3) -= m*v[0]*v[1]; quantity_(i,4) -= m*v[0]*v[2]; quantity_(i,5) -= m*v[1]*v[2]; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class FluctuatingKineticTensor //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- FluctuatingKineticTensor::FluctuatingKineticTensor(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, PerAtomQuantity<double> * atomMasses, PerAtomQuantity<double> * atomMeanVelocities, AtomType atomType) : ProtectedAtomQuantity<double>(atc,6,atomType), atomVelocities_(atomVelocities), atomMasses_(atomMasses) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) { atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); } if (!atomMasses_) { atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } if (!atomMeanVelocities_) { - atomMeanVelocities_ = interscaleManager.per_atom_quantity("AtomicMeanVelocity"); + atomMeanVelocities_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); } atomVelocities_->register_dependence(this); atomMasses_->register_dependence(this); atomMeanVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- FluctuatingKineticTensor::~FluctuatingKineticTensor() { atomVelocities_->remove_dependence(this); atomMasses_->remove_dependence(this); atomMeanVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void FluctuatingKineticTensor::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocity(atomVelocities_->quantity()); const DENS_MAT & meanVelocity(atomMeanVelocities_->quantity()); // K = m * (v \otimes v) for (int i = 0; i < quantity_.nRows(); i++) { double m = mass(i,0); double v[3] = {velocity(i,0)-meanVelocity(i,0), velocity(i,1)-meanVelocity(i,0), velocity(i,2)-meanVelocity(i,0)}; quantity_(i,0) -= m*v[0]*v[0]; quantity_(i,1) -= m*v[1]*v[1]; quantity_(i,2) -= m*v[2]*v[2]; quantity_(i,3) -= m*v[0]*v[1]; quantity_(i,4) -= m*v[0]*v[2]; quantity_(i,5) -= m*v[1]*v[2]; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class MixedKePeEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- MixedKePeEnergy::MixedKePeEnergy(ATC_Method * atc, double keMultiplier, double peMultiplier, PerAtomQuantity<double> * twiceKineticEnergy, PerAtomQuantity<double> * potentialEnergy, AtomType atomType) : AtomicEnergyForTemperature(atc,atomType), keMultiplier_(keMultiplier), peMultiplier_(peMultiplier), twiceKineticEnergy_(twiceKineticEnergy), potentialEnergy_(potentialEnergy) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!twiceKineticEnergy_) { twiceKineticEnergy_ = interscaleManager.per_atom_quantity("AtomicTwiceKineticEnergy"); } if (!potentialEnergy_) { potentialEnergy_ = interscaleManager.per_atom_quantity("AtomicFluctuatingPotentialEnergy"); } twiceKineticEnergy_->register_dependence(this); potentialEnergy_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- MixedKePeEnergy::~MixedKePeEnergy() { twiceKineticEnergy_->remove_dependence(this); potentialEnergy_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void MixedKePeEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & twoKe(twiceKineticEnergy_->quantity()); const DENS_MAT & pe(potentialEnergy_->quantity()); // q = peScale * pe + keScale * ke quantity_ = pe; quantity_ *= peMultiplier_; quantity_ += (keMultiplier_/2.)*twoKe; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class TotalEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- TotalEnergy::TotalEnergy(ATC_Method * atc, PerAtomQuantity<double> * twiceKineticEnergy, PerAtomQuantity<double> * potentialEnergy, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atomType), twiceKineticEnergy_(twiceKineticEnergy), potentialEnergy_(potentialEnergy) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!twiceKineticEnergy_) { twiceKineticEnergy_ = interscaleManager.per_atom_quantity("AtomicTwiceKineticEnergy"); } if (!potentialEnergy_) { potentialEnergy_ = interscaleManager.per_atom_quantity("AtomicPotentialEnergy"); } twiceKineticEnergy_->register_dependence(this); potentialEnergy_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- TotalEnergy::~TotalEnergy() { twiceKineticEnergy_->remove_dependence(this); potentialEnergy_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void TotalEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & twoKe(twiceKineticEnergy_->quantity()); const DENS_MAT & pe(potentialEnergy_->quantity()); quantity_ = pe; quantity_ += (0.5)*twoKe; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class FluctuatingPotentialEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- FluctuatingPotentialEnergy::FluctuatingPotentialEnergy(ATC_Method * atc, PerAtomQuantity<double> * potentialEnergy, PerAtomQuantity<double> * referencePotential, AtomType atomType) : AtomicEnergyForTemperature(atc,atomType), potentialEnergy_(potentialEnergy), referencePotential_(referencePotential) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!potentialEnergy_) { potentialEnergy_ = interscaleManager.per_atom_quantity("AtomicPotentialEnergy"); } if (!referencePotential_) { referencePotential_ = interscaleManager.per_atom_quantity("AtomicReferencePotential"); } potentialEnergy_->register_dependence(this); referencePotential_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- FluctuatingPotentialEnergy::~FluctuatingPotentialEnergy() { potentialEnergy_->remove_dependence(this); referencePotential_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void FluctuatingPotentialEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & pe(potentialEnergy_->quantity()); const DENS_MAT & refPe(referencePotential_->quantity()); quantity_ = pe; quantity_ -= refPe; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class DotTwiceKineticEnergy //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- DotTwiceKineticEnergy::DotTwiceKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomForces, PerAtomQuantity<double> * atomVelocities, AtomType atomType) : ProtectedAtomQuantity<double>(atc,1,atomType), atomForces_(atomForces), atomVelocities_(atomVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomForces_) atomForces_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE, atomType); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); atomForces_->register_dependence(this); atomVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- DotTwiceKineticEnergy::~DotTwiceKineticEnergy() { atomForces_->remove_dependence(this); atomVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void DotTwiceKineticEnergy::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & velocity(atomVelocities_->quantity()); const DENS_MAT & force(atomForces_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { quantity_(i,0) = velocity(i,0)*force(i,0); for (int j = 1; j < velocity.nCols(); j++) { quantity_(i,0) += velocity(i,j)*force(i,j); } quantity_(i,0) *= 2.; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocitySquared //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- VelocitySquared::VelocitySquared(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities, AtomType atomType) : ProtectedAtomQuantity<double>(atc,1,atomType), atomVelocities_(atomVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, - atomType); + atomType); atomVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- VelocitySquared::~VelocitySquared() { atomVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void VelocitySquared::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & velocity(atomVelocities_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { quantity_(i,0) = velocity(i,0)*velocity(i,0); for (int j = 1; j < velocity.nCols(); j++) { quantity_(i,0) += velocity(i,j)*velocity(i,j); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class LambdaSquared //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- LambdaSquared::LambdaSquared(ATC_Method * atc, PerAtomQuantity<double> * atomMasses, PerAtomQuantity<double> * atomVelocitiesSquared, PerAtomQuantity<double> * atomLambdas, AtomType atomType) : ProtectedAtomQuantity<double>(atc,1,atomType), atomMasses_(atomMasses), atomVelocitiesSquared_(atomVelocitiesSquared), atomLambdas_(atomLambdas) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomMasses_) { atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } atomMasses_->register_dependence(this); if (!atomVelocitiesSquared) { atomVelocitiesSquared_ = interscaleManager.per_atom_quantity("LambdaSquared"); } atomVelocitiesSquared_->register_dependence(this); if (!atomLambdas_) { atomLambdas_ = interscaleManager.per_atom_quantity("AtomLambdaEnergy"); } atomLambdas_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- LambdaSquared::~LambdaSquared() { atomMasses_->remove_dependence(this); atomVelocitiesSquared_->remove_dependence(this); atomLambdas_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void LambdaSquared::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocitySquared(atomVelocitiesSquared_->quantity()); const DENS_MAT & lambda(atomLambdas_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { quantity_(i,0) = lambda(i,0)*lambda(i,0)*velocitySquared(i,0)/mass(i,0); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomToType //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomToType::AtomToType(ATC_Method * atc, int type, AtomType atomType) : LargeToSmallAtomMap(atc,atomType), type_(type) { // DO NOTHING } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomToType::reset() const { if (need_reset()) { PerAtomQuantity<int>::reset(); quantity_ = -1.; size_ = 0; const int * type = lammpsInterface_->atom_type(); const Array<int> & quantityToLammps = atc_.atc_to_lammps_map(); for (int i = 0; i < quantity_.nRows(); ++i) { int atomIdx = quantityToLammps(i); if (type[atomIdx] == type_) { quantity_(i,0) = size_; ++size_; } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomToGroup //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomToGroup::AtomToGroup(ATC_Method * atc, int group, AtomType atomType) : LargeToSmallAtomMap(atc,atomType), group_(group) { // DO NOTHING } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomToGroup::reset() const { if (need_reset()) { PerAtomQuantity<int>::reset(); quantity_ = -1.; size_ = 0; const int * mask = lammpsInterface_->atom_mask(); const Array<int> & quantityToLammps = atc_.atc_to_lammps_map(); for (int i = 0; i < quantity_.nRows(); ++i) { int atomIdx = quantityToLammps(i); if (mask[atomIdx] & group_) { quantity_(i,0) = size_; ++size_; } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomToNodeset //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomToNodeset::AtomToNodeset(ATC_Method * atc, SetDependencyManager<int> * subsetNodes, PerAtomQuantity<int> * atomElement, AtomType atomType) : LargeToSmallAtomMap(atc,atomType), subsetNodes_(subsetNodes), atomElement_(atomElement), feMesh_((atc->fe_engine())->fe_mesh()) { if (!atomElement_) { atomElement_ = (atc->interscale_manager()).per_atom_int_quantity("AtomElement"); } if (atomElement_) { atomElement_->register_dependence(this); } else { throw ATC_Error("PerAtomQuantityLibrary::AtomToRegulated - No AtomElement provided"); } subsetNodes_->register_dependence(this); } //-------------------------------------------------------- // quantity //-------------------------------------------------------- void AtomToNodeset::reset() const { //so it has been commented out. /* if (need_reset()) { PerAtomQuantity<int>::reset(); const INT_ARRAY & atomElement(atomElement_->quantity()); const set<int> & subsetNodes(subsetNodes_->quantity()); int nLocal = atomElement.nRows(); quantity_.resize(nLocal,1); quantity_ = -1; size_ = 0; for (int i = 0; i < nLocal; ++i) { feMesh_->element_connectivity_unique(atomElement(i,0),_nodes_); for (int j = 0; j < _nodes_.size(); ++j) { if (subsetNodes.find(_nodes_(j)) != subsetNodes.end()) { quantity_(i,0) = size_; size_++; break; } } } } */ } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomToElementset //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomToElementset::AtomToElementset(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, bool> * elementMask, PerAtomQuantity<int> * atomElement, AtomType atomType) : LargeToSmallAtomMap(atc,atomType), elementMask_(elementMask), atomElement_(atomElement), feMesh_((atc->fe_engine())->fe_mesh()) { if (!atomElement_) { atomElement_ = (atc->interscale_manager()).per_atom_int_quantity("AtomElement"); } if (atomElement_) { atomElement_->register_dependence(this); } else { throw ATC_Error("PerAtomQuantityLibrary::AtomToRegulated - No AtomElement provided"); } elementMask_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomToElementset::~AtomToElementset() { atomElement_->remove_dependence(this); elementMask_->remove_dependence(this); } //-------------------------------------------------------- // quantity //-------------------------------------------------------- void AtomToElementset::reset() const { if (need_reset()) { PerAtomQuantity<int>::reset(); const INT_ARRAY & atomElement(atomElement_->quantity()); const DenseMatrix<bool> & elementMask(elementMask_->quantity()); int nLocal = atomElement.nRows(); quantity_.resize(nLocal,1); quantity_ = -1; size_ = 0; for (int i = 0; i < nLocal; ++i) { if (elementMask(atomElement(i,0),0)) { quantity_(i,0) = size_; size_++; } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class MappedAtomQuantity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- MappedAtomQuantity::MappedAtomQuantity(ATC_Method * atc, PerAtomQuantity<double> * source, LargeToSmallAtomMap * map, AtomType atomType) : ProtectedMappedAtomQuantity<double>(atc,map,source->nCols(),atomType), source_(source), map_(map) { source_->register_dependence(this); map_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void MappedAtomQuantity::reset() const { if (needReset_) { ProtectedMappedAtomQuantity<double>::reset(); const DENS_MAT & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); quantity_.resize(map_->size(),source.nCols()); for (int i = 0; i < source.nRows(); i++) { int idx = map(i,0); if (idx > -1) { for (int j = 0; j < source.nCols(); j++) { quantity_(idx,j) = source(i,j); } } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class VelocitySquaredMapped //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- VelocitySquaredMapped::VelocitySquaredMapped(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * atomMap, PerAtomQuantity<double> * atomVelocities, AtomType atomType) : ProtectedMappedAtomQuantity<double>(atc,atomMap,1,atomType), atomVelocities_(atomVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomVelocities_) atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); atomVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- VelocitySquaredMapped::~VelocitySquaredMapped() { atomVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void VelocitySquaredMapped::reset() const { if (need_reset()) { ProtectedMappedAtomQuantity<double>::reset(); const DENS_MAT & velocity(atomVelocities_->quantity()); const INT_ARRAY & atomMap(atomMap_->quantity()); for (int i = 0; i < atomMap.nRows(); i++) { int idx = atomMap(i,0); if (idx > -1) { quantity_(idx,0) = velocity(i,0)*velocity(i,0); for (int j = 1; j < velocity.nCols(); j++) { quantity_(idx,0) += velocity(i,j)*velocity(i,j); } } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class LambdaSquaredMapped //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- LambdaSquaredMapped::LambdaSquaredMapped(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * atomMap, PerAtomQuantity<double> * atomMasses, PerAtomQuantity<double> * atomVelocitiesSquared, PerAtomQuantity<double> * atomLambdas, AtomType atomType) : ProtectedMappedAtomQuantity<double>(atc,atomMap,1,atomType), atomMasses_(atomMasses), atomVelocitiesSquared_(atomVelocitiesSquared), atomLambdas_(atomLambdas) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomMasses_) { atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } atomMasses_->register_dependence(this); if (!atomVelocitiesSquared) { atomVelocitiesSquared_ = interscaleManager.per_atom_quantity("LambdaSquared"); } atomVelocitiesSquared_->register_dependence(this); if (!atomLambdas_) { atomLambdas_ = interscaleManager.per_atom_quantity("AtomLambdaEnergy"); } atomLambdas_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- LambdaSquaredMapped::~LambdaSquaredMapped() { atomMasses_->remove_dependence(this); atomVelocitiesSquared_->remove_dependence(this); atomLambdas_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void LambdaSquaredMapped::reset() const { if (need_reset()) { ProtectedMappedAtomQuantity<double>::reset(); const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & velocitySquared(atomVelocitiesSquared_->quantity()); const DENS_MAT & lambda(atomLambdas_->quantity()); const INT_ARRAY & atomMap(atomMap_->quantity()); for (int i = 0; i < atomMap.nRows(); i++) { int idx = atomMap(i,0); if (idx > -1) { quantity_(idx,0) = lambda(i,0)*lambda(i,0)*velocitySquared(idx,0)/mass(i,0); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class HeatCapacity // computes the classical atomic heat capacity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- HeatCapacity::HeatCapacity(ATC_Method * atc, AtomType atomType) : ConstantQuantity<double>(atc,1,1,atomType) { constant_ = (atc->nsd())*(lammpsInterface_->kBoltzmann()); } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicVelocityRescaleFactor //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicVelocityRescaleFactor::AtomicVelocityRescaleFactor(ATC_Method * atc, PerAtomQuantity<double> * atomLambdas, AtomType atomType) : ProtectedAtomQuantity<double>(atc,1,atomType), - atomMasses_(NULL), atomLambdas_(atomLambdas) { InterscaleManager & interscaleManager(atc->interscale_manager()); - atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, - atomType); if (!atomLambdas) { atomLambdas_ = interscaleManager.per_atom_quantity("AtomLambdaEnergy"); } - - atomMasses_->register_dependence(this); atomLambdas_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicVelocityRescaleFactor::~AtomicVelocityRescaleFactor() { - atomMasses_->remove_dependence(this); atomLambdas_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicVelocityRescaleFactor::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); - const DENS_MAT & mass(atomMasses_->quantity()); const DENS_MAT & lambda(atomLambdas_->quantity()); for (int i = 0; i < quantity_.nRows(); i++) { - quantity_(i,0) = sqrt(lambda(i,0)/mass(i,0)); + quantity_(i,0) = sqrt(lambda(i,0)); + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicFluctuatingVelocityRescaled + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // constructor + //-------------------------------------------------------- + AtomicFluctuatingVelocityRescaled::AtomicFluctuatingVelocityRescaled(ATC_Method * atc, + PerAtomQuantity<double> * atomRescaleFactor, + PerAtomQuantity<double> * atomFluctuatingVelocity, + AtomType atomType) : + ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), + atomRescaleFactor_(atomRescaleFactor), + atomFluctuatingVelocity_(atomFluctuatingVelocity) + { + InterscaleManager & interscaleManager(atc->interscale_manager()); + if (!atomRescaleFactor_) { + atomRescaleFactor_ = interscaleManager.per_atom_quantity("AtomVelocityRescaling"); + } + if (!atomFluctuatingVelocity_) { + atomFluctuatingVelocity_ = interscaleManager.per_atom_quantity("AtomicFluctuatingVelocity"); + } + + atomRescaleFactor_->register_dependence(this); + atomFluctuatingVelocity_->register_dependence(this); + } + + //-------------------------------------------------------- + // destructor + //-------------------------------------------------------- + AtomicFluctuatingVelocityRescaled::~AtomicFluctuatingVelocityRescaled() + { + atomRescaleFactor_->remove_dependence(this); + atomFluctuatingVelocity_->remove_dependence(this); + } + + //-------------------------------------------------------- + // reset + //-------------------------------------------------------- + void AtomicFluctuatingVelocityRescaled::reset() const + { + if (need_reset()) { + PerAtomQuantity<double>::reset(); + const DENS_MAT & factor(atomRescaleFactor_->quantity()); + const DENS_MAT & v(atomFluctuatingVelocity_->quantity()); + + for (int i = 0; i < quantity_.nRows(); i++) { + for (int j = 0; j < quantity_.nCols(); j++) { + quantity_(i,j) = factor(i,0)*v(i,j); + } + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicCombinedRescaleThermostatError + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // constructor + //-------------------------------------------------------- + AtomicCombinedRescaleThermostatError::AtomicCombinedRescaleThermostatError(ATC_Method * atc, + PerAtomQuantity<double> * atomFluctuatingMomentumRescaled, + PerAtomQuantity<double> * atomMeanVelocity, + PerAtomQuantity<double> * atomStreamingVelocity, + PerAtomQuantity<double> * atomMass, + AtomType atomType) : + ProtectedAtomQuantity<double>(atc,1,atomType), + atomFluctuatingMomentumRescaled_(atomFluctuatingMomentumRescaled), + atomMeanVelocity_(atomMeanVelocity), + atomStreamingVelocity_(atomStreamingVelocity), + atomMass_(atomMass) + { + InterscaleManager & interscaleManager(atc->interscale_manager()); + if (!atomFluctuatingMomentumRescaled_) { + atomFluctuatingMomentumRescaled_ = interscaleManager.per_atom_quantity("AtomFluctuatingMomentumRescaled"); + } + if (!atomMeanVelocity_) { + atomMeanVelocity_ = interscaleManager.per_atom_quantity(field_to_prolongation_name(VELOCITY)); + } + if (!atomStreamingVelocity_) { + atomStreamingVelocity_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); + } + if (!atomMass_) { + atomMass_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, + atomType); + } + + atomFluctuatingMomentumRescaled_->register_dependence(this); + atomMeanVelocity_->register_dependence(this); + atomStreamingVelocity_->register_dependence(this); + atomMass_->register_dependence(this); + } + + //-------------------------------------------------------- + // destructor + //-------------------------------------------------------- + AtomicCombinedRescaleThermostatError::~AtomicCombinedRescaleThermostatError() + { + atomFluctuatingMomentumRescaled_->remove_dependence(this); + atomMeanVelocity_->remove_dependence(this); + atomStreamingVelocity_->remove_dependence(this); + atomMass_->remove_dependence(this); + } + + //-------------------------------------------------------- + // reset + //-------------------------------------------------------- + void AtomicCombinedRescaleThermostatError::reset() const + { + if (need_reset()) { + PerAtomQuantity<double>::reset(); + const DENS_MAT & m(atomMass_->quantity()); + const DENS_MAT & p(atomFluctuatingMomentumRescaled_->quantity()); + const DENS_MAT & v(atomMeanVelocity_->quantity()); + const DENS_MAT & s(atomStreamingVelocity_->quantity()); + + double diff; + for (int i = 0; i < quantity_.nRows(); i++) { + diff = s(i,0)-v(i,0); + quantity_(i,0) = 2.*p(i,0)*diff + m(i,0)*diff*diff; + for (int j = 1; j < p.nCols(); j++) { + diff = s(i,j)-v(i,j); + quantity_(i,0) += 2.*p(i,j)*diff + m(i,0)*diff*diff; + } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicThermostatForce //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicThermostatForce::AtomicThermostatForce(ATC_Method * atc, PerAtomQuantity<double> * atomLambdas, PerAtomQuantity<double> * atomVelocities, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), atomLambdas_(atomLambdas), atomVelocities_(atomVelocities) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomLambdas) { atomLambdas_ = interscaleManager.per_atom_quantity("AtomLambdaEnergy"); } if (!atomVelocities_) { atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY, atomType); } atomLambdas_->register_dependence(this); atomVelocities_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicThermostatForce::~AtomicThermostatForce() { atomLambdas_->remove_dependence(this); atomVelocities_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicThermostatForce::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & v(atomVelocities_->quantity()); const DENS_MAT & lambda(atomLambdas_->quantity()); // force = -lambda*v quantity_ = v; quantity_ *= lambda; quantity_ *= -1.; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicKinetostatForceDisplacement //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicKinetostatForceDisplacement::AtomicKinetostatForceDisplacement(ATC_Method * atc, PerAtomQuantity<double> * atomLambda, PerAtomQuantity<double> * atomMass, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), atomLambda_(atomLambda), atomMass_(atomMass) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomLambda) { atomLambda_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); } if (!atomMass_) { atomMass_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS, atomType); } atomLambda_->register_dependence(this); atomMass_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicKinetostatForceDisplacement::~AtomicKinetostatForceDisplacement() { atomLambda_->remove_dependence(this); atomMass_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicKinetostatForceDisplacement::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & m(atomMass_->quantity()); const DENS_MAT & lambda(atomLambda_->quantity()); double timeFactor = time_step_factor(0.5*atc_.dt()); //force = -lambda*m*(timestep factor) quantity_ = lambda; quantity_ *= m; quantity_ *= -1.*timeFactor; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomicKinetostatForceStress //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- AtomicKinetostatForceStress::AtomicKinetostatForceStress(ATC_Method * atc, PerAtomQuantity<double> * atomLambda, AtomType atomType) : ProtectedAtomQuantity<double>(atc,atc->nsd(),atomType), atomLambda_(atomLambda) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomLambda_) { atomLambda_ = interscaleManager.per_atom_quantity("AtomLambdaMomentum"); } atomLambda_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- AtomicKinetostatForceStress::~AtomicKinetostatForceStress() { atomLambda_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void AtomicKinetostatForceStress::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & lambda(atomLambda_->quantity()); // force = -lambda quantity_ = lambda; quantity_ *= -1.; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class PerAtomKernelFunction //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- PerAtomKernelFunction::PerAtomKernelFunction(ATC_Method * atc, PerAtomQuantity<double> * atomPositions, AtomType atomType) : ProtectedAtomSparseMatrix<double>(atc,(atc->fe_engine())->num_nodes(),atc->accumulant_bandwidth(),atomType), atomPositions_(atomPositions), feEngine_(atc->fe_engine()) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomPositions_) { atomPositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); } atomPositions_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- PerAtomKernelFunction::~PerAtomKernelFunction() { atomPositions_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void PerAtomKernelFunction::reset() const { if (need_reset()) { PerAtomSparseMatrix<double>::reset(); const DENS_MAT & positions(atomPositions_->quantity()); if (positions.nRows() > 0) { feEngine_->evaluate_kernel_functions(positions,quantity_); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class PerAtomShapeFunction //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- PerAtomShapeFunction::PerAtomShapeFunction(ATC_Method * atc, PerAtomQuantity<double> * atomPositions, PerAtomQuantity<int> * atomElements, AtomType atomType) : ProtectedAtomSparseMatrix<double>(atc,atc->num_nodes(),(atc->fe_engine())->num_nodes_per_element(),atomType), atomPositions_(atomPositions), atomElements_(atomElements), feEngine_(atc->fe_engine()) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomPositions_) { atomPositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); } if (!atomElements_) { atomElements_ = interscaleManager.per_atom_int_quantity("AtomElement"); } atomPositions_->register_dependence(this); atomElements_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- PerAtomShapeFunction::~PerAtomShapeFunction() { atomPositions_->remove_dependence(this); atomElements_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void PerAtomShapeFunction::reset() const { if (need_reset()) { PerAtomSparseMatrix<double>::reset(); const DENS_MAT & positions(atomPositions_->quantity()); const INT_ARRAY & elements(atomElements_->quantity()); if (positions.nRows() > 0) { feEngine_->evaluate_shape_functions(positions, elements, quantity_); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class LambdaCouplingMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- LambdaCouplingMatrix::LambdaCouplingMatrix(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap, SPAR_MAN * shapeFunction) : ProtectedMappedAtomSparseMatrix<double>(atc,nodeToOverlapMap->size(), (atc->fe_engine())->num_nodes_per_element(), INTERNAL), shapeFunction_(shapeFunction), nodeToOverlapMap_(nodeToOverlapMap) { if (!shapeFunction_) { shapeFunction_ = (atc->interscale_manager()).per_atom_sparse_matrix("Interpolant");; } if (!nodeToOverlapMap_) { nodeToOverlapMap_ = (atc->interscale_manager()).dense_matrix_int("NodeToOverlapMap"); } shapeFunction_->register_dependence(this); nodeToOverlapMap_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void LambdaCouplingMatrix::reset() const { if (need_reset()) { PerAtomSparseMatrix<double>::reset(); int nNodeOverlap = nodeToOverlapMap_->size(); const SPAR_MAT & shapeFunction(shapeFunction_->quantity()); quantity_.reset(shapeFunction.nRows(),nNodeOverlap); // number of atoms X number of nodes overlapping MD region const INT_ARRAY nodeToOverlapMap(nodeToOverlapMap_->quantity()); for (int i = 0; i < shapeFunction.size(); ++i) { TRIPLET<double> myTriplet = shapeFunction.triplet(i); int myCol = nodeToOverlapMap(myTriplet.j,0); if (myCol > -1) { quantity_.set(myTriplet.i,myCol,myTriplet.v); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class LocalLambdaCouplingMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- LocalLambdaCouplingMatrix::LocalLambdaCouplingMatrix(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * lambdaAtomMap, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap, SPAR_MAN * shapeFunction) : LambdaCouplingMatrix(atc,nodeToOverlapMap,shapeFunction), lambdaAtomMap_(lambdaAtomMap) { if (!lambdaAtomMap_) { lambdaAtomMap_ = (atc->interscale_manager()).dense_matrix_int("LambdaAtomMap"); } lambdaAtomMap_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void LocalLambdaCouplingMatrix::reset() const { if (need_reset()) { PerAtomSparseMatrix<double>::reset(); int nNodeOverlap = nodeToOverlapMap_->size(); int nLocalLambda = lambdaAtomMap_->size(); quantity_.reset(nLocalLambda,nNodeOverlap); // number of regulated atoms X number of nodes containing them const SPAR_MAT & shapeFunction(shapeFunction_->quantity()); const INT_ARRAY nodeToOverlapMap(nodeToOverlapMap_->quantity()); const INT_ARRAY lambdaAtomMap(lambdaAtomMap_->quantity()); for (int i = 0; i < shapeFunction.size(); ++i) { TRIPLET<double> myTriplet = shapeFunction.triplet(i); int myRow = lambdaAtomMap(myTriplet.i,0); int myCol = nodeToOverlapMap(myTriplet.j,0); if ((myRow > -1) && (myCol > -1)) { quantity_.set(myRow,myCol,myTriplet.v); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class GhostCouplingMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- GhostCouplingMatrix::GhostCouplingMatrix(ATC_Method * atc, SPAR_MAN * shapeFunction, SetDependencyManager<int> * subsetNodes, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap) : LambdaCouplingMatrix(atc,nodeToOverlapMap,shapeFunction), subsetNodes_(subsetNodes) { subsetNodes_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void GhostCouplingMatrix::reset() const { if (need_reset()) { PerAtomSparseMatrix<double>::reset(); const SPAR_MAT & shapeFunction(shapeFunction_->quantity()); const set<int> & subsetNodes(subsetNodes_->quantity()); int nNodes = nodeToOverlapMap_->nRows(); int nLocalGhost = shapeFunction.nRows(); quantity_.reset(nLocalGhost,nNodes); const INT_ARRAY nodeToOverlapMap(nodeToOverlapMap_->quantity()); for (int i = 0; i < shapeFunction.size(); ++i) { TRIPLET<double> myTriplet = shapeFunction.triplet(i); int myCol = myTriplet.j; if (nodeToOverlapMap(myCol,0) > -1) { quantity_.set(myTriplet.i,myCol,myTriplet.v); } } quantity_.compress(); //int nNodes = nodeToOverlapMap_->nRows(); _activeNodes_.reset(nNodes); for (int i = 0; i < nNodes; ++i) { if (subsetNodes.find(i) != subsetNodes.end()) { _activeNodes_(i) = 1.; } } //const SPAR_MAT & shapeFunction(shapeFunction_->quantity()); //int nLocalGhost = shapeFunction.nRows(); _weights_ = shapeFunction*_activeNodes_; _weightMatrix_.resize(nLocalGhost,nLocalGhost); for (int i = 0; i < nLocalGhost; ++i) { _weightMatrix_(i,i) = 1./_weights_(i); } quantity_ = _weightMatrix_*quantity_; } } }; diff --git a/lib/atc/PerAtomQuantityLibrary.h b/lib/atc/PerAtomQuantityLibrary.h index e6847ef6a..dbc1f16e2 100644 --- a/lib/atc/PerAtomQuantityLibrary.h +++ b/lib/atc/PerAtomQuantityLibrary.h @@ -1,1727 +1,1804 @@ // A library for defining various atomic quantities #ifndef PER_ATOM_QUANTITY_LIBRARY_H #define PER_ATOM_QUANTITY_LIBRARY_H #include "PerAtomQuantity.h" #include "FundamentalAtomicQuantity.h" #include <set> #include <map> #include <vector> #include <string> namespace ATC { // forward declarations class LammpsInterface; class FE_Mesh; class FE_Engine; template <class T> class DenseMatrixTransfer; // need to add capability to take in group bit (JAT, 04/02/11) /** * @class AtomNumber * @brief Class for identifying atoms based on a specified group */ class AtomNumber : public ProtectedAtomQuantity<double> { public: // constructor AtomNumber(ATC_Method * atc, AtomType atomType = INTERNAL); // destructor virtual ~AtomNumber() {}; /** reset the quantity */ virtual void reset() const; protected: // int groupBit_; ATC_Method * atc_; private: // do not define AtomNumber(); }; /** * @class AtomTypeVector * @brief Class for identifying atoms based on a specified group */ class AtomTypeVector : public ProtectedAtomQuantity<double> { public: // constructor AtomTypeVector(ATC_Method * atc, std::vector<int> typeList, AtomType atomType = INTERNAL); AtomTypeVector(ATC_Method * atc, std::vector<int> typeList, std::vector<int> grpList, AtomType atomType = INTERNAL); // destructor virtual ~AtomTypeVector() {}; /** reset the quantity */ virtual void reset() const; protected: ATC_Method * atc_; int ntypes_; std::vector<int> typeList_,index_; // lammps->atc & atc->lammps std::vector<int> groupList_; private: AtomTypeVector(); // do not define }; // inherited classes are used for this task because // lammps changes pointer location so it can only be // accessed by functions /** * @class XrefWrapper * @brief Class for wrapping the xref_ array */ class XrefWrapper : public ProtectedClonedAtomQuantity<double> { public: // constructor XrefWrapper(ATC_Method * atc, AtomType atomType=INTERNAL); // destructor virtual ~XrefWrapper() {}; protected: /** pointer to atc to access raw pointer */ ATC_Method * atc_; /** gets appropriate pointer for lammps data */ double ** lammps_vector() const; private: // do not define XrefWrapper(); }; /** * @class AtomToElementMap * @brief Class for identifying the element associated with an atom */ class AtomToElementMap : public ProtectedAtomQuantity<int> { public: // constructor AtomToElementMap(ATC_Method * atc, PerAtomQuantity<double> * atomPositions = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomToElementMap(); protected: /** resets the data if necessary */ virtual void reset() const; /** atomic positions */ PerAtomQuantity<double> * atomPositions_; private: // do not define AtomToElementMap(); }; /** * @class AtomToElementMap * @brief Class list of atoms in an element set */ class AtomInElementSet : public DependencyManager { public: // constructor AtomInElementSet(ATC_Method * atc, PerAtomQuantity<int> * map, ESET eset, int type); // destructor virtual ~AtomInElementSet(); // accessors virtual const ID_LIST & quantity(); virtual ID_LIST & set_quantity() {return list_;} int size() {if (needReset_) reset(); return list_.size(); } ID_PAIR item(int i) {if (needReset_) reset(); return list_[i]; } protected: /** resets the data if necessary */ virtual void reset(); PaqAtcUtility atc_; /** atom to element map */ PerAtomQuantity<int> * map_; ESET eset_; int type_; const Array<int> & quantityToLammps_; ID_LIST list_; private: // do not define AtomInElementSet(); }; /** * @class AtomVolumeUser * @brief Class for defining the volume per atom based on a user specification */ class AtomVolumeUser : public ProtectedAtomDiagonalMatrix<double> { public: // constructor AtomVolumeUser(ATC_Method * atc, std::map<int,double> & atomGroupVolume, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeUser() {}; protected: /** resets the data if necessary */ virtual void reset() const; /** reference to the map of atom group ids to atom volumes */ std::map<int,double> & atomGroupVolume_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; /** map from atc indices to lammps indices */ const Array<int> & atcToLammps_; private: // do not define AtomVolumeUser(); }; /** * @class AtomVolumeGroup * @brief Class for defining the volume per atom based on the atom count in a group and its volume */ class AtomVolumeGroup : public AtomVolumeUser { public: // constructor AtomVolumeGroup(ATC_Method * atc, std::map<int,double> & atomGroupVolume, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeGroup() {}; protected: /** map from group to group atom volume */ std::map<int,double> atomGroupVolume_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; /** reference to array mapping atc indices to lammps indices */ const Array<int> & atcToLammps_; private: // do not define AtomVolumeGroup(); }; /** * @class AtomVolumeLattice * @brief Class for defining the volume per atom based on the lattice type and size */ class AtomVolumeLattice : public ProtectedAtomDiagonalMatrix<double> { public: // constructor AtomVolumeLattice(ATC_Method * atc, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeLattice() {}; protected: /** resets the data if necessary */ virtual void reset() const; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; private: // do not define AtomVolumeLattice(); }; /** * @class AtomVolumeElement * @brief Class for defining the volume per atom based on the atom count per element and elemental volume */ class AtomVolumeElement : public ProtectedAtomDiagonalMatrix<double> { public: // constructor AtomVolumeElement(ATC_Method * atc, PerAtomQuantity<int> * atomElement = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeElement(); protected: /** resets the data if necessary */ virtual void reset() const; /** pointer to the atom to element map */ PerAtomQuantity<int> * atomElement_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; /** pointer to mesh object */ const FE_Mesh * feMesh_; // workspace variables mutable INT_VECTOR _elementAtomCountLocal_; mutable INT_VECTOR _elementAtomCount_; mutable DENS_VEC _elementAtomVolume_; mutable DENS_MAT _nodalCoordinates_; private: // do not define AtomVolumeElement(); }; /** * @class AtomVolumeRegion * @brief Class for defining the volume per atom based on the atom count in the MD regions and their volumes. * It will only be meaningful if atoms completely fill all the regions. */ class AtomVolumeRegion : public ProtectedAtomDiagonalMatrix<double> { public: // constructor AtomVolumeRegion(ATC_Method * atc, DENS_MAN * atomCoarseGrainingPositions = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeRegion(); protected: /** resets the data if necessary */ virtual void reset() const; /** pointer to atomic coordinates data */ DENS_MAN * atomCoarseGrainingPositions_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; /** vector from region index to volume */ DENS_VEC regionalAtomVolume_; private: // do not define AtomVolumeRegion(); }; /** * @class AtomVolumeFile * @brief Class for defining the volume per atom based on data read in from a file */ class AtomVolumeFile : public ProtectedAtomDiagonalMatrix<double> { public: // constructor AtomVolumeFile(ATC_Method * atc, const std::string & atomVolumeFile, AtomType atomType = INTERNAL); // destructor virtual ~AtomVolumeFile() {}; protected: /** resets the data if necessary */ virtual void reset() const; /** file name containing the atomic information */ const std::string & atomVolumeFile_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; private: // do not define AtomVolumeFile(); }; /** * @class AtomicMassWeightedDisplacement * @brief Class for computing the precursor atomic quantity m*(x - x_ref) */ class AtomicMassWeightedDisplacement : public ProtectedAtomQuantity<double> { public: // constructor AtomicMassWeightedDisplacement(ATC_Method * atc, PerAtomQuantity<double> * atomPositions = NULL, PerAtomQuantity<double> * atomMasses = NULL, PerAtomQuantity<double> * atomReferencePositions = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicMassWeightedDisplacement(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic positions */ PerAtomQuantity<double> * atomPositions_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; /** atomic reference positions */ PerAtomQuantity<double> * atomReferencePositions_; private: // do not define AtomicMassWeightedDisplacement(); }; /** * @class FluctuatingVelocity * @brief Class for computing the atomic quantity v - bar{v} */ class FluctuatingVelocity : public ProtectedAtomQuantity<double> { public: // constructor FluctuatingVelocity(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMeanVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~FluctuatingVelocity(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic mean velocities */ PerAtomQuantity<double> * atomMeanVelocities_; private: // do not define FluctuatingVelocity(); }; /** * @class ChargeVelcity * @brief Class for computing the atomic quantity q v' */ class ChargeVelocity : public ProtectedAtomQuantity<double> { public: // constructor ChargeVelocity(ATC_Method * atc, PerAtomQuantity<double> * fluctuatingVelocities = NULL, FundamentalAtomQuantity * atomCharges = NULL, AtomType atomType = INTERNAL); // destructor virtual ~ChargeVelocity(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * fluctuatingVelocities_; /** atomic mean velocities */ FundamentalAtomQuantity * atomCharge_; private: // do not define ChargeVelocity(); }; /** * @class SpeciesVelcity * @brief Class for computing the atomic quantity m^(a) v' */ class SpeciesVelocity : public ProtectedAtomQuantity<double> { public: // constructor SpeciesVelocity(ATC_Method * atc, PerAtomQuantity<double> * fluctuatingVelocities = NULL, PerAtomQuantity<double> * atomTypeVector = NULL, AtomType atomType = INTERNAL); // destructor virtual ~SpeciesVelocity(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * fluctuatingVelocities_; /** atomic mean velocities */ PerAtomQuantity<double> * atomTypeVector_; private: // do not define SpeciesVelocity(); }; /** * @class AtomicMomentum * @brief Class for computing the precursor atomic quantity m*v */ class AtomicMomentum : public ProtectedAtomQuantity<double> { public: // constructor AtomicMomentum(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMasses = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicMomentum(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; private: // do not define AtomicMomentum(); }; /** * @class AtomicEnergyForTemperature * @brief Base class for accessing quantities needed for computing temperature */ class AtomicEnergyForTemperature : public ProtectedAtomQuantity<double> { public: // constructor AtomicEnergyForTemperature(ATC_Method * atc, AtomType atomType = INTERNAL) : ProtectedAtomQuantity<double>(atc, 1, atomType) {}; // destructor virtual ~AtomicEnergyForTemperature() {}; // returns coefficient which multiplies kinetic energy in temperature definition virtual double kinetic_energy_multiplier() const = 0; private: // do not define AtomicEnergyForTemperature(); }; /** * @class TwiceKineticEnergy * @brief Class for computing the precursor atomic quantity m*v*v * (used when the kinetic definition of temperature is required) */ class TwiceKineticEnergy : public AtomicEnergyForTemperature { public: // constructor TwiceKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMasses = NULL, AtomType atomType = INTERNAL); // destructor virtual ~TwiceKineticEnergy(); // returns coefficient which multiplies kinetic energy in temperature definition virtual double kinetic_energy_multiplier() const {return 2.;}; protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; private: // do not define TwiceKineticEnergy(); }; /** * @class KineticTensor * @brief Class for computing the atomic quantity m v (x) v */ class KineticTensor : public ProtectedAtomQuantity<double> { public: // constructor KineticTensor(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMasses = NULL, AtomType atomType = INTERNAL); // destructor virtual ~KineticTensor(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; private: // do not define KineticTensor(); }; /** * @class FluctuatingKineticTensor * @brief Class for computing the atomic quantity m v (x) v */ class FluctuatingKineticTensor : public ProtectedAtomQuantity<double> { public: // constructor FluctuatingKineticTensor(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMasses = NULL, PerAtomQuantity<double> * atomMeanVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~FluctuatingKineticTensor(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; /** atomic mean velocities */ PerAtomQuantity<double> * atomMeanVelocities_; private: // do not define FluctuatingKineticTensor(); }; /** * @class TwiceFluctuatingKineticEnergy * @brief Class for computing the precursor atomic quantity m*(v-vr)*(v-vr) * (used when the kinetic definition of temperature is required) */ class TwiceFluctuatingKineticEnergy : public AtomicEnergyForTemperature { public: // constructor TwiceFluctuatingKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomVelocities = NULL, PerAtomQuantity<double> * atomMasses = NULL, PerAtomQuantity<double> * atomMeanVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~TwiceFluctuatingKineticEnergy(); // returns coefficient which multiplies kinetic energy in temperature definition virtual double kinetic_energy_multiplier() const {return 2.;}; protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; /** atomic mean velocities */ PerAtomQuantity<double> * atomMeanVelocities_; private: // do not define TwiceFluctuatingKineticEnergy(); }; /** * @class MixedKePeEnergy * @brief Class for computing the precursor atomic quantity for * a mixed temperature definition involving both KE and PE */ class MixedKePeEnergy : public AtomicEnergyForTemperature { public: // constructor MixedKePeEnergy(ATC_Method * atc, double keMultiplier, double peMultiplier, PerAtomQuantity<double> * twiceKineticEnergy = NULL, PerAtomQuantity<double> * potentialEnergy = NULL, AtomType atomType = INTERNAL); // destructor virtual ~MixedKePeEnergy(); // returns coefficient which multiplies kinetic energy in temperature definition virtual double kinetic_energy_multiplier() const {return keMultiplier_;}; protected: /** handles resetting of data */ virtual void reset() const; /** factor multiplying kinetic energy */ double keMultiplier_; /** factor multiplying potential energy */ double peMultiplier_; /** twice the kinetic energy of each atom */ PerAtomQuantity<double> * twiceKineticEnergy_; /** potential energy of each atom */ PerAtomQuantity<double> * potentialEnergy_; private: // do not define MixedKePeEnergy(); }; /** * @class TotalEnergy * @brief Class for the atomic total energy */ class TotalEnergy : public ProtectedAtomQuantity<double> { public: // constructor TotalEnergy(ATC_Method * atc, PerAtomQuantity<double> * twiceKineticEnergy = NULL, PerAtomQuantity<double> * potentialEnergy = NULL, AtomType atomType = INTERNAL); // destructor virtual ~TotalEnergy(); protected: /** handles resetting of data */ virtual void reset() const; /** twice the kinetic energy of each atom */ PerAtomQuantity<double> * twiceKineticEnergy_; /** potential energy of each atom */ PerAtomQuantity<double> * potentialEnergy_; private: TotalEnergy(); // do not define }; /** * @class FluctuatingPotentialEnergy * @brief Class for computing the precursor atomic quantity for * a configurational (PE-based) temperature */ class FluctuatingPotentialEnergy : public AtomicEnergyForTemperature { public: // constructor FluctuatingPotentialEnergy(ATC_Method * atc, PerAtomQuantity<double> * potentialEnergy = NULL, PerAtomQuantity<double> * referencePotential = NULL, AtomType atomType = INTERNAL); // destructor virtual ~FluctuatingPotentialEnergy(); // returns coefficient which multiplies kinetic energy in temperature definition virtual double kinetic_energy_multiplier() const {return 0.;;}; protected: /** handles resetting of data */ virtual void reset() const; /** potential energy of each atom */ PerAtomQuantity<double> * potentialEnergy_; /** twice the kinetic energy of each atom */ PerAtomQuantity<double> * referencePotential_; private: // do not define FluctuatingPotentialEnergy(); }; /** * @class DotTwiceKineticEnergy * @brief Class for computing the precursor atomic power 2*v*f * (used when the kinetic definition of temperature is required) */ class DotTwiceKineticEnergy : public ProtectedAtomQuantity<double> { public: // constructor DotTwiceKineticEnergy(ATC_Method * atc, PerAtomQuantity<double> * atomForces = NULL, PerAtomQuantity<double> * atomVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~DotTwiceKineticEnergy(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic forces */ PerAtomQuantity<double> * atomForces_; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; private: // do not define DotTwiceKineticEnergy(); }; /** * @class VelocitySquared * @brief Class for computing the quantity |v|^2 * (used for weights in the thermostat) */ class VelocitySquared : public ProtectedAtomQuantity<double> { public: // constructor VelocitySquared(ATC_Method *atc, PerAtomQuantity<double> * atomVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~VelocitySquared(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; private: // do not define VelocitySquared(); }; /** * @class LambdaSquared * @brief Class for computing the 2nd order RHS fractional step * contribution to the equation for lambda, with appropriate weights */ class LambdaSquared : public ProtectedAtomQuantity<double> { public: // constructor LambdaSquared(ATC_Method *atc, PerAtomQuantity<double> * atomMasses = NULL, PerAtomQuantity<double> * atomVelocitiesSquared = NULL, PerAtomQuantity<double> * atomLambdas = NULL, AtomType atomType = INTERNAL); // destructor virtual ~LambdaSquared(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; /** atomic velocities squared */ PerAtomQuantity<double> * atomVelocitiesSquared_; /** atomic lambdas */ PerAtomQuantity<double> * atomLambdas_; private: // do not define LambdaSquared(); }; /** * @class LargeToSmallAtomMap * @brief mapping from a larger set of atoms to a smaller set * this implementation maximizes storage but reduces execution times, * including taking advantage of MPI communcation */ class LargeToSmallAtomMap : public ProtectedAtomQuantity<int> { public: // constructor LargeToSmallAtomMap(ATC_Method * atc, AtomType atomType = INTERNAL) : ProtectedAtomQuantity<int>(atc,1,atomType), size_(0) {}; // destructor virtual ~LargeToSmallAtomMap() {}; /** change map when atoms change */ virtual void reset_nlocal() {this->set_reset();}; /** get the number of elements associated with the map */ virtual int size() const {this->quantity(); return size_;}; /** sets quantity to lammps data, if needed, should be called in pre_exchange */ virtual void prepare_exchange() {}; /** sets quantity to lammps data, if needed */ virtual void post_exchange() {this->set_reset();}; /** returns how much lammps memory is used in this function */ virtual int memory_usage() const {return 0;}; /** packs up data for parallel transfer when atoms change processors */ virtual int pack_exchange(int i, double *buffer) {return 0;}; /** unpacks data after parallel transfer when atoms change processors */ virtual int unpack_exchange(int i, double *buffer) {return 0;}; /** packs up data for parallel transfer to ghost atoms on other processors */ virtual int pack_comm(int index, double *buf, int pbc_flag, int *pbc) {return 0;}; /** unpacks data after parallel transfer to ghost atoms on other processors */ virtual int unpack_comm(int index, double *buf) {return 0;}; /** returns size of per-atom communication */ virtual int size_comm() {return 0;}; /** changes size of temperary lammps storage data if transfer is being used */ virtual void grow_lammps_array(int nmax, const std::string & tag) {}; /** rearrange memory of temporary lammps storage data, called from copy_array */ virtual void copy_lammps_array(int i, int j) {}; protected: /** number of nodes in the map */ mutable int size_; }; /** * @class AtomToType * @brief mapping from all atoms to the subset of atoms of a specified type */ class AtomToType : public LargeToSmallAtomMap { public: // constructor AtomToType(ATC_Method * atc, int type, AtomType atomType = INTERNAL); // destructor virtual ~AtomToType() {}; protected: /** handles resetting of data */ virtual void reset() const; /** tag for type */ int type_; private: // do not define AtomToType(); }; /** * @class AtomToGroup * @brief mapping from all atoms to the subset of atoms of a specified group */ class AtomToGroup : public LargeToSmallAtomMap { public: // constructor AtomToGroup(ATC_Method * atc, int group, AtomType atomType = INTERNAL); // destructor virtual ~AtomToGroup() {}; protected: /** handles resetting of data */ virtual void reset() const; /** tag for group */ int group_; private: // do not define AtomToGroup(); }; /** * @class AtomToNodeset * @brief mapping from all atoms to a subset of nodes */ class AtomToNodeset : public LargeToSmallAtomMap { public: // constructor AtomToNodeset(ATC_Method * atc, SetDependencyManager<int> * subsetNodes, PerAtomQuantity<int> * atomElement = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomToNodeset() { atomElement_->remove_dependence(this); subsetNodes_->remove_dependence(this); }; protected: /** handles resetting of data */ virtual void reset() const; /** set of nodes which are being regulated */ SetDependencyManager<int> * subsetNodes_; /** map from atom to element in which it resides */ PerAtomQuantity<int> * atomElement_; /** pointer to the finite element engine */ const FE_Mesh * feMesh_; // workspace mutable Array<int> _nodes_; // nodes associated with an element private: // do not define AtomToNodeset(); }; /** * @class AtomToElementset * @brief mapping from all atoms to a subset of elements */ class AtomToElementset : public LargeToSmallAtomMap { public: // constructor AtomToElementset(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, bool> * elementMask, PerAtomQuantity<int> * atomElement = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomToElementset(); protected: /** handles resetting of data */ virtual void reset() const; /** set of nodes which are being regulated */ MatrixDependencyManager<DenseMatrix, bool> * elementMask_; /** map from atom to element in which it resides */ PerAtomQuantity<int> * atomElement_; /** pointer to the finite element engine */ const FE_Mesh * feMesh_; private: // do not define AtomToElementset(); }; /** * @class MappedAtomQuantity * @brief generic reduced mapping */ class MappedAtomQuantity : public ProtectedMappedAtomQuantity<double> { public: // constructor MappedAtomQuantity(ATC_Method * atc, PerAtomQuantity<double> * source, LargeToSmallAtomMap * map, AtomType atomType = INTERNAL); // destructor virtual ~MappedAtomQuantity() { source_->remove_dependence(this); map_->remove_dependence(this); }; protected: /** handles resetting of data */ virtual void reset() const; /** original quantity */ PerAtomQuantity<double> * source_; /** mapping transfer */ LargeToSmallAtomMap * map_; private: // do not define MappedAtomQuantity(); }; /** * @class VelocitySquaredMapped * @brief Class for computing the quantity |v|^2 on a subset of atoms * (used for weights in the thermostat) */ class VelocitySquaredMapped : public ProtectedMappedAtomQuantity<double> { public: // constructor VelocitySquaredMapped(ATC_Method *atc, MatrixDependencyManager<DenseMatrix, int> * atomMap, PerAtomQuantity<double> * atomVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~VelocitySquaredMapped(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; private: // do not define VelocitySquaredMapped(); }; /** * @class LambdaSquaredMapped * @brief Class for computing the 2nd order RHS fractional step * contribution to the equation for lambda, with appropriate weights */ class LambdaSquaredMapped : public ProtectedMappedAtomQuantity<double> { public: // constructor LambdaSquaredMapped(ATC_Method *atc, MatrixDependencyManager<DenseMatrix, int> * atomMap, PerAtomQuantity<double> * atomMasses = NULL, PerAtomQuantity<double> * atomVelocitiesSquared = NULL, PerAtomQuantity<double> * atomLambdas = NULL, AtomType atomType = INTERNAL); // destructor virtual ~LambdaSquaredMapped(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic masses */ PerAtomQuantity<double> * atomMasses_; /** atomic velocities squared */ PerAtomQuantity<double> * atomVelocitiesSquared_; /** atomic lambdas */ PerAtomQuantity<double> * atomLambdas_; private: // do not define LambdaSquaredMapped(); }; /** * @class HeatCapacity * @brief Class for the classical atomic heat capacity */ class HeatCapacity : public ConstantQuantity<double> { public: // constructor HeatCapacity(ATC_Method * atc, AtomType atomType = INTERNAL); // destructor virtual ~HeatCapacity() {}; protected: private: // do not define HeatCapacity(); }; /** * @class AtomicVelocityRescaleFactor * @brief Class for computing the atomic rescaling induced by the rescaling thermostat */ class AtomicVelocityRescaleFactor : public ProtectedAtomQuantity<double> { public: // constructor AtomicVelocityRescaleFactor(ATC_Method * atc, PerAtomQuantity<double> * atomLambdas = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicVelocityRescaleFactor(); protected: /** handles resetting of data */ virtual void reset() const; - /** atomic masses */ - FundamentalAtomQuantity * atomMasses_; - /** atomic lambdas */ PerAtomQuantity<double> * atomLambdas_; private: // do not define AtomicVelocityRescaleFactor(); }; + /** + * @class AtomicFluctuatingVelocityRescaled + * @brief Class for computing the atomic rescaling of the velocity fluctuations by the rescaling thermostat + */ + + class AtomicFluctuatingVelocityRescaled : public ProtectedAtomQuantity<double> { + + public: + + // constructor + AtomicFluctuatingVelocityRescaled(ATC_Method * atc, + PerAtomQuantity<double> * atomRescaleFactor = NULL, + PerAtomQuantity<double> * atomFluctuatingVelocity = NULL, + AtomType atomType = INTERNAL); + + // destructor + virtual ~AtomicFluctuatingVelocityRescaled(); + + protected: + + /** handles resetting of data */ + virtual void reset() const; + + /** atomic rescaling factor */ + PerAtomQuantity<double> * atomRescaleFactor_; + + /** atomic fluctuating velocity */ + PerAtomQuantity<double> * atomFluctuatingVelocity_; + + private: + + // do not define + AtomicFluctuatingVelocityRescaled(); + + }; + + /** + * @class AtomicCombinedRescaleThermostatError + * @brief Class for computing the atomic error in the rescaling thermostat when used in combination with a specified streaming velocity + */ + + class AtomicCombinedRescaleThermostatError : public ProtectedAtomQuantity<double> { + + public: + + // constructor + AtomicCombinedRescaleThermostatError(ATC_Method * atc, + PerAtomQuantity<double> * atomFluctuatingMomentumRescaled = NULL, + PerAtomQuantity<double> * atomMeanVelocity = NULL, + PerAtomQuantity<double> * atomStreamingVelocity = NULL, + PerAtomQuantity<double> * atomMass = NULL, + AtomType atomType = INTERNAL); + + // destructor + virtual ~AtomicCombinedRescaleThermostatError(); + + protected: + + /** handles resetting of data */ + virtual void reset() const; + + /** atomic rescaled fluctuating momentum */ + PerAtomQuantity<double> * atomFluctuatingMomentumRescaled_; + + /** atomic mean (prolonged FE) velocity */ + PerAtomQuantity<double> * atomMeanVelocity_; + + /** atomic streaming velocity, as computed by rescaling kinetothermostat */ + PerAtomQuantity<double> * atomStreamingVelocity_; + + /** atomic masses */ + PerAtomQuantity<double> * atomMass_; + + private: + + // do not define + AtomicCombinedRescaleThermostatError(); + + }; + /** * @class AtomicThermostatForce * @brief Class for computing the atomic force induced by the GLC-based thermostats */ class AtomicThermostatForce : public ProtectedAtomQuantity<double> { public: // constructor AtomicThermostatForce(ATC_Method * atc, PerAtomQuantity<double> * atomLambdas = NULL, PerAtomQuantity<double> * atomVelocities = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicThermostatForce(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic lambdas */ PerAtomQuantity<double> * atomLambdas_; /** atomic velocities */ PerAtomQuantity<double> * atomVelocities_; private: // do not define AtomicThermostatForce(); }; /** * @class AtomicKinetostatForceDisplacement * @brief Class for computing the atomic force induced by the GLC-based kinetostats */ class AtomicKinetostatForceDisplacement : public ProtectedAtomQuantity<double> { public: // constructor AtomicKinetostatForceDisplacement(ATC_Method * atc, PerAtomQuantity<double> * atomLambda = NULL, PerAtomQuantity<double> * atomMass = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicKinetostatForceDisplacement(); protected: /** handles resetting of data */ virtual void reset() const; /** computes the multiplication factor assocaited with the controlled quantity being an integral of the degrees of freedom */ virtual double time_step_factor(double dt) const {return 1./dt/dt;}; /** atomic lambdas */ PerAtomQuantity<double> * atomLambda_; /** atomic velocities */ PerAtomQuantity<double> * atomMass_; private: // do not define AtomicKinetostatForceDisplacement(); }; /** * @class AtomicKinetostatForceVelocity * @brief Class for computing the atomic force induced by the GLC-based kinetostats */ class AtomicKinetostatForceVelocity : public AtomicKinetostatForceDisplacement { public: // constructor AtomicKinetostatForceVelocity(ATC_Method * atc, PerAtomQuantity<double> * atomLambda = NULL, PerAtomQuantity<double> * atomMass = NULL, AtomType atomType = INTERNAL) : AtomicKinetostatForceDisplacement(atc,atomLambda,atomMass,atomType) {}; // destructor virtual ~AtomicKinetostatForceVelocity() {}; protected: /** computes the multiplication factor assocaited with the controlled quantity being an integral of the degrees of freedom */ virtual double time_step_factor(double dt) const {return 1./dt;}; private: // do not define AtomicKinetostatForceVelocity(); }; /** * @class AtomicKinetostatForceStress * @brief Class for computing the atomic force induced by the stress-based kinetostats */ class AtomicKinetostatForceStress : public ProtectedAtomQuantity<double> { public: // constructor AtomicKinetostatForceStress(ATC_Method * atc, PerAtomQuantity<double> * atomLambda = NULL, AtomType atomType = INTERNAL); // destructor virtual ~AtomicKinetostatForceStress(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic lambdas */ PerAtomQuantity<double> * atomLambda_; private: // do not define AtomicKinetostatForceStress(); }; /** * @class PerAtomKernelFunction * @brief Class for computing the kernel function at each atom location */ class PerAtomKernelFunction : public ProtectedAtomSparseMatrix<double> { public: // constructor PerAtomKernelFunction(ATC_Method * atc, PerAtomQuantity<double> * atomPositions = NULL, AtomType atomType = INTERNAL); // destructor virtual ~PerAtomKernelFunction(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic coarse-graining positions */ PerAtomQuantity<double> * atomPositions_; /** finite element engine */ const FE_Engine * feEngine_; private: // do not define PerAtomKernelFunction(); }; /** * @class PerAtomShapeFunction * @brief Class for computing the shape function at each atom location */ class PerAtomShapeFunction : public ProtectedAtomSparseMatrix<double> { public: // constructor PerAtomShapeFunction(ATC_Method * atc, PerAtomQuantity<double> * atomPositions = NULL, PerAtomQuantity<int> * atomElements = NULL, AtomType atomType = INTERNAL); // destructor virtual ~PerAtomShapeFunction(); protected: /** handles resetting of data */ virtual void reset() const; /** atomic coarse-graining positions */ PerAtomQuantity<double> * atomPositions_; /** atom to element map */ PerAtomQuantity<int> * atomElements_; /** finite element engine */ const FE_Engine * feEngine_; private: // do not define PerAtomShapeFunction(); }; /** * @class LambdaCouplingMatrix * @brief constructs the coupling matrix needed to solve for lambda, i.e. N in N^T w N L = b */ class LambdaCouplingMatrix : public ProtectedMappedAtomSparseMatrix<double> { public: // constructor LambdaCouplingMatrix(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap = NULL, SPAR_MAN * shapeFunction = NULL); // destructor virtual ~LambdaCouplingMatrix() { shapeFunction_->remove_dependence(this); nodeToOverlapMap_->remove_dependence(this); }; protected: /** does the actual computation of the quantity */ virtual void reset() const; /** base shape function */ SPAR_MAN * shapeFunction_; /** map from all nodes to regulated ones */ MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap_; private: // do not define LambdaCouplingMatrix(); }; /** * @class LocalLambdaCouplingMatrix * @brief constructs the coupling matrix needed to solve for lambda, i.e. N in N^T w N L = b * when localization is being used for the constraint */ class LocalLambdaCouplingMatrix : public LambdaCouplingMatrix { public: // constructor LocalLambdaCouplingMatrix(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int> * lambdaAtomMap = NULL, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap = NULL, SPAR_MAN * shapeFunction = NULL); // destructor virtual ~LocalLambdaCouplingMatrix() { lambdaAtomMap_->remove_dependence(this); }; protected: /** does the actual computation of the quantity */ virtual void reset() const; /** map from all atoms to regulated ones */ MatrixDependencyManager<DenseMatrix, int> * lambdaAtomMap_; private: // do not define LocalLambdaCouplingMatrix(); }; /** * @class GhostCouplingMatrix * @brief constructs the modified shape functions used to compute the total forces between ghost and internal atoms */ class GhostCouplingMatrix : public LambdaCouplingMatrix { public: // constructor GhostCouplingMatrix(ATC_Method * atc, SPAR_MAN * shapeFunction, SetDependencyManager<int> * subsetNodes, MatrixDependencyManager<DenseMatrix, int> * nodeToOverlapMap = NULL); // destructor virtual ~GhostCouplingMatrix() { subsetNodes_->remove_dependence(this); }; protected: /** does the actual computation of the quantity */ virtual void reset() const; /** set of nodes which are being regulated */ SetDependencyManager<int> * subsetNodes_; // workspace mutable DENS_VEC _activeNodes_; // nodes which are being regulated are 1, otherwise 0 mutable DENS_VEC _weights_; // required weighting for each shape function row to enforce partition of unity mutable DIAG_MAT _weightMatrix_; // diagonal with necessary scaling for partition of unity private: // do not define GhostCouplingMatrix(); }; } #endif diff --git a/lib/atc/PoissonSolver.cpp b/lib/atc/PoissonSolver.cpp index a5b4668e5..b3c077b59 100644 --- a/lib/atc/PoissonSolver.cpp +++ b/lib/atc/PoissonSolver.cpp @@ -1,213 +1,218 @@ #include "PoissonSolver.h" #include "ATC_Coupling.h" #include "FE_Engine.h" #include "PhysicsModel.h" #include "PrescribedDataManager.h" #include "LinearSolver.h" #include <utility> #include <iostream> using std::pair; namespace ATC { // ==================================================================== // PoissonSolver // ==================================================================== PoissonSolver::PoissonSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, const Array2D<bool> & rhsMask, const int solverType, bool parallel ) : atc_(atc), feEngine_(feEngine), prescribedDataMgr_(prescribedDataMgr), physicsModel_(physicsModel), fieldName_(fieldName), rhsMask_(rhsMask), linear_(false), solver_(NULL), solverNL_(NULL), tangent_(NULL), solverType_(solverType), solverTol_(0), solverMaxIter_(0), integrationType_(FULL_DOMAIN), parallel_(parallel) { if (physicsModel_->has_linear_rhs(fieldName)) { linear_ = true; rhsMask_(fieldName,FLUX) = false; } else { rhsMask_(fieldName,FLUX) = true; rhsMask_(fieldName,SOURCE) = true; } + if (prescribedDataMgr_->has_robin_source(fieldName)) { rhsMask_(fieldName,ROBIN_SOURCE) = true; } } // -------------------------------------------------------------------- PoissonSolver::~PoissonSolver() { if (tangent_) delete tangent_; if (solverNL_) delete solverNL_; if (solver_) delete solver_; } // -------------------------------------------------------------------- // Parser // -------------------------------------------------------------------- bool PoissonSolver::modify(int narg, char **arg) { bool match = false; /*! \page man_poisson_solver fix_modify AtC poisson_solver \section syntax fix_modify AtC poisson_solver mesh create <nx> <ny> <nz> <region-id> <f|p> <f|p> <f|p> - nx ny nz = number of elements in x, y, z - region-id = id of region that is to be meshed - f p p = perioidicity flags for x, y, z \section examples <TT> fix_modify AtC poisson_solver mesh create 10 1 1 feRegion p p p </TT> \section description Creates a uniform mesh in a rectangular region \section restrictions creates only uniform rectangular grids in a rectangular region \section related \section default none */ int argIdx = 0; if (strcmp(arg[argIdx],"poisson_solver")==0) { argIdx++; if (strcmp(arg[argIdx],"mesh")==0) { argIdx++; // create a FE_Engine //feEngine_ = new FE_Engine(this); need alternate constructor? // send args to new engine // arg[0] = "mesh"; // arg[1] = "create"; // feEngine_->modify(narg,arg); } } return match; } // -------------------------------------------------------------------- // Initialize // -------------------------------------------------------------------- void PoissonSolver::initialize(void) { nNodes_ = feEngine_->num_nodes(); + if (atc_->source_atomic_quadrature(fieldName_)) + integrationType_ = FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE; + // compute penalty for Dirichlet boundaries if (prescribedDataMgr_->none_fixed(fieldName_)) throw ATC_Error("Poisson solver needs Dirichlet data"); const BC_SET & bcs = (prescribedDataMgr_->bcs(fieldName_))[0]; if (linear_) { // constant rhs if (! solver_ ) { pair<FieldName,FieldName> row_col(fieldName_,fieldName_); Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(fieldName_,FLUX) = true; + if (prescribedDataMgr_->has_robin_source(fieldName_)) { rhsMask(fieldName_,ROBIN_SOURCE) = true; } // compute stiffness for Poisson solve atc_->compute_rhs_tangent(row_col, rhsMask, atc_->fields(), stiffness_, FULL_DOMAIN, physicsModel_); // create solver - solver_ = new LinearSolver(stiffness_,bcs,solverType_,-1,parallel_); + solver_ = new LinearSolver(stiffness_,bcs,solverType_,LinearSolver::AUTO_HANDLE_CONSTRAINTS,parallel_); } else { // re-initialize solver_->initialize(&bcs); } if (solverTol_) solver_->set_tolerance(solverTol_); if (solverMaxIter_) solver_->set_max_iterations(solverMaxIter_); } else { // print_mask(rhsMask_); if ( solverNL_ ) delete solverNL_; tangent_ = new PhysicsModelTangentOperator(atc_,physicsModel_, rhsMask_, integrationType_, fieldName_); solverNL_ = new NonLinearSolver(tangent_,&bcs,0,parallel_); if (solverTol_) solverNL_->set_residual_tolerance(solverTol_); if (solverMaxIter_) solverNL_->set_max_iterations(solverMaxIter_); } } // -------------------------------------------------------------------- // Solve // -------------------------------------------------------------------- bool PoissonSolver::solve(FIELDS & fields, FIELDS & rhs) { atc_->compute_rhs_vector(rhsMask_, fields, rhs, integrationType_, physicsModel_); CLON_VEC f = column(fields[fieldName_].set_quantity(),0); CLON_VEC r = column(rhs[fieldName_].quantity(),0); bool converged = false; if (linear_) {converged = solver_->solve(f,r);} else {converged = solverNL_->solve(f);} if (atc_->source_atomic_quadrature(fieldName_) && LammpsInterface::instance()->atom_charge() ) set_charges(fields); return converged; } bool PoissonSolver::solve(DENS_MAT & field, const DENS_MAT & rhs) { CLON_VEC f = column(field,0); CLON_VEC r = column(rhs,0); bool converged = false; if (linear_) {converged = solver_->solve(f,r);} else {converged = solverNL_->solve(f);} if (atc_->source_atomic_quadrature(fieldName_) && LammpsInterface::instance()->atom_charge() ) set_charges(atc_->fields()); return converged; } // -------------------------------------------------------------------- // set charges on atoms // -------------------------------------------------------------------- void PoissonSolver::set_charges(FIELDS & fields) { FIELD_MATS sources; atc_->compute_sources_at_atoms(rhsMask_, fields, physicsModel_,sources); FIELD_MATS::const_iterator nField = sources.find(fieldName_); if (nField != sources.end()) { const DENS_MAT & electronCharges = nField->second; double * q = LammpsInterface::instance()->atom_charge(); int nLocal = atc_->nlocal(); if (nLocal > 0) { const Array<int> & i2a = atc_->internal_to_atom_map(); for (int i=0; i < nLocal; i++) { int atomIdx = i2a(i); q[atomIdx] = -electronCharges(i,0); } } } } } // namespace ATC diff --git a/lib/atc/PrescribedDataManager.cpp b/lib/atc/PrescribedDataManager.cpp index 8d27f7c21..bd649c8ac 100644 --- a/lib/atc/PrescribedDataManager.cpp +++ b/lib/atc/PrescribedDataManager.cpp @@ -1,579 +1,623 @@ #include "PrescribedDataManager.h" #include "FE_Engine.h" #include "ATC_Error.h" #include <sstream> #include <iostream> using std::stringstream; using std::make_pair; using std::map; using std::set; using std::pair; using std::string; using std::cout; namespace ATC { //------------------------------------------------------------------------- // PrescribedDataManager //------------------------------------------------------------------------- PrescribedDataManager::PrescribedDataManager (FE_Engine * feEngine, const map<FieldName,int> & fieldSize) : fieldSizes_(fieldSize), feEngine_(feEngine) { // construct & initialize internal data nNodes_ = feEngine_->num_nodes(); nElems_ = feEngine_->num_elements(); map<FieldName,int>::const_iterator field; for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; int thisSize = field->second; // nodal ics & essential bcs ics_[thisField].reset(nNodes_,thisSize); bcs_[thisField].reset(nNodes_,thisSize); for (int inode = 0; inode < nNodes_ ; ++inode) { for (int idof = 0; idof < thisSize ; ++idof) { ics_[thisField](inode,idof) = NULL; bcs_[thisField](inode,idof) = NULL; } } // compact inode, value lists (bcValues_[thisField]).resize(thisSize); // element based sources elementSources_[thisField].reset(nElems_,thisSize); for (int ielem = 0; ielem < nElems_ ; ++ielem) { for (int idof = 0; idof < thisSize ; ++idof) { elementSources_[thisField](ielem,idof) = NULL; } } + // node based sources + nodalSources_[thisField].reset(nNodes_,thisSize); } } //------------------------------------------------------------------------- // ~PrescribedDataManager //------------------------------------------------------------------------- PrescribedDataManager::~PrescribedDataManager() { } //------------------------------------------------------------------------- // add_field //------------------------------------------------------------------------- void PrescribedDataManager::add_field(FieldName fieldName, int size) { // check to see if field exists if (fieldSizes_.find(fieldName) == fieldSizes_.end()) return; // construct & initialize internal data nNodes_ = feEngine_->num_nodes(); nElems_ = feEngine_->num_elements(); // nodal ics & essential bcs ics_[fieldName].reset(nNodes_,size); bcs_[fieldName].reset(nNodes_,size); for (int inode = 0; inode < nNodes_ ; ++inode) { for (int idof = 0; idof < size ; ++idof) { ics_[fieldName](inode,idof) = NULL; bcs_[fieldName](inode,idof) = NULL; } } // element based sources elementSources_[fieldName].reset(nElems_,size); for (int ielem = 0; ielem < nElems_ ; ++ielem) { for (int idof = 0; idof < size ; ++idof) { elementSources_[fieldName](ielem,idof) = NULL; } } } //------------------------------------------------------------------------- // remove_field //------------------------------------------------------------------------- void PrescribedDataManager::remove_field(FieldName fieldName) { // check to see if field exists if (fieldSizes_.find(fieldName) == fieldSizes_.end()) return; // delete field in maps fieldSizes_.erase(fieldName); ics_.erase(fieldName); bcs_.erase(fieldName); elementSources_.erase(fieldName); } //------------------------------------------------------------------------- // fix_initial_field //------------------------------------------------------------------------- void PrescribedDataManager::fix_initial_field (const string nodesetName, const FieldName thisField, const int thisIndex, const XT_Function * f) { - using std::set; set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); set<int>::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { int inode = *iset; ics_[thisField](inode,thisIndex) = (XT_Function*) f; } } //------------------------------------------------------------------------- // fix_field //------------------------------------------------------------------------- void PrescribedDataManager::fix_field (const std::set<int> nodeSet, const FieldName thisField, const int thisIndex, const XT_Function * f) { - using std::set; // fix fields set<int>::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { int inode = *iset; bcs_[thisField](inode,thisIndex) = (XT_Function*) f; } } void PrescribedDataManager::fix_field (const string nodesetName, const FieldName thisField, const int thisIndex, const XT_Function * f) { - using std::set; set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); fix_field(nodeSet,thisField,thisIndex,f); } //------------------------------------------------------------------------- // unfix_field //------------------------------------------------------------------------- void PrescribedDataManager::unfix_field (const string nodesetName, const FieldName thisField, const int thisIndex) { - using std::set; set<int> nodeSet = (feEngine_->fe_mesh())->nodeset(nodesetName); set<int>::const_iterator iset; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { int inode = *iset; bcs_[thisField](inode,thisIndex) = NULL; } } //------------------------------------------------------------------------- // fix_field //------------------------------------------------------------------------- void PrescribedDataManager::fix_field (const int nodeId, const FieldName thisField, const int thisIndex, const XT_Function * f) { bcs_[thisField](nodeId,thisIndex) = (XT_Function*) f; } //------------------------------------------------------------------------- // unfix_field //------------------------------------------------------------------------- void PrescribedDataManager::unfix_field (const int nodeId, const FieldName thisField, const int thisIndex) { bcs_[thisField](nodeId,thisIndex) = NULL; } //------------------------------------------------------------------------- // fix_flux //------------------------------------------------------------------------- void PrescribedDataManager::fix_flux (const string facesetName, const FieldName thisField, const int thisIndex, const XT_Function * f) { const set< pair <int,int> > * fset = & ( (feEngine_->fe_mesh())->faceset(facesetName)); set< pair<int,int> >::const_iterator iset; for (iset = fset->begin(); iset != fset->end(); iset++) { pair<int,int> face = *iset; // allocate, if necessary Array < XT_Function * > & dof = faceSources_[thisField][face]; if (dof.size() == 0) { int ndof = (fieldSizes_.find(thisField))->second; dof.reset(ndof); for(int i = 0; i < ndof; i++) dof(i) = NULL; } dof(thisIndex) = (XT_Function*) f; } } //------------------------------------------------------------------------- // unfix_flux //------------------------------------------------------------------------- void PrescribedDataManager::unfix_flux (const string facesetName, const FieldName thisField, const int thisIndex) { const set< pair <int,int> > * fset = & ( (feEngine_->fe_mesh())->faceset(facesetName)); set< pair<int,int> >::const_iterator iset; for (iset = fset->begin(); iset != fset->end(); iset++) { pair<int,int> face = *iset; Array < XT_Function * > & dof = faceSources_[thisField][face]; dof(thisIndex) = NULL; } } //------------------------------------------------------------------------- // fix_robin //------------------------------------------------------------------------- void PrescribedDataManager::fix_robin (const string facesetName, const FieldName thisField, const int thisIndex, const UXT_Function * f) { const set< pair <int,int> > * fset = & ( (feEngine_->fe_mesh())->faceset(facesetName)); set< pair<int,int> >::const_iterator iset; for (iset = fset->begin(); iset != fset->end(); iset++) { pair<int,int> face = *iset; // allocate, if necessary Array < UXT_Function * > & dof = faceSourcesRobin_[thisField][face]; if (dof.size() == 0) { int ndof = (fieldSizes_.find(thisField))->second; dof.reset(ndof); for(int i = 0; i < ndof; i++) dof(i) = NULL; } dof(thisIndex) = (UXT_Function*) f; } } //------------------------------------------------------------------------- // unfix_robin //------------------------------------------------------------------------- void PrescribedDataManager::unfix_robin (const string facesetName, const FieldName thisField, const int thisIndex) { const set< pair <int,int> > * fset = & ( (feEngine_->fe_mesh())->faceset(facesetName)); set< pair<int,int> >::const_iterator iset; for (iset = fset->begin(); iset != fset->end(); iset++) { pair<int,int> face = *iset; Array < UXT_Function * > & dof = faceSourcesRobin_[thisField][face]; dof(thisIndex) = NULL; } } //------------------------------------------------------------------------- +// fix_open +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_open + (const string facesetName, + const FieldName thisField) + { + const set< pair <int,int> > * fset + = & ( (feEngine_->fe_mesh())->faceset(facesetName)); + set< pair<int,int> >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair<int,int> face = *iset; + facesOpen_[thisField].insert(face); // uniquely insert this face + } + } +//------------------------------------------------------------------------- +// unfix_open +//------------------------------------------------------------------------- + void PrescribedDataManager::unfix_open + (const string facesetName, + const FieldName thisField) + { + const set< pair <int,int> > * fset + = & ( (feEngine_->fe_mesh())->faceset(facesetName)); + set< pair<int,int> >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair<int,int> face = *iset; + facesOpen_[thisField].erase(face); + } + } +//------------------------------------------------------------------------- // fix_source //------------------------------------------------------------------------- void PrescribedDataManager::fix_source (const string elemsetName, const FieldName thisField, const int thisIndex, const XT_Function *f) { - using std::set; set<int> elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); set<int>::const_iterator iset; for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { int ielem = *iset; // fix source elementSources_[thisField](ielem,thisIndex) = (XT_Function*) f; } } //------------------------------------------------------------------------- +// fix_source +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_source + (const FieldName thisField, + const int thisIndex, + const set<pair<int,double> > & s) + { + set<pair<int,double> >::const_iterator iset; + DENS_MAT & n(nodalSources_[thisField].set_quantity()); + for (iset = s.begin(); iset != s.end(); iset++) { + pair<int,double> p = *iset; + int inode = p.first; + double v = p.second; + n(inode,thisIndex) = v; + } + } +//------------------------------------------------------------------------- // unfix_source //------------------------------------------------------------------------- void PrescribedDataManager::unfix_source (const string elemsetName, const FieldName thisField, const int thisIndex) { - using std::set; set<int> elemSet = (feEngine_->fe_mesh())->elementset(elemsetName); set<int>::const_iterator iset; for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { int ielem = *iset; elementSources_[thisField](ielem,thisIndex) = NULL; } } //------------------------------------------------------------------------- // set_initial_conditions //------------------------------------------------------------------------- void PrescribedDataManager::set_initial_conditions(const double t, FIELDS &fields, FIELDS &dot_fields, FIELDS &ddot_fields, FIELDS &dddot_fields) { map<FieldName,int>::const_iterator field; for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; int thisSize = field->second; DENS_MAT & myField(fields[thisField].set_quantity()); DENS_MAT & myDotField(dot_fields[thisField].set_quantity()); DENS_MAT & myDDotField(ddot_fields[thisField].set_quantity()); DENS_MAT & myDDDotField(dddot_fields[thisField].set_quantity()); bool warn = false; for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { XT_Function *f = ics_[thisField](inode,thisIndex); if (!f) f = bcs_[thisField](inode,thisIndex); if (f) { DENS_VEC coords(3); coords = (feEngine_->fe_mesh())->nodal_coordinates(inode); double *x = coords.ptr(); myField(inode,thisIndex) = f->f(x,t); myDotField(inode,thisIndex) = f->dfdt(x,t); myDDotField(inode,thisIndex) = f->ddfdt(x,t); myDDDotField(inode,thisIndex) = f->dddfdt(x,t); } else { myField(inode,thisIndex) = 0; myDotField(inode,thisIndex) = 0; myDDotField(inode,thisIndex) = 0; myDDDotField(inode,thisIndex) = 0; warn = true; } } } // if (warn && is_dynamic(thisField)) { need access tp physics model or return warn per field if (warn) { stringstream ss; ss << ("WARNING: all initial conditions for " +field_to_string(thisField)+" have not been defined and the undefined are assumed zero"); ATC::LammpsInterface::instance()->print_msg_once(ss.str()); } } } //------------------------------------------------------------------------- // set_fixed_fields //------------------------------------------------------------------------- void PrescribedDataManager::set_fixed_fields(const double t, FIELDS &fields, FIELDS &dot_fields, FIELDS &ddot_fields, FIELDS &dddot_fields) { map<FieldName,int>::const_iterator field; for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; int thisSize = field->second; for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { BC_SET & bcs = (bcValues_[thisField])[thisIndex]; bcs.clear(); for (int inode = 0; inode < nNodes_ ; ++inode) { XT_Function * f = bcs_[thisField](inode,thisIndex); if (f) { DENS_VEC coords(3); coords = (feEngine_->fe_mesh())->nodal_coordinates(inode); double * x = coords.ptr(); double val = f->f(x,t); (fields [thisField].set_quantity())(inode,thisIndex) = val; (dot_fields [thisField].set_quantity())(inode,thisIndex) = f->dfdt(x,t); (ddot_fields [thisField].set_quantity())(inode,thisIndex) = f->ddfdt(x,t); (dddot_fields[thisField].set_quantity())(inode,thisIndex) = f->dddfdt(x,t); // compact set pair <int, double > bc = make_pair(inode,val); bcs.insert(bc); } } } } } //------------------------------------------------------------------------- // set_fixed_field //------------------------------------------------------------------------- void PrescribedDataManager::set_fixed_field( const double t, const FieldName & fieldName, DENS_MAT & fieldMatrix) { map<FieldName,int>::iterator fieldSizeIter = fieldSizes_.find(fieldName); if (fieldSizeIter == fieldSizes_.end()) { throw ATC_Error( "Unrecognized FieldName in PrescribedDataManager::set_fixed_field()"); } int thisSize = fieldSizeIter->second; for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { XT_Function * f = bcs_[fieldName](inode,thisIndex); if (f) { DENS_VEC coords(3); coords = (feEngine_->fe_mesh())->nodal_coordinates(inode); fieldMatrix(inode,thisIndex) = f->f(coords.ptr(),t); } } } } //------------------------------------------------------------------------- // set_fixed_dfield //------------------------------------------------------------------------- void PrescribedDataManager::set_fixed_dfield( const double t, const FieldName & fieldName, DENS_MAT & dfieldMatrix) { map<FieldName,int>::iterator fieldSizeIter = fieldSizes_.find(fieldName); if (fieldSizeIter == fieldSizes_.end()) { throw ATC_Error( "Unrecognized FieldName in PrescribedDataManager::set_fixed_dfield()"); } int thisSize = fieldSizeIter->second; for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { XT_Function * f = bcs_[fieldName](inode,thisIndex); if (f) { DENS_VEC coords(3); coords = (feEngine_->fe_mesh())->nodal_coordinates(inode); dfieldMatrix(inode,thisIndex) = f->dfdt(coords.ptr(),t); } } } } //------------------------------------------------------------------------- // set_sources //------------------------------------------------------------------------- void PrescribedDataManager::set_sources (double t, FIELDS & sources) { // zero Array<bool> fieldMask(NUM_FIELDS); fieldMask = false; map<FieldName,int>::const_iterator field; for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; fieldMask(thisField) = true; int thisSize = field->second; sources[thisField].reset(nNodes_,thisSize); } // compute boundary fluxes feEngine_->add_fluxes(fieldMask,t,faceSources_,sources); // compute internal sources feEngine_->add_sources(fieldMask,t,elementSources_,sources); + feEngine_->add_sources(fieldMask,t,nodalSources_,sources); // mask out nodes with essential bcs for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; int thisSize = field->second; for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { XT_Function * f = bcs_[thisField](inode,thisIndex); if (f) { (sources[thisField].set_quantity())(inode,thisIndex) = 0.0; } } } } } //------------------------------------------------------------------------- // print //------------------------------------------------------------------------- void PrescribedDataManager::print(void) { // print and check consistency enum dataType {FREE=0,FIELD,SOURCE}; Array2D < int > bcTypes; Array <int> conn; map<FieldName,int>::const_iterator field; XT_Function * f; for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { FieldName thisField = field->first; int thisFieldSize = field->second; string fieldName = field_to_string(thisField); int thisSize = field->second; bcTypes.reset(nNodes_,thisSize); for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { f = bcs_[thisField](inode,thisIndex); if (f) { bcTypes(inode,thisIndex) = FIELD; } else { bcTypes(inode,thisIndex) = FREE; } } } // FIXED has higher precidence than SOURCE for (int ielem = 0; ielem < nElems_ ; ++ielem) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { f = elementSources_[thisField](ielem,thisIndex); if (f) { feEngine_->element_connectivity(ielem,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); if (bcTypes(inode,thisIndex) != FIELD) { bcTypes(inode,thisIndex) = SOURCE; } } } } } map < pair<int,int>, Array < XT_Function * > > & fset = faceSources_[thisField]; map < pair<int,int>, Array < XT_Function * > > ::const_iterator iset; for (iset = fset.begin(); iset != fset.end(); iset++) { pair<int,int> face = iset->first; Array < XT_Function * > fs = iset->second; for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { f = fs(thisIndex); if (f) { feEngine_->face_connectivity(face,conn); for (int i = 0; i < conn.size() ; ++i) { int inode = conn(i); if (bcTypes(inode,thisIndex) != FIELD) { bcTypes(inode,thisIndex) = SOURCE; } } } } } for (int inode = 0; inode < nNodes_ ; ++inode) { for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { cout << "node: " << inode << " " << fieldName; if (thisFieldSize > 1) { cout << " " << thisIndex; } f = ics_[thisField](inode,thisIndex); if (f) { cout << " IC"; } if (bcTypes(inode,thisIndex) == FIELD ) {cout << " FIXED"; } else if (bcTypes(inode,thisIndex) == SOURCE) {cout << " SOURCE"; } cout << "\n"; } } } } //------------------------------------------------------------------------- // get_bcs of a subset of nodes //------------------------------------------------------------------------- void PrescribedDataManager::bcs(const FieldName thisField, const set<int> nodeSet, BCS & bcs, bool local) const { bcs.clear(); int thisSize = (fieldSizes_.find(thisField))->second; bcs.resize(thisSize); for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { set<int>::const_iterator iset; int i = 0; for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { int inode = *iset; const BC_SET & allBCs = ((bcValues_.find(thisField))->second)[thisIndex]; BC_SET::const_iterator bset; for (bset = allBCs.begin(); bset != allBCs.end(); bset++) { int bnode = (*bset).first; if (inode == bnode) { double val = (*bset).second; if (local) inode = i; // use a local numbering pair <int, double > bc = make_pair(inode,val); (bcs[thisIndex]).insert(bc); } } i++; } } } } // end namespace diff --git a/lib/atc/PrescribedDataManager.h b/lib/atc/PrescribedDataManager.h index 1801360a8..05f553ab2 100644 --- a/lib/atc/PrescribedDataManager.h +++ b/lib/atc/PrescribedDataManager.h @@ -1,393 +1,413 @@ #ifndef PRESCRIBED_DATA_MANAGER_H #define PRESCRIBED_DATA_MANAGER_H #include <vector> #include <map> #include <set> #include <string> #include <utility> #include "ATC_TypeDefs.h" #include "Function.h" #include "PhysicsModel.h" #include "FE_Element.h" #include "Array.h" #include "Array2D.h" #include "FE_Engine.h" namespace ATC { /** * @class PrescribedDataManager * @brief Base class for managing initial conditions, essential/natural "boundary" conditions and sources */ class PrescribedDataManager { public: /** exclusive conditions: free | fixed field | flux or domain source */ //enum Bc_Type {FREE=0,FIELD,SOURCE}; PrescribedDataManager(FE_Engine * feEngine, const std::map<FieldName,int> & fieldSize); ~PrescribedDataManager(); /** add/remove a field */ void add_field(FieldName fieldName, int size); void remove_field(FieldName fieldName); /** direct access to ics */ std::map < FieldName, Array2D < XT_Function * > > * ics(void) { return & ics_; } const Array2D < XT_Function * > * ics(FieldName fieldName) { return & ics_[fieldName]; } /** direct access to bcs */ const std::map < FieldName, BCS > & bcs(void) const { return bcValues_; } /** */ const BCS & bcs(const FieldName fieldName) const { return (bcValues_.find(fieldName))->second; } /** */ void bcs (const FieldName fieldName, const std::set<int> nodes, BCS & bcs, bool local = false) const; /** */ std::map < FieldName, Array2D < XT_Function * > > * bc_functions(void) { return & bcs_; } /** */ const Array2D < XT_Function * > * bc_functions(FieldName fieldName) { return & bcs_[fieldName]; } /** */ ROBIN_SURFACE_SOURCE * robin_functions(void) { return & faceSourcesRobin_; } bool has_robin_source(FieldName fieldName) const { return ((faceSourcesRobin_.find(fieldName)->second).size() > 0) ; } /** */ const std::map<PAIR, Array<UXT_Function*> > * robin_functions(FieldName fieldName) { return & faceSourcesRobin_[fieldName]; } + /** */ + OPEN_SURFACE * open_faces(void) { return & facesOpen_; } + bool has_open_face(FieldName fieldName) const { + return ((facesOpen_.find(fieldName)->second).size() > 0) ; + } + /** */ + const std::set<PAIR> * + open_faces(FieldName fieldName) { return & facesOpen_[fieldName]; } /** query initial state */ bool is_initially_fixed(const int node, const FieldName thisField, const int thisIndex=0) const { return ((ics_.find(thisField)->second))(node,thisIndex) ? true : false ; } /** query state */ bool is_fixed(const int node, const FieldName thisField, const int thisIndex=0) const { return ((bcs_.find(thisField)->second))(node,thisIndex) ? true : false ; } /** */ std::set<int> fixed_nodes( const FieldName thisField, const int thisIndex=0) const { std::set<int> fixed; const Array2D < XT_Function *> & bcs = bcs_.find(thisField)->second; for (int node = 0; node < bcs.nRows() ; node++) { if (bcs(node,thisIndex)) fixed.insert(node); } return fixed; } /** */ void fixed_nodes( const FieldName thisField, std::set<int> & fixed, const int thisIndex=0) const { const Array2D < XT_Function *> & bcs = bcs_.find(thisField)->second; for (int node = 0; node < bcs.nRows() ; node++) { if (bcs(node,thisIndex)) fixed.insert(node); } } /** to determine whether a solution is needed */ bool all_fixed( const FieldName thisField, const int thisIndex=-1) const { if (thisIndex < 0) { // static_casts are to iterface with std::vector without compiler warngings bool allFixed = (fixed_nodes(thisField,0).size() == static_cast<unsigned>(nNodes_) ); int ndof = (fieldSizes_.find(thisField)->second); for (int j = 1; j < ndof; ++j) { allFixed = allFixed && (fixed_nodes(thisField,j).size() == static_cast<unsigned>(nNodes_)); } return allFixed; } else { return (fixed_nodes(thisField,thisIndex).size() == static_cast<unsigned>(nNodes_) ); } } /** a function to determine if the tangent is invertible */ bool none_fixed( const FieldName thisField, const int thisIndex=0) const { return (fixed_nodes(thisField,thisIndex).size() == 0 ) - && (faceSourcesRobin_.size() == 0); + && (faceSourcesRobin_.size() == 0) + && (facesOpen_.size() == 0); } /** */ std::set<int> flux_face_nodes( const FieldName thisField, const int thisIndex=0) const { std::set<int> fluxes; //list of nodes to insert. //1 for nodes to insert, 0 for nodes not to insert. int toInsert[nNodes_]; for (int i = 0; i < nNodes_; ++i) toInsert[i] = 0; const std::map < std::pair <int, int>, Array < XT_Function * > > & sources = faceSources_.find(thisField)->second; std::map < std::pair <int, int>, Array < XT_Function * > >::const_iterator fset_iter; for (fset_iter = sources.begin(); fset_iter != sources.end(); fset_iter++) { int ielem = fset_iter->first.first; // if this is not our element, do not do calculations if (!feEngine_->fe_mesh()->is_owned_elt(ielem)) continue; const Array <XT_Function*> &fs = fset_iter->second; if (fs(thisIndex)) { Array<int> nodes; feEngine_->face_connectivity(fset_iter->first,nodes); for (int node = 0; node < nodes.size(); node++) { toInsert[nodes(node)] = 1; } } } // gather partial results LammpsInterface::instance()->logical_or(MPI_IN_PLACE, toInsert, nNodes_); // insert selected elements into fluxes for (int node = 0; node < nNodes_; ++node) { if (toInsert[node]) fluxes.insert(node); } return fluxes; } /** */ void flux_face_nodes( const FieldName thisField, std::set<int> fluxes, const int thisIndex=0) const { //list of nodes to insert. //1 for nodes to insert, 0 for nodes not to insert. int toInsert[nNodes_]; for (int i = 0; i < nNodes_; ++i) toInsert[i] = 0; const std::map < std::pair <int, int>, Array < XT_Function * > > & sources = faceSources_.find(thisField)->second; std::map < std::pair <int, int>, Array < XT_Function * > >::const_iterator fset_iter; for (fset_iter = sources.begin(); fset_iter != sources.end(); fset_iter++) { int ielem = fset_iter->first.first; // if this is not our element, do not do calculations if (!feEngine_->fe_mesh()->is_owned_elt(ielem)) continue; const Array <XT_Function*> &fs = fset_iter->second; if (fs(thisIndex)) { Array<int> nodes; feEngine_->face_connectivity(fset_iter->first,nodes); for (int node = 0; node < nodes.size(); node++) { toInsert[nodes(node)] = 1; } } } // gather partial results LammpsInterface::instance()->logical_or(MPI_IN_PLACE, toInsert, nNodes_); // insert selected elements into fluxes for (int node = 0; node < nNodes_; ++node) { if (toInsert[node]) fluxes.insert(node); } } /** */ std::set<int> flux_element_nodes( const FieldName thisField, const int thisIndex=0) const { std::set<int> fluxes; //list of nodes to insert. //1 for nodes to insert, 0 for nodes not to insert. int toInsert[nNodes_]; for (int i = 0; i < nNodes_; ++i) toInsert[i] = 0; const Array2D < XT_Function *> & sources = elementSources_.find(thisField)->second; for (int element = 0; element < sources.nRows() ; element++) { // if this is not our element, do not do calculations if (!feEngine_->fe_mesh()->is_owned_elt(element)) continue; if (sources(element,thisIndex)) { Array<int> nodes; feEngine_->element_connectivity(element,nodes); for (int node = 0; node < nodes.size(); node++) { toInsert[nodes(node)] = 1; } } } // gather partial results LammpsInterface::instance()->logical_or(MPI_IN_PLACE, toInsert, nNodes_); // insert selected elements into fluxes for (int node = 0; node < nNodes_; ++node) { if (toInsert[node]) fluxes.insert(node); } return fluxes; } /** */ void flux_element_nodes( const FieldName thisField, std::set<int> fluxes, const int thisIndex=0) const { //list of nodes to insert. //1 for nodes to insert, 0 for nodes not to insert. int toInsert[nNodes_]; for (int i = 0; i < nNodes_; ++i) toInsert[i] = 0; const Array2D < XT_Function *> & sources = elementSources_.find(thisField)->second; for (int element = 0; element < sources.nRows() ; element++) { // if this is not our element, do not do calculations if (!feEngine_->fe_mesh()->is_owned_elt(element)) continue; if (sources(element,thisIndex)) { Array<int> nodes; feEngine_->element_connectivity(element,nodes); for (int node = 0; node < nodes.size(); node++) { toInsert[nodes(node)] = 1; } } } // gather partial results LammpsInterface::instance()->logical_or(MPI_IN_PLACE, toInsert, nNodes_); // insert selected elements into fluxes for (int node = 0; node < nNodes_; ++node) { if (toInsert[node]) fluxes.insert(node); } } /** */ bool no_fluxes( const FieldName thisField, const int thisIndex=0) const { return ((flux_element_nodes(thisField,thisIndex).size() == 0) && (flux_face_nodes(thisField,thisIndex).size() == 0)); } /** set initial field values */ void fix_initial_field (const std::string nodesetName, const FieldName thisField, const int thisIndex, const XT_Function * f); /** un/set field values at fixed nodesets */ void fix_field (const std::set<int> nodeset, const FieldName thisField, const int thisIndex, const XT_Function * f); /** un/set field values at fixed nodesets */ void fix_field (const std::string nodesetName, const FieldName thisField, const int thisIndex, const XT_Function * f); void unfix_field (const std::string nodesetName, const FieldName thisField, const int thisIndex); /** un/set field values at fixed nodes */ void fix_field (const int nodeId, const FieldName thisField, const int thisIndex, const XT_Function * f); void unfix_field (const int nodeId, const FieldName thisField, const int thisIndex); /** un/set fluxes */ void fix_flux (const std::string facesetName, const FieldName thisField, const int thisIndex, const XT_Function * f); void unfix_flux(const std::string facesetName, const FieldName thisField, const int thisIndex); void fix_robin (const std::string facesetName, const FieldName thisField, const int thisIndex, const UXT_Function * f); void unfix_robin(const std::string facesetName, const FieldName thisField, const int thisIndex); + void fix_open (const std::string facesetName, + const FieldName thisField); + void unfix_open(const std::string facesetName, + const FieldName thisField); /** un/set sources */ - void fix_source(const std::string nodesetName, + void fix_source(const std::string elemsetName, const FieldName thisField, const int thisIndex, const XT_Function * f); - void unfix_source(const std::string nodesetName, + void fix_source( const FieldName thisField, + const int thisIndex, + const std::set<std::pair<int,double> > & source); + void unfix_source(const std::string elemsetName, const FieldName thisField, const int thisIndex); /** get initial conditions */ void set_initial_conditions(const double time, FIELDS & fields, FIELDS & dot_fields, FIELDS & ddot_fields, FIELDS & dddot_fields); /** get "boundary" conditions on fields */ void set_fixed_fields(const double time, FIELDS & fields, FIELDS & dot_fields, FIELDS & ddot_fields, FIELDS & dddot_fields); /** get "boundary" conditions on a single field */ void set_fixed_field(const double time, const FieldName & fieldName, DENS_MAT & fieldMatrix); /** get "boundary" conditions on a single time derivative field */ void set_fixed_dfield(const double time, const FieldName & fieldName, DENS_MAT & dfieldMatrix); /** get "sources" (flux and sources: divided by leading coef of ODE) */ void set_sources(const double time, FIELDS & sources); /** debugging status output */ void print(void); private: /** number of unique nodes */ int nNodes_; /** number of elements */ int nElems_; /** names and sizes of fields */ std::map<FieldName,int> fieldSizes_; /** access to all the FE computations */ FE_Engine * feEngine_; // node numbering & dof numbering : contiguous // fieldname & bc_type : types/enums /** ics : XT_Function * f = ics_[field](inode,idof) */ std::map < FieldName, Array2D < XT_Function * > > ics_; /** bcs: essential bcs XT_Function * f = bcs_[field][face](idof) */ std::map < FieldName, Array2D < XT_Function * > > bcs_; /** sources : XT_Function * f = faceSources_[field][face](idof) */ std::map < FieldName, std::map < std::pair <int, int>, Array < XT_Function * > > > faceSources_; /** sources : UXT_Function * f = faceSourcesRobin_[field][face](idof) */ std::map < FieldName, std::map < std::pair <int, int>, Array < UXT_Function * > > > faceSourcesRobin_; + /** sources : facesOpen_[field][face] */ + std::map < FieldName, std::set < std::pair <int, int> > > + facesOpen_; /** sources : XT_Function * f = elementSources_[field](ielem,idof) */ std::map < FieldName, Array2D < XT_Function * > > elementSources_; + FIELDS nodalSources_; /** values of bcs in a compact set */ std::map < FieldName, BCS > bcValues_; }; } #endif diff --git a/lib/atc/SchrodingerSolver.cpp b/lib/atc/SchrodingerSolver.cpp index 191ddbb46..0bbfde8e2 100644 --- a/lib/atc/SchrodingerSolver.cpp +++ b/lib/atc/SchrodingerSolver.cpp @@ -1,559 +1,955 @@ // ATC Headers #include "SchrodingerSolver.h" #include "ATC_Error.h" #include "ATC_Coupling.h" #include "LammpsInterface.h" #include "PrescribedDataManager.h" #include "PhysicsModel.h" #include "LinearSolver.h" #include "PoissonSolver.h" +#include "Utility.h" #include <utility> - using std::pair; using std::set; +using std::stringstream; +using std::min; +using ATC_Utility::to_string; +using ATC_Utility::sgn; const double tol = 1.e-8; const double zero_tol = 1.e-12; const double f_tol = 1.e-8; namespace ATC { -enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX}; +enum oneDconservationEnum {ONED_DENSITY=0, ONED_FLUX, ONED_GLOBAL_FLUX}; + + + + double fermi_dirac(const double E, const double T) { double f = 1.0; - if (T > 0) f = 1.0 / ( exp(E/kBeV_/T)+1.0 ); + if (T > 0) f = 1.0 / ( exp(E/(kBeV_*T))+1.0 ); else if (E > 0) f = 0; return f; }; - //-------------------------------------------------------- + //======================================================== // Schrodinger solve - //-------------------------------------------------------- + //======================================================== SchrodingerSolver::SchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, - const int solverType, bool parallel ) : atc_(atc), feEngine_(feEngine), prescribedDataMgr_(prescribedDataMgr), physicsModel_(physicsModel), fieldName_(fieldName), - solver_(NULL), - solverType_(solverType), nNodes_(atc->num_nodes()), parallel_(parallel) { } - SchrodingerSolver::~SchrodingerSolver() - { - if (solver_) delete solver_; - } - + //----------------------------------------------------- void SchrodingerSolver::initialize() { SPAR_MAT sparseM; atc_->fe_engine()->compute_mass_matrix(sparseM); M_ = sparseM.dense_copy(); } - + //----------------------------------------------------- bool SchrodingerSolver::solve(FIELDS & fields) { // typedef struct{float real, imag;} COMPLEX; SPAR_MAT stiffness_; Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_WAVEFUNCTION,FLUX) = true; rhsMask(ELECTRON_WAVEFUNCTION,SOURCE) = true; pair<FieldName,FieldName> row_col(ELECTRON_WAVEFUNCTION, ELECTRON_WAVEFUNCTION); //set_fixed_nodes(); atc_->fe_engine()->compute_tangent_matrix( rhsMask, row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), stiffness_); DENS_MAT K(stiffness_.dense_copy()); set<int> fixedNodes = prescribedDataMgr_->fixed_nodes(ELECTRON_WAVEFUNCTION); const BC_SET & bcs = (prescribedDataMgr_->bcs(ELECTRON_WAVEFUNCTION))[0]; DENS_MAT & psi = (atc_->field(ELECTRON_WAVEFUNCTION)).set_quantity(); DENS_MAT & eVecs = (atc_->field(ELECTRON_WAVEFUNCTIONS)).set_quantity(); DENS_MAT & eVals = (atc_->field(ELECTRON_WAVEFUNCTION_ENERGIES)).set_quantity(); if (prescribedDataMgr_->all_fixed(ELECTRON_WAVEFUNCTION)) { ATC::LammpsInterface::instance()->print_msg("all wavefunctions fixed"); psi.reset(nNodes_,1); eVecs.reset(nNodes_,1); eVals.reset(nNodes_,1); return true; } // (1) Helmholtz solve for inhomongeneous bcs LinearSolver helmholtzSolver_(K,bcs,LinearSolver::AUTO_SOLVE,-1,parallel_); psi.reset(nNodes_,1); // (2) Eigenvalue solve helmholtzSolver_.eigen_system(eVals,eVecs,&M_); return true; } - //-------------------------------------------------------- + //======================================================== // Schrodinger solve on slices - //-------------------------------------------------------- + //======================================================== SliceSchrodingerSolver::SliceSchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, const Array< set<int> > & oneDslices, - const int solverType, + const Array< double > & oneDdxs, bool parallel ) : SchrodingerSolver(fieldName, physicsModel, feEngine, prescribedDataMgr, - atc, solverType, parallel), - oneDslices_(oneDslices) - { - } - SliceSchrodingerSolver::~SliceSchrodingerSolver() - { - } + atc, parallel), + oneDslices_(oneDslices), + oneDdxs_(oneDdxs) + {} + //-------------------------------------------------------- void SliceSchrodingerSolver::initialize() { SchrodingerSolver::initialize(); } - + //-------------------------------------------------------- + // compute charge density per slice + //-------------------------------------------------------- bool SliceSchrodingerSolver::solve(FIELDS & fields) { // fields DENS_MAT & psi = (atc_->field(ELECTRON_WAVEFUNCTION)).set_quantity(); DENS_MAT & eVecs = (atc_->field(ELECTRON_WAVEFUNCTIONS)).set_quantity(); DENS_MAT & eVals = (atc_->field(ELECTRON_WAVEFUNCTION_ENERGIES)).set_quantity(); psi.reset(nNodes_,1); eVecs.reset(nNodes_,nNodes_); eVals.reset(nNodes_,1); DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); DENS_MAT & n = (atc_->field(ELECTRON_DENSITY)).set_quantity(); DENS_MAT & T = (atc_->field(ELECTRON_TEMPERATURE)).set_quantity(); - // stiffness = K + V M SPAR_MAT stiffness_; Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_WAVEFUNCTION,FLUX) = true; rhsMask(ELECTRON_WAVEFUNCTION,SOURCE) = true; pair<FieldName,FieldName> row_col(ELECTRON_WAVEFUNCTION, ELECTRON_WAVEFUNCTION); atc_->fe_engine()->compute_tangent_matrix( rhsMask, row_col, atc_->fields(), physicsModel_, atc_->element_to_material_map(), stiffness_); DENS_MAT K(stiffness_.dense_copy()); // Eigenvalue solve DENS_MAT K1,M1; int nslices = oneDslices_.size(); DENS_MAT b ; DENS_MAT evals1,evecs1 ; DENS_MAT n1 ; BCS bcs; set <int> one; one.insert(0); set <int> eindex; int iEVal = 0; for (int islice = 0; islice < nslices ; islice++) { set<int> & slice = oneDslices_(islice); int snodes = slice.size(); prescribedDataMgr_->bcs(ELECTRON_WAVEFUNCTION,slice,bcs,true); const BC_SET & bc = bcs[0]; int nfixed = bc.size(); if (nfixed != snodes) { + // A: solve for e-values and wavefunctions K.map(slice,slice,K1); M_.map(slice,slice,M1); - LinearSolver eigensolver(K1,bc,LinearSolver::AUTO_SOLVE,-1,parallel_); + LinearSolver eigensolver(K1,bc,LinearSolver::AUTO_SOLVE,-1); // wave functions evals1.reset(snodes,1); evecs1.reset(snodes,snodes); eigensolver.eigen_system(evals1,evecs1,&M1); eindex.clear(); for (int j = 0; j < snodes; j++) eindex.insert(iEVal++); eVals.insert(eindex,one, evals1); eindex.clear(); for (int j = 0; j < snodes; j++) eindex.insert(j); eVecs.insert(slice,eindex,evecs1); - // electron density + // slice charge density n1.reset(snodes,1); set<int>::const_iterator iset; double aveE_f = 0; for (iset = slice.begin(); iset != slice.end(); iset++) { int gnode = *iset; aveE_f += Ef(gnode,0); } aveE_f /= snodes; - +//#define VERBOSE +#ifdef VERBOSE + stringstream ss; + ss << " slice "+to_string(islice+1)+" E_f "+to_string(aveE_f) << "\n" + << "#-----------------------------------------------\n" + << "# E-Ef f psi n\n" + << "#-----------------------------------------------\n"; +#endif + // B: compute charge density on slice int node = 0; for (iset = slice.begin(); iset != slice.end(); iset++) { // node int gnode = *iset; double temp = T(gnode,0); - //double E_f = Ef(gnode,0); for (int mode = 0; mode < snodes-nfixed; mode++) { double Ei = evals1(mode,0); double E = Ei-aveE_f; double f = fermi_dirac(E,temp); - if (f < f_tol) break; // take advantage of E ordering double psi1 = evecs1(node,mode); // 2nd index corresp to evals order +#ifdef VERBOSE + ss << node<<":"<<mode << " " << to_string(6,E) << " " << to_string(6,f) << " " << to_string(6,psi1) << " " << to_string(6,n1(node,0)+psi1*psi1*f) << "\n"; +#endif + if (f < f_tol) break; // take advantage of E ordering n1(node,0) += psi1*psi1*f; } node++; } +#ifdef VERBOSE + ATC::LammpsInterface::instance()->print_msg_once(ss.str()); +#endif n.insert(slice,one, n1); // note not "assemble" } } return true; } - //-------------------------------------------------------- + //======================================================== // Schrodinger-Poisson Manager - //-------------------------------------------------------- + //======================================================== SchrodingerPoissonManager::SchrodingerPoissonManager() : maxConsistencyIter_(0), maxConstraintIter_(0), oneD_(false), oneDconserve_(ONED_FLUX), Ef_shift_(0.), - safe_dEf_(0.) - { - } - SchrodingerPoissonManager::~SchrodingerPoissonManager() + safe_dEf_(0.), + tol_(1.e-10), + mu_(1.),D_(0.) { } - + //---------------------------------------------------------- bool SchrodingerPoissonManager::modify(int narg, char **arg) { bool match = false; int argIndx = 0; if (strcmp(arg[argIndx],"self_consistency")==0) { argIndx++; maxConsistencyIter_ = atoi(arg[argIndx]); match = true; } else if (strcmp(arg[argIndx],"conserve")==0) { oneD_ = true; argIndx++; - if (strcmp(arg[argIndx],"density")==0) oneDconserve_ = ONED_DENSITY; - else oneDconserve_ = ONED_FLUX; + if (strcmp(arg[argIndx],"density")==0) oneDconserve_ = ONED_DENSITY; + else if (strcmp(arg[argIndx],"flux")==0) oneDconserve_ = ONED_FLUX; + else oneDconserve_ = ONED_GLOBAL_FLUX; argIndx++; maxConstraintIter_ = atoi(arg[argIndx]); match = true; } else if (strcmp(arg[argIndx],"initial_fermi_level")==0) { argIndx++; Ef_shift_ = atof(arg[argIndx]); match = true; } else if (strcmp(arg[argIndx],"safe_fermi_increment")==0) { argIndx++; safe_dEf_ = atof(arg[argIndx]); match = true; } + else if (strcmp(arg[argIndx],"relaxation")==0) { + argIndx++; + alpha_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"tolerance")==0) { + argIndx++; + tol_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"mobility")==0) { + argIndx++; + mu_ = atof(arg[argIndx]); + match = true; + } + else if (strcmp(arg[argIndx],"diffusivity")==0) { + argIndx++; + D_ = atof(arg[argIndx]); + match = true; + } return match; } - + //---------------------------------------------------------------- SchrodingerPoissonSolver * SchrodingerPoissonManager::initialize( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel ) { SchrodingerPoissonSolver * ptr; if (oneD_) { + if (oneDconserve_ == ONED_GLOBAL_FLUX) { + ptr = new GlobalSliceSchrodingerPoissonSolver(atc, + schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter_, + maxConstraintIter_, oneDconserve_, Ef_shift_, alpha_, safe_dEf_, tol_, + mu_,D_); + } + else { ptr = new SliceSchrodingerPoissonSolver(atc, schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter_, maxConstraintIter_, oneDconserve_, Ef_shift_, safe_dEf_); + } } else { ptr = new SchrodingerPoissonSolver(atc, schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter_); } return ptr; } - //------------------------------------------------------------------- + //=================================================================== // SchrodingerPoissonSolver - //------------------------------------------------------------------- + //=================================================================== SchrodingerPoissonSolver::SchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel, int maxConsistencyIter ) : atc_(atc), schrodingerSolver_(schrodingerSolver), poissonSolver_(poissonSolver), physicsModel_(physicsModel), maxConsistencyIter_(maxConsistencyIter), nNodes_(atc_->num_nodes()) { } - SchrodingerPoissonSolver::~SchrodingerPoissonSolver(void) - { - } + //---------------------------------------------------------------------- void SchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) { if ((atc_->prescribed_data_manager()->all_fixed(ELECTRON_WAVEFUNCTION)) && (atc_->prescribed_data_manager()->all_fixed(ELECTRIC_POTENTIAL))) { return; } double norm = 1.0, norm0 = 1.0; // normPrev = 1.0; DENS_MAT nPrev,psiPrev,phiPrev; DENS_MAT & psi = (atc_->field(ELECTRON_WAVEFUNCTIONS)).set_quantity(); DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); DENS_MAT & E_I = (atc_->field(ELECTRON_WAVEFUNCTION_ENERGIES)).set_quantity(); DENS_MAT & Te = (atc_->field(ELECTRON_TEMPERATURE)).set_quantity(); atc_->set_fixed_nodes(); DENS_MAT Te0 = Te; // save const double tol = 1.e-4; -// double Tmax = Te.max(); int k = 0; double logRatio = 3; int maxIter = (int) logRatio; double base = 2.0; // temperature relaxation loop for (int i = 0; i < maxIter ; ++i) { //double alpha = ((double) i) /( (double) maxIter-1); //double beta = 0.1; //alpha = (exp(beta*i)-1.0)/(exp(beta*(maxIter-1))-1.0); double alpha = pow(base,logRatio-i-1); // self consistency loop int j = 0; // for storage of last iterate for (j = 0; j < maxConsistencyIter_ ; ++j) { // compute eigen-values and vectors atc_->set_fixed_nodes(); Te = alpha*Te0; schrodingerSolver_->solve(atc_->fields()); for (int l = 0; l < nNodes_; l++) { int count = 0; double T_e = Te(l,0); for (int m = 0; m < nNodes_; m++) { double f = fermi_dirac(E_I(m,0), T_e); if (f > tol) count++; } } // compute charge density DENS_MAN & n = atc_->field(ELECTRON_DENSITY); //(n.quantity()).print("DENSITY"); atc_->nodal_projection(ELECTRON_DENSITY,physicsModel_,n); atc_->set_fixed_nodes(); // solve poisson eqn for electric potential atc_->set_fixed_nodes(); Te = alpha*Te0; poissonSolver_->solve(atc_->fields(),rhs); //DENS_MAT dn = n; //DENS_MAT dpsi = psi; //DENS_MAT dphi = phi; if (i == 0 && j==0) { nPrev = n.quantity(); psiPrev = psi; phiPrev = phi; } //dn -= nPrev; //dpsi -= psiPrev; //dphi -= phiPrev; norm = (n.quantity()-nPrev).norm(); if (i == 0 && j==0) norm0 = (n.quantity()).norm(); //normPrev = norm; //psi_normPrev = psi_norm; //phi_normPrev = phi_norm; nPrev = n.quantity(); psiPrev = psi; phiPrev = phi; k++; if (j > 0 && norm <= tol*norm0) break; } // Tmax_ *= 0.5; } } - //---------------------------------------------------------------------------- - // SchrodingerPoissonSolver - //------------------------------------------------------------------- + //=================================================================== + // SliceSchrodingerPoissonSolver + //=================================================================== SliceSchrodingerPoissonSolver::SliceSchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel, int maxConsistencyIter, int maxConstraintIter, int oneDconserve, double Ef_shift, double safe_dEf ) : SchrodingerPoissonSolver(atc,schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter), - maxConstraintIter_(maxConstraintIter), oneDconserve_(oneDconserve), oneDcoor_(0), - Ef_shift_(Ef_shift), - safe_dEf_(safe_dEf), - oneDslices_(((SliceSchrodingerSolver *) schrodingerSolver_)->slices()) + oneDslices_(((SliceSchrodingerSolver *) schrodingerSolver_)->slices()), + oneDdxs_(((SliceSchrodingerSolver *) schrodingerSolver_)->dxs()) { + Ef_shift_=Ef_shift; + safe_dEf_=safe_dEf; + maxConstraintIter_=maxConstraintIter; EfHistory_.reset(oneDslices_.size(),2); } - SliceSchrodingerPoissonSolver::~SliceSchrodingerPoissonSolver(void) - { - } + //-------------------------------------------------------------------------- void SliceSchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) { const double tol = 1.e-4; // tolerance on consistency & constraint double norm = 1.0, norm0 = 1.0; DENS_MAT nPrev; DENS_MAT & n = (atc_->field(ELECTRON_DENSITY)).set_quantity(); DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); // fermi energy DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); Ef.reset(nNodes_,1); int nslices = oneDslices_.size(); Array2D<double> nHistory(nslices,2); // target for constraint double target = 0.0; + set<int> & slice = oneDslices_(0); // note assume first slice is fixed if (oneDconserve_ == ONED_FLUX) atc_->set_sources(); DENS_MAT & nSource = (atc_->source(ELECTRON_DENSITY)).set_quantity(); for (set<int>::const_iterator iset = slice.begin(); iset != slice.end(); iset++) { if (oneDconserve_ == ONED_FLUX) target += nSource(*iset,0); else target += n(*iset,0); } target /= slice.size(); +#ifdef VERBOSE + if (oneDconserve_ == ONED_FLUX) { + if (target > 0) ATC::LammpsInterface::instance()->print_msg_once(" influx target "+ to_string(target)); + else ATC::LammpsInterface::instance()->print_msg_once(" efflux target "+ to_string(target)); + } +#endif - // self consistency loop between Phi and n(psi_i) + // A: self consistency loop between Phi and n(psi_i) double error = 1.0; for (int i = 0; i < maxConsistencyIter_ ; ++i) { atc_->set_fixed_nodes(); if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) poissonSolver_->solve(atc_->fields(),rhs); if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) { // iterate on Ef //if (i==0) Ef = -1.0*phi;// E ~ -|e| \Phi, charge of electron e = 1 - Ef = -1.0*phi; // E ~ -|e| \Phi, charge of electron e = 1 in eV + Ef = -1.0*phi; + Ef +=Ef_shift_; + // B: conservation constraint for (int j = 0; j < maxConstraintIter_ ; ++j) { - - schrodingerSolver_->solve(atc_->fields()); - + schrodingerSolver_->solve(atc_->fields()); // n(E_f) atc_->set_fixed_nodes(); - error = update_fermi_energy(target,(j==0),fluxes); + error = update_fermi_energy(target,(j==0),fluxes);// root finder +#ifdef VERBOSE + ATC::LammpsInterface::instance()->print_msg_once(to_string(i)+":"+to_string(j)+" constraint_error "+to_string(error)+" / "+to_string(tol*target)+"\n"); +#endif // exit condition based on constraint satisfaction - if (error < tol*target) break; + if (error < tol*fabs(target)) break; } // loop j : flux constraint // error based on change in field (Cauchy convergence) if (i == 0) { norm = norm0 = n.norm(); } else { DENS_MAT dn = n; dn -= nPrev; norm = dn.norm(); } nPrev = n; +#ifdef VERBOSE +#if 0 + if (i > 0) ATC::LammpsInterface::instance()->print_msg_once(to_string(i)+" density_change: "+to_string(norm)+" / "+to_string(norm0)); + else ATC::LammpsInterface::instance()->print_msg_once("initial norm "+to_string(norm)); +#endif +#endif if (i > 0 && norm <= tol*norm0 && error < tol) break; } } // loop i : self consistency } //-------------------------------------------------------- // update fermi energy //-------------------------------------------------------- double SliceSchrodingerPoissonSolver::update_fermi_energy (double target, bool first, GRAD_FIELD_MATS & fluxes) { DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); - double safe_dEf = safe_dEf_; - DENS_MAT & n = (atc_->field(ELECTRON_DENSITY)).set_quantity(); + DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); const DENS_MAT * y = &n; if (oneDconserve_ == ONED_FLUX) { // compute J_x Array2D <bool> rhsMask(NUM_FIELDS,NUM_FLUX); rhsMask = false; rhsMask(ELECTRON_DENSITY,FLUX) = true; +//#define WIP_REJ atc_->compute_flux(rhsMask,atc_->fields_,fluxes,physicsModel_); y = & ( fluxes[ELECTRON_DENSITY][oneDcoor_] ); } - BCS bcs; double error = 0; // slice for (int islice = 0; islice < oneDslices_.size(); islice++) { +#ifdef VERBOSE + std::string cStr(" conserved "); + std::string Estr(" Ef"); +#endif set<int> & slice = oneDslices_(islice); int nSlice = slice.size(); - //atc_->prescribedDataMgr_->bcs(ELECTRON_DENSITY,slice,bcs,true); atc_->prescribedDataMgr_->bcs(ELECTRON_WAVEFUNCTION,slice,bcs,true); const BC_SET & bc = bcs[0]; int nFixed = bc.size(); - if (nFixed == nSlice) continue; + if (nFixed == nSlice) continue; // skip if all fixed double Y = 0.0, X = 0.0; -double nave = 0.0; + double nAve = 0., phiAve = 0.; for (set<int>::const_iterator iset = slice.begin(); iset != slice.end(); iset++) { int gnode = *iset; X += Ef(gnode,0); Y += (*y)(gnode,0); -nave += n(gnode,0); + nAve += n(gnode,0); + phiAve += phi(gnode,0); } X /= nSlice; Y /= nSlice; -nave /= nSlice; - + nAve /= nSlice; + phiAve /= nSlice; + // now adjust Ef for each slice double dY = Y - EfHistory_(islice,0); double dX = X - EfHistory_(islice,1); - if (fabs(dY) < zero_tol*dX) throw ATC_Error("zero increment in conserved field on slice"); double err = target - Y; if (target*Y < -zero_tol*target) { - //throw ATC_Error("target and quantity opposite signs"); - ATC::LammpsInterface::instance()->print_msg_once("WARNING: target and quantity opposite signs"); +#ifdef VERBOSE + cStr = " opp. SIGNS"; +#else + ATC::LammpsInterface::instance()->print_msg_once("WARNING: slice "+to_string(islice)+" target and quantity opposite signs "+to_string(Y)); +#endif } error += fabs(err); - //error = max(error,err); - double dEf = err / dY * dX; + double dEf = 0.; if (first) { - dEf = (err < 0) ? -safe_dEf : safe_dEf; + dEf = (err < 0) ? -safe_dEf_ : safe_dEf_; } - else if (fabs(dEf) > safe_dEf) { - dEf = safe_dEf * dEf / fabs(dEf); + else { + if (fabs(dY) < zero_tol*dX) throw ATC_Error("zero increment in conserved field on slice:"+to_string(islice)); + dEf = err / dY * dX; + if (fabs(dEf) > safe_dEf_) { + dEf = safe_dEf_* dEf / fabs(dEf); +#ifdef VERBOSE + Estr = " !!"; +#else + ATC::LammpsInterface::instance()->print_msg_once("WARNING: slice "+to_string(islice)+ " large Delta E_f "+to_string(dEf)); +#endif + } } for (set<int>::const_iterator iset = slice.begin(); iset != slice.end(); iset++) { int gnode = *iset; - Ef(gnode,0) += dEf; + Ef(gnode,0) += dEf; } EfHistory_(islice,0) = Y; EfHistory_(islice,1) = X; + if ( std::isnan(Y) ) throw ATC_Error("target on slice is not a number"); +#ifdef VERBOSE + ATC::LammpsInterface::instance()->print_msg_once(" slice"+to_string(islice,2) +cStr+to_string(4,Y/target) +Estr+to_string(4,X)+" n"+to_string(5,nAve)+" phi"+to_string(4,phiAve)); + //ATC::LammpsInterface::instance()->print_msg_once(" slice "+to_string(islice) +cStr+to_string(4,Y/target) +" E_f"+to_string(4,X)+dEstr+to_string(4,X-EfHistory_(std::max(0,islice-1),1))+" n"+to_string(4,nAve)+" phi"+to_string(4,phiAve)+" "+to_string(nFixed)+" dn "+to_string(4,dnAve)+" dphi "+to_string(4,dphiAve)); +#endif } // loop slice return error; } + //=================================================================== + // GlobalSliceSchrodingerPoissonSolver + //=================================================================== + GlobalSliceSchrodingerPoissonSolver::GlobalSliceSchrodingerPoissonSolver( + /*const*/ ATC_Coupling * atc, + SchrodingerSolver * schrodingerSolver, + PoissonSolver * poissonSolver, + const PhysicsModel * physicsModel, + int maxConsistencyIter, + int maxConstraintIter, + int oneDconserve, + double Ef0, + double alpha, + double safe_dEf, + double tol, + double mu, double D + ) : + SliceSchrodingerPoissonSolver(atc,schrodingerSolver,poissonSolver,physicsModel,maxConsistencyIter,maxConstraintIter,oneDconserve,0,0), + solver_(NULL), + mobility_(mu),diffusivity_(D) + { + Ef0_ = Ef0; + alpha_ = alpha; + safe_dEf_ = safe_dEf; + if (safe_dEf_ < 1.e-20) throw ATC_Error("safe dE_f must be positive"); + ATC::LammpsInterface::instance()->print_msg("mobility:"+to_string(mobility_)+" diffusivity:"+to_string(diffusivity_)); + tol_ = tol; + nslices_ = oneDslices_.size(); + sliceSize_ = (oneDslices_(0)).size(); + nNodes_ = nslices_*sliceSize_; + flux_.reset(nNodes_); + J_.reset(nslices_); + //nfixed_ = 2; + nfixed_ = 1; + nfreeSlices_ = nslices_-nfixed_; + nLambda_ = nslices_-1; + lambda_.reset(nLambda_); + dJ_.reset(nLambda_); + F_.reset(nslices_); + Phi_.reset(nslices_); + n_.reset(nslices_); + // form stiffness, lhs dirichlet bc, rhs homogeneous neumann bc + //int m = nfreeSlices_; + int m = nLambda_; + DENS_MAT A(m,m); + for (int i = 1; i < m; ++i) { + A(i,i) = -2; + if (i>0) A(i,i-1) = 1; + if (i<m-1) A(i,i+1) = 1; + } + A(0,0) = -2; + A(0,1) = 1; + A(m-1,m-1) = -2; + A(m-1,m-2) = 1; + //if (nfixed_ == 1) { A(m-1,m-1) = -1; } + double dx = oneDdxs_(0); + A *= 1./dx; + A.print("stiffness",4); + SPAR_MAT K(A); + K_ = K; + // form gradient (account for lhs bc) + int n = nslices_; + DENS_MAT B(m,n); + //for (int i = 0; i < m-1; ++i) { + for (int i = 0; i < m; ++i) { + B(i,i) =-1; + B(i,i+1) = 1; //B(i,i+2) = 1; + } + if (nfixed_ == 1) { + B(m-1,n-2) = -1; + B(m-1,n-1) = 1; + } + B.print("gradient",4); + SPAR_MAT G(B); + G_ = G; + + DENS_MAT C(nNodes_,nNodes_); + // local to ATC nodemap: k --> gnode = *iset + int k = 0; + set<int>::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + double v = 0.5/dx; + if ( k < sliceSize_ || k+1 > (nslices_-1)*sliceSize_ ) v *=2.0; + if (islice > 0) { C(k,k-sliceSize_) += v; } + else { C(k,k) += v; } + if (islice < nslices_-1) { C(k,k+sliceSize_) -= v; } + else { C(k,k) -= v; } + k++; + } + } + //C.print("2D gradient",4); + SPAR_MAT G2(C); + G2_ = G2; + solver_ = new LinearSolver(K_); // for lambda + rhsMask_.reset(NUM_FIELDS,NUM_FLUX); rhsMask_ = false; + rhsMask_(ELECTRON_DENSITY,FLUX) = true; + + // report + if (nfixed_ ==2) + ATC::LammpsInterface::instance()->print_msg_once("schrodinger-poisson solver: Dirichlet INLET, Dirichlet; OUTLET"); + else if (nfixed_ ==1) + ATC::LammpsInterface::instance()->print_msg_once("schrodinger-poisson solver: Dirichlet INLET, Neumann; OUTLET"); + else + ATC_Error("schrodinger-poisson solver:too many fixed"); + } + GlobalSliceSchrodingerPoissonSolver::~GlobalSliceSchrodingerPoissonSolver(void) { + if (solver_) delete solver_; + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::solve(FIELDS & rhs, GRAD_FIELD_MATS & fluxes) + { + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + const DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).quantity(); + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); + Ef.reset(phi.nRows(),1); + norm_ = norm0_ = 1.0; + for (int i = 0; i < maxConstraintIter_ ; ++i) { + atc_->set_fixed_nodes(); + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRIC_POTENTIAL) ) { + poissonSolver_->solve(atc_->fields(),rhs); + } + else { + ATC::LammpsInterface::instance()->print_msg_once("WARNING: phi is fixed"); + } + if (i == 0) { report(0); } + if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_DENSITY) ) { + update_fermi_level(); // update Ef = Ef0 +lambda + schrodingerSolver_->solve(atc_->fields()); // updates n(E_f) + //exponential_electron_density(); // surrogate + compute_flux(n,phi); // compute J(n,phi) & dJ_ + solver_->solve(lambda_,dJ_); // conservation constraint + //lambda_.print("lambda"); + //lambda_.print("[[J}}"); + } + else { + ATC::LammpsInterface::instance()->print_msg_once("WARNING: rho is fixed"); + } + norm_ = dJ_.norm(); + report(i+1); + if (i == 0 && norm_ > tol_) norm0_ = norm_; + else { if (norm_ < tol_*norm0_) break; } + } + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::exponential_electron_density() + { + std::cout << "******************HACK******************\n"; + + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).set_quantity(); + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).set_quantity(); + double T = 300; + double n0 = 1.e-2; + set<int>::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + double aveE_f = 0.0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + aveE_f += Ef(gnode,0); + } + aveE_f /= slice.size(); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + //std::cout << phi(gnode,0)+aveE_f << "\n"; + //n(gnode,0) = -n0*exp(-(phi(gnode,0)+aveE_f)/(kBeV_*T)); + //n(gnode,0) = -n0*exp((-phi(gnode,0))/(kBeV_*T)); + //n(gnode,0) = -n0*exp(aveE_f/(kBeV_*T)); + //n(gnode,0) = aveE_f+0.01; + //n(gnode,0) = aveE_f; + //n(gnode,0) = phi(gnode,0); + //n(gnode,0) = -n0*(phi(gnode,0)+aveE_f)/(kBeV_*T); + n(gnode,0) = -n0*(aveE_f)/(kBeV_*T); + } + } + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::report(int i) + { + const DENS_MAT & phi = (atc_->fields_[ELECTRIC_POTENTIAL]).quantity(); + const DENS_MAT & n = (atc_->fields_[ELECTRON_DENSITY] ).quantity(); + const DENS_MAT & Ef = (atc_->field(FERMI_ENERGY)).quantity(); + set<int>::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + double Phi = 0.0; + double N = 0.0; + double EF = 0.0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Phi += phi(gnode,0); + N += n(gnode,0); + EF += Ef(gnode,0); + } + Phi /= slice.size(); + Phi_(islice) = Phi; // average potential + N /= slice.size(); + n_(islice) = N; // average electron density + EF /= slice.size(); + F_(islice) = EF; // average Fermi level + } + stringstream header; + header << "\n"; + header << "#----------------------------------------------------------------------\n"; + header << "# [[J]] lambda E_f phi n J\n"; + header << "#----------------------------------------------------------------------\n"; + if (i == 0) { + ATC::LammpsInterface::instance()->write_file("slice.dat",header.str()); + } + stringstream ss; + ss << "\n"; + // first slice (fixed E_F) + double dJ0 = J_(1)-J_(0); + ss << to_string(1,2) << "*" << to_string(6,dJ0) << " " << to_string(6,0.) << " " << to_string(6,F_(0)) << " " << to_string(6,Phi_(0)) << " " << to_string(6,n_(0)) << " " << to_string(6,J_(0)) << "\n"; + // interior + for (int j = 1; j < nslices_-1; ++j) { + ss << to_string(j+1,2) << " " << to_string(6,dJ_(j-1)) << " " << to_string(6,lambda_(j-1)) << " " << to_string(6,F_(j)) << " " << to_string(6,Phi_(j)) << " " << to_string(6,n_(j)) << " " << to_string(6,J_(j)) << "\n"; + } + // last slice (fixed E_F) + double dJn = J_(nslices_-1)-J_(nslices_-2); + int j = nslices_-1; + double lambdaN = 0.; + std::string space = "*"; + if (nfixed_ == 1) { + lambdaN = lambda_(nslices_-2); + space = " "; + } + ss << to_string(nslices_,2) << space << to_string(6,dJn) << " " << to_string(6,lambdaN) << " " << to_string(6,F_(j)) << " " << to_string(6,Phi_(j)) << " " << to_string(6,n_(j)) << " " << to_string(6,J_(j)) << "\n"; + stringstream is; + is << "\n# iteration: " << to_string(i)+"/ "+to_string(maxConstraintIter_)+" constraint norm:"+to_string(6,norm_/norm0_) << " " << nslices_ << " slices"; + ATC::LammpsInterface::instance()->print_msg(is.str()+header.str()+ss.str()); + ATC::LammpsInterface::instance()->write_file("slice.dat",ss.str()+is.str()+"\n",std::ofstream::app); + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::compute_flux( + const DENS_MAT & n, const DENS_MAT & phi) + { + DENS_VEC f(nNodes_); + DENS_VEC gradphi(nNodes_); + DENS_VEC gradn(nNodes_); + int k = 0; + set<int>::const_iterator iset; + // grad phi + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + f(k) = phi(gnode,0); + k++; + } + } + //f.print("phi"); + gradphi = G2_*f; + //gradphi.print("grad phi"); + k = 0; + // grad n + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + f(k) = n(gnode,0); + k++; + } + } + //f.print("n"); + gradn = G2_*f; + ////gradn.print("grad n"); + flux_.reset(nNodes_); + for (k = 0; k < nNodes_; k++) { + flux_(k) = -mobility_*f(k)*gradphi(k)-diffusivity_*gradn(k); + } + //flux_.print("flux"); + // per slice flux and diference + k = 0; + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + J_(islice) = 0; + for (iset = slice.begin(); iset != slice.end(); iset++) { + J_(islice) += flux_(k); + k++; + } + J_(islice) /= slice.size(); + //std::cout << islice << " J " << J_(islice) << "\n"; + } + //J_.print("J"); + dJ_ = G_*J_; + } + //-------------------------------------------------------------------------- + void GlobalSliceSchrodingerPoissonSolver::update_fermi_level() + { + + DENS_MAT & Ef = (atc_->field(FERMI_ENERGY) ).set_quantity(); + DENS_MAT & phi = (atc_->field(ELECTRIC_POTENTIAL)).set_quantity(); + DENS_MAT & n = (atc_->field(ELECTRON_DENSITY) ).set_quantity(); + set<int>::const_iterator iset; + for (int islice = 0; islice < nslices_; islice++) { + set<int> & slice = oneDslices_(islice); + double Phi = 0.; + double N = 0.; + //F_(islice) = Ef0_; + if (islice > 0 && islice < nslices_-1) { + F_(islice) += alpha_*lambda_(islice-1); + } + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Phi += phi(gnode,0); + N += n(gnode,0); + } + Phi /= slice.size(); + Phi_(islice) = Phi; // average potential + N /= slice.size(); + n_(islice) = N; // average electron density + + //F_(j) += min(fabs(alpha_*lambda),safe_dEf_)*sgn(lambda); + for (iset = slice.begin(); iset != slice.end(); iset++) { + int gnode = *iset; + Ef(gnode,0) = F_(islice); + } + } + //Ef.print("Ef"); + } }; diff --git a/lib/atc/SchrodingerSolver.h b/lib/atc/SchrodingerSolver.h index e190c8eae..9b8616b45 100644 --- a/lib/atc/SchrodingerSolver.h +++ b/lib/atc/SchrodingerSolver.h @@ -1,204 +1,268 @@ #ifndef SCHRODINGER_SOLVER_H #define SCHRODINGER_SOLVER_H // ATC includes #include "Array2D.h" #include "LinearSolver.h" // other includes #include <vector> #include <map> #include <set> namespace ATC { // Forward class declarations class ATC_Coupling; class FE_Engine; class PrescribedDataManager; class PhysicsModel; class PoissonSolver; /** * @class SchrodingerSolver * @brief a class to solve the (time-independent) Schrodinger equation */ class SchrodingerSolver { public: /** Constructor */ SchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, - const int solverType = ATC::LinearSolver::DIRECT_SOLVE, - bool parallel = false + bool parallel = true ); /** Destructor */ - virtual ~SchrodingerSolver(); + virtual ~SchrodingerSolver(){}; /** parser */ bool modify(int narg, char **arg){ return false;} /** initialize */ void initialize(void); /** solve */ virtual bool solve(FIELDS & fields); protected: /** Pointer to ATC */ ATC_Coupling * atc_; /** Pointer to FE_Engine */ const FE_Engine * feEngine_; /** Pointer to PrescribedDataManager */ const PrescribedDataManager * prescribedDataMgr_; /** Pointer to FE_Engine */ const PhysicsModel * physicsModel_; /** field to solve for */ FieldName fieldName_; - /** linear solver */ - LinearSolver * solver_; - int solverType_; - /** number of nodes */ int nNodes_; - /** stiffness matrix */ - - //SPAR_MAT stiffness_; - - //SPAR_MAT massMatrix_; + /** mass matrix */ DENS_MAT M_; bool parallel_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger equation on slices + */ class SliceSchrodingerSolver : public SchrodingerSolver { public: /** Constructor */ SliceSchrodingerSolver( const FieldName fieldName, const PhysicsModel * physicsModel, const FE_Engine * feEngine, const PrescribedDataManager * prescribedDataMgr, /*const*/ ATC_Coupling * atc, const Array< std::set<int> > & oneDslices, - const int solverType = ATC::LinearSolver::DIRECT_SOLVE, - bool parallel = false + const Array< double > & oneDdxs, + bool parallel = true ); /** Destructor */ - virtual ~SliceSchrodingerSolver(); + virtual ~SliceSchrodingerSolver(){}; /** parser */ bool modify(int narg, char **arg){return false;} /** initialize */ void initialize(void); /** solve */ virtual bool solve(FIELDS & fields); Array< std::set<int> > & slices(void){ return oneDslices_;} + Array< double > & dxs(void){ return oneDdxs_;} protected: Array< std::set<int> > oneDslices_; + Array< double > oneDdxs_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system + */ class SchrodingerPoissonSolver { public: SchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel, int maxConsistencyIter ); - virtual ~SchrodingerPoissonSolver(void); + virtual ~SchrodingerPoissonSolver(void){}; virtual void solve( FIELDS & rhs, GRAD_FIELD_MATS & fluxes ); protected: ATC_Coupling * atc_; SchrodingerSolver * schrodingerSolver_; PoissonSolver * poissonSolver_; const PhysicsModel * physicsModel_; int maxConsistencyIter_; + int maxConstraintIter_; int nNodes_; + double Ef0_; + double Ef_shift_; + double safe_dEf_; + double tol_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system on slices + */ class SliceSchrodingerPoissonSolver : public SchrodingerPoissonSolver { public: SliceSchrodingerPoissonSolver( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel, int maxConsistencyIter, int maxConstraintIter, int oneDconserve, double Ef_shift, double safe_dEf ); - virtual ~SliceSchrodingerPoissonSolver(void); + virtual ~SliceSchrodingerPoissonSolver(void){}; virtual void solve( FIELDS & rhs, GRAD_FIELD_MATS & fluxes ); protected: + int nslices_; double update_fermi_energy(double target, bool first, GRAD_FIELD_MATS & fluxes); - int maxConstraintIter_; int oneDconserve_; int oneDcoor_; - double Ef_shift_; - double safe_dEf_; Array< std::set<int> > & oneDslices_; + Array< double > & oneDdxs_; Array2D<double> EfHistory_; }; +/** + * @class SchrodingerSolver + * @brief a class to solve the Schrodinger-Poisson system on slices + */ +class GlobalSliceSchrodingerPoissonSolver : public SliceSchrodingerPoissonSolver { + public: + GlobalSliceSchrodingerPoissonSolver( + /*const*/ ATC_Coupling * atc, + SchrodingerSolver * schrodingerSolver, + PoissonSolver * poissonSolver, + const PhysicsModel * physicsModel, + int maxConsistencyIter, + int maxConstraintIter, + int oneDconserve, + double Ef0, + double alpha, + double safe_dEf, + double tol, + double mu, double D + ); + virtual ~GlobalSliceSchrodingerPoissonSolver(void); + virtual void solve( + FIELDS & rhs, + GRAD_FIELD_MATS & fluxes + ); + protected: + void compute_flux(const DENS_MAT & n, const DENS_MAT & phi); + void update_fermi_level(); + void report(int i); + void exponential_electron_density(); + class LinearSolver * solver_; + double alpha_; + int sliceSize_, nNodes_, nfreeSlices_, nfixed_, nLambda_; + SPAR_MAT K_; + SPAR_MAT G_,G2_; // 1D & 2D grad mats = int N gradN dv + DENS_VEC J_; + DENS_VEC flux_; + DENS_VEC dJ_; + DENS_VEC lambda_; + DENS_VEC F_; + DENS_VEC Phi_; + DENS_VEC n_; + Array2D <bool> rhsMask_; + double mobility_; + double diffusivity_; + double norm_, norm0_; +}; + +/** + * @class SchrodingerSolver + * @brief a manager class + */ class SchrodingerPoissonManager { public: SchrodingerPoissonManager(); - ~SchrodingerPoissonManager(); + ~SchrodingerPoissonManager(){}; /** parser */ bool modify(int narg, char **arg); /** initialize */ SchrodingerPoissonSolver * initialize( /*const*/ ATC_Coupling * atc, SchrodingerSolver * schrodingerSolver, PoissonSolver * poissonSolver, const PhysicsModel * physicsModel ); protected: int maxConsistencyIter_; int maxConstraintIter_; bool oneD_; int oneDconserve_; double Ef_shift_; double safe_dEf_; + double alpha_; + double tol_; + double mu_, D_; }; } // namespace ATC #endif diff --git a/lib/atc/SystemProcessor.h b/lib/atc/SystemProcessor.h new file mode 100644 index 000000000..9ef511f99 --- /dev/null +++ b/lib/atc/SystemProcessor.h @@ -0,0 +1,283 @@ +/* + *_________________________________________________________________________* + * POEMS: PARALLELIZABLE OPEN SOURCE EFFICIENT MULTIBODY SOFTWARE * + * DESCRIPTION: SEE READ-ME * + * FILE NAME: SystemProcessor.h * + * AUTHORS: See Author List * + * GRANTS: See Grants List * + * COPYRIGHT: (C) 2005 by Authors as listed in Author's List * + * LICENSE: Please see License Agreement * + * DOWNLOAD: Free at www.rpi.edu/~anderk5 * + * ADMINISTRATOR: Prof. Kurt Anderson * + * Computational Dynamics Lab * + * Rensselaer Polytechnic Institute * + * 110 8th St. Troy NY 12180 * + * CONTACT: anderk5@rpi.edu * + *_________________________________________________________________________*/ + +#ifndef _SYS_PROCESSOR_H_ +#define _SYS_PROCESSOR_H_ +#include "poemslist.h" +#include "poemstree.h" +#include "POEMSChain.h" + + +struct POEMSNode { + List<POEMSNode> links; + List<bool> taken; + int idNumber; + bool visited; + + ~POEMSNode(){ + for(int i = 0; i < taken.GetNumElements(); i++) + { + delete taken(i); + } + }; +}; + + +class SystemProcessor{ +private: + Tree nodes; +// List<POEMSNode> forDeletion; + List<POEMSChain> headsOfSystems; + List<List<int> > ringsInSystem; + POEMSNode * findSingleLink(TreeNode * aNode); + POEMSChain * AddNewChain(POEMSNode * currentNode); + bool setLinkVisited(POEMSNode * firstNode, POEMSNode * secondNode); +public: + SystemProcessor(void); + + ~SystemProcessor(void) { + headsOfSystems.DeleteValues(); + for(int i = 0; i < ringsInSystem.GetNumElements(); i++) + { + for(int k = 0; k < ringsInSystem(i)->GetNumElements(); i++) + { + delete (*ringsInSystem(i))(k); + } + } + }; + void processArray(int** links, int numLinks); + List<POEMSChain> * getSystemData(); + int getNumberOfHeadChains(); +}; + +SystemProcessor::SystemProcessor(void){ +} + +void SystemProcessor::processArray(int** links, int numLinks) +{ + bool * false_var; //holds the value false; needed because a constant cannot be put into a list; the list requires a + //reference. + for(int i = 0; i < numLinks; i++) //go through all the links in the input array + { + if(!nodes.Find(links[i][0])) //if the first node in the pair is not found in the storage tree + { + POEMSNode * newNode = new POEMSNode; //make a new node +// forDeletion.Append(newNode); + newNode->idNumber = links[i][0]; //set its ID to the value + newNode->visited = false; //set it to be unvisited + nodes.Insert(links[i][0], links[i][0], (void *) newNode); //and add it to the tree storage structure + } + if(!nodes.Find(links[i][1])) //repeat process for the other half of each link + { + POEMSNode * newNode = new POEMSNode; +// forDeletion.Append(newNode); + newNode->idNumber = links[i][1]; + newNode->visited = false; + nodes.Insert(links[i][1], links[i][1], (void *) newNode); + } + POEMSNode * firstNode = (POEMSNode *)nodes.Find(links[i][0]); //now that we are sure both nodes exist, + POEMSNode * secondNode = (POEMSNode *)nodes.Find(links[i][1]); //we can get both of them out of the tree + firstNode->links.Append(secondNode); //and add the link from the first to the second... + false_var = new bool; + *false_var = false; //make a new false boolean to note that the link between these two + firstNode->taken.Append(false_var); //has not already been taken, and append it to the taken list + secondNode->links.Append(firstNode); //repeat process for link from second node to first + false_var = new bool; + *false_var = false; + secondNode->taken.Append(false_var); + } + + TreeNode * temp = nodes.GetRoot(); //get the root node of the node storage tree + POEMSNode * currentNode; + do + { + currentNode = findSingleLink(temp); //find the start of the next available chain + if(currentNode != NULL) + { + headsOfSystems.Append(AddNewChain(currentNode)); //and add it to the headsOfSystems list of chains + } + } + while(currentNode != NULL); //repeat this until all chains have been added +} + +POEMSChain * SystemProcessor::AddNewChain(POEMSNode * currentNode){ + if(currentNode == NULL) //Termination condition; if the currentNode is null, then return null + { + return NULL; + } + int * tmp; + POEMSNode * nextNode = NULL; //nextNode stores the proposed next node to add to the chain. this will be checked to make sure no backtracking is occuring before being assigned as the current node. + POEMSChain * newChain = new POEMSChain; //make a new POEMSChain object. This will be the object returned + + if(currentNode->links.GetNumElements() == 0) //if we have no links from this node, then the whole chain is only one node. Add this node to the chain and return it; mark node as visited for future reference + { + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); + return newChain; + } + while(currentNode->links.GetNumElements() <= 2) //we go until we get to a node that branches, or both branches have already been taken both branches can already be taken if a loop with no spurs is found in the input data + { + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); //append the current node to the chain & mark as visited + //cout << "Appending node " << currentNode->idNumber << " to chain" << endl; + nextNode = currentNode->links.GetHeadElement()->value; //the next node is the first or second value stored in the links array + //of the current node. We get the first value... + if(!setLinkVisited(currentNode, nextNode)) //...and see if it points back to where we came from. If it does... + { //either way, we set this link as visited + if(currentNode->links.GetNumElements() == 1) //if it does, then if that is the only link to this node, we're done with the chain, so append the chain to the list and return the newly created chain + { +// headsOfSystems.Append(newChain); + return newChain; + } + nextNode = currentNode->links.GetHeadElement()->next->value;//follow the other link if there is one, so we go down the chain + if(!setLinkVisited(currentNode, nextNode)) //mark link as followed, so we know not to backtrack + { + // headsOfSystems.Append(newChain); + return newChain; //This condition, where no branches have occurred but both links have already + //been taken can only occur in a loop with no spurs; add this loop to the + //system (currently added as a chain for consistency), and return. + } + } + currentNode = nextNode; //set the current node to be the next node in the chain + } + currentNode->visited = true; + tmp = new int; + *tmp = currentNode->idNumber; + newChain->listOfNodes.Append(tmp); //append the last node before branch (node shared jointly with branch chains) + //re-mark as visited, just to make sure + ListElement<POEMSNode> * tempNode = currentNode->links.GetHeadElement(); //go through all of the links, one at a time that branch + POEMSChain * tempChain = NULL; //temporary variable to hold data + while(tempNode != NULL) //when we have followed all links, stop + { + if(setLinkVisited(tempNode->value, currentNode)) //dont backtrack, or create closed loops + { + tempChain = AddNewChain(tempNode->value); //Add a new chain created out of the next node down that link + tempChain->parentChain = newChain; //set the parent to be this chain + newChain->childChains.Append(tempChain); //append the chain to this chain's list of child chains + } + tempNode = tempNode->next; //go to process the next chain + } + //headsOfSystems.Append(newChain); //append this chain to the system list + return newChain; +} + +POEMSNode * SystemProcessor::findSingleLink(TreeNode * aNode) +//This function takes the root of a search tree containing POEMSNodes and returns a POEMSNode corresponding to the start of a chain in the +//system. It finds a node that has not been visited before, and only has one link; this node will be used as the head of the chain. +{ + if(aNode == NULL) + { + return NULL; + } + POEMSNode * returnVal = (POEMSNode *)aNode->GetAuxData(); //get the poemsnode data out of the treenode + POEMSNode * detectLoneLoops = NULL; //is used to handle a loop that has no protruding chains + if(returnVal->visited == false) + { + detectLoneLoops = returnVal; //if we find any node that has not been visited yet, save it + } + if(returnVal->links.GetNumElements() == 1 && returnVal->visited == false) //see if it has one element and hasnt been visited already + { + return returnVal; //return the node is it meets this criteria + } + returnVal = findSingleLink(aNode->Left()); //otherwise, check the left subtree + if(returnVal == NULL) //and if we find nothing... + { + returnVal = findSingleLink(aNode->Right()); //check the right subtree + } + if(returnVal == NULL) //if we could not find any chains + { + returnVal = detectLoneLoops; //see if we found any nodes at all that havent been processed + } + return returnVal; //return what we find (will be NULL if no new chains are + //found) +} + +bool SystemProcessor::setLinkVisited(POEMSNode * firstNode, POEMSNode * secondNode) +//setLinkVisited sets the links between these two nodes as visited. If they are already visited, it returns false. Otherwise, it sets +//them as visited and returns true. This function is used to see whether a certain path has been taken already in the graph structure. +//If it has been, then we need to know so we dont follow it again; this prevents infinite recursion when there is a loop, and prevents +//backtracking up a chain that has already been made. The list of booleans denoting if a link has been visited is called 'taken' and is +//part of the POEMSNode struct. The list is the same size as the list of pointers to other nodes, and stores the boolean visited/unvisited +//value for that particular link. Because each link is represented twice, (once at each node in the link), both of the boolean values need +//to be set in the event that the link has to be set as visited. +{ + //cout << "Checking link between nodes " << firstNode->idNumber << " and " << secondNode->idNumber << "... "; + ListElement<POEMSNode> * tmp = firstNode->links.GetHeadElement(); //get the head element of the list of pointers for node 1 + ListElement<bool> * tmp2 = firstNode->taken.GetHeadElement(); //get the head element of the list of bool isVisited flags for node 1 + while(tmp->value != NULL || tmp2->value != NULL) //go through untill we reach the end of the lists + { + if(tmp->value == secondNode) //if we find the link to the other node + { + if(*(tmp2->value) == true) //if the link has already been visited + { + //cout << "visited already" << endl; + return false; //return false to indicate that the link has been visited before this attempt + } + else //otherwise, visit it + { + *tmp2->value = true; + } + break; + } + tmp = tmp->next; //go check next link + tmp2 = tmp2->next; + } + + tmp = secondNode->links.GetHeadElement(); //now, if the link was unvisited, we need to go set the other node's list such that + //it also knows this link is being visited + tmp2 = secondNode->taken.GetHeadElement(); + while(tmp->value != NULL || tmp2->value != NULL) //go through the list + { + if(tmp->value == firstNode) //if we find the link + { + if(*(tmp2->value) == true) //and it has already been visited, then signal an error; this shouldnt ever happen + { + cout << "Error in parsing structure! Should never reach this condition! \n" << + "Record of visited links out of synch between two adjacent nodes.\n"; + return false; + } + else + { + *tmp2->value = true; //set the appropriate value to true to indicate this link has been visited + } + break; + } + tmp = tmp->next; + tmp2 = tmp2->next; + } + //cout << "not visited" << endl; + return true; //return true to indicate that this is the first time the link has been visited +} + +List<POEMSChain> * SystemProcessor::getSystemData(void) //Gets the list of POEMSChains that comprise the system. Might eventually only + //return chains linked to the reference plane, but currently returns every chain + //in the system. +{ + return &headsOfSystems; +} + +int SystemProcessor::getNumberOfHeadChains(void) //This function isnt implemented yet, and might be taken out entirely; this was a holdover + //from when I intended to return an array of chain pointers, rather than a list of chains + //It will probably be deleted once I finish figuring out exactly what needs to be returned +{ + return 0; +} +#endif diff --git a/lib/atc/Thermostat.cpp b/lib/atc/Thermostat.cpp index 110d633ac..41312bd85 100644 --- a/lib/atc/Thermostat.cpp +++ b/lib/atc/Thermostat.cpp @@ -1,2381 +1,2621 @@ #include "Thermostat.h" #include "ATC_Coupling.h" #include "ATC_Error.h" #include "PrescribedDataManager.h" #include "ThermalTimeIntegrator.h" #include "TransferOperator.h" using namespace std; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class Thermostat //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- Thermostat::Thermostat(ATC_Coupling * atc, const string & regulatorPrefix) : AtomicRegulator(atc,regulatorPrefix), lambdaMaxIterations_(myLambdaMaxIterations) { // nothing to do } //-------------------------------------------------------- // modify: // parses and adjusts thermostat state based on // user input, in the style of LAMMPS user input //-------------------------------------------------------- bool Thermostat::modify(int narg, char **arg) { bool foundMatch = false; int argIndex = 0; if (strcmp(arg[argIndex],"thermal")==0) { argIndex++; // thermostat type /*! \page man_control_thermal fix_modify AtC control thermal \section syntax fix_modify AtC control thermal <control_type> <optional_args> - control_type (string) = none | rescale | hoover | flux\n fix_modify AtC control thermal rescale <frequency> \n - frequency (int) = time step frequency for applying velocity rescaling \n fix_modify AtC control thermal hoover \n fix_modify AtC control thermal flux <boundary_integration_type(optional)> <face_set_id(optional)>\n - boundary_integration_type (string) = faceset | interpolate\n - face_set_id (string), optional = id of boundary face set, if not specified (or not possible when the atomic domain does not line up with mesh boundaries) defaults to an atomic-quadrature approximate evaulation, does not work with interpolate\n \section examples <TT> fix_modify AtC control thermal none </TT> \n <TT> fix_modify AtC control thermal rescale 10 </TT> \n <TT> fix_modify AtC control thermal hoover </TT> \n <TT> fix_modify AtC control thermal flux </TT> \n <TT> fix_modify AtC control thermal flux faceset bndy_faces </TT> \n \section description Sets the energy exchange mechansim from the finite elements to the atoms, managed through a control algorithm. Rescale computes a scale factor for each atom to match the finite element temperature. Hoover is a Gaussian least-constraint isokinetic thermostat enforces that the nodal restricted atomic temperature matches the finite element temperature. Flux is a similar mode, but rather adds energy to the atoms based on conservation of energy. Hoover and flux allows the prescription of sources or fixed temperatures on the atoms. \section restrictions only for be used with specific transfers : thermal (rescale, hoover, flux), two_temperature (flux) \n rescale not valid with time filtering activated \section related \section default none\n rescale frequency is 1\n flux boundary_integration_type is interpolate */ if (strcmp(arg[argIndex],"none")==0) { // restore defaults regulatorTarget_ = NONE; couplingMode_ = UNCOUPLED; howOften_ = 1; boundaryIntegrationType_ = NO_QUADRATURE; foundMatch = true; } else if (strcmp(arg[argIndex],"rescale")==0) { argIndex++; howOften_ = atoi(arg[argIndex]); if (howOften_ < 1) { throw ATC_Error("Bad rescaling thermostat frequency"); } else { regulatorTarget_ = FIELD; couplingMode_ = UNCOUPLED; boundaryIntegrationType_ = NO_QUADRATURE; foundMatch = true; } } else if (strcmp(arg[argIndex],"hoover")==0) { regulatorTarget_ = DYNAMICS; couplingMode_ = FIXED; howOften_ = 1; boundaryIntegrationType_ = NO_QUADRATURE; foundMatch = true; } else if (strcmp(arg[argIndex],"flux")==0) { regulatorTarget_ = DYNAMICS; couplingMode_ = FLUX; howOften_ = 1; argIndex++; boundaryIntegrationType_ = atc_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_); foundMatch = true; } // set parameters for numerical matrix solutions unique to this thermostat /*! \page man_control_thermal_correction_max_iterations fix_modify AtC control thermal correction_max_iterations \section syntax fix_modify AtC control thermal correction_max_iterations <max_iterations> - max_iterations (int) = maximum number of iterations that will be used by iterative matrix solvers\n \section examples <TT> fix_modify AtC control thermal correction_max_iterations 10 </TT> \n \section description Sets the maximum number of iterations to compute the 2nd order in time correction term for lambda with the fractional step method. The method uses the same tolerance as the controller's matrix solver. \section restrictions only for use with thermal physics using the fractional step method. \section related \section default correction_max_iterations is 20 */ else if (strcmp(arg[argIndex],"correction_max_iterations")==0) { argIndex++; lambdaMaxIterations_ = atoi(arg[argIndex]); if (lambdaMaxIterations_ < 1) { throw ATC_Error("Bad correction maximum iteration count"); } foundMatch = true; } } if (!foundMatch) foundMatch = AtomicRegulator::modify(narg,arg); if (foundMatch) needReset_ = true; return foundMatch; } //-------------------------------------------------------- // reset_lambda_contribution: // resets the thermostat generated power to a // prescribed value //-------------------------------------------------------- void Thermostat::reset_lambda_contribution(const DENS_MAT & target) { DENS_MAN * lambdaPowerFiltered = regulator_data("LambdaPowerFiltered",1); *lambdaPowerFiltered = target; } //-------------------------------------------------------- // construct_methods: // instantiations desired regulator method(s) // dependence, but in general there is also a // time integrator dependence. In general the // precedence order is: // time filter -> time integrator -> thermostat // In the future this may need to be added if // different types of time integrators can be // specified. //-------------------------------------------------------- void Thermostat::construct_methods() { // get data associated with stages 1 & 2 of ATC_Method::initialize AtomicRegulator::construct_methods(); if (atc_->reset_methods()) { // eliminate existing methods delete_method(); // update time filter TimeIntegrator::TimeIntegrationType myIntegrationType = (atc_->time_integrator(TEMPERATURE))->time_integration_type(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (timeFilterManager->need_reset() ) { - if (myIntegrationType == TimeIntegrator::GEAR) - timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); - else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) - timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + if (myIntegrationType == TimeIntegrator::GEAR) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); + } + else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } } if (timeFilterManager->filter_dynamics()) { switch (regulatorTarget_) { case NONE: { regulatorMethod_ = new RegulatorMethod(this); break; } case FIELD: { // error check, rescale and filtering not supported together throw ATC_Error("Cannot use rescaling thermostat with time filtering"); break; } case DYNAMICS: { switch (couplingMode_) { case FIXED: { if (use_lumped_lambda_solve()) { throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); } if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (md_flux_nodes(TEMPERATURE)) { if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fluxes but no fixed or coupled nodes - regulatorMethod_ = new ThermostatFluxFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFluxFiltered(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixedFiltered(this); + regulatorMethod_ = new ThermostatFluxFixedFiltered(this,lambdaMaxIterations_); } } else { // there are only fixed nodes - regulatorMethod_ = new ThermostatFixedFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFixedFiltered(this,lambdaMaxIterations_); } } else { regulatorMethod_ = new ThermostatHooverVerletFiltered(this); } break; } case FLUX: { if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (use_lumped_lambda_solve()) { throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); } if (md_fixed_nodes(TEMPERATURE)) { if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fixed nodes but no fluxes - regulatorMethod_ = new ThermostatFixedFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFixedFiltered(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixedFiltered(this); + regulatorMethod_ = new ThermostatFluxFixedFiltered(this,lambdaMaxIterations_); } } else { // there are only flux nodes - regulatorMethod_ = new ThermostatFluxFiltered(this); + regulatorMethod_ = new ThermostatIntegratorFluxFiltered(this,lambdaMaxIterations_); } } else { if (use_localized_lambda()) { if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && atc_->boundary_integration_type() != NO_QUADRATURE) { throw ATC_Error("Cannot use flux coupling with localized lambda"); } } regulatorMethod_ = new ThermostatPowerVerletFiltered(this); } break; } default: throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); } break; } default: throw ATC_Error("Unknown thermostat type in Thermostat::initialize"); } } else { switch (regulatorTarget_) { case NONE: { regulatorMethod_ = new RegulatorMethod(this); break; } case FIELD: { if (atc_->temperature_def()==KINETIC) regulatorMethod_ = new ThermostatRescale(this); else if (atc_->temperature_def()==TOTAL) regulatorMethod_ = new ThermostatRescaleMixedKePe(this); else throw ATC_Error("Unknown temperature definition"); break; } case DYNAMICS: { switch (couplingMode_) { case FIXED: { if (use_lumped_lambda_solve()) { throw ATC_Error("Thermostat:construct_methods - lumped lambda solve cannot be used with Hoover thermostats"); } if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (md_flux_nodes(TEMPERATURE)) { if (!md_fixed_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fluxes but no fixed or coupled nodes - regulatorMethod_ = new ThermostatFlux(this); + regulatorMethod_ = new ThermostatIntegratorFlux(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixed(this); + regulatorMethod_ = new ThermostatFluxFixed(this,lambdaMaxIterations_); } } else { // there are only fixed nodes - regulatorMethod_ = new ThermostatFixed(this); + regulatorMethod_ = new ThermostatIntegratorFixed(this,lambdaMaxIterations_); } } else { regulatorMethod_ = new ThermostatHooverVerlet(this); } break; } case FLUX: { if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) { if (use_lumped_lambda_solve()) { throw ATC_Error("Thermostat:construct_methods - lumped lambda solve has been depricated for fractional step thermostats"); } if (md_fixed_nodes(TEMPERATURE)) { if (!md_flux_nodes(TEMPERATURE) && (boundaryIntegrationType_ == NO_QUADRATURE)) { // there are fixed nodes but no fluxes - regulatorMethod_ = new ThermostatFixed(this); + regulatorMethod_ = new ThermostatIntegratorFixed(this,lambdaMaxIterations_); } else { // there are both fixed and flux nodes - regulatorMethod_ = new ThermostatFluxFixed(this); + regulatorMethod_ = new ThermostatFluxFixed(this,lambdaMaxIterations_); } } else { // there are only flux nodes - regulatorMethod_ = new ThermostatFlux(this); + regulatorMethod_ = new ThermostatIntegratorFlux(this,lambdaMaxIterations_); } } else { if (use_localized_lambda()) { if (!((atc_->prescribed_data_manager())->no_fluxes(TEMPERATURE)) && atc_->boundary_integration_type() != NO_QUADRATURE) { throw ATC_Error("Cannot use flux coupling with localized lambda"); } } regulatorMethod_ = new ThermostatPowerVerlet(this); } break; } default: throw ATC_Error("Unknown coupling mode in Thermostat::initialize"); } break; } default: throw ATC_Error("Unknown thermostat target in Thermostat::initialize"); } } AtomicRegulator::reset_method(); } else { set_all_data_to_used(); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatShapeFunction //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatShapeFunction::ThermostatShapeFunction(Thermostat * thermostat, + ThermostatShapeFunction::ThermostatShapeFunction(AtomicRegulator * thermostat, const string & regulatorPrefix) : RegulatorShapeFunction(thermostat,regulatorPrefix), - thermostat_(thermostat), mdMassMatrix_(atc_->set_mass_mat_md(TEMPERATURE)), atomVelocities_(NULL) { fieldMask_(TEMPERATURE,FLUX) = true; - lambda_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatShapeFunction::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); RegulatorShapeFunction::construct_transfers(); // get atom velocity data from manager atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); // construct lambda evaluated at atom locations atomLambdas_ = new FtaShapeFunctionProlongation(atc_, lambda_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); } //--------------------------------------------------------- // set_weights: // set the diagonal weighting matrix to be the atomic // temperatures //--------------------------------------------------------- void ThermostatShapeFunction::set_weights() { if (this->use_local_shape_functions()) { VelocitySquaredMapped * myWeights = new VelocitySquaredMapped(atc_,lambdaAtomMap_); weights_ = myWeights; (atc_->interscale_manager()).add_per_atom_quantity(myWeights, regulatorPrefix_+"AtomVelocitySquaredMapped"); } else { VelocitySquared * myWeights = new VelocitySquared(atc_); weights_ = myWeights; (atc_->interscale_manager()).add_per_atom_quantity(myWeights, regulatorPrefix_+"AtomVelocitySquared"); } } - - + //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatRescale //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatRescale::ThermostatRescale(Thermostat * thermostat) : + ThermostatRescale::ThermostatRescale(AtomicRegulator * thermostat) : ThermostatShapeFunction(thermostat), nodalTemperature_(atc_->field(TEMPERATURE)), atomVelocityRescalings_(NULL) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatRescale::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // set up data for linear solver shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); (atc_->interscale_manager()).add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixEnergy"); linearSolverType_ = AtomicRegulator::CG_SOLVE; // base class transfers ThermostatShapeFunction::construct_transfers(); // velocity rescaling factor atomVelocityRescalings_ = new AtomicVelocityRescaleFactor(atc_,atomLambdas_); interscaleManager.add_per_atom_quantity(atomVelocityRescalings_, regulatorPrefix_+"AtomVelocityRescaling"); } + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatRescale::set_weights() + { + weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicEnergyForTemperature"); + } + //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat in the post corrector phase //-------------------------------------------------------- void ThermostatRescale::apply_post_corrector(double dt) + { + compute_thermostat(dt); + + // application of rescaling lambda due + apply_to_atoms(atomVelocities_); + } + + //-------------------------------------------------------- + // compute_thermostat + // manages the solution of the + // thermostat equations and variables + //-------------------------------------------------------- + void ThermostatRescale::compute_thermostat(double dt) { // compute right-hand side - _rhs_ = mdMassMatrix_.quantity()*nodalTemperature_.quantity(); - correct_rhs(_rhs_); // correct right-hand side for complex temperature definitions, e.g., when the potential energy is included + this->set_rhs(_rhs_); // solve equations solve_for_lambda(_rhs_,lambda_->set_quantity()); + } - // application of rescaling lambda due - apply_to_atoms(atomVelocities_); + //-------------------------------------------------------- + // set_rhs: + // constructs the RHS vector with the target + // temperature + //-------------------------------------------------------- + void ThermostatRescale::set_rhs(DENS_MAT & rhs) + { + rhs = mdMassMatrix_.quantity()*nodalTemperature_.quantity(); } //-------------------------------------------------------- // apply_lambda_to_atoms: // applies the velocity rescale with an existing lambda // note oldAtomicQuantity and dt are not used //-------------------------------------------------------- void ThermostatRescale::apply_to_atoms(PerAtomQuantity<double> * atomVelocities) { *atomVelocities *= atomVelocityRescalings_->quantity(); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void ThermostatRescale::output(OUTPUT_LIST & outputData) { DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData["Lambda"] = λ + outputData["LambdaEnergy"] = λ } } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatRescaleMixedKePe //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatRescaleMixedKePe::ThermostatRescaleMixedKePe(Thermostat * thermostat) : + ThermostatRescaleMixedKePe::ThermostatRescaleMixedKePe(AtomicRegulator * thermostat) : ThermostatRescale(thermostat), nodalAtomicFluctuatingPotentialEnergy_(NULL) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatRescaleMixedKePe::construct_transfers() { ThermostatRescale::construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); // get fluctuating PE at nodes nodalAtomicFluctuatingPotentialEnergy_ = interscaleManager.dense_matrix("NodalAtomicFluctuatingPotentialEnergy"); } + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatRescaleMixedKePe::set_weights() + { + weights_ = (atc_->interscale_manager()).per_atom_quantity("AtomicTwiceKineticEnergy"); + } + //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void ThermostatRescaleMixedKePe::initialize() { ThermostatRescale::initialize(); InterscaleManager & interscaleManager(atc_->interscale_manager()); // multipliers for KE and PE AtomicEnergyForTemperature * atomEnergyForTemperature = static_cast<AtomicEnergyForTemperature * >(interscaleManager.per_atom_quantity("AtomicEnergyForTemperature")); keMultiplier_ = atomEnergyForTemperature->kinetic_energy_multiplier(); peMultiplier_ = 2. - keMultiplier_; keMultiplier_ /= 2.; // account for use of 2 X KE in matrix equation } //-------------------------------------------------------- - // correct_rhs: + // set_rhs: // accounts for potential energy contribution to // definition of atomic temperature //-------------------------------------------------------- - void ThermostatRescaleMixedKePe::correct_rhs(DENS_MAT & rhs) + void ThermostatRescaleMixedKePe::set_rhs(DENS_MAT & rhs) { + ThermostatRescale::set_rhs(rhs); rhs -= peMultiplier_*(nodalAtomicFluctuatingPotentialEnergy_->quantity()); rhs /= keMultiplier_; } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatFsSolver + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatFsSolver::ThermostatFsSolver(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + RegulatorShapeFunction(thermostat,regulatorPrefix), + lambdaMaxIterations_(lambdaMaxIterations), + rhsLambdaSquared_(NULL), + dtFactor_(1.) + { + fieldMask_(TEMPERATURE,FLUX) = true; + lambda_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); // data associated with stage 3 in ATC_Method::initialize + } + + //-------------------------------------------------------- + // initialize + // creates mapping from all nodes to those to which + // the thermostat applies + //-------------------------------------------------------- + void ThermostatFsSolver::initialize() + { + RegulatorShapeFunction::initialize(); + + rhsMap_.resize(overlapToNodeMap_->nRows(),1); + DENS_MAT rhsMapGlobal(nNodes_,1); + const set<int> & applicationNodes(applicationNodes_->quantity()); + for (int i = 0; i < nNodes_; i++) { + if (applicationNodes.find(i) != applicationNodes.end()) { + rhsMapGlobal(i,0) = 1.; + } + else { + rhsMapGlobal(i,0) = 0.; + } + } + map_unique_to_overlap(rhsMapGlobal,rhsMap_); + } + + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatFsSolver::set_weights() + { + if (this->use_local_shape_functions()) { + VelocitySquaredMapped * myWeights = new VelocitySquaredMapped(atc_,lambdaAtomMap_); + weights_ = myWeights; + (atc_->interscale_manager()).add_per_atom_quantity(myWeights, + regulatorPrefix_+"AtomVelocitySquaredMapped"); + } + else { + VelocitySquared * myWeights = new VelocitySquared(atc_); + weights_ = myWeights; + (atc_->interscale_manager()).add_per_atom_quantity(myWeights, + regulatorPrefix_+"AtomVelocitySquared"); + } + } + + //-------------------------------------------------------- + // compute_lambda: + // solves linear system for lambda, if the + // bool is true it iterators to a non-linear solution + //-------------------------------------------------------- + void ThermostatFsSolver::compute_lambda(const DENS_MAT & rhs, + bool iterateSolution) + { + // solve linear system for lambda guess + DENS_MAT & lambda(lambda_->set_quantity()); + solve_for_lambda(rhs,lambda); + + // iterate to solution + if (iterateSolution) { + iterate_lambda(rhs); + } + } + + //-------------------------------------------------------- + // iterate_lambda: + // iteratively solves the equations for lambda + // for the higher order dt corrections, assuming + // an initial guess for lambda + //-------------------------------------------------------- + void ThermostatFsSolver::iterate_lambda(const MATRIX & rhs) + { + int nNodeOverlap = overlapToNodeMap_->nRows(); + DENS_VEC _lambdaOverlap_(nNodeOverlap); + DENS_MAT & lambda(lambda_->set_quantity()); + map_unique_to_overlap(lambda,_lambdaOverlap_); + double factor = 0.5*dtFactor_*atc_->dt(); + + _lambdaOld_.resize(nNodes_,1); + _rhsOverlap_.resize(nNodeOverlap,1); + map_unique_to_overlap(rhs,_rhsOverlap_); + _rhsTotal_.resize(nNodeOverlap); + + // solve assuming we get initial guess for lambda + double error(-1.); + for (int i = 0; i < lambdaMaxIterations_; ++i) { + _lambdaOld_ = lambda; + + // solve the system with the new rhs + const DENS_MAT & rhsLambdaSquared(rhsLambdaSquared_->quantity()); + for (int i = 0; i < nNodeOverlap; i++) { + if (rhsMap_(i,0) == 1.) { + _rhsTotal_(i) = _rhsOverlap_(i,0) + factor*rhsLambdaSquared(i,0); + } + else { + _rhsTotal_(i) = 0.; + } + } + matrixSolver_->execute(_rhsTotal_,_lambdaOverlap_); + + // check convergence + map_overlap_to_unique(_lambdaOverlap_,lambda); + lambda_->force_reset(); + DENS_MAT difference = lambda-_lambdaOld_; + error = difference.col_norm()/_lambdaOld_.col_norm(); + if (error < tolerance_) + break; + } + + if (error >= tolerance_) { + stringstream message; + message << "WARNING: Iterative solve for lambda failed to converge after " << lambdaMaxIterations_ << " iterations, final tolerance was " << error << "\n"; + ATC::LammpsInterface::instance()->print_msg(message.str()); + } + } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatGlcFs //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatGlcFs::ThermostatGlcFs(Thermostat * thermostat, + ThermostatGlcFs::ThermostatGlcFs(AtomicRegulator * thermostat, + int lambdaMaxIterations, const string & regulatorPrefix) : - ThermostatShapeFunction(thermostat,regulatorPrefix), + RegulatorMethod(thermostat,regulatorPrefix), + lambdaSolver_(NULL), + mdMassMatrix_(atc_->set_mass_mat_md(TEMPERATURE)), + atomVelocities_(NULL), temperature_(atc_->field(TEMPERATURE)), timeFilter_(atomicRegulator_->time_filter()), nodalAtomicLambdaPower_(NULL), lambdaPowerFiltered_(NULL), + atomLambdas_(NULL), atomThermostatForces_(NULL), atomMasses_(NULL), - rhsLambdaSquared_(NULL), isFirstTimestep_(true), - lambdaMaxIterations_(thermostat->lambda_max_iterations()), nodalAtomicEnergy_(NULL), atomPredictedVelocities_(NULL), nodalAtomicPredictedEnergy_(NULL), - firstHalfAtomForces_(NULL), - dtFactor_(0.) + firstHalfAtomForces_(NULL) { // construct/obtain data corresponding to stage 3 of ATC_Method::initialize nodalAtomicLambdaPower_ = thermostat->regulator_data(regulatorPrefix_+"NodalAtomicLambdaPower",1); - lambdaPowerFiltered_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + lambdaPowerFiltered_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatGlcFs::construct_transfers() { - ThermostatShapeFunction::construct_transfers(); + lambdaSolver_->construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); + // get atom velocity data from manager + atomVelocities_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_VELOCITY); + + // construct lambda evaluated at atom locations + atomLambdas_ = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"); + if (!atomLambdas_) { + DENS_MAN * lambda = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1); + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } + // get data from manager atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), nodalAtomicEnergy_ = interscaleManager.dense_matrix("NodalAtomicEnergy"); // thermostat forces based on lambda and the atomic velocities atomThermostatForces_ = new AtomicThermostatForce(atc_,atomLambdas_); interscaleManager.add_per_atom_quantity(atomThermostatForces_, regulatorPrefix_+"AtomThermostatForce"); // predicted temperature quantities: atom velocities, atom energies, and restricted atom energies - AtcAtomQuantity<double> * atomPredictedVelocities = new AtcAtomQuantity<double>(atc_,nsd_); - interscaleManager.add_per_atom_quantity(atomPredictedVelocities, + atomPredictedVelocities_ = new AtcAtomQuantity<double>(atc_,atc_->nsd()); + // MAKE THINGS WORK WITH ONLY ONE PREDICTED VELOCITY, CHECK IT EXISTS + interscaleManager.add_per_atom_quantity(atomPredictedVelocities_, regulatorPrefix_+"AtomicPredictedVelocities"); - atomPredictedVelocities_ = atomPredictedVelocities; AtomicEnergyForTemperature * atomPredictedEnergyForTemperature = new TwiceKineticEnergy(atc_, - atomPredictedVelocities); + atomPredictedVelocities_); interscaleManager.add_per_atom_quantity(atomPredictedEnergyForTemperature, regulatorPrefix_+"AtomicPredictedTwiceKineticEnergy"); nodalAtomicPredictedEnergy_ = new AtfShapeFunctionRestriction(atc_, atomPredictedEnergyForTemperature, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicPredictedEnergy_, - regulatorPrefix_+"NodalAtomicPredictedEnergy"); + regulatorPrefix_+"NodalAtomicPredictedEnergy"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void ThermostatGlcFs::initialize() { - ThermostatShapeFunction::initialize(); - - // reset data to zero - deltaEnergy1_.reset(nNodes_,1); - deltaEnergy2_.reset(nNodes_,1); - _lambdaPowerOutput_.reset(nNodes_,1); + RegulatorMethod::initialize(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { // we should reset lambda and lambdaForce to zero in this case // implies an initial condition of 0 for the filtered nodal lambda power // initial conditions will always be needed when using time filtering // however, the fractional step scheme must assume the instantaneous // nodal lambda power is 0 initially because all quantities are in delta form - *lambda_ = 0.; // ensures initial lambda force is zero + lambdaSolver_->initialize(); // ensures initial lambda force is zero + lambdaSolver_->set_lambda_to_value(0.); *nodalAtomicLambdaPower_ = 0.; // energy change due to thermostats *lambdaPowerFiltered_ = 0.; // filtered energy change due to thermostats } else { + lambdaSolver_->initialize(); // we can grab lambda power variables using time integrator and atc transfer in cases for equilibration } // sets up time filter for cases where variables temporally filtered if (timeFilterManager->need_reset()) { // the form of this integrator implies no time filters that require history data can be used timeFilter_->initialize(nodalAtomicLambdaPower_->quantity()); } atomThermostatForces_->quantity(); // initialize atomThermostatForces_->fix_quantity(); firstHalfAtomForces_ = atomThermostatForces_; // initialize - +#ifdef OBSOLETE compute_rhs_map(); +#endif } //-------------------------------------------------------- - // compute_rhs_map - // creates mapping from all nodes to those to which - // the thermostat applies + // reset_atom_materials: + // resets the localized atom to material map //-------------------------------------------------------- - void ThermostatGlcFs::compute_rhs_map() + void ThermostatGlcFs::reset_atom_materials(const Array<int> & elementToMaterialMap, + const MatrixDependencyManager<DenseMatrix, int> * atomElement) { - rhsMap_.resize(overlapToNodeMap_->nRows(),1); - DENS_MAT rhsMapGlobal(nNodes_,1); - const set<int> & applicationNodes(applicationNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (applicationNodes.find(i) != applicationNodes.end()) { - rhsMapGlobal(i,0) = 1.; - } - else { - rhsMapGlobal(i,0) = 0.; - } - } - map_unique_to_overlap(rhsMapGlobal,rhsMap_); + lambdaSolver_->reset_atom_materials(elementToMaterialMap, + atomElement); } //-------------------------------------------------------- // apply_to_atoms: // determines what if any contributions to the // atomic moition is needed for // consistency with the thermostat // and computes the instantaneous induced power //-------------------------------------------------------- void ThermostatGlcFs::apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, const DENS_MAN * nodalAtomicEnergy, const DENS_MAT & lambdaForce, DENS_MAT & nodalAtomicLambdaPower, double dt) { // compute initial contributions to lambda power nodalAtomicLambdaPower = nodalAtomicEnergy->quantity(); nodalAtomicLambdaPower *= -1.; // apply lambda force to atoms _velocityDelta_ = lambdaForce; _velocityDelta_ /= atomMasses_->quantity(); _velocityDelta_ *= dt; (*atomicVelocity) += _velocityDelta_; // finalize lambda power nodalAtomicLambdaPower += nodalAtomicEnergy->quantity(); } //-------------------------------------------------------- // full_prediction: // flag to perform a full prediction calcalation // for lambda rather than using the old value //-------------------------------------------------------- bool ThermostatGlcFs::full_prediction() { if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) && (atc_->atom_to_element_map_frequency() > 1) && (atc_->step() % atc_->atom_to_element_map_frequency() == 0 ))) { return true; } return false; } //-------------------------------------------------------- // apply_predictor: // apply the thermostat to the atoms in the first step // of the Verlet algorithm //-------------------------------------------------------- void ThermostatGlcFs::apply_pre_predictor(double dt) { DENS_MAT & myLambdaPowerFiltered(lambdaPowerFiltered_->set_quantity()); DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); // update filtered power timeFilter_->apply_pre_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); // equivalent update to measure power as change in energy due to thermostat // apply lambda force to atoms and compute instantaneous lambda power for first half of time step this->apply_to_atoms(atomVelocities_,nodalAtomicEnergy_, firstHalfAtomForces_->quantity(), myNodalAtomicLambdaPower,0.5*dt); // update nodal variables for first half of time step this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy1_,0.5*dt); // start update of filtered lambda power myNodalAtomicLambdaPower = 0.; // temporary power for first part of update timeFilter_->apply_post_step1(myLambdaPowerFiltered,myNodalAtomicLambdaPower,dt); } //-------------------------------------------------------- // apply_pre_corrector: // apply the thermostat to the atoms in the first part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- void ThermostatGlcFs::apply_pre_corrector(double dt) { (*atomPredictedVelocities_) = atomVelocities_->quantity(); // do full prediction if we just redid the shape functions if (full_prediction()) { this->compute_lambda(dt); atomThermostatForces_->unfix_quantity(); // allow update of atomic force applied by lambda } // apply lambda force to atoms and compute instantaneous lambda power to predict second half of time step DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); apply_to_atoms(atomPredictedVelocities_, nodalAtomicPredictedEnergy_, firstHalfAtomForces_->quantity(), myNodalAtomicLambdaPower,0.5*dt); if (full_prediction()) atomThermostatForces_->fix_quantity(); // update predicted nodal variables for second half of time step this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); - deltaEnergy1_ += deltaEnergy2_; + // following manipulations performed this way for efficiency + deltaEnergy1_ += deltaEnergy2_; atc_->apply_inverse_mass_matrix(deltaEnergy1_,TEMPERATURE); temperature_ += deltaEnergy1_; } //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- void ThermostatGlcFs::apply_post_corrector(double dt) { // remove predicted power effects DENS_MAT & myTemperature(temperature_.set_quantity()); atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); myTemperature -= deltaEnergy2_; // set up equation and update lambda this->compute_lambda(dt); // apply lambda force to atoms and compute instantaneous lambda power for second half of time step DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); - atomThermostatForces_->unfix_quantity(); // allow computation of force applied by lambda + // allow computation of force applied by lambda using current velocities + atomThermostatForces_->unfix_quantity(); + atomThermostatForces_->quantity(); + atomThermostatForces_->fix_quantity(); apply_to_atoms(atomVelocities_,nodalAtomicEnergy_, atomThermostatForces_->quantity(), myNodalAtomicLambdaPower,0.5*dt); - atomThermostatForces_->fix_quantity(); // finalize filtered lambda power by adding latest contribution timeFilter_->apply_post_step2(lambdaPowerFiltered_->set_quantity(), myNodalAtomicLambdaPower,dt); // update nodal variables for second half of time step this->add_to_energy(myNodalAtomicLambdaPower,deltaEnergy2_,0.5*dt); atc_->apply_inverse_mass_matrix(deltaEnergy2_,TEMPERATURE); myTemperature += deltaEnergy2_; isFirstTimestep_ = false; } //-------------------------------------------------------- // compute_lambda: // sets up and solves linear system for lambda, if the // bool is true it iterators to a non-linear solution //-------------------------------------------------------- void ThermostatGlcFs::compute_lambda(double dt, bool iterateSolution) { // set up rhs for lambda equation - rhs_.resize(nNodes_,1); this->set_thermostat_rhs(rhs_,0.5*dt); - + + // solve system + lambdaSolver_->compute_lambda(rhs_,iterateSolution); +#ifdef OBSOLETE // solve linear system for lambda guess - DENS_MAT & myLambda(lambda_->set_quantity()); - solve_for_lambda(rhs_,myLambda); + DENS_MAT & lambda(lambda_->set_quantity()); + solve_for_lambda(rhs_,lambda); // iterate to solution if (iterateSolution) { iterate_lambda(rhs_); } +#endif } //-------------------------------------------------------- - // iterate_lambda: - // iteratively solves the equations for lambda - // for the higher order dt corrections, assuming - // an initial guess for lambda + // compute_boundary_flux + // default computation of boundary flux based on + // finite //-------------------------------------------------------- - void ThermostatGlcFs::iterate_lambda(const MATRIX & rhs) + void ThermostatGlcFs::compute_boundary_flux(FIELDS & fields) { - int nNodeOverlap = overlapToNodeMap_->nRows(); - DENS_VEC _lambdaOverlap_(nNodeOverlap); - DENS_MAT & lambda(lambda_->set_quantity()); - map_unique_to_overlap(lambda,_lambdaOverlap_); - double factor = 0.5*dtFactor_*atc_->dt(); - - _lambdaOld_.resize(nNodes_,1); - _rhsOverlap_.resize(nNodeOverlap,1); - map_unique_to_overlap(rhs,_rhsOverlap_); - _rhsTotal_.resize(nNodeOverlap); - - // solve assuming we get initial guess for lambda - double error(-1.); - for (int i = 0; i < lambdaMaxIterations_; ++i) { - _lambdaOld_ = lambda; - - // solve the system with the new rhs - const DENS_MAT & rhsLambdaSquared(rhsLambdaSquared_->quantity()); - for (int i = 0; i < nNodeOverlap; i++) { - if (rhsMap_(i,0) == 1.) { - _rhsTotal_(i) = _rhsOverlap_(i,0) + factor*rhsLambdaSquared(i,0); - } - else { - _rhsTotal_(i) = 0.; - } - } - matrixSolver_->execute(_rhsTotal_,_lambdaOverlap_); - - // check convergence - map_overlap_to_unique(_lambdaOverlap_,lambda); - lambda_->force_reset(); - DENS_MAT difference = lambda-_lambdaOld_; - error = difference.col_norm()/_lambdaOld_.col_norm(); - if (error < tolerance_) - break; - } - if (error >= tolerance_) { - stringstream message; - message << " Iterative solve for lambda failed to converge after " << lambdaMaxIterations_ << " iterations, final tolerance was " << error << "\n"; - ATC::LammpsInterface::instance()->print_msg(message.str()); - } + lambdaSolver_->compute_boundary_flux(fields); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void ThermostatGlcFs::output(OUTPUT_LIST & outputData) { _lambdaPowerOutput_ = nodalAtomicLambdaPower_->quantity(); // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { - outputData[regulatorPrefix_+"Lambda"] = λ + outputData[regulatorPrefix_+"LambdaEnergy"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); } } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFlux + // Class ThermostatSolverFlux //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFlux::ThermostatFlux(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatGlcFs(thermostat,regulatorPrefix), - heatSource_(atc_->atomic_source(TEMPERATURE)) + ThermostatSolverFlux::ThermostatSolverFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatFsSolver(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- - void ThermostatFlux::construct_transfers() + void ThermostatSolverFlux::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // set up data for linear solver shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixEnergy"); if (elementMask_) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); } if (atomicRegulator_->use_localized_lambda()) { linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { linearSolverType_ = AtomicRegulator::CG_SOLVE; } // base class transfers - ThermostatGlcFs::construct_transfers(); + ThermostatFsSolver::construct_transfers(); // add transfers for computation of extra RHS term accounting of O(lambda^2) // lambda squared followed by fractional step RHS contribution - PerAtomQuantity<double> * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + atomLambdas_ = (interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + if (!atomLambdas_) { + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda_, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } LambdaSquared * lambdaSquared = new LambdaSquared(atc_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquaredMapped"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); interscaleManager.add_dense_matrix(rhsLambdaSquared_, regulatorPrefix_+"RhsLambdaSquared"); } - //-------------------------------------------------------- - // initialize - // initializes all method data - //-------------------------------------------------------- - void ThermostatFlux::initialize() - { - ThermostatGlcFs::initialize(); - - // timestep factor - dtFactor_ = 1.; - } - //-------------------------------------------------------- // construct_regulated_nodes: // constructs the set of nodes being regulated //-------------------------------------------------------- - void ThermostatFlux::construct_regulated_nodes() + void ThermostatSolverFlux::construct_regulated_nodes() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // matrix requires all entries even if localized for correct lumping - regulatedNodes_ = new RegulatedNodes(atc_); - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"ThermostatRegulatedNodes"); - + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + if (!regulatedNodes_) { + regulatedNodes_ = new RegulatedNodes(atc_); + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"ThermostatRegulatedNodes"); + } + // if localized monitor nodes with applied fluxes if (atomicRegulator_->use_localized_lambda()) { if ((atomicRegulator_->coupling_mode() == Thermostat::FLUX) && (atomicRegulator_->boundary_integration_type() != NO_QUADRATURE)) { // include boundary nodes applicationNodes_ = new FluxBoundaryNodes(atc_); boundaryNodes_ = new BoundaryNodes(atc_); interscaleManager.add_set_int(boundaryNodes_, regulatorPrefix_+"ThermostatBoundaryNodes"); } else { // fluxed nodes only applicationNodes_ = new FluxNodes(atc_); } interscaleManager.add_set_int(applicationNodes_, regulatorPrefix_+"ThermostatApplicationNodes"); } else { applicationNodes_ = regulatedNodes_; } // special set of boundary elements for boundary flux quadrature if ((atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION) && (atomicRegulator_->use_localized_lambda())) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } } } + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatIntegratorFlux + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatIntegratorFlux::ThermostatIntegratorFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatGlcFs(thermostat,lambdaMaxIterations,regulatorPrefix), + heatSource_(atc_->atomic_source(TEMPERATURE)) + { + lambdaSolver_ = new ThermostatSolverFlux(thermostat, + lambdaMaxIterations, + regulatorPrefix); + } + //-------------------------------------------------------- // add_to_temperature // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFlux::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFlux::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.resize(nNodes_,1); const DENS_MAT & myBoundaryFlux(boundaryFlux_[TEMPERATURE].quantity()); for (int i = 0; i < nNodes_; i++) { deltaEnergy(i,0) = nodalLambdaPower(i,0) + dt*myBoundaryFlux(i,0); } } + //-------------------------------------------------------- + // initialize + // initializes all method data + //-------------------------------------------------------- + void ThermostatIntegratorFlux::initialize() + { + ThermostatGlcFs::initialize(); + + // timestep factor + lambdaSolver_->set_timestep_factor(1.); + } + //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFlux::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFlux::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // only tested with flux != 0 + ess bc = 0 // (a) for flux based : // form rhs : 2/3kB * W_I^-1 * \int N_I r dV // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I // fluxes are set in ATC transfer const DENS_MAT & heatSource(heatSource_.quantity()); - const set<int> & applicationNodes(applicationNodes_->quantity()); +#if true + const set<int> & applicationNodes((lambdaSolver_->application_nodes())->quantity()); + rhs.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (applicationNodes.find(i) != applicationNodes.end()) { rhs(i,0) = heatSource(i,0); } else { rhs(i,0) = 0.; } } +#else + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { + rhs(i,0) = heatSource(i,0); + } +#endif } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFixed + // Class ThermostatSolverFixed //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFixed::ThermostatFixed(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatGlcFs(thermostat,regulatorPrefix), - atomThermostatForcesPredVel_(NULL), - filterCoefficient_(1.) + ThermostatSolverFixed::ThermostatSolverFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatFsSolver(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- - void ThermostatFixed::construct_transfers() + void ThermostatSolverFixed::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // determine if map is needed and set up if so if (this->use_local_shape_functions()) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, lambdaAtomMap_, nodeToOverlapMap_); } else { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); } interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixEnergy"); linearSolverType_ = AtomicRegulator::CG_SOLVE; // base class transfers, e.g. weights - ThermostatGlcFs::construct_transfers(); + ThermostatFsSolver::construct_transfers(); // add transfers for computation of extra RHS term accounting of O(lambda^2) // lambda squared followed by fractional step RHS contribution - PerAtomQuantity<double> * lambdaAtom(interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy")); + atomLambdas_ = interscaleManager.per_atom_quantity(regulatorPrefix_+"AtomLambdaEnergy"); + if (!atomLambdas_) { + atomLambdas_ = new FtaShapeFunctionProlongation(atc_, + lambda_, + interscaleManager.per_atom_sparse_matrix("Interpolant")); + interscaleManager.add_per_atom_quantity(atomLambdas_,regulatorPrefix_+"AtomLambdaEnergy"); + } if (lambdaAtomMap_) { LambdaSquaredMapped * lambdaSquared = new LambdaSquaredMapped(atc_, lambdaAtomMap_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquared"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); } else { LambdaSquared * lambdaSquared = new LambdaSquared(atc_, - atomMasses_, + interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS), weights_, - lambdaAtom); + atomLambdas_); interscaleManager.add_per_atom_quantity(lambdaSquared, regulatorPrefix_+"LambdaSquaredMapped"); rhsLambdaSquared_ = new AtfShapeFunctionRestriction(atc_,lambdaSquared,shapeFunctionMatrix_); } interscaleManager.add_dense_matrix(rhsLambdaSquared_, - regulatorPrefix_+"RhsLambdaSquared"); + regulatorPrefix_+"RhsLambdaSquared"); + } + + //-------------------------------------------------------- + // construct_regulated_nodes: + // constructs the set of nodes being regulated + //-------------------------------------------------------- + void ThermostatSolverFixed::construct_regulated_nodes() + { + InterscaleManager & interscaleManager(atc_->interscale_manager()); + regulatedNodes_ = interscaleManager.set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + + if (!regulatedNodes_) { + if (!atomicRegulator_->use_localized_lambda()) { + regulatedNodes_ = new RegulatedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == AtomicRegulator::FLUX) { + regulatedNodes_ = new FixedNodes(atc_); + } + else if (atomicRegulator_->coupling_mode() == AtomicRegulator::FIXED) { + // include boundary nodes + regulatedNodes_ = new FixedBoundaryNodes(atc_); + } + else { + throw ATC_Error("ThermostatSolverFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); + } + + interscaleManager.add_set_int(regulatedNodes_, + regulatorPrefix_+"ThermostatRegulatedNodes"); + } + + applicationNodes_ = regulatedNodes_; + + // special set of boundary elements for defining regulated atoms + if (atomicRegulator_->use_localized_lambda()) { + elementMask_ = interscaleManager.dense_matrix_bool(regulatorPrefix_+"BoundaryElementMask"); + if (!elementMask_) { + elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); + interscaleManager.add_dense_matrix_bool(elementMask_, + regulatorPrefix_+"BoundaryElementMask"); + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatIntegratorFixed + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatIntegratorFixed::ThermostatIntegratorFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatGlcFs(thermostat,lambdaMaxIterations,regulatorPrefix), + atomThermostatForcesPredVel_(NULL), + filterCoefficient_(1.) + { + lambdaSolver_ = new ThermostatSolverFixed(thermostat, + lambdaMaxIterations, + regulatorPrefix); + } + + //-------------------------------------------------------- + // construct_regulated_nodes: + // constructs the set of nodes being regulated + //-------------------------------------------------------- + void ThermostatIntegratorFixed::construct_transfers() + { + ThermostatGlcFs::construct_transfers(); + + InterscaleManager & interscaleManager(atc_->interscale_manager()); // predicted forces for halving update atomThermostatForcesPredVel_ = new AtomicThermostatForce(atc_,atomLambdas_,atomPredictedVelocities_); interscaleManager.add_per_atom_quantity(atomThermostatForcesPredVel_, regulatorPrefix_+"AtomThermostatForcePredictedVelocity"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- - void ThermostatFixed::initialize() + void ThermostatIntegratorFixed::initialize() { ThermostatGlcFs::initialize(); InterscaleManager & interscaleManager(atc_->interscale_manager()); // set KE multiplier AtomicEnergyForTemperature * atomEnergyForTemperature = static_cast<AtomicEnergyForTemperature * >(interscaleManager.per_atom_quantity("AtomicEnergyForTemperature")); keMultiplier_ = atomEnergyForTemperature->kinetic_energy_multiplier(); // reset data to zero deltaFeEnergy_.reset(nNodes_,1); deltaNodalAtomicEnergy_.reset(nNodes_,1); // initialize filtered energy TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { nodalAtomicEnergyFiltered_ = nodalAtomicEnergy_->quantity(); } // timestep factor - dtFactor_ = 0.5; + lambdaSolver_->set_timestep_factor(0.5); } //-------------------------------------------------------- // halve_force: // flag to halve the lambda force for improved // accuracy //-------------------------------------------------------- - bool ThermostatFixed::halve_force() + bool ThermostatIntegratorFixed::halve_force() { if (isFirstTimestep_ || ((atc_->atom_to_element_map_type() == EULERIAN) && (atc_->atom_to_element_map_frequency() > 1) && (atc_->step() % atc_->atom_to_element_map_frequency() == 1))) { return true; } return false; } - //-------------------------------------------------------- - // construct_regulated_nodes: - // constructs the set of nodes being regulated - //-------------------------------------------------------- - void ThermostatFixed::construct_regulated_nodes() - { - InterscaleManager & interscaleManager(atc_->interscale_manager()); - - if (!atomicRegulator_->use_localized_lambda()) { - regulatedNodes_ = new RegulatedNodes(atc_); - } - else if (thermostat_->coupling_mode() == AtomicRegulator::FLUX) { - regulatedNodes_ = new FixedNodes(atc_); - } - else if (thermostat_->coupling_mode() == AtomicRegulator::FIXED) { - // include boundary nodes - regulatedNodes_ = new FixedBoundaryNodes(atc_); - } - else { - throw ATC_Error("ThermostatFixed::construct_regulated_nodes - couldn't determine set of regulated nodes"); - } - - interscaleManager.add_set_int(regulatedNodes_, - regulatorPrefix_+"RegulatedNodes"); - - applicationNodes_ = regulatedNodes_; - - // special set of boundary elements for defining regulated atoms - if (atomicRegulator_->use_localized_lambda()) { - elementMask_ = new ElementMaskNodeSet(atc_,applicationNodes_); - interscaleManager.add_dense_matrix_bool(elementMask_, - regulatorPrefix_+"BoundaryElementMask"); - } - } - //-------------------------------------------------------- // initialize_delta_nodal_atomic_energy: // initializes storage for the variable tracking // the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixed::initialize_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixed::initialize_delta_nodal_atomic_energy(double dt) { // initialize delta energy const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); initialNodalAtomicEnergy_ = myNodalAtomicEnergy; initialNodalAtomicEnergy_ *= -1.; // initially stored as negative for efficiency timeFilter_->apply_pre_step1(nodalAtomicEnergyFiltered_.set_quantity(), myNodalAtomicEnergy,dt); } //-------------------------------------------------------- // compute_delta_nodal_atomic_energy: // computes the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixed::compute_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixed::compute_delta_nodal_atomic_energy(double dt) { // set delta energy based on predicted atomic velocities const DENS_MAT & myNodalAtomicEnergy(nodalAtomicEnergy_->quantity()); timeFilter_->apply_post_step1(nodalAtomicEnergyFiltered_.set_quantity(), myNodalAtomicEnergy,dt); deltaNodalAtomicEnergy_ = initialNodalAtomicEnergy_; deltaNodalAtomicEnergy_ += myNodalAtomicEnergy; } //-------------------------------------------------------- // compute_lambda: - // sets up and solves linear system for lambda, if the - // bool is true it iterators to a non-linear solution + // sets up and solves linear system for lambda //-------------------------------------------------------- - void ThermostatFixed::compute_lambda(double dt, - bool iterateSolution) + void ThermostatIntegratorFixed::compute_lambda(double dt, + bool iterateSolution) { // compute predicted changes in nodal atomic energy compute_delta_nodal_atomic_energy(dt); // change in finite element energy deltaFeEnergy_ = initialFeEnergy_; deltaFeEnergy_ += (mdMassMatrix_.quantity())*(temperature_.quantity()); ThermostatGlcFs::compute_lambda(dt,iterateSolution); } //-------------------------------------------------------- // apply_predictor: // apply the thermostat to the atoms in the first step // of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_pre_predictor(double dt) + void ThermostatIntegratorFixed::apply_pre_predictor(double dt) { // initialize values to be track change in finite element energy over the timestep initialize_delta_nodal_atomic_energy(dt); initialFeEnergy_ = -1.*((mdMassMatrix_.quantity())*(temperature_.quantity())); // initially stored as negative for efficiency ThermostatGlcFs::apply_pre_predictor(dt); } //-------------------------------------------------------- // apply_pre_corrector: // apply the thermostat to the atoms in the first part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_pre_corrector(double dt) + void ThermostatIntegratorFixed::apply_pre_corrector(double dt) { // do full prediction if we just redid the shape functions if (full_prediction()) { firstHalfAtomForces_ = atomThermostatForces_; // reset in case this time step needed special treatment _tempNodalAtomicEnergyFiltered_ = nodalAtomicEnergyFiltered_.quantity(); } ThermostatGlcFs::apply_pre_corrector(dt); if (full_prediction()) { // reset temporary variables nodalAtomicEnergyFiltered_ = _tempNodalAtomicEnergyFiltered_; } if (halve_force()) { // save old velocities if we are doing halving calculation of lambda force // copy velocities over into temporary storage (*atomPredictedVelocities_) = atomVelocities_->quantity(); } } - //-------------------------------------------------------- - // add_to_temperature - // add in contributions from lambda power and boundary - // flux to the FE temperature - //-------------------------------------------------------- - void ThermostatFixed::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) - { - deltaEnergy.resize(nNodes_,1); - const set<int> & regulatedNodes(regulatedNodes_->quantity()); - for (int i = 0; i < nNodes_; i++) { - if (regulatedNodes.find(i) != regulatedNodes.end()) { - deltaEnergy(i,0) = 0.; - } - else { - deltaEnergy(i,0) = nodalLambdaPower(i,0); - } - } - } - //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFixed::apply_post_corrector(double dt) + void ThermostatIntegratorFixed::apply_post_corrector(double dt) { bool halveForce = halve_force(); ThermostatGlcFs::apply_post_corrector(dt); // update filtered energy with lambda power DENS_MAT & myNodalAtomicLambdaPower(nodalAtomicLambdaPower_->set_quantity()); timeFilter_->apply_post_step2(nodalAtomicEnergyFiltered_.set_quantity(), myNodalAtomicLambdaPower,dt); if (halveForce) { // Halve lambda force due to fixed temperature constraints // 1) makes up for poor initial condition // 2) accounts for possibly large value of lambda when atomic shape function values change // from eulerian mapping after more than 1 timestep // avoids unstable oscillations arising from // thermostat having to correct for error introduced in lambda changing the // shape function matrices - *lambda_ *= 0.5; + lambdaSolver_->scale_lambda(0.5); firstHalfAtomForces_ = atomThermostatForcesPredVel_; atomThermostatForcesPredVel_->unfix_quantity(); } else { firstHalfAtomForces_ = atomThermostatForces_; } } + //-------------------------------------------------------- + // add_to_temperature + // add in contributions from lambda power and boundary + // flux to the FE temperature + //-------------------------------------------------------- + void ThermostatIntegratorFixed::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) + { + deltaEnergy.resize(nNodes_,1); + + SetDependencyManager<int> * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set<int> & regulatedNodes(myRegulatedNodes->quantity()); + + for (int i = 0; i < nNodes_; i++) { + if (regulatedNodes.find(i) != regulatedNodes.end()) { + deltaEnergy(i,0) = 0.; + } + else { + deltaEnergy(i,0) = nodalLambdaPower(i,0); + } + } + } + //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFixed::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFixed::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // for essential bcs (fixed nodes) : // form rhs : (delThetaV - delTheta)/dt - const set<int> & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager<int> * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set<int> & regulatedNodes(myRegulatedNodes->quantity()); double factor = (1./dt)/keMultiplier_; + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0)); } else { rhs(i,0) = 0.; } } } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFluxFiltered + // Class ThermostatIntegratorFluxFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFluxFiltered::ThermostatFluxFiltered(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatFlux(thermostat,regulatorPrefix) + ThermostatIntegratorFluxFiltered::ThermostatIntegratorFluxFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatIntegratorFlux(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- - void ThermostatFluxFiltered::initialize() + void ThermostatIntegratorFluxFiltered::initialize() { - ThermostatFlux::initialize(); + ThermostatIntegratorFlux::initialize(); TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { // always must start as zero because of filtering scheme heatSourceOld_.reset(nNodes_,1); instantHeatSource_.reset(nNodes_,1); timeStepSource_.reset(nNodes_,1); } } //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- - void ThermostatFluxFiltered::apply_post_corrector(double dt) + void ThermostatIntegratorFluxFiltered::apply_post_corrector(double dt) { // compute lambda - ThermostatFlux::apply_post_corrector(dt); + ThermostatIntegratorFlux::apply_post_corrector(dt); // store data needed for filter inversion of heat flux for thermostat rhs instantHeatSource_ = rhs_; heatSourceOld_ = heatSource_.quantity(); } //-------------------------------------------------------- // add_to_temperature // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFluxFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFluxFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.reset(nNodes_,1); double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); const DENS_MAT & myBoundaryFlux(boundaryFlux_[TEMPERATURE].quantity()); for (int i = 0; i < nNodes_; i++) { deltaEnergy(i,0) = coef*nodalLambdaPower(i,0) + dt*myBoundaryFlux(i,0); } } //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- - void ThermostatFluxFiltered::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFluxFiltered::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { // only tested with flux != 0 + ess bc = 0 // (a) for flux based : // form rhs : 2/3kB * W_I^-1 * \int N_I r dV // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I // fluxes are set in ATC transfer // invert heatSource_ to get unfiltered source // relevant coefficients from time filter double coefF1 = timeFilter_->filtered_coefficient_pre_s1(2.*dt); double coefF2 = timeFilter_->filtered_coefficient_post_s1(2.*dt); double coefU1 = timeFilter_->unfiltered_coefficient_pre_s1(2.*dt); double coefU2 = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); const DENS_MAT & heatSource(heatSource_.quantity()); - const set<int> & applicationNodes(applicationNodes_->quantity()); + SetDependencyManager<int> * myApplicationNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatApplicationNodes"); + const set<int> & applicationNodes(myApplicationNodes->quantity()); + rhs.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (applicationNodes.find(i) != applicationNodes.end()) { rhs(i,0) = heatSource(i,0) - coefF1*coefF2*heatSourceOld_(i,0) - coefU1*coefF2*instantHeatSource_(i,0); rhs(i,0) /= coefU2; } else { rhs(i,0) = 0.; } } } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- - void ThermostatFluxFiltered::output(OUTPUT_LIST & outputData) + void ThermostatIntegratorFluxFiltered::output(OUTPUT_LIST & outputData) { _lambdaPowerOutput_ = lambdaPowerFiltered_->quantity(); // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); } } //-------------------------------------------------------- //-------------------------------------------------------- - // Class ThermostatFixedFiltered + // Class ThermostatIntegratorFixedFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatFixedFiltered::ThermostatFixedFiltered(Thermostat * thermostat, - const string & regulatorPrefix) : - ThermostatFixed(thermostat,regulatorPrefix) + ThermostatIntegratorFixedFiltered::ThermostatIntegratorFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const string & regulatorPrefix) : + ThermostatIntegratorFixed(thermostat,lambdaMaxIterations,regulatorPrefix) { // do nothing } //-------------------------------------------------------- // initialize_delta_nodal_atomic_energy: // initializes storage for the variable tracking // the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixedFiltered::initialize_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixedFiltered::initialize_delta_nodal_atomic_energy(double dt) { // initialize delta energy DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity()); initialNodalAtomicEnergy_ = myNodalAtomicEnergyFiltered; initialNodalAtomicEnergy_ *= -1.; // initially stored as negative for efficiency timeFilter_->apply_pre_step1(myNodalAtomicEnergyFiltered, nodalAtomicEnergy_->quantity(),dt); } //-------------------------------------------------------- // compute_delta_nodal_atomic_energy: // computes the change in the nodal atomic energy // that has occured over the past timestep //-------------------------------------------------------- - void ThermostatFixedFiltered::compute_delta_nodal_atomic_energy(double dt) + void ThermostatIntegratorFixedFiltered::compute_delta_nodal_atomic_energy(double dt) { // set delta energy based on predicted atomic velocities DENS_MAT & myNodalAtomicEnergyFiltered(nodalAtomicEnergyFiltered_.set_quantity()); timeFilter_->apply_post_step1(myNodalAtomicEnergyFiltered, nodalAtomicEnergy_->quantity(),dt); deltaNodalAtomicEnergy_ = initialNodalAtomicEnergy_; deltaNodalAtomicEnergy_ += myNodalAtomicEnergyFiltered; } //-------------------------------------------------------- // add_to_temperature // add in contributions from lambda power and boundary // flux to the FE temperature //-------------------------------------------------------- - void ThermostatFixedFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, - DENS_MAT & deltaEnergy, - double dt) + void ThermostatIntegratorFixedFiltered::add_to_energy(const DENS_MAT & nodalLambdaPower, + DENS_MAT & deltaEnergy, + double dt) { deltaEnergy.resize(nNodes_,1); - const set<int> & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager<int> * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set<int> & regulatedNodes(myRegulatedNodes->quantity()); double coef = timeFilter_->unfiltered_coefficient_post_s1(2.*dt); for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { deltaEnergy(i,0) = 0.; } else { deltaEnergy(i,0) = coef*nodalLambdaPower(i,0); } } } //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side for fixed // (coupling & prescribed) temperature values //-------------------------------------------------------- - void ThermostatFixedFiltered::set_thermostat_rhs(DENS_MAT & rhs, - double dt) + void ThermostatIntegratorFixedFiltered::set_thermostat_rhs(DENS_MAT & rhs, + double dt) { - // (b) for essential bcs (fixed nodes) : + // (b) for essential bcs (fixed nodes): // form rhs : (delThetaV - delTheta)/dt - const set<int> & regulatedNodes(regulatedNodes_->quantity()); + SetDependencyManager<int> * myRegulatedNodes = + (atc_->interscale_manager()).set_int(regulatorPrefix_+"ThermostatRegulatedNodes"); + const set<int> & regulatedNodes(myRegulatedNodes->quantity()); double factor = (1./dt)/keMultiplier_; factor /= timeFilter_->unfiltered_coefficient_post_s1(2.*dt); + rhs.resize(nNodes_,1); + for (int i = 0; i < nNodes_; i++) { if (regulatedNodes.find(i) != regulatedNodes.end()) { rhs(i,0) = factor*(deltaNodalAtomicEnergy_(i,0) - deltaFeEnergy_(i,0)); } else { rhs(i,0) = 0.; } } } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- - void ThermostatFixedFiltered::output(OUTPUT_LIST & outputData) + void ThermostatIntegratorFixedFiltered::output(OUTPUT_LIST & outputData) { _lambdaPowerOutput_ = lambdaPowerFiltered_->quantity(); // approximate value for lambda power double dt = LammpsInterface::instance()->dt(); _lambdaPowerOutput_ *= (2./dt); - DENS_MAT & lambda(lambda_->set_quantity()); + DENS_MAT & lambda((atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaEnergy",1))->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData[regulatorPrefix_+"Lambda"] = λ outputData[regulatorPrefix_+"NodalLambdaPower"] = &(_lambdaPowerOutput_); } } - - // have one for combined and one for lumped (lumped has two sub-thermostats) - // use node sets to call out fixed and fluxed locations - // derived off lumped lambda class that only considers certain atoms and certain nodes based on a mask - // move matrix solver construction to thermostats that actually do something - // thermostats can instantiate the type of shape function object they want in the future, for now we'll need hacks, - // for now we'll have to construct them with appropriate shape function matrix - //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatFluxFixed //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatFluxFixed::ThermostatFluxFixed(Thermostat * thermostat, + ThermostatFluxFixed::ThermostatFluxFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, bool constructThermostats) : RegulatorMethod(thermostat), thermostatFlux_(NULL), thermostatFixed_(NULL), thermostatBcs_(NULL) { if (constructThermostats) { - thermostatFlux_ = new ThermostatFlux(thermostat,regulatorPrefix_+"Flux"); - thermostatFixed_ = new ThermostatFixed(thermostat,regulatorPrefix_+"Fixed"); + thermostatFlux_ = new ThermostatIntegratorFlux(thermostat,lambdaMaxIterations,regulatorPrefix_+"Flux"); + thermostatFixed_ = new ThermostatIntegratorFixed(thermostat,lambdaMaxIterations,regulatorPrefix_+"Fixed"); // need to choose BC type based on coupling mode if (thermostat->coupling_mode() == AtomicRegulator::FLUX) { thermostatBcs_ = thermostatFlux_; } else if (thermostat->coupling_mode() == AtomicRegulator::FIXED) { thermostatBcs_ = thermostatFixed_; } else { throw ATC_Error("ThermostatFluxFixed:create_thermostats - invalid thermostat type provided"); } } } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ThermostatFluxFixed::~ThermostatFluxFixed() { if (thermostatFlux_) delete thermostatFlux_; if (thermostatFixed_) delete thermostatFixed_; } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatFluxFixed::construct_transfers() { thermostatFlux_->construct_transfers(); thermostatFixed_->construct_transfers(); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void ThermostatFluxFixed::initialize() { - thermostatFlux_->initialize(); thermostatFixed_->initialize(); + thermostatFlux_->initialize(); } //-------------------------------------------------------- // apply_predictor: // apply the thermostat to the atoms in the first step // of the Verlet algorithm //-------------------------------------------------------- void ThermostatFluxFixed::apply_pre_predictor(double dt) { thermostatFixed_->apply_pre_predictor(dt); thermostatFlux_->apply_pre_predictor(dt); } //-------------------------------------------------------- // apply_pre_corrector: // apply the thermostat to the atoms in the first part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- void ThermostatFluxFixed::apply_pre_corrector(double dt) { thermostatFlux_->apply_pre_corrector(dt); if (thermostatFixed_->full_prediction()) { atc_->set_fixed_nodes(); } thermostatFixed_->apply_pre_corrector(dt); } //-------------------------------------------------------- // apply_post_corrector: // apply the thermostat to the atoms in the second part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- void ThermostatFluxFixed::apply_post_corrector(double dt) { thermostatFlux_->apply_post_corrector(dt); atc_->set_fixed_nodes(); thermostatFixed_->apply_post_corrector(dt); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void ThermostatFluxFixed::output(OUTPUT_LIST & outputData) { thermostatFlux_->output(outputData); thermostatFixed_->output(outputData); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatFluxFixedFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatFluxFixedFiltered::ThermostatFluxFixedFiltered(Thermostat * thermostat) : - ThermostatFluxFixed(thermostat,false) + ThermostatFluxFixedFiltered::ThermostatFluxFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations) : + ThermostatFluxFixed(thermostat,lambdaMaxIterations,false) { - thermostatFlux_ = new ThermostatFluxFiltered(thermostat,regulatorPrefix_+"Flux"); - thermostatFixed_ = new ThermostatFixedFiltered(thermostat,regulatorPrefix_+"Fixed"); + thermostatFlux_ = new ThermostatIntegratorFluxFiltered(thermostat,lambdaMaxIterations,regulatorPrefix_+"Flux"); + thermostatFixed_ = new ThermostatIntegratorFixedFiltered(thermostat,lambdaMaxIterations,regulatorPrefix_+"Fixed"); // need to choose BC type based on coupling mode if (thermostat->coupling_mode() == AtomicRegulator::FLUX) { thermostatBcs_ = thermostatFlux_; } else if (thermostat->coupling_mode() == AtomicRegulator::FIXED) { thermostatBcs_ = thermostatFixed_; } else { throw ATC_Error("ThermostatFluxFixed:create_thermostats - invalid thermostat type provided"); } } //-------------------------------------------------------- // Class ThermostatGlc //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- - ThermostatGlc::ThermostatGlc(Thermostat * thermostat) : + ThermostatGlc::ThermostatGlc(AtomicRegulator * thermostat) : ThermostatShapeFunction(thermostat), timeFilter_(atomicRegulator_->time_filter()), lambdaPowerFiltered_(NULL), atomThermostatForces_(NULL), prescribedDataMgr_(atc_->prescribed_data_manager()), atomMasses_(NULL) { // consistent with stage 3 of ATC_Method::initialize - lambdaPowerFiltered_= thermostat_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); + lambdaPowerFiltered_= atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaPowerFiltered",1); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatGlc::construct_transfers() { ThermostatShapeFunction::construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); // get data from manager atomMasses_ = interscaleManager.fundamental_atom_quantity(LammpsInterface::ATOM_MASS); // thermostat forces based on lambda and the atomic velocities AtomicThermostatForce * atomThermostatForces = new AtomicThermostatForce(atc_); interscaleManager.add_per_atom_quantity(atomThermostatForces, regulatorPrefix_+"AtomThermostatForce"); atomThermostatForces_ = atomThermostatForces; } //-------------------------------------------------------- // apply_to_atoms: // determines what if any contributions to the // atomic moition is needed for // consistency with the thermostat //-------------------------------------------------------- void ThermostatGlc::apply_to_atoms(PerAtomQuantity<double> * atomVelocities, const DENS_MAT & lambdaForce, double dt) { _velocityDelta_ = lambdaForce; _velocityDelta_ /= atomMasses_->quantity(); _velocityDelta_ *= dt; (*atomVelocities) += _velocityDelta_; } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatPowerVerlet //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatPowerVerlet::ThermostatPowerVerlet(Thermostat * thermostat) : + ThermostatPowerVerlet::ThermostatPowerVerlet(AtomicRegulator * thermostat) : ThermostatGlc(thermostat), nodalTemperatureRoc_(atc_->field_roc(TEMPERATURE)), heatSource_(atc_->atomic_source(TEMPERATURE)), nodalAtomicPower_(NULL), nodalAtomicLambdaPower_(NULL) { // do nothing } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatPowerVerlet::construct_transfers() { InterscaleManager & interscaleManager(atc_->interscale_manager()); // set up node mappings create_node_maps(); // determine if mapping is needed and set up if so if (atomicRegulator_->use_localized_lambda()) { lambdaAtomMap_ = new AtomToElementset(atc_,elementMask_); interscaleManager.add_per_atom_int_quantity(lambdaAtomMap_, regulatorPrefix_+"LambdaAtomMap"); } // set up linear solver if (atomicRegulator_->use_lumped_lambda_solve()) { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); linearSolverType_ = AtomicRegulator::RSL_SOLVE; } else { if (lambdaAtomMap_) { shapeFunctionMatrix_ = new LocalLambdaCouplingMatrix(atc_, lambdaAtomMap_, nodeToOverlapMap_); } else { shapeFunctionMatrix_ = new LambdaCouplingMatrix(atc_,nodeToOverlapMap_); } linearSolverType_ = AtomicRegulator::CG_SOLVE; } interscaleManager.add_per_atom_sparse_matrix(shapeFunctionMatrix_, regulatorPrefix_+"LambdaCouplingMatrixEnergy"); // base class transfers, e.g. weights ThermostatGlc::construct_transfers(); // get managed data nodalAtomicPower_ = interscaleManager.dense_matrix("NodalAtomicPower"); // power induced by lambda DotTwiceKineticEnergy * atomicLambdaPower = new DotTwiceKineticEnergy(atc_,atomThermostatForces_); interscaleManager.add_per_atom_quantity(atomicLambdaPower, regulatorPrefix_+"AtomicLambdaPower"); // restriction to nodes of power induced by lambda nodalAtomicLambdaPower_ = new AtfShapeFunctionRestriction(atc_, atomicLambdaPower, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicLambdaPower_, regulatorPrefix_+"NodalAtomicLambdaPower"); } //-------------------------------------------------------- // initialize // initializes all method data //-------------------------------------------------------- void ThermostatPowerVerlet::initialize() { ThermostatGlc::initialize(); // sets up time filter for cases where variables temporally filtered TimeFilterManager * timeFilterManager = atc_->time_filter_manager(); if (!timeFilterManager->end_equilibrate()) { _nodalAtomicLambdaPowerOut_ = 0.; *lambdaPowerFiltered_ = 0.; timeFilter_->initialize(lambdaPowerFiltered_->quantity()); } } //-------------------------------------------------------- // apply_predictor: // apply the thermostat to the atoms in the first step // of the Verlet algorithm //-------------------------------------------------------- void ThermostatPowerVerlet::apply_pre_predictor(double dt) { atomThermostatForces_->unfix_quantity(); compute_thermostat(0.5*dt); // apply lambda force to atoms const DENS_MAT & thermostatForces(atomThermostatForces_->quantity()); atomThermostatForces_->fix_quantity(); apply_to_atoms(atomVelocities_,thermostatForces,0.5*dt); } //-------------------------------------------------------- // apply_pre_corrector: // apply the thermostat to the atoms in the first part // of the corrector step of the Verlet algorithm //-------------------------------------------------------- void ThermostatPowerVerlet::apply_pre_corrector(double dt) { atomThermostatForces_->unfix_quantity(); compute_thermostat(0.5*dt); // apply lambda force to atoms const DENS_MAT & thermostatForces(atomThermostatForces_->quantity()); atomThermostatForces_->fix_quantity(); apply_to_atoms(atomVelocities_,thermostatForces,0.5*dt); } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the thermostat //-------------------------------------------------------- void ThermostatPowerVerlet::add_to_rhs(FIELDS & rhs) { rhs[TEMPERATURE] += nodalAtomicLambdaPower_->quantity() + boundaryFlux_[TEMPERATURE].quantity(); } //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- void ThermostatPowerVerlet::set_thermostat_rhs(DENS_MAT & rhs_nodes) { // (a) for flux based : // form rhs : \int N_I r dV // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I // fluxes are set in ATC transfer rhs_nodes = heatSource_.quantity(); // (b) for ess. bcs // form rhs : {sum_a (2 * N_Ia * v_ia * f_ia) - (dtheta/dt)_I} // replace rhs for prescribed nodes const DENS_MAT & myNodalAtomicPower(nodalAtomicPower_->quantity()); const DIAG_MAT & myMdMassMatrix(mdMassMatrix_.quantity()); const DENS_MAT & myNodalTemperatureRoc(nodalTemperatureRoc_.quantity()); for (int i = 0; i < nNodes_; i++) { if (prescribedDataMgr_->is_fixed(i,TEMPERATURE,0)) { rhs_nodes(i,0) = 0.5*(myNodalAtomicPower(i,0) - myMdMassMatrix(i,i)*myNodalTemperatureRoc(i,0)); } } } //-------------------------------------------------------- // compute_thermostat: // sets up and solves the thermostat equations since // they are the same at different parts of the time // step //-------------------------------------------------------- void ThermostatPowerVerlet::compute_thermostat(double dt) { // set up rhs set_thermostat_rhs(_rhs_); // solve linear system for lambda DENS_MAT & myLambda(lambda_->set_quantity()); solve_for_lambda(_rhs_,myLambda); nodalAtomicLambdaPower_->unfix_quantity(); // enable computation of force applied by lambda timeFilter_->apply_pre_step1(lambdaPowerFiltered_->set_quantity(), nodalAtomicLambdaPower_->quantity(),dt); nodalAtomicLambdaPower_->fix_quantity(); } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void ThermostatPowerVerlet::output(OUTPUT_LIST & outputData) { _nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); DENS_MAT & lambda(lambda_->set_quantity()); if ((atc_->lammps_interface())->rank_zero()) { outputData["lambda"] = λ outputData["nodalLambdaPower"] = &(_nodalAtomicLambdaPowerOut_); } } //-------------------------------------------------------- // finish: // final tasks after a run //-------------------------------------------------------- void ThermostatPowerVerlet::finish() { _nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatHooverVerlet //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatHooverVerlet::ThermostatHooverVerlet(Thermostat * thermostat) : + ThermostatHooverVerlet::ThermostatHooverVerlet(AtomicRegulator * thermostat) : ThermostatPowerVerlet(thermostat), lambdaHoover_(NULL), nodalAtomicHooverLambdaPower_(NULL) { // set up data consistent with stage 3 of ATC_Method::initialize - lambdaHoover_ = thermostat_->regulator_data(regulatorPrefix_+"LambdaHoover",1); + lambdaHoover_ = atomicRegulator_->regulator_data(regulatorPrefix_+"LambdaHoover",1); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatHooverVerlet::construct_transfers() { ThermostatPowerVerlet::construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); FtaShapeFunctionProlongation * atomHooverLambdas = new FtaShapeFunctionProlongation(atc_, lambdaHoover_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_per_atom_quantity(atomHooverLambdas, regulatorPrefix_+"AtomHooverLambda"); AtomicThermostatForce * atomHooverThermostatForces = new AtomicThermostatForce(atc_,atomHooverLambdas); interscaleManager.add_per_atom_quantity(atomHooverThermostatForces, regulatorPrefix_+"AtomHooverThermostatForce"); SummedAtomicQuantity<double> * atomTotalThermostatForces = new SummedAtomicQuantity<double>(atc_,atomThermostatForces_,atomHooverThermostatForces); interscaleManager.add_per_atom_quantity(atomTotalThermostatForces, regulatorPrefix_+"AtomTotalThermostatForce"); atomThermostatForces_ = atomTotalThermostatForces; // transfers dependent on time integration method DotTwiceKineticEnergy * atomicHooverLambdaPower = new DotTwiceKineticEnergy(atc_,atomHooverThermostatForces); interscaleManager.add_per_atom_quantity(atomicHooverLambdaPower, regulatorPrefix_+"AtomicHooverLambdaPower"); nodalAtomicHooverLambdaPower_ = new AtfShapeFunctionRestriction(atc_, atomicHooverLambdaPower, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicHooverLambdaPower_, regulatorPrefix_+"NodalAtomicHooverLambdaPower"); } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the thermostat //-------------------------------------------------------- void ThermostatHooverVerlet::add_to_rhs(FIELDS & rhs) { rhs[TEMPERATURE] += _nodalAtomicLambdaPowerOut_; } //-------------------------------------------------------- // compute_thermostat: // sets up and solves the thermostat equations since // they are the same at different parts of the time // step //-------------------------------------------------------- void ThermostatHooverVerlet::compute_thermostat(double dt) { // apply prescribed/extrinsic sources and fixed nodes ThermostatPowerVerlet::compute_thermostat(0.5*dt); _nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); // save power from lambda in power-based thermostat // set up Hoover rhs set_hoover_rhs(_rhs_); // solve linear system for lambda DENS_MAT & myLambda(lambdaHoover_->set_quantity()); solve_for_lambda(_rhs_,myLambda); // compute force applied by lambda // compute nodal atomic power from Hoover coupling // only add in contribution to uncoupled nodes if (atomicRegulator_->use_localized_lambda()) add_to_lambda_power(atomThermostatForces_->quantity(),0.5*dt); } //-------------------------------------------------------- // set_hoover_rhs: // sets up the right-hand side for fixed value, // i.e. Hoover coupling //-------------------------------------------------------- void ThermostatHooverVerlet::set_hoover_rhs(DENS_MAT & rhs) { // form rhs : sum_a ( N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I rhs = nodalAtomicPower_->quantity(); rhs -= mdMassMatrix_.quantity()*nodalTemperatureRoc_.quantity(); rhs /= 2.; } //-------------------------------------------------------- // add_to_nodal_lambda_power: // determines the power exerted by the Hoover // thermostat at each FE node //-------------------------------------------------------- void ThermostatHooverVerlet::add_to_lambda_power(const DENS_MAT & myLambdaForce, double dt) { _myNodalLambdaPower_ = nodalAtomicHooverLambdaPower_->quantity(); const INT_ARRAY & nodeToOverlapMap(nodeToOverlapMap_->quantity()); for (int i = 0; i < nNodes_; ++i) { if (nodeToOverlapMap(i,0)==-1) _nodalAtomicLambdaPowerOut_(i,0) += _myNodalLambdaPower_(i,0); else _myNodalLambdaPower_(i,0) = 0.; } timeFilter_->apply_post_step1(lambdaPowerFiltered_->set_quantity(),_myNodalLambdaPower_,dt); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatPowerVerletFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(Thermostat * thermostat) : + ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(AtomicRegulator * thermostat) : ThermostatPowerVerlet(thermostat), nodalTemperature2Roc_(atc_->field_2roc(TEMPERATURE)), fieldsRoc_(atc_->fields_roc()), filterScale_((atc_->time_filter_manager())->filter_scale()) { heatSourceRoc_.reset(nNodes_,1); fluxRoc_[TEMPERATURE].reset(nNodes_,1); } //-------------------------------------------------------- // compute_boundary_flux // also sets time derivatives of boundary flux and // heat sources //-------------------------------------------------------- void ThermostatPowerVerletFiltered::compute_boundary_flux(FIELDS & fields) { ThermostatPowerVerlet::compute_boundary_flux(fields); // compute boundary flux rate of change fluxRoc_[TEMPERATURE] = 0.; atc_->compute_boundary_flux(fieldMask_, fieldsRoc_, fluxRoc_, atomMaterialGroups_, shpFcnDerivs_); // compute extrinsic model rate of change (atc_->extrinsic_model_manager()).set_sources(fieldsRoc_,fluxRoc_); heatSourceRoc_ = fluxRoc_[TEMPERATURE].quantity(); } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the thermostat //-------------------------------------------------------- void ThermostatPowerVerletFiltered::add_to_rhs(FIELDS & rhs) { rhs[TEMPERATURE] += lambdaPowerFiltered_->quantity() + boundaryFlux_[TEMPERATURE].quantity(); } //-------------------------------------------------------- // set_thermostat_rhs: // sets up the right-hand side including boundary // fluxes (coupling & prescribed), heat sources, and // fixed (uncoupled) nodes //-------------------------------------------------------- void ThermostatPowerVerletFiltered::set_thermostat_rhs(DENS_MAT & rhs_nodes) { // (a) for flux based : // form rhs : \int N_I r dV // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I // fluxes are set in ATC transfer rhs_nodes = heatSource_.quantity() + filterScale_*heatSourceRoc_.quantity(); // (b) for ess. bcs // form rhs : {sum_a (N_Ia * v_ia * f_ia) - 0.5*(dtheta/dt)_I} const DENS_MAT & myNodalAtomicPower(nodalAtomicPower_->quantity()); const DIAG_MAT & myMdMassMatrix(mdMassMatrix_.quantity()); const DENS_MAT & myNodalTemperatureRoc(nodalTemperatureRoc_.quantity()); const DENS_MAT & myNodalTemperature2Roc(nodalTemperature2Roc_.quantity()); for (int i = 0; i < nNodes_; i++) { if (prescribedDataMgr_->is_fixed(i,TEMPERATURE,0)) { rhs_nodes(i,0) = 0.5*(myNodalAtomicPower(i,0) - myMdMassMatrix(i,i)*(myNodalTemperatureRoc(i,0)+ filterScale_*myNodalTemperature2Roc(i,0))); } } } //-------------------------------------------------------- // output: // adds all relevant output to outputData //-------------------------------------------------------- void ThermostatPowerVerletFiltered::output(OUTPUT_LIST & outputData) { outputData["lambda"] = &(lambda_->set_quantity()); outputData["nodalLambdaPower"] = &(lambdaPowerFiltered_->set_quantity()); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ThermostatHooverVerletFiltered //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor // Grab references to ATC and thermostat data //-------------------------------------------------------- - ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(Thermostat * thermostat) : + ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(AtomicRegulator * thermostat) : ThermostatPowerVerletFiltered(thermostat), lambdaHoover_(NULL), nodalAtomicHooverLambdaPower_(NULL) { // consistent with stage 3 of ATC_Method::initialize - lambdaHoover_ = thermostat_->regulator_data("LambdaHoover",1); + lambdaHoover_ = atomicRegulator_->regulator_data("LambdaHoover",1); } //-------------------------------------------------------- // constructor_transfers // instantiates or obtains all dependency managed data //-------------------------------------------------------- void ThermostatHooverVerletFiltered::construct_transfers() { ThermostatPowerVerletFiltered::construct_transfers(); InterscaleManager & interscaleManager(atc_->interscale_manager()); FtaShapeFunctionProlongation * atomHooverLambdas = new FtaShapeFunctionProlongation(atc_, lambdaHoover_, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_per_atom_quantity(atomHooverLambdas, regulatorPrefix_+"AtomHooverLambda"); AtomicThermostatForce * atomHooverThermostatForces = new AtomicThermostatForce(atc_,atomHooverLambdas); interscaleManager.add_per_atom_quantity(atomHooverThermostatForces, regulatorPrefix_+"AtomHooverThermostatForce"); SummedAtomicQuantity<double> * atomTotalThermostatForces = new SummedAtomicQuantity<double>(atc_,atomThermostatForces_,atomHooverThermostatForces); interscaleManager.add_per_atom_quantity(atomTotalThermostatForces, regulatorPrefix_+"AtomTotalThermostatForce"); atomThermostatForces_ = atomTotalThermostatForces; // transfers dependent on time integration method DotTwiceKineticEnergy * atomicHooverLambdaPower = new DotTwiceKineticEnergy(atc_,atomHooverThermostatForces); interscaleManager.add_per_atom_quantity(atomicHooverLambdaPower, regulatorPrefix_+"AtomicHooverLambdaPower"); nodalAtomicHooverLambdaPower_ = new AtfShapeFunctionRestriction(atc_, atomicHooverLambdaPower, interscaleManager.per_atom_sparse_matrix("Interpolant")); interscaleManager.add_dense_matrix(nodalAtomicHooverLambdaPower_, regulatorPrefix_+"NodalAtomicHooverLambdaPower"); } //-------------------------------------------------------- // compute_thermostat: // sets up and solves the thermostat equations since // they are the same at different parts of the time // step //-------------------------------------------------------- void ThermostatHooverVerletFiltered::compute_thermostat(double dt) { // apply prescribed/extrinsic sources and fixed nodes ThermostatPowerVerletFiltered::compute_thermostat(0.5*dt); _nodalAtomicLambdaPowerOut_ = nodalAtomicLambdaPower_->quantity(); // save power from lambda in power-based thermostat // set up Hoover rhs set_hoover_rhs(_rhs_); // solve linear system for lambda DENS_MAT & myLambda(lambdaHoover_->set_quantity()); solve_for_lambda(_rhs_,myLambda); // compute force applied by lambda // compute nodal atomic power from Hoover coupling // only add in contribution to uncoupled nodes if (atomicRegulator_->use_localized_lambda()) add_to_lambda_power(atomThermostatForces_->quantity(),0.5*dt); } //-------------------------------------------------------- // set_hoover_rhs: // sets up the right-hand side for fixed value, // i.e. Hoover coupling //-------------------------------------------------------- void ThermostatHooverVerletFiltered::set_hoover_rhs(DENS_MAT & rhs) { // form rhs : sum_a (N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I rhs = nodalAtomicPower_->quantity(); rhs -= mdMassMatrix_.quantity()*(nodalTemperatureRoc_.quantity() + filterScale_*nodalTemperature2Roc_.quantity()); rhs /= 2.; } //-------------------------------------------------------- // add_to_nodal_lambda_power: // determines the power exerted by the Hoover // thermostat at each FE node //-------------------------------------------------------- void ThermostatHooverVerletFiltered::add_to_lambda_power(const DENS_MAT & myLambdaForce, double dt) { _myNodalLambdaPower_ = nodalAtomicHooverLambdaPower_->quantity(); const INT_ARRAY nodeToOverlapMap(nodeToOverlapMap_->quantity()); for (int i = 0; i < nNodes_; ++i) { if (nodeToOverlapMap(i,0)==-1) _nodalAtomicLambdaPowerOut_(i,0) += _myNodalLambdaPower_(i,0); else _myNodalLambdaPower_(i,0) = 0.; } timeFilter_->apply_post_step1(lambdaPowerFiltered_->set_quantity(),_myNodalLambdaPower_,dt); } //-------------------------------------------------------- // add_to_rhs: // determines what if any contributions to the // finite element equations are needed for // consistency with the thermostat //-------------------------------------------------------- void ThermostatHooverVerletFiltered::add_to_rhs(FIELDS & rhs) { rhs[TEMPERATURE] += lambdaPowerFiltered_->quantity(); } }; diff --git a/lib/atc/Thermostat.h b/lib/atc/Thermostat.h index aa2658156..65dbba7e1 100644 --- a/lib/atc/Thermostat.h +++ b/lib/atc/Thermostat.h @@ -1,925 +1,1061 @@ #ifndef THERMOSTAT_H #define THERMOSTAT_H #include "AtomicRegulator.h" #include "PerAtomQuantityLibrary.h" #include <map> #include <set> #include <string> namespace ATC { static const int myLambdaMaxIterations = 50; // forward declarations class ThermalTimeIntegrator; class AtfShapeFunctionRestriction; class FundamentalAtomQuantity; class PrescribedDataManager; /** * @class Thermostat * @brief Manager class for atom-continuum control of thermal energy */ class Thermostat : public AtomicRegulator { public: // constructor Thermostat(ATC_Coupling * atc, const std::string & regulatorPrefix = ""); // destructor virtual ~Thermostat(){}; /** parser/modifier */ virtual bool modify(int narg, char **arg); /** instantiate up the desired method(s) */ virtual void construct_methods(); // data access, intended for method objects /** reset the nodal power to a prescribed value */ virtual void reset_lambda_contribution(const DENS_MAT & target); /** return value for the correction maximum number of iterations */ int lambda_max_iterations() {return lambdaMaxIterations_;}; protected: // data regarding fixed nodes and applied fluxes /** set of all fixed nodes */ std::set<int> fixedNodes_; /** set of all nodes which have a flux applied */ std::set<int> fluxNodes_; /** maximum number of iterations used in iterative solve for lambda */ int lambdaMaxIterations_; private: // DO NOT define this Thermostat(); }; /** * @class ThermostatShapeFunction * @brief Class for thermostat algorithms using the shape function matrices * (thermostats have general for of N^T w N lambda = rhs) */ class ThermostatShapeFunction : public RegulatorShapeFunction { public: - ThermostatShapeFunction(Thermostat * thermostat, + ThermostatShapeFunction(AtomicRegulator * thermostat, const std::string & regulatorPrefix = ""); virtual ~ThermostatShapeFunction() {}; /** instantiate all needed data */ virtual void construct_transfers(); protected: // methods /** set weighting factor for in matrix Nhat^T * weights * Nhat */ virtual void set_weights(); // member data - /** pointer to thermostat object for data */ - Thermostat * thermostat_; - /** MD mass matrix */ DIAG_MAN & mdMassMatrix_; /** pointer to atom velocities */ FundamentalAtomQuantity * atomVelocities_; /** workspace variables */ DENS_VEC _weightVector_, _maskedWeightVector_; private: // DO NOT define this ThermostatShapeFunction(); }; /** * @class ThermostatRescale * @brief Enforces constraint on atomic kinetic energy based on FE temperature */ class ThermostatRescale : public ThermostatShapeFunction { public: + + friend class KinetoThermostatRescale; // since this is sometimes used as a set of member functions for friend - ThermostatRescale(Thermostat * thermostat); + ThermostatRescale(AtomicRegulator * thermostat); virtual ~ThermostatRescale() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) + {boundaryFlux_[TEMPERATURE] = 0.;}; + /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + /** sets up and solves thermostat equations */ + virtual void compute_thermostat(double dt); + /** apply solution to atomic quantities */ void apply_to_atoms(PerAtomQuantity<double> * atomVelocities); - /** correct the RHS for complex temperature definitions */ - virtual void correct_rhs(DENS_MAT & rhs) {}; // base class does no correction, assuming kinetic definition + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); /** FE temperature field */ DENS_MAN & nodalTemperature_; /** construction for prolongation of lambda to atoms */ AtomicVelocityRescaleFactor * atomVelocityRescalings_; /** workspace variables */ DENS_MAT _rhs_; private: // DO NOT define this ThermostatRescale(); }; /** * @class ThermostatRescaleMixedKePe * @brief Enforces constraint on atomic kinetic energy based on FE temperature * when the temperature is a mix of the KE and PE */ class ThermostatRescaleMixedKePe : public ThermostatRescale { public: - ThermostatRescaleMixedKePe(Thermostat * thermostat); + ThermostatRescaleMixedKePe(AtomicRegulator * thermostat); virtual ~ThermostatRescaleMixedKePe() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); protected: - /** correct the RHS for inclusion of the PE */ - virtual void correct_rhs(DENS_MAT & rhs); + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + /** construct the RHS vector */ + virtual void set_rhs(DENS_MAT & rhs); /** nodal fluctuating potential energy */ DENS_MAN * nodalAtomicFluctuatingPotentialEnergy_; /** fraction of temperature from KE */ double keMultiplier_; /** fraction of temperature from PE */ double peMultiplier_; private: // DO NOT define this ThermostatRescaleMixedKePe(); + }; + + /** + * @class ThermostatFsSolver + * @brief Class for solving the linear system for lambda + * (thermostats have general for of N^T w N lambda = rhs) + */ + + class ThermostatFsSolver : public RegulatorShapeFunction { + + + public: + + ThermostatFsSolver(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatFsSolver() {}; + + /** pre-run initialization of method data */ + virtual void initialize(); + + /* sets up and solves the linear system for lambda */ + virtual void compute_lambda(const DENS_MAT & rhs, + bool iterateSolution = true); + + /* scales lambda */ + virtual void scale_lambda(double factor) {*lambda_ *= factor;}; + + /** change the time step factor */ + virtual void set_timestep_factor(double dtFactor) {dtFactor_ = dtFactor;}; + + protected: + + // methods + /** solves the non-linear equation for lambda iteratively */ + void iterate_lambda(const MATRIX & rhs); + + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(); + + // data + /** mapping from all to regulated nodes */ + DENS_MAT rhsMap_; + + /** maximum number of iterations used in iterative solve for lambda */ + int lambdaMaxIterations_; + + /** pointer to the values of lambda interpolated to atoms */ + DENS_MAN * rhsLambdaSquared_; + + /** fraction of timestep over which constraint is exactly enforced */ + double dtFactor_; + + // workspace + DENS_MAT _lambdaOld_; // lambda from previous iteration + DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes + DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop + DENS_VEC _weightVector_, _maskedWeightVector_; + + private: + + // DO NOT define this + ThermostatFsSolver(); + }; /** * @class ThermostatGlcFs - * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm + * @brief Class for thermostat algorithms which perform the time-integration component of the fractional step method */ - class ThermostatGlcFs : public ThermostatShapeFunction { + class ThermostatGlcFs : public RegulatorMethod { public: - ThermostatGlcFs(Thermostat * thermostat, + ThermostatGlcFs(AtomicRegulator * thermostat, + int lambdaMaxIterations, const std::string & regulatorPrefix = ""); virtual ~ThermostatGlcFs() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); /** applies thermostat to atoms in the pre-corrector phase */ virtual void apply_pre_corrector(double dt); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); + /** compute boundary flux, requires regulator input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /* flag for performing the full lambda prediction calculation */ bool full_prediction(); + /** set up atom to material identification */ + virtual void reset_atom_materials(const Array<int> & elementToMaterialMap, + const MatrixDependencyManager<DenseMatrix, int> * atomElement); + protected: // methods - /** determine mapping from all nodes to those to which the thermostat applies */ - void compute_rhs_map(); - /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt) = 0; /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, const DENS_MAN * nodalAtomicEnergy, const DENS_MAT & lambdaForce, DENS_MAT & nodalAtomicLambdaPower, double dt); /** add contributions from thermostat to FE energy */ virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, DENS_MAT & deltaEnergy, double dt) = 0; /* sets up and solves the linear system for lambda */ virtual void compute_lambda(double dt, bool iterateSolution = true); - /** solves the non-linear equation for lambda iteratively */ - void iterate_lambda(const MATRIX & rhs); - // member data + /** solver for lambda linear system */ + ThermostatFsSolver * lambdaSolver_; + + + /** MD mass matrix */ + DIAG_MAN & mdMassMatrix_; + + /** pointer to atom velocities */ + FundamentalAtomQuantity * atomVelocities_; + /** reference to AtC FE temperature */ DENS_MAN & temperature_; /** pointer to a time filtering object */ TimeFilter * timeFilter_; /** power induced by lambda */ DENS_MAN * nodalAtomicLambdaPower_; /** filtered lambda power */ DENS_MAN * lambdaPowerFiltered_; + /** lambda at atomic locations */ + PerAtomQuantity<double> * atomLambdas_; + /** atomic force induced by lambda */ AtomicThermostatForce * atomThermostatForces_; /** pointer to atom masses */ FundamentalAtomQuantity * atomMasses_; - /** pointer to the values of lambda interpolated to atoms */ - DENS_MAN * rhsLambdaSquared_; - /** hack to determine if first timestep has been passed */ bool isFirstTimestep_; - /** maximum number of iterations used in iterative solve for lambda */ - int lambdaMaxIterations_; - /** nodal atomic energy */ DENS_MAN * nodalAtomicEnergy_; /** local version of velocity used as predicted final veloctiy */ PerAtomQuantity<double> * atomPredictedVelocities_; /** predicted nodal atomic energy */ AtfShapeFunctionRestriction * nodalAtomicPredictedEnergy_; /** pointer for force applied in first time step */ DENS_MAN * firstHalfAtomForces_; /** FE temperature change from thermostat during predictor phase in second half of timestep */ DENS_MAT deltaEnergy1_; /** FE temperature change from thermostat during corrector phase in second half of timestep */ DENS_MAT deltaEnergy2_; /** right-hand side data for thermostat equation */ DENS_MAT rhs_; - /** mapping from all to regulated nodes */ - DENS_MAT rhsMap_; - - /** fraction of timestep over which constraint is exactly enforced */ - double dtFactor_; - // workspace DENS_MAT _lambdaPowerOutput_; // power applied by lambda in output format DENS_MAT _velocityDelta_; // change in velocity when lambda force is applied DENS_VEC _lambdaOverlap_; // lambda in MD overlapping FE nodes - DENS_MAT _lambdaOld_; // lambda from previous iteration - DENS_MAT _rhsOverlap_; // normal RHS vector mapped to overlap nodes - DENS_VEC _rhsTotal_; // normal + 2nd order RHS for the iteration loop private: // DO NOT define this ThermostatGlcFs(); }; /** - * @class ThermostatFlux + * @class ThermostatSolverFlux * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ - class ThermostatFlux : public ThermostatGlcFs { + class ThermostatSolverFlux : public ThermostatFsSolver { public: - ThermostatFlux(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatSolverFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFlux() {}; + virtual ~ThermostatSolverFlux() {}; /** instantiate all needed data */ virtual void construct_transfers(); + + protected: + + // methods + /** sets up the transfer which is the set of nodes being regulated */ + virtual void construct_regulated_nodes(); + + private: + + // DO NOT define this + ThermostatSolverFlux(); + + }; + + /** + * @class ThermostatIntegratorFlux + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration + */ + + class ThermostatIntegratorFlux : public ThermostatGlcFs { + + public: + + ThermostatIntegratorFlux(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatIntegratorFlux() {}; /** pre-run initialization of method data */ virtual void initialize(); protected: /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from thermostat to FE energy */ virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, DENS_MAT & deltaEnergy, double dt); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes(); - // data /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN & heatSource_; private: // DO NOT define this - ThermostatFlux(); + ThermostatIntegratorFlux(); }; /** - * @class ThermostatFixed + * @class ThermostatSolverFixed * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration */ - class ThermostatFixed : public ThermostatGlcFs { + class ThermostatSolverFixed : public ThermostatFsSolver { public: - ThermostatFixed(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatSolverFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); + + virtual ~ThermostatSolverFixed() {}; + + /** instantiate all needed data */ + virtual void construct_transfers(); + + protected: + + // methods + /** sets up the transfer which is the set of nodes being regulated */ + virtual void construct_regulated_nodes(); + + private: + + // DO NOT define this + ThermostatSolverFixed(); + + }; + + /** + * @class ThermostatIntegratorFixed + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration + */ + + class ThermostatIntegratorFixed : public ThermostatGlcFs { + + public: + + ThermostatIntegratorFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFixed() {}; + virtual ~ThermostatIntegratorFixed() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); /** applies thermostat to atoms in the pre-corrector phase */ virtual void apply_pre_corrector(double dt); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return atomicRegulator_->use_localized_lambda();}; protected: // methods /** initialize data for tracking the change in nodal atomic temperature */ virtual void initialize_delta_nodal_atomic_energy(double dt); /** compute the change in nodal atomic temperature */ virtual void compute_delta_nodal_atomic_energy(double dt); /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from thermostat to FE energy */ virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, DENS_MAT & deltaEnergy, double dt); /* sets up and solves the linear system for lambda */ virtual void compute_lambda(double dt, bool iterateSolution = true); /** flag for halving the applied force to mitigate numerical errors */ bool halve_force(); - /** sets up the transfer which is the set of nodes being regulated */ - virtual void construct_regulated_nodes(); - // data /** change in FE energy over a timestep */ DENS_MAT deltaFeEnergy_; /** initial FE energy used to compute change */ DENS_MAT initialFeEnergy_; /** change in restricted atomic FE energy over a timestep */ DENS_MAT deltaNodalAtomicEnergy_; /** intial restricted atomic FE energy used to compute change */ DENS_MAT initialNodalAtomicEnergy_; /** filtered nodal atomic energy */ DENS_MAN nodalAtomicEnergyFiltered_; /** forces depending on predicted velocities for correct updating with fixed nodes */ AtomicThermostatForce * atomThermostatForcesPredVel_; /** coefficient to account for effect of time filtering on rhs terms */ double filterCoefficient_; /** kinetic energy multiplier in total energy (used for temperature expression) */ double keMultiplier_; // workspace DENS_MAT _tempNodalAtomicEnergyFiltered_; // stores filtered energy change in atoms for persistence during predictor private: // DO NOT define this - ThermostatFixed(); + ThermostatIntegratorFixed(); }; /** - * @class ThermostatFluxFiltered - * @brief Class enforces GLC on atomic forces based on FE power when using fractional step time integration + * @class ThermostatIntegratorFluxFiltered + * @brief Class integrates GLC on atomic forces based on FE power when using fractional step time integration * in conjunction with time filtering */ - class ThermostatFluxFiltered : public ThermostatFlux { + class ThermostatIntegratorFluxFiltered : public ThermostatIntegratorFlux { public: - ThermostatFluxFiltered(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatIntegratorFluxFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFluxFiltered() {}; + virtual ~ThermostatIntegratorFluxFiltered() {}; /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from thermostat to FE energy */ virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, DENS_MAT & deltaEnergy, double dt); // data /** heat source time history required to get instantaneous heat sources */ DENS_MAT heatSourceOld_; DENS_MAT instantHeatSource_; DENS_MAT timeStepSource_; private: // DO NOT define this - ThermostatFluxFiltered(); + ThermostatIntegratorFluxFiltered(); }; /** - * @class ThermostatFixedFiltered + * @class ThermostatIntegratorFixedFiltered * @brief Class for thermostatting using the temperature matching constraint and is compatible with the fractional step time-integration with time filtering */ - class ThermostatFixedFiltered : public ThermostatFixed { + class ThermostatIntegratorFixedFiltered : public ThermostatIntegratorFixed { public: - ThermostatFixedFiltered(Thermostat * thermostat, - const std::string & regulatorPrefix = ""); + ThermostatIntegratorFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations, + const std::string & regulatorPrefix = ""); - virtual ~ThermostatFixedFiltered() {}; + virtual ~ThermostatIntegratorFixedFiltered() {}; /** get data for output */ virtual void output(OUTPUT_LIST & outputData); protected: // methods /** initialize data for tracking the change in nodal atomic temperature */ virtual void initialize_delta_nodal_atomic_energy(double dt); /** compute the change in nodal atomic temperature */ virtual void compute_delta_nodal_atomic_energy(double dt); /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs, double dt); /** add contributions from thermostat to temperature for uncoupled nodes */ virtual void add_to_energy(const DENS_MAT & nodalLambdaPower, DENS_MAT & deltaEnergy, double dt); private: // DO NOT define this - ThermostatFixedFiltered(); + ThermostatIntegratorFixedFiltered(); }; /** * @class ThermostatFluxFixed * @brief Class for thermostatting using the temperature matching constraint one one set of nodes and the flux matching constraint on another */ class ThermostatFluxFixed : public RegulatorMethod { public: - ThermostatFluxFixed(Thermostat * thermostat, + ThermostatFluxFixed(AtomicRegulator * thermostat, + int lambdaMaxIterations, bool constructThermostats = true); virtual ~ThermostatFluxFixed(); /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); /** applies thermostat to atoms in the pre-corrector phase */ virtual void apply_pre_corrector(double dt); /** applies thermostat to atoms in the post-corrector phase */ virtual void apply_post_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields) {thermostatBcs_->compute_boundary_flux(fields);}; protected: // data /** thermostat for imposing the fluxes */ - ThermostatFlux * thermostatFlux_; + ThermostatIntegratorFlux * thermostatFlux_; /** thermostat for imposing fixed nodes */ - ThermostatFixed * thermostatFixed_; + ThermostatIntegratorFixed * thermostatFixed_; /** pointer to whichever thermostat should compute the flux, based on coupling method */ ThermostatGlcFs * thermostatBcs_; private: // DO NOT define this ThermostatFluxFixed(); }; /** * @class ThermostatFluxFixedFiltered * @brief Class for thermostatting using the temperature matching constraint one one set of nodes and the flux matching constraint on another with time filtering */ class ThermostatFluxFixedFiltered : public ThermostatFluxFixed { public: - ThermostatFluxFixedFiltered(Thermostat * thermostat); + ThermostatFluxFixedFiltered(AtomicRegulator * thermostat, + int lambdaMaxIterations); virtual ~ThermostatFluxFixedFiltered(){}; private: // DO NOT define this ThermostatFluxFixedFiltered(); }; /** * @class ThermostatGlc * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) */ class ThermostatGlc : public ThermostatShapeFunction { public: - ThermostatGlc(Thermostat * thermostat); + ThermostatGlc(AtomicRegulator * thermostat); virtual ~ThermostatGlc() {}; /** instantiate all needed data */ virtual void construct_transfers(); protected: // methods /** apply forces to atoms */ virtual void apply_to_atoms(PerAtomQuantity<double> * atomicVelocity, const DENS_MAT & lambdaForce, double dt); // member data /** pointer to a time filtering object */ TimeFilter * timeFilter_; /** filtered lambda power */ DENS_MAN * lambdaPowerFiltered_; /** atomic force induced by lambda */ PerAtomQuantity<double> * atomThermostatForces_; /** pointer to access prescribed data for fixed nodes */ PrescribedDataManager * prescribedDataMgr_; /** pointer to atom masses */ FundamentalAtomQuantity * atomMasses_; /** workspace variables */ DENS_MAT _velocityDelta_; private: // DO NOT define this ThermostatGlc(); }; /** * @class ThermostatPowerVerlet * @brief Class for thermostatting using the heat flux matching constraint and is compatible with the Gear time-integration */ class ThermostatPowerVerlet : public ThermostatGlc { public: - ThermostatPowerVerlet(Thermostat * thermostat); + ThermostatPowerVerlet(AtomicRegulator * thermostat); virtual ~ThermostatPowerVerlet() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** pre-run initialization of method data */ virtual void initialize(); /** applies thermostat to atoms in the predictor phase */ virtual void apply_pre_predictor(double dt); /** applies thermostat to atoms in the pre-corrector phase */ virtual void apply_pre_corrector(double dt); /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** final tasks of a run */ virtual void finish(); /** determine if local shape function matrices are needed */ virtual bool use_local_shape_functions() const {return (!(atomicRegulator_->use_lumped_lambda_solve()) && atomicRegulator_->use_localized_lambda());}; protected: /** nodal temperature rate of change */ DENS_MAN & nodalTemperatureRoc_; /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN & heatSource_; /** pointer to nodal atomic power */ DENS_MAN * nodalAtomicPower_; /** power applied to each atom by lambda force */ AtfShapeFunctionRestriction * nodalAtomicLambdaPower_; /** workspace variables */ DENS_MAT _rhs_; /** sets up and solves thermostat equations */ virtual void compute_thermostat(double dt); /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes); /** add contributions (if any) to the finite element right-hand side */ virtual void add_to_rhs(FIELDS & rhs); // workspace DENS_MAT _nodalAtomicLambdaPowerOut_; // power induced by lambda in output format private: // DO NOT define this ThermostatPowerVerlet(); }; /** * @class ThermostatHooverVerlet * @brief Classfor thermostatting using the temperature matching constraint and is compatible with Gear time-integration */ class ThermostatHooverVerlet : public ThermostatPowerVerlet { public: - ThermostatHooverVerlet(Thermostat * thermostat); + ThermostatHooverVerlet(AtomicRegulator * thermostat); virtual ~ThermostatHooverVerlet() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** final tasks of a run */ virtual void finish() {}; /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; protected: /** lambda coupling parameter for hoover thermostat */ DENS_MAN * lambdaHoover_; /** workspace variables */ DENS_MAT _myNodalLambdaPower_; /** sets up and solves thermostat equations */ virtual void compute_thermostat(double dt); /** sets up Hoover component of the thermostat */ void set_hoover_rhs(DENS_MAT & rhs); /** add Hoover contributions to lambda power */ void add_to_lambda_power(const DENS_MAT & myLambdaForce, double dt); /** power applied to each atom by hoover lambda force */ AtfShapeFunctionRestriction * nodalAtomicHooverLambdaPower_; /** add contributions (if any) to the finite element right-hand side */ virtual void add_to_rhs(FIELDS & rhs); private: // DO NOT implement this ThermostatHooverVerlet(); }; /** * @class ThermostatPowerVerletFiltered * @brief Class for thermostatting using the heat flux matching constraint and is compatible with Gear time-integration with time filtering */ class ThermostatPowerVerletFiltered : public ThermostatPowerVerlet { public: - ThermostatPowerVerletFiltered(Thermostat * thermostat); + ThermostatPowerVerletFiltered(AtomicRegulator * thermostat); virtual ~ThermostatPowerVerletFiltered(){}; /** get data for output */ virtual void output(OUTPUT_LIST & outputData); /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields); protected: /** sets up appropriate rhs for thermostat equations */ virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes); /** add contributions (if any) to the finite element right-hand side */ virtual void add_to_rhs(FIELDS & rhs); /** nodal temperature 2nd rate of change (i.e. second time derivative) */ DENS_MAN & nodalTemperature2Roc_; /** reference to ATC rate of change sources coming from prescribed data, AtC coupling, and extrinsic coupling */ DENS_MAN heatSourceRoc_; /** references to ATC field rates of changing for inverting the filtered heat sources */ FIELDS & fieldsRoc_; /** flux rate of changes for inverting filtered fluxes */ FIELDS fluxRoc_; /** time scale for the time filter */ double filterScale_; private: // DO NOT define this ThermostatPowerVerletFiltered(); }; /** * @class ThermostatHooverVerletFiltered * @brief Class for thermostatting using the temperature matching constraint and is compatible with Gear time-integration with time filtering */ class ThermostatHooverVerletFiltered : public ThermostatPowerVerletFiltered { public: - ThermostatHooverVerletFiltered(Thermostat * thermostat); + ThermostatHooverVerletFiltered(AtomicRegulator * thermostat); virtual ~ThermostatHooverVerletFiltered() {}; /** instantiate all needed data */ virtual void construct_transfers(); /** final tasks of a run */ virtual void finish() {}; /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; protected: /** lambda coupling parameter for hoover thermostat */ DENS_MAN * lambdaHoover_; /** workspace variables */ DENS_MAT _myNodalLambdaPower_; /** sets up and solves thermostat equations */ virtual void compute_thermostat(double dt); /** sets up Hoover component of the thermostat */ void set_hoover_rhs(DENS_MAT & rhs); /** add Hoover contributions to lambda power */ void add_to_lambda_power(const DENS_MAT & myLambdaForce, double dt); /** power applied to each atom by hoover lambda force */ DENS_MAN * nodalAtomicHooverLambdaPower_; /** add contributions (if any) to the finite element right-hand side */ virtual void add_to_rhs(FIELDS & rhs); private: // DO NOT implement this ThermostatHooverVerletFiltered(); }; }; #endif diff --git a/lib/atc/TimeIntegrator.h b/lib/atc/TimeIntegrator.h index 22e4b716f..2753386d0 100644 --- a/lib/atc/TimeIntegrator.h +++ b/lib/atc/TimeIntegrator.h @@ -1,380 +1,382 @@ #ifndef TIME_INTEGRATOR_H #define TIME_INTEGRATOR_H #include "MatrixLibrary.h" #include "TimeFilter.h" #include "ATC_TypeDefs.h" namespace ATC { // forward declarations class ATC_Method; class ATC_Coupling; class TimeIntegrationMethod; /** * @class AtomTimeIntegrator * @brief Base class for various time integrators for atomic quantities (replacing other lammps fixes) */ class AtomTimeIntegrator { public: // constructor AtomTimeIntegrator(){}; // destructor virtual ~AtomTimeIntegrator(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(){}; /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt){}; /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt){}; /** Corrector phase, Verlet second step for velocity */ virtual void final_integrate(double dt){}; }; /** * @class AtomTimeIntegratorType * @brief class for applying velocity-verlet based on atom type */ class AtomTimeIntegratorType : public AtomTimeIntegrator { public: // constructor AtomTimeIntegratorType(ATC_Method * atc, AtomType atomType); // destructor virtual ~AtomTimeIntegratorType(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(); /** Predictor phase, Verlet first step for velocity */ virtual void init_integrate_velocity(double dt); /** Predictor phase, Verlet first step for position */ virtual void init_integrate_position(double dt); /** Corrector phase, Verlet second step for velocity */ virtual void final_integrate(double dt); protected: /** pointer to atc object */ ATC_Method * atc_; /** atom type this is applied to */ AtomType atomType_; /** atomic masses */ DENS_MAN * mass_; /** atomic positions */ DENS_MAN * position_; /** atomic velocities */ DENS_MAN * velocity_; /** atomic forces */ DENS_MAN * force_; // workspace DENS_MAT _deltaQuantity_; private: // DO NOT define this AtomTimeIntegratorType(); }; /** * @class TimeIntegrator * @brief Base class for various time integrators for FE quantities */ class TimeIntegrator { public: /** types of time integration */ enum TimeIntegrationType { + NONE=0, STEADY, VERLET, GEAR, FRACTIONAL_STEP, EXPLICIT, IMPLICIT, - CRANK_NICOLSON + CRANK_NICOLSON, + DIRECT }; // constructor TimeIntegrator(ATC_Coupling * atc, TimeIntegrationType timeIntegrationType = STEADY); // destructor virtual ~TimeIntegrator(); /** parser/modifier */ virtual bool modify(int narg, char **arg){return false;}; /** create objects to implement requested numerical method */ virtual void construct_methods() = 0; /** create and get necessary transfer operators */ virtual void construct_transfers(); /** pre time integration initialization of data */ virtual void initialize(); /** flag if reset is needed */ bool need_reset() const {return needReset_;}; // time step methods, corresponding to ATC_Coupling /** first part of pre_initial_integrate */ virtual void pre_initial_integrate1(double dt); /** second part of pre_initial_integrate */ virtual void pre_initial_integrate2(double dt); /** first part of post_initial_integrate */ virtual void post_initial_integrate1(double dt); /** second part of post_initial_integrate */ virtual void post_initial_integrate2(double dt); /** first part of pre_final_integrate */ virtual void pre_final_integrate1(double dt); /** second part of pre_final_integrate */ virtual void pre_final_integrate2(double dt); /** first part of post_final_integrate */ virtual void post_final_integrate1(double dt); /** second part of post_final_integrate */ virtual void post_final_integrate2(double dt); /** third part of post_final_integrate */ virtual void post_final_integrate3(double dt); /** checks to see if first RHS computation is needed */ virtual bool has_final_predictor(); /** checks to see if second RHS computation is needed */ virtual bool has_final_corrector(); /** adds any contributions from time integrator to RHS */ virtual void add_to_rhs(); /** post processing step prior to output */ virtual void post_process(); /** add output data */ virtual void output(OUTPUT_LIST & outputData); /** pack persistent fields */ virtual void pack_fields(RESTART_LIST & data); /** finalize any data */ virtual void finish(); // Member data access /** access to time integration type */ TimeIntegrationType time_integration_type() const { return timeIntegrationType_; }; /** access to ATC Transfer object */ ATC_Coupling * atc() {return atc_;}; /** access to time filter object */ TimeFilter * time_filter() {return timeFilter_;}; /** access to time filter manager object */ TimeFilterManager * time_filter_manager() {return timeFilterManager_;}; /** force the integrator to be reset */ void force_reset() {needReset_ = true;}; /** force the integrator not to be reset */ void force_no_reset() {needReset_ = false;}; protected: /** pointer to time integrator method */ TimeIntegrationMethod * timeIntegrationMethod_; /** pointer to access ATC methods */ ATC_Coupling * atc_; /** time filter for specific updates */ TimeFilter * timeFilter_; /** time filter manager for getting time filtering info */ TimeFilterManager * timeFilterManager_; /** type of integration scheme being used */ TimeIntegrationType timeIntegrationType_; /** flat to reset data */ bool needReset_; private: // DO NOT define this TimeIntegrator(); }; /** * @class TimeIntegrationMethod * @brief Base class for time integration methods which update FE quantities */ class TimeIntegrationMethod { public: // constructor TimeIntegrationMethod(TimeIntegrator * timeIntegrator); // destructor virtual ~TimeIntegrationMethod(){}; /** create and get necessary transfer operators */ virtual void construct_transfers(){}; /** pre time integration */ virtual void initialize(){}; // time step methods, corresponding to ATC_Coupling and TimeIntegrator /** first part of pre_initial_integrate */ virtual void pre_initial_integrate1(double dt){}; /** second part of pre_initial_integrate */ virtual void pre_initial_integrate2(double dt){}; /** first part of post_initial_integrate */ virtual void post_initial_integrate1(double dt){}; /** second part of post_initial_integrate */ virtual void post_initial_integrate2(double dt){}; /** first part of pre_final_integrate */ virtual void pre_final_integrate1(double dt){}; /** second part of pre_final_integrate */ virtual void pre_final_integrate2(double dt){}; /** first part of post_final_integrate */ virtual void post_final_integrate1(double dt){}; /** second part of post_final_integrate */ virtual void post_final_integrate2(double dt){}; /** third part of post_final_integrate */ virtual void post_final_integrate3(double dt){}; /** checks to see if first RHS computation is needed */ virtual bool has_final_predictor() {return false;}; /** checks to see if second RHS computation is needed */ virtual bool has_final_corrector() {return false;}; /** adds any contributions from time integrator to RHS */ virtual void add_to_rhs() {}; /** post processing step */ virtual void post_process(){}; /** add output data */ virtual void output(OUTPUT_LIST & outputData){}; /** pack persistent fields */ virtual void pack_fields(RESTART_LIST & data){}; /** finalize any states */ virtual void finish(){}; protected: /** owning time integrator */ TimeIntegrator * timeIntegrator_; /** associated ATC transfer object */ ATC_Coupling * atc_; private: // DO NOT define this TimeIntegrationMethod(); }; //-------------------------------------------------------- //-------------------------------------------------------- // time integration functions not associated // with any particular class //-------------------------------------------------------- //-------------------------------------------------------- inline void gear1_4_predict(MATRIX & f, MATRIX & dot_f, MATRIX & ddot_f, const MATRIX & dddot_f, double dt) // 4th order Gear integrator for 1rst order ODE predictor step { f = f + dot_f*dt + ddot_f*(1./2.*dt*dt) + dddot_f*(1./6.*dt*dt*dt); dot_f = dot_f + ddot_f*dt+dddot_f*(1./2.*dt*dt); ddot_f = ddot_f + dddot_f*dt; }; inline void gear1_3_predict(MATRIX & f, MATRIX & dot_f, const MATRIX & ddot_f, double dt) // 3rd order Gear integrator for 1rst order ODE predictor step { f = f + dot_f*dt + ddot_f*(1./2.*dt*dt); dot_f = dot_f + ddot_f*dt; }; inline void gear1_4_correct(MATRIX & f, MATRIX & dot_f, MATRIX & ddot_f, MATRIX & dddot_f, const MATRIX & R_f, double dt) // 4th order Gear integrator for 1rst order ODE corrector step { f = f + (3./8.)*R_f; dot_f = dot_f + (1./dt)*R_f; ddot_f = ddot_f + (3./2./dt/dt)*R_f; dddot_f = dddot_f + (1./dt/dt/dt)*R_f; }; inline void gear1_3_correct(MATRIX & f, MATRIX & dot_f, MATRIX & ddot_f, const MATRIX & R_f, double dt) // 3rd order Gear integrator for 1rst order ODE corrector step { f = f + (5./12.)*R_f; dot_f = dot_f + (1./dt)*R_f; ddot_f = ddot_f + (1./dt/dt)*R_f; }; inline void explicit_1(MATRIX & f, const MATRIX & dot_f, double dt) // 1rst order explict ODE update { f = f + dt*dot_f; }; inline void explicit_2(MATRIX & f, const MATRIX & dot_f, const MATRIX & ddot_f, double dt) // 2nd order explict ODE update { f = f + dt*dot_f + .5*dt*dt*ddot_f; }; }; #endif diff --git a/lib/atc/TransferLibrary.cpp b/lib/atc/TransferLibrary.cpp index f4cdb172a..a6bba5ca2 100644 --- a/lib/atc/TransferLibrary.cpp +++ b/lib/atc/TransferLibrary.cpp @@ -1,2008 +1,2008 @@ // ATC headers #include "TransferLibrary.h" #include "ATC_Coupling.h" #include "PrescribedDataManager.h" #include "LinearSolver.h" #include "PerAtomQuantityLibrary.h" #include "KernelFunction.h" #include "MoleculeSet.h" //#include <typeinfo> #include <set> #include <sstream> #include <utility> #include <vector> using std::set; using std::map; using std::string; using std::stringstream; using std::pair; using std::vector; namespace ATC { //-------------------------------------------------------- //-------------------------------------------------------- // Class NodalAtomVolume //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- NodalAtomVolume::NodalAtomVolume(ATC_Method * atc, SPAR_MAN * shapeFunction) : atc_(atc), shapeFunction_(shapeFunction), lammpsInterface_(atc->lammps_interface()), feEngine_(atc->fe_engine()), tol_(1.e-10) { shapeFunction_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void NodalAtomVolume::reset_quantity() const { // solve equation \sum_a N_Ia \sum_J N_Ja dV_J = \int_Omega N_I dV // form left-hand side int nNodes = shapeFunction_->nCols(); SPAR_MAT lhs(nNodes,nNodes); atc_->compute_consistent_md_mass_matrix(shapeFunction_->quantity(),lhs); // form right-hand side _rhsMatrix_.resize(nNodes,nNodes); feEngine_->compute_lumped_mass_matrix(_rhsMatrix_); _rhs_.resize(nNodes); _rhs_.copy(_rhsMatrix_.ptr(),_rhsMatrix_.size(),1); // change entries for all entries if no atoms in shape function support double totalVolume = _rhs_.sum(); double averageVolume = averaging_operation(totalVolume); _scale_.resize(nNodes); for (int i = 0; i < nNodes; i++) { if ((abs(lhs(i,i)) > 0.)) _scale_(i) = 1.; else _scale_(i) = 0.; } lhs.row_scale(_scale_); for (int i = 0; i < nNodes; i++) { if (_scale_(i) < 0.5) { lhs.set(i,i,1.); _rhs_(i) = averageVolume; } } lhs.compress(); // solve equation LinearSolver solver(lhs, ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC, true); solver.set_max_iterations(lhs.nRows()); solver.set_tolerance(tol_); quantity_.reset(nNodes,1); CLON_VEC tempQuantity(quantity_,CLONE_COL,0); solver.solve(tempQuantity,_rhs_); } //-------------------------------------------------------- // averaging_operation //-------------------------------------------------------- double NodalAtomVolume::averaging_operation(const double totalVolume) const { int nLocal[1] = {shapeFunction_->nRows()}; int nGlobal[1] = {0}; lammpsInterface_->int_allsum(nLocal,nGlobal,1); return totalVolume/(double(nGlobal[0])); } //-------------------------------------------------------- // overloading operation to get number of rows //-------------------------------------------------------- int NodalAtomVolume::nRows() const { return atc_->num_nodes(); } //-------------------------------------------------------- // overloading operation to get number of columns //-------------------------------------------------------- int NodalAtomVolume::nCols() const { return 1; } //-------------------------------------------------------- //-------------------------------------------------------- // Class NodalVolume //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // averaging_operation //-------------------------------------------------------- double NodalVolume::averaging_operation(const double totalVolume) const { int nNodes = shapeFunction_->nCols(); return totalVolume/nNodes; } //-------------------------------------------------------- //-------------------------------------------------------- // Class NodalAtomVolumeElement //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- NodalAtomVolumeElement::NodalAtomVolumeElement(ATC_Method * atc, SPAR_MAN * shapeFunction, PerAtomQuantity<int> * atomElement) : atc_(atc), shapeFunction_(shapeFunction), atomElement_(atomElement), feEngine_(atc->fe_engine()), tol_(1.e-10) { shapeFunction_->register_dependence(this); if (!atomElement_) { InterscaleManager & interscaleManager = atc_->interscale_manager(); atomElement_ = interscaleManager.per_atom_int_quantity("AtomElement"); } atomElement_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void NodalAtomVolumeElement::reset_quantity() const { // Using analyses by G. Wagner and J. Templeton, weights ~ phi*M^{-1}*V // where phi are the dimensionless shape/weighting functions, // M is the "mass" matrix M_IJ, // V is the vector of nodal and element volumes // // // form atom-element shape functions and elemental volumes const FE_Mesh * feMesh = feEngine_->fe_mesh(); int nElts = feMesh->num_elements(); int nNodes = shapeFunction_->nCols(); int nLocal = shapeFunction_->nRows(); // form "mass" matrix M_IJ (I,J = 0, 1, ..., nNodes+nElts-1) int neSize = nNodes+nElts; const INT_ARRAY & atomElement(atomElement_->quantity()); SPAR_MAT nodEltShpFcnMatrix(nLocal,neSize); const SPAR_MAT & shapeFunction(shapeFunction_->quantity()); for(int a = 0; a < nLocal; a++) { for(int I = 0; I < nNodes; I++) { nodEltShpFcnMatrix.set(a,I,shapeFunction(a,I)); } int thisCol = nNodes+atomElement(a,0); nodEltShpFcnMatrix.set(a,thisCol,1); } SPAR_MAT neMassMatrix(neSize,neSize); atc_->compute_consistent_md_mass_matrix(nodEltShpFcnMatrix,neMassMatrix); // form vector of nodal and elemental volumes _nodeVolumesMatrix_.resize(nNodes,nNodes); feEngine_->compute_lumped_mass_matrix(_nodeVolumesMatrix_); _nodeVolumes_.resize(nNodes); _nodeVolumes_.copy(_nodeVolumesMatrix_.ptr(),_nodeVolumesMatrix_.size(),1); DENS_VEC _nodeElementVolumes_(neSize); for(int I = 0; I < nNodes; I++) { _nodeElementVolumes_(I) = _nodeVolumes_(I); } double averageEltVolume = 0.0; for(int E = 0; E < nElts; E++) { double minx, maxx, miny, maxy, minz, maxz; feMesh->element_coordinates(E,_nodalCoords_); minx = _nodalCoords_(0,0); maxx = _nodalCoords_(0,0); miny = _nodalCoords_(1,0); maxy = _nodalCoords_(1,0); minz = _nodalCoords_(2,0); maxz = _nodalCoords_(2,0); for (int j = 1; j < _nodalCoords_.nCols(); ++j) { if (_nodalCoords_(0,j)<minx) minx = _nodalCoords_(0,j); if (_nodalCoords_(0,j)>maxx) maxx = _nodalCoords_(0,j); if (_nodalCoords_(1,j)<miny) miny = _nodalCoords_(1,j); if (_nodalCoords_(1,j)>maxy) maxy = _nodalCoords_(1,j); if (_nodalCoords_(2,j)<minz) minz = _nodalCoords_(2,j); if (_nodalCoords_(2,j)>maxz) maxz = _nodalCoords_(2,j); } _nodeElementVolumes_(nNodes+E) = (maxx-minx)*(maxy-miny)*(maxz-minz); averageEltVolume += (maxx-minx)*(maxy-miny)*(maxz-minz); } averageEltVolume /= nElts; // correct entries of mass matrix if no atoms in shape function support double totalNodalVolume = _nodeVolumes_.sum(); double averageNodalVolume = totalNodalVolume/nNodes; _scale_.resize(neSize); for (int i = 0; i < neSize; i++) { if ((abs(neMassMatrix(i,i)) > 0.)) { _scale_(i) = 1.; } else { printf("No atoms are in support of node/element %i\n",i); _scale_(i) = 0.; } } neMassMatrix.row_scale(_scale_); for (int i = 0; i < neSize; i++) { if (_scale_(i) < 0.5) { neMassMatrix.set(i,i,1.); if (i < nNodes) { _nodeElementVolumes_(i) = averageNodalVolume; } else { _nodeElementVolumes_(i) = averageEltVolume; } } } neMassMatrix.compress(); // solve equation LinearSolver solver(neMassMatrix, ATC::LinearSolver::ITERATIVE_SOLVE_SYMMETRIC, true); solver.set_max_iterations(neMassMatrix.nRows()); double myTol = 1.e-10; solver.set_tolerance(myTol); quantity_.resize(neSize,0); CLON_VEC tempQuantity(quantity_,CLONE_COL,0); solver.solve(tempQuantity,_nodeElementVolumes_); } //-------------------------------------------------------- // overloading operation to get number of rows //-------------------------------------------------------- int NodalAtomVolumeElement::nRows() const { return atc_->num_nodes(); } //-------------------------------------------------------- // overloading operation to get number of columns //-------------------------------------------------------- int NodalAtomVolumeElement::nCols() const { return 1; } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomTypeElement //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomTypeElement::AtomTypeElement(ATC_Coupling * atc, PerAtomQuantity<int> * atomElement) : atomElement_(atomElement), nElts_((atc->fe_engine())->num_elements()) { if (!atomElement_) { atomElement_ = (atc->interscale_manager()).per_atom_int_quantity("AtomElement"); } atomElement_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void AtomTypeElement::reset_quantity() const { // determine which elements contain internal atoms quantity_.resize(nElts_,1); _quantityLocal_.resize(nElts_,1); _quantityLocal_ = 0; const INT_ARRAY & atomElement(atomElement_->quantity()); for (int i = 0; i < atomElement_->nRows(); ++i) { _quantityLocal_(atomElement(i,0),0) = 1; } // swap contributions lammpsInterface_->logical_or(_quantityLocal_.ptr(), quantity_.ptr(),nElts_); } //-------------------------------------------------------- //-------------------------------------------------------- // Class ElementMask //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ElementMask::ElementMask(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal, MatrixDependencyManager<DenseMatrix, int> * hasGhost) : hasInternal_(hasInternal), hasGhost_(hasGhost), feEngine_(atc->fe_engine()) { if (!hasInternal_) { hasInternal_ = (atc->interscale_manager()).dense_matrix_int("ElementHasInternal"); } if (!hasGhost_) { hasGhost_ = (atc->interscale_manager()).dense_matrix_int("ElementHasGhost"); } hasInternal_->register_dependence(this); if (hasGhost_) hasGhost_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void ElementMask::reset_quantity() const { const INT_ARRAY & hasInternal(hasInternal_->quantity()); int nElts = hasInternal.size(); quantity_.resize(nElts,1); if (hasGhost_) { const INT_ARRAY & hasGhost(hasGhost_->quantity()); for (int i = 0; i < nElts; ++i) { quantity_(i,0) = !hasInternal(i,0) || hasGhost(i,0); } } else { for (int i = 0; i < nElts; ++i) { quantity_(i,0) = !hasInternal(i,0); } } const set<int> & nullElements = feEngine_->null_elements(); set<int>::const_iterator iset; for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { int ielem = *iset; quantity_(ielem,0) = false; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class AtomElementMask //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AtomElementMask::AtomElementMask(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasAtoms) : hasAtoms_(hasAtoms), feEngine_(atc->fe_engine()) { if (!hasAtoms_) { hasAtoms_ = (atc->interscale_manager()).dense_matrix_int("ElementHasInternal"); } if (!hasAtoms_) { throw ATC_Error("AtomElementMask::AtomElementMask - no element has atoms transfer provided"); } hasAtoms_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void AtomElementMask::reset_quantity() const { const INT_ARRAY & hasAtoms(hasAtoms_->quantity()); int nElts = hasAtoms.size(); quantity_.resize(nElts,1); for (int i = 0; i < nElts; ++i) { quantity_(i,0) = hasAtoms(i,0); } // this seems to cause problems because many materials end up being null const set<int> & nullElements = feEngine_->null_elements(); set<int>::const_iterator iset; for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { int ielem = *iset; quantity_(ielem,0) = false; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class ElementMaskNodeSet //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ElementMaskNodeSet::ElementMaskNodeSet(ATC_Coupling * atc, - RegulatedNodes * nodeSet) : + SetDependencyManager<int> * nodeSet) : nodeSet_(nodeSet), feMesh_((atc->fe_engine())->fe_mesh()) { nodeSet_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void ElementMaskNodeSet::reset_quantity() const { quantity_.resize(feMesh_->num_elements(),1); quantity_ = false; // get the maximal element set corresponding to those nodes set<int> elementSet; feMesh_->nodeset_to_maximal_elementset(nodeSet_->quantity(),elementSet); set<int>::const_iterator iset; for (iset = elementSet.begin(); iset != elementSet.end(); iset++) { quantity_(*iset,0) = true; } } //-------------------------------------------------------- //-------------------------------------------------------- // Class NodalGeometryType //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- NodalGeometryType::NodalGeometryType(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal, MatrixDependencyManager<DenseMatrix, int> * hasGhost) : hasInternal_(hasInternal), hasGhost_(hasGhost), feEngine_(atc->fe_engine()), nNodes_(atc->num_nodes()), nElts_((atc->fe_engine())->num_elements()) { if (!hasInternal_) { hasInternal_ = (atc->interscale_manager()).dense_matrix_int("ElementHasInternal"); } if (!hasGhost_) { hasGhost_ = (atc->interscale_manager()).dense_matrix_int("ElementHasGhost"); } hasInternal_->register_dependence(this); if (hasGhost_) hasGhost_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void NodalGeometryType::reset_quantity() const { const INT_ARRAY & hasInternal(hasInternal_->quantity()); _nodesInternal_.resize(nNodes_); _nodesInternal_ = 0; _nodesGhost_.reset(nNodes_); _nodesGhost_ = 0; Array<int> nodes; vector<int> myElems = feEngine_->fe_mesh()->owned_elts(); if (hasGhost_) { const INT_ARRAY & hasGhost(hasGhost_->quantity()) ; // iterate through all elements owned by this processor for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; if (hasInternal(ielem,0) || hasGhost(ielem,0)) { feEngine_->element_connectivity(ielem,nodes); for (int j = 0; j < nodes.size(); j++) { if (hasInternal(ielem,0)) { _nodesInternal_(nodes(j)) = 1; } if (hasGhost(ielem,0)) { _nodesGhost_(nodes(j)) = 1; } } } } // sum up partial result arrays lammpsInterface_->logical_or(MPI_IN_PLACE, _nodesInternal_.ptr(), _nodesInternal_.size()); lammpsInterface_->logical_or(MPI_IN_PLACE, _nodesGhost_.ptr(), _nodesGhost_.size()); } else { // iterate through all elements owned by this processor for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; if (hasInternal(ielem,0)) { feEngine_->element_connectivity(ielem,nodes); for (int j = 0; j < nodes.size(); j++) { _nodesInternal_(nodes(j)) = 1; } } } // sum up partial result arrays lammpsInterface_->logical_or(MPI_IN_PLACE, _nodesInternal_.ptr(), _nodesInternal_.size()); } quantity_.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (_nodesInternal_(i) && _nodesGhost_(i)) { quantity_(i,0) = BOUNDARY; } else if (_nodesInternal_(i)) { quantity_(i,0) = MD_ONLY; } else { quantity_(i,0) = FE_ONLY; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class NodalGeometryTypeElementSet //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- NodalGeometryTypeElementSet::NodalGeometryTypeElementSet(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal) : hasInternal_(hasInternal), feEngine_(atc->fe_engine()), nNodes_(atc->num_nodes()), nElts_((atc->fe_engine())->num_elements()) { if (!hasInternal_) { hasInternal_ = (atc->interscale_manager()).dense_matrix_int("ElementHasInternal"); } if (!hasInternal_) { throw ATC_Error("NodalGeometryTypeElementSet: No ElementHasInternal object provided or exists"); } hasInternal_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void NodalGeometryTypeElementSet::reset_quantity() const { const INT_ARRAY & hasInternal(hasInternal_->quantity()); _nodesInternal_.resize(nNodes_); _nodesInternal_ = 0; _nodesGhost_.reset(nNodes_); _nodesGhost_ = 0; Array<int> nodes; vector<int> myElems = feEngine_->fe_mesh()->owned_elts(); // iterate through all elements owned by this processor for (vector<int>::iterator elemsIter = myElems.begin(); elemsIter != myElems.end(); ++elemsIter) { int ielem = *elemsIter; if (hasInternal(ielem,0)) { feEngine_->element_connectivity(ielem,nodes); for (int j = 0; j < nodes.size(); j++) { _nodesInternal_(nodes(j)) = 1; } } else { feEngine_->element_connectivity(ielem,nodes); for (int j = 0; j < nodes.size(); j++) { _nodesGhost_(nodes(j)) = 1; } } } // sum up partial result arrays lammpsInterface_->logical_or(MPI_IN_PLACE, _nodesInternal_.ptr(), _nodesInternal_.size()); lammpsInterface_->logical_or(MPI_IN_PLACE, _nodesGhost_.ptr(), _nodesGhost_.size()); quantity_.resize(nNodes_,1); for (int i = 0; i < nNodes_; i++) { if (_nodesInternal_(i) && _nodesGhost_(i)) { quantity_(i,0) = BOUNDARY; } else if (_nodesInternal_(i)) { quantity_(i,0) = MD_ONLY; } else { quantity_(i,0) = FE_ONLY; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class NodeToSubset //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- NodeToSubset::NodeToSubset(ATC_Method * atc, SetDependencyManager<int> * subsetNodes) : LargeToSmallMap(), atc_(atc), subsetNodes_(subsetNodes) { subsetNodes_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void NodeToSubset::reset_quantity() const { int nNodes = atc_->num_nodes(); const set<int> & subsetNodes(subsetNodes_->quantity()); quantity_.resize(nNodes,1); size_ = 0; for (int i = 0; i < nNodes; i++) { if (subsetNodes.find(i) != subsetNodes.end()) { quantity_(i,0) = size_; size_++; } else { quantity_(i,0) = -1; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class SubsetToNode //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- SubsetToNode::SubsetToNode(NodeToSubset * nodeToSubset) : nodeToSubset_(nodeToSubset) { nodeToSubset_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void SubsetToNode::reset_quantity() const { const INT_ARRAY & nodeToSubset(nodeToSubset_->quantity()); int nNodes = nodeToSubset.nRows(); int count = 0; quantity_.resize(nodeToSubset_->size(),1); for (int i = 0; i < nNodes; i++) { if (nodeToSubset(i,0) > -1) { quantity_(count,0) = i; count++; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class ReducedSparseMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- ReducedSparseMatrix::ReducedSparseMatrix(ATC_Method * atc, SPAR_MAN * source, LargeToSmallAtomMap * map) : SparseMatrixTransfer<double>(), source_(source), map_(map) { source_->register_dependence(this); map_->register_dependence(this); } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- ReducedSparseMatrix::~ReducedSparseMatrix() { source_->remove_dependence(this); map_->remove_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void ReducedSparseMatrix::reset_quantity() const { const SPAR_MAT & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); quantity_.reset(source.nRows(),source.nCols()); for (int i = 0; i < source.nRows(); i++) { int idx = map(i,0); if (idx > -1) { source.row(i,_row_,_index_); for (int j = 0; j < _row_.size(); j++) { quantity_.set(i,_index_(j),_row_(j)); } } } quantity_.compress(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class RowMappedSparseMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void RowMappedSparseMatrix::reset_quantity() const { const SPAR_MAT & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); quantity_.reset(map_->size(),source.nCols()); for (int i = 0; i < source.nRows(); i++) { int idx = map(i,0); if (idx > -1) { source.row(i,_row_,_index_); for (int j = 0; j < _row_.size(); j++) { quantity_.set(idx,_index_(j),_row_(j)); } } } quantity_.compress(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class RowMappedSparseMatrixVector //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- RowMappedSparseMatrixVector::RowMappedSparseMatrixVector(ATC_Method * atc, VectorDependencyManager<SPAR_MAT * > * source, LargeToSmallAtomMap * map) : VectorTransfer<SPAR_MAT * >(), source_(source), map_(map) { source_->register_dependence(this); map_->register_dependence(this); } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- RowMappedSparseMatrixVector::~RowMappedSparseMatrixVector() { source_->remove_dependence(this); map_->remove_dependence(this); for (unsigned i = 0; i < quantity_.size(); ++i) { if (quantity_[i]) delete quantity_[i]; } } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void RowMappedSparseMatrixVector::reset_quantity() const { const vector<SPAR_MAT * > & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); for (unsigned i = 0; i < quantity_.size(); ++i) { if (quantity_[i]) delete quantity_[i]; } quantity_.resize(source.size(),NULL); for (unsigned i = 0; i < source.size(); i++) { quantity_[i] = new SPAR_MAT(map_->size(),source[i]->nCols()); } for (unsigned i = 0; i < source.size(); i++) { for (int j = 0; j < source[i]->nRows(); j++) { int idx = map(j,0); if (idx > -1) { source[i]->row(j,_row_,_index_); for (int k = 0; k < _row_.size(); k++) { quantity_[i]->set(idx,_index_(k),_row_(k)); } } } } for (unsigned i = 0; i < source.size(); i++) { quantity_[i]->compress(); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class MappedDiagonalMatrix //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- MappedDiagonalMatrix::MappedDiagonalMatrix(ATC_Method * atc, DIAG_MAN * source, LargeToSmallAtomMap * map) : DiagonalMatrixTransfer<double>(), source_(source), map_(map) { source_->register_dependence(this); map_->register_dependence(this); } //-------------------------------------------------------- // Destructor //-------------------------------------------------------- MappedDiagonalMatrix::~MappedDiagonalMatrix() { source_->remove_dependence(this); map_->remove_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void MappedDiagonalMatrix::reset_quantity() const { const DIAG_MAT & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); quantity_.resize(map_->size(),map_->size()); for (int i = 0; i < source.nRows(); i++) { int idx = map(i,0); if (idx > -1) { quantity_(idx,idx) = source(i,i); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class MappedQuantity //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- MappedQuantity::MappedQuantity(ATC_Method * atc, DENS_MAN * source, LargeToSmallMap * map) : DenseMatrixTransfer<double>(), source_(source), map_(map) { source_->register_dependence(this); map_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void MappedQuantity::reset_quantity() const { const DENS_MAT & source(source_->quantity()); const INT_ARRAY & map(map_->quantity()); quantity_.resize(map_->size(),source.nCols()); for (int i = 0; i < source.nRows(); i++) { int idx = map(i,0); if (idx > -1) { for (int j = 0; j < source.nCols(); j++) { quantity_(idx,j) = source(i,j); } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class RegulatedNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- RegulatedNodes::RegulatedNodes(ATC_Coupling * atc, FieldName fieldName, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType) : SetTransfer<int>(), nodalGeometryType_(nodalGeometryType), atc_(atc), feEngine_(atc->fe_engine()), prescribedDataManager_(atc->prescribed_data_manager()) { if (!nodalGeometryType_) { nodalGeometryType_ = (atc_->interscale_manager()).dense_matrix_int("NodalGeometryType"); } if (nodalGeometryType_) { nodalGeometryType_->register_dependence(this); } else { throw ATC_Error("TransferLibrary::RegulatedNodes - No Nodal Geometry Type provided"); } const map<FieldName,int> & atcFieldSizes(atc_->field_sizes()); if (fieldName == NUM_TOTAL_FIELDS) { fieldSizes_ = atcFieldSizes; } else { map<FieldName,int>::const_iterator fs_iter = atcFieldSizes.find(fieldName); fieldSizes_.insert(pair<FieldName,int>(fieldName,fs_iter->second)); } } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void RegulatedNodes::reset_quantity() const { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); quantity_.clear(); for (int i = 0; i < nodeType.size(); ++i) { if (nodeType(i,0) != FE_ONLY) { quantity_.insert(i); } } } //-------------------------------------------------------- // insert_boundary_nodes //-------------------------------------------------------- void RegulatedNodes::insert_boundary_nodes() const { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); for (int i = 0; i < nodeType.size(); ++i) { if (nodeType(i,0) == BOUNDARY) { quantity_.insert(i); } } } //-------------------------------------------------------- // insert_boundary_faces //-------------------------------------------------------- void RegulatedNodes::insert_boundary_faces() const { const set<string> & boundaryFaceNames(atc_->boundary_face_names()); set<string>::const_iterator iter; set<int>::const_iterator nodeIter; for (iter = boundaryFaceNames.begin(); iter != boundaryFaceNames.end(); iter++) { set<int> nodeSet; feEngine_->fe_mesh()->faceset_to_nodeset(*iter,nodeSet); for (nodeIter = nodeSet.begin(); nodeIter != nodeSet.end(); nodeIter++) { quantity_.insert(*nodeIter); } } } //-------------------------------------------------------- // insert_fixed_nodes //-------------------------------------------------------- void RegulatedNodes::insert_fixed_nodes() const { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); map<FieldName,int>::const_iterator fs_iter; for (int i = 0; i < nodeType.size(); ++i) { bool isFixed = false; for (fs_iter = fieldSizes_.begin(); fs_iter != fieldSizes_.end(); fs_iter++) { for (int j = 0; j < fs_iter->second; j++) { isFixed = prescribedDataManager_->is_fixed(i,fs_iter->first,j); if (isFixed) break; } } if (isFixed && ((nodeType(i,0)==MD_ONLY) || (nodeType(i,0)==BOUNDARY))) { quantity_.insert(i); } } } //-------------------------------------------------------- // insert_face_fluxes //-------------------------------------------------------- void RegulatedNodes::insert_face_fluxes() const { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); set<int>::const_iterator inode; map<FieldName,int>::const_iterator fs_iter; for (fs_iter = fieldSizes_.begin(); fs_iter != fieldSizes_.end(); fs_iter++) { for (int j = 0; j < fs_iter->second; j++) { set<int> faceFluxNodes = prescribedDataManager_->flux_face_nodes(fs_iter->first,j); for (inode = faceFluxNodes.begin(); inode != faceFluxNodes.end(); inode++) { if (((nodeType(*inode,0)==MD_ONLY) || (nodeType(*inode,0)==BOUNDARY))) { quantity_.insert(*inode); } } } } } //-------------------------------------------------------- // insert_element_fluxes //-------------------------------------------------------- void RegulatedNodes::insert_element_fluxes() const { const INT_ARRAY & nodeType(nodalGeometryType_->quantity()); set<int>::const_iterator inode; map<FieldName,int>::const_iterator fs_iter; for (fs_iter = fieldSizes_.begin(); fs_iter != fieldSizes_.end(); fs_iter++) { for (int j = 0; j < fs_iter->second; j++) { set<int> elementFluxNodes = prescribedDataManager_->flux_element_nodes(fs_iter->first,j); for (inode = elementFluxNodes.begin(); inode != elementFluxNodes.end(); inode++) { if (((nodeType(*inode,0)==MD_ONLY) || (nodeType(*inode,0)==BOUNDARY))) { quantity_.insert(*inode); } } } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class BoundaryNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void BoundaryNodes::reset_quantity() const { quantity_.clear(); // a) they are a boundary node RegulatedNodes::insert_boundary_nodes(); // b) they are in a specified boundary faceset RegulatedNodes::insert_boundary_faces(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class FluxNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void FluxNodes::reset_quantity() const { quantity_.clear(); // a) they have a fixed face flux RegulatedNodes::insert_face_fluxes(); // b) they have a fixed element flux RegulatedNodes::insert_element_fluxes(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class FluxBoundaryNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void FluxBoundaryNodes::reset_quantity() const { FluxNodes::reset_quantity(); // a) they are a boundary node RegulatedNodes::insert_boundary_nodes(); // b) they are in a specified boundary faceset RegulatedNodes::insert_boundary_faces(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class AllRegulatedNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void AllRegulatedNodes::reset_quantity() const { FluxBoundaryNodes::reset_quantity(); // a) they are a fixed node RegulatedNodes::insert_fixed_nodes(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class FixedNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void FixedNodes::reset_quantity() const { quantity_.clear(); // a) they are a fixed node RegulatedNodes::insert_fixed_nodes(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class FixedBoundaryNodes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void FixedBoundaryNodes::reset_quantity() const { FixedNodes::reset_quantity(); // a) they are a boundary node RegulatedNodes::insert_boundary_nodes(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class DenseMatrixQuotient //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- DenseMatrixQuotient::DenseMatrixQuotient(DENS_MAN* matrixNumerator, DENS_MAN* matrixDenominator): matrixNumerator_(matrixNumerator), matrixDenominator_(matrixDenominator) { matrixNumerator_->register_dependence(this); matrixDenominator_->register_dependence(this); } //-------------------------------------------------------- // Reset_quantity //-------------------------------------------------------- void DenseMatrixQuotient::reset_quantity() const { quantity_ = matrixNumerator_->quantity(); quantity_.divide_zero_safe(matrixDenominator_->quantity()); } //-------------------------------------------------------- //-------------------------------------------------------- // Class DenseMatrixSum //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- DenseMatrixSum::DenseMatrixSum(DENS_MAN* matrixOne, DENS_MAN* matrixTwo): matrixOne_(matrixOne), matrixTwo_(matrixTwo) { matrixOne_->register_dependence(this); matrixTwo_->register_dependence(this); } //-------------------------------------------------------- // Reset_quantity //-------------------------------------------------------- void DenseMatrixSum::reset_quantity() const { quantity_ = matrixOne_->quantity(); quantity_ += (matrixTwo_->quantity()); } //-------------------------------------------------------- //-------------------------------------------------------- // Class DenseMatrixDelta //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- DenseMatrixDelta::DenseMatrixDelta(DENS_MAN* matrix, DENS_MAT* reference): matrix_(matrix), reference_(reference) { matrix_->register_dependence(this); } //-------------------------------------------------------- // Reset_quantity //-------------------------------------------------------- void DenseMatrixDelta::reset_quantity() const { quantity_ = matrix_->quantity(); quantity_ -= *reference_; } //-------------------------------------------------------- //-------------------------------------------------------- // Class PointToElementMap //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- PointToElementMap::PointToElementMap(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, double> * pointPositions) : DenseMatrixTransfer<int>(), pointPositions_(pointPositions), feMesh_((atc->fe_engine())->fe_mesh()) { pointPositions_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- PointToElementMap::~PointToElementMap() { pointPositions_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void PointToElementMap::reset_quantity() const { const DENS_MAT & position(pointPositions_->quantity()); int nsd = position.nCols(); int nRows = position.nRows(); quantity_.resize(nRows,nsd); DENS_VEC coords(nsd); for (int i = 0; i < nRows; i++) { for (int j = 0; j < nsd; j++) { coords(j) = position(i,j); } quantity_(i,0) = feMesh_->map_to_element(coords); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class Interpolant //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- Interpolant::Interpolant(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* pointToElementMap, DENS_MAN* pointPositions) : SparseMatrixTransfer<double>(), pointToElementMap_(pointToElementMap), pointPositions_(pointPositions), feEngine_(atc->fe_engine()) { pointToElementMap_->register_dependence(this); pointPositions_->register_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void Interpolant::reset_quantity() const { const DENS_MAT & positions(pointPositions_->quantity()); const INT_ARRAY & elements(pointToElementMap_->quantity()); if (positions.nRows() > 0) { feEngine_->evaluate_shape_functions(positions, elements, this->quantity_); } else { quantity_.resize(0,feEngine_->num_nodes()); } } //-------------------------------------------------------- //-------------------------------------------------------- // Class InterpolantGradient //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- InterpolantGradient::InterpolantGradient(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* pointToElementMap, DENS_MAN* pointPositions) : VectorTransfer<SPAR_MAT * >(), pointToElementMap_(pointToElementMap), pointPositions_(pointPositions), feEngine_(atc->fe_engine()) { pointToElementMap_->register_dependence(this); pointPositions_->register_dependence(this); quantity_.resize(atc->nsd(),NULL); for (int i = 0; i < atc->nsd(); ++i) { quantity_[i] = new SPAR_MAT(); } } //-------------------------------------------------------- // destructor //-------------------------------------------------------- InterpolantGradient::~InterpolantGradient() { pointToElementMap_->remove_dependence(this); pointPositions_->remove_dependence(this); for (unsigned i = 0; i < quantity_.size(); ++i) { if (quantity_[i]) delete quantity_[i]; } } //-------------------------------------------------------- // reset_quantity() //-------------------------------------------------------- void InterpolantGradient::reset_quantity() const { const DENS_MAT & positions(pointPositions_->quantity()); const INT_ARRAY & elements(pointToElementMap_->quantity()); if (positions.nRows() > 0) { feEngine_->evaluate_shape_function_derivatives(positions, elements, this->quantity_); } else { for (unsigned i = 0; i < quantity_.size(); ++i) { (this->quantity_)[i]->resize(0,feEngine_->num_nodes()); } } } //-------------------------------------------------------- // Class PerAtomShapeFunctionGradient //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- PerAtomShapeFunctionGradient::PerAtomShapeFunctionGradient(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* atomToElementMap, DENS_MAN* atomPositions, const string & tag, AtomType atomType) : VectorTransfer<SPAR_MAT * >(), atomToElementMap_(atomToElementMap), atomPositions_(atomPositions), feEngine_(atc->fe_engine()) { InterscaleManager & interscaleManager(atc->interscale_manager()); if (!atomToElementMap_) { atomToElementMap_ = interscaleManager.per_atom_int_quantity("AtomElement"); } if (!atomPositions_) { atomPositions_ = interscaleManager.per_atom_quantity("AtomicCoarseGrainingPositions"); } atomToElementMap_->register_dependence(this); atomPositions_->register_dependence(this); // storage container matrices_.resize(atc->nsd(),NULL); for (int i = 0; i < atc->nsd(); ++i) { matrices_[i] = new AtcAtomSparseMatrix<double>(atc,feEngine_->num_nodes(), feEngine_->num_nodes_per_element(), atomType); stringstream myint; myint << i; interscaleManager.add_per_atom_sparse_matrix(matrices_[i], tag+myint.str()); matrices_[i]->register_dependence(this); } quantity_.resize(atc->nsd(),NULL); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- PerAtomShapeFunctionGradient::~PerAtomShapeFunctionGradient() { atomToElementMap_->remove_dependence(this); atomPositions_->remove_dependence(this); for (unsigned i = 0; i < matrices_.size(); ++i) { matrices_[i]->remove_dependence(this); } } //-------------------------------------------------------- // reset_quantity() //-------------------------------------------------------- void PerAtomShapeFunctionGradient::reset_quantity() const { const DENS_MAT & positions(atomPositions_->quantity()); const INT_ARRAY & elements(atomToElementMap_->quantity()); for (unsigned i = 0; i < quantity_.size(); ++i) { (this->quantity_)[i] = & matrices_[i]->set_quantity(); } if (positions.nRows() > 0) { feEngine_->evaluate_shape_function_derivatives(positions, elements, this->quantity_); } else { for (unsigned i = 0; i < quantity_.size(); ++i) { (this->quantity_)[i]->resize(0,feEngine_->num_nodes()); } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class InterpolantSmallMolecule //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- InterpolantSmallMolecule::InterpolantSmallMolecule(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* moleculeToElementMap, DENS_MAN* moleculePositions, MoleculeSet * moleculeSet) : Interpolant(atc,moleculeToElementMap,moleculePositions), moleculeSet_(moleculeSet) { moleculeSet_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- InterpolantSmallMolecule::~InterpolantSmallMolecule() { moleculeSet_->remove_dependence(this); } //-------------------------------------------------------- // reset //-------------------------------------------------------- void InterpolantSmallMolecule::reset_quantity() const { Interpolant::reset_quantity(); // scale rows by fraction of molecules on this proc _fractions_.resize((this->quantity_).nRows()); for (int i = 0; i < moleculeSet_->local_molecule_count(); i++) _fractions_(i) = moleculeSet_->local_fraction(i); (this->quantity_).row_scale(_fractions_); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyKernelAccumulation //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyKernelAccumulation::OnTheFlyKernelAccumulation(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions): atc_(atc), source_(source), kernelFunction_(kernelFunction), atomCoarseGrainingPositions_(atomCoarseGrainingPositions), feMesh_((atc_->fe_engine())->fe_mesh()) { source_->register_dependence(this); atomCoarseGrainingPositions_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyKernelAccumulation::reset_quantity() const { const DENS_MAT & positions(atomCoarseGrainingPositions_->quantity()); const DENS_MAT & source(source_->quantity()); int nNodes = feMesh_->num_nodes_unique(); quantity_.resize(nNodes,source.nCols()); _quantityLocal_.reset(nNodes,source.nCols()); if (source.nRows()>0) { DENS_VEC xI(positions.nCols()),xa(positions.nCols()),xaI(positions.nCols()); double val; for (int i = 0; i < nNodes; i++) { xI = feMesh_->nodal_coordinates(i); for (int j = 0; j < positions.nRows(); j++) { for (int k = 0; k < positions.nCols(); ++k) { xa(k) = positions(j,k); } xaI = xa - xI; atc_->lammps_interface()->periodicity_correction(xaI.ptr()); val = kernelFunction_->value(xaI); if (val > 0) { for (int k=0; k < source.nCols(); k++) { _quantityLocal_(i,k) += val*source(j,k); } } } } } // accumulate across processors int count = quantity_.nRows()*quantity_.nCols(); lammpsInterface_->allsum(_quantityLocal_.ptr(),quantity_.ptr(),count); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyKernelAccumulationNormalized //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyKernelAccumulationNormalized::OnTheFlyKernelAccumulationNormalized(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN * weights): OnTheFlyKernelAccumulation(atc,source,kernelFunction,atomCoarseGrainingPositions), weights_(weights) { weights_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyKernelAccumulationNormalized::reset_quantity() const { OnTheFlyKernelAccumulation::reset_quantity(); quantity_ *= weights_->quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyKernelAccumulationNormalizedReferenced //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyKernelAccumulationNormalizedReferenced::OnTheFlyKernelAccumulationNormalizedReferenced(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, DENS_MAN * reference): OnTheFlyKernelAccumulationNormalized(atc,source,kernelFunction,atomCoarseGrainingPositions,weights), reference_(reference) { reference_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyKernelAccumulationNormalizedReferenced::reset_quantity() const { OnTheFlyKernelAccumulationNormalized::reset_quantity(); quantity_ -= reference_->quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyKernelAccumulationNormalizedScaled //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyKernelAccumulationNormalizedScaled::OnTheFlyKernelAccumulationNormalizedScaled(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, const double scale): OnTheFlyKernelAccumulationNormalized(atc,source,kernelFunction,atomCoarseGrainingPositions,weights), scale_(scale) { } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyKernelAccumulationNormalizedScaled::reset_quantity() const { OnTheFlyKernelAccumulationNormalized::reset_quantity(); quantity_ *= scale_; } //-------------------------------------------------------- //-------------------------------------------------------- // Class AccumulantWeights //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- AccumulantWeights::AccumulantWeights(SPAR_MAN* accumulant): DiagonalMatrixTransfer<double>(), accumulant_(accumulant) { accumulant_->register_dependence(this); } //-------------------------------------------------------- // Reset_quantity //-------------------------------------------------------- void AccumulantWeights::reset_quantity() const { const SPAR_MAT accumulant(accumulant_->quantity()); int nNodes = accumulant.nCols(); // get summation of atoms per node _localWeights_.reset(nNodes); _weights_.resize(nNodes); if (accumulant.nRows()>0) { _localWeights_ = (accumulant_->quantity()).col_sum(); } lammpsInterface_->allsum(_localWeights_.ptr(),_weights_.ptr(),nNodes); // assign weights quantity_.resize(nNodes,nNodes); for (int i = 0; i < nNodes; i++) { if (_weights_(i) > 0.) { quantity_(i,i) = 1./_weights_(i); } else { quantity_(i,i) = 0.; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyKernelWeights //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyKernelWeights::OnTheFlyKernelWeights(DENS_MAN* weights): DiagonalMatrixTransfer<double>(), weights_(weights) { weights_->register_dependence(this); } //-------------------------------------------------------- // Reset_quantity //-------------------------------------------------------- void OnTheFlyKernelWeights::reset_quantity() const { const DENS_MAT & weights(weights_->quantity()); int nNodes = weights.nRows(); // assign weights quantity_.resize(nNodes,nNodes); for (int i = 0; i < nNodes; i++) { if (weights(i,0) > 0.) { quantity_(i,i) = 1./weights(i,0); } else { quantity_(i,i) = 0.; } } } //-------------------------------------------------------- //-------------------------------------------------------- // Class KernelInverseVolumes //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- KernelInverseVolumes::KernelInverseVolumes(ATC_Method * atc, KernelFunction* kernelFunction): kernelFunction_(kernelFunction), feMesh_((atc->fe_engine())->fe_mesh()) { // do nothing } //-------------------------------------------------------- // multiplication by transpose //-------------------------------------------------------- void KernelInverseVolumes::reset_quantity() const { int nNodes = feMesh_->num_nodes_unique(); quantity_.resize(nNodes,nNodes); quantity_ = kernelFunction_->inv_vol(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyMeshAccumulation //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyMeshAccumulation::OnTheFlyMeshAccumulation(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions): atc_(atc), source_(source), atomCoarseGrainingPositions_(atomCoarseGrainingPositions), feMesh_((atc_->fe_engine())->fe_mesh()) { source_->register_dependence(this); atomCoarseGrainingPositions_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyMeshAccumulation::reset_quantity() const { const DENS_MAT & positions(atomCoarseGrainingPositions_->quantity()); const DENS_MAT & source(source_->quantity()); int nNodes = feMesh_->num_nodes_unique(); int nodesPerElement = feMesh_->num_nodes_per_element(); Array<int> node_list(nodesPerElement); DENS_VEC shp(nodesPerElement); quantity_.resize(nNodes,source.nCols()); _quantityLocal_.reset(nNodes,source.nCols()); DENS_VEC xj(atc_->nsd()); if (source.nRows()>0) { for (int j = 0; j < source.nRows(); j++) { for (int k = 0; k < atc_->nsd(); k++) { xj(k) = positions(j,k); } feMesh_->shape_functions(xj,shp,node_list); for (int I = 0; I < nodesPerElement; I++) { int inode = node_list(I); for (int k = 0; k < source.nCols(); k++) { //quantity_(inode,k) += shp(I)*source(j,k); _quantityLocal_(inode,k) += shp(I)*source(j,k); } } } } // accumulate across processors int count = quantity_.nRows()*quantity_.nCols(); lammpsInterface_->allsum(_quantityLocal_.ptr(),quantity_.ptr(),count); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyMeshAccumulationNormalized //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyMeshAccumulationNormalized::OnTheFlyMeshAccumulationNormalized(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN * weights): OnTheFlyMeshAccumulation(atc,source,atomCoarseGrainingPositions), weights_(weights) { weights_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyMeshAccumulationNormalized::reset_quantity() const { OnTheFlyMeshAccumulation::reset_quantity(); quantity_ *= weights_->quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyMeshAccumulationNormalizedReferenced //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyMeshAccumulationNormalizedReferenced::OnTheFlyMeshAccumulationNormalizedReferenced(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, DENS_MAN * reference): OnTheFlyMeshAccumulationNormalized(atc,source,atomCoarseGrainingPositions,weights), reference_(reference) { reference_->register_dependence(this); } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyMeshAccumulationNormalizedReferenced::reset_quantity() const { OnTheFlyMeshAccumulationNormalized::reset_quantity(); quantity_ -= reference_->quantity(); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyMeshAccumulationNormalizedScaled //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyMeshAccumulationNormalizedScaled::OnTheFlyMeshAccumulationNormalizedScaled(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, const double scale): OnTheFlyMeshAccumulationNormalized(atc,source,atomCoarseGrainingPositions,weights), scale_(scale) { } //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyMeshAccumulationNormalizedScaled::reset_quantity() const { OnTheFlyMeshAccumulationNormalized::reset_quantity(); quantity_ *= scale_; } //-------------------------------------------------------- //-------------------------------------------------------- // Class NativeShapeFunctionGradient //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // constructor //-------------------------------------------------------- NativeShapeFunctionGradient::NativeShapeFunctionGradient(ATC_Method * atc) : VectorTransfer<SPAR_MAT * >(), feEngine_(atc->fe_engine()) { quantity_.resize(atc->nsd(),NULL); for (int i = 0; i < atc->nsd(); ++i) { quantity_[i] = new SPAR_MAT(); } } //-------------------------------------------------------- // destructor //-------------------------------------------------------- NativeShapeFunctionGradient::~NativeShapeFunctionGradient() { for (unsigned i = 0; i < quantity_.size(); ++i) { if (quantity_[i]) delete quantity_[i]; } } //-------------------------------------------------------- // reset_quantity() //-------------------------------------------------------- void NativeShapeFunctionGradient::reset_quantity() const { feEngine_->compute_gradient_matrix(quantity_); } //-------------------------------------------------------- //-------------------------------------------------------- // Class OnTheFlyShapeFunctionProlongation //-------------------------------------------------------- //-------------------------------------------------------- //-------------------------------------------------------- // Constructor //-------------------------------------------------------- OnTheFlyShapeFunctionProlongation::OnTheFlyShapeFunctionProlongation(ATC_Method * atc, DENS_MAN * source, DENS_MAN * atomCoarseGrainingPositions): FeToAtomTransfer(atc,source), atomCoarseGrainingPositions_(atomCoarseGrainingPositions), feMesh_((atc->fe_engine())->fe_mesh()) { atomCoarseGrainingPositions_->register_dependence(this); } //-------------------------------------------------------- // destructor //-------------------------------------------------------- OnTheFlyShapeFunctionProlongation::~OnTheFlyShapeFunctionProlongation() { atomCoarseGrainingPositions_->remove_dependence(this); }; //-------------------------------------------------------- // reset_quantity //-------------------------------------------------------- void OnTheFlyShapeFunctionProlongation::reset() const { if (need_reset()) { PerAtomQuantity<double>::reset(); const DENS_MAT & positions(atomCoarseGrainingPositions_->quantity()); const DENS_MAT & source(source_->quantity()); int nodesPerElement = feMesh_->num_nodes_per_element(); Array<int> node_list(nodesPerElement); DENS_VEC shp(nodesPerElement); DENS_VEC xj(positions.nCols()); int nLocal = positions.nRows(); quantity_ = 0.; for (int j = 0; j < nLocal; j++) { for (int k = 0; k < source.nCols(); k++) { xj(k) = positions(j,k); } feMesh_->shape_functions(xj,shp,node_list); for (int I = 0; I < nodesPerElement; I++) { int inode = node_list(I); for (int k = 0; k < source.nCols(); k++) { quantity_(j,k) += shp(I)*source(inode,k); } } } } } } diff --git a/lib/atc/TransferLibrary.h b/lib/atc/TransferLibrary.h index 380cb6dcf..3a0f420f3 100644 --- a/lib/atc/TransferLibrary.h +++ b/lib/atc/TransferLibrary.h @@ -1,1724 +1,1724 @@ // a library for the various transfer operators used in ATC #ifndef TRANSFER_LIBRARY_H #define TRANSFER_LIBRARY_H #include <string> #include <map> #include <vector> // ATC_Method headers #include "TransferOperator.h" namespace ATC { // forward declarations class ATC_Method; class ATC_Coupling; class FE_Engine; class FE_Mesh; class PrescribedDataManager; class LargeToSmallAtomMap; class MoleculeSet; /** * @class NodalAtomVolume * @brief Computes the nodal volumes which coarse grain the volume per atom */ class NodalAtomVolume : public DenseMatrixTransfer<double> { public: // constructor NodalAtomVolume(ATC_Method * atc, SPAR_MAN * shapeFunction); // destructor virtual ~NodalAtomVolume() {shapeFunction_->remove_dependence(this);}; /** apply transfer operator */ virtual void reset_quantity() const; /** overload function to get the number rows */ virtual int nRows() const; /** overload function to get the number columns */ virtual int nCols() const; protected: /** applies the needed averaging operation to get the appropriate average volume */ virtual double averaging_operation(const double totalVolume) const; /** pointer to atc method object */ const ATC_Method * atc_; /** shape function matrix used to base volumes on */ SPAR_MAN * shapeFunction_; /** pointer to lammps interface */ const LammpsInterface * lammpsInterface_; /** pointer to the FE engine */ const FE_Engine * feEngine_; /** tolerance used in the linear solver */ const double tol_; // workspace variables mutable DIAG_MAT _rhsMatrix_; mutable DENS_VEC _rhs_; mutable DENS_VEC _scale_; private: // do not define NodalAtomVolume(); }; /** * @class NodalVolume * @brief Computes the nodal volumes associated with the support of each nodal shape function */ class NodalVolume : public NodalAtomVolume { public: // constructor NodalVolume(ATC_Method * atc, SPAR_MAN * shapeFunction) : NodalAtomVolume(atc,shapeFunction) {}; // destructor virtual ~NodalVolume() {}; protected: /** applies the needed averaging operation to get the appropriate average volume */ virtual double averaging_operation(const double totalVolume) const; private: // do not define NodalVolume(); }; /** * @class NodalAtomVolumeElement * @brief Computes the nodal volumes which coarse grain the volume per atom based on element volumes */ class NodalAtomVolumeElement : public DenseMatrixTransfer<double> { public: // constructor NodalAtomVolumeElement(ATC_Method * atc, SPAR_MAN * shapeFunction, PerAtomQuantity<int> * atomElement=NULL); // destructor virtual ~NodalAtomVolumeElement() { shapeFunction_->remove_dependence(this); atomElement_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; /** overload function to get the number rows */ virtual int nRows() const; /** overload function to get the number columns */ virtual int nCols() const; protected: /** pointer to atc method object */ ATC_Method * atc_; /** shape function matrix used to base volumes on */ SPAR_MAN * shapeFunction_; /** object containing the elements associated with each atom */ PerAtomQuantity<int> * atomElement_; /** pointer to the FE engine */ const FE_Engine * feEngine_; /** tolerance used in the linear solver */ const double tol_; // workspace variables mutable DIAG_MAT _nodeVolumesMatrix_; mutable DENS_VEC _nodeVolumes_; mutable DENS_VEC _nodeElementVolumes_; mutable DENS_MAT _nodalCoords_; mutable DENS_VEC _scale_; private: // do not define NodalAtomVolumeElement(); }; /** * @class AtomTypeElement * @brief determines if the given atom type is in each element */ class AtomTypeElement : public DenseMatrixTransfer<int> { public: // constructor AtomTypeElement(ATC_Coupling * atc, PerAtomQuantity<int> * atomElement = NULL); // destructor virtual ~AtomTypeElement() { atomElement_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** map from atom to the element in which it resides */ PerAtomQuantity<int> * atomElement_; /** number of element */ int nElts_; // workspace mutable INT_ARRAY _quantityLocal_; private: // do not define AtomTypeElement(); }; /** * @class ElementMask * @brief determines which elements should be used for FE quadrature */ class ElementMask : public DenseMatrixTransfer<bool> { public: // constructor ElementMask(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal = NULL, MatrixDependencyManager<DenseMatrix, int> * hasGhost = NULL); // destructor virtual ~ElementMask() { hasInternal_->remove_dependence(this); if (hasGhost_) hasGhost_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** shape function matrix over the internal atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasInternal_; /** shape function matrix over the ghost atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasGhost_; /** finite element engine */ const FE_Engine * feEngine_; private: // do not define ElementMask(); }; /** * @class AtomElementMask * @brief determines which elements should be used for FE quadrature based on presence of atoms */ class AtomElementMask : public DenseMatrixTransfer<bool> { public: // constructor AtomElementMask(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasAtoms = NULL); // destructor virtual ~AtomElementMask() { hasAtoms_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** shape function matrix over the atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasAtoms_; /** finite element engine */ const FE_Engine * feEngine_; private: // do not define AtomElementMask(); }; /** * @class NodalGeometryType * @brief Computes the computational geometry associated with each node with respect to the FE and MD regions */ class NodalGeometryType : public DenseMatrixTransfer<int> { public: // constructor NodalGeometryType(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal = NULL, MatrixDependencyManager<DenseMatrix, int> * hasGhost = NULL); // destructor virtual ~NodalGeometryType() { hasInternal_->remove_dependence(this); if (hasGhost_) hasGhost_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** shape function matrix over the internal atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasInternal_; /** shape function matrix over the ghost atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasGhost_; /** finite element engine */ const FE_Engine * feEngine_; /** number of nodes in the mesh */ int nNodes_; /** number of elements in the mesh */ int nElts_; // workspace // ints so they can be shared through MPI mutable Array<int> _nodesInternal_, _nodesGhost_; private: // do not define NodalGeometryType(); }; /** * @class NodalGeometryTypeElementSet * @brief Divdes nodes into MD, FE, and boundary based on if they are connected to nodes in internal and not internal */ class NodalGeometryTypeElementSet : public DenseMatrixTransfer<int> { public: // constructor NodalGeometryTypeElementSet(ATC_Coupling * atc, MatrixDependencyManager<DenseMatrix, int> * hasInternal = NULL); // destructor virtual ~NodalGeometryTypeElementSet() { hasInternal_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** shape function matrix over the internal atoms used to help define the geometry */ MatrixDependencyManager<DenseMatrix, int> * hasInternal_; /** finite element engine */ const FE_Engine * feEngine_; /** number of nodes in the mesh */ int nNodes_; /** number of elements in the mesh */ int nElts_; // workspace // ints so they can be shared through MPI mutable Array<int> _nodesInternal_, _nodesGhost_; private: // do not define NodalGeometryTypeElementSet(); }; /** * @class LargeToSmallMap * @brief mapping from a larger set to a smaller set */ class LargeToSmallMap : public DenseMatrixTransfer<int> { public: // constructor LargeToSmallMap() : size_(0) {}; // destructor virtual ~LargeToSmallMap() {}; /** get the number of elements associated with the map */ virtual int size() const {this->quantity(); return size_;}; protected: /** number of nodes in the map */ mutable int size_; }; /** * @class NodeToSubset * @brief mapping from all nodes to a subset */ class NodeToSubset : public LargeToSmallMap { public: // constructor NodeToSubset(ATC_Method * atc, SetDependencyManager<int> * subsetNodes); // destructor virtual ~NodeToSubset() { subsetNodes_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** pointer to atc to get the number of nodes */ const ATC_Method * atc_; /** map from atom to the element in which it resides */ SetDependencyManager<int> * subsetNodes_; private: // do not define NodeToSubset(); }; /** * @class SubsetToNode * @brief mapping from a subset of nodes to all nodes */ class SubsetToNode : public DenseMatrixTransfer<int> { public: // constructor SubsetToNode(NodeToSubset * nodeToSubset); // destructor virtual ~SubsetToNode() { nodeToSubset_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** map from atom to the element in which it resides */ NodeToSubset * nodeToSubset_; private: // do not define SubsetToNode(); }; /** * @class ReducedSparseMatrix * @brief reduction of a sparse matrix to only those columns consistent with the map */ class ReducedSparseMatrix : public SparseMatrixTransfer<double> { public: // constructor ReducedSparseMatrix(ATC_Method * atc, SPAR_MAN * source, LargeToSmallAtomMap * map); // destructor virtual ~ReducedSparseMatrix(); /** apply transfer operator */ virtual void reset_quantity() const; protected: /** original quantity */ SPAR_MAN * source_; /** mapping transfer */ LargeToSmallAtomMap * map_; // workspace mutable DenseVector<double> _row_; // vector storing the row values of a matrix mutable DenseVector<INDEX> _index_; // vector storing the column indices of the values private: // do not define ReducedSparseMatrix(); }; /** * @class RowMappedSparseMatrix * @brief mapping of rows from a sparse matrix to a new sparse matrix */ class RowMappedSparseMatrix : public ReducedSparseMatrix { public: // constructor RowMappedSparseMatrix(ATC_Method * atc, SPAR_MAN * source, LargeToSmallAtomMap * map) : ReducedSparseMatrix(atc,source,map) {}; // destructor virtual ~RowMappedSparseMatrix() {}; /** apply transfer operator */ virtual void reset_quantity() const; protected: private: // do not define RowMappedSparseMatrix(); }; /** * @class RowMappedSparseMatrixVector * @brief mapping of rows from a vector sparse matrices to a new vector of sparse matrices */ class RowMappedSparseMatrixVector : public VectorTransfer<SPAR_MAT * > { public: // constructor RowMappedSparseMatrixVector(ATC_Method * atc, VectorDependencyManager<SPAR_MAT * > * source, LargeToSmallAtomMap * map); // destructor virtual ~RowMappedSparseMatrixVector(); /** apply transfer operator */ virtual void reset_quantity() const; protected: /** original quantity */ VectorDependencyManager<SPAR_MAT * > * source_; /** mapping transfer */ LargeToSmallAtomMap * map_; // workspace mutable DenseVector<double> _row_; // vector storing the row values of a matrix mutable DenseVector<INDEX> _index_; // vector storing the column indices of the values private: // do not define RowMappedSparseMatrixVector(); }; /** * @class MappedDiagonalMatrix * @brief mapping of a diagonal matrix to a new diagronal matrix */ class MappedDiagonalMatrix : public DiagonalMatrixTransfer<double> { public: // constructor MappedDiagonalMatrix(ATC_Method * atc, DIAG_MAN * source, LargeToSmallAtomMap * map); // destructor virtual ~MappedDiagonalMatrix(); /** apply transfer operator */ virtual void reset_quantity() const; protected: /** original quantity */ DIAG_MAN * source_; /** mapping transfer */ LargeToSmallAtomMap * map_; private: // do not define MappedDiagonalMatrix(); }; /** * @class MappedQuantity * @brief generic reduced mapping */ class MappedQuantity : public DenseMatrixTransfer<double> { public: // constructor MappedQuantity(ATC_Method * atc, DENS_MAN * source, LargeToSmallMap * map); // destructor virtual ~MappedQuantity() { source_->remove_dependence(this); map_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** original quantity */ DENS_MAN * source_; /** mapping transfer */ LargeToSmallMap * map_; private: // do not define MappedQuantity(); }; /** * @class RegulatedNodes * @brief set of all nodes being controlled by an atomic regulator */ class RegulatedNodes : public SetTransfer<int> { public: // constructor RegulatedNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL); // destructor virtual ~RegulatedNodes() { nodalGeometryType_->remove_dependence(this); }; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; /** map from fields to their sizes */ std::map<FieldName,int> fieldSizes_; /** map from atom to element in which it resides */ MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType_; /** pointer to atc object */ ATC_Coupling * atc_; /** pointer to the finite element engine */ const FE_Engine * feEngine_; /** pointer to the prescribed data manager */ const PrescribedDataManager * prescribedDataManager_; // methods /** adds boundary nodes */ void insert_boundary_nodes() const; /** adds nodes associated with boundary facesets */ void insert_boundary_faces() const; /** adds fixed nodes */ void insert_fixed_nodes() const; /** adds nodes associated with face fluxes */ void insert_face_fluxes() const; /** adds nodes associated with element fluxes */ void insert_element_fluxes() const; private: // do not define RegulatedNodes(); }; /** * @class FluxNodes * @brief set of all nodes being controlled by an atomic regulator for fluxes */ class FluxNodes : public RegulatedNodes { public: // constructor FluxNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : RegulatedNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~FluxNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define FluxNodes(); }; /** * @class BoundaryNodes * @brief set of all nodes associated with the FE/MD boundary */ class BoundaryNodes : public RegulatedNodes { public: // constructor BoundaryNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : RegulatedNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~BoundaryNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define BoundaryNodes(); }; /** * @class FluxBoundaryNodes * @brief set of all nodes being controlled by an atomic regulator for fluxes */ class FluxBoundaryNodes : public FluxNodes { public: // constructor FluxBoundaryNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : FluxNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~FluxBoundaryNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define FluxBoundaryNodes(); }; /** * @class AllRegulatedNodes * @brief set of all nodes being controlled by an atomic regulator when localization is used */ class AllRegulatedNodes : public FluxBoundaryNodes { public: // constructor AllRegulatedNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : FluxBoundaryNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~AllRegulatedNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define AllRegulatedNodes(); }; /** * @class FixedNodes * @brief set of all nodes being controlled by an atomic regulator which are fixed */ class FixedNodes : public RegulatedNodes { public: // constructor FixedNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : RegulatedNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~FixedNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define FixedNodes(); }; /** * @class FixedBoundaryNodes * @brief set of all nodes being controlled by an atomic regulator which are fixed, including for coupling */ class FixedBoundaryNodes : public FixedNodes { public: // constructor FixedBoundaryNodes(ATC_Coupling * atc, FieldName fieldName = NUM_TOTAL_FIELDS, MatrixDependencyManager<DenseMatrix, int> * nodalGeometryType = NULL) : FixedNodes(atc,fieldName,nodalGeometryType) {}; // destructor virtual ~FixedBoundaryNodes() {}; protected: /** recomputes the set of regulated nodes based on prescribed data and atom locations */ virtual void reset_quantity() const; private: // do not define FixedBoundaryNodes(); }; /** * @class ElementMaskNodeSet * @brief determines which elements should be used for FE quadrature based on them possessing certain nodes */ class ElementMaskNodeSet : public DenseMatrixTransfer<bool> { public: // constructor ElementMaskNodeSet(ATC_Coupling * atc, - RegulatedNodes * nodeSet); + SetDependencyManager<int> * nodeSet); // destructor virtual ~ElementMaskNodeSet() { nodeSet_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** transfer determining used nodes */ - RegulatedNodes * nodeSet_; + SetDependencyManager<int> * nodeSet_; /** finite element mesh */ const FE_Mesh * feMesh_; private: // do not define ElementMaskNodeSet(); }; /** * @class PointToElementMap * @brief Class for identifying the element associated with a given point */ class PointToElementMap : public DenseMatrixTransfer<int> { public: // constructor PointToElementMap(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, double> * pointPositions); // destructor virtual ~PointToElementMap(); protected: /** resets the data if necessary */ virtual void reset_quantity() const; /** point positions */ MatrixDependencyManager<DenseMatrix, double> * pointPositions_; /** pointer to finite element mesh */ const FE_Mesh * feMesh_; private: // do not define PointToElementMap(); }; /** * @class Interpolant * @brief constructs the spatial values of the shape functions */ class Interpolant : public SparseMatrixTransfer<double> { public: // constructor Interpolant(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* pointToElementMap, DENS_MAN* pointPositions); // destructor virtual ~Interpolant() { pointToElementMap_->remove_dependence(this); pointPositions_->remove_dependence(this); }; protected: /** does the actual computation of the quantity */ virtual void reset_quantity() const; protected: /** map from point coordinates to element */ MatrixDependencyManager<DenseMatrix, int>* pointToElementMap_; /** coordinates used to evaluate shape functions */ DENS_MAN* pointPositions_; /** pointer to the FE engine */ const FE_Engine * feEngine_; private: // do not define Interpolant(); }; /** * @class InterpolantGradient * @brief constructs the spatial derivatives of the shape functions */ class InterpolantGradient : public VectorTransfer<SPAR_MAT * > { public: // constructor InterpolantGradient(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* pointToElementMap, DENS_MAN* pointPositions); // destructor virtual ~InterpolantGradient(); protected: /** does the actual computation of the quantity */ virtual void reset_quantity() const; protected: /** map from point coordinates to element */ MatrixDependencyManager<DenseMatrix, int>* pointToElementMap_; /** coordinates used to evaluate shape functions */ DENS_MAN* pointPositions_; /** pointer to the FE engine */ const FE_Engine * feEngine_; private: // do not define InterpolantGradient(); }; /** * @class PerAtomShapeFunctionGradient * @brief constructs the spatial derivatives of the shape functions at each atom */ class PerAtomShapeFunctionGradient : public VectorTransfer<SPAR_MAT * > { public: // constructor PerAtomShapeFunctionGradient(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* atomToElementMap = NULL, DENS_MAN* atomPositions = NULL, const std::string & tag = "AtomicShapeFunctionGradient", AtomType atomType = INTERNAL); // destructor virtual ~PerAtomShapeFunctionGradient(); protected: /** does the actual computation of the quantity */ virtual void reset_quantity() const; protected: /** map from point coordinates to element */ MatrixDependencyManager<DenseMatrix, int>* atomToElementMap_; /** coordinates used to evaluate shape functions */ DENS_MAN* atomPositions_; /** pointer to the FE engine */ const FE_Engine * feEngine_; /** container for dependency managers */ std::vector<AtcAtomSparseMatrix<double> * > matrices_; private: // do not define PerAtomShapeFunctionGradient(); }; /** * @class InterpolantSmallMolecule * @brief constructs the spatial values of the shape functions for small molecules */ class InterpolantSmallMolecule : public Interpolant { public: // constructor InterpolantSmallMolecule(ATC_Method * atc, MatrixDependencyManager<DenseMatrix, int>* moleculeToElementMap, DENS_MAN* moleculePositions, MoleculeSet * moleculeSet); // destructor virtual ~InterpolantSmallMolecule(); protected: /** does the actual computation of the quantity */ virtual void reset_quantity() const; protected: /** pointer to atc base class */ MoleculeSet * moleculeSet_; // workspace mutable DENS_VEC _fractions_; private: // do not define InterpolantSmallMolecule(); }; /** * @class DenseMatrixQuotient * @brief quotient of two matrices */ class DenseMatrixQuotient : public DenseMatrixTransfer<double> { public: // constructor DenseMatrixQuotient(DENS_MAN * matrixNumerator, DENS_MAN * matrixDenominator); // destructor virtual ~DenseMatrixQuotient() { matrixNumerator_->remove_dependence(this); matrixDenominator_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; /** get number of columns */ virtual int nCols() const {return matrixNumerator_->nCols();}; protected: DENS_MAN* matrixNumerator_; DENS_MAN* matrixDenominator_; private: // do not define DenseMatrixQuotient(); }; /** * @class DenseMatrixSum * @brief sum of two matrices */ class DenseMatrixSum : public DenseMatrixTransfer<double> { public: // constructor DenseMatrixSum(DENS_MAN * matrixOne, DENS_MAN * matrixTwo); // destructor virtual ~DenseMatrixSum() { matrixOne_->remove_dependence(this); matrixTwo_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; /** get number of columns */ virtual int nCols() const {return matrixOne_->nCols();}; protected: DENS_MAN* matrixOne_; DENS_MAN* matrixTwo_; private: // do not define DenseMatrixSum(); }; /** * @class DenseMatrixDelta * @brief change relative to a reference value */ class DenseMatrixDelta : public DenseMatrixTransfer<double> { public: // constructor DenseMatrixDelta(DENS_MAN * current, DENS_MAT * reference); // destructor virtual ~DenseMatrixDelta() { matrix_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: DENS_MAN* matrix_; DENS_MAT* reference_; private: // do not define DenseMatrixDelta(); }; /** * @class OnTheFlyKernelAccumulation * @brief implements the accumulant on the fly */ class OnTheFlyKernelAccumulation : public DenseMatrixTransfer<double> { public: // constructor OnTheFlyKernelAccumulation(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions); // destructor virtual ~OnTheFlyKernelAccumulation() { source_->remove_dependence(this); atomCoarseGrainingPositions_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: /** pointer to atc base class */ const ATC_Method * atc_; /** pointer to source data */ PerAtomQuantity<double> * source_; /** map from atom coordinates to element */ KernelFunction* kernelFunction_; /** atomic coordinates used for coarse graining operations */ DENS_MAN* atomCoarseGrainingPositions_; /** access to mesh */ const FE_Mesh * feMesh_; // workspace mutable DENS_MAT _quantityLocal_; private: // do not define OnTheFlyKernelAccumulation(); }; /** * @class OnTheFlyKernelAccumulationNormalized * @brief implements a normalized accumulant on the fly */ class OnTheFlyKernelAccumulationNormalized : public OnTheFlyKernelAccumulation { public: // constructor OnTheFlyKernelAccumulationNormalized(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights); // destructor virtual ~OnTheFlyKernelAccumulationNormalized() { weights_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: DIAG_MAN * weights_; private: // do not define OnTheFlyKernelAccumulationNormalized(); }; /** * @class OnTheFlyKernelAccumulationNormalizedReferenced * @brief implements a normalized referenced accumulant on the fly */ class OnTheFlyKernelAccumulationNormalizedReferenced : public OnTheFlyKernelAccumulationNormalized { public: // constructor OnTheFlyKernelAccumulationNormalizedReferenced(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, DENS_MAN * reference); // destructor virtual ~OnTheFlyKernelAccumulationNormalizedReferenced() { reference_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: /** reference value */ DENS_MAN * reference_; private: // do not define OnTheFlyKernelAccumulationNormalizedReferenced(); }; /** * @class OnTheFlyKernelNormalizedAccumulationScaled * @brief implements a scaled accumulant on the fly */ class OnTheFlyKernelAccumulationNormalizedScaled : public OnTheFlyKernelAccumulationNormalized { public: // constructor OnTheFlyKernelAccumulationNormalizedScaled(ATC_Method * atc, PerAtomQuantity<double> * source, KernelFunction* kernelFunction, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, const double scale); // destructor virtual ~OnTheFlyKernelAccumulationNormalizedScaled() { atomCoarseGrainingPositions_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: const double scale_; private: // do not define OnTheFlyKernelAccumulationNormalizedScaled(); }; /** * @class AccumulantWeights * @brief weights for kernel function accumulants */ class AccumulantWeights : public DiagonalMatrixTransfer<double> { public: // constructor AccumulantWeights(SPAR_MAN * accumulant); // destructor virtual ~AccumulantWeights() { accumulant_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: SPAR_MAN* accumulant_; // workspace mutable DENS_VEC _localWeights_; mutable DENS_VEC _weights_; private: // do not define AccumulantWeights(); }; /** * @class OnTheFlyKernelWeights * @brief weights for on-the-fly kernel function */ class OnTheFlyKernelWeights : public DiagonalMatrixTransfer<double> { public: // constructor OnTheFlyKernelWeights(DENS_MAN * weights); // destructor virtual ~OnTheFlyKernelWeights() { weights_->remove_dependence(this); }; /** apply transfer operator */ virtual void reset_quantity() const; protected: DENS_MAN* weights_; private: // do not define OnTheFlyKernelWeights(); }; /** * @class KernelInverseVolumes * @brief inverse volumes for kernel function accumulants */ class KernelInverseVolumes : public DiagonalMatrixTransfer<double> { public: // constructor KernelInverseVolumes(ATC_Method * atc, KernelFunction* kernelFunction); // destructor virtual ~KernelInverseVolumes() {}; /** apply transfer operator */ virtual void reset_quantity() const; protected: /** kernel function being used */ KernelFunction* kernelFunction_; /** access to mesh */ const FE_Mesh * feMesh_; private: // do not define KernelInverseVolumes(); }; /** * @class OnTheFlyMeshAccumulation * @brief implements the mesh-based accumulant on the fly */ class OnTheFlyMeshAccumulation : public DenseMatrixTransfer<double> { public: // constructor OnTheFlyMeshAccumulation(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions); // destructor virtual ~OnTheFlyMeshAccumulation() { source_->remove_dependence(this); atomCoarseGrainingPositions_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; /** access nCols_ */ virtual int nCols() const { return source_->nCols(); } protected: /** pointer to atc base class */ const ATC_Method * atc_; /** pointer to source data */ PerAtomQuantity<double> * source_; /** atomic coordinates used for coarse graining operations */ DENS_MAN* atomCoarseGrainingPositions_; /** access to mesh */ const FE_Mesh * feMesh_; // workspace mutable DENS_MAT _quantityLocal_; private: // do not define OnTheFlyMeshAccumulation(); }; /** * @class OnTheFlyMeshAccumulationNormalized * @brief implements a normalized mesh-based accumulant on the fly */ class OnTheFlyMeshAccumulationNormalized : public OnTheFlyMeshAccumulation { public: // constructor OnTheFlyMeshAccumulationNormalized(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights); // destructor virtual ~OnTheFlyMeshAccumulationNormalized() { weights_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: DIAG_MAN * weights_; private: // do not define OnTheFlyMeshAccumulationNormalized(); }; /** * @class OnTheFlyMeshAccumulationNormalizedReferenced * @brief implements a normalized referenced mesh-based accumulant on the fly */ class OnTheFlyMeshAccumulationNormalizedReferenced : public OnTheFlyMeshAccumulationNormalized { public: // constructor OnTheFlyMeshAccumulationNormalizedReferenced(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, DENS_MAN * reference); // destructor virtual ~OnTheFlyMeshAccumulationNormalizedReferenced() { reference_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: /** reference value */ DENS_MAN * reference_; private: // do not define OnTheFlyMeshAccumulationNormalizedReferenced(); }; /** * @class OnTheFlyMeshAccumulationNormalizedScaled * @brief implements a scaled mesh-based accumulant on the fly */ class OnTheFlyMeshAccumulationNormalizedScaled : public OnTheFlyMeshAccumulationNormalized { public: // constructor OnTheFlyMeshAccumulationNormalizedScaled(ATC_Method * atc, PerAtomQuantity<double> * source, DENS_MAN* atomCoarseGrainingPositions, DIAG_MAN* weights, const double scale); // destructor virtual ~OnTheFlyMeshAccumulationNormalizedScaled() { atomCoarseGrainingPositions_->remove_dependence(this); }; /** do nothing */ virtual void reset_quantity() const; protected: const double scale_; private: // do not define OnTheFlyMeshAccumulationNormalizedScaled(); }; /** * @class NativeShapeFunctionGradient * @brief constructs the spatial derivatives of the shape functions */ class NativeShapeFunctionGradient : public VectorTransfer<SPAR_MAT * > { public: // constructor NativeShapeFunctionGradient(ATC_Method * atc); // destructor virtual ~NativeShapeFunctionGradient(); protected: /** does the actual computation of the quantity */ virtual void reset_quantity() const; protected: /** pointer to the FE engine */ const FE_Engine * feEngine_; private: // do not define NativeShapeFunctionGradient(); }; /** * @class OnTheFlyShapeFunctionProlongation * @brief implements the interpolant on the fly */ class OnTheFlyShapeFunctionProlongation : public FeToAtomTransfer { public: // constructor OnTheFlyShapeFunctionProlongation(ATC_Method * atc, DENS_MAN * source, DENS_MAN * atomCoarseGrainingPositions); // destructor virtual ~OnTheFlyShapeFunctionProlongation(); /** do nothing */ virtual void reset() const; protected: /** pointer to atc base class */ //const ATC_Method * atc_; /** pointer to source data */ //DENS_MAN * source_; /** atomic coordinates used for coarse graining operations */ DENS_MAN* atomCoarseGrainingPositions_; /** access to mesh */ const FE_Mesh * feMesh_; // workspace //mutable DENS_MAT _quantityLocal_; private: // do not define OnTheFlyShapeFunctionProlongation(); }; } #endif diff --git a/lib/atc/Utility.h b/lib/atc/Utility.h index aa5104293..f3bbd7d1f 100644 --- a/lib/atc/Utility.h +++ b/lib/atc/Utility.h @@ -1,305 +1,334 @@ #ifndef UTILITY_H #define UTILITY_H #include <iostream> #include <ctime> #include <limits> #undef near #include <vector> #include <fstream> #include <cstring> #include <string> #include <sstream> #include <iomanip> #include <algorithm> #include "math.h" #include "ATC_Error.h" namespace ATC_Utility { /** constants */ - static const double Pi = 4.0*atan(1.0); - static const double Big = 1.e20; - const static double parsetol = 1.0e-10; - const static double parsebig = 1.0e10; + static const double Pi_ = 4.0*atan(1.0); + static const double Big_ = 1.e20; + const static double parsetol_ = 1.0e-8; + //const static double parsetol_ = 1.0e-10; + const static double parsebig_ = 1.0e10; /** scalar triple product */ inline double det3(double * v1, double * v2, double * v3) { return -v1[2]*v2[1]*v3[0] + v1[1]*v2[2]*v3[0] + v1[2]*v2[0]*v3[1] -v1[0]*v2[2]*v3[1] - v1[1]*v2[0]*v3[2] + v1[0]*v2[1]*v3[2]; } inline void plane_coords(int i, int & i1, int & i2) { if (i==0) { i1 = 1; i2 = 2; } else if (i==1) { i1 = 2; i2 = 0; } else if (i==2) { i1 = 0; i2 = 1; } } /** 2d cross product */ inline double cross2(double * v1, double * v2) { return v1[0]*v2[1] - v1[1]*v2[0]; } /** 3d cross product */ inline void cross3(double * v1, double * v2, double * v) { v[0] = v1[1]*v2[2] - v1[2]*v2[1]; v[1] = v1[2]*v2[0] - v1[0]*v2[3]; v[2] = v1[0]*v2[1] - v1[1]*v2[0]; } inline double norm3(double * v) {return sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); } inline int sgn(double x) { return (int) ((x>0) - (x<0)); } +// template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); } inline int rnd(double x) { return (int) (x+sgn(x)*0.5); } /** Compares doubles acceptably */ inline bool dbl_geq(double dblL, double dblR, int tolMult=2) { return ( (dblL > dblR) || ((dblL <= (dblR + \ std::numeric_limits<double>::epsilon() * \ tolMult * std::max(abs(dblL), abs(dblR)))) && (dblR <= (dblL + \ std::numeric_limits<double>::epsilon() * \ tolMult * std::max(abs(dblL), abs(dblR)))))); } - + inline double tolerance(double x, double tol = parsetol_) { + return std::max(tol,tol*fabs(x)); + } + inline double nudge_up(double x) { return x+tolerance(x); } + inline double nudge_down(double x) { return x-tolerance(x); } inline double parse_min(const char * arg) { - if (std::strcmp(arg,"INF") == 0) return -parsebig; - else return atof(arg); } + if (std::strcmp(arg,"INF") == 0) return -parsebig_; + else if (std::strcmp(arg,"-INF") == 0) return -parsebig_; + else return (atof(arg)); + } inline double parse_max(const char * arg) { - if (std::strcmp(arg,"INF") == 0) return parsebig; - else return atof(arg); + if (std::strcmp(arg,"INF") == 0) return parsebig_; + else return (atof(arg)); } inline double parse_minmax(const char * arg) { - if (std::strcmp(arg,"-INF") == 0) return -parsebig; - else if (std::strcmp(arg,"INF") == 0) return parsebig; - else return atof(arg); } + if (std::strcmp(arg,"-INF") == 0) return -parsebig_; + else if (std::strcmp(arg,"INF") == 0) return parsebig_; + else return atof(arg); + } inline void split_values(double & min, double & max) { - double eps = std::max(parsetol,parsetol*fabs(min)); - min -= eps; - max += eps; + min = nudge_down(min); + max = nudge_up(max); } /** Returns true if the value v is between min & max */ template<typename T> inline bool in_range(const T &v, const T &min, const T &max) { return v >= min && v <= max; } /** Returns true if the value v is between min & max within tolerance TOL */ template<typename T> inline bool in_range(const T &v, const T &min, const T &max, const T &tol) { return in_range(v, min-tol, max+tol); } /** Returns the value with the larger absolute value */ template<typename T> inline T max_abs(const T &a, const T &b) { return (a*a > b*b) ? a : b; } /** Returns the value with the smaller absolute value */ template<typename T> inline T min_abs(const T &a, const T &b) { return (a*a < b*b) ? a : b; } /** A simple Matlab like timing function */ inline double tictoc() { double t_new = clock() / (double) CLOCKS_PER_SEC; static double t = t_new; double dt = t_new - t; t = t_new; return dt; } /** A simple timer */ inline double timer() { double t_new = clock() / (double) CLOCKS_PER_SEC; static double t = t_new; // done once at first time the function is evoked double dt = t_new - t; return dt; } /** Binary search between low & high for value (assumes array is sorted) */ template<typename T> inline int search_sorted(const T* A, T value, int low, int high) { int mid; while (low < high) { mid = (low + high) >> 1; // set mid to mean of high and low, ">>" is a bit shift which divides by 2, rounding down if (A[mid] > value) high = mid; // mid was too high, reduce high else if (A[mid] < value) low = mid+1; // mid was too low, increase low else return mid; } return -1; // value not found in array, return -1 } /** Flat search between low & high for value (assumes array is NOT sorted) */ template<typename T> inline int search_unsorted(const T* A, T value, int low, int high) { for (int i=low; i<high; i++) if (A[i] == value) return i; return -1; } /** Regular swap */ template<typename T> inline void swap(T &a, T &b) { T temp = a; a = b; b = temp; } //=================================================================== /** A collection of string convesion/manipulation routines */ //=================================================================== /** converts anything that has iostream::<< defined to a string */ template<typename T> inline std::string to_string(const T &v, int precision=0) { std::ostringstream out; - if (precision) out << std::setprecision(precision); + if (precision) { + out << std::setprecision(precision); + out << std::setw(precision+3); + out << std::showpoint; + } out << v; + out << std::noshowpoint; return out.str(); } + /** formatted double to a string */ + inline std::string to_string(int precision, const double v) + { + char b[50]; + sprintf(b, "%*.*f",4+precision,precision, v); + std::string s(b); + return s; + } + /** conversion to string */ inline std::string true_false(const double &v) { if (v) return "TRUE"; else return "FALSE"; } /** test conversion to double */ inline bool is_numeric(const std::string &s) { double v; std::istringstream in(s); return (in >> v); } /** convert a string to anything that has iostream::>> defined, second arg is to disambibuate type */ template<typename T> inline T str2T(const std::string &s, T v) { std::istringstream in(s); if (!(in >> v)) throw ATC::ATC_Error("str2T invalid string conversion"); return v; } /** convert a string to a double */ inline double str2dbl(const std::string &s) { return str2T(s, double(0.0)); } /** tests if conversion to double is possible */ inline bool is_dbl(const std::string &s) { char *endptr; strtod(s.c_str(), &endptr); if(endptr != NULL && *endptr == '\0') return true; return false; } + + inline bool is_number(const std::string& s) + { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); + } /** convert a string to an int */ inline int str2int(const std::string &s) { return str2T(s, int(4)); } //* replaces all characters in a set of characters with a character //* @param str input string //* @param *s pointer to array of characters that will be replaced //* @param r character to replace characters in s[] with static void replace_all_of(std::string &str, const char *s, const char r) { size_t found; found = str.find_first_of(s); while (found != std::string::npos) { str[found] = r; found = str.find_first_of(s, found+1); } } /** converts the string to lowercase */ inline std::string& to_lower(std::string &s) { std::transform(s.begin(),s.end(),s.begin(),static_cast<int(*)(int)>(tolower)); return s; } /** converts the string to uppercase */ inline std::string& to_upper(std::string &s) { std::transform(s.begin(),s.end(),s.begin(),static_cast<int(*)(int)>(toupper)); return s; } /** removes any whitespace from the beginning or end of string */ static std::string& trim(std::string &s) { if (s.empty()) return s; size_t found = s.find_last_not_of(" \t\n"); if (found < s.size()-1) s.erase(found+1); found = s.find_first_not_of(" \t\n"); if (found != std::string::npos) s = s.substr(found); return s; } /** splits delimited string into a vector of strings */ static void split(const std::string &s, std::vector<std::string> &ss, char del=' ') { ss.clear(); size_t begin=0, end=0; while (end != std::string::npos) { begin = s.find_first_not_of(del, end); // find beginning of fragment end = s.find_first_of(del,begin); if (begin != std::string::npos) // safe if end is npos-1 ss.push_back(s.substr(begin,end-begin)); } } static std::string cap(std::string & s) { s[0] = toupper(s[0]); return s; } /* turns Aa_Bb_Cc into aa_bb_cc */ static std::string to_cap(const std::string &s) { std::vector<std::string> words; std::string delimiter = "_"; split(s,words,(delimiter.c_str())[0]); std::string name = ""; for (unsigned int i = 0; i < words.size(); i++) { name = name + cap(words[i]); } return name; } //* scans a string for a list of commands delimited by ', \t' with # comments //* @param line The input line to be parsed //* @cs A vector of strings parsed from the input line static void command_strings(std::string line, std::vector<std::string> &cs) { if (line.find('#') != std::string::npos) line.erase(line.find("#")); replace_all_of(line, "\t,", ' '); to_lower(trim(line)); split(line, cs); } //* reads a single line from a file and splits it into a vector of strings //* returns the number of strings in the vector inline int command_line(std::fstream &fid, std::vector<std::string> &cs) { std::string line; getline(fid, line); command_strings(line, cs); return cs.size(); } } #endif diff --git a/lib/atc/Vector.h b/lib/atc/Vector.h index 5630b0ee1..338183d00 100644 --- a/lib/atc/Vector.h +++ b/lib/atc/Vector.h @@ -1,206 +1,234 @@ #ifndef VECTOR_H #define VECTOR_H #include "Matrix.h" namespace ATC_matrix { /////////////////////////////////////////////////////////////////////////////// // forward declarations /////////////////////////////////////////////////////// //* Matrix-vector product //template<typename T> //void MultMv(const Matrix<T> &A, const Vector<T> &v, DenseVector<T> &c, // const bool At=0, T a=1, T b=0); /****************************************************************************** * abstract class Vector ******************************************************************************/ template<typename T> class Vector : public Matrix<T> { public: Vector() {} Vector(const Vector<T> &c); // do not implement! virtual ~Vector() {} std::string to_string() const; // pure virtual functions virtual T operator()(INDEX i, INDEX j=0) const=0; virtual T& operator()(INDEX i, INDEX j=0) =0; virtual T operator[](INDEX i) const=0; virtual T& operator[](INDEX i) =0; virtual INDEX nRows() const=0; virtual T* ptr() const=0; virtual void resize(INDEX nRows, INDEX nCols=1, bool copy=0)=0; virtual void reset(INDEX nRows, INDEX nCols=1, bool zero=0)=0; virtual void copy(const T * ptr, INDEX nRows, INDEX nCols=1)=0; void write_restart(FILE *f) const; // will be virtual // output to matlab using Matrix<T>::matlab; void matlab(std::ostream &o, const std::string &s="v") const; using Matrix<T>::operator=; INDEX nCols() const; bool in_range(INDEX i) const; bool same_size(const Vector &m) const; static bool same_size(const Vector &a, const Vector &b); protected: + void _set_equal(const Matrix<T> &r); //* don't allow this Vector& operator=(const Vector &r); }; /////////////////////////////////////////////////////////////////////////////// //* performs a matrix-vector multiply with default naive implementation template<typename T> void MultMv(const Matrix<T> &A, const Vector<T> &v, DenseVector<T> &c, const bool At, T a, T b) { const INDEX sA[2] = {A.nRows(), A.nCols()}; // m is sA[At] k is sA[!At] const INDEX M=sA[At], K=sA[!At]; GCK(A, v, v.size()!=K, "MultAb<T>: matrix-vector multiply"); if (c.size() != M) { c.resize(M); // set size of C c.zero(); // do not add result to C } else c *= b; for (INDEX p=0; p<M; p++) for (INDEX r=0; r<K; r++) c[p] += A(p*!At+r*At, p*At+r*!At) * v[r]; } /////////////////////////////////////////////////////////////////////////////// //* Operator for Matrix-vector product template<typename T> DenseVector<T> operator*(const Matrix<T> &A, const Vector<T> &b) { DenseVector<T> c; MultMv(A, b, c, 0, 1.0, 0.0); return c; } /////////////////////////////////////////////////////////////////////////////// //* Operator for Vector-matrix product template<typename T> DenseVector<T> operator*(const Vector<T> &a, const Matrix<T> &B) { DenseVector<T> c; MultMv(B, a, c, 1, 1.0, 0.0); return c; } /////////////////////////////////////////////////////////////////////////////// //* Multiply a vector by a scalar template<typename T> DenseVector<T> operator*(const Vector<T> &v, const T s) { DenseVector<T> r(v); r*=s; return r; } /////////////////////////////////////////////////////////////////////////////// //* Multiply a vector by a scalar - communitive template<typename T> DenseVector<T> operator*(const T s, const Vector<T> &v) { DenseVector<T> r(v); r*=s; return r; } /////////////////////////////////////////////////////////////////////////////// //* inverse scaling operator - must always create memory template<typename T> DenseMatrix<T> operator/(const Vector<T> &v, const T s) { DenseVector<T> r(v); r*=(1.0/s); // for integer types this may be worthless return ; } /////////////////////////////////////////////////////////////////////////////// //* Operator for Vector-Vector sum template<typename T> DenseVector<T> operator+(const Vector<T> &a, const Vector<T> &b) { DenseVector<T> c(a); c+=b; return c; } /////////////////////////////////////////////////////////////////////////////// //* Operator for Vector-Vector subtraction template<typename T> DenseVector<T> operator-(const Vector<T> &a, const Vector<T> &b) { DenseVector<T> c(a); c-=b; return c; } /////////////////////////////////////////////////////////////////////////////// // Template definitions /////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //* output operator template<typename T> std::string Vector<T>::to_string() const { std::string s; int sz = this->size(); for (INDEX i = 0; i < sz; i++) s += std::string(i?"\t":"") + ATC_Utility::to_string((*this)[i],myPrecision); return s; } /////////////////////////////////////////////////////////////////////////////// //* Writes a matlab script defining the vector to the stream template<typename T> void Vector<T>::matlab(std::ostream &o, const std::string &s) const { o << s <<"=zeros(" << this->size() << ",1);\n"; int sz = this->size(); for (INDEX i = 0; i < sz; i++) o << s << "("<<i+1<<") = " << (*this)[i] << ";\n"; } /////////////////////////////////////////////////////////////////////////////// //* writes the vector data to a file template <typename T> void Vector<T>::write_restart(FILE *f) const { INDEX size = this->size(); fwrite(&size, sizeof(INDEX),1,f); if (size) fwrite(this->ptr(), sizeof(T), this->size(), f); } /////////////////////////////////////////////////////////////////////////////// //* returns the number of columns; always 1 template<typename T> inline INDEX Vector<T>::nCols() const { return 1; } /////////////////////////////////////////////////////////////////////////////// //* returns true if INDEX i is within the range of the vector template<typename T> bool Vector<T>::in_range(INDEX i) const { return i<this->size(); } /////////////////////////////////////////////////////////////////////////////// //* returns true if m has the same number of elements this vector template<typename T> bool Vector<T>::same_size(const Vector &m) const { return this->size() == m.size(); } /////////////////////////////////////////////////////////////////////////////// //* returns true if a and b have the same number of elements template<typename T> inline bool Vector<T>::same_size(const Vector &a, const Vector &b) { return a.same_size(b); } - +//---------------------------------------------------------------------------- +// general matrix assignment (for densely packed matrices) +//---------------------------------------------------------------------------- +template<typename T> +void Vector<T>::_set_equal(const Matrix<T> &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix<T> *pr = &r; +#ifdef OBSOLETE + if (const SparseMatrix<T> *ps = dynamic_cast<const SparseMatrix<T>*>(pr))//sparse_cast(pr)) + copy_sparse_to_matrix(ps, *this); + + else if (dynamic_cast<const DiagonalMatrix<T>*>(pr))//diag_cast(pr)) // r is Diagonal? + { + this->zero(); + for (INDEX i=0; i<r.size(); i++) (*this)(i,i) = r[i]; + } + else memcpy(this->ptr(), r.ptr(), r.size()*sizeof(T)); +#else + const Vector<T> *pv = dynamic_cast<const Vector<T>*> (pr); + if (pv) this->copy(pv->ptr(),pv->nRows()); + else + { + std::cout <<"Error in general vector assignment\n"; + exit(1); + } +#endif +} } // end namespace #endif diff --git a/lib/atc/WeakEquationElectronContinuity.cpp b/lib/atc/WeakEquationElectronContinuity.cpp index 67ffc581d..3b2a18b0f 100644 --- a/lib/atc/WeakEquationElectronContinuity.cpp +++ b/lib/atc/WeakEquationElectronContinuity.cpp @@ -1,105 +1,106 @@ #include "WeakEquationElectronContinuity.h" #include "Material.h" #include <iostream> #include <fstream> namespace ATC { //============================================================== // Class WeakEquationElectronContinuity //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronContinuity::WeakEquationElectronContinuity() : WeakEquation(DYNAMIC_PDE,ELECTRON_DENSITY,1) {} //-------------------------------------------------------------- // Destructor //--------------------------------------------------------------------- WeakEquationElectronContinuity::~WeakEquationElectronContinuity(void) {} //--------------------------------------------------------------------- void WeakEquationElectronContinuity::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT & density ) const { FIELD_MATS::const_iterator nField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = nField->second; density.resize(n.nRows(),n.nCols()); density = 1; } //--------------------------------------------------------------------- void WeakEquationElectronContinuity::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { material->electron_flux(fields, grad_fields, flux); } //--------------------------------------------------------------------- bool WeakEquationElectronContinuity::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &recombination) const { return material->electron_recombination(fields, grad_fields, recombination); } //============================================================== // Class WeakEquationElectronEquilbrium //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronEquilibrium::WeakEquationElectronEquilibrium() : WeakEquation(PROJECTION_PDE,ELECTRON_DENSITY,1) {} //-------------------------------------------------------------- // Destructor //--------------------------------------------------------------------- WeakEquationElectronEquilibrium::~WeakEquationElectronEquilibrium(void) {} //--------------------------------------------------------------------- void WeakEquationElectronEquilibrium::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT & density ) const { FIELD_MATS::const_iterator nField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = nField->second; density.reset(n.nRows(),n.nCols()); density = 1; } //--------------------------------------------------------------------- + bool WeakEquationElectronEquilibrium::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const { bool flag = material->electron_charge_density(fields, flux); flux *= -1.; // transform from charge density to number density return flag; } //--------------------------------------------------------------------- void WeakEquationElectronEquilibrium::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { material->electron_flux(fields, grad_fields, flux); } }; diff --git a/lib/atc/WeakEquationElectronMomentum.cpp b/lib/atc/WeakEquationElectronMomentum.cpp index 4ae48ed70..37484f61d 100644 --- a/lib/atc/WeakEquationElectronMomentum.cpp +++ b/lib/atc/WeakEquationElectronMomentum.cpp @@ -1,185 +1,186 @@ #include "WeakEquationElectronMomentum.h" #include "Material.h" #include "LammpsInterface.h" namespace ATC { //============================================================== // Class WeakEquationElectronMomentum //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronMomentum::WeakEquationElectronMomentum() : WeakEquation(STATIC_PDE,ELECTRON_VELOCITY,3) {} //-------------------------------------------------------------- // Destructor //-------------------------------------------------------------- WeakEquationElectronMomentum::~WeakEquationElectronMomentum() {} void WeakEquationElectronMomentum::convection(const FIELD_MATS &fields, const Material * material, DENS_MAT_VEC & flux) const { // set up mass density FIELD_MATS::const_iterator nField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = nField->second; DENS_MAT nMe(n.nRows(),n.nCols()); material->inv_effective_mass(fields,nMe); nMe = n.div_by_element(nMe); // set up velocity and flux FIELD_MATS::const_iterator vField = fields.find(ELECTRON_VELOCITY); const DENS_MAT & velocity = vField->second; const CLON_VEC u(velocity,CLONE_COL,0); const CLON_VEC v(velocity,CLONE_COL,1); const CLON_VEC w(velocity,CLONE_COL,2); flux[0] = velocity; flux[1] = velocity; flux[2] = velocity; CLON_VEC nuu(flux[0],CLONE_COL,0); CLON_VEC nuv(flux[1],CLONE_COL,0); CLON_VEC nuw(flux[2],CLONE_COL,0); CLON_VEC nvu(flux[0],CLONE_COL,1); CLON_VEC nvv(flux[1],CLONE_COL,1); CLON_VEC nvw(flux[2],CLONE_COL,1); CLON_VEC nwu(flux[0],CLONE_COL,2); CLON_VEC nwv(flux[1],CLONE_COL,2); CLON_VEC nww(flux[2],CLONE_COL,2); for (int i = 0; i < n.nRows(); i++) { // tensor product of velocities nuu(i) *= nMe(i,0)*u(i); nuv(i) *= nMe(i,0)*v(i); nuw(i) *= nMe(i,0)*w(i); nvu(i) *= nMe(i,0)*u(i); nvv(i) *= nMe(i,0)*v(i); nvw(i) *= nMe(i,0)*w(i); nwu(i) *= nMe(i,0)*u(i); nwv(i) *= nMe(i,0)*v(i); nww(i) *= nMe(i,0)*w(i); } }; //--------------------------------------------------------------------- // compute mass density //--------------------------------------------------------------------- void WeakEquationElectronMomentum::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT & density ) const { material->electron_mass_density(fields, density); } //-------------------------------------------------------------- void WeakEquationElectronMomentum::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { convection(fields,material,flux); } //============================================================== // Class WeakEquationElectronMomentumDDM //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronMomentumDDM::WeakEquationElectronMomentumDDM() : WeakEquationElectronMomentum() { DENS_MAT dummy; _dnCp_.reserve(nsd_); for (int i = 0; i < nsd_; i++) _dnCp_.push_back(dummy); _electricForce_.reserve(nsd_); for (int i = 0; i < nsd_; i++) _electricForce_.push_back(dummy); } //-------------------------------------------------------------- // Destructor //-------------------------------------------------------------- WeakEquationElectronMomentumDDM::~WeakEquationElectronMomentumDDM() {} void WeakEquationElectronMomentumDDM::thermal_stress(const FIELD_MATS &fields, const GRAD_FIELD_MATS &gradFields, const Material * material, DENS_MAT &flux) const { GRAD_FIELD_MATS::const_iterator dtField = gradFields.find(ELECTRON_TEMPERATURE); const DENS_MAT_VEC & DTe = dtField->second; CLON_VEC tsx(flux,CLONE_COL,0); CLON_VEC tsy(flux,CLONE_COL,1); CLON_VEC tsz(flux,CLONE_COL,2); // ith velocity component has thermal stress of // d_i n * Cp * Te DENS_MAT nCp(DTe[0].nRows(),DTe[0].nCols()); material->electron_heat_capacity(fields,nCp); nCp *= 2./3.; // correction to capacity account for convection tsx += nCp.mult_by_element(DTe[0]); tsy += nCp.mult_by_element(DTe[1]); tsz += nCp.mult_by_element(DTe[2]); FIELD_MATS::const_iterator tField = fields.find(ELECTRON_TEMPERATURE); const DENS_MAT & Te = tField->second; material->D_electron_heat_capacity(fields,gradFields,_dnCp_); for (int i = 0; i < nsd_; i++) _dnCp_[i] *= 2./3.; // correction to capacity account for convection tsx += Te.mult_by_element(_dnCp_[0]); tsy += Te.mult_by_element(_dnCp_[1]); tsz += Te.mult_by_element(_dnCp_[2]); } //--------------------------------------------------------------------- // compute mass density //--------------------------------------------------------------------- void WeakEquationElectronMomentumDDM::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT & density ) const { material->electron_drag_velocity_coefficient(fields, density); } //-------------------------------------------------------------- bool WeakEquationElectronMomentumDDM::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const { FIELD_MATS::const_iterator vField = fields.find(ELECTRON_VELOCITY); const DENS_MAT & velocity = vField->second; flux.reset(velocity.nRows(),velocity.nCols()); thermal_stress(fields, grad_fields, material, flux); material->electric_displacement(fields, grad_fields, _electricForce_); FIELD_MATS::const_iterator nField = fields.find(ELECTRON_DENSITY); const DENS_MAT & n = nField->second; + CLON_VEC tsx(flux,CLONE_COL,0); CLON_VEC tsy(flux,CLONE_COL,1); CLON_VEC tsz(flux,CLONE_COL,2); tsx += n.mult_by_element(_electricForce_[0]); tsy += n.mult_by_element(_electricForce_[1]); tsz += n.mult_by_element(_electricForce_[2]); return true; } }; // END namespace diff --git a/lib/atc/WeakEquationElectronTemperature.cpp b/lib/atc/WeakEquationElectronTemperature.cpp index 3841f619a..97a21628d 100644 --- a/lib/atc/WeakEquationElectronTemperature.cpp +++ b/lib/atc/WeakEquationElectronTemperature.cpp @@ -1,227 +1,230 @@ #include "WeakEquationElectronTemperature.h" #include "Material.h" #include <iostream> #include <fstream> namespace ATC { //============================================================== // Class WeakEquationElectronTemperature //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronTemperature::WeakEquationElectronTemperature() : WeakEquation(DYNAMIC_PDE,ELECTRON_TEMPERATURE,1) { } //-------------------------------------------------------------- // Destructor //--------------------------------------------------------------------- WeakEquationElectronTemperature::~WeakEquationElectronTemperature(void) {} //--------------------------------------------------------------------- // compute energy //--------------------------------------------------------------------- void WeakEquationElectronTemperature::E_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT & energy ) const { material->electron_thermal_energy(fields, energy); } //--------------------------------------------------------------------- // compute heat capacities //--------------------------------------------------------------------- void WeakEquationElectronTemperature::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT & capacity ) const { material->electron_heat_capacity(fields, capacity); } //--------------------------------------------------------------------- // compute heat fluxes //--------------------------------------------------------------------- void WeakEquationElectronTemperature::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { material->electron_heat_flux(fields, grad_fields, flux); } //--------------------------------------------------------------------- // compute exchange fluxes //--------------------------------------------------------------------- bool WeakEquationElectronTemperature::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const { DENS_MAT exchange_flux; bool has = material->electron_phonon_exchange(fields, exchange_flux); if (has) flux = -1.*exchange_flux; return has; } //============================================================== // Class WeakEquationElectronJouleHeating //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronTemperatureJouleHeating::WeakEquationElectronTemperatureJouleHeating() : WeakEquationElectronTemperature() { // convert charge * voltage --> mass length^2 / time^2 //eV2E_ = (ATC::LammpsInterface::instance()->qe2f()) // * (ATC::LammpsInterface::instance()->ftm2v()); eV2E_ = ATC::LammpsInterface::instance()->qv2e(); int nSD = 3; _J_.assign(nSD, DENS_MAT()); _E_.assign(nSD, DENS_MAT()); } //-------------------------------------------------------------- // Destructor //--------------------------------------------------------------------- WeakEquationElectronTemperatureJouleHeating::~WeakEquationElectronTemperatureJouleHeating(void) {} //--------------------------------------------------------------------- void WeakEquationElectronTemperatureJouleHeating::E_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &energy) const { WeakEquationElectronTemperature::E_integrand(fields, grad_fields, material, energy); } //--------------------------------------------------------------------- void WeakEquationElectronTemperatureJouleHeating::M_integrand( const FIELD_MATS &fields, const Material * material, DENS_MAT &capacity) const { WeakEquationElectronTemperature::M_integrand(fields, material, capacity); } //--------------------------------------------------------------------- void WeakEquationElectronTemperatureJouleHeating::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { WeakEquationElectronTemperature::B_integrand(fields, grad_fields, material, flux); } //--------------------------------------------------------------------- bool WeakEquationElectronTemperatureJouleHeating::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const { + // call base class to get electron_temperature terms WeakEquationElectronTemperature::N_integrand(fields, grad_fields, material, flux); + // Joule heating = -I.grad Psi = J.grad Psi \approx J.E DENS_MAT jouleHeating; - material->electron_flux (fields, grad_fields, _J_); + material->electron_flux (fields, grad_fields, _J_); material->electric_field(fields, grad_fields, _E_); jouleHeating = _J_[0].mult_by_element(_E_[0]); for (DENS_MAT_VEC::size_type i=1; i < _J_.size(); i++) jouleHeating += _J_[i].mult_by_element(_E_[i]); jouleHeating *= eV2E_; - flux += jouleHeating; + flux -= jouleHeating; return true; } //============================================================== // Class WeakEquationElectronConvection //============================================================== //-------------------------------------------------------------- // Constructor //-------------------------------------------------------------- WeakEquationElectronTemperatureConvection::WeakEquationElectronTemperatureConvection() - : WeakEquationElectronTemperature() + : WeakEquationElectronTemperatureJouleHeating() { int nSD = 3; _convectiveFlux_.assign(nSD, DENS_MAT()); } //-------------------------------------------------------------- // Destructor //--------------------------------------------------------------------- WeakEquationElectronTemperatureConvection::~WeakEquationElectronTemperatureConvection(void) { // do nothing } //--------------------------------------------------------------------- void WeakEquationElectronTemperatureConvection::B_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const { // add diffusion term - WeakEquationElectronTemperature::B_integrand(fields, grad_fields, material, flux); + WeakEquationElectronTemperatureJouleHeating::B_integrand(fields, grad_fields, material, flux); //flux[0] = 0.; //flux[1] = 0.; //flux[2] = 0.; // add convection term DENS_MAT_VEC convectiveFlux; material->electron_heat_convection(fields,_convectiveFlux_); flux[0] += _convectiveFlux_[0]; flux[1] += _convectiveFlux_[1]; flux[2] += _convectiveFlux_[2]; } //--------------------------------------------------------------------- bool WeakEquationElectronTemperatureConvection::N_integrand( const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const { // call base class to get electron_temperature terms - WeakEquationElectronTemperature::N_integrand(fields, grad_fields, material, flux); + WeakEquationElectronTemperatureJouleHeating::N_integrand(fields, grad_fields, material, flux); +#ifdef TEST // add exchange with kinetic energy DENS_MAT keExchange; DENS_MAT capacity; material->electron_heat_capacity(fields, capacity); capacity *= 2./3.; // correction in DDM equations //FIELD_MATS::const_iterator dField = fields.find(ELECTRON_DENSITY); FIELD_MATS::const_iterator tField = fields.find(ELECTRON_TEMPERATURE); //const DENS_MAT & density = dField->second; const DENS_MAT & temperature = tField->second; GRAD_FIELD_MATS::const_iterator velocityGradients = grad_fields.find(ELECTRON_VELOCITY); const DENS_MAT_VEC & dv = velocityGradients->second; CLON_VEC vxx(dv[0],CLONE_COL,0); CLON_VEC vyy(dv[1],CLONE_COL,1); CLON_VEC vzz(dv[2],CLONE_COL,2); keExchange = vxx + vyy + vzz; //keExchange *= density; keExchange *= temperature; keExchange *= capacity; flux -= keExchange; - +#endif return true; } }; // end namespace diff --git a/lib/atc/WeakEquationElectronTemperature.h b/lib/atc/WeakEquationElectronTemperature.h index 8680de31a..1a5ecbab0 100644 --- a/lib/atc/WeakEquationElectronTemperature.h +++ b/lib/atc/WeakEquationElectronTemperature.h @@ -1,180 +1,180 @@ #ifndef WEAK_EQUATION_ELECTRON_TEMPERATURE_H #define WEAK_EQUATION_ELECTRON_TEMPERATURE_H #include <set> #include <string> #include "WeakEquation.h" namespace ATC{ /** * @class WeakEquationElectronTemperature * @brief Electron temperature * c T_e,t = div q_e + g(T_e-T_p) --> * int M c T_e,t = int B q_e + int N g */ class WeakEquationElectronTemperature : public WeakEquation { public: // constructor WeakEquationElectronTemperature(); // destructor virtual ~WeakEquationElectronTemperature(); /** integrand that used to form the energy */ virtual bool has_E_integrand(void) const {return true;} virtual void E_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &energy ) const ; /** density that used to form the mass matrix */ virtual bool has_M_integrand(void) const {return true;} virtual void M_integrand(const FIELD_MATS &fields, const Material * material, DENS_MAT &density ) const ; /** flux that is integrated with B = Grad N as its weight */ virtual bool has_B_integrand(void) const {return true;} virtual void B_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const ; /** flux that is integrated with N as its weight */ virtual bool has_N_integrand(void) const {return true;} virtual bool N_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const ; /** necessary interfaces */ virtual std::set<std::string> needs_material_functions(void) const { std::string list[4] = {"electron_thermal_energy", "electron_heat_capacity", "electron_phonon_exchange", "electron_heat_flux"}; std::set<std::string> needs(list,list+4); return needs; } }; /** * @class WeakEquationElectronTemperatureJouleHeating * @brief Electron temperature with Joule heating * c T_e,t = div q_e + g(T_e-T_p) + J.E --> * int M c T_e,t = int B q_e + int N ( g + J.E) */ class WeakEquationElectronTemperatureJouleHeating : public WeakEquationElectronTemperature { public: // constructor WeakEquationElectronTemperatureJouleHeating(); // destructor virtual ~WeakEquationElectronTemperatureJouleHeating(); /** integrand that used to form the energy */ virtual bool has_E_integrand(void) const {return true;} virtual void E_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &energy ) const; /** density that used to form the mass matrix */ virtual bool has_M_integrand(void) const {return true;} virtual void M_integrand(const FIELD_MATS &fields, const Material * material, DENS_MAT &density ) const; /** flux that is integrated with B = Grad N as its weight */ virtual bool has_B_integrand(void) const {return true;} virtual void B_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const; /** flux that is integrated with N as its weight */ virtual bool has_N_integrand(void) const {return true;} virtual bool N_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const; /** necessary interfaces */ virtual std::set<std::string> needs_material_functions(void) const { std::set<std::string> needs = WeakEquationElectronTemperature::needs_material_functions(); needs.insert("electric_field"); return needs; } protected: /** conversion factor for charge * voltage --> mass length^2 / time^2 */ double eV2E_; mutable DENS_MAT_VEC _J_, _E_; }; /** * @class WeakEquationElectronTemperatureConvection * @brief Electron temperature with convection * c ( T_e,t + grad m_e J.E T_e / e ) = div q_e + g(T_e-T_p) + c n T_e grad J.E/(n e) --> * int M c T_e,t = int B ( q_e - c m_e J.E T_e / e ) + int N ( g + c n T_e grad J.E/(n e) ) */ class WeakEquationElectronTemperatureConvection : - public WeakEquationElectronTemperature + public WeakEquationElectronTemperatureJouleHeating { public: // constructor WeakEquationElectronTemperatureConvection(); // destructor virtual ~WeakEquationElectronTemperatureConvection(); /** flux that is integrated with B = Grad N as its weight */ virtual bool has_B_integrand(void) const {return true;} virtual void B_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT_VEC &flux) const; /** flux that is integrated with N as its weight */ virtual bool has_N_integrand(void) const {return true;} virtual bool N_integrand(const FIELD_MATS &fields, const GRAD_FIELD_MATS &grad_fields, const Material * material, DENS_MAT &flux) const; /** necessary interfaces */ virtual std::set<std::string> needs_material_functions(void) const { std::set<std::string> needs = WeakEquationElectronTemperature::needs_material_functions(); needs.insert("electron_drag_power"); return needs; } protected: /** workspace variable for the convective flux */ mutable DENS_MAT_VEC _convectiveFlux_; }; }; // end namespace #endif