ATC version 2.0, date: Aug7

git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10561 f3b2605a-c512-4ea7-a41b-209d697bcdaa
This commit is contained in:
rjones
2013-08-07 21:34:54 +00:00
parent fafff431e9
commit 7855ef6dda
195 changed files with 83477 additions and 4499 deletions

1711
lib/atc/ATC_Coupling.cpp Normal file

File diff suppressed because it is too large Load Diff

424
lib/atc/ATC_Coupling.h Normal file
View File

@ -0,0 +1,424 @@
#ifndef ATC_COUPLING_H
#define ATC_COUPLING_H
// ATC headers
#include "ATC_Method.h"
#include "ExtrinsicModel.h"
using namespace std;
namespace ATC {
// Forward declarations
class PrescribedDataManager;
class AtomicRegulator;
class TimeIntegrator;
class ReferencePositions;
/**
* @class ATC_Coupling
* @brief Base class for atom-continuum coupling
*/
class ATC_Coupling : public ATC_Method {
public: /** methods */
friend class ExtrinsicModel; // friend is not inherited
friend class ExtrinsicModelTwoTemperature;
friend class ExtrinsicModelDriftDiffusion;
friend class ExtrinsicModelDriftDiffusionConvection;
friend class ExtrinsicModelElectrostatic;
friend class ExtrinsicModelElectrostaticMomentum;
friend class SchrodingerPoissonSolver;
friend class SliceSchrodingerPoissonSolver;
/** constructor */
ATC_Coupling(string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix);
/** destructor */
virtual ~ATC_Coupling();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre neighbor */
virtual void pre_neighbor();
/** pre exchange */
virtual void pre_exchange();
virtual void reset_atoms(){};
/** pre force */
virtual void pre_force() {};
/** post force */
virtual void post_force();
/** pre integration run */
virtual void initialize();
/** post integration run : called at end of run or simulation */
virtual void finish();
/** Predictor phase, Verlet first step for velocity */
virtual void init_integrate_velocity();
/** Predictor phase, executed between velocity and position Verlet */
virtual void mid_init_integrate(){};
/** Predictor phase, Verlet first step for position */
virtual void init_integrate_position();
/** Predictor phase, executed after Verlet */
virtual void post_init_integrate();
/** Corrector phase, executed before Verlet */
virtual void pre_final_integrate(){};
/** Corrector phase, Verlet second step for velocity */
virtual void final_integrate();
/** Corrector phase, executed after Verlet*/
virtual void post_final_integrate() {lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+1);};
/** pre/post atomic force calculation in minimize */
virtual void min_pre_force(){};
virtual void min_post_force(){};
// data access
/** get map general atomic shape function matrix to overlap region */
SPAR_MAT &get_atom_to_overlap_mat() {return atomToOverlapMat_.set_quantity();};
/** get map general atomic shape function matrix to overlap region */
SPAR_MAN &atom_to_overlap_mat() {return atomToOverlapMat_;};
/** check if atomic quadrature is being used for MD_ONLY nodes */
bool atom_quadrature_on(){return atomQuadForInternal_;};
const set<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 set< pair<int,int> > * boundaryFaceSet)
{bndyFaceSet_ = boundaryFaceSet;};
BoundaryIntegrationType parse_boundary_integration
(int narg, char **arg, const set< 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< 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);
/** access to full right hand side / forcing vector */
FIELDS &rhs() {return rhs_;};
DENS_MAN &field_rhs(FieldName thisField) { return rhs_[thisField]; };
/** allow FE_Engine to construct ATC structures after mesh is constructed */
virtual void initialize_mesh_data(void);
// public for FieldIntegrator
bool source_atomic_quadrature(FieldName field)
{ return (sourceIntegration_ == FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE); }
ATC::IntegrationDomainType source_integration()
{ return sourceIntegration_; }
/** wrapper for FE_Engine's compute_sources */
void compute_sources_at_atoms(const RHS_MASK & rhsMask,
const FIELDS & fields,
const PhysicsModel * physicsModel,
FIELD_MATS & atomicSources);
/** wrapper for FE_Engine's compute_rhs_vector functions */
void compute_rhs_vector(const RHS_MASK & rhs_mask,
const FIELDS &fields,
FIELDS &rhs,
const IntegrationDomainType domain, // = FULL_DOMAIN
const PhysicsModel * physicsModel=NULL);
/** wrapper for FE_Engine's compute_tangent_matrix */
void compute_rhs_tangent(const pair<FieldName,FieldName> row_col,
const RHS_MASK & rhsMask,
const FIELDS & fields,
SPAR_MAT & stiffness,
const IntegrationDomainType integrationType,
const PhysicsModel * physicsModel=NULL);
/** PDE type */
WeakEquation::PDE_Type pde_type(const FieldName fieldName) const;
/** is dynamic PDE */
bool is_dynamic(const FieldName fieldName) const;
// public for ImplicitSolveOperator
/** return pointer to PrescribedDataManager */
PrescribedDataManager * prescribed_data_manager()
{ return prescribedDataMgr_; }
// public for Kinetostat
DIAG_MAT &get_mass_mat(FieldName thisField)
{ return massMats_[thisField].set_quantity();};
/** */
DENS_MAN &atomic_source(FieldName thisField){return atomicSources_[thisField];};
//---------------------------------------------------------------
/** \name materials */
//---------------------------------------------------------------
/*@{*/
/** access to element to material map */
Array<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,
string matFileName);
/** access to physics model */
PhysicsModel * physics_model() {return physicsModel_; };
/*@}*/
//---------------------------------------------------------------
/** \name creation */
//---------------------------------------------------------------
/*@{*/
/** set up atom to material identification */
virtual void reset_atom_materials();
/** */
void reset_node_mask();
/** */
void reset_overlap_map();
/*@}*/
//---------------------------------------------------------------
/** \name output/restart */
//---------------------------------------------------------------
/*@{*/
void pack_fields(RESTART_LIST & data);
void output() { ATC_Method::output(); }
/*@}*/
//---------------------------------------------------------------
/** \name initial & boundary conditions */
//---------------------------------------------------------------
/*@{*/
/** mask for computation of fluxes */
void set_fixed_nodes();
/** set initial conditions by changing fields */
void set_initial_conditions();
/*@}*/
//---------------------------------------------------------------
/** \name sources */
//---------------------------------------------------------------
/** calculate and set matrix of sources_ */
void set_sources();
/** assemble various contributions to the heat flux in the atomic region */
void compute_atomic_sources(const Array2D<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();
/** 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);
/** 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);
private: /** methods */
ATC_Coupling(); // do not define
protected: /** data */
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** sets up all data necessary to define the computational geometry */
virtual void set_computational_geometry();
/** constructs all data which is updated with time integration, i.e. fields */
virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
//---------------------------------------------------------------
/** status */
//---------------------------------------------------------------
/*@{*/
/** flag on if FE nodes in MD region should be initialized to projected MD values */
bool consistentInitialization_;
bool equilibriumStart_;
bool useFeMdMassMatrix_;
/** flag to determine if charge is tracked */
bool trackCharge_;
/** temperature definition model */
TemperatureDefType temperatureDef_;
/*@}*/
//---------------------------------------------------------------
/** \name managers */
//---------------------------------------------------------------
/*@{*/
/** prescribed data handler */
PrescribedDataManager * prescribedDataMgr_;
/** pointer to physics model */
PhysicsModel * physicsModel_;
/** manager for extrinsic models */
ExtrinsicModelManager extrinsicModelManager_;
/** manager for regulator */
AtomicRegulator * atomicRegulator_;
/** managers for time integrators per field */
map<FieldName,TimeIntegrator * > timeIntegrators_;
/** time integrator iterator */
mutable map<FieldName,TimeIntegrator * >::iterator _tiIt_;
/** time integrator const iterator */
mutable map<FieldName,TimeIntegrator * >::const_iterator _ctiIt_;
/*@}*/
//---------------------------------------------------------------
/** materials */
//---------------------------------------------------------------
/*@{*/
Array<int> elementToMaterialMap_; // ATOMIC_TAG * elementToMaterialMap_;
/** atomic ATC material tag */
Array< set <int> > atomMaterialGroups_; // ATOMIC_TAG*atomMaterialGroups_;
Array< set <int> > atomMaterialGroupsMask_; // ATOMIC_TAG*atomMaterialGroupsMask_;
/*@}*/
//---------------------------------------------------------------
/** computational geometry */
//---------------------------------------------------------------
/*@{*/
bool atomQuadForInternal_;
MatrixDependencyManager<DenseMatrix, bool> * elementMask_;
LargeToSmallAtomMap * internalToMask_;
AtomTypeElement * internalElement_;
AtomTypeElement * ghostElement_;
NodalGeometryType * nodalGeometryType_;
/*@}*/
/** \name boundary integration */
/*@{*/
/** boundary flux quadrature */
int bndyIntType_;
const set< pair<int,int> > * bndyFaceSet_;
set<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
/*@}*/
// workspace variables
mutable DENS_MAT _deltaQuantity_;
};
};
#endif

View File

@ -0,0 +1,728 @@
// ATC_Transfer headers
#include "ATC_CouplingEnergy.h"
#include "Thermostat.h"
#include "ATC_Error.h"
#include "PrescribedDataManager.h"
#include "FieldManager.h"
// Other Headers
#include <vector>
#include <set>
#include <utility>
#include <typeinfo>
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class ATC_CouplingEnergy
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ATC_CouplingEnergy::ATC_CouplingEnergy(string groupName,
double ** & perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile,
ExtrinsicModelType extrinsicModel)
: ATC_Coupling(groupName,perAtomArray,thisFix),
nodalAtomicHeatCapacity_(NULL),
nodalAtomicKineticTemperature_(NULL),
nodalAtomicConfigurationalTemperature_(NULL)
{
// Allocate PhysicsModel
create_physics_model(THERMAL, matParamFile);
// create extrinsic physics model
if (extrinsicModel != NO_MODEL) {
extrinsicModelManager_.create_model(extrinsicModel,matParamFile);
}
// Defaults
set_time();
bndyIntType_ = FE_INTERPOLATION;
// set up field data based on physicsModel
physicsModel_->num_fields(fieldSizes_,fieldMask_);
// set up atomic regulator
atomicRegulator_ = new Thermostat(this);
// set up physics specific time integrator and thermostat
timeIntegrators_[TEMPERATURE] = new ThermalTimeIntegrator(this,TimeIntegrator::GEAR);
// default physics
temperatureDef_ = KINETIC;
// output variable vector info:
// output[1] = total coarse scale thermal energy
// output[2] = average temperature
vectorFlag_ = 1;
sizeVector_ = 2;
scalarVectorFreq_ = 1;
extVector_ = 1;
if (extrinsicModel != NO_MODEL)
sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_);
// create PE per atom ccompute
//lammpsInterface_->create_compute_pe_peratom();
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ATC_CouplingEnergy::~ATC_CouplingEnergy()
{
// clear out all managed memory to avoid conflicts with dependencies on class member data
interscaleManager_.clear();
}
//--------------------------------------------------------
// initialize
// sets up all the necessary data
//--------------------------------------------------------
void ATC_CouplingEnergy::initialize()
{
// Base class initalizations
ATC_Coupling::initialize();
// resetting precedence:
// time integrator -> thermostat -> time filter
// init_filter uses fieldRateNdFiltered which comes from the time integrator,
// which is why the time integrator is initialized first
// set the reference potential, if necessary, because the nodal energy is needed to initialize the time integrator
if (!initialized_) {
if (temperatureDef_==TOTAL) {
PerAtomQuantity<double> * atomicReferencePotential = interscaleManager_.per_atom_quantity("AtomicReferencePotential");
PerAtomQuantity<double> * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy");
atomicReferencePotential->set_quantity() = atomicPotentialEnergy->quantity();
}
}
// other initializations
if (reset_methods()) {
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->initialize();
}
atomicRegulator_->initialize();
}
extrinsicModelManager_.initialize();
// reset thermostat power for time filter initial conditions for special cases
if (timeFilterManager_.need_reset()) {
init_filter();
}
timeFilterManager_.initialize(); // clears need for reset
if (!initialized_) {
// initialize sources based on initial FE temperature
double dt = lammpsInterface_->dt();
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(fieldMask_,fields_,atomicSources_);
// read in field data if necessary
if (useRestart_) {
RESTART_LIST data;
read_restart_data(restartFileName_,data);
useRestart_ = false;
}
// set consistent initial conditions, if requested
if (!timeFilterManager_.filter_dynamics()) {
if (consistentInitialization_) {
DENS_MAT & temperature(fields_[TEMPERATURE].set_quantity());
DENS_MAN * nodalAtomicTemperature(interscaleManager_.dense_matrix("NodalAtomicTemperature"));
const DENS_MAT & atomicTemperature(nodalAtomicTemperature->quantity());
const INT_ARRAY & nodeType(nodalGeometryType_->quantity());
for (int i = 0; i<nNodes_; ++i) {
if (nodeType(i,0)==MD_ONLY)
temperature(i,0) = atomicTemperature(i,0);
}
}
}
initialized_ = true;
}
// reset integration field mask
temperatureMask_.reset(NUM_FIELDS,NUM_FLUX);
temperatureMask_ = false;
for (int i = 0; i < NUM_FLUX; i++)
temperatureMask_(TEMPERATURE,i) = fieldMask_(TEMPERATURE,i);
}
//--------------------------------------------------------
// construct_methods
// have managers instantiate requested algorithms
// and methods
//--------------------------------------------------------
void ATC_CouplingEnergy::construct_methods()
{
ATC_Coupling::construct_methods();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_methods();
}
atomicRegulator_->construct_methods();
}
//--------------------------------------------------------
// construct_transfers
// constructs needed transfer operators
//--------------------------------------------------------
void ATC_CouplingEnergy::construct_transfers()
{
ATC_Coupling::construct_transfers();
// always need kinetic energy
AtomicEnergyForTemperature * atomicTwiceKineticEnergy = new TwiceKineticEnergy(this);
AtomicEnergyForTemperature * atomEnergyForTemperature = NULL;
// Appropriate per-atom quantity based on desired temperature definition
if (temperatureDef_==KINETIC) {
atomEnergyForTemperature = atomicTwiceKineticEnergy;
}
else if (temperatureDef_==TOTAL) {
if (timeIntegrators_[TEMPERATURE]->time_integration_type() != TimeIntegrator::FRACTIONAL_STEP)
throw ATC_Error("ATC_CouplingEnergy:construct_transfers() on the fractional step time integrator can be used with non-kinetic defitions of the temperature");
// kinetic energy
interscaleManager_.add_per_atom_quantity(atomicTwiceKineticEnergy,
"AtomicTwiceKineticEnergy");
// atomic potential energy
//ComputedAtomQuantity * atomicPotentialEnergy = new ComputedAtomQuantity(this,lammpsInterface_->compute_pe_name(), 1./(lammpsInterface_->mvv2e()));
//interscaleManager_.add_per_atom_quantity(atomicPotentialEnergy,
// "AtomicPotentialEnergy");
PerAtomQuantity<double> * atomicPotentialEnergy = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy");
/////////////////////////////////////
FieldManager fmgr(this);
fmgr.nodal_atomic_field(REFERENCE_POTENTIAL_ENERGY);
PerAtomQuantity<double> * atomicReferencePotential =
interscaleManager_.per_atom_quantity("AtomicReferencePotential");
/////////////////////////////////////
// fluctuating potential energy
AtomicEnergyForTemperature * atomicFluctuatingPotentialEnergy = new FluctuatingPotentialEnergy(this,
atomicPotentialEnergy,
atomicReferencePotential);
interscaleManager_.add_per_atom_quantity(atomicFluctuatingPotentialEnergy,
"AtomicFluctuatingPotentialEnergy");
// atomic total energy
atomEnergyForTemperature = new MixedKePeEnergy(this,1,1);
// kinetic temperature measure for post-processing
// nodal restriction of the atomic energy quantity for the temperature definition
AtfShapeFunctionRestriction * nodalAtomicTwiceKineticEnergy = new AtfShapeFunctionRestriction(this,
atomicTwiceKineticEnergy,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicTwiceKineticEnergy,
"NodalAtomicTwiceKineticEnergy");
nodalAtomicKineticTemperature_ = new AtfShapeFunctionMdProjection(this,
nodalAtomicTwiceKineticEnergy,
TEMPERATURE);
interscaleManager_.add_dense_matrix(nodalAtomicKineticTemperature_,
"NodalAtomicKineticTemperature");
// potential temperature measure for post-processing (must multiply by 2 for configurational temperature
// nodal restriction of the atomic energy quantity for the temperature definition
AtfShapeFunctionRestriction * nodalAtomicFluctuatingPotentialEnergy = new AtfShapeFunctionRestriction(this,
atomicFluctuatingPotentialEnergy,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicFluctuatingPotentialEnergy,
"NodalAtomicFluctuatingPotentialEnergy");
nodalAtomicConfigurationalTemperature_ = new AtfShapeFunctionMdProjection(this,
nodalAtomicFluctuatingPotentialEnergy,
TEMPERATURE);
interscaleManager_.add_dense_matrix(nodalAtomicConfigurationalTemperature_,
"NodalAtomicConfigurationalTemperature");
}
// register the per-atom quantity for the temperature definition
interscaleManager_.add_per_atom_quantity(atomEnergyForTemperature,
"AtomicEnergyForTemperature");
// nodal restriction of the atomic energy quantity for the temperature definition
AtfShapeFunctionRestriction * nodalAtomicEnergy = new AtfShapeFunctionRestriction(this,
atomEnergyForTemperature,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicEnergy,
"NodalAtomicEnergy");
// nodal atomic temperature field
AtfShapeFunctionMdProjection * nodalAtomicTemperature = new AtfShapeFunctionMdProjection(this,
nodalAtomicEnergy,
TEMPERATURE);
interscaleManager_.add_dense_matrix(nodalAtomicTemperature,
"NodalAtomicTemperature");
if (!useFeMdMassMatrix_) {
// classical thermodynamic heat capacity of the atoms
HeatCapacity * heatCapacity = new HeatCapacity(this);
interscaleManager_.add_per_atom_quantity(heatCapacity,
"AtomicHeatCapacity");
// atomic thermal mass matrix
nodalAtomicHeatCapacity_ = new AtfShapeFunctionRestriction(this,
heatCapacity,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicHeatCapacity_,
"NodalAtomicHeatCapacity");
}
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_transfers();
}
atomicRegulator_->construct_transfers();
}
//---------------------------------------------------------
// init_filter
// sets up the time filtering operations in all objects
//---------------------------------------------------------
void ATC_CouplingEnergy::init_filter()
{
TimeIntegrator::TimeIntegrationType timeIntegrationType = timeIntegrators_[TEMPERATURE]->time_integration_type();
if (timeFilterManager_.end_equilibrate()) {
if (timeIntegrationType==TimeIntegrator::GEAR) {
if (equilibriumStart_) {
if (atomicRegulator_->regulator_target()==AtomicRegulator::DYNAMICS) { // based on FE equation
DENS_MAT vdotflamMat(-2.*(nodalAtomicFields_[TEMPERATURE].quantity())); // note 2 is for 1/2 vdotflam addition
atomicRegulator_->reset_lambda_contribution(vdotflamMat);
}
else { // based on MD temperature equation
DENS_MAT vdotflamMat(-1.*(nodalAtomicFields_[TEMPERATURE].quantity()));
atomicRegulator_->reset_lambda_contribution(vdotflamMat);
}
}
}
else if (timeIntegrationType==TimeIntegrator::FRACTIONAL_STEP) {
if (equilibriumStart_) {
DENS_MAT powerMat(-1.*(nodalAtomicFields_[TEMPERATURE].quantity()));
atomicRegulator_->reset_lambda_contribution(powerMat);
}
}
}
}
//---------------------------------------------------------
// compute_md_mass_matrix
// compute the mass matrix arising from only atomistic
// quadrature and contributions as a summation
//---------------------------------------------------------
void ATC_CouplingEnergy::compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMat)
{
if (thisField == TEMPERATURE)
massMat.reset(nodalAtomicHeatCapacity_->quantity());
}
//--------------------------------------------------------
// finish
// final clean up after a run
//--------------------------------------------------------
void ATC_CouplingEnergy::finish()
{
// base class
ATC_Coupling::finish();
atomicRegulator_->finish();
}
//--------------------------------------------------------
// modify
// parses inputs and modifies state of the filter
//--------------------------------------------------------
bool ATC_CouplingEnergy::modify(int narg, char **arg)
{
bool foundMatch = false;
int argIndx = 0;
// check to see if input is a transfer class command
// check derived class before base class
// pass-through to thermostat
if (strcmp(arg[argIndx],"control")==0) {
argIndx++;
foundMatch = atomicRegulator_->modify(narg-argIndx,&arg[argIndx]);
}
// pass-through to timeIntegrator class
else if (strcmp(arg[argIndx],"time_integration")==0) {
argIndx++;
foundMatch = timeIntegrators_[TEMPERATURE]->modify(narg-argIndx,&arg[argIndx]);
}
// switch for the kind of temperature being used
/*! \page man_temperature_definition fix_modify AtC temperature_definition
\section syntax
fix_modify AtC temperature_definition <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_);
foundMatch = true;
needReset_ = true;
}
// no match, call base class parser
if (!foundMatch) {
foundMatch = ATC_Coupling::modify(narg, arg);
}
return foundMatch;
}
//--------------------------------------------------
// pack_fields
// bundle all allocated field matrices into a list
// for output needs
//--------------------------------------------------
void ATC_CouplingEnergy::pack_thermal_fields(RESTART_LIST & data)
{
atomicRegulator_->pack_fields(data);
}
//--------------------------------------------------
// write_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingEnergy::write_restart_data(string fileName, RESTART_LIST & data)
{
pack_thermal_fields(data);
ATC_Method::write_restart_data(fileName,data);
}
//--------------------------------------------------
// read_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingEnergy::read_restart_data(string fileName, RESTART_LIST & data)
{
pack_thermal_fields(data);
ATC_Method::read_restart_data(fileName,data);
}
//--------------------------------------------------
void ATC_CouplingEnergy::reset_nlocal()
{
ATC_Coupling::reset_nlocal();
atomicRegulator_->reset_nlocal();
}
//--------------------------------------------------
// reset_atom_materials
// update the atom materials map
//--------------------------------------------------
void ATC_CouplingEnergy::reset_atom_materials()
{
ATC_Coupling::reset_atom_materials();
atomicRegulator_->reset_atom_materials(elementToMaterialMap_,
atomElement_);
}
//--------------------------------------------------
// pre_init_integrate
// time integration before the lammps atomic
// integration of the Verlet step 1
//--------------------------------------------------
void ATC_CouplingEnergy::pre_init_integrate()
{
ATC_Coupling::pre_init_integrate();
double dt = lammpsInterface_->dt();
// Perform any initialization, no actual integration
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate1(dt);
}
// Apply thermostat force to atom velocities
atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep());
// Predict nodal temperatures and time derivatives based on FE data
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate2(dt);
}
extrinsicModelManager_.pre_init_integrate();
}
//--------------------------------------------------------
// mid_init_integrate
// time integration between the velocity update and
// the position lammps update of Verlet step 1
//--------------------------------------------------------
void ATC_CouplingEnergy::mid_init_integrate()
{
// CONTINUOUS VELOCITY UPDATE
ATC_Coupling::mid_init_integrate();
double dt = lammpsInterface_->dt();
// Compute nodal velocity at n+1/2
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->mid_initial_integrate1(dt);
}
atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep());
extrinsicModelManager_.mid_init_integrate();
}
//--------------------------------------------------------
// post_init_integrate
// time integration after the lammps atomic updates of
// Verlet step 1
//--------------------------------------------------------
void ATC_CouplingEnergy::post_init_integrate()
{
double dt = lammpsInterface_->dt();
// Compute nodal velocity at n+1
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_initial_integrate1(dt);
}
// Update kinetostat quantities if displacement is being regulated
atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep());
// Update extrisic model
extrinsicModelManager_.post_init_integrate();
// fixed values, non-group bcs handled through FE
set_fixed_nodes();
update_time(0.5);
ATC_Coupling::post_init_integrate();
}
//--------------------------------------------------------
// post_final_integrate
// integration after the second stage lammps atomic
// update of Verlet step 2
//--------------------------------------------------------
void ATC_CouplingEnergy::post_final_integrate()
{
double dt = lammpsInterface_->dt();
// update changes in atomic energy or from atomic work, if needed
// this is here to simplify computing changes in total atomic energy
// even though all the data needed is available by pre_final_integrate
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_final_integrate1(dt);
}
// Set prescribed sources for current time
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
// predictor step in extrinsic model
extrinsicModelManager_.pre_final_integrate();
// predict thermostat contributions
// compute sources based on predicted FE temperature
if (timeIntegrators_[TEMPERATURE]->has_final_predictor()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(temperatureMask_,fields_,atomicSources_);
}
// Compute thermostat forces
atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep());
// Determine FE contributions to d theta/dt
// Compute atom-integrated rhs
// parallel communication happens within FE_Engine
// Determine FE contributions to dT/dt-----------------------
compute_rhs_vector(temperatureMask_,fields_,rhs_,FE_DOMAIN);
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->add_to_rhs();
}
// For flux matching, add appropriate fraction of "drag" power
atomicRegulator_->add_to_rhs(rhs_);
// final phase predictor step
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate1(dt);
}
// fix nodes, non-group bcs applied through FE
set_fixed_nodes();
// corrector step extrinsic model
extrinsicModelManager_.post_final_integrate();
// correct thermostat and finish
if (timeIntegrators_[TEMPERATURE]->has_final_corrector()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(temperatureMask_,fields_,atomicSources_);
}
// finish FE temperature update
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate2(dt);
}
// apply corrector phase of thermostat
atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep());
// finalalize time filtering
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate3(dt);
}
// Fix nodes, non-group bcs applied through FE
set_fixed_nodes();
update_time(0.5);
output();
ATC_Coupling::post_final_integrate(); // adds next step to computes
}
//--------------------------------------------------------------------
// compute_vector
//--------------------------------------------------------------------
// this is for direct output to lammps thermo
double ATC_CouplingEnergy::compute_vector(int n)
{
// output[1] = total coarse scale thermal energy
// output[2] = average temperature
double mvv2e = lammpsInterface_->mvv2e(); // convert to lammps energy units
if (n == 0) {
Array<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();
}
}
};

View File

@ -0,0 +1,127 @@
#ifndef ATC_COUPLING_ENERGY_H
#define ATC_COUPLING_ENERGY_H
// ATC headers
#include "ATC_Coupling.h"
#include "ThermalTimeIntegrator.h"
//TEMP_JAT - remove when needs_reset is moved to base class
#include "AtomicRegulator.h"
// Other headers
#include <map>
namespace ATC {
class AtfShapeFunctionRestriction;
class AtfShapeFunctionMdProjection;
/**
* @class ATC_CouplingEnergy
* @brief A class for atom-continuum transfers & control involving heat transport
* (owned field/s: TEMPERATURE)
*/
class ATC_CouplingEnergy : public ATC_Coupling {
public:
// constructor
ATC_CouplingEnergy(std::string groupName,
double ** & perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile,
ExtrinsicModelType extrinsic = NO_MODEL);
// destructor
virtual ~ATC_CouplingEnergy();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** flags whether a methods reset is required */
virtual bool reset_methods() const {
bool resetMethods = ATC_Method::reset_methods() || atomicRegulator_->need_reset();
_ctiIt_ = timeIntegrators_.find(TEMPERATURE);
if (_ctiIt_ == timeIntegrators_.end()) return resetMethods;
return resetMethods || (_ctiIt_->second)->need_reset();
};
/** post time integration */
virtual void finish();
/** first time substep routines */
virtual void pre_init_integrate();
/** first time, after atomic velocity but before position integration */
virtual void mid_init_integrate();
/** first time, after atomic integration */
virtual void post_init_integrate();
/** second time substep routine */
virtual void post_final_integrate();
/** compute vector for output */
virtual double compute_vector(int n);
/** output */
virtual void output();
/** set up atom to material identification */
virtual void reset_atom_materials();
protected:
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** constructs all data which is updated with time integration, i.e. fields */
//virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
/** sets the position/velocity of the ghost atoms */
virtual void set_ghost_atoms(){};
/** adds resetting of any thermostat arrays associated with local atom count */
virtual void reset_nlocal();
/** compute the mass matrix components coming from MD integration */
virtual void compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMats);
/** operator to compute mass matrix from MD */
AtfShapeFunctionRestriction * nodalAtomicHeatCapacity_;
/** physics specific filter initialization */
void init_filter();
/** field mask for velocity integration */
Array2D<bool> temperatureMask_;
double compute_lambda_power(int gid);
/** kinetic temperature for post-processing */
AtfShapeFunctionMdProjection * nodalAtomicKineticTemperature_;
/** configurational temperature for post-processing */
AtfShapeFunctionMdProjection * nodalAtomicConfigurationalTemperature_;
/** workspace matrices for output */
DENS_MAT _keTemp_, _peTemp_;
// Add in fields for restarting
virtual void read_restart_data(string fileName_, RESTART_LIST & data);
virtual void write_restart_data(string fileName_, RESTART_LIST & data);
void pack_thermal_fields(RESTART_LIST & data);
};
};
#endif

View File

@ -0,0 +1,544 @@
// ATC_Transfer headers
#include "ATC_CouplingMass.h"
#include "ATC_Error.h"
#include "FE_Engine.h"
#include "SpeciesTimeIntegrator.h"
#include "PrescribedDataManager.h"
#include "ExtrinsicModelElectrostatic.h"
#include "PoissonSolver.h"
#include "ChargeRegulator.h"
#include "ConcentrationRegulator.h"
#include "PerAtomQuantityLibrary.h"
#include "TransferOperator.h"
#include "AtomToMoleculeTransfer.h"
#include "MoleculeSet.h"
#include "FieldManager.h"
// Other Headers
#include <vector>
#include <map>
#include <set>
#include <utility>
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class ATC_CouplingMass
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ATC_CouplingMass::ATC_CouplingMass(string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile,
ExtrinsicModelType extrinsicModel)
: ATC_Coupling(groupName,perAtomArray,thisFix),
resetNlocal_(false)
{
// Allocate PhysicsModel
create_physics_model(SPECIES, matParamFile);
// create extrinsic physics model
if (extrinsicModel != NO_MODEL) {
extrinsicModelManager_.create_model(extrinsicModel,matParamFile);
}
// Defaults
set_time();
bndyIntType_ = NO_QUADRATURE;
// set up field data based on physicsModel
physicsModel_->num_fields(fieldSizes_,fieldMask_);
// regulator
atomicRegulator_ = new ConcentrationRegulator(this);
// set up physics specific time integrator
//WIP_JAT should be species concentration
timeIntegrators_[MASS_DENSITY] = new SpeciesTimeIntegrator(this,TimeIntegrator::FRACTIONAL_STEP);
// output variable vector info:
// output[1] = system mass density
vectorFlag_ = 1;
sizeVector_ = 0;
scalarVectorFreq_ = 1;
extVector_ = 1;
if (extrinsicModel != NO_MODEL)
sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_);
sizeVector_ += atomicRegulator_->size_vector(sizeVector_);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ATC_CouplingMass::~ATC_CouplingMass()
{
interscaleManager_.clear();
}
//--------------------------------------------------------
// modify
// parses inputs and modifies state
//--------------------------------------------------------
bool ATC_CouplingMass::modify(int narg, char **arg)
{
bool match = false;
// check to see if it is a transfer class command
// check derived class before base class
int argIndex = 0;
// pass-through to concentration regulator
if (strcmp(arg[argIndex],"control")==0) {
argIndex++;
if (strcmp(arg[argIndex],"concentration")==0) {
argIndex++;
match = atomicRegulator_->modify(narg-argIndex,&arg[argIndex]);
}
}
// no match, call base class parser
if (!match) {
match = ATC_Coupling::modify(narg, arg);
}
return match;
}
//--------------------------------------------------------
// initialize
// sets up all the necessary data
//--------------------------------------------------------
void ATC_CouplingMass::initialize()
{
fieldSizes_[SPECIES_CONCENTRATION] = speciesIds_.size();
// Base class initalizations
ATC_Coupling::initialize();
// check that only all atoms
if (bndyIntType_ != NO_QUADRATURE) throw ATC_Error("ATC_CouplingMass: only all atoms simulations are supported");
// set consistent initial conditions, if requested
if (!timeFilterManager_.filter_dynamics()) {
if (consistentInitialization_) {
DENS_MAT & massDensity(fields_[MASS_DENSITY].set_quantity());
const DENS_MAT & atomicMassDensity(nodalAtomicFields_[MASS_DENSITY].quantity());
DENS_MAT & speciesConcentration(fields_[SPECIES_CONCENTRATION].set_quantity());
//const DENS_MAT & atomicSpeciesConcentration(nodalAtomicFields_[SPECIES_CONCENTRATION].quantity());
const DENS_MAT & atomicSpeciesConcentration(atomicFields_[SPECIES_CONCENTRATION]->quantity());
const INT_ARRAY & nodeType(nodalGeometryType_->quantity());
for (int i = 0; i<nNodes_; ++i) {
if (nodeType(i,0)==MD_ONLY) {
massDensity(i,0) = atomicMassDensity(i,0);
for (int j = 0; j < atomicSpeciesConcentration.nCols(); ++j) {
speciesConcentration(i,j) = atomicSpeciesConcentration(i,j);
}
}
}
}
}
// other initializatifields_[SPECIES_CONCENTRATION].quantity()ons
if (reset_methods()) {
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->initialize();
}
}
extrinsicModelManager_.initialize(); // always needed to construct new Poisson solver
if (timeFilterManager_.need_reset()) { init_filter(); }
timeFilterManager_.initialize(); // clears need for reset
atomicRegulator_->initialize();
if (!initialized_) {
// initialize sources based on initial FE temperature
double dt = lammpsInterface_->dt();
// set sources
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
compute_atomic_sources(fieldMask_,fields_,atomicSources_);
// read in field data if necessary
if (useRestart_) {
RESTART_LIST data;
read_restart_data(restartFileName_,data);
useRestart_ = false;
}
initialized_ = true;
}
// reset integration field mask
speciesMask_.reset(NUM_FIELDS,NUM_FLUX);
speciesMask_ = false;
}
//--------------------------------------------------------
// construct_methods
// have managers instantiate requested algorithms
// and methods
//--------------------------------------------------------
void ATC_CouplingMass::construct_methods()
{
ATC_Coupling::construct_methods();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_methods();
}
atomicRegulator_->construct_methods();
}
void ATC_CouplingMass::construct_transfers()
{
ATC_Coupling::construct_transfers();
FieldManager fmgr(this);
atomicFields_[MASS_DENSITY] = fmgr.nodal_atomic_field(MASS_DENSITY, field_to_intrinsic_name(MASS_DENSITY));
if (has_tracked_species()) {
atomicFields_[SPECIES_CONCENTRATION] = fmgr.nodal_atomic_field(SPECIES_CONCENTRATION, field_to_intrinsic_name(SPECIES_CONCENTRATION));
//if (atomicRegulator_->needs_temperature()) {
atomicFields_[TEMPERATURE] = fmgr.nodal_atomic_field(KINETIC_TEMPERATURE, field_to_intrinsic_name(TEMPERATURE));
//atomicFields_[TEMPERATURE] = fmgr.nodal_atomic_field(TEMPERATURE, field_to_intrinsic_name(TEMPERATURE));
field(TEMPERATURE) = atomicFields_[TEMPERATURE]->quantity();
//}
}
else {
throw ATC_Error("ATC_CouplingMass: no tracked species");
}
//==========================================================================
// add molecule mass density transfer operators
//==========================================================================
map<string,pair<MolSize,int> >::const_iterator molecule;
FundamentalAtomQuantity * mass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS,
PROC_GHOST);
for (molecule = moleculeIds_.begin(); molecule != moleculeIds_.end(); molecule++) {
const string moleculeName = molecule->first;
SmallMoleculeSet * smallMoleculeSet = interscaleManager_.small_molecule_set(moleculeName);
SPAR_MAN * shpFcnMol = interscaleManager_.sparse_matrix("ShapeFunction"+moleculeName);
AtomToSmallMoleculeTransfer<double> * moleculeMass =
new AtomToSmallMoleculeTransfer<double>(this,mass,smallMoleculeSet);
interscaleManager_.add_dense_matrix(moleculeMass,"MoleculeMass"+moleculeName);
MotfShapeFunctionRestriction * nodalAtomicMoleculeMass =
new MotfShapeFunctionRestriction(moleculeMass,shpFcnMol);
interscaleManager_.add_dense_matrix(nodalAtomicMoleculeMass,"NodalMoleculeMass"+moleculeName);
AtfShapeFunctionMdProjection * nodalAtomicMoleculeMassDensity =
new AtfShapeFunctionMdProjection(this,nodalAtomicMoleculeMass,MASS_DENSITY);
interscaleManager_.add_dense_matrix(nodalAtomicMoleculeMassDensity,"NodalMoleculeMassDensity"+moleculeName);
}
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_transfers();
}
}
void ATC_CouplingMass::init_filter()
{
ATC_Coupling::init_filter();
}
void ATC_CouplingMass::compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMat)
{
if (thisField == MASS_DENSITY ||
thisField == SPECIES_CONCENTRATION) {
massMat.reset(nodalAtomicVolume_->quantity());
}
}
//--------------------------------------------------
// pack_fields
// bundle all allocated field matrices into a list
// for output needs
//--------------------------------------------------
void ATC_CouplingMass::pack_species_fields(RESTART_LIST & data)
{
}
//--------------------------------------------------
// write_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingMass::write_restart_data(string fileName, RESTART_LIST & data)
{
pack_species_fields(data);
ATC_Method::write_restart_data(fileName,data);
}
//--------------------------------------------------
// write_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingMass::read_restart_data(string fileName, RESTART_LIST & data)
{
pack_species_fields(data);
ATC_Method::read_restart_data(fileName,data);
}
//--------------------------------------------------------
// pre_force
// prior to calculation of forces
//--------------------------------------------------------
void ATC_CouplingMass::pre_force()
{
ATC_Coupling::pre_force();
atomicRegulator_->pre_force();
}
//--------------------------------------------------------
// pre_exchange
// prior to exchange of atoms
//--------------------------------------------------------
void ATC_CouplingMass::pre_exchange()
{
ATC_Coupling::pre_exchange();
//if (atomicRegulator_->needs_temperature()) {
field(TEMPERATURE) = atomicFields_[TEMPERATURE]->quantity();
///}
atomicRegulator_->pre_exchange();
if (resetNlocal_) {
this->reset_nlocal();
resetNlocal_ = false;
}
}
//--------------------------------------------------------
// pre_init_integrate
// time integration before the lammps atomic
// integration of the Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMass::pre_init_integrate()
{
ATC_Coupling::pre_init_integrate();
double dt = lammpsInterface_->dt();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate1(dt);
}
// Apply thermostat force to atom velocities
atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep());
// Predict nodal temperatures and time derivatives based on FE data
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate2(dt);
}
extrinsicModelManager_.pre_init_integrate();
}
//--------------------------------------------------------
// mid_init_integrate
// time integration between the velocity update and
// the position lammps update of Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMass::mid_init_integrate()
{
ATC_Coupling::mid_init_integrate();
double dt = lammpsInterface_->dt();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->mid_initial_integrate1(dt);
}
atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep());
extrinsicModelManager_.mid_init_integrate();
}
//--------------------------------------------------------
// post_init_integrate
// time integration after the lammps atomic updates of
// Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMass::post_init_integrate()
{
double dt = lammpsInterface_->dt();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_initial_integrate1(dt);
}
atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep());
extrinsicModelManager_.post_init_integrate();
set_fixed_nodes();
update_time(0.5); // half step
ATC_Coupling::post_init_integrate();
}
//--------------------------------------------------------
// post_final_integrate
// integration after the second stage lammps atomic
// update of Verlet step 2
//--------------------------------------------------------
void ATC_CouplingMass::post_final_integrate()
{
double dt = lammpsInterface_->dt();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_final_integrate1(dt);
}
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
extrinsicModelManager_.pre_final_integrate();
if (timeIntegrators_[MASS_DENSITY]->has_final_predictor()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
compute_atomic_sources(speciesMask_,fields_,atomicSources_);
}
// set state-based RHS
// Determine FE contributions to dv/dt-----------------------
// Compute atom-integrated rhs
// parallel communication happens within FE_Engine
compute_rhs_vector(speciesMask_,fields_,rhs_,FE_DOMAIN);
// Compute and add atomic contributions to FE equations
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->add_to_rhs();
}
atomicRegulator_->add_to_rhs(rhs_);
// final phase predictor step
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate1(dt);
}
set_fixed_nodes();
// corrector step extrinsic model
extrinsicModelManager_.post_final_integrate();
if (timeIntegrators_[MASS_DENSITY]->has_final_corrector()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
compute_atomic_sources(speciesMask_,fields_,atomicSources_);
}
// finish FE temperature update
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate2(dt);
}
// apply corrector phase of thermostat
atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep());
// finalize time integration
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate3(dt);
}
// Fix nodes, non-group bcs applied through FE
set_fixed_nodes();
update_time(0.5);
output();
ATC_Coupling::post_final_integrate(); // addstep for computes
}
//--------------------------------------------------------
// output
// does post-processing steps and outputs data
//--------------------------------------------------------
void ATC_CouplingMass::output()
{
if (output_now()) {
feEngine_->departition_mesh();
OUTPUT_LIST outputData;
// base class output
ATC_Coupling::output();
// push atc fields time integrator modifies into output arrays
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_process();
}
// auxilliary data
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->output(outputData);
}
extrinsicModelManager_.output(outputData);
atomicRegulator_->output(outputData);
FIELD_POINTERS::iterator itr;
for (itr=atomicFields_.begin(); itr!=atomicFields_.end();itr++) {
FieldName name = itr->first;
const DENS_MAT & data = (itr->second)->quantity();
outputData[field_to_intrinsic_name(name)] = & data;
}
// compute partial forces
int * type =lammpsInterface_->atom_type();
double ** f =lammpsInterface_->fatom();
for (unsigned int j = 0; j < typeList_.size(); j++) {
string speciesName = typeNames_[j];
int sType = typeList_[j];
double localF[3] = {0,0,0}, F[3] = {0,0,0};
for (int i = 0; i < nLocal_; i++) {
int a = internalToAtom_(i);
if (sType == type[a]) {
double * fa = f[a];
localF[0] += fa[0];
localF[1] += fa[1];
localF[2] += fa[2];
}
}
lammpsInterface_->allsum(localF,F,3);
if (lammpsInterface_->rank_zero()) {
for (int i = 0; i < 3; ++i) {
feEngine_->add_global(speciesName+"_F"+to_string(i+1), F[i]);
}
}
}
if (lammpsInterface_->rank_zero()) {
// tagged data
map<string,DENS_MAN>::iterator densMan;
for (densMan = taggedDensMan_.begin(); densMan != taggedDensMan_.end(); densMan++) {
outputData[densMan->first] = & (densMan->second).set_quantity();
}
feEngine_->write_data(output_index(), fields_, & outputData);
}
// force reset of tagged data to keep in sync
map<string,DENS_MAN>::iterator densMan;
for (densMan = taggedDensMan_.begin(); densMan != taggedDensMan_.end(); densMan++)
(densMan->second).force_reset();
feEngine_->partition_mesh();
}
}
//--------------------------------------------------------------------
// compute_vector
//--------------------------------------------------------------------
// this is for direct output to lammps thermo
double ATC_CouplingMass::compute_vector(int n)
{
return atomicRegulator_->compute_vector(n);
}
};

114
lib/atc/ATC_CouplingMass.h Normal file
View File

@ -0,0 +1,114 @@
#ifndef ATC_COUPLING_MASS_H
#define ATC_COUPLING_MASS_H
/** owned field/s: MASS_DENSITY */
// ATC headers
#include "ATC_Coupling.h"
// Other headers
#include <map>
namespace ATC {
// Forward declarations
class FE_Engine;
class SpeciesTimeIntegrator;
class ChargeRegulator;
class ConcentrationRegulator;
/**
* @class ATC_CouplingMass
* @brief A class for atom-continuum transfers & control for species transport
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ATC_CouplingMass
//--------------------------------------------------------
//--------------------------------------------------------
class ATC_CouplingMass : public ATC_Coupling {
public:
// constructor
ATC_CouplingMass(std::string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile,
ExtrinsicModelType extrinsic = NO_MODEL);
// destructor
virtual ~ATC_CouplingMass();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** prior to force */
virtual void pre_force();
/** prior to exchange */
virtual void pre_exchange();
virtual void reset_atoms() { resetNlocal_=true;}
/** first time, before atomic integration */
virtual void pre_init_integrate();
/** first time, after atomic velocity but before position integration */
virtual void mid_init_integrate();
/** first time, after atomic integration */
virtual void post_init_integrate();
/** second time, after atomic integration */
virtual void post_final_integrate();
/** compute vector for output */
virtual double compute_vector(int n);
/** output routines */
virtual void output();
protected:
// functions
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** constructs all data which is updated with time integration, i.e. fields */
//virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
/** sets the position/velocity of the ghost atoms */
virtual void set_ghost_atoms(){};
/** compute the mass matrix components coming from MD integration */
virtual void compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMats);
/** physics specific filter initialization */
void init_filter();
/** field mask for velocity integration */
Array2D<bool> speciesMask_;
// Add in fields for restarting
virtual void read_restart_data(string fileName_, RESTART_LIST & data);
virtual void write_restart_data(string fileName_, RESTART_LIST & data);
void pack_species_fields(RESTART_LIST & data);
// DATA structures for tracking individual species and molecules
FIELD_POINTERS atomicFields_;
bool resetNlocal_;
// i.e. we only need the correct shape function matrix for restriction
};
};
#endif

View File

@ -0,0 +1,978 @@
// ATC headers
#include "ATC_CouplingMomentum.h"
#include "ATC_Error.h"
#include "LammpsInterface.h"
#include "PrescribedDataManager.h"
#include "PerAtomQuantity.h"
#include "TransferOperator.h"
// Other Headers
#include <vector>
#include <map>
#include <set>
#include <utility>
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class ATC_CouplingMomentum
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ATC_CouplingMomentum::ATC_CouplingMomentum(string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile,
PhysicsType intrinsicModel,
ExtrinsicModelType extrinsicModel)
: ATC_Coupling(groupName,perAtomArray,thisFix),
nodalAtomicMass_(NULL),
nodalAtomicCount_(NULL),
boundaryDynamics_(PRESCRIBED),
gamma_(0),mu_(1),kappa_(1),
refPE_(0)
{
// Allocate PhysicsModel
create_physics_model(intrinsicModel, matParamFile);
// create extrinsic physics model
if (extrinsicModel != NO_MODEL) {
extrinsicModelManager_.create_model(extrinsicModel,matParamFile);
}
// set up field data based on physicsModel
physicsModel_->num_fields(fieldSizes_,fieldMask_);
// Defaults
set_time();
bndyIntType_ = FE_INTERPOLATION;
trackCharge_ = false;
// use a kinetostat
atomicRegulator_ = new Kinetostat(this);
// set time integrator and change any defaults based on model type
if (intrinsicModel == ELASTIC) {
trackDisplacement_ = true;
fieldSizes_[DISPLACEMENT] = fieldSizes_[VELOCITY];
timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::VERLET);
}
else if (intrinsicModel == SHEAR) {
atomToElementMapType_ = EULERIAN;
atomToElementMapFrequency_ = 1;
timeIntegrators_[VELOCITY] = new MomentumTimeIntegrator(this,TimeIntegrator::GEAR);
}
// output variable vector info:
// output[1] = total coarse scale kinetic energy
// output[2] = total coarse scale potential energy
// output[3] = total coarse scale energy
scalarFlag_ = 1;
vectorFlag_ = 1;
sizeVector_ = 5;
scalarVectorFreq_ = 1;
extVector_ = 1;
thermoEnergyFlag_ = 1;
if (extrinsicModel != NO_MODEL)
sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ATC_CouplingMomentum::~ATC_CouplingMomentum()
{
interscaleManager_.clear();
}
//--------------------------------------------------------
// initialize
// sets up all the necessary data
//--------------------------------------------------------
void ATC_CouplingMomentum::initialize()
{
// clear displacement entries if requested
if (!trackDisplacement_) {
fieldSizes_.erase(DISPLACEMENT);
for (int i = 0; i < NUM_FLUX; i++)
fieldMask_(DISPLACEMENT,i) = false;
}
// Base class initalizations
ATC_Coupling::initialize();
// check resetting precedence:
// time integrator -> kinetostat -> time filter
// other initializations
if (reset_methods()) {
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->initialize();
}
atomicRegulator_->initialize();
}
extrinsicModelManager_.initialize();
if (timeFilterManager_.need_reset()) { // reset kinetostat power
init_filter();
}
timeFilterManager_.initialize(); // clears need for reset
if (!initialized_) {
// initialize sources based on initial FE temperature
double dt = lammpsInterface_->dt();
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(fieldMask_,fields_,atomicSources_);
// read in field data if necessary
if (useRestart_) {
RESTART_LIST data;
read_restart_data(restartFileName_,data);
useRestart_ = false;
}
// set consistent initial conditions, if requested
if (!timeFilterManager_.filter_dynamics()) {
if (consistentInitialization_) {
DENS_MAT & velocity(fields_[VELOCITY].set_quantity());
DENS_MAN * nodalAtomicVelocity(interscaleManager_.dense_matrix("NodalAtomicVelocity"));
const DENS_MAT & atomicVelocity(nodalAtomicVelocity->quantity());
const INT_ARRAY & nodeType(nodalGeometryType_->quantity());
for (int i = 0; i<nNodes_; ++i) {
if (nodeType(i,0)==MD_ONLY) {
for (int j = 0; j < nsd_; j++) {
velocity(i,j) = atomicVelocity(i,j);
}
}
}
if (trackDisplacement_) {
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);
}
}
}
}
}
}
initialized_ = true;
}
// reset integration field mask
velocityMask_.reset(NUM_FIELDS,NUM_FLUX);
velocityMask_ = false;
for (int i = 0; i < NUM_FLUX; i++)
velocityMask_(VELOCITY,i) = fieldMask_(VELOCITY,i);
refPE_=0;
refPE_=potential_energy();
}
//--------------------------------------------------------
// construct_methods
// have managers instantiate requested algorithms
// and methods
//--------------------------------------------------------
void ATC_CouplingMomentum::construct_methods()
{
ATC_Coupling::construct_methods();
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_methods();
}
atomicRegulator_->construct_methods();
}
//--------------------------------------------------------
// construct_transfers
// constructs needed transfer operators
//--------------------------------------------------------
void ATC_CouplingMomentum::construct_transfers()
{
ATC_Coupling::construct_transfers();
// momentum of each atom
AtomicMomentum * atomicMomentum = new AtomicMomentum(this);
interscaleManager_.add_per_atom_quantity(atomicMomentum,
"AtomicMomentum");
// nodal momentum for RHS
AtfShapeFunctionRestriction * nodalAtomicMomentum = new AtfShapeFunctionRestriction(this,
atomicMomentum,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicMomentum,
"NodalAtomicMomentum");
// nodal forces
FundamentalAtomQuantity * atomicForce = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_FORCE);
AtfShapeFunctionRestriction * nodalAtomicForce = new AtfShapeFunctionRestriction(this,
atomicForce,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicForce,
"NodalAtomicForce");
// nodal velocity derived only from atoms
AtfShapeFunctionMdProjection * nodalAtomicVelocity = new AtfShapeFunctionMdProjection(this,
nodalAtomicMomentum,
VELOCITY);
interscaleManager_.add_dense_matrix(nodalAtomicVelocity,
"NodalAtomicVelocity");
if (trackDisplacement_) {
// mass-weighted (center-of-mass) displacement of each atom
AtomicMassWeightedDisplacement * atomicMassWeightedDisplacement;
if (needXrefProcessorGhosts_ || groupbitGhost_) { // explicit construction on internal group
PerAtomQuantity<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");
}
// atomic mass matrix data
if (!useFeMdMassMatrix_) {
// atomic momentum mass matrix
FundamentalAtomQuantity * atomicMass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS);
nodalAtomicMass_ = new AtfShapeFunctionRestriction(this,
atomicMass,
shpFcn_);
interscaleManager_.add_dense_matrix(nodalAtomicMass_,
"AtomicMomentumMassMat");
// atomic dimensionless mass matrix
ConstantQuantity<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");
}
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->construct_transfers();
}
atomicRegulator_->construct_transfers();
}
//---------------------------------------------------------
// init_filter
// sets up the time filtering operations in all objects
//---------------------------------------------------------
void ATC_CouplingMomentum::init_filter()
{
ATC_Coupling::init_filter();
if (timeFilterManager_.end_equilibrate() && equilibriumStart_) // set up correct initial lambda forces to enforce initial accerlation
if (atomicRegulator_->coupling_mode()==AtomicRegulator::FLUX || atomicRegulator_->coupling_mode()==AtomicRegulator::GHOST_FLUX)
// nothing needed in other cases since kinetostat force is balanced by boundary flux in FE equations
atomicRegulator_->reset_lambda_contribution(nodalAtomicFieldsRoc_[VELOCITY].quantity());
}
//---------------------------------------------------------
// compute_md_mass_matrix
// compute the mass matrix arising from only atomistic
// quadrature and contributions as a summation
//---------------------------------------------------------
void ATC_CouplingMomentum::compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMat)
{
if (thisField == DISPLACEMENT || thisField == VELOCITY)
massMat.reset(nodalAtomicMass_->quantity());
else if (thisField == MASS_DENSITY) { // dimensionless mass matrix
massMat.reset(nodalAtomicCount_->quantity());
}
}
//--------------------------------------------------------
// finish
// final clean up after a run
//--------------------------------------------------------
void ATC_CouplingMomentum::finish()
{
// base class
ATC_Coupling::finish();
atomicRegulator_->finish();
}
//--------------------------------------------------------
// modify
// parses inputs and modifies state of the filter
//--------------------------------------------------------
bool ATC_CouplingMomentum::modify(int narg, char **arg)
{
bool foundMatch = false;
int argIndex = 0;
// check to see if it is a transfer class command
// check derived class before base class
// pass-through to kinetostat
if (strcmp(arg[argIndex],"control")==0) {
argIndex++;
foundMatch = atomicRegulator_->modify(narg-argIndex,&arg[argIndex]);
}
// pass-through to timeIntegrator class
else if (strcmp(arg[argIndex],"time_integration")==0) {
argIndex++;
foundMatch = timeIntegrators_[VELOCITY]->modify(narg-argIndex,&arg[argIndex]);
}
// switch for if displacement is tracked or not
/*! \page man_disp_control fix_modify AtC transfer track_displacement
\section syntax
fix_modify AtC transfer track_displacement <on/off> \n
\section description
Determines whether displacement is tracked or not. For solids problems this is a useful quantity, but for fluids it is not relevant.
\section restrictions
Some constitutive models require the displacement field
\section default
on
*/
else if (strcmp(arg[argIndex],"track_displacement")==0) {
argIndex++;
if (strcmp(arg[argIndex],"on")==0) {
trackDisplacement_ = true;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"off")==0) {
trackDisplacement_ = false;
foundMatch = true;
}
if (foundMatch) {
needReset_ = true;
}
}
/*! \page man_boundary_dynamics fix_modify AtC transfer boundary_dynamics
\section syntax
fix_modify AtC transfer boundary_dynamics <type> \n
\section description
\section restrictions
\section default
on
*/
else if (strcmp(arg[argIndex],"boundary_dynamics")==0) {
argIndex++;
gamma_ = 0;
kappa_ = 0;
mu_ = 0;
if (strcmp(arg[argIndex],"damped_harmonic")==0) {
argIndex++;
gamma_ = atof(arg[argIndex++]);
kappa_ = atof(arg[argIndex++]);
mu_ = atof(arg[argIndex++]);
boundaryDynamics_ = DAMPED_HARMONIC;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"prescribed")==0) {
boundaryDynamics_ = PRESCRIBED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"coupled")==0) {
boundaryDynamics_ = COUPLED;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"none")==0) {
boundaryDynamics_ = NO_BOUNDARY_DYNAMICS;
foundMatch = true;
}
}
// no match, call base class parser
if (!foundMatch) {
foundMatch = ATC_Coupling::modify(narg, arg);
}
return foundMatch;
}
//--------------------------------------------------
// pack_fields
// bundle all allocated field matrices into a list
// for output needs
//--------------------------------------------------
void ATC_CouplingMomentum::pack_elastic_fields(RESTART_LIST & data)
{
atomicRegulator_->pack_fields(data);
}
//--------------------------------------------------
// write_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingMomentum::write_restart_data(string fileName, RESTART_LIST & data)
{
pack_elastic_fields(data);
ATC_Method::write_restart_data(fileName,data);
}
//--------------------------------------------------
// write_restart_file
// bundle matrices that need to be saved and call
// fe_engine to write the file
//--------------------------------------------------
void ATC_CouplingMomentum::read_restart_data(string fileName, RESTART_LIST & data)
{
pack_elastic_fields(data);
ATC_Method::read_restart_data(fileName,data);
}
//--------------------------------------------------------
void ATC_CouplingMomentum::reset_nlocal()
{
ATC_Coupling::reset_nlocal();
atomicRegulator_->reset_nlocal();
}
//--------------------------------------------------
// reset_atom_materials
// update the atom materials map
//--------------------------------------------------
void ATC_CouplingMomentum::reset_atom_materials()
{
ATC_Coupling::reset_atom_materials();
atomicRegulator_->reset_atom_materials(elementToMaterialMap_,
atomElement_);
}
//--------------------------------------------------------
// pre_init_integrate
// time integration before the lammps atomic
// integration of the Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMomentum::pre_init_integrate()
{
ATC_Coupling::pre_init_integrate();
double dt = lammpsInterface_->dt();
// get any initial data before its modified
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate1(dt);
}
// apply kinetostat force, if needed
atomicRegulator_->apply_pre_predictor(dt,lammpsInterface_->ntimestep());
// predict nodal velocities
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_initial_integrate2(dt);
}
extrinsicModelManager_.pre_init_integrate();
}
//--------------------------------------------------------
// mid_init_integrate
// time integration between the velocity update and
// the position lammps update of Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMomentum::mid_init_integrate()
{
// CONTINUOUS VELOCITY UPDATE
ATC_Coupling::mid_init_integrate();
double dt = lammpsInterface_->dt();
// Compute nodal velocity at n+1/2
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->mid_initial_integrate1(dt);
}
atomicRegulator_->apply_mid_predictor(dt,lammpsInterface_->ntimestep());
extrinsicModelManager_.mid_init_integrate();
}
//--------------------------------------------------------
// post_init_integrate
// time integration after the lammps atomic updates of
// Verlet step 1
//--------------------------------------------------------
void ATC_CouplingMomentum::post_init_integrate()
{
// CONTINUOUS DISPLACEMENT UPDATE
double dt = lammpsInterface_->dt();
// Compute nodal velocity at n+1
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_initial_integrate1(dt);
}
// Update kinetostat quantities if displacement is being regulated
atomicRegulator_->apply_post_predictor(dt,lammpsInterface_->ntimestep());
// Update extrisic model
extrinsicModelManager_.post_init_integrate();
// fixed values, non-group bcs handled through FE
set_fixed_nodes();
// enforce atomic boundary conditions
if (boundaryDynamics_==PRESCRIBED) set_ghost_atoms();
else if (boundaryDynamics_==DAMPED_HARMONIC) initial_integrate_ghost();
else if (boundaryDynamics_==COUPLED) initial_integrate_ghost();
// update time by a half dt
update_time(0.5);
ATC_Coupling::post_init_integrate();
}
//--------------------------------------------------------
// pre_final_integrate
// integration before the second stage lammps atomic
// update of Verlet step 2
//--------------------------------------------------------
void ATC_CouplingMomentum::pre_final_integrate()
{
ATC_Coupling::pre_final_integrate();
if (boundaryDynamics_==DAMPED_HARMONIC) {
apply_ghost_forces();
final_integrate_ghost();
}
else if (boundaryDynamics_==COUPLED) {
add_ghost_forces();
final_integrate_ghost();
}
}
//--------------------------------------------------------
// post_final_integrate
// integration after the second stage lammps atomic
// update of Verlet step 2
//--------------------------------------------------------
void ATC_CouplingMomentum::post_final_integrate()
{
// COMPUTE FORCES FOR FE VELOCITY RHS
double dt = lammpsInterface_->dt();
// updating of data based on atomic forces
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->pre_final_integrate1(dt);
}
// Set prescribed sources for current time
prescribedDataMgr_->set_sources(time()+0.5*dt,sources_);
// predictor step in extrinsic model
extrinsicModelManager_.pre_final_integrate();
if (timeIntegrators_[VELOCITY]->has_final_predictor()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(velocityMask_,fields_,atomicSources_);
}
// Compute kinetostat forces and add kinetostat contributions to FE equations
atomicRegulator_->apply_pre_corrector(dt,lammpsInterface_->ntimestep()); // computes but does not apply kstat, and only for StressFlux
// set state-based RHS
// Determine FE contributions to dv/dt-----------------------
// Compute atom-integrated rhs
// parallel communication happens within FE_Engine
compute_rhs_vector(velocityMask_,fields_,rhs_,FE_DOMAIN);
// Compute and add atomic contributions to FE equations
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->add_to_rhs();
}
// add in kinetostat contributions to FE equations
atomicRegulator_->add_to_rhs(rhs_);
// final phase predictor step
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate1(dt);
}
// fix nodes, non-group bcs applied through FE
set_fixed_nodes();
// CONTINUOUS VELOCITY RHS UPDATE
// corrector step extrinsic model
extrinsicModelManager_.post_final_integrate();
if (timeIntegrators_[VELOCITY]->has_final_corrector()) {
// set state-based sources
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
atomicRegulator_->compute_boundary_flux(fields_);
compute_atomic_sources(velocityMask_,fields_,atomicSources_);
}
// Finish update of FE velocity
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate2(dt);
}
// Apply kinetostat to atoms
atomicRegulator_->apply_post_corrector(dt,lammpsInterface_->ntimestep());
// finalize time integration
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_final_integrate3(dt);
}
// Fix nodes, non-group bcs applied through FE
set_fixed_nodes();
// update time by a half dt
update_time(0.5);
output();
ATC_Coupling::post_final_integrate(); // addstep for computes
}
//--------------------------------------------------------
// min_pre_force
// add to interatomic forces for minimize
//--------------------------------------------------------
void ATC_CouplingMomentum::min_pre_force()
{
}
//--------------------------------------------------------
// min_post_force
// add to interatomic forces for minimize
// this determines the search direction
//--------------------------------------------------------
void ATC_CouplingMomentum::min_post_force()
{
// reset positions and shape functions
ATC_Method::min_post_force();
// Set sources
prescribedDataMgr_->set_sources(time(),sources_);
extrinsicModelManager_.set_sources(fields_,extrinsicSources_);
extrinsicModelManager_.pre_final_integrate();
if (outputNow_) {
update_time(1.0);
update_step();
output();
outputNow_ = false;
}
localStep_ += 1;
}
//--------------------------------------------------------
// output
// does post-processing steps and outputs data
//--------------------------------------------------------
void ATC_CouplingMomentum::output()
{
if (output_now()) {
feEngine_->departition_mesh();
OUTPUT_LIST outputData;
// base class output
ATC_Method::output();
// push atc fields time integrator modifies into output arrays
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->post_process();
}
// auxilliary data
for (_tiIt_ = timeIntegrators_.begin(); _tiIt_ != timeIntegrators_.end(); ++_tiIt_) {
(_tiIt_->second)->output(outputData);
}
atomicRegulator_->output(outputData);
extrinsicModelManager_.output(outputData);
DENS_MAT & velocity(nodalAtomicFields_[VELOCITY].set_quantity());
DENS_MAT & rhs(rhs_[VELOCITY].set_quantity());
if (lammpsInterface_->rank_zero()) {
// mesh data
outputData["NodalAtomicVelocity"] = &velocity;
outputData["FE_Force"] = &rhs;
if (trackDisplacement_) {
outputData["NodalAtomicDisplacement"] = & nodalAtomicFields_[DISPLACEMENT].set_quantity();
}
feEngine_->write_data(output_index(), fields_, & outputData);
}
// force optional variables to reset to keep in sync
if (trackDisplacement_) {
nodalAtomicFields_[DISPLACEMENT].force_reset();
}
feEngine_->partition_mesh();
}
}
//--------------------------------------------------------
// set_ghost_atoms
// sets ghost atom positions to finite element
// displacements based on shape functions
//--------------------------------------------------------
void ATC_CouplingMomentum::set_ghost_atoms()
{
// set atomic displacements based on FE displacements
double ** x = lammpsInterface_->xatom();
// prolong
DenseMatrix<double> ghostAtomData(nLocalGhost_,nsd_);
if (nLocalGhost_>0)
ghostAtomData = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity());
for (int i = 0; i < nLocalGhost_; ++i)
for (int j = 0; j < nsd_; ++j)
x[ghostToAtom_(i)][j] = ghostAtomData(i,j)+xref_[ghostToAtom_(i)][j];
}
//--------------------------------------------------------
// add_ghost_forces
// add forces to dynamic ghosts
//--------------------------------------------------------
void ATC_CouplingMomentum::add_ghost_forces()
{
double **x = lammpsInterface_->xatom();
double **v = lammpsInterface_->vatom();
double **f = lammpsInterface_->fatom();
// add forces
DENS_MAT coarseDisp(nLocalGhost_,nsd_);
DENS_MAT coarseVel(nLocalGhost_,nsd_);
if (nLocalGhost_>0) {
coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity());
coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity());
}
// dynamics one-way coupled to real atoms in a well tied to coarse scale
for (int i = 0; i < nLocalGhost_; ++i) {
for (int j = 0; j < nsd_; ++j) {
double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j];
double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j];
f[ghostToAtom_(i)][j] += mu_*du + gamma_*dv;
}
}
}
void ATC_CouplingMomentum::apply_ghost_forces()
{
double **x = lammpsInterface_->xatom();
double **v = lammpsInterface_->vatom();
double **f = lammpsInterface_->fatom();
// add forces
DENS_MAT coarseDisp(nLocalGhost_,nsd_);
DENS_MAT coarseVel(nLocalGhost_,nsd_);
if (nLocalGhost_>0) {
coarseDisp = (shpFcnGhost_->quantity())*(fields_[DISPLACEMENT].quantity());
coarseVel = (shpFcnGhost_->quantity())*(fields_[VELOCITY].quantity());
}
// dynamics one-way coupled to real atoms in a well tied to coarse scale
for (int i = 0; i < nLocalGhost_; ++i) {
for (int j = 0; j < nsd_; ++j) {
double du = coarseDisp(i,j)+xref_[ghostToAtom_(i)][j]-x[ghostToAtom_(i)][j];
double dv = coarseVel(i,j)-v[ghostToAtom_(i)][j];
f[ghostToAtom_(i)][j] = mu_*du + gamma_*dv;
}
}
}
//--------------------------------------------------------
// initial_integrate_ghost
// does the first step of the Verlet integration for
// ghost atoms, to be used with non-reflecting BCs
//--------------------------------------------------------
void ATC_CouplingMomentum::initial_integrate_ghost()
{
double dtfm;
double **x = lammpsInterface_->xatom();
double **v = lammpsInterface_->vatom();
double **f = lammpsInterface_->fatom();
const int *mask = lammpsInterface_->atom_mask();
int nlocal = lammpsInterface_->nlocal();
double dtv = lammpsInterface_->dt();
double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v();
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbitGhost_) {
dtfm = dtf / mu_;
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
x[i][0] += dtv * v[i][0];
x[i][1] += dtv * v[i][1];
x[i][2] += dtv * v[i][2];
}
}
}
//--------------------------------------------------------
// final_integrate_ghost
// does the second step of the Verlet integration for
// ghost atoms, to be used with non-reflecting BCs
//--------------------------------------------------------
void ATC_CouplingMomentum::final_integrate_ghost()
{
double dtfm;
double **v = lammpsInterface_->vatom();
double **f = lammpsInterface_->fatom();
const int *mask = lammpsInterface_->atom_mask();
int nlocal = lammpsInterface_->nlocal();
double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v();
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbitGhost_) {
dtfm = dtf / mu_;
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
}
}
}
//--------------------------------------------------------------------
// compute_scalar : added energy
// this is used in the line search
//--------------------------------------------------------------------
double ATC_CouplingMomentum::compute_scalar(void)
{
double energy = extrinsicModelManager_.compute_scalar();
return energy;
}
//--------------------------------------------------------------------
// kinetic energy
//--------------------------------------------------------------------
double ATC_CouplingMomentum::kinetic_energy(const IntegrationDomainType domain) // const
{
const MATRIX & M = massMats_[VELOCITY].quantity();
const DENS_MAT & velocity(fields_[VELOCITY].quantity());
double kineticEnergy = 0;
for (int j = 0; j < nsd_; j++) {
CLON_VEC v = column(velocity,j);
kineticEnergy += v.dot(M*v);
}
if (domain == FE_DOMAIN) {
Array<FieldName> massMask(1);
massMask(0) = VELOCITY;
feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel_,atomMaterialGroups_,
atomVolume_->quantity(),shpFcn_->quantity(),
Ma_);
const MATRIX & Ma = Ma_[VELOCITY].quantity();
for (int j = 0; j < nsd_; j++) {
CLON_VEC v = column(velocity,j);
kineticEnergy -= v.dot(Ma*v);
}
}
double mvv2e = lammpsInterface_->mvv2e();
kineticEnergy *= 0.5*mvv2e; // convert to LAMMPS units
return kineticEnergy;
}
//--------------------------------------------------------------------
// potential/strain energy
//--------------------------------------------------------------------
double ATC_CouplingMomentum::potential_energy(const IntegrationDomainType domain) const
{
Array<FieldName> mask(1);
mask(0) = VELOCITY;
FIELD_MATS energy;
feEngine_->compute_energy(mask,
fields_,
physicsModel_,
elementToMaterialMap_,
energy,
&(elementMask_->quantity()),
domain);
double potentialEnergy = energy[VELOCITY].col_sum();
double mvv2e = lammpsInterface_->mvv2e();
potentialEnergy *= mvv2e; // convert to LAMMPS units
return potentialEnergy-refPE_;
}
//--------------------------------------------------------------------
// compute_vector
//--------------------------------------------------------------------
// this is for direct output to lammps thermo
double ATC_CouplingMomentum::compute_vector(int n)
{
// output[1] = total coarse scale kinetic energy
// output[2] = total coarse scale potential energy
// output[3] = total coarse scale energy
// output[4] = fe-only coarse scale kinetic energy
// output[5] = fe-only coarse scale potential energy
if (n == 0) {
return kinetic_energy();
}
else if (n == 1) {
return potential_energy();
}
else if (n == 2) {
return kinetic_energy()+potential_energy();
}
else if (n == 3) {
return kinetic_energy(FE_DOMAIN);
}
else if (n == 4) {
return potential_energy(FE_DOMAIN);
}
else if (n > 4) {
double extrinsicValue = extrinsicModelManager_.compute_vector(n);
return extrinsicValue;
}
return 0.;
}
};

View File

@ -0,0 +1,149 @@
#ifndef ATC_COUPLING_MOMENTUM_H
#define ATC_COUPLING_MOMENTUM_H
/** owned field/s: DISPLACEMENT, VELOCITY */
// ATC headers
#include "ATC_Coupling.h"
#include "Kinetostat.h"
#include "ElasticTimeIntegrator.h"
// Other headers
#include <map>
namespace ATC {
// Forward declarations
class AtfShapeFunctionRestriction;
class AtfShapeFunctionMdProjection;
/**
* @class ATC_CouplingMomentum
* @brief A class for atom-continuum transfers & control involving mechanical motion
* (owned field/s: DISPLACEMENT, VELOCITY)
*/
class ATC_CouplingMomentum : public ATC_Coupling {
public:
// constructor
ATC_CouplingMomentum(std::string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile,
PhysicsType intrinsicModel,
ExtrinsicModelType extrinsicModel = NO_MODEL);
// destructor
virtual ~ATC_CouplingMomentum();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** flags whether a methods reset is required */
virtual bool reset_methods() const {
bool resetMethods = ATC_Method::reset_methods() ||atomicRegulator_->need_reset();
_ctiIt_ = timeIntegrators_.find(VELOCITY);
if (_ctiIt_ == timeIntegrators_.end()) return resetMethods;
return resetMethods || (_ctiIt_->second)->need_reset();
};
/** post time integration */
virtual void finish();
/** first time, before atomic integration */
virtual void pre_init_integrate();
/** first time, after atomic velocity but before position integration */
virtual void mid_init_integrate();
/** first time, after atomic integration */
virtual void post_init_integrate();
/** second time, before atomic integration */
virtual void pre_final_integrate();
/** second time, after atomic integration */
virtual void post_final_integrate();
/** pre/post atomic force calculation in minimize */
virtual void min_pre_force();
virtual void min_post_force();
/** compute scalar for output - added energy */
virtual double compute_scalar(void);
/** compute vector for output */
virtual double compute_vector(int n);
double kinetic_energy(const IntegrationDomainType domain=FULL_DOMAIN); // const;
double potential_energy(const IntegrationDomainType domain=FULL_DOMAIN) const;
/** output routines */
virtual void output(void);
/** set up atom to material identification */
virtual void reset_atom_materials();
protected:
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** constructs all data which is updated with time integration, i.e. fields */
//virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
// ghost atom routines
/** set ghost function positions based on FE state */
virtual void set_ghost_atoms();
/** analagous to FixATC initial_integrate applied to ghosts */
void initial_integrate_ghost();
/** analagous to FixATC final_integrate applied to ghosts */
void final_integrate_ghost();
/** forces on dynamic ghosts */
void add_ghost_forces();
void apply_ghost_forces();
/** adds resetting of any kinetostat arrays associated with local atom count */
virtual void reset_nlocal();
/** compute the mass matrix components coming from MD integration */
virtual void compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMats);
/** operator to compute the mass matrix for the momentum equation from MD integration */
AtfShapeFunctionRestriction * nodalAtomicMass_;
/** operator to compute the dimensionless mass matrix from MD integration */
AtfShapeFunctionRestriction * nodalAtomicCount_;
/** physics specific filter initialization */
void init_filter();
/** field mask for velocity integration */
Array2D<bool> velocityMask_;
// Add in fields for restarting
virtual void read_restart_data(string fileName_, RESTART_LIST & data);
virtual void write_restart_data(string fileName_, RESTART_LIST & data);
void pack_elastic_fields(RESTART_LIST & data);
// boundary dynamics
BoundaryDynamicsType boundaryDynamics_;
double gamma_, mu_, kappa_;
// data
double refPE_;
/** mass matrix computed with atomic quadrature for KE output */
MASS_MATS Ma_;
};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
#ifndef ATC_COUPLING_MOMENTUM_ENERGY_H
#define ATC_COUPLING_MOMENTUM_ENERGY_H
// ATC headers
#include "ATC_Coupling.h"
#include "Kinetostat.h"
#include "Thermostat.h"
#include "ElasticTimeIntegrator.h"
#include "ThermalTimeIntegrator.h"
// Other headers
#include <map>
namespace ATC {
class AtfShapeFunctionRestriction;
class AtfShapeFunctionMdProjection;
/**
* @class ATC_CouplingMomentumEnergy
* @brief A class for atom-continuum transfers & control involving momentum and heat transport
* (owned field/s: DISPLACEMENT, VELOCITY, TEMPERATURE)
*/
class ATC_CouplingMomentumEnergy : public ATC_Coupling {
public:
// constructor
ATC_CouplingMomentumEnergy(std::string groupName,
double ** & perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile,
ExtrinsicModelType extrinsic = NO_MODEL);
// destructor
virtual ~ATC_CouplingMomentumEnergy();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** flags whether a methods reset is required */
virtual bool reset_methods() const {
bool resetMethods = ATC_Method::reset_methods() || atomicRegulator_->need_reset();
for (_ctiIt_ = timeIntegrators_.begin(); _ctiIt_ != timeIntegrators_.end(); ++_ctiIt_) {
resetMethods |= (_ctiIt_->second)->need_reset();
}
return resetMethods;
};
/** post time integration */
virtual void finish();
/** first time, before atomic integration */
virtual void pre_init_integrate();
/** first time, after atomic velocity but before position integration */
virtual void mid_init_integrate();
/** first time, after atomic integration */
virtual void post_init_integrate();
/** second time, before atomic integration */
virtual void pre_final_integrate();
/** second time, after atomic integration */
virtual void post_final_integrate();
/** compute scalar for output - added energy */
virtual double compute_scalar(void);
/** compute vector for output */
virtual double compute_vector(int n);
double kinetic_energy();
double potential_energy();
/** output */
virtual void output();
/** set up atom to material identification */
virtual void reset_atom_materials();
protected:
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** constructs all data which is updated with time integration, i.e. fields */
//virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
/** sets the position/velocity of the ghost atoms */
virtual void set_ghost_atoms();
/** analagous to FixATC initial_integrate applied to ghosts */
void initial_integrate_ghost();
/** analagous to FixATC final_integrate applied to ghosts */
void final_integrate_ghost();
/** forces on dynamic ghosts */
void add_ghost_forces();
void apply_ghost_forces();
/** adds resetting of any kinetostat/thermostat arrays associated with local atom count */
virtual void reset_nlocal();
/** compute the mass matrix components coming from MD integration */
virtual void compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMats);
/** operator to compute the mass matrix for the momentum equation from MD integration */
AtfShapeFunctionRestriction * nodalAtomicMass_;
/** operator to compute the dimensionless mass matrix from MD integration */
AtfShapeFunctionRestriction * nodalAtomicCount_;
/** operator to compute mass matrix from MD */
AtfShapeFunctionRestriction * nodalAtomicHeatCapacity_;
/** physics specific filter initialization */
void init_filter();
/** field mask for velocity integration */
Array2D<bool> velocityMask_;
/** field mask for temperature integration */
Array2D<bool> temperatureMask_;
/** kinetic temperature for post-processing */
AtfShapeFunctionMdProjection * nodalAtomicKineticTemperature_;
/** configurational temperature for post-processing */
AtfShapeFunctionMdProjection * nodalAtomicConfigurationalTemperature_;
/** workspace matrices for output */
DENS_MAT _keTemp_, _peTemp_;
// Add in fields for restarting
virtual void read_restart_data(string fileName_, RESTART_LIST & data);
virtual void write_restart_data(string fileName_, RESTART_LIST & data);
void pack_quantity_fields(RESTART_LIST & data);
// boundary dynamics
BoundaryDynamicsType boundaryDynamics_;
double gamma_, mu_, kappa_;
// data
double refPE_;
};
};
#endif

56
lib/atc/ATC_Error.h Normal file
View File

@ -0,0 +1,56 @@
// 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

2194
lib/atc/ATC_Method.cpp Normal file

File diff suppressed because it is too large Load Diff

854
lib/atc/ATC_Method.h Normal file
View File

@ -0,0 +1,854 @@
#ifndef ATC_METHOD_H
#define ATC_METHOD_H
// ATC_Method headers
#include "ATC_TypeDefs.h"
#include "PhysicsModel.h"
#include "MatrixLibrary.h"
#include "Array.h"
#include "Array2D.h"
#include "OutputManager.h"
#include "Function.h"
#include "FE_Element.h"
#include "TimeFilter.h"
#include "LammpsInterface.h"
#include "FE_Engine.h"
#include "ExtrinsicModel.h"
#include "InterscaleOperators.h"
#include "TransferLibrary.h"
// Other headers
#include <vector>
#include <set>
using namespace std;
using LAMMPS_NS::Fix;
namespace ATC {
/**
* @class ATC_Method
* @brief Base class for atom-continuum coupling or transfer operators
*/
class ATC_Method {
public: /** methods */
/** constructor */
ATC_Method(string groupName, double **& perAtomArray, LAMMPS_NS::Fix * thisFix);
/** destructor */
virtual ~ATC_Method();
string version() {return "2.0";}
/** parser/modifier */
virtual bool modify(int narg, char **arg);
void parse_field(/*const*/ char ** args, int &argIndex,
FieldName &thisField, int &thisIndex);
/** initialize any computes that will be needed prior to the first timestep */
virtual void init_computes() {
lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep());
};
/** pre integration run */
virtual void initialize();
/** Predictor phase, executed before Verlet */
virtual void pre_init_integrate() {
feEngine_->partition_mesh();
update_step();
};
/** Predictor phase, Verlet first step for velocity */
virtual void init_integrate_velocity(){};
/** Predictor phase, executed between velocity and position Verlet */
virtual void mid_init_integrate(){};
/** Predictor phase, Verlet first step for position */
virtual void init_integrate_position(){};
/** Predictor phase, executed after Verlet */
virtual void post_init_integrate(){};
/** Corrector phase, executed before Verlet */
virtual void pre_final_integrate(){};
/** Corrector phase, Verlet second step for velocity */
virtual void final_integrate(){};
/** Corrector phase, executed after Verlet*/
virtual void post_final_integrate();
/** post integration run : called at end of run or simulation */
virtual void finish();
/** pre/post atomic force calculation */
virtual void pre_force(){};
/** pre/post atomic force calculation in minimize */
virtual void min_pre_force(){};
virtual void min_post_force();
/** called at end of step for run or minimize */
virtual void end_of_step();
//---------------------------------------------------------------
/** \name memory management and processor information exchange */
//---------------------------------------------------------------
/*@{*/
/** pre_exchange is our indicator that atoms have moved across processors */
virtual void pre_exchange();
void setup_pre_exchange();
virtual void pre_neighbor();
virtual void post_force();
virtual int memory_usage();
virtual void grow_arrays(int);
void copy_arrays(int, int);
int pack_exchange(int, double *);
int unpack_exchange(int, double *);
int comm_forward(void) {return sizeComm_;}
int pack_comm(int , int *, double *, int, int *);
void unpack_comm(int, int, double *);
/*@}*/
//---------------------------------------------------------------
/** \name managers */
//---------------------------------------------------------------
/*@{*/
/** access to FE engine */
const FE_Engine * fe_engine() const {return feEngine_;};
/** access to interscale manager */
InterscaleManager & interscale_manager() {return interscaleManager_;};
/** access to lammps interface */
LammpsInterface const * lammps_interface() const {return lammpsInterface_;};
/** access to time filter */
TimeFilterManager * time_filter_manager() {return &timeFilterManager_;};
/*@}*/
//---------------------------------------------------------------
/** \name access methods for output computation data */
//---------------------------------------------------------------
/*@{*/
/** compute scalar for output */
virtual double compute_scalar() {return 0.;}
/** compute vector for output */
virtual double compute_vector(int n) {return 0.;}
/** compute vector for output */
virtual double compute_array(int irow, int icol) {return 0.;};
int scalar_flag() const {return scalarFlag_;}
int vector_flag() const {return vectorFlag_;}
int size_vector() const {return sizeVector_;}
int peratom_flag() const {return sizePerAtomCols_ > 0;}
int size_peratom_cols() const {return sizePerAtomCols_;}
int peratom_freq() const {return 1;}
void set_peratom_pointer(double ** & ptr) { ptr = perAtomOutput_; }
int global_freq() const {return scalarVectorFreq_;};
int extscalar() const {return extScalar_;};
int extvector() const {return extVector_;};
int * extlist() {return extList_;};
int thermo_energy_flag() const {return thermoEnergyFlag_;};
bool parallel_consistency() const {return parallelConsistency_;};
/** access to step number */
int step() const {return stepCounter_;};
double time() const {return simTime_;};
double dt() const {return lammpsInterface_->dt();}
/** time/step functions */
bool sample_now(void) const
{
int s = step();
bool now = ( (sampleFrequency_ > 0) && (s % sampleFrequency_ == 0));
return now;
}
bool output_now(void) const
{
int s = step();
bool now = ( (outputFrequency_ > 0) && (s == 1 || s % outputFrequency_ == 0) );
now = now || outputNow_;
return now;
}
double output_index(void) const
{
if (outputTime_) return time();
else return step();
}
/** print tracked types and groups */
int print_tracked() const
{
string msg = "species:\n";
for(unsigned int i = 0; i < typeList_.size(); i++) {
msg+=" type:"+to_string(typeList_[i])+" name: "+ typeNames_[i]+"\n"; }
for(unsigned int i = 0; i < groupList_.size(); i++) {
msg+=" group (bit):"+to_string(groupList_[i])+" name: "+ groupNames_[i]+"\n";
}
ATC::LammpsInterface::instance()->print_msg_once(msg);
return typeList_.size()+groupList_.size();
}
vector<string> tracked_names() const
{
vector<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(string tag) const {
for(unsigned int i = 0; i < typeList_.size(); i++) {
if (tag == typeNames_[i]) return typeList_[i];
}
return -1;
}
int type_index(int t) const {
for(unsigned int i = 0; i < typeList_.size(); i++) {
if (t == typeList_[i]) return i;
}
return -1;
}
/*@}*/
//---------------------------------------------------------------
/** \name Access methods for sizes */
//---------------------------------------------------------------
/*@{*/
/** get number of unique FE nodes */
int num_nodes() const {return nNodes_;};
/** get number of spatial dimensions */
int nsd() const {return nsd_;};
/** get number of ATC internal atoms on this processor */
int nlocal() const {return nLocal_;};
/** get total number of LAMMPS atoms on this processor */
int nlocal_total() const {return nLocalTotal_;};
/** get number of ATC ghost atoms on this processor */
int nlocal_ghost() const {return nLocalGhost_;};
/** get the number of all LAMMPS real and parallel ghost atoms on this processor */
int nproc_ghost() const {return nLocalTotal_ + lammpsInterface_->nghost();};
/** get number of ATC internal atoms */
int ninternal() const {return nInternal_;}
/** get number of ATC ghost atoms */
int nghost() const {return nGhost_;};
/** match group bits */
bool is_ghost_group(int grpbit) { return (grpbit == groupbitGhost_); }
bool is_internal_group(int grpbit) { return (grpbit == groupbit_); }
unsigned int ntracked() { return typeList_.size()+groupList_.size(); }
bool has_tracked_species() { return typeList_.size()+groupList_.size() > 0; }
/*@}*/
virtual void initialize_mesh_data(void){meshDataInitialized_=true;}
//---------------------------------------------------------------
/** \name Access methods for data used by various methods */
//---------------------------------------------------------------
/*@{*/
/** access to name FE fields */
DENS_MAN &field(FieldName thisField){return fields_[thisField];};
/** access to FE field time derivatives */
DENS_MAT &get_dot_field(FieldName thisField){return dot_fields_[thisField].set_quantity();};
DENS_MAN &dot_field(FieldName thisField){return dot_fields_[thisField];};
/** access to nodal fields of atomic variables */
DENS_MAT &get_atomic_field(FieldName thisField)
{ return nodalAtomicFields_[thisField].set_quantity(); };
DENS_MAN &nodal_atomic_field(FieldName thisField)
{ return nodalAtomicFields_[thisField]; };
/** access to all fields */
FIELDS &fields() {return fields_;};
/** access to all fields rates of change (roc) */
FIELDS &fields_roc() {return dot_fields_;};
/** add a new field */
void add_fields(map<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 map<int,int> & atom_to_internal_map() {return atomToInternal_;};
/** access to xref */
double ** xref() {return xref_;};
/** access to faceset names */
const set<PAIR> &faceset(const string & name) const {return (feEngine_->fe_mesh())->faceset(name);};
DENS_VEC copy_nodal_coordinates(int i) const { return feEngine_->fe_mesh()->nodal_coordinates(i); }
/** access to set of DENS_MANs accessed by tagging */
DENS_MAN & tagged_dens_man(const string & tag) {return taggedDensMan_[tag];};
/** access to atom to element type map */
AtomToElementMapType atom_to_element_map_type() {return atomToElementMapType_;};
/** access to atom to element update frequency */
int atom_to_element_map_frequency() {return atomToElementMapFrequency_;};
/** flag on whether atc is initialized */
bool is_initialized() const {return initialized_;};
/** step number within a run or minimize */
int local_step() const {return localStep_;};
/** flags whether a methods reset is required */
virtual bool reset_methods() const {return (!initialized_) || timeFilterManager_.need_reset() || timeFilterManager_.end_equilibrate();};
/** sizes of each field being considered */
const map<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 species ids */
const map<string,pair<IdType,int> > & species_ids() const {return speciesIds_;};
/** access to molecule ids */
const map<string,pair<MolSize,int> > & molecule_ids() const {return moleculeIds_;};
//----------------------------------------------------------------
/** \name mass matrix operations */
//----------------------------------------------------------------
// inverted using GMRES
void apply_inverse_mass_matrix(MATRIX & data, FieldName thisField)
{
if (useConsistentMassMatrix_(thisField)) {
//data = consistentMassInverse_*data;
data = (consistentMassMatsInv_[thisField].quantity())*data;
return;
}
data = (massMatsInv_[thisField].quantity())*data;
};
/** multiply inverse mass matrix times given data and return result */
void apply_inverse_mass_matrix(const MATRIX & data_in, MATRIX & data_out,
FieldName thisField)
{
if (useConsistentMassMatrix_(thisField)) {
//data_out = consistentMassInverse_*data_in;
data_out = (consistentMassMatsInv_[thisField].quantity())*data_in;
return;
}
data_out = (massMatsInv_[thisField].quantity())*data_in;
};
void apply_inverse_md_mass_matrix(const MATRIX & data_in, MATRIX & data_out,
FieldName thisField)
{ data_out = (massMatsMdInv_[thisField].quantity())*data_in; };
DIAG_MAN &mass_mat(FieldName thisField)
{ return massMats_[thisField];};
//---------------------------------------------------------------
/** \name mass matrices */
//---------------------------------------------------------------
/*@{*/
/** access to mass matrices */
/** access to inverse mass matrices */
DIAG_MAT &get_mass_mat_inv(FieldName thisField)
{ return massMatsInv_[thisField].set_quantity();};
DIAG_MAN &mass_mat_inv(FieldName thisField)
{ return massMatsInv_[thisField];};
/** nodal volumes associated with the atoms, used for the atomic mass matrix */
AdmtfShapeFunctionRestriction * nodalAtomicVolume_;
void register_mass_matrix_dependency(DependencyManager * dependent,
FieldName thisField)
{
if (useConsistentMassMatrix_(thisField)) {
consistentMassMatsInv_[thisField].register_dependence(dependent);
return;
}
massMatsInv_[thisField].register_dependence(dependent);
};
void apply_inverse_md_mass_matrix(MATRIX & data, FieldName thisField)
{ data = (massMatsMdInv_[thisField].quantity())*data; };
void register_md_mass_matrix_dependency(DependencyManager * dependent,
FieldName thisField)
{massMatsMdInv_[thisField].register_dependence(dependent);}
// /** determine weighting method for atomic integration */
// void compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix,
// SPAR_MAT & mdMassMatrix);
virtual void compute_md_mass_matrix(FieldName thisField,
DIAG_MAT & massMat) {};
/** access to md mass matrices */
DIAG_MAN &mass_mat_md_inv(FieldName thisField)
{ return massMatsMdInv_[thisField];};
DIAG_MAN &set_mass_mat_md(FieldName thisField)
{ return massMatsMd_[thisField]; };
const DIAG_MAN &mass_mat_md(FieldName thisField) const
{
MASS_MATS::const_iterator man = massMatsMd_.find(thisField);
if (man == massMatsMd_.end() ) {
string msg = " MD mass for " + field_to_string(thisField) + " does not exist";
throw ATC_Error(msg);
}
return man->second;
};
/*@}*/
//----------------------------------------------------------------
/** \name Interscale operators */
//----------------------------------------------------------------
bool use_md_mass_normalization() const { return mdMassNormalization_;}
bool kernel_based() { return kernelBased_; }
bool kernel_on_the_fly() const { return kernelOnTheFly_;}
bool has_kernel_function() { return kernelFunction_ != NULL; }
KernelFunction * kernel_function() { return kernelFunction_; }
vector<int> & type_list() { return typeList_; }
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_; }
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, set<int>& nset, set<int>& aset, double tol =1.e-8);
int nodal_influence(const int groupbit, set<int>& nset, 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 */
//---------------------------------------------------------------
/*@{*/
bool has_ref_pe(void) const { return hasRefPE_; }
const DENS_MAT * nodal_ref_potential_energy(void) { return &nodalRefPotentialEnergy_; }
protected: /** methods */
/** time functions */
void set_time(double t=0) {simTime_=t;};
void update_time(double alpha = 1.0)
{
double dt = lammpsInterface_->dt();
simTime_ += alpha*dt;
if (dt == 0.0) simTime_ = stepCounter_;
}
// note step counter different than lammps step e.g. min
void update_step(void) { ++stepCounter_; }
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** gets baseline data from continuum model */
virtual void set_continuum_data();
/** sets up all data necessary to define the computational geometry */
virtual void set_computational_geometry();
/** constructs all data which is updated with time integration, i.e. fields */
virtual void construct_time_integration_data() = 0;
/** create methods, e.g. time integrators, filters */
virtual void construct_methods() = 0;
/** set up data which is dependency managed */
virtual void construct_transfers();
/** update the peratom output pointers */
void update_peratom_output(void);
virtual void read_restart_data(string fileName_, RESTART_LIST & data);
virtual void write_restart_data(string fileName_, RESTART_LIST & data);
void pack_fields(RESTART_LIST & data);
/** mass matrices */
MASS_MATS massMats_;
MASS_MATS massMatsInv_;
MASS_MATS massMatsMd_;
MASS_MATS massMatsMdInstantaneous_;
MASS_MATS massMatsMdInv_;
MASS_MATS massMatsFE_;
MASS_MATS massMatsAq_;
MASS_MATS massMatsAqInstantaneous_;
Array<bool> useConsistentMassMatrix_;
map<FieldName,SPAR_MAN> consistentMassMats_;
map<FieldName,DENS_MAN> consistentMassMatsInv_;
map<FieldName,TimeFilter * > massMatTimeFilters_;
//---------------------------------------------------------------
/** \name quadrature weight function */
//---------------------------------------------------------------
/*@{*/
void write_atomic_weights(const string filename,const DIAG_MAT & atomicVolumeMatrix);
/** resets shape function matrices based on atoms on this processor */
virtual void reset_nlocal();
virtual void reset_coordinates();
/*@}*/
/** re-read reference positions */
bool read_atomic_ref_positions(const char * filename);
void remap_ghost_ref_positions(void);
void adjust_xref_pbc();
//---------------------------------------------------------------
/** \name output functions */
//---------------------------------------------------------------
/*@{*/
virtual void output();
void compute_nodeset_output(void);
void compute_faceset_output(void);
void compute_elementset_output(void);
/*@}*/
//---------------------------------------------------------------
/** \name types, groups, and molecules */
//---------------------------------------------------------------
/*@{*/
/** map from species string tag to LAMMPS type id or group bit */
map<string,pair<IdType,int> > speciesIds_; // OBSOLETE
map<string,pair<MolSize,int> > moleculeIds_;
/** a list of lammps types & groups ATC tracks */
vector<string> typeNames_;
vector<string> groupNames_;
vector<int> typeList_;
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_;
/** 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_;
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_;
/** atomic ATC material tag */
double Xprd_,Yprd_,Zprd_; // lengths of periodic box in reference frame
double XY_,YZ_,XZ_;
double boxXlo_,boxXhi_; // lo/hi bounds of periodic box in reference frame
double boxYlo_,boxYhi_; // lo/hi bounds of periodic box in reference frame
double boxZlo_,boxZhi_; // lo/hi bounds of periodic box in reference frame
// next data members are for consistency with existing ATC_Transfer, but are redundant and do not
// conform to naming standards, and should be accessible through the mesh
/** periodicity flags and lengths */
int periodicity[3];
double box_bounds[2][3];
double box_length[3];
/** pointers to needed atom quantities and transfers */
FundamentalAtomQuantity * atomMasses_;
FundamentalAtomQuantity * atomPositions_;
FundamentalAtomQuantity * atomVelocities_;
FundamentalAtomQuantity * atomForces_;
//---------------------------------------------------------------
/** \name output data */
//---------------------------------------------------------------
/*@{*/
//private:
bool parallelConsistency_;
/** base name for output files */
string outputPrefix_;
/** output flag */
bool outputNow_;
/** output time or step (for lammps compatibility) */
bool outputTime_;
/** output frequency */
int outputFrequency_;
/** sample frequency */
int sampleFrequency_;
/** sample counter */
int sampleCounter_;
TAG_FIELDS filteredData_;
double peScale_,keScale_;
//protected:
/*@}*/
//---------------------------------------------------------------
/** \name member data related to compute_scalar() and compute_vector() */
//---------------------------------------------------------------
/*@{*/
int scalarFlag_; // 0/1 if compute_scalar() function exists
int vectorFlag_; // 0/1 if compute_vector() function exists
int sizeVector_; // N = size of global vector
int scalarVectorFreq_; // frequency compute s/v data is available at
int sizePerAtomCols_; // N = size of per atom vector to dump
double **perAtomOutput_; // per atom data
double **&perAtomArray_; // per atom data
int extScalar_; // 0/1 if scalar is intensive/extensive
int extVector_; // 0/1/-1 if vector is all int/ext/extlist
int *extList_; // list of 0/1 int/ext for each vec component
int thermoEnergyFlag_; // 0/1 if fix adds to overall energy
/*@}*/
//---------------------------------------------------------------
/** \name fields and necessary data for FEM */
//---------------------------------------------------------------
/*@{*/
map<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_;
string atomicWeightsFile_;
bool atomicWeightsWriteFlag_;
int atomicWeightsWriteFrequency_;
double atomicVolume_; // global atomic volume for homogeneous set of atoms
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_;
string groupTag_;
string groupTagGhost_;
/** number of atoms of correct type,
ghosts are atoms outside our domain of interest
boundary are atoms contributing to boundary flux terms */
/** Number of "internal" atoms on this processor */
int nLocal_;
/** Number of atoms on this processor */
int nLocalTotal_;
int nLocalGhost_;
int nInternal_;
int nGhost_;
Array<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_;
SPAR_MAN * shpFcnGhost_;
VectorDependencyManager<SPAR_MAT * > * shpFcnDerivsGhost_;
/** map from species string tag to the species density */
map<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_;
/*@}*/
//---------------------------------------------------------------
/** \name restart procedures */
//---------------------------------------------------------------
bool useRestart_;
string restartFileName_;
//---------------------------------------------------------------
/** \name data specific to node/faceset for global output */
//---------------------------------------------------------------
/** group computes : type, group_id -> value */
map< pair < string, FieldName > , NodesetOperationType> nsetData_;
map < pair<string,string>, FacesetIntegralType > fsetData_;
map < pair<string, FieldName>,ElementsetOperationType > esetData_;
//---------------------------------------------------------------
/** \name reference data */
//---------------------------------------------------------------
bool hasRefPE_;
bool setRefPE_;
bool setRefPEvalue_;
double refPEvalue_;
bool readRefPE_;
string nodalRefPEfile_;
DENS_MAT nodalRefPotentialEnergy_;
void set_reference_potential_energy(void);
private: /** data */
/** current time in simulation */
double simTime_;
/** step counter */
int stepCounter_;
};
};
#endif

2064
lib/atc/ATC_Transfer.cpp Normal file

File diff suppressed because it is too large Load Diff

253
lib/atc/ATC_Transfer.h Normal file
View File

@ -0,0 +1,253 @@
/** ATC_Transfer : coarse-graining methods */
#ifndef ATC_TRANSFER_H
#define ATC_TRANSFER_H
// ATC headers
#include "ATC_Method.h"
#include "MoleculeSet.h"
#include "AtomToMoleculeTransfer.h"
// Other headers
#include <map>
#include <list>
using std::list;
namespace ATC {
// Forward declarations
class FE_Engine;
class StressCauchyBorn;
class TimeFilter;
class ATC_Transfer : public ATC_Method {
public:
// constructor
ATC_Transfer(std::string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile = "none");
// destructor
virtual ~ATC_Transfer();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** post time integration */
virtual void finish();
/** first time substep routines */
virtual void pre_init_integrate();
/** second time substep routine */
virtual void pre_final_integrate();
//virtual void final_integrate(){};
virtual void post_final_integrate();
/** communication routines */
virtual void pre_neighbor() {ATC_Method::pre_neighbor(); neighborReset_ = true;};
/** output function */
virtual void output();
/** external access to hardy data and other information*/
const DENS_MAT * hardy_data(string field) { return &hardyData_[field].quantity(); }
protected:
/** pointer to position data : either x_reference or x_current */
double ** xPointer_;
/** data */
TAG_FIELDS hardyData_;
SmallMoleculeSet * smallMoleculeSet_; // KKM add
SmallMoleculeCentroid * moleculeCentroid_; // KKM add
SmallMoleculeDipoleMoment * dipoleMoment_; // KKM add
SmallMoleculeQuadrupoleMoment * quadrupoleMoment_; // KKM add
/** container for dependency managed data */
vector < DENS_MAN * > outputFields_;
map < string, DENS_MAN * > outputFieldsTagged_;
DENS_MAN * restrictedCharge_; // WIP/TEMP
/** work space */
DENS_MAT atomicScalar_;
DENS_MAT atomicVector_;
DENS_MAT atomicTensor_;
/** calculation flags */
Array<bool> fieldFlags_;
Array<bool> outputFlags_;
Array<bool> gradFlags_;
Array<bool> rateFlags_;
map<string,int> computes_;
bool outputStepZero_;
/** check whether atoms have shifted box or element or neighbors changed */
bool neighborReset_;
//---------------------------------------------------------------
/** initialization routines */
//---------------------------------------------------------------
/** gets baseline data from continuum model */
virtual void set_continuum_data();
/** sets up all data necessary to define the computational geometry */
virtual void set_computational_geometry();
/** constructs all data which is updated with time integration, i.e. fields */
virtual void construct_time_integration_data();
/** create methods, e.g. time integrators, filters */
virtual void construct_methods();
/** set up data which is dependency managed */
virtual void construct_transfers();
/** compute atom to nodal quantities */
// OBSOLETE
void compute_energy(DENS_MAT & energy);
void compute_internal_energy(DENS_MAT & energy);
void compute_stress(DENS_MAT & stress);
void compute_heatflux(DENS_MAT & flux);
/** derived quantities: compute nodal to nodal quantities */
void compute_eshelby_stress(DENS_MAT & eshebly_stress,
const DENS_MAT & energy, const DENS_MAT & stress,
const DENS_MAT & displacement_gradient);
void cauchy_born_stress(const DENS_MAT &dudx, DENS_MAT &T, const DENS_MAT *temp=0);
void cauchy_born_energy(const DENS_MAT &dudx, DENS_MAT &T, const DENS_MAT *temp=0);
void cauchy_born_entropic_energy(const DENS_MAT &dudx, DENS_MAT &E, const DENS_MAT & T);
void compute_transformed_stress(DENS_MAT & stress,
const DENS_MAT & T, const DENS_MAT & displacement_gradient);
void compute_polar_decomposition(DENS_MAT & rotation,
DENS_MAT & stretch, const DENS_MAT & displacement_gradient);
void compute_elastic_deformation_gradient(DENS_MAT & elastic_def_grad,
const DENS_MAT & stress, const DENS_MAT & displacement_gradient);
void compute_elastic_deformation_gradient2(DENS_MAT & elastic_def_grad,
const DENS_MAT & stress, const DENS_MAT & displacement_gradient);
/** hybrid computes? */
void compute_electric_potential(DENS_MAT & electric_potential);
void compute_vacancy_concentration(DENS_MAT & vacancy_concentration,
const DENS_MAT & displacement_gradient,
const DENS_MAT & number_density);
/** calculate kinetic part of stress */
virtual void compute_kinetic_stress(DENS_MAT& stress);
/** calculate stress on-the-fly */
virtual void compute_potential_stress(DENS_MAT& stress) = 0;
/** calculate kinetic part of heat flux */
virtual void compute_kinetic_heatflux(DENS_MAT& flux);
/** calculate force part of the heat flux on-the-fly */
virtual void compute_potential_heatflux(DENS_MAT& flux) = 0;
/** compute molecule to nodal quantities */
void compute_dipole_moment(DENS_MAT & dipole_moment);
void compute_quadrupole_moment(DENS_MAT & quadrupole_moment);
/** calculate dislocation density tensor from DXA output */
virtual void compute_dislocation_density(DENS_MAT & dislocation_density) = 0;
/** compute smooth fields */
void compute_fields(void);
void time_filter_pre (double dt);
void time_filter_post(double dt);
/** mapping of atomic pairs to pair index value */
class PairMap * pairMap_;
class BondMatrix * bondMatrix_;
class PairVirial * pairVirial_;
class PairPotentialHeatFlux * pairHeatFlux_;
/** routine to calculate matrix of force & position dyads */
void compute_force_matrix();
/** routine to calculate matrix of heat flux vector components */
void compute_heat_matrix();
/** routine to calculate matrix of kernel functions */
virtual void compute_kernel_matrix_molecule() = 0; //KKM add
/** calculate projection on the fly*/
// REFACTOR use AtfKernelFunctionRestriction and derivatives
virtual void compute_projection(const DENS_MAT & atomData,
DENS_MAT & nodeData) = 0;
/** routine to calculate matrix of bond functions */
virtual void compute_bond_matrix();
/** routine to set xPointer to xref or xatom */
void set_xPointer();
/** number of atom types */
int nTypes_;
/** project : given w_\alpha,
w_I = \sum_\alpha N_{I\alpha} w_\alpha */
// REFACTOR AtfShapeFunctionRestriction
void project(const DENS_MAT & atomData,
DENS_MAT & nodeData);
void project_molecule(const DENS_MAT & molData,
DENS_MAT & nodeData); //KKM add
void project_molecule_gradient(const DENS_MAT & molData,
DENS_MAT & nodeData); //KKM add
/** project (number density): given w_\alpha,
w_I = \sum_\alpha N_{I\alpha} w_\alpha */
// REFACTOR AtfNodeWeightedShapeFunctionRestriction
void project_count_normalized(const DENS_MAT & atomData,
DENS_MAT & nodeData);
/** hardy_project (volume density): given w_\alpha,
w_I = 1/\Omega_I \sum_\alpha N_{I\alpha} w_\alpha
where \Omega_I = \int_{support region of node I} N_{I} dV */
// REFACTOR AtfNodeWeightedShapeFunctionRestriction
void project_volume_normalized(const DENS_MAT & atomData,
DENS_MAT & nodeData);
void project_volume_normalized_molecule(const DENS_MAT & molData,
DENS_MAT & nodeData); // KKM add
void project_volume_normalized_molecule_gradient(const DENS_MAT & molData,
DENS_MAT & nodeData); // KKM add
/** gradient_compute: given w_I,
w_J = \sum_I N_I'{xJ} \dyad w_I
where N_I'{xJ} is the gradient of the normalized
shape function of node I evaluated at node J */
// REFACTOR MatToGradBySparse
void gradient_compute(const DENS_MAT & inNodeData,
DENS_MAT & outNodeData);
int nNodesGlobal_;
int nComputes_;
/** workset data */
VectorDependencyManager<SPAR_MAT * > * gradientMatrix_;
SPAR_MAT atomicBondMatrix_;
DENS_MAT atomicForceMatrix_;
DENS_MAT atomicHeatMatrix_;
/** use pair/bond forces */
bool hasPairs_;
bool hasBonds_;
/** need to reset kernel function and bond matrix */
bool resetKernelFunction_;
/** use "exact" serial mode when using DXA to compute dislocation densities */
bool dxaExactMode_;
/** a continuum model to compare to and/or estimate quantities */
StressCauchyBorn * cauchyBornStress_;
Array<TimeFilter *> timeFilters_;
/** check consistency of fieldFlags_ */
void check_field_dependencies();
};
};
#endif

View File

@ -0,0 +1,484 @@
// ATC headers
#include "ATC_TransferKernel.h"
#include "ATC_Error.h"
#include "FE_Engine.h"
#include "KernelFunction.h"
#include "LammpsInterface.h"
#include "Quadrature.h"
#include "PerPairQuantity.h"
#include "Stress.h"
#ifdef HAS_DXA
#include "DislocationExtractor.h"
#endif
// Other Headers
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <fstream>
#include <exception>
using namespace std;
namespace ATC {
using ATC_Utility::to_string;
ATC_TransferKernel::ATC_TransferKernel(string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile)
: ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile)
{
kernelBased_ = true;
}
//-------------------------------------------------------------------
ATC_TransferKernel::~ATC_TransferKernel()
{
}
//-------------------------------------------------------------------
bool ATC_TransferKernel::modify(int narg, char **arg)
{
bool match = false;
/*! \page man_hardy_kernel fix_modify AtC kernel
\section syntax
fix_modify AtC kernel <type> <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
*/
// no match, call base class parser
if (!match) {
match = ATC_Transfer::modify(narg, arg);
}
return match;
}
//-------------------------------------------------------------------
void ATC_TransferKernel::compute_kernel_matrix_molecule(void) // KKM add
{
int nLocalMol = smallMoleculeSet_->local_molecule_count();
if (nLocal_>0) {
SPAR_MAT & N(kernelAccumulantMol_.set_quantity());
N.reset(nLocalMol,nNodes_);
SPAR_MAT & dN(kernelAccumulantMolGrad_.set_quantity());
dN.reset(nLocalMol,nNodes_);
DENS_VEC derivKer(nsd_);
DENS_VEC xI(nsd_),xm(nsd_),xmI(nsd_);
const DENS_MAT & centroidMolMatrix(moleculeCentroid_->quantity());
ATC::LammpsInterface::instance()->stream_msg_once("computing kernel matrix molecule ",true,false);
int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10);
for (int i = 0; i < nNodes_; i++) {
if (i % heartbeatFreq == 0 ) {
ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);
}
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
for (int j = 0; j < nLocalMol; j++) {
for (int k = 0; k < nsd_; k++) {
xm(k) = centroidMolMatrix(j,k);
}
xmI = xm - xI;
lammpsInterface_->periodicity_correction(xmI.ptr());
double val = kernelFunction_->value(xmI);
if (val > 0) N.add(j,i,val);
kernelFunction_->derivative(xmI,derivKer);
double val_grad = derivKer(2);
if (val_grad!= 0) dN.add(j,i,val_grad);
}
}
// reset kernelShpFunctions with the weights of molecules on processors
DENS_VEC fractions(N.nRows());
DENS_VEC fractions_deriv(dN.nRows());
for (int i = 0; i < nLocalMol; i++) {
fractions(i) = smallMoleculeSet_->local_fraction(i);
}
N.row_scale(fractions);
N.compress();
dN.row_scale(fractions);
dN.compress();
if (lammpsInterface_->rank_zero()) {
ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);
}
}
}
//-------------------------------------------------------------------
void ATC_TransferKernel::compute_projection(const DENS_MAT & atomData,
DENS_MAT & nodeData)
{
DENS_MAT workNodeArray(nNodes_, atomData.nCols());
workNodeArray.zero();
nodeData.reset(workNodeArray.nRows(),workNodeArray.nCols());
nodeData.zero();
if (nLocal_>0) {
DENS_VEC xI(nsd_),xa(nsd_),xaI(nsd_);
double val;
for (int i = 0; i < nNodes_; i++) {
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
for (int j = 0; j < nLocal_; j++) {
int lammps_j = internalToAtom_(j);
xa.copy(xPointer_[lammps_j],3);
xaI = xa - xI;
lammpsInterface_->periodicity_correction(xaI.ptr());
val = kernelFunction_->value(xaI);
if (val > 0) {
for (int k=0; k < atomData.nCols(); k++) {
workNodeArray(i,k) += val*atomData(j,k);
}
}
}
}
}
// accumulate across processors
int count = workNodeArray.nRows()*workNodeArray.nCols();
lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count);
}
//-------------------------------------------------------------------
void ATC_TransferKernel::compute_bond_matrix()
{
atomicBondMatrix_=bondMatrix_->quantity();
}
//-------------------------------------------------------------------
// on-the-fly calculation of stress
void ATC_TransferKernel::compute_potential_stress(DENS_MAT& stress)
{
set_xPointer();
stress.zero();
// neighbor lists
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
double ** xatom = lammpsInterface_->xatom();
double lam1,lam2;
double bond_value;
// process differently for mesh vs translation-invariant kernels
ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false);
int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10);
// "normal" kernel functions
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
double kernel_inv_vol = kernelFunction_->inv_vol();
for (int i = 0; i < nNodes_; i++) {
if (i % heartbeatFreq == 0 ) {
ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);
}
// point
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
if (!kernelFunction_->node_contributes(xI)) {
continue;
}
int inode = i;
for (int j = 0; j < nLocal_; j++) {
// second (neighbor) atom location
int lammps_j = internalToAtom_(j);
xa.copy(xPointer_[lammps_j],3);
// difference vector
xaI = xa - xI;
lammpsInterface_->periodicity_correction(xaI.ptr());
for (int k = 0; k < numneigh[lammps_j]; ++k) {
int lammps_k = firstneigh[lammps_j][k];
// first atom location
xb.copy(xPointer_[lammps_k],3);
// difference vector
xba = xb - xa;
xbI = xba + xaI;
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
// compute virial
if (lam1 < lam2) {
bond_value
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
double rsq = delx*delx + dely*dely + delz*delz;
double fforce = 0;
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
fforce *= 0.5; // dbl count
if (atomToElementMapType_ == LAGRANGIAN) {
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
stress(inode,0) +=-delx*fforce*delX*bond_value;
stress(inode,1) +=-delx*fforce*delY*bond_value;
stress(inode,2) +=-delx*fforce*delZ*bond_value;
stress(inode,3) +=-dely*fforce*delX*bond_value;
stress(inode,4) +=-dely*fforce*delY*bond_value;
stress(inode,5) +=-dely*fforce*delZ*bond_value;
stress(inode,6) +=-delz*fforce*delX*bond_value;
stress(inode,7) +=-delz*fforce*delY*bond_value;
stress(inode,8) +=-delz*fforce*delZ*bond_value;
}
else { //EULERIAN
stress(inode,0) +=-delx*delx*fforce*bond_value;
stress(inode,1) +=-dely*dely*fforce*bond_value;
stress(inode,2) +=-delz*delz*fforce*bond_value;
stress(inode,3) +=-delx*dely*fforce*bond_value;
stress(inode,4) +=-delx*delz*fforce*bond_value;
stress(inode,5) +=-dely*delz*fforce*bond_value;
}
}
}
}
}
ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);
}
//-------------------------------------------------------------------
// on-the-fly calculation of the heat flux
void ATC_TransferKernel::compute_potential_heatflux(DENS_MAT& flux)
{
set_xPointer();
flux.zero();
// neighbor lists
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
double ** xatom = lammpsInterface_->xatom();
double ** vatom = lammpsInterface_->vatom();
double lam1,lam2;
double bond_value;
// process differently for mesh vs translation-invariant kernels
// "normal" kernel functions
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
double kernel_inv_vol = kernelFunction_->inv_vol();
for (int i = 0; i < nNodes_; i++) {
int inode = i;
// point
xI = (feEngine_->fe_mesh())->nodal_coordinates(i);
if (!kernelFunction_->node_contributes(xI)) {
continue;
}
for (int j = 0; j < nLocal_; j++) {
int lammps_j = internalToAtom_(j);
xa.copy(xPointer_[lammps_j],3);
// difference vector
xaI = xa - xI;
lammpsInterface_->periodicity_correction(xaI.ptr());
for (int k = 0; k < numneigh[lammps_j]; ++k) {
int lammps_k = firstneigh[lammps_j][k];
// first atom location
xb.copy(xPointer_[lammps_k],3);
// difference vector
xba = xb - xa;
xbI = xba + xaI;
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
// compute virial
if (lam1 < lam2) {
bond_value
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
double rsq = delx*delx + dely*dely + delz*delz;
double fforce = 0;
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
fforce *= 0.5; // dbl count
double * v = vatom[lammps_j];
fforce *= (delx*v[0] + dely*v[1] + delz*v[2]);
if (atomToElementMapType_ == LAGRANGIAN) {
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
flux(inode,0) +=fforce*delX*bond_value;
flux(inode,1) +=fforce*delY*bond_value;
flux(inode,2) +=fforce*delZ*bond_value;
}
else { // EULERIAN
flux(inode,0) +=fforce*delx*bond_value;
flux(inode,1) +=fforce*dely*bond_value;
flux(inode,2) +=fforce*delz*bond_value;
}
}
}
}
}
}
//-------------------------------------------------------------------
// calculation of the dislocation density tensor
void ATC_TransferKernel::compute_dislocation_density(DENS_MAT & A)
{
A.reset(nNodes_,9);
#ifdef HAS_DXA
double cnaCutoff = lammpsInterface_->near_neighbor_cutoff();
// Extract dislocation lines within the processor's domain.
DXADislocationExtractor extractor(lammpsInterface_->lammps_pointer(),dxaExactMode_);
extractor.extractDislocations(lammpsInterface_->neighbor_list(), cnaCutoff);
// Calculate scalar dislocation density and density tensor.
double dislocationDensity = 0.0;
double dislocationDensityTensor[9] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
const std::vector<DislocationSegment*>& segments = extractor.getSegments();
int localNumberLines = (int) segments.size();
int totalNumberLines;
lammpsInterface_->int_allsum(&localNumberLines,&totalNumberLines,1);
if (totalNumberLines == 0) {
ATC::LammpsInterface::instance()->print_msg_once("no dislocation lines found");
return;
}
// for output
int nPt = 0, nSeg = 0;
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
DislocationSegment* segment = segments[segmentIndex];
const std::deque<Point3>& line = segment->line;
nPt += line.size();
nSeg += line.size()-1;
}
DENS_MAT segCoor(3,nPt);
Array2D<int> segConn(2,nSeg);
DENS_MAT segBurg(nPt,3);
DENS_MAT local_A(nNodes_,9);
local_A.zero();
DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_);
double kernel_inv_vol = kernelFunction_->inv_vol();
int iPt = 0, iSeg= 0;
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
DislocationSegment* segment = segments[segmentIndex];
const std::deque<Point3>& line = segment->line;
Vector3 burgers = segment->burgersVectorWorld;
Point3 x1, x2;
for(std::deque<Point3>::const_iterator p1 = line.begin(), p2 = line.begin() + 1; p2 < line.end(); ++p1, ++p2) {
x1 = (*p1);
x2 = (*p2);
Vector3 delta = x2 - x1;
// totals
dislocationDensity += Length(delta);
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
dislocationDensityTensor[3*j+i] += delta[i] * burgers[j];
}
}
// nodal partition
for(int k = 0; k < 3; k++) {
xa(k) = x1[k];
xb(k) = x2[k];
xba(k) = delta[k];
}
for (int I = 0; I < nNodes_; I++) {
xI = (feEngine_->fe_mesh())->nodal_coordinates(I);
if (!kernelFunction_->node_contributes(xI)) {
continue;
}
xaI = xa - xI;
lammpsInterface_->periodicity_correction(xaI.ptr());
xbI = xba + xaI;
double lam1=0,lam2=0;
kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2);
if (lam1 < lam2) {
double bond_value
= kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2));
local_A(I,0) += xba(0)*burgers[0]*bond_value;
local_A(I,1) += xba(0)*burgers[1]*bond_value;
local_A(I,2) += xba(0)*burgers[2]*bond_value;
local_A(I,3) += xba(1)*burgers[0]*bond_value;
local_A(I,4) += xba(1)*burgers[1]*bond_value;
local_A(I,5) += xba(1)*burgers[2]*bond_value;
local_A(I,6) += xba(2)*burgers[0]*bond_value;
local_A(I,7) += xba(2)*burgers[1]*bond_value;
local_A(I,8) += xba(2)*burgers[2]*bond_value;
}
}
segCoor(0,iPt) = x1[0];
segCoor(1,iPt) = x1[1];
segCoor(2,iPt) = x1[2];
segBurg(iPt,0) = burgers[0];
segBurg(iPt,1) = burgers[1];
segBurg(iPt,2) = burgers[2];
segConn(0,iSeg) = iPt;
segConn(1,iSeg) = iPt+1;
iPt++;
iSeg++;
}
segCoor(0,iPt) = x2[0];
segCoor(1,iPt) = x2[1];
segCoor(2,iPt) = x2[2];
segBurg(iPt,0) = burgers[0];
segBurg(iPt,1) = burgers[1];
segBurg(iPt,2) = burgers[2];
iPt++;
}
int count = nNodes_*9;
lammpsInterface_->allsum(local_A.ptr(),A.ptr(),count);
double totalDislocationDensity;
lammpsInterface_->allsum(&dislocationDensity,&totalDislocationDensity,1);
double totalDislocationDensityTensor[9];
lammpsInterface_->allsum(dislocationDensityTensor,totalDislocationDensityTensor,9);
int totalNumberSegments;
lammpsInterface_->int_allsum(&nSeg,&totalNumberSegments,1);
// output
double volume = lammpsInterface_->domain_volume();
stringstream ss;
ss << "total dislocation line length = " << totalDislocationDensity;
ss << " lines = " << totalNumberLines << " segments = " << totalNumberSegments;
ss << "\n ";
ss << "total dislocation density tensor = \n";
for(int i = 0; i < 3; i++) {
ss << " ";
for(int j = 0; j < 3; j++) {
totalDislocationDensityTensor[3*j+i] /= volume;
ss << totalDislocationDensityTensor[3*j+i] << " ";
}
ss << "\n";
}
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
if (nSeg > 0) {
set<int> otypes;
otypes.insert(VTK);
otypes.insert(FULL_GNUPLOT);
string name = "dislocation_segments_step=" ;
name += to_string(output_index());
OutputManager segOutput(name,otypes);
segOutput.write_geometry(&segCoor,&segConn);
OUTPUT_LIST segOut;
segOut["burgers_vector"] = &segBurg;
segOutput.write_data(0,&segOut);
}
#else
throw ATC_Error("unimplemented function compute_dislocation_density (DXA support not included");
#endif
}
} // end namespace ATC

View File

@ -0,0 +1,53 @@
/** ATC_Transfer : smoothing */
#ifndef ATC_TRANSFER_KERNEL_H
#define ATC_TRANSFER_KERNEL_H
// ATC headers
#include "ATC_Transfer.h"
namespace ATC {
class KernelFunction;
class ATC_TransferKernel : public ATC_Transfer {
public:
// constructor
ATC_TransferKernel(std::string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile = "none");
// destructor
virtual ~ATC_TransferKernel();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
protected:
/** routine to calculate matrix of kernel functions */
virtual void compute_kernel_matrix_molecule();
/** calculate projection on the fly*/
virtual void compute_projection(const DENS_MAT & atomData,
DENS_MAT & nodeData);
/** routine to calculate matrix of bond functions */
virtual void compute_bond_matrix();
/** routine to calculate stress on-the-fly */
virtual void compute_potential_stress(DENS_MAT& stress);
/** routine to calculate force part of the heat flux on-the-fly */
virtual void compute_potential_heatflux(DENS_MAT& flux);
/** calculate dislocation density tensor from DXA output */
virtual void compute_dislocation_density(DENS_MAT & dislocation_density);
};
};
#endif

View File

@ -0,0 +1,506 @@
// ATC headers
#include "ATC_TransferPartitionOfUnity.h"
#include "ATC_Error.h"
#include "FE_Engine.h"
#include "LammpsInterface.h"
#include "Quadrature.h"
#include "PerPairQuantity.h"
#ifdef HAS_DXA
#include "DislocationExtractor.h"
#endif
// Other Headers
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <fstream>
#include <exception>
using namespace std;
static const int line_ngauss = 10;
static double line_xg[line_ngauss], line_wg[line_ngauss];
namespace ATC {
ATC_TransferPartitionOfUnity::ATC_TransferPartitionOfUnity(
string groupName,
double ** & perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile)
: ATC_Transfer(groupName,perAtomArray,thisFix,matParamFile)
{
ATC::Quadrature::instance()->set_line_quadrature(line_ngauss,line_xg,line_wg);
// transform gauss points from [-1,1] to [0,1]
double lam1 = 0.0, lam2 = 1.0;
double del_lambda = 0.5*(lam2 - lam1);
double avg_lambda = 0.5*(lam2 + lam1);
for (int i = 0; i < line_ngauss; i++) {
double lambda = del_lambda*line_xg[i] +avg_lambda;
line_xg[i] = lambda;
line_wg[i] *= 0.5;
}
}
//-------------------------------------------------------------------
ATC_TransferPartitionOfUnity::~ATC_TransferPartitionOfUnity()
{
// clear out all managed memory to avoid conflicts with dependencies on class member data
interscaleManager_.clear();
}
//-------------------------------------------------------------------
void ATC_TransferPartitionOfUnity::compute_projection(
const DENS_MAT & atomData, DENS_MAT & nodeData)
{
throw ATC_Error("unimplemented function");
}
//-------------------------------------------------------------------
void ATC_TransferPartitionOfUnity::compute_bond_matrix()
{
atomicBondMatrix_ = bondMatrix_->quantity();
}
//-------------------------------------------------------------------
// kinetic energy portion of stress
/**
* @class KineticTensor
* @brief Class for computing the quantity - m v' (x) v'
*/
void ATC_TransferPartitionOfUnity::compute_kinetic_stress(
DENS_MAT& stress)
{
compute_variation_velocity();
int * type = lammpsInterface_->atom_type();
double * mass = lammpsInterface_->atom_mass();
double * rmass = lammpsInterface_->atom_rmass();
double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy]
DENS_MAT & v = variationVelocity_;
atomicTensor_.reset(nLocal_,6);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx];
ma *= mvv2e; // convert mass to appropriate units
atomicTensor_(i,0) -= ma*v(i,0)*v(i,0);
atomicTensor_(i,1) -= ma*v(i,1)*v(i,1);
atomicTensor_(i,2) -= ma*v(i,2)*v(i,2);
atomicTensor_(i,3) -= ma*v(i,0)*v(i,1);
atomicTensor_(i,4) -= ma*v(i,0)*v(i,2);
atomicTensor_(i,5) -= ma*v(i,1)*v(i,2);
}
project_volume_normalized(atomicTensor_, stress);
}
//-------------------------------------------------------------------
// on-the-fly calculation of stress
void ATC_TransferPartitionOfUnity::compute_potential_stress(DENS_MAT& stress)
{
int nCols;
if (atomToElementMapType_ == LAGRANGIAN)
nCols = 9;
else // EULERIAN
nCols = 6;
stress.reset(nNodes_,nCols);
// neighbor lists
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
double ** xatom = lammpsInterface_->xatom();
Array<bool> latticePeriodicity(3);
latticePeriodicity(0) = (bool) periodicity[0];
latticePeriodicity(1) = (bool) periodicity[1];
latticePeriodicity(2) = (bool) periodicity[2];
// process differently for mesh vs translation-invariant kernels
ATC::LammpsInterface::instance()->stream_msg_once("computing potential stress: ",true,false);
int heartbeatFreq = (nLocal_ <= 10 ? 1 : (int) nLocal_ / 10);
// mesh-based kernel functions
int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element();
Array<int> node_list(nodesPerElement);
DENS_VEC shp(nodesPerElement);
DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_);
DENS_VEC virial(nCols);
for (int j = 0; j < nLocal_; j++) {
if (j % heartbeatFreq == 0 ) {
ATC::LammpsInterface::instance()->stream_msg_once(".",false,false);
}
// first atom location
int lammps_j = internalToAtom_(j);
xa.copy(xPointer_[lammps_j],3);
for (int k = 0; k < numneigh[lammps_j]; ++k) {
int lammps_k = firstneigh[lammps_j][k];
//if (lammps_k < lammps_j) continue; // full neighbor list
// second (neighbor) atom location
xb.copy(xPointer_[lammps_k],3);
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
double rsq = delx*delx + dely*dely + delz*delz;
double fforce = 0;
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
fforce *= 0.5; // 1/2 sum_ab = sum_(ab)
if (atomToElementMapType_ == LAGRANGIAN) {
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
virial[0] =-delx*fforce*delX;
virial[1] =-delx*fforce*delY;
virial[2] =-delx*fforce*delZ;
virial[3] =-dely*fforce*delX;
virial[4] =-dely*fforce*delY;
virial[5] =-dely*fforce*delZ;
virial[6] =-delz*fforce*delX;
virial[7] =-delz*fforce*delY;
virial[8] =-delz*fforce*delZ;
}
else {// EULERIAN
virial[0] =-delx*delx*fforce;
virial[1] =-dely*dely*fforce;
virial[2] =-delz*delz*fforce;
virial[3] =-delx*dely*fforce;
virial[4] =-delx*delz*fforce;
virial[5] =-dely*delz*fforce;
}
xab = xa - xb;
for (int i = 0; i < line_ngauss; i++) {
double lambda = line_xg[i];
xlambda = lambda*xab + xb;
lammpsInterface_->periodicity_correction(xlambda.ptr());
feEngine_->shape_functions(xlambda,shp,node_list);
// accumulate to nodes whose support overlaps the integration point
for (int I = 0; I < nodesPerElement; I++) {
int inode = node_list(I);
double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode);
double bond_value = inv_vol*shp(I)*line_wg[i];
for (int j = 0; j < nCols; j++)
stress(inode,j) += virial[j]*bond_value;
}
}
}
}
if (lammpsInterface_->comm_rank() == 0) {
ATC::LammpsInterface::instance()->stream_msg_once("done",false,true);
}
}
//-------------------------------------------------------------------
// compute kinetic part of heat flux
void ATC_TransferPartitionOfUnity::compute_kinetic_heatflux(
DENS_MAT& flux)
{
compute_variation_velocity();
int * type = lammpsInterface_->atom_type();
double * mass = lammpsInterface_->atom_mass();
double * rmass = lammpsInterface_->atom_rmass();
double mvv2e = lammpsInterface_->mvv2e();
double * atomPE = lammpsInterface_->compute_pe_peratom();
double atomKE, atomEnergy;
atomicVector_.reset(nLocal_,3);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx];
ma *= mvv2e; // convert mass to appropriate units
atomKE = 0.0;
for (int j = 0; j < nsd_; j++) {
atomKE += 0.5*ma*(variationVelocity_(i,j)*variationVelocity_(i,j));
}
atomEnergy = atomKE + atomPE[atomIdx];
for (int j = 0; j < nsd_; j++) {
atomicVector_(i,j) += atomEnergy*variationVelocity_(i,j);
}
}
project_volume_normalized(atomicVector_,flux);
}
//-------------------------------------------------------------------
// on-the-fly calculation of the heat flux
void ATC_TransferPartitionOfUnity::compute_potential_heatflux(DENS_MAT& flux)
{
compute_variation_velocity();
flux.zero();
// neighbor lists
int *numneigh = lammpsInterface_->neighbor_list_numneigh();
int **firstneigh = lammpsInterface_->neighbor_list_firstneigh();
double ** xatom = lammpsInterface_->xatom();
Array<bool> latticePeriodicity(3);
latticePeriodicity(0) = (bool) periodicity[0];
latticePeriodicity(1) = (bool) periodicity[1];
latticePeriodicity(2) = (bool) periodicity[2];
// process differently for mesh vs translation-invariant kernels
// mesh-based kernel functions
int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element();
Array<int> node_list(nodesPerElement);
DENS_VEC shp(nodesPerElement);
DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_);
for (int j = 0; j < nLocal_; j++) {
// first atom location
int lammps_j = internalToAtom_(j);
xa.copy(xPointer_[lammps_j],3);
for (int k = 0; k < numneigh[lammps_j]; ++k) {
int lammps_k = firstneigh[lammps_j][k];
// second (neighbor) atom location
xb.copy(xPointer_[lammps_k],3);
double delx = xatom[lammps_j][0] - xatom[lammps_k][0];
double dely = xatom[lammps_j][1] - xatom[lammps_k][1];
double delz = xatom[lammps_j][2] - xatom[lammps_k][2];
double rsq = delx*delx + dely*dely + delz*delz;
double fforce = 0;
lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce);
fforce *= 0.5; // 1/2 sum_ab = sum_(ab)
fforce *= (delx*variationVelocity_(j,0) +
dely*variationVelocity_(j,1) +
delz*variationVelocity_(j,2));
double flux_vec[3];
if (atomToElementMapType_ == LAGRANGIAN) {
double delX = xref_[lammps_j][0] - xref_[lammps_k][0];
double delY = xref_[lammps_j][1] - xref_[lammps_k][1];
double delZ = xref_[lammps_j][2] - xref_[lammps_k][2];
flux_vec[0] =fforce*delX;
flux_vec[1] =fforce*delY;
flux_vec[2] =fforce*delZ;
}
else {// EULERIAN
flux_vec[0] =fforce*delx;
flux_vec[1] =fforce*dely;
flux_vec[2] =fforce*delz;
}
xab = xa - xb;
for (int i = 0; i < line_ngauss; i++) {
double lambda = line_xg[i];
xlambda = lambda*xab + xb;
lammpsInterface_->periodicity_correction(xlambda.ptr());
feEngine_->shape_functions(xlambda,shp,node_list);
// accumulate to nodes whose support overlaps the integration point
for (int I = 0; I < nodesPerElement; I++) {
int inode = node_list(I);
double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode);
double bond_value = inv_vol*shp(I)*line_wg[i];
flux(inode,0) += flux_vec[0]*bond_value;
flux(inode,1) += flux_vec[1]*bond_value;
flux(inode,2) += flux_vec[2]*bond_value;
}
}
}
}
}
//-------------------------------------------------------------------
void ATC_TransferPartitionOfUnity::compute_variation_velocity()
{
// now compute v'_a = v_a - N_Ia v_I
variationVelocity_.reset(nLocal_,nsd_);
if (nLocal_>0) {
// interpolate nodal velocities to the atoms
vbar_.reset(nLocal_,nsd_);
double ** v = lammpsInterface_->vatom();
PerAtomQuantity<double> * vbar = interscaleManager_.per_atom_quantity(field_to_prolongation_name(VELOCITY));
if (!vbar) {
DENS_MAN * nodeVelocity = interscaleManager_.dense_matrix(field_to_string(VELOCITY));
if (this->kernel_on_the_fly()) {
vbar = new OnTheFlyShapeFunctionProlongation(this,
nodeVelocity,this->atom_coarsegraining_positions());
} else {
vbar = new FtaShapeFunctionProlongation(this,
nodeVelocity,this->interpolant());
}
interscaleManager_.add_per_atom_quantity(vbar,
field_to_prolongation_name(VELOCITY));
}
// use of prolong assumes atom system contained within mesh
vbar_ = vbar->quantity();
// compute and store variation velocities of atoms
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
for (int j = 0; j < nsd_; j++) {
variationVelocity_(i,j) = v[atomIdx][j] - vbar_(i,j);
}
}
}
}
//-------------------------------------------------------------------
// calculation of the dislocation density tensor
void ATC_TransferPartitionOfUnity::compute_dislocation_density(DENS_MAT & A)
{
A.reset(nNodes_,9);
#ifdef HAS_DXA
double cnaCutoff = lammpsInterface_->near_neighbor_cutoff();
// Extract dislocation lines within the processor's domain.
DXADislocationExtractor extractor(lammpsInterface_->lammps_pointer(),dxaExactMode_);
extractor.extractDislocations(lammpsInterface_->neighbor_list(), cnaCutoff);
// Calculate scalar dislocation density and density tensor.
double dislocationDensity = 0.0;
double dislocationDensityTensor[9] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
const std::vector<DislocationSegment*>& segments = extractor.getSegments();
int localNumberLines = (int) segments.size();
int totalNumberLines;
lammpsInterface_->int_allsum(&localNumberLines,&totalNumberLines,1);
if (totalNumberLines == 0) {
ATC::LammpsInterface::instance()->print_msg_once("no dislocation lines found");
return;
}
// for output
int nPt = 0, nSeg = 0;
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
DislocationSegment* segment = segments[segmentIndex];
const std::deque<Point3>& line = segment->line;
nPt += line.size();
nSeg += line.size()-1;
}
DENS_MAT segCoor(3,nPt);
Array2D<int> segConn(2,nSeg);
DENS_MAT segBurg(nPt,3);
DENS_MAT local_A(nNodes_,9);
local_A.zero();
Array<bool> latticePeriodicity(3);
latticePeriodicity(0) = (bool) periodicity[0];
latticePeriodicity(1) = (bool) periodicity[1];
latticePeriodicity(2) = (bool) periodicity[2];
// mesh-based kernel functions
int nodesPerElement = feEngine_->fe_mesh()->num_nodes_per_element();
Array<int> node_list(nodesPerElement);
DENS_VEC shp(nodesPerElement);
DENS_VEC xa(nsd_),xb(nsd_),xba(nsd_),xlambda(nsd_);
int iPt = 0, iSeg= 0;
for(unsigned segmentIndex = 0; segmentIndex < segments.size(); segmentIndex++) {
DislocationSegment* segment = segments[segmentIndex];
const std::deque<Point3>& line = segment->line;
Vector3 burgers = segment->burgersVectorWorld;
Point3 x1, x2;
for(std::deque<Point3>::const_iterator p1 = line.begin(), p2 = line.begin() + 1; p2 < line.end(); ++p1, ++p2) {
x1 = (*p1);
x2 = (*p2);
Vector3 delta = x2 - x1;
// totals
dislocationDensity += Length(delta);
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
dislocationDensityTensor[3*j+i] += delta[i] * burgers[j];
}
}
// nodal partition
for(int k = 0; k < 3; k++) {
xa(k) = x1[k];
xb(k) = x2[k];
xba(k) = delta[k];
}
for (int i = 0; i < line_ngauss; i++) {
double lambda = line_xg[i];
xlambda = lambda*xba + xa;
lammpsInterface_->periodicity_correction(xlambda.ptr());
feEngine_->shape_functions(xlambda,shp,node_list);
// accumulate to nodes whose support overlaps the integration point
for (int I = 0; I < nodesPerElement; I++) {
int inode = node_list(I);
double inv_vol = (accumulantInverseVolumes_->quantity())(inode,inode);
double bond_value = inv_vol*shp(I)*line_wg[i];
local_A(inode,0) += xba(0)*burgers[0]*bond_value;
local_A(inode,1) += xba(0)*burgers[1]*bond_value;
local_A(inode,2) += xba(0)*burgers[2]*bond_value;
local_A(inode,3) += xba(1)*burgers[0]*bond_value;
local_A(inode,4) += xba(1)*burgers[1]*bond_value;
local_A(inode,5) += xba(1)*burgers[2]*bond_value;
local_A(inode,6) += xba(2)*burgers[0]*bond_value;
local_A(inode,7) += xba(2)*burgers[1]*bond_value;
local_A(inode,8) += xba(2)*burgers[2]*bond_value;
}
}
segCoor(0,iPt) = x1[0];
segCoor(1,iPt) = x1[1];
segCoor(2,iPt) = x1[2];
segBurg(iPt,0) = burgers[0];
segBurg(iPt,1) = burgers[1];
segBurg(iPt,2) = burgers[2];
segConn(0,iSeg) = iPt;
segConn(1,iSeg) = iPt+1;
iPt++;
iSeg++;
}
segCoor(0,iPt) = x2[0];
segCoor(1,iPt) = x2[1];
segCoor(2,iPt) = x2[2];
segBurg(iPt,0) = burgers[0];
segBurg(iPt,1) = burgers[1];
segBurg(iPt,2) = burgers[2];
iPt++;
}
int count = nNodes_*9;
lammpsInterface_->allsum(local_A.ptr(),A.ptr(),count);
double totalDislocationDensity;
lammpsInterface_->allsum(&dislocationDensity,&totalDislocationDensity,1);
double totalDislocationDensityTensor[9];
lammpsInterface_->allsum(dislocationDensityTensor,totalDislocationDensityTensor,9);
int totalNumberSegments;
lammpsInterface_->int_allsum(&nSeg,&totalNumberSegments,1);
// output
double volume = lammpsInterface_->domain_volume();
stringstream ss;
ss << "total dislocation line length = " << totalDislocationDensity;
ss << " lines = " << totalNumberLines << " segments = " << totalNumberSegments;
ss << "\n ";
ss << "total dislocation density tensor = \n";
for(int i = 0; i < 3; i++) {
ss << " ";
for(int j = 0; j < 3; j++) {
totalDislocationDensityTensor[3*j+i] /= volume;
ss << totalDislocationDensityTensor[3*j+i] << " ";
}
ss << "\n";
}
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
ss.str("");
DENS_VEC A_avg(9);
for (int i = 0; i < nNodes_; i++) {
for (int j = 0; j < 9; j++) {
A_avg(j) += A(i,j);
}
}
A_avg /= nNodes_;
ss << "average nodal dislocation density tensor = \n";
ss << A_avg(0) << " " << A_avg(1) << " " << A_avg(2) << "\n";
ss << A_avg(3) << " " << A_avg(4) << " " << A_avg(5) << "\n";
ss << A_avg(6) << " " << A_avg(7) << " " << A_avg(8) << "\n";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
if (nSeg > 0) {
set<int> otypes;
otypes.insert(VTK);
otypes.insert(FULL_GNUPLOT);
string name = "dislocation_segments_step=" ;
name += to_string(output_index());
OutputManager segOutput(name,otypes);
segOutput.write_geometry(&segCoor,&segConn);
OUTPUT_LIST segOut;
segOut["burgers_vector"] = &segBurg;
segOutput.write_data(0,&segOut);
}
#else
throw ATC_Error("TransferParititionOfUnity::compute_dislocaton_density - unimplemented function");
#endif
}
} // end namespace ATC

View File

@ -0,0 +1,59 @@
#ifndef ATC_TRANSFER_PARTITION_OF_UNITY_H
#define ATC_TRANSFER_PARTITION_OF_UNITY_H
// ATC headers
#include "ATC_Transfer.h"
namespace ATC {
class ATC_TransferPartitionOfUnity : public ATC_Transfer {
public:
// constructor
ATC_TransferPartitionOfUnity(std::string groupName,
double **& perAtomArray,
LAMMPS_NS::Fix * thisFix,
std::string matParamFile = "none");
// destructor
virtual ~ATC_TransferPartitionOfUnity();
protected:
/** routine to calculate matrix of kernel functions */
virtual void compute_kernel_matrix_molecule() {};
/** calculate projection on the fly*/
virtual void compute_projection(const DENS_MAT & atomData,
DENS_MAT & nodeData);
/** routine to calculate matrix of bond functions */
virtual void compute_bond_matrix();
/** routine to calculate kinetic part of stress */
virtual void compute_kinetic_stress(DENS_MAT& stress);
/** routine to calculate stress on-the-fly */
virtual void compute_potential_stress(DENS_MAT& stress);
/** routine to calculate kinetic part of heat flux */
virtual void compute_kinetic_heatflux(DENS_MAT& flux);
/** routine to calculate force part of the heat flux on-the-fly */
virtual void compute_potential_heatflux(DENS_MAT& flux);
/** compute velocity & difference betw atomic and (local) mean velocity */
void compute_variation_velocity();
/** calculate dislocation density tensor from DXA output */
virtual void compute_dislocation_density(DENS_MAT & dislocation_density);
private:
DENS_MAT variationVelocity_;
DENS_MAT vbar_;
};
}
#endif

622
lib/atc/ATC_TypeDefs.h Normal file
View File

@ -0,0 +1,622 @@
#ifndef ATC_TYPEDEFS_H
#define ATC_TYPEDEFS_H
#include <set>
using std::pair;
using std::set;
#ifdef NEW_LAMMPS
#include "lmptype.h"
#endif
#include "Array.h"
#include "Array2D.h"
using namespace ATC_matrix;
#include "MatrixLibrary.h"
#include "DependencyManager.h"
namespace ATC
{
/** physical constants */
static const double kBeV_ = 8.617343e-5;// [eV/K]
/** unsigned ints, when needed */
typedef int INDEX;
/** elementset integral */
enum ElementsetOperationType {
ELEMENTSET_TOTAL=0,
ELEMENTSET_AVERAGE
};
/** faceset integral */
enum FacesetIntegralType {
BOUNDARY_INTEGRAL=0,
CONTOUR_INTEGRAL
};
/** nodeset operation */
enum NodesetOperationType {
NODESET_SUM=0,
NODESET_AVERAGE
};
/** boundary integration */
enum BoundaryIntegrationType {
NO_QUADRATURE=0,
FE_QUADRATURE,
FE_INTERPOLATION
};
/** domain integration */
enum IntegrationDomainType {
FULL_DOMAIN=0,
ATOM_DOMAIN,
FE_DOMAIN,
FULL_DOMAIN_ATOMIC_QUADRATURE_SOURCE
};
/** domain decomposition */
enum DomainDecompositionType {
REPLICATED_MEMORY=0,
DISTRIBUTED_MEMORY
};
/** atomic weight specification */
enum AtomicWeightType {
USER=0,
LATTICE,
ELEMENT,
REGION,
GROUP,
MULTISCALE,
NODE,
NODE_ELEMENT,
READ_IN
};
/** geometry location with respect to MD domain */
enum GeometryType {
FE_ONLY = 0,
MD_ONLY,
BOUNDARY
};
/** enumerated type for atomic reference frame */
enum AtomToElementMapType {
LAGRANGIAN=0,
EULERIAN
};
/* enumerated type for coupling matrix structure */
enum MatrixStructure {
FULL=0, // contributions from all nodes
LOCALIZED, // contributions only from nodes with sources
LUMPED // row-sum lumped version of full matrix
};
/* enumerated type for distinguishing ghost from internal atoms */
enum AtomType {
INTERNAL=0,
GHOST,
ALL,
PROC_GHOST,
NO_ATOMS,
NUM_ATOM_TYPES
};
/** field types */
enum FieldName {
TIME=-2,
POSITION=-1,
TEMPERATURE=0, // Intrinsic Fields
DISPLACEMENT,
VELOCITY,
MASS_DENSITY,
CHARGE_DENSITY,
SPECIES_CONCENTRATION,
ELECTRON_DENSITY, // Extrinsic Fields
ELECTRON_VELOCITY,
ELECTRON_TEMPERATURE,
ELECTRIC_POTENTIAL,
ELECTRON_WAVEFUNCTION,
ELECTRON_WAVEFUNCTIONS,
ELECTRON_WAVEFUNCTION_ENERGIES,
FERMI_ENERGY,
MOMENTUM,
PROJECTED_VELOCITY,
KINETIC_TEMPERATURE,
THERMAL_ENERGY,
KINETIC_ENERGY,
STRESS,
HEAT_FLUX,
CHARGE_FLUX,
SPECIES_FLUX,
INTERNAL_ENERGY,
REFERENCE_POTENTIAL_ENERGY,
POTENTIAL_ENERGY,
ENERGY,
NUMBER_DENSITY,
ESHELBY_STRESS,
CAUCHY_BORN_STRESS,
CAUCHY_BORN_ENERGY,
CAUCHY_BORN_ESHELBY_STRESS,
TRANSFORMED_STRESS,
VACANCY_CONCENTRATION,
ROTATION,
STRETCH,
DIPOLE_MOMENT,
QUADRUPOLE_MOMENT,
CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT,
DISLOCATION_DENSITY,
NUM_TOTAL_FIELDS
};
const int NUM_FIELDS = ELECTRON_WAVEFUNCTION+1;
#define NDIM 3
static const int FieldSizes[NUM_TOTAL_FIELDS] = {
1, // TEMPERATURE
NDIM, // DISPLACEMENT
NDIM, // VELOCITY
1, // MASS_DENSITY
1, // CHARGE_DENSITY
0, // SPECIES_CONCENTRATION - VARIABLE
1, // ELECTRON_DENSITY
NDIM, // ELECTRON_VELOCITY
1, // ELECTRON_TEMPERATURE
1, // ELECTRIC_POTENTIAL
1, // ELECTRON_WAVEFUNCTION ?
0, // ELECTRON_WAVEFUNCTIONS - VARIABLE
0, // ELECTRON_WAVEFUNCTION_ENERGIES - VARIABLE
1, // FERMI_ENERGY
NDIM, // MOMENTUM
NDIM, // PROJECTED_VELOCITY
1, // KINETIC_TEMPERATURE
1, // THERMAL_ENERGY
1, // KINETIC_ENERGY
NDIM*NDIM, // STRESS
NDIM, // HEAT_FLUX
NDIM, // CHARGE_FLUX
0, // SPECIES_FLUX - VARIABLE
1, // INTERNAL_ENERGY
1, // REFERENCE_POTENTIAL_ENERGY
1, // POTENTIAL_ENERGY
1, // ENERGY
1, // NUMBER_DENSITY
NDIM*NDIM, // ESHELBY_STRESS
NDIM*NDIM, // CAUCHY_BORN_STRESS,
1, // CAUCHY_BORN_ENERGY,
NDIM*NDIM, // CAUCHY_BORN_ESHELBY_STRESS,
NDIM*NDIM, // TRANSFORMED_STRESS,
1, // VACANCY_CONCENTRATION,
NDIM*NDIM, // ROTATION,
NDIM*NDIM, // STRETCH,
NDIM, // DIPOLE_MOMENT,
NDIM, // QUADRUPOLE_MOMENT,
NDIM*NDIM, // CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT,
NDIM*NDIM // DISLOCATION_DENSITY
};
enum hardyNormalization {
NO_NORMALIZATION=0,
VOLUME_NORMALIZATION, NUMBER_NORMALIZATION, MASS_NORMALIZATION
};
/** enums for FE Element and Interpolate classes */
enum FeEltGeometry {HEXA, TETRA};
enum FeIntQuadrature {NODAL, GAUSS1, GAUSS2, GAUSS3, FACE};
/** field name enum to string */
inline FeIntQuadrature string_to_FIQ(const string &str)
{
if (str == "nodal")
return NODAL;
else if (str == "gauss1")
return GAUSS1;
else if (str == "gauss2")
return GAUSS2;
else if (str == "gauss3")
return GAUSS3;
else if (str == "face")
return FACE;
else
throw ATC_Error("Bad quadrature input" + str + ".");
}
/** field name enum to string */
inline string field_to_string(const FieldName index)
{
switch (index) {
case TEMPERATURE:
return "temperature";
case DISPLACEMENT:
return "displacement";
case VELOCITY:
return "velocity";
case MASS_DENSITY:
return "mass_density";
case CHARGE_DENSITY:
return "charge_density";
case ELECTRON_DENSITY:
return "electron_density";
case ELECTRON_VELOCITY:
return "electron_velocity";
case ELECTRON_TEMPERATURE:
return "electron_temperature";
case ELECTRIC_POTENTIAL:
return "electric_potential";
case ELECTRON_WAVEFUNCTION:
return "electron_wavefunction";
case ELECTRON_WAVEFUNCTIONS:
return "electron_wavefunctions";
case ELECTRON_WAVEFUNCTION_ENERGIES:
return "electron_wavefunction_energies";
case FERMI_ENERGY:
return "fermi_energy";
case MOMENTUM:
return "momentum";
case PROJECTED_VELOCITY:
return "projected_velocity";
case KINETIC_TEMPERATURE:
return "kinetic_temperature";
case THERMAL_ENERGY:
return "thermal_energy";
case KINETIC_ENERGY:
return "kinetic_energy";
case STRESS:
return "stress";
case ESHELBY_STRESS:
return "eshelby_stress";
case CAUCHY_BORN_STRESS:
return "cauchy_born_stress";
case CAUCHY_BORN_ENERGY:
return "cauchy_born_energy";
case CAUCHY_BORN_ESHELBY_STRESS:
return "cauchy_born_eshelby_stress";
case HEAT_FLUX:
return "heat_flux";
case CHARGE_FLUX:
return "charge_flux";
case SPECIES_FLUX:
return "species_flux";
case INTERNAL_ENERGY:
return "internal_energy";
case POTENTIAL_ENERGY:
return "potential_energy";
case REFERENCE_POTENTIAL_ENERGY:
return "reference_potential_energy";
case ENERGY:
return "energy";
case NUMBER_DENSITY:
return "number_density";
case TRANSFORMED_STRESS:
return "transformed_stress";
case VACANCY_CONCENTRATION:
return "vacancy_concentration";
case SPECIES_CONCENTRATION:
return "species_concentration";
case ROTATION:
return "rotation";
case STRETCH:
return "stretch";
case DIPOLE_MOMENT:
return "dipole_moment";
case QUADRUPOLE_MOMENT:
return "quadrupole_moment";
default:
throw ATC_Error("field not found in field_to_string");
}
};
/** string to field enum */
inline FieldName string_to_field(const string & name)
{
if (name=="temperature")
return TEMPERATURE;
else if (name=="displacement")
return DISPLACEMENT;
else if (name=="velocity")
return VELOCITY;
else if (name=="mass_density")
return MASS_DENSITY;
else if (name=="charge_density")
return CHARGE_DENSITY;
else if (name=="electron_density")
return ELECTRON_DENSITY;
else if (name=="electron_velocity")
return ELECTRON_VELOCITY;
else if (name=="electron_temperature")
return ELECTRON_TEMPERATURE;
else if (name=="electric_potential")
return ELECTRIC_POTENTIAL;
else if (name=="electron_wavefunction")
return ELECTRON_WAVEFUNCTION;
else if (name=="electron_wavefunctions")
return ELECTRON_WAVEFUNCTIONS;
else if (name=="electron_wavefunction_energies")
return ELECTRON_WAVEFUNCTION_ENERGIES;
else if (name=="fermi_energy")
return FERMI_ENERGY;
else if (name=="momentum")
return MOMENTUM;
else if (name=="projected_velocity")
return PROJECTED_VELOCITY;
else if (name=="kinetic_temperature")
return KINETIC_TEMPERATURE; // temperature from total KE
else if (name=="thermal_energy")
return THERMAL_ENERGY;
else if (name=="kinetic_energy")
return KINETIC_ENERGY;
else if (name=="stress")
return STRESS;
else if (name=="eshelby_stress")
return ESHELBY_STRESS;
else if (name=="cauchy_born_stress")
return CAUCHY_BORN_STRESS;
else if (name=="cauchy_born_energy")
return CAUCHY_BORN_ENERGY;
else if (name=="cauchy_born_eshelby_stress")
return CAUCHY_BORN_ESHELBY_STRESS;
else if (name=="heat_flux")
return HEAT_FLUX;
else if (name=="charge_flux")
return CHARGE_FLUX;
else if (name=="species_flux")
return SPECIES_FLUX;
else if (name=="internal_energy")
return INTERNAL_ENERGY;
else if (name=="reference_potential_energy")
return REFERENCE_POTENTIAL_ENERGY;
else if (name=="potential_energy")
return POTENTIAL_ENERGY;
else if (name=="energy")
return ENERGY;
else if (name=="number_density")
return NUMBER_DENSITY;
else if (name=="transformed_stress")
return TRANSFORMED_STRESS;
else if (name=="vacancy_concentration")
return VACANCY_CONCENTRATION;
else if (name=="species_concentration")
return SPECIES_CONCENTRATION;
else if (name=="rotation")
return ROTATION;
else if (name=="stretch")
return STRETCH;
else if (name=="dipole_moment")
return DIPOLE_MOMENT;
else if (name=="quadrupole_moment")
return QUADRUPOLE_MOMENT;
else
throw ATC_Error(name + " is not a valid field");
};
inline bool is_intrinsic(const FieldName & field_enum)
{
if (field_enum==TEMPERATURE
|| field_enum==DISPLACEMENT
|| field_enum==VELOCITY
|| field_enum==MASS_DENSITY
|| field_enum==CHARGE_DENSITY
|| field_enum==SPECIES_CONCENTRATION
|| field_enum==KINETIC_TEMPERATURE
|| field_enum==POTENTIAL_ENERGY
|| field_enum==REFERENCE_POTENTIAL_ENERGY
) return true;
else return false;
};
inline string field_to_intrinsic_name(const FieldName index)
{
if (is_intrinsic(index)) {
return "NodalAtomic"+ATC_Utility::to_cap(field_to_string(index));
}
else {
throw ATC_Error("field "+field_to_string(index)+" is not an intrinsic field");
}
}
inline string field_to_restriction_name(const FieldName index)
{
if (is_intrinsic(index)) {
return "Restricted"+ATC_Utility::to_cap(field_to_string(index));
}
else {
throw ATC_Error("field "+field_to_string(index)+" is not an intrinsic field");
}
}
inline string field_to_prolongation_name(const FieldName index)
{
return "Prolonged"+ATC_Utility::to_cap(field_to_string(index));
}
/** types of temperature definitions */
enum TemperatureDefType {
NONE = 0,
KINETIC,
TOTAL
};
/** types of ghost boundary conditions in momentum */
enum BoundaryDynamicsType {
NO_BOUNDARY_DYNAMICS=0,
PRESCRIBED,
DAMPED_HARMONIC,
COUPLED
};
/** string to temperature definition enum */
inline bool string_to_temperature_def(const string & name, TemperatureDefType & index) {
if (name=="none")
index = NONE;
else if (name=="kinetic")
index = KINETIC;
else if (name=="total")
index = TOTAL;
else {
throw ATC_Error("temperature operator type "+name+" not valid");
return false;
}
return true;
};
/** solver types */
enum SolverType { DIRECT=0, ITERATIVE};
enum DirichletType {DIRICHLET_PENALTY=0, DIRICHLET_CONDENSE};
/** physics types */
enum PhysicsType
{
NO_PHYSICS=0, // for post-processing only
THERMAL,
ELASTIC,
SHEAR,
THERMO_ELASTIC,
SPECIES // aka Mass
};
/** rhs types */
enum FluxType
{
FLUX = 0, // has a source weighted by gradient of shape function
SOURCE, // has a source term weighted by the shape function
PRESCRIBED_SOURCE, // has a prescribed source term
ROBIN_SOURCE, // has a Robin source term
EXTRINSIC_SOURCE, // has an extrinsic source term
NUM_FLUX
};
/** stiffness/ derivative of rhs types */
enum StiffnessType
{
BB_STIFFNESS = 0,
NN_STIFFNESS,
BN_STIFFNESS,
NB_STIFFNESS,
NUM_STIFFNESS
};
/** LAMMPS atom type identifiers */
enum IdType {
ATOM_TYPE=0,
ATOM_GROUP
};
/** molecule size identifiers */
enum MolSize {
MOL_SMALL=0,
MOL_LARGE
};
/** basic */
typedef pair<int, int> PAIR;
/** typedefs for compact set of bc values */
typedef set < pair < int, double> > BC_SET; // node, value
typedef vector< BC_SET > BCS; // dof (node, value)
typedef set<int> NSET; // nodeset
typedef set<PAIR> FSET; // faceset
typedef set<int> ESET; // elementset
/** typedefs for N and B integrand functions */
typedef set<FieldName> ARG_NAMES;
typedef map<FieldName, DenseMatrix<double> > ARGS;
typedef MatrixDependencyManager<DenseMatrix, double> FIELD;
typedef vector<MatrixDependencyManager<DenseMatrix, double> > GRAD_FIELD;
typedef map<FieldName, MatrixDependencyManager<DenseMatrix, double> > FIELDS;
typedef map<FieldName, MatrixDependencyManager<DenseMatrix, double> * > FIELD_POINTERS;
typedef map<FieldName, DenseMatrix<double> > FIELD_MATS;
typedef map<string, MatrixDependencyManager<DenseMatrix, double> > TAG_FIELDS;
typedef map<FieldName, vector<MatrixDependencyManager<DenseMatrix, double> > > GRAD_FIELDS;
typedef map<FieldName, vector<DenseMatrix<double> > > GRAD_FIELD_MATS;
typedef map<FieldName, MatrixDependencyManager<DiagonalMatrix, double> > MASS_MATS;
typedef map<FieldName, MatrixDependencyManager<SparseMatrix, double> > CON_MASS_MATS;
typedef MatrixDependencyManager<DenseMatrix, double> DENS_MAN;
typedef MatrixDependencyManager<SparseMatrix, double> SPAR_MAN;
typedef MatrixDependencyManager<ParSparseMatrix, double> PAR_SPAR_MAN;
typedef MatrixDependencyManager<DiagonalMatrix, double> DIAG_MAN;
typedef MatrixDependencyManager<ParDiagonalMatrix, double> PAR_DIAG_MAN;
/** typedefs for WeakEquation evaluation */
typedef Array2D<bool> RHS_MASK;
/** typedefs for input/output */
typedef map<string, const Matrix<double>*> OUTPUT_LIST;
typedef map<string, Matrix<double>*> RESTART_LIST;
typedef pair<int, int> ID_PAIR;
typedef vector< pair<int, int> > ID_LIST;
/** misc typedefs */
class XT_Function;
class UXT_Function;
typedef map<FieldName, map<PAIR, Array<XT_Function*> > > SURFACE_SOURCE;
typedef map<FieldName, map<PAIR, Array<UXT_Function*> > > ROBIN_SURFACE_SOURCE;
typedef map<FieldName, Array2D<XT_Function *> > VOLUME_SOURCE;
typedef map<string, MatrixDependencyManager<DenseMatrix, double> > ATOMIC_DATA;
/** typedefs for FE_Mesh */
typedef map<string, set<int > > NODE_SET_MAP;
typedef map<string, set<int > > ELEMENT_SET_MAP;
typedef map<string, 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 string & dim, int & index, int & sgn)
{
char dir;
if (dim.empty()) return false;
sgn = (dim[0] == '-') ? -1 : 1;
dir = dim[dim.size()-1]; // dir is last character
if (dir == 'x') index = 0;
else if (dir == 'y') index = 1;
else if (dir == 'z') index = 2;
else return false;
return true;
};
/** string to index */
inline string index_to_string(const int &index)
{
if (index==0) return "x";
else if (index==1) return "y";
else if (index==2) return "z";
return "unknown";
};
/** string to index */
inline bool string_to_index(const string &dim, int &index)
{
if (dim=="x")
index = 0;
else if (dim=="y")
index = 1;
else if (dim=="z")
index = 2;
else
return false;
return true;
};
inline string print_mask(const Array2D<bool> & rhsMask)
{
string msg;
for (int i = 0; i < NUM_FIELDS; i++) {
FieldName field = (FieldName) i;
string name = field_to_string(field);
if (rhsMask(field,FLUX)
|| rhsMask(field,SOURCE)
|| rhsMask(field,PRESCRIBED_SOURCE)
|| rhsMask(field,ROBIN_SOURCE)
|| rhsMask(field,EXTRINSIC_SOURCE)) {
msg = "RHS_MASK: " + name;
if (rhsMask(field,FLUX)) msg += " flux";
if (rhsMask(field,SOURCE)) msg += " source";
if (rhsMask(field,PRESCRIBED_SOURCE)) msg += " prescribed_src";
if (rhsMask(field,ROBIN_SOURCE)) msg += " robin_src";
if (rhsMask(field,EXTRINSIC_SOURCE)) msg += " extrinsic_src";
}
}
return msg;
}
}
#endif

320
lib/atc/Array.h Normal file
View File

@ -0,0 +1,320 @@
#ifndef ARRAY_H
#define ARRAY_H
#include <cstdlib>
#include <iostream>
#include <string>
#include <stdio.h>
// for macros
#include "MatrixDef.h"
namespace ATC_matrix {
/**
* @class Array
* @brief Base class for creating, sizing and operating on 1-D arrays of data
*/
template<typename T>
class Array {
public:
Array();
Array(int len);
Array(const Array<T>& A);
virtual ~Array();
// Resize and reinitialize the array
virtual void reset(int len);
//* resizes the matrix, copy what fits default to OFF
virtual void resize(int len, bool copy=false);
// Access method to get the element i:
T& operator() (int i);
const T& operator() (int i) const;
// Assignment
virtual Array<T>& operator= (const Array<T> &other);
virtual Array<T>& operator= (const T &value);
// Get length of array
int size() const;
// Do I have this element?
bool has_member(T val) const;
// range
bool check_range(T min, T max) const;
void range(T & min, T & max) const;
// search an ordered array
int index(T& val) const;
// Return pointer to internal data
const T* data() const;
T* ptr() const;
// print
void print(std::string name = "") const;
// Dump templated type to disk; operation not safe for all types
void write_restart(FILE *f) const;
protected:
int len_;
T *data_;
};
template<typename T>
class AliasArray {
public:
AliasArray();
AliasArray(const Array<T>& A);
AliasArray(const AliasArray<T>& A);
AliasArray(int len, T * A);
virtual ~AliasArray();
virtual AliasArray<T>& operator= (const Array<T> &other);
virtual AliasArray<T>& operator= (const T &value);
const T& operator() (int i) const;
int size() const;
T* ptr() const;
protected:
int len_;
T *data_;
};
template<typename T>
Array<T>::Array(void) {
len_ = 0;
data_ = NULL;
}
template<typename T>
Array<T>::Array(int len) {
len_ = len;
data_ = new T[len_];
}
template<typename T>
Array<T>::Array(const Array<T>& A) {
len_ = A.len_;
if (A.data_==NULL)
data_ = NULL;
else {
data_ = new T[len_];
for(int i=0;i<len_;i++)
data_[i] = A.data_[i];
}
}
template<typename T>
Array<T>::~Array() {
if (data_ != NULL) delete[] data_;
}
template<typename T>
void Array<T>::reset(int len) {
if (len_ == len) { // no size change; don't realloc memory
return;
}
else { // size change, realloc memory
len_ = len;
if (data_ != NULL)
delete[] data_;
if (len_ > 0)
data_ = new T[len_];
else {
data_ = NULL;
len_ = 0;
}
}
}
template<typename T>
void Array<T>::resize(int len, bool copy) {
if (len_ == len) { // no size change; don't realloc memory
return;
}
else { // size change, realloc memory
len_ = len;
if (len_ > 0) {
if (copy && data_ != NULL) {
Array<T> temp(*this);
delete[] data_;
data_ = new T[len_];
for (int i = 0 ; i < len_; i++) {
if (i < temp.size())
data_[i] = temp.data_[i];
}
}
else {
if (data_ != NULL) delete[] data_;
data_ = new T[len_];
}
}
else {
data_ = NULL;
len_ = 0;
}
}
}
template<typename T>
T& Array<T>::operator() (int i) {
return data_[i];
}
template<typename T>
Array<T>& Array<T>::operator= (const Array<T> &other) {
if (data_ == NULL) { // initialize my internal storage to match LHS
len_ = other.len_;
if (other.data_==NULL)
data_ = NULL;
else
data_ = new T[len_];
}
for(int i=0;i<len_;i++)
data_[i] = other.data_[i];
return *this;
}
template<typename T>
Array<T>& Array<T>::operator= (const T &value) {
for(int i=0;i<len_;i++) data_[i] = value;
return *this;
}
template<typename T>
const T& Array<T>::operator() (int i) const {
return data_[i];
}
template<typename T>
int Array<T>::size(void) const {
return len_;
}
template<typename T>
bool Array<T>::has_member(T val) const {
int i;
bool retval = false;
for(i=0;i<len_;i++)
if (val == data_[i])
retval = true;
return(retval);
}
template<typename T>
bool Array<T>::check_range(T min, T max) const {
int i;
for(i=0;i<len_;i++) {
T val = data_[i];
if (val > max) return false;
else if (val < min) return false;
}
return true;
}
template<typename T>
void Array<T>::range(T& min, T& max) const {
int i;
min = max = data_[0];
for(i=1;i<len_;i++) {
T val = data_[i];
if (val > max) max = val;
else if (val < min) min = val;
}
}
template<typename T>
int Array<T>::index(T& val) const {
int idx = -1;
int i;
for(i=0;i<len_;i++) {
T x = data_[i];
if (val <= x) return idx;
idx++;
}
return idx;
}
template<typename T>
void Array<T>::write_restart(FILE *f) const {
fwrite(&len_,sizeof(int),1,f);
if (len_ > 0)
fwrite(data_,sizeof(T),len_,f);
}
template<typename T>
const T* Array<T>::data() const {
return data_;
}
template<typename T>
T* Array<T>::ptr() const {
return data_;
}
template<typename T>
void Array<T>::print(std::string name) const {
std::cout << "------- Begin "<<name<<" -----------------\n";
if (data_ != NULL) {
for(int i=0;i<len_;i++) std::cout << data_[i] << " ";
std::cout << "\n";
}
std::cout << "\n------- End "<<name<<" -------------------\n";
}
template<typename T>
AliasArray<T>::AliasArray(void) {
}
template<typename T>
AliasArray<T>::AliasArray(const AliasArray<T> & other) {
len_ = other.size();
data_ = other.ptr();
}
// for a mem continguous slice
template<typename T>
AliasArray<T>::AliasArray(int len, T * ptr) {
len_ = len;
data_ = ptr;
}
template<typename T>
AliasArray<T>::AliasArray(const Array<T>& A) {
len_ = A.len_;
data_ = A.ptr();
}
template<typename T>
AliasArray<T>::~AliasArray(void) {
len_ = 0;
data_ = NULL; // trick base class into not deleting parent data
}
template<typename T>
AliasArray<T>& AliasArray<T>::operator= (const Array<T> &other) {
len_ = other.size();
data_ = other.ptr();
return *this;
}
template<typename T>
AliasArray<T>& AliasArray<T>::operator= (const T &value) {
for(int i=0;i < len_;i++)
data_[i] = value;
return *this;
}
template<typename T>
const T& AliasArray<T>::operator() (int i) const {
return data_[i];
}
template<typename T>
int AliasArray<T>::size(void) const {
return len_;
}
template<typename T>
T* AliasArray<T>::ptr() const {
return data_;
}
} // end namespace
#endif // Array.h

194
lib/atc/Array2D.h Normal file
View File

@ -0,0 +1,194 @@
#ifndef ARRAY2D_H
#define ARRAY2D_H
#include <cstdlib>
#include <string>
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include "Array.h"
// for macros
#include "MatrixDef.h"
namespace ATC_matrix {
/**
* @class Array2D
* @brief Base class for creating, sizing and operating on 2-D arrays of data
*/
template<typename T>
class Array2D {
public:
Array2D();
Array2D(int nrows, int ncols);
Array2D(const Array2D<T>& A); // copy constructor
~Array2D();
// Resize and reinitalize matrix
void reset(int nrows, int ncols);
// Access method to get the (i,j) element:
T& operator() (int i, int j);
// Access method to get the i-th col
AliasArray<T> column(int i) const;
// Access method to get the (i,j) element:
const T& operator() (int i, int j) const;
// Copy operator
Array2D<T>& operator= (const Array2D<T>& other);
// assignment operator
Array2D<T>& operator= (const T other);
// Get size of Array2D
int nRows() const;
int nCols() const;
// Do I have this element?
bool has_member(T val) const;
// print
void print(std::string name ="") const;
// Dump templated type to disk; operation not safe for all types
void write_restart(FILE *f) const;
private:
int nrows_, ncols_;
T *data_;
};
template<typename T>
Array2D<T>::Array2D() {
nrows_ = 0;
ncols_ = 0;
data_ = NULL;
}
template<typename T>
Array2D<T>::Array2D(int nrows, int ncols) {
nrows_ = nrows;
ncols_ = ncols;
data_ = new T[nrows_ * ncols_];
}
template<typename T>
Array2D<T>::Array2D(const Array2D<T>& A) {
nrows_ = A.nrows_;
ncols_ = A.ncols_;
if (A.data_==NULL)
data_ = NULL;
else {
data_ = new T[nrows_ * ncols_];
for(int i=0;i<nrows_*ncols_;i++)
data_[i] = A.data_[i];
}
}
template<typename T>
void Array2D<T>::reset(int nrows, int ncols) {
if (nrows_ == nrows && ncols_ == ncols) { // no size change; don't realloc memory
return;
}
else { // size changed; realloc memory
nrows_ = nrows;
ncols_ = ncols;
if (data_ != NULL)
delete [] data_;
if (ncols_ > 0 && nrows_ > 0)
data_ = new T[nrows_ * ncols_];
else {
data_ = NULL;
nrows_ = 0;
ncols_ = 0;
}
}
}
template<typename T>
T& Array2D<T>::operator() (int row, int col) {
// Array bounds checking
return data_[col*nrows_ + row];
}
template<typename T>
const T& Array2D<T>::operator() (int row, int col) const {
// Array bounds checking
return data_[col*nrows_ + row];
}
template<typename T>
AliasArray<T> Array2D<T>::column(int col) const {
// Array bounds checking
return AliasArray<T>(nrows_,&(data_[col*nrows_]));
}
template<typename T>
Array2D<T>& Array2D<T>::operator= (const Array2D<T>& other) {
if (data_ == NULL) { // initialize my internal storage to match LHS
nrows_ = other.nrows_;
ncols_ = other.ncols_;
if (other.data_==NULL)
data_ = NULL;
else
data_ = new T[nrows_ * ncols_];
}
for(int i=0;i<nrows_*ncols_;i++)
data_[i] = other.data_[i];
return *this;
}
template<typename T>
Array2D<T>& Array2D<T>::operator= (const T other) {
for(int i=0;i<nrows_*ncols_;i++)
data_[i] = other;
return *this;
}
template<typename T>
int Array2D<T>::nRows() const {
return nrows_;
}
template<typename T>
int Array2D<T>::nCols() const {
return ncols_;
}
template<typename T>
bool Array2D<T>::has_member(T val) const {
int i;
bool retval = false;
for(i=0;i<nrows_*ncols_;i++)
if (val == data_[i])
retval = true;
return(retval);
}
template<typename T>
void Array2D<T>::write_restart(FILE *f) const {
fwrite(&nrows_,sizeof(int),1,f);
fwrite(&ncols_,sizeof(int),1,f);
if (nrows_*ncols_ > 0)
fwrite(data_,sizeof(T),nrows_*ncols_,f);
}
template<typename T>
Array2D<T>::~Array2D() {
if (data_ != NULL)
delete[] data_;
}
template<typename T>
void Array2D<T>::print(std::string name) const {
std::cout << "------- Begin "<<name<<" -----------------\n";
if (data_ != NULL) {
for(int col=0;col<ncols_;col++) {
for(int row=0;row<nrows_;row++) {
std::cout << data_[col*nrows_ + row] << " ";
}
std::cout << "\n";
}
}
std::cout << "\n------- End "<<name<<" -------------------\n";
}
} // end namespace
#endif // Array2D.h

View File

@ -0,0 +1,223 @@
// ATC headers
#include "AtomToMoleculeTransfer.h"
#include "ATC_Method.h"
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class SmallMoleculeCentroid
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
SmallMoleculeCentroid::SmallMoleculeCentroid(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions) : AtomToSmallMoleculeTransfer<double>(atc, source, smallMoleculeSet), atomPositions_(atomPositions)
{
atomPositions_->register_dependence(this);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
SmallMoleculeCentroid::~SmallMoleculeCentroid()
{
atomPositions_->remove_dependence(this);
}
//--------------------------------------------------------
// Quantity
//--------------------------------------------------------
void SmallMoleculeCentroid::reset_quantity() const
{
const LammpsInterface * lammps(atc_->lammps_interface());
const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i \x_i
double xi[3], xj[3], xjImage[3];
const DENS_MAT & atomPosMatrix(atomPositions_->quantity());
int nLocalMol = smallMoleculeSet_->local_molecule_count();
quantity_.reset(nLocalMol,atc_->nsd());
for (int i = 0; i < nLocalMol; i++) {
const set<int> & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i);
set<int>::const_iterator atomsLocalMolID;
double totalSourceMol = 0.0; // for total source
for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end(); atomsLocalMolID++) {
totalSourceMol += sourceMatrix(*atomsLocalMolID,0);
} // compute total source
atomsLocalMolID = atomsLocalMolArray.begin();
for (int j = 0; j < atc_->nsd(); j++) {
xi[j] = atomPosMatrix(*atomsLocalMolID,j);
}
for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end(); atomsLocalMolID++) {
for (int j = 0; j < atc_->nsd(); j++){
xj[j] = atomPosMatrix(*atomsLocalMolID,j);
}
lammps->closest_image(xi,xj,xjImage);
for (int j = 0; j < atc_->nsd() ; j++) {
quantity_(i,j) += sourceMatrix(*atomsLocalMolID,0) * xjImage[j]/totalSourceMol;
}
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class SmallMoleculeDipoleMoment
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
SmallMoleculeDipoleMoment::SmallMoleculeDipoleMoment(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions, SmallMoleculeCentroid * centroid) : SmallMoleculeCentroid(atc, source, smallMoleculeSet, atomPositions), centroid_(centroid) //check here
{
centroid_->register_dependence(this);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
SmallMoleculeDipoleMoment::~SmallMoleculeDipoleMoment()
{
centroid_->remove_dependence(this);
}
//--------------------------------------------------------
// Quantity
//--------------------------------------------------------
void SmallMoleculeDipoleMoment::reset_quantity() const
{
const LammpsInterface * lammps(atc_->lammps_interface());
const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i
const DENS_MAT & atomPosMatrix(atomPositions_->quantity());
int nLocalMol = smallMoleculeSet_->local_molecule_count();
int nsd = atc_->nsd();
quantity_.reset(nLocalMol,nsd);
double dx[3];
//call the SmallMoleculeCentroid here to find Centroid ....
const DENS_MAT & centroidMolMatrix(centroid_->quantity());
for (int i = 0; i < nLocalMol; i++) {
const set<int> & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i);
set<int>::const_iterator atomsLocalMolID;;
for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end();atomsLocalMolID++) {
for (int j = 0; j < nsd; j++) {
dx[j] = atomPosMatrix(*atomsLocalMolID,j) - centroidMolMatrix(i,j);
}
lammps->minimum_image(dx[0], dx[1], dx[2]);
for(int j = 0; j < nsd; j++) {
quantity_(i,j) += sourceMatrix(*atomsLocalMolID,0) * dx[j];
}
}
}
}
//--------------------------------------------------------
// Class SmallMoleculeQuadrupoleMoment
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
SmallMoleculeQuadrupoleMoment::SmallMoleculeQuadrupoleMoment(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions, SmallMoleculeCentroid * centroid) : SmallMoleculeCentroid(atc, source, smallMoleculeSet, atomPositions), centroid_(centroid)
{
centroid_->register_dependence(this);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
SmallMoleculeQuadrupoleMoment::~SmallMoleculeQuadrupoleMoment()
{
centroid_->remove_dependence(this);
}
//--------------------------------------------------------
// Quantity
//--------------------------------------------------------
void SmallMoleculeQuadrupoleMoment::reset_quantity() const
{
const LammpsInterface * lammps(atc_->lammps_interface());
const DENS_MAT & sourceMatrix(source_->quantity()); // source is always a scalar quantity here \sum m_i
const DENS_MAT & atomPosMatrix(atomPositions_->quantity());
int nLocalMol = smallMoleculeSet_->local_molecule_count();
int nsd = atc_->nsd();
quantity_.reset(nLocalMol,nsd);
double dx[3];
//call the SmallMoleculeCentroid here to find Centroid ....
const DENS_MAT & centroidMolMatrix(centroid_->quantity());
for (int i = 0; i < nLocalMol; i++) {
const set<int> & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i);
set<int>::const_iterator atomsLocalMolID;;
for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID != atomsLocalMolArray.end();atomsLocalMolID++) {
for (int j = 0; j < nsd; j++) {
dx[j] = atomPosMatrix(*atomsLocalMolID,j) - centroidMolMatrix(i,j);
}
lammps->minimum_image(dx[0], dx[1], dx[2]);
for(int j = 0; j < nsd; j++) {
quantity_(i,j) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j] * dx[2];
/* quantity_(i,3*j) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[0];
quantity_(i,3*j+1) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[1];
quantity_(i,3*j+2) += 0.5*sourceMatrix(*atomsLocalMolID,0) * dx[j]*dx[2]; */
}
}
}
}
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
MotfShapeFunctionRestriction::MotfShapeFunctionRestriction(PerMoleculeQuantity<double> * source,
SPAR_MAN * shapeFunction) :
MatToMatTransfer<double>(source),
shapeFunction_(shapeFunction)
{
shapeFunction_->register_dependence(this);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
MotfShapeFunctionRestriction::~MotfShapeFunctionRestriction()
{
shapeFunction_->remove_dependence(this);
}
//--------------------------------------------------------
// reset_quantity
//--------------------------------------------------------
void MotfShapeFunctionRestriction::reset_quantity() const
{
// computes nodeData = N*atomData where N are the shape functions
const DENS_MAT & sourceMatrix(source_->quantity());
// reallocate memory only if sizing has changed
const SPAR_MAT & shapeFunctionMatrix(shapeFunction_->quantity());
quantity_.resize(shapeFunctionMatrix.nCols(),sourceMatrix.nCols());
local_restriction(sourceMatrix,shapeFunctionMatrix);
// communicate for total restriction
int count = quantity_.nRows()*quantity_.nCols();
lammpsInterface_->allsum(_workspace_.ptr(),quantity_.ptr(),count);
}
//--------------------------------------------------------
// local_restriction
//--------------------------------------------------------
void MotfShapeFunctionRestriction::local_restriction(const DENS_MAT & sourceMatrix,
const SPAR_MAT & shapeFunctionMatrix) const
{
if (sourceMatrix.nRows() > 0)
_workspace_ = shapeFunctionMatrix.transMat(sourceMatrix);
else
_workspace_.reset(quantity_.nRows(),quantity_.nCols());
}
} // end namespace

View File

@ -0,0 +1,239 @@
// A class for defining transfer operations molecular centers of mass (centroid), dipole moments, quadrupole moments
#ifndef ATOM_TO_MOLECULE_TRANSFER_H
#define ATOM_TO_MOLECULE_TRANSFER_H
// ATC_Method headers
#include "TransferOperator.h"
#include "MoleculeSet.h"
#include "PaqAtcUtility.h"
using namespace std;
namespace ATC {
// forward declarations
class ATC_Method;
/**
* @class PerMoleculeQuantity
*
*/
template <typename T>
class PerMoleculeQuantity : public DenseMatrixTransfer<T> {
public:
PerMoleculeQuantity(ATC_Method * atc):DenseMatrixTransfer<T>(), atc_(atc) {};
virtual ~PerMoleculeQuantity() {};
protected:
/** utility object for atc information */
ATC_Method * atc_;
private:
//do not define
PerMoleculeQuantity();
};
/**
* @class AtomtoSmallMoleculeTransfer
* @brief Class for defining objects to transfer total mass or charge of a molecule
*/
template <typename T>
class AtomToSmallMoleculeTransfer : public PerMoleculeQuantity<T> {
public:
//constructor
// had to define all functions in header, not sure why (JAT, 12/14/11)
AtomToSmallMoleculeTransfer(ATC_Method * atc, PerAtomQuantity<T> * source, SmallMoleculeSet * smallMoleculeSet) :
PerMoleculeQuantity<T>(atc),
source_(source),
smallMoleculeSet_(smallMoleculeSet)
{
source_->register_dependence(this);
smallMoleculeSet_->register_dependence(this);
};
//destructor
virtual ~AtomToSmallMoleculeTransfer()
{
source_->remove_dependence(this);
smallMoleculeSet_->remove_dependence(this);
};
// apply transfer operator
void reset_quantity() const
{
const DenseMatrix<T> & sourceMatrix(source_->quantity());
int nLocalMol = smallMoleculeSet_->local_molecule_count();
(this->quantity_).reset(nLocalMol,sourceMatrix.nCols());
for (int i = 0; i < nLocalMol ; i++) {
const set<int> & atomsLocalMolArray = smallMoleculeSet_->atoms_by_local_molecule(i);
set<int>::const_iterator atomsLocalMolID;
for (atomsLocalMolID = atomsLocalMolArray.begin(); atomsLocalMolID!= atomsLocalMolArray.end();atomsLocalMolID++) {
for (int j = 0; j < sourceMatrix.nCols(); j++){
(this->quantity_)(i,j) += sourceMatrix(*atomsLocalMolID,j);
}
}
}
};
protected:
// pointer to source atomic quantity data
PerAtomQuantity<T> * source_;
// pointer to molecule data
SmallMoleculeSet * smallMoleculeSet_;
private:
// do not define
AtomToSmallMoleculeTransfer();
};
/**
* @class SmallMoleculeCentroid
* @brief Class for defining objects to transfer molecular centroid (center of mass)
*/
class SmallMoleculeCentroid : public AtomToSmallMoleculeTransfer<double> {
public:
//constructor
SmallMoleculeCentroid(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions);
//destructor
virtual ~SmallMoleculeCentroid();
// apply transfer operator
virtual void reset_quantity() const;
protected:
// pointer to source atomic quantity date : positions of atoms in a molecule
PerAtomQuantity<double> * atomPositions_;
private:
//do not define
SmallMoleculeCentroid();
};
/**
* @class SmallMoleculeDipoleMoment
* @brief Class for defining objects to transfer molecular dipole moments
*/
class SmallMoleculeDipoleMoment : public SmallMoleculeCentroid {
public:
//constructor
SmallMoleculeDipoleMoment(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions, SmallMoleculeCentroid * centroid);
//destructor
virtual ~SmallMoleculeDipoleMoment();
// apply transfer operator
virtual void reset_quantity() const;
protected:
//pointer to the centroid data
SmallMoleculeCentroid * centroid_;
private:
//do not define
SmallMoleculeDipoleMoment();
};
/**
* @class AtomToFeTransfer
* @brief Class for defining objects to transfer molecular quadrupole moments
*/
class SmallMoleculeQuadrupoleMoment : public SmallMoleculeCentroid {
public:
//constructor
SmallMoleculeQuadrupoleMoment(ATC_Method * atc, PerAtomQuantity<double> * source, SmallMoleculeSet * smallMoleculeSet, PerAtomQuantity<double> * atomPositions, SmallMoleculeCentroid * centroid);
//destructor
virtual ~SmallMoleculeQuadrupoleMoment();
//apply transfer operator
virtual void reset_quantity() const;
protected:
//pointer to the centroid data
SmallMoleculeCentroid * centroid_;
private:
//do not define
SmallMoleculeQuadrupoleMoment();
};
/**
* @class MotfShapeFunctionRestriction
* @brief Class for defining objects that transfer atomistic quantities to FE using shape functions
* (implements restrict_volumetric_quantity)
*/
class MotfShapeFunctionRestriction : public MatToMatTransfer<double> {
public:
// constructor
MotfShapeFunctionRestriction(PerMoleculeQuantity<double> * source,
SPAR_MAN * shapeFunction);
// destructor
virtual ~MotfShapeFunctionRestriction();
/** apply transfer operator */
virtual void reset_quantity() const;
protected:
/** reference to shape function matrix */
SPAR_MAN * shapeFunction_;
/** persistant workspace */
mutable DENS_MAT _workspace_;
/** applies restriction operation on this processor */
virtual void local_restriction(const DENS_MAT & sourceMatrix,
const SPAR_MAT & shapeFunctionMatrix) const;
private:
// do not define
MotfShapeFunctionRestriction();
};
}
#endif

976
lib/atc/AtomicRegulator.cpp Normal file
View File

@ -0,0 +1,976 @@
// ATC Headers
#include "AtomicRegulator.h"
#include "ATC_Error.h"
#include "ATC_Coupling.h"
#include "PrescribedDataManager.h"
#include "TimeIntegrator.h"
#include "LinearSolver.h"
namespace ATC {
// only one regulator method at time, i.e. fixed & flux, thermo & elastic
// regulator manages lambda variables, creates new ones when requested with dimensions and zero ics (map of tag to lambda)
// regulator keeps track of which lambda are being used, unused lambdas deleted (map of tag to bool), all tags set to unused on start of initialization
// method requests needed lambda from regulator
// method sets up all needed linear solvers, null linear solver does nothing
// regulator adds nodes to fixed or fluxed lists it owns, based on localization and type
// method gets lists of fixed nodes and fluxed nodes
// method lumps fluxed lambdas and truncates fixed lambdas based on single localized bool in regulator
// inherited methods should be fixed, fluxed, combined
//--------------------------------------------------------
//--------------------------------------------------------
// Class AtomicRegulator
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
AtomicRegulator::AtomicRegulator(ATC_Coupling * atc,
const string & regulatorPrefix) :
atc_(atc),
howOften_(1),
needReset_(true),
maxIterations_(myMaxIterations),
tolerance_(myTolerance),
regulatorTarget_(NONE),
couplingMode_(UNCOUPLED),
nNodes_(0),
nsd_(atc_->nsd()),
nLocal_(0),
useLocalizedLambda_(false),
useLumpedLambda_(false),
timeFilter_(NULL),
regulatorMethod_(NULL),
boundaryIntegrationType_(NO_QUADRATURE),
regulatorPrefix_(regulatorPrefix)
{
applyInDirection_.resize(atc_->nsd(),true);
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
AtomicRegulator::~AtomicRegulator()
{
delete_method();
set_all_data_to_unused();
delete_unused_data();
}
//--------------------------------------------------------
// delete_method:
// deletes the method
//--------------------------------------------------------
void AtomicRegulator::delete_method()
{
if (regulatorMethod_)
delete regulatorMethod_;
}
//--------------------------------------------------------
// delete_unused_data:
// deletes all data that is currently not in use
//--------------------------------------------------------
void AtomicRegulator::delete_unused_data()
{
map<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 <physics_type>
\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 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"));
if (!nodeToOverlapMap_) {
nodeToOverlapMap_ = new NodeToSubset(atc_,regulatedNodes_);
interscaleManager.add_dense_matrix_int(nodeToOverlapMap_,
regulatorPrefix_+"NodeToOverlapMap");
}
overlapToNodeMap_ = static_cast<SubsetToNode * >(interscaleManager.dense_matrix_int("OverlapToNodeMap"));
if (!overlapToNodeMap_) {
overlapToNodeMap_ = new SubsetToNode(nodeToOverlapMap_);
interscaleManager.add_dense_matrix_int(overlapToNodeMap_,
regulatorPrefix_+"OverlapToNodeMap");
}
}
//--------------------------------------------------------
// construct_transfers
// - create all the needed transfer operators, in this
// case weights for the lambda matrix
//--------------------------------------------------------
void RegulatorShapeFunction::construct_transfers()
{
this->set_weights(); // construct specific weighting matrix transfer
// specialized quantities for boundary flux integration if the lambda atom map exists
if (lambdaAtomMap_ && (atomicRegulator_->boundary_integration_type() == FE_INTERPOLATION)) {
InterscaleManager & interscaleManager(atc_->interscale_manager());
// atomic weights
PerAtomDiagonalMatrix<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
shpFcnDerivs_ = new RowMappedSparseMatrixVector(atc_,
interscaleManager.vector_sparse_matrix("InterpolantGradient"),
lambdaAtomMap_);
interscaleManager.add_vector_sparse_matrix(shpFcnDerivs_,
regulatorPrefix_+"RegulatorShapeFunctionGradient");
}
}
//--------------------------------------------------------
// initialize
// - pre-run work, in this cases constructs the linear
// solver
//--------------------------------------------------------
void RegulatorShapeFunction::initialize()
{
if (!shapeFunctionMatrix_) {
throw ATC_Error("RegulatorShapeFunction::initialize - shapeFunctionMatrix_ must be created before the initialize phase");
}
if (matrixSolver_)
delete matrixSolver_;
if (linearSolverType_ == AtomicRegulator::RSL_SOLVE) {
matrixSolver_ = new LambdaMatrixSolverLumped(matrixTemplate_,
shapeFunctionMatrix_,
maxIterations_,
tolerance_,
applicationNodes_,
nodeToOverlapMap_);
}
else if (linearSolverType_ == AtomicRegulator::CG_SOLVE) {
matrixSolver_ = new LambdaMatrixSolverCg(matrixTemplate_,
shapeFunctionMatrix_,
maxIterations_,
tolerance_);
}
else {
throw ATC_Error("RegulatorShapeFunction::initialize - unsupported solver type");
}
compute_sparsity();
}
//--------------------------------------------------------
// compute_sparsity
// - creates sparsity template
//--------------------------------------------------------
void RegulatorShapeFunction::compute_sparsity(void)
{
// first get local pattern from N N^T
int nNodeOverlap = nodeToOverlapMap_->size();
DENS_MAT tmpLocal(nNodeOverlap,nNodeOverlap);
DENS_MAT tmp(nNodeOverlap,nNodeOverlap);
const SPAR_MAT & myShapeFunctionMatrix(shapeFunctionMatrix_->quantity());
if (myShapeFunctionMatrix.nRows() > 0) {
tmpLocal = myShapeFunctionMatrix.transMat(myShapeFunctionMatrix);
}
// second accumulate total pattern across processors
LammpsInterface::instance()->allsum(tmpLocal.ptr(), tmp.ptr(), tmp.size());
// third extract non-zero entries & construct sparse template
SPAR_MAT & myMatrixTemplate(matrixTemplate_.set_quantity());
myMatrixTemplate.reset(nNodeOverlap,nNodeOverlap);
for (int i = 0; i < nNodeOverlap; i++) {
for (int j = 0; j < nNodeOverlap; j++) {
if (abs(tmp(i,j))>0) {
myMatrixTemplate.add(i,j,0.);
}
}
}
myMatrixTemplate.compress();
}
//--------------------------------------------------------
// solve_for_lambda
// solves matrix equation for lambda using given rhs
//--------------------------------------------------------
void RegulatorShapeFunction::solve_for_lambda(const DENS_MAT & rhs,
DENS_MAT & lambda)
{
// assemble N^T W N with appropriate weighting matrix
DIAG_MAT weights;
if (shapeFunctionMatrix_->nRows() > 0) {
weights.reset(weights_->quantity());
}
matrixSolver_->assemble_matrix(weights);
// solve on overlap nodes
int nNodeOverlap = nodeToOverlapMap_->size();
DENS_MAT rhsOverlap(nNodeOverlap,rhs.nCols());
map_unique_to_overlap(rhs, rhsOverlap);
DENS_MAT lambdaOverlap(nNodeOverlap,lambda.nCols());
for (int i = 0; i < rhs.nCols(); i++) {
CLON_VEC tempLambda(lambdaOverlap,CLONE_COL,i);
if (atomicRegulator_->apply_in_direction(i)) {
CLON_VEC tempRHS(rhsOverlap,CLONE_COL,i);
matrixSolver_->execute(tempRHS,tempLambda);
}
else {
tempLambda = 0.;
}
}
// map solution back to all nodes
map_overlap_to_unique(lambdaOverlap,lambda);
}
//--------------------------------------------------------
// reset_nlocal:
// resets data dependent on local atom count
//--------------------------------------------------------
void RegulatorShapeFunction::reset_nlocal()
{
RegulatorMethod::reset_nlocal();
nLocal_ = atomicRegulator_->nlocal();
//compute_sparsity();
}
//--------------------------------------------------------
// reset_atom_materials:
// resets the localized atom to material map
//--------------------------------------------------------
void RegulatorShapeFunction::reset_atom_materials(const Array<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"));
if (!regulatedNodes_) {
if (!(atomicRegulator_->use_localized_lambda())) {
regulatedNodes_ = new RegulatedNodes(atc_);
}
else {
regulatedNodes_ = new AllRegulatedNodes(atc_);
}
interscaleManager.add_set_int(regulatedNodes_,
regulatorPrefix_+"RegulatedNodes");
}
// application and regulated are same, unless specified
applicationNodes_ = regulatedNodes_;
// boundary and regulated nodes are same, unless specified
boundaryNodes_ = regulatedNodes_;
// special set of boundary elements
if (atomicRegulator_->use_localized_lambda()) {
elementMask_ = new ElementMaskNodeSet(atc_,boundaryNodes_);
interscaleManager.add_dense_matrix_bool(elementMask_,
regulatorPrefix_+"BoundaryElementMask");
}
}
//--------------------------------------------------------
// compute_boundary_flux
// default computation of boundary flux based on
// finite
//--------------------------------------------------------
void RegulatorShapeFunction::compute_boundary_flux(FIELDS & fields)
{
atc_->compute_boundary_flux(fieldMask_,
fields,
boundaryFlux_,
atomMaterialGroups_,
shpFcnDerivs_,
shpFcn_,
atomicWeights_,
elementMask_,
boundaryNodes_);
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class LambdaMatrixSolver
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to necessary data
//--------------------------------------------------------
LambdaMatrixSolver::LambdaMatrixSolver(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance) :
matrixTemplate_(matrixTemplate),
shapeFunctionMatrix_(shapeFunctionMatrix),
maxIterations_(maxIterations),
tolerance_(tolerance)
{
// do nothing
}
//--------------------------------------------------------
// assemble_matrix
// Assemble the matrix using the shape function
// matrices and weights. This improves efficiency
// when multiple solves or iterations are required.
//--------------------------------------------------------
void LambdaMatrixSolver::assemble_matrix(DIAG_MAT & weights)
{
// form matrix : sum_a N_Ia * W_a * N_Ja
SPAR_MAT lambdaMatrixLocal(matrixTemplate_.quantity());
if (weights.nRows()>0)
lambdaMatrixLocal.weighted_least_squares(shapeFunctionMatrix_->quantity(),weights);
// swap contributions
lambdaMatrix_ = matrixTemplate_.quantity();
LammpsInterface::instance()->allsum(lambdaMatrixLocal.ptr(),
lambdaMatrix_.ptr(), lambdaMatrix_.size());
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class LambdaMatrixSolverLumped
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab references to necessary data
//--------------------------------------------------------
LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance, const RegulatedNodes * applicationNodes, const NodeToSubset * nodeToOverlapMap) :
LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance),
applicationNodes_(applicationNodes),
nodeToOverlapMap_(nodeToOverlapMap)
{
// do nothing
}
//--------------------------------------------------------
// assemble_matrix
// Assemble the matrix using the shape function
// matrices and weights. This improves efficiency
// when multiple solves or iterations are required.
//--------------------------------------------------------
void LambdaMatrixSolverLumped::assemble_matrix(DIAG_MAT & weights)
{
LambdaMatrixSolver::assemble_matrix(weights);
lumpedMatrix_ = lambdaMatrix_.row_sum_lump();
}
void LambdaMatrixSolverLumped::execute(VECTOR & rhs, VECTOR & lambda)
{
// solve lumped equation
const set<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);
}
};

625
lib/atc/AtomicRegulator.h Normal file
View File

@ -0,0 +1,625 @@
/** Atomic Regulator : a base class class for atom-continuum control */
#ifndef ATOMICREGULATOR_H
#define ATOMICREGULATOR_H
// ATC headers
#include "ATC_TypeDefs.h"
// other headers
#include <map>
#include <set>
#include <vector>
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 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 string tag, int nCols);
/** can externally set regulator dynamic contributions */
virtual void reset_lambda_contribution(const DENS_MAT & target, const FieldName field=NUM_TOTAL_FIELDS) {};
/** returns a const pointer to the DENS_MAN associated with the tag, or NULL */
const DENS_MAN * regulator_data(const string tag) const;
/** return the maximum number of iterations */
int max_iterations() {return maxIterations_;};
/** return the solver tolerance */
double tolerance() {return tolerance_;};
/** access for ATC transfer */
ATC_Coupling * atc_transfer() {return atc_;};
/** access for time filter */
TimeFilter * time_filter() {return timeFilter_;};
/** access for number of nodes */
int num_nodes() {return nNodes_;};
/** access for number of spatial dimensions */
int nsd() {return nsd_;};
/** access for number of local atoms */
int nlocal() {return nLocal_;};
/** access for boundary integration methods */
BoundaryIntegrationType boundary_integration_type()
{return boundaryIntegrationType_;};
/** access for boundary face sets */
const set< pair<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 string & regulator_prefix() const {return regulatorPrefix_;};
protected:
// methods
/** deletes the current regulator method */
void delete_method();
/** deletes all unused data */
void delete_unused_data();
/** sets all data to be unused */
void set_all_data_to_unused();
/** sets all data to be used */
void set_all_data_to_used();
// data
/** point to atc_transfer object */
ATC_Coupling * atc_;
/** how often in number of time steps regulator is applied */
int howOften_;
// reset/reinitialize flags
/** flag to reset data */
bool needReset_;
/** reinitialize method */
void reset_method();
// regulator data
/** container for all data, string is tag, bool is false if currently in use */
map<string, 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 */
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 set< pair<int,int> > * boundaryFaceSet_;
/** prefix string for registering data */
const string regulatorPrefix_;
private:
// DO NOT define this
AtomicRegulator();
};
/**
* @class RegulatorMethod
* @brief Base class for implementation of control algorithms
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class RegulatorMethod
//--------------------------------------------------------
//--------------------------------------------------------
class RegulatorMethod {
public:
RegulatorMethod(AtomicRegulator * atomicRegulator,
const string & regulatorPrefix = "");
virtual ~RegulatorMethod(){};
/** instantiate all needed data */
virtual void construct_transfers(){};
/** pre-"run" initialization */
virtual void initialize(){};
/** reset number of local atoms, as well as atomic data */
virtual void reset_nlocal(){};
/** set up atom to material identification */
virtual void reset_atom_materials(const Array<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 string regulatorPrefix_;
/** mapping for atom materials for atomic FE quadrature */
Array<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 string & regulatorPrefix = "");
virtual ~RegulatorShapeFunction();
/** instantiate all needed data */
virtual void construct_transfers();
/** pre-"run" initialization */
virtual void initialize();
/** reset number of local atoms, as well as atomic data */
virtual void reset_nlocal();
/** set up atom to material identification */
virtual void reset_atom_materials(const Array<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);
/** 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_;
/** 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_;
/** set of nodes on which lambda is non-zero */
RegulatedNodes * applicationNodes_;
/** set of nodes needed for localized boundary quadrature */
RegulatedNodes * boundaryNodes_;
/** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */
NodeToSubset * nodeToOverlapMap_;
/** mapping from overlap nodes to unique nodes */
SubsetToNode * overlapToNodeMap_;
/** shape function matrix for boundary atoms */
SPAR_MAN * shpFcn_;
/** atomic weights for boundary atoms */
DIAG_MAN * atomicWeights_;
/** element mask for boundary elements corresponding to nodeToOverlapMap_ */
ElementMaskNodeSet * elementMask_;
/** maps atoms from atc indexing to regulator indexing */
LargeToSmallAtomMap * lambdaAtomMap_;
/** weight per-atom transfer */
PerAtomQuantity<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);
virtual ~LambdaMatrixSolverLumped(){};
/** assemble the matrix */
virtual void assemble_matrix(DIAG_MAT & weights);
/** execute the solver */
virtual void execute(VECTOR & rhs, VECTOR & lambda);
protected:
/** lumped version of the matrix governing lamda */
DIAG_MAT lumpedMatrix_;
/** set of regulated nodes */
const RegulatedNodes * applicationNodes_;
/** mapping from all nodes to overlap nodes: -1 is no overlap, otherwise entry is overlap index */
const NodeToSubset * nodeToOverlapMap_;
private:
// DO NOT define this
LambdaMatrixSolverLumped();
};
//--------------------------------------------------------
//--------------------------------------------------------
// Class LambdaMatrixSolverCg
//--------------------------------------------------------
//--------------------------------------------------------
class LambdaMatrixSolverCg : public LambdaMatrixSolver {
public:
LambdaMatrixSolverCg(SPAR_MAN & matrixTemplate, SPAR_MAN * shapeFunctionMatrix, int maxIterations, double tolerance);
virtual ~LambdaMatrixSolverCg(){};
/** execute the solver */
virtual void execute(VECTOR & rhs, VECTOR & lambda);
protected:
private:
// DO NOT define this
LambdaMatrixSolverCg();
};
};
#endif

32
lib/atc/BodyForce.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "BodyForce.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
using ATC_Utility::str2int;
namespace ATC {
BodyForceViscous::BodyForceViscous(
fstream &fileId, map<string,double> & parameters)
: BodyForce(), gamma_(0)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "gamma") {
gamma_ = value;
parameters["gamma"] = gamma_;
}
}
}
}

72
lib/atc/BodyForce.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef BODY_FORCE_H
#define BODY_FORCE_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "ATC_TypeDefs.h"
#include "Function.h"
namespace ATC {
/**
* @class BodyForce
* @brief Base class for models of body forces in the momentum eqn
*/
class BodyForce
{
public:
BodyForce() {};
virtual ~BodyForce() {};
virtual bool body_force(const FIELD_MATS &fields,
DENS_MAT &flux) const { return false; };
};
/**
* @class BodyForceViscous
* @brief viscous body forces
*/
class BodyForceViscous : public BodyForce
{
public:
BodyForceViscous(fstream &matfile,map<string,double> & parameters);
virtual ~BodyForceViscous() {};
virtual bool body_force(const FIELD_MATS &fields,
DENS_MAT &flux) const
{
FIELD_MATS::const_iterator v_field = fields.find(VELOCITY);
const DENS_MAT & v = v_field->second;
flux = -gamma_*v;
return true;
}
protected:
double gamma_;
};
/**
* @class BodyForceElectricField
* @brief electric field body forces
*/
class BodyForceElectricField : public BodyForce
{
public:
BodyForceElectricField(fstream &matfile,map<string,double> & parameters)
{ throw ATC_Error("unimplemented due to issues with accessing electric field"); }
virtual ~BodyForceElectricField() {};
virtual bool body_force(const FIELD_MATS &fields,
DENS_MAT &flux) const
{
FIELD_MATS::const_iterator v_field = fields.find(VELOCITY);
const DENS_MAT & v = v_field->second;
int nNodes = v.nRows();
flux.reset(nNodes,1);
return true;
}
};
}
#endif

202
lib/atc/CBLattice.cpp Normal file
View File

@ -0,0 +1,202 @@
#include "CBLattice.h"
#include "CbPotential.h"
namespace ATC {
// removes any overlapping atoms (avoid calling because it scales n^2.)
INDEX AtomCluster::remove_overlap()
{
INDEX removed_count = 0;
std::vector<double>::iterator r(cur_bond_len_.begin());
std::vector<DENS_VEC>::iterator R(ref_coords_.begin()), Rp;
const double TOL = 1.0e-6 * R->dot(*R);
for (; R!=ref_coords_.end(); R++, r++) {
for (Rp=R+1; Rp!=ref_coords_.end(); Rp++) {
if (sum_difference_squared(*Rp, *R) < TOL) {
ref_coords_.erase(R--);
cur_bond_len_.erase(r--);
++removed_count;
break;
}
}
}
return removed_count;
}
//=========================================================================
// writes cluster to 3 column data format
//=========================================================================
void AtomCluster::write_to_dat(std::string path, bool current_config)
{
const int npts = int(ref_coords_.size());
if (path.substr(path.size()-5,4) != ".dat") path += ".dat";
fstream fid(path.c_str(), std::ios::out);
for (int i=0; i<npts; i++) {
DENS_VEC x (current_config ? r(i):R(i));
for (INDEX j=0; j<x.size(); j++) fid << x(j) << " ";
fid << " " << x.norm() << "\n";
}
}
//=========================================================================
// writes cluster to vtk format, (in either reference or current config)
//=========================================================================
void AtomCluster::write_to_vtk(std::string path, bool current_config)
{
const int npts = int(ref_coords_.size());
if (path.substr(path.size()-5,4) != ".vtk") path += ".vtk";
fstream fid(path.c_str(), std::ios::out);
// write header
fid << "# vtk DataFile Version 2.0\nWritten from FE-LAMMPS\n";
fid << "ASCII\nDATASET UNSTRUCTURED_GRID\n";
fid << "POINTS " << npts << " float\n";
for (int i=0; i<npts; i++) {
DENS_VEC x (current_config ? r(i):R(i));
for (INDEX j=0; j<x.size(); j++) fid << x(j) << " ";
fid << ((i+1)%3==0 ? "\n" : " ");
}
fid << "\nCELLS "<<npts<<" "<<2*npts<<"\n";
for (int i=0; i<npts; i++) fid << "1" << " " << i << "\n";
fid << "CELL_TYPES " << npts << "\n";
for (int i=0; i<npts; i++) fid << "1" << "\n";
}
//===========================================================================
// constructor
// @param N 3x3 DenseMatrix with each column as a base vector
// @param B 3xn DenseMatrix with each column being a basis vector
// @param R vector of 3D bond Vectors to representative atom in ref config
// @param r vector of bond lengths to representative atom in current config
// @param RC cutoff radius of bond potential
//===========================================================================
CBLattice::CBLattice(const MATRIX &N, const MATRIX &B)
: n_(N), b_(B), N_(N), B_(B)
{
// builds the default queue
for (int a=-1; a<2; a++)
for (int b=-1; b<2; b++)
for (int c=-1; c<2; c++)
if ( a!=0 || b!=0 || c!=0) queue0_.push(hash(a,b,c));
}
//=============================================================================
// writes out default lattice parameters
//=============================================================================
std::ostream & operator<<(std::ostream& o, const CBLattice& lattice)
{
o<<"cutoff radius = "<<sqrt(lattice.RC2_)<<"\n";
lattice.N_.print("Reference base vectors");
lattice.B_.print("Reference basis vectors");
return o;
}
//===========================================================================
// Constructs the virtual atom cluster of neighbors within cutoff
// @param F the deformation gradient tensor
//===========================================================================
void CBLattice::atom_cluster(const MATRIX &F, double cutoff, AtomCluster &v)
{
RC2_ = cutoff*cutoff;
// compute new base and basis vectors
v.clear();
v.F_ = F;
n_ = F*N_;
b_ = F*B_;
// add basis from the center cell (not including representative atom)
for (int i=1; i<B_.nCols(); i++) {
v.cur_bond_len_.push_back(b_.col_norm(i));
v.ref_coords_.push_back(column(B_,i));
}
_FindAtomsInCutoff(v);
}
//=============================================================================
// Computes forces on central atom, with atom I displaced by u.
//=============================================================================
DENS_VEC AtomCluster::perturbed_force(const CbPotential *p, int I, DENS_VEC *u) const
{
DENS_VEC f(3);
for (INDEX i=0; i<size(); i++) {
DENS_VEC ri = r(i);
if (u && i+1==I) ri += *u;
if (u && I==0) ri -= *u;
const double d = ri.norm();
f.add_scaled(ri, -p->phi_r(d)/d);
}
return f;
}
//=============================================================================
// Computes the force constant matrix between atoms I and 0.
//=============================================================================
DENS_MAT AtomCluster::force_constants(INDEX I, const CbPotential *p) const
{
DENS_MAT D(3,3);
for (INDEX i=0; i<3; i++) {
DENS_VEC du(3);
du(i) = 1.0e-6; // take central difference
row(D,i) = perturbed_force(p, I, &du);
du(i) = -du(i);
row(D,i) -= perturbed_force(p, I, &du);
}
D *= 0.5e6;
return D;
}
//=============================================================================
// performs an iterative search for all neighbors within cutoff
//=============================================================================
void CBLattice::_FindAtomsInCutoff(AtomCluster &v)
{
static const int dir[2] = {-1, 1};
int a, b, c, abc;
std::stack<int> queue(queue0_);
std::set<int> done;
// search in each direction
while (!queue.empty()) {
abc = queue.top();
queue.pop();
if (done.find(abc) == done.end()) { // value not in set
unhash(abc,a,b,c); // convert abc to a,b,c
if (_CheckUnitCell(a, b, c, v)) {
done.insert(abc);
// add direct 'outward' neighbors to queue
queue.push(hash(a+dir[a>0], b, c));
queue.push(hash(a, b+dir[b>0], c));
queue.push(hash(a, b, c+dir[c>0]));
}
}
}
}
//===========================================================================
// Computes \f$r^2 = \Vert a n_1 + b n_2 +c n_3 + b_d \Vert^2 \f$
// and adds atom (a,b,c,d) if \f$r^2 < r_{cutoff}^2 \f$
// @param a cell x-index
// @param b cell y-index
// @param c cell z-index
//===========================================================================
bool CBLattice::_CheckUnitCell(char a, char b, char c, AtomCluster &v)
{
const int nsd = n_.nRows(); // number of spatial dimensions
const double A=double(a), B=double(b), C=double(c);
bool found=false;
DENS_VEC r0(nsd,false), R0(nsd,false), Rd(nsd,false); // don't initialize
for (int i=0; i<nsd; i++) { // precompute locations of cell
R0(i) = A*N_(0,i) + B*N_(1,i) + C*N_(2,i); // reference
}
r0 = v.F_*R0; // deformed
for (int d=0; d<b_.nCols(); d++) {
double ri = r0(0) + b_(0,d);
double r2 = ri*ri;
for (int i=1; i<nsd; i++) {
ri = r0(i) + b_(i,d);
r2 += ri*ri;
}
if (r2 <= RC2_) {
v.ref_coords_.push_back(R0);
v.ref_coords_.back() += column(B_,d); // position is R0 + B_[d]
v.cur_bond_len_.push_back(sqrt(r2));
found = true;
}
}
return found;
}
} // end ATC

98
lib/atc/CBLattice.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef CBLATTICE_H
#define CBLATTICE_H
#include <set>
#include <vector>
#include <stack>
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
using std::vector;
namespace ATC
{
class CbPotential; // forward definition for potentials.
class StressArgs;
/**
* @class AtomCluster
* @brief Base class for a container for a cluster of atoms around an atom at the origin
* (which is not included in the lists).
* Provides routines for outputting the atoms in the cluster to a vtk file,
* and checking for any overlapping atoms (which should not happen).
*/
class AtomCluster
{
friend class CBLattice;
friend DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args);
friend int test_FCB(const StressArgs &args);
public:
//* Returns the number of atoms in the cluster.
INDEX size() const { return cur_bond_len_.size(); }
//* removes all atoms from the cluster
void clear() { cur_bond_len_.clear(), ref_coords_.clear(); }
//* Removes any overlapping atoms (if they exist).
INDEX remove_overlap();
//* Writes the atom positions of the cluster to a dat file.
void write_to_dat(std::string path, bool current_config=true);
//* Writes the atom positions of the cluster to a vtk file.
void write_to_vtk(std::string path, bool current_config=true);
//* Returns an atom coordinate in the reference configuration.
const DENS_VEC &R(INDEX i) const { return ref_coords_[i]; }
//* Returns an atom coordinate in the current configuration.
DENS_VEC r(INDEX i) const { return F_*R(i); }
//* Returns the ith atoms bond length to the central atom.
double bond_length(INDEX i) const { return cur_bond_len_[i]; }
//* Returns a reference to the deformation gradient tensor.
const DENS_MAT& deformation_gradient() const { return F_; }
//* Computes forces on central atom, with atom I displaced by u.
DENS_VEC perturbed_force(const CbPotential *p, int I, DENS_VEC *u) const;
//* Computes the force constant matrix between atoms I and 0.
DENS_MAT force_constants(INDEX I, const CbPotential *p) const;
private:
std::vector<double> cur_bond_len_; //*> Bond lengths (current)
std::vector<DENS_VEC> ref_coords_; //*> Atom coordinates (ref)
DENS_MAT F_; //*> Deformation gradient
};
/**
* @class CBLattice
* @brief Base class that generates a virtual atom clusters given a lattice and
* a deformation gradient.
*/
class CBLattice
{
protected:
double RC2_; //*> cutoff radius^2
std::stack<int> queue0_; //*> cell nghbrs of representative cell
DENS_MAT n_, b_; //*> cols are def base and basis vectors
const MATRIX &N_, &B_; //*> cols are ref base and basis vectors
public:
//* Operator that outputs the lattice and basis to a stream.
friend std::ostream& operator<<(std::ostream& o, const CBLattice& lattice);
CBLattice(const MATRIX &N, const MATRIX &B);
//* generates the virtual atom cluster
void atom_cluster(const MATRIX &F, double cutoff, AtomCluster &v);
protected:
void _FindAtomsInCutoff(AtomCluster &v);
bool _CheckUnitCell(char a, char b, char c, AtomCluster &v);
};
// hash functions: a, b, c must in range [-128, 127]
inline int hash(int a,int b,int c) {return(a+128)|((b+128)<<8)|((c+128)<<16);}
inline void unhash(int r, int &a, int &b, int &c)
{
a = (r&0xFF) - 128;
b = ((r>>8)&0xFF) - 128;
c = ((r>>16)&0xFF) - 128;
}
}
#endif

84
lib/atc/CG.h Normal file
View File

@ -0,0 +1,84 @@
//*****************************************************************
// Iterative template routine -- CG
//
// CG solves the symmetric positive definite linear
// system Ax=b using the Conjugate Gradient method.
//
// CG follows the algorithm described on p. 15 in the
// SIAM Templates book.
//
// The return value indicates convergence within max_iter (input)
// iterations (0), or no convergence within max_iter iterations (1).
//
// Upon successful return, output arguments have the following values:
//
// x -- approximate solution to Ax = b
// max_iter -- the number of iterations performed before the
// tolerance was reached
// tol -- the residual after the final iteration
//
//*****************************************************************
/**
* @class CG
* @brief Base class for solving the linear system Ax=b using the Conjugate Gradient method
*/
template < class Matrix, class Vector, class DataVector, class Preconditioner, class Real >
int
CG(const Matrix &A, Vector &x, const DataVector &b, const Preconditioner &M, int &max_iter, Real &tol) {
Real resid;
DenseVector<Real> p, z, q;
Real alpha, beta, rho, rho_1(0);
DenseVector<Real> tmp;
tmp.reset(b.size());
p.reset(b.size());
z.reset(b.size());
q.reset(b.size());
Real normb = b.norm();
DenseVector<Real> r;
tmp = A*x;
r = b - tmp;
// Implicit assumption that only diagonal matrices are being used for preconditioning
Preconditioner Minv = M.inv();
if (normb == 0.0)
normb = 1;
if ((resid = r.norm() / normb) <= tol) {
tol = resid;
max_iter = 0;
return 0;
}
for (int i = 0; i < max_iter; i++) {
z = Minv*r;
rho = r.dot(z);
if (i == 0)
p = z;
else {
beta = rho / rho_1;
tmp = p*beta;
p = z + tmp;
}
q = A*p;
alpha = rho / p.dot(q);
x += p*alpha;
r -= q*alpha;
if ((resid = r.norm() / normb) <= tol)
{
tol = resid;
max_iter = i+1;
return 0;
}
rho_1 = rho;
}
tol = resid;
return 1;
}

572
lib/atc/CauchyBorn.cpp Normal file
View File

@ -0,0 +1,572 @@
#include "CauchyBorn.h"
#include "VoigtOperations.h"
#include "CBLattice.h"
#include "CbPotential.h"
using voigt3::to_voigt;
namespace ATC {
//============================================================================
// Computes the electron density for EAM potentials
//============================================================================
double cb_electron_density(const StressArgs &args )
{
double e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
}
return e_density;
}
//============================================================================
// Computes the stress at a quadrature point
//============================================================================
void cb_stress(const StressArgs &args, StressAtIP &s, double *F)
{
const double &T = args.temperature;
const bool finite_temp = T > 0.0;
DENS_MAT D; // dynamical matrix (finite temp)
DENS_MAT_VEC dDdF; // derivative of dynamical matrix (finite temp)
double e_density(0.),embed(0.),embed_p(0.),embed_pp(0.),embed_ppp(0.);
DENS_VEC l0;
DENS_MAT L0;
DENS_MAT_VEC M0;
// If temperature is nonzero then allocate space for
// dynamical matrix and its derivative with respect to F.
if (finite_temp) {
D.reset(3,3);
dDdF.assign(6, DENS_MAT(3,3));
M0.assign(3, DENS_MAT(3,3));
L0.reset(3,3);
l0.reset(3);
}
if (F) *F = 0.0;
// if using EAM potential, calculate embedding function and derivatives
if (args.potential->terms.embedding) {
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
pair.r = args.vac.r(a);
pair.rho_r = args.potential->rho_r(pair.d);
pair.rho_rr = args.potential->rho_rr(pair.d);
if (finite_temp) {
l0 += pair.r*pair.di*pair.rho_r;
DENS_MAT rR = tensor_product(pair.r, pair.R);
L0.add_scaled(rR, pair.di*pair.rho_r);
DENS_MAT rr = tensor_product(pair.r, pair.r);
rr *= pair.di*pair.di*(pair.rho_rr - pair.di*pair.rho_r);
diagonal(rr) += pair.di*pair.rho_r;
for (int i = 0; i < 3; i++) {
for (int k = 0; k < 3; k++) {
for (int L = 0; L < 3; L++) {
M0[i](k,L) += rr(i,k)*args.vac.R(a)(L);
}
}
}
}
}
embed = args.potential->F(e_density); // "F" in usual EAM symbology
embed_p = args.potential->F_p(e_density);
embed_pp = args.potential->F_pp(e_density);
embed_ppp = args.potential->F_ppp(e_density);
if (F) *F += embed;
if (finite_temp) {
const DENS_MAT ll = tensor_product(l0, l0);
D.add_scaled(ll, embed_pp);
const DENS_VEC llvec = to_voigt(ll);
for (int v = 0; v < 6; v++) {
dDdF[v].add_scaled(L0, embed_ppp*llvec(v));
}
dDdF[0].add_scaled(M0[0], 2*embed_pp*l0(0));
dDdF[1].add_scaled(M0[1], 2*embed_pp*l0(1));
dDdF[2].add_scaled(M0[2], 2*embed_pp*l0(2));
dDdF[3].add_scaled(M0[1], embed_pp*l0(2));
dDdF[3].add_scaled(M0[2], embed_pp*l0(1));
dDdF[4].add_scaled(M0[0], embed_pp*l0(2));
dDdF[4].add_scaled(M0[2], embed_pp*l0(0));
dDdF[5].add_scaled(M0[0], embed_pp*l0(1));
dDdF[5].add_scaled(M0[1], embed_pp*l0(0));
}
}
// Loop on all cluster atoms (origin atom not included).
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
if (args.potential->terms.pairwise) {
if (F) *F += 0.5*args.potential->phi(pair.d);
pair.phi_r = args.potential->phi_r(pair.d);
pairwise_stress(pair, s);
}
if (args.potential->terms.embedding) {
pair.F_p = embed_p;
pair.rho_r = args.potential->rho_r(pair.d);
embedding_stress(pair, s);
}
if (finite_temp) { // Compute finite T terms.
pair.r = args.vac.r(a);
if (args.potential->terms.pairwise) {
pair.phi_rr = args.potential->phi_rr(pair.d);
pair.phi_rrr = args.potential->phi_rrr(pair.d);
pairwise_thermal(pair, D, &dDdF);
}
if (args.potential->terms.embedding) {
pair.rho_rr = args.potential->rho_rr(pair.d);
pair.rho_rrr = args.potential->rho_rrr(pair.d);
pair.F_pp = embed_pp;
pair.F_ppp = embed_ppp;
embedding_thermal(pair,D,L0,&dDdF);
}
}
// if has three-body terms ... TODO compute three-body terms
}
// Finish finite temperature Cauchy-Born.
if (finite_temp) {
const DENS_MAT &F = args.vac.deformation_gradient();
thermal_end(dDdF, D, F, T, args.boltzmann_constant, s);
}
}
//===========================================================================
// Computes the elastic energy (free or potential if T=0).
//===========================================================================
double cb_energy(const StressArgs &args)
{
const double &T = args.temperature;
bool finite_temp = (T > 0.0);
//const bool finite_temp = T > 0.0;
DENS_MAT D; // dynamical matrix (finite temp)
double e_density,embed,embed_p(0.),embed_pp(0.),embed_ppp(0.);
DENS_VEC l0;
DENS_MAT L0;
DENS_MAT_VEC M0;
// If temperature is nonzero then allocate space for dynamical matrix.
if (finite_temp) {
D.reset(3,3);
l0.reset(3);
}
double F = 0.0;
// Do pairwise terms, loop on all cluster atoms (origin atom not included).
// if using EAM potential, calculate embedding function and derivatives
if (args.potential->terms.embedding) {
e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
pair.r = args.vac.r(a);
if (finite_temp) {
l0 += pair.r*pair.di*pair.rho_r;
}
}
embed = args.potential->F(e_density);
embed_p = args.potential->F_p(e_density);
embed_pp = args.potential->F_pp(e_density);
embed_ppp = args.potential->F_ppp(e_density);
F += embed;
if (finite_temp) {
const DENS_MAT ll = tensor_product(l0, l0);
D.add_scaled(ll, embed_pp);
}
}
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
if (args.potential->terms.pairwise) {
F += 0.5*args.potential->phi(pair.d);
}
if (finite_temp) { // Compute finite T terms.
pair.r = args.vac.r(a);
if (args.potential->terms.pairwise) {
pair.phi_r = args.potential->phi_r(pair.d);
pair.phi_rr = args.potential->phi_rr(pair.d);
pair.phi_rrr = args.potential->phi_rrr(pair.d);
pairwise_thermal(pair, D);
}
if (args.potential->terms.embedding) {
pair.rho_r = args.potential->rho_r(pair.d);
pair.rho_rr = args.potential->rho_rr(pair.d);
pair.rho_rrr = args.potential->rho_rrr(pair.d);
pair.F_p = embed_p;
pair.F_pp = embed_pp;
pair.F_ppp = embed_ppp;
embedding_thermal(pair,D,L0);
}
}
// if has three-body terms ... TODO compute three-body terms
}
// Finish finite temperature Cauchy-Born.
const double kB = args.boltzmann_constant;
const double hbar = args.planck_constant;
if (finite_temp) {
F += kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D)));
}
//if (finite_temp) F += 0.5*args.boltzmann_constant*T*log(det(D));
return F;
}
//===========================================================================
// Computes the entropic energy TS (minus c_v T)
//===========================================================================
double cb_entropic_energy(const StressArgs &args)
{
const double &T = args.temperature;
DENS_MAT D(3,3); // dynamical matrix (finite temp)
double e_density,embed_p(0.),embed_pp(0.),embed_ppp(0.);
DENS_VEC l0(3);
DENS_MAT L0;
DENS_MAT_VEC M0;
// if using EAM potential, calculate embedding function and derivatives
if (args.potential->terms.embedding) {
e_density = 0.0;
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
e_density += args.potential->rho(pair.d);
pair.r = args.vac.r(a);
l0 += pair.r*pair.di*pair.rho_r;
//DENS_MAT rR = tensor_product(pair.r, pair.R);
//L0.add_scaled(rR, pair.di*args.potential->rho_r(pair.d));
}
//embed = args.potential->F(e_density);
embed_p = args.potential->F_p(e_density);
embed_pp = args.potential->F_pp(e_density);
embed_ppp = args.potential->F_ppp(e_density);
const DENS_MAT ll = tensor_product(l0, l0);
D.add_scaled(ll, embed_pp);
}
// Compute the dynamical matrix
// Loop on all cluster atoms (origin atom not included).
for (INDEX a=0; a<args.vac.size(); a++) {
// Compute pairwise terms needed for pairwise_stress.
PairParam pair(args.vac.R(a), args.vac.bond_length(a));
pair.r = args.vac.r(a);
if (args.potential->terms.pairwise) {
pair.phi_r = args.potential->phi_r(pair.d);
pair.phi_rr = args.potential->phi_rr(pair.d);
pair.phi_rrr = args.potential->phi_rrr(pair.d);
pairwise_thermal(pair, D);
}
if (args.potential->terms.embedding) {
pair.rho_r = args.potential->rho_r(pair.d);
pair.rho_rr = args.potential->rho_rr(pair.d);
pair.rho_rrr = args.potential->rho_rrr(pair.d);
pair.F_p = embed_p;
pair.F_pp = embed_pp;
pair.F_ppp = embed_ppp;
embedding_thermal(pair,D,L0);
}
}
// Finish finite temperature Cauchy-Born.
const double kB = args.boltzmann_constant;
const double hbar = args.planck_constant;;
double F = kB*T*log(pow(hbar/(kB*T),3.0)*sqrt(det(D)));
return F;
}
//===========================================================================
// Computes the stress contribution given the pairwise parameters.
//===========================================================================
inline void pairwise_stress(const PairParam &p, StressAtIP &s)
{
for (INDEX i=0; i<p.R.size(); i++)
for (INDEX j=i; j<p.R.size(); j++)
s(i,j) += 0.5*p.di * p.phi_r * p.R(i) * p.R(j);
}
//===========================================================================
// Computes the stress contribution given the embedding parameters.
//===========================================================================
inline void embedding_stress(const PairParam &p, StressAtIP &s)
{
for (INDEX i=0; i<p.R.size(); i++)
for (INDEX j=i; j<p.R.size(); j++)
s(i,j) += p.di * p.F_p * p.rho_r * p.R(i) * p.R(j);
}
//===========================================================================
// Computes the pairwise thermal components for the stress
//===========================================================================
void pairwise_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT_VEC *dDdF)
{
const double di2 = p.di*p.di;
const double g = p.di*p.phi_r;
const double g_d = p.di*p.phi_rr - p.di*g; // units (energy / length^3)
const double f = di2 * (p.phi_rr - g); // units (energy / length^4)
const double f_d = di2*(p.phi_rrr-g_d) - 2.0*p.di*f;
// compute needed tensor products of r and R
const DENS_MAT rr = tensor_product(p.r, p.r);
// compute the dynamical matrix
D.add_scaled(rr, f);
diagonal(D) += g;
if (!dDdF) return; // skip derivative
const double gp_r = g_d*p.di;
const double fp_r = f_d*p.di;
const double fr[] = {f*p.r(0), f*p.r(1), f*p.r(2)};
const DENS_MAT rR = tensor_product(p.r, p.R);
DENS_MAT_VEC &dD = *dDdF;
// compute first term in A.13
dD[0].add_scaled(rR, fp_r*rr(0,0) + gp_r);
dD[1].add_scaled(rR, fp_r*rr(1,1) + gp_r);
dD[2].add_scaled(rR, fp_r*rr(2,2) + gp_r);
dD[3].add_scaled(rR, fp_r*rr(1,2));
dD[4].add_scaled(rR, fp_r*rr(0,2));
dD[5].add_scaled(rR, fp_r*rr(0,1));
// compute second term in A.13
for (INDEX L=0; L<p.R.size(); L++) {
dD[0](0,L) += p.R[L] * 2.0*fr[0];
dD[1](1,L) += p.R[L] * 2.0*fr[1];
dD[2](2,L) += p.R[L] * 2.0*fr[2];
dD[3](1,L) += p.R[L] * fr[2];
dD[3](2,L) += p.R[L] * fr[1];
dD[4](0,L) += p.R[L] * fr[2];
dD[4](2,L) += p.R[L] * fr[0];
dD[5](0,L) += p.R[L] * fr[1];
dD[5](1,L) += p.R[L] * fr[0];
}
}
//===========================================================================
// Computes the embedding thermal components for the stress
//===========================================================================
void embedding_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT &L0, DENS_MAT_VEC *dDdF)
{
const double di = p.di;
const double di2 = p.di*p.di;
const double di3 = p.di*p.di*p.di;
const double x = p.F_pp*p.rho_r*p.rho_r + 2*p.F_p*p.rho_rr;
const double z = di*(2*p.F_p*p.rho_r);
const double y = di2*(x-z);
// compute needed tensor products of r and R
const DENS_MAT rr = tensor_product(p.r, p.r);
// compute the dynamical matrix
D.add_scaled(rr, y);
diagonal(D) += z;
if (!dDdF) return; // skip derivative
DENS_MAT_VEC &dD = *dDdF;
const DENS_MAT rR = tensor_product(p.r, p.R);
double rho_term1 = p.rho_rr - di*p.rho_r;
double rho_term2 = p.rho_r*rho_term1;
double rho_term3 = p.rho_rrr - 3*di*p.rho_rr + 3*di2*p.rho_r;
const double a = di2*2*p.F_p*rho_term1;
const double b = di2*(p.F_ppp*p.rho_r*p.rho_r + 2*p.F_pp*rho_term1);
const double c = di3*(2*p.F_pp*rho_term2 + 2*p.F_p*rho_term3);
const double w = di2*p.F_pp*p.rho_r*p.rho_r;
//first add terms that multiply rR
dD[0].add_scaled(rR, a + c*rr(0,0));
dD[1].add_scaled(rR, a + c*rr(1,1));
dD[2].add_scaled(rR, a + c*rr(2,2));
dD[3].add_scaled(rR, c*rr(1,2));
dD[4].add_scaled(rR, c*rr(0,2));
dD[5].add_scaled(rR, c*rr(0,1));
//add terms that multiply L0
dD[0].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(0,0));
dD[1].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(1,1));
dD[2].add_scaled(L0, di*2*p.F_pp*p.rho_r + b*rr(2,2));
dD[3].add_scaled(L0, b*rr(1,2));
dD[4].add_scaled(L0, b*rr(0,2));
dD[5].add_scaled(L0, b*rr(0,1));
//add remaining term
const double aw = a + w;
const double awr[] = {aw*p.r(0), aw*p.r(1), aw*p.r(2)};
for (INDEX L=0; L<p.R.size(); L++) {
dD[0](0,L) += 2*awr[0]*p.R[L];
dD[1](1,L) += 2*awr[1]*p.R[L];
dD[2](2,L) += 2*awr[2]*p.R[L];
dD[3](2,L) += awr[1]*p.R[L];
dD[3](1,L) += awr[2]*p.R[L];
dD[4](2,L) += awr[0]*p.R[L];
dD[4](0,L) += awr[2]*p.R[L];
dD[5](1,L) += awr[0]*p.R[L];
dD[5](0,L) += awr[1]*p.R[L];
}
}
//===========================================================================
// Last stage of the pairwise finite-T Cauchy-Born stress computation.
//===========================================================================
inline void thermal_end(const DENS_MAT_VEC &DF, // dynamical matrix derivative
const DENS_MAT &D, // dynamical matrix
const DENS_MAT &F, // deformation gradient
const double &T, // temperature
const double &kb, // boltzmann constant
StressAtIP &s, // output stress (-)
double* F_w) // output free energy (optional)
{
DENS_MAT c = adjugate(D), dd(3,3);
dd.add_scaled(DF[0], c(0,0));
dd.add_scaled(DF[1], c(1,1));
dd.add_scaled(DF[2], c(2,2));
dd.add_scaled(DF[3], c(1,2) + c(2,1));
dd.add_scaled(DF[4], c(0,2) + c(2,0));
dd.add_scaled(DF[5], c(0,1) + c(1,0));
const double detD = det(D);
const double factor = 0.5*kb*T/detD;
// converts from PK1 to PK2
dd = inv(F)*dd;
for (INDEX i=0; i<3; i++)
for (INDEX j=i; j<3; j++)
s(i,j) += factor * dd(i,j);
// If f_W is not NULL then append thermal contribution.
if (F_w) *F_w += 0.5*kb*T*log(detD);
}
//============================================================================
// Returns the stretch tensor and its derivative with respect to C (R C-G).
//============================================================================
void stretch_tensor_derivative(const DENS_VEC &C, DENS_VEC &U, DENS_MAT &dU)
{
// Compute the invariants of C
const DENS_VEC C2(voigt3::dsymm(C,C));
const double Ic = voigt3::tr(C);
const double IIc = 0.5*(Ic*Ic - voigt3::tr(C2));
const double IIIc = voigt3::det(C);
const DENS_VEC I = voigt3::eye(3);
// Compute the derivatives of the invarants of C
DENS_VEC dIc ( I );
DENS_VEC dIIc ( Ic*dIc - C );
DENS_VEC dIIIc ( voigt3::inv(C) * IIIc );
for (INDEX i=3; i<6; i++) {
dIIc(i) *= 2.0;
dIIIc(i) *= 2.0;
}
// Check if C is an isotropic tensor (simple case)
const double k = Ic*Ic - 3.0*IIc;
const DENS_VEC dk (2.0*Ic*dIc - 3.0*dIIc);
if (k < 1e-8) {
const double lambda = sqrt((1.0/3.0)*Ic);
const double dlambda = 0.5/(3.0*lambda);
U = I*lambda;
dU = tensor_product(dIc*dlambda, dIc); // may not be correct
return;
}
// Find the largest eigenvalue of C
const double L = Ic*(Ic*Ic - 4.5*IIc) + 13.5*IIIc;
DENS_VEC dL( (3.0*Ic*Ic-4.5*IIc)*dIc );
dL.add_scaled(dIIc, -4.5*Ic);
dL.add_scaled(dIIIc, 13.5);
const double kpow = pow(k,-1.5);
const double dkpow = -1.5*kpow/k;
const double phi = acos(L*kpow); // phi - good
// temporary factors for dphi
const double d1 = -1.0/sqrt(1.0-L*L*kpow*kpow);
const double d2 = d1*kpow;
const double d3 = d1*L*dkpow;
const DENS_VEC dphi (d2*dL + d3*dk);
const double sqrt_k=sqrt(k), cos_p3i=cos((1.0/3.0)*phi);
const double lam2 = (1.0/3.0)*(Ic + 2.0*sqrt_k*cos_p3i);
DENS_VEC dlam2 = (1.0/3.0)*dIc;
dlam2.add_scaled(dk, (1.0/3.0)*cos_p3i/sqrt_k);
dlam2.add_scaled(dphi, (-2.0/9.0)*sqrt_k*sin((1.0/3.0)*phi));
const double lambda = sqrt(lam2);
const DENS_VEC dlambda = (0.5/lambda)*dlam2;
// Compute the invariants of U
const double IIIu = sqrt(IIIc);
const DENS_VEC dIIIu (0.5/IIIu*dIIIc);
const double Iu = lambda + sqrt(-lam2 + Ic + 2.0*IIIu/lambda);
const double invrt = 1.0/(Iu-lambda);
DENS_VEC dIu(dlambda); dIu *= 1.0 + invrt*(-lambda - IIIu/lam2);
dIu.add_scaled(dIc, 0.5*invrt);
dIu.add_scaled(dIIIu, invrt/lambda);
const double IIu = 0.5*(Iu*Iu - Ic);
const DENS_VEC dIIu ( Iu*dIu - 0.5*dIc );
// Compute U and its derivatives
const double fct = 1.0/(Iu*IIu-IIIu);
DENS_VEC dfct = dIu; dfct *= IIu;
dfct.add_scaled(dIIu, Iu);
dfct -= dIIIu;
dfct *= -fct*fct;
U = voigt3::eye(3, Iu*IIIu);
U.add_scaled(C, Iu*Iu-IIu);
U -= C2;
DENS_MAT da = tensor_product(I, dIu); da *= IIIu;
da.add_scaled(tensor_product(I, dIIIu), Iu);
da += tensor_product(C, 2.0*Iu*dIu-dIIu);
da.add_scaled(eye<double>(6,6),Iu*Iu-IIu);
da -= voigt3::derivative_of_square(C);
dU = tensor_product(U, dfct);
dU.add_scaled(da, fct);
U *= fct;
}
//============================================================================
// Computes the dynamical matrix (TESTING FUNCTION)
//============================================================================
DENS_MAT compute_dynamical_matrix(const StressArgs &args)
{
DENS_MAT D(3,3);
for (INDEX a=0; a<args.vac.size(); a++) {
PairParam pair(args.vac.R(a), args.vac.r(a).norm());
pair.phi_r = args.potential->phi_r(pair.d);
pair.r = args.vac.r(a);
pair.phi_rr = args.potential->phi_rr(pair.d);
pair.phi_rrr = args.potential->phi_rrr(pair.d);
pairwise_thermal(pair, D);
}
return D;
}
//============================================================================
// Computes the determinant of the dynamical matrix (TESTING FUNCTION)
//============================================================================
double compute_detD(const StressArgs &args)
{
return det(compute_dynamical_matrix(args));
}
//============================================================================
// Computes the derivative of the dynamical matrix (TESTING FUNCTION)
//============================================================================
DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args)
{
const double EPS = 1.0e-6;
DENS_MAT_VEC dDdF(6, DENS_MAT(3,3));
for (INDEX i=0; i<3; i++) {
for (INDEX j=0; j<3; j++) {
// store original F
const double Fij = args.vac.F_(i,j);
args.vac.F_(i,j) = Fij + EPS;
DENS_MAT Da = compute_dynamical_matrix(args);
args.vac.F_(i,j) = Fij - EPS;
DENS_MAT Db = compute_dynamical_matrix(args);
args.vac.F_(i,j) = Fij;
dDdF[0](i,j) = (Da(0,0)-Db(0,0))*(0.5/EPS);
dDdF[1](i,j) = (Da(1,1)-Db(1,1))*(0.5/EPS);
dDdF[2](i,j) = (Da(2,2)-Db(2,2))*(0.5/EPS);
dDdF[3](i,j) = (Da(1,2)-Db(1,2))*(0.5/EPS);
dDdF[4](i,j) = (Da(0,2)-Db(0,2))*(0.5/EPS);
dDdF[5](i,j) = (Da(0,1)-Db(0,1))*(0.5/EPS);
}
}
return dDdF;
}
}

113
lib/atc/CauchyBorn.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef CAUCHYBORN_H
#define CAUCHYBORN_H
#include "CbPotential.h"
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
// This file provides all routines necessary for computing the Cauchy-Born
// stresses.
namespace ATC {
// forward declaration of CbPotential
class CbPotential;
class AtomCluster;
/**
* @class StressAtIP
* @brief Class for passing the vector of stresses at quadrature points
* Done by storing the quadrature point and providing indexing
*/
class StressAtIP {
INDEX q; //*> Quadrature point.
DENS_MAT_VEC &S; //*> Stress components at all quad points.
public:
//* Constructor - sets stress reference and current quadrature point.
StressAtIP(DENS_MAT_VEC &s, INDEX _q=0) : q(_q), S(s) {}
//* Indexing operator, gets stress components for the quadrature point.
double& operator()(INDEX i, INDEX j) const { return S[i](q, j); }
//* Sets quadrature point to a new index.
void set_quadrature_point(INDEX qp) { q = qp; }
//* Operator that outputs the stress
friend std::ostream& operator<<(std::ostream& o, const StressAtIP& s)
{ o << "stress\n";
o << s(0,0) << " " << s(0,1) << " " << s(0,2) << "\n";
o << s(1,0) << " " << s(1,1) << " " << s(1,2) << "\n";
o << s(2,0) << " " << s(2,1) << " " << s(2,2) << "\n";
return o;
}
};
/**
* @class StressArgs
* @brief Class for storing parameters needed for computing the Cauchy-Born stress
*/
struct StressArgs {
StressArgs(AtomCluster &v, CbPotential *p, double kB, double hbar, double T)
: vac(v), potential(p), boltzmann_constant(kB), planck_constant(hbar),
temperature(T) {}
AtomCluster &vac;
CbPotential *potential;
double boltzmann_constant;
double planck_constant;
double temperature;
};
/**
* @class PairParam
* @brief Class for storing parameters used in pairwise stress computations
*/
struct PairParam {
PairParam(const DENS_VEC &_R, double _d) : R(_R), d(_d), di(1.0/_d) {}
const DENS_VEC &R; //*> Reference bond vector.
DENS_VEC r; //*> Current bond vector.
double d, di; //*> Current bond length and its inverse.
double phi_r; //*> First derivative of pairwise term.
double phi_rr; //*> Second derivative of pairwise term.
double phi_rrr; //*> Third derivative of pairwise term.
double rho_r;
double rho_rr;
double rho_rrr;
double F_p;
double F_pp;
double F_ppp;
};
//* for EAM, calculate electron density
double cb_electron_density(const StressArgs &args);
//* Compute stress, from virtual atom cluster, potential
//* temperature (can be 0K), and StressAtIP object to write to.
void cb_stress(const StressArgs &args, StressAtIP &s, double *F=0);
//* Computes the elastic energy (free or potential if T=0).
double cb_energy(const StressArgs &args);
//* Computes the entropic energy
double cb_entropic_energy(const StressArgs &args);
//* Auxiliary functions for cb_stress
//@{
//* Computes the stress contribution given the pairwise parameters.
void pairwise_stress(const PairParam &p, StressAtIP &s);
//* Computes the stress contribution given the embedding parameters.
void embedding_stress(const PairParam &p, StressAtIP &s);
//* Computes the pairwise thermal components for the stress and free energy.
void pairwise_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT_VEC *dDdF=0);
//* Computes the embedding thermal components for the stress and free energy.
void embedding_thermal(const PairParam &p, DENS_MAT &D, DENS_MAT &L0, DENS_MAT_VEC *dDdF=0);
//* Last stage of the pairwise finite-T Cauchy-Born stress computation.
void thermal_end(const DENS_MAT_VEC &DF, const DENS_MAT &D, const DENS_MAT &F,
const double &T, const double &kb, StressAtIP &s, double *F_w=0);
//* Returns the stretch tensor and its derivative with respect to C (R C-G).
void stretch_tensor_derivative(const DENS_VEC &C, DENS_VEC &U, DENS_MAT &dU);
//@}
//* Testing functions (to be removed when all CB code is completed)
//@{
//* Computes the dynamical matrix (TESTING FUNCTION)
DENS_MAT compute_dynamical_matrix(const StressArgs &args);
//* Computes the determinant of the dynamical matrix (TESTING FUNCTION)
double compute_detD(const StressArgs &args);
//* Computes the derivative of the dynamical matrix (TESTING FUNCTION)
DENS_MAT_VEC compute_dynamical_derivative(StressArgs &args);
//@}
}
#endif

212
lib/atc/CbEam.h Normal file
View File

@ -0,0 +1,212 @@
#ifndef CBEAM_H
#define CBEAM_H
#include <iostream>
#include <cstdlib>
#include "CbPotential.h"
#include "LammpsInterface.h"
#include "pair_eam.h"
//#define MIN(a,b) ((a) < (b) ? (a) : (b))
//#define MAX(a,b) ((a) > (b) ? (a) : (b))
namespace ATC
{
/**
* @class CbEam
* @brief Class for computing Cauchy-Born quantities for an Embeded-Atom Method material
* (A factor of one-half is already included to split the
* bond energy between atoms)
*/
class CbEam : public CbPotential
{
public:
//! Constructor
CbEam(void) : CbPotential(Interactions(PAIRWISE,EAM)) {
// get pointer to lammps' pair_eam object
lammps_eam = ATC::LammpsInterface::instance()->pair_eam();
nrho = &lammps_eam->nrho;
nr = &lammps_eam->nr;
nfrho = &lammps_eam->nfrho;
nrhor = &lammps_eam->nrhor;
nz2r = &lammps_eam->nz2r;
type2frho = lammps_eam->type2frho;
type2rhor = lammps_eam->type2rhor;
type2z2r = lammps_eam->type2z2r;
dr = &lammps_eam->dr;
rdr = &lammps_eam->rdr;
drho = &lammps_eam->drho;
rdrho = &lammps_eam->rdrho;
rhor_spline = &lammps_eam->rhor_spline;
frho_spline = &lammps_eam->frho_spline;
z2r_spline = &lammps_eam->z2r_spline;
cutmax = &lammps_eam->cutmax;
}
//! Returns the cutoff readius of the EAM potential functions rho and z2r.
double cutoff_radius() const { return *cutmax; }
//! Returns the EAM pair energy
double phi(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*z2r_spline)[type2z2r[1][1]][m];
double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6];
return z2/r;
}
//! Returns the first derivative of the pair energy.
double phi_r(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*z2r_spline)[type2z2r[1][1]][m];
double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6];
double z2p = (coeff[0]*p + coeff[1])*p + coeff[2];
return (1.0/r)*(z2p-z2/r);
}
//! Returns the second derivative of the pair energy.
double phi_rr(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*z2r_spline)[type2z2r[1][1]][m];
double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6];
double z2p = (coeff[0]*p + coeff[1])*p + coeff[2];
double z2pp = (*rdr)*(2.0*coeff[0]*p + coeff[1]);
return (1.0/r)*(z2pp-2.0*z2p/r+2.0*z2/(r*r));
}
//! Returns the third derivative of the pair energy.
double phi_rrr(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*z2r_spline)[type2z2r[1][1]][m];
double z2 = ((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6];
double z2p = (coeff[0]*p + coeff[1])*p + coeff[2];
double z2pp = (*rdr)*(2.0*coeff[0]*p + coeff[1]);
double z2ppp = (*rdr)*(*rdr)*2.0*coeff[0];
return (1.0/r)*(z2ppp-3.0*z2pp/r+6.0*z2p/(r*r)-6.0*z2/(r*r*r));
}
//! Returns the EAM atomic charge density.
double rho(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*rhor_spline)[type2rhor[1][1]][m];
return (((coeff[3]*p + coeff[4])*p + coeff[5])*p + coeff[6]);
}
//! Returns the first derivative of the atomic charge density.
double rho_r(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*rhor_spline)[type2rhor[1][1]][m];
return ((coeff[0]*p + coeff[1])*p + coeff[2]);
}
//! Returns the second derivative of the atomic charge density.
double rho_rr(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*rhor_spline)[type2rhor[1][1]][m];
return ((*rdr)*(2.0*coeff[0]*p + coeff[1]));
}
//! Returns the third derivative of the atomic charge density.
double rho_rrr(const double &r) const
{
double p = r*(*rdr) + 1.0;
int m = static_cast<int> (p);
m = MIN(m,(*nr)-1);
p -= m;
p = MIN(p,1.0);
// for now, assume itype = jtype = 1
double *coeff = (*rhor_spline)[type2rhor[1][1]][m];
return ((*rdr)*(*rdr)*2.0*coeff[0]);
}
//! Returns the EAM embedding energy.
double F(const double &p) const
{
double q = p*(*rdrho) + 1.0;
int m = static_cast<int> (q);
m = MIN(m,(*nrho)-1);
q -= m;
q = MIN(q,1.0);
// for now, assume itype = 1
double *coeff = (*frho_spline)[type2frho[1]][m];
return (((coeff[3]*q + coeff[4])*q + coeff[5])*q + coeff[6]);
}
//! Returns the first derivative of the embedding energy.
double F_p(const double &p) const
{
double q = p*(*rdrho) + 1.0;
int m = static_cast<int> (q);
m = MIN(m,(*nrho)-1);
q -= m;
q = MIN(q,1.0);
// for now, assume itype = 1
double *coeff = (*frho_spline)[type2frho[1]][m];
return ((coeff[0]*q + coeff[1])*q + coeff[2]);
}
//! Returns the second derivative of the atomic charge density.
double F_pp(const double &p) const
{
double q = p*(*rdrho) + 1.0;
int m = static_cast<int> (q);
m = MIN(m,(*nrho)-1);
q -= m;
q = MIN(q,1.0);
// for now, assume itype = 1
double *coeff = (*frho_spline)[type2frho[1]][m];
return ((*rdrho)*(2.0*coeff[0]*q + coeff[1]));
}
//! Returns the third derivative of the atomic charge density.
double F_ppp(const double &p) const
{
double q = p*(*rdrho) + 1.0;
int m = static_cast<int> (q);
m = MIN(m,(*nrho)-1);
q -= m;
q = MIN(q,1.0);
// for now, assume itype = 1
double *coeff = (*frho_spline)[type2frho[1]][m];
return ((*rdrho)*(*rdrho)*2.0*coeff[0]);
}
int *nrho,*nr,*nfrho,*nrhor,*nz2r;
int *type2frho,**type2rhor,**type2z2r;
double *cutmax;
double *dr,*rdr,*drho,*rdrho;
double ****rhor_spline,****frho_spline,****z2r_spline;
LAMMPS_NS::PairEAM* lammps_eam;
};
}
#endif

62
lib/atc/CbLjCut.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef CBLJCUT_H
#define CBLJCUT_H
#include <iostream>
#include <cstdlib>
#include "CbPotential.h"
namespace ATC
{
/**
* @class CbLjCut
* @brief Class for computing Cauchy-Born quantities for a Lennard-Jones/cut material
* (A factor of one-half is already included to split the
* bond energy between atoms)
*/
class CbLjCut : public CbPotential
{
public:
//! Constructor - initializes coefficients and enables PAIRWISE term.
CbLjCut(double eps, double sig, double cutoff_radius)
: CbPotential(Interactions(PAIRWISE)),
A (4.0*eps*pow(sig, 12)),
B (4.0*eps*pow(sig, 6)),
rcut(cutoff_radius)
{ }
//! Returns the cutoff readius of the LJ potential.
double cutoff_radius() const { return rcut; }
//! Returns the LJ energy between two interacting atoms (6,12).
double phi(const double &r) const
{
const double r6i = 1.0/((r*r*r)*(r*r*r));
return r6i*(A*r6i - B);
}
//! Returns the first derivative of the LJ energy (7,13).
double phi_r(const double &r) const
{
const double ri = 1.0/r;
const double r6i = (ri*ri*ri)*(ri*ri*ri);
return r6i*ri*(6.0*B - 12.0*A*r6i);
}
//! Returns the second derivative of the LJ energy (8,14).
double phi_rr(const double &r) const
{
const double r2i = 1.0/(r*r);
const double r6i = r2i*r2i*r2i;
return r6i*r2i*(13.0*12.0*A*r6i - 7.0*6.0*B);
}
//! Returns the third derivative of the LJ bond energy (9,15).
double phi_rrr(const double &r) const
{
const double r3i = 1.0/(r*r*r);
const double r9i = r3i*r3i*r3i;
return r9i*(8.0*7.0*6.0*B - 14.0*13.0*12.0*A*r3i*r3i);
}
const double A, B; //!< phi = Ar^-12 + Br^-6
const double rcut; //!< cutoff force
};
}
#endif

View File

@ -0,0 +1,88 @@
#ifndef CBLJSMOOTHLINEAR_H
#define CBLJSMOOTHLINEAR_H
#include <iostream>
#include <cstdlib>
#include "CbPotential.h"
namespace ATC
{
/**
* @class CbLjSmoothLinear
* @brief Class for computing Cauchy-Born quantities for a Lennard-Jones/smooth/linear material
* (A factor of one-half is already included to split the
* bond energy between atoms)
*/
class CbLjSmoothLinear : public CbPotential
{
public:
//! Constructor - initializes coefficients and enables PAIRWISE term.
CbLjSmoothLinear(double eps, double sig, double cutoff_radius)
: CbPotential(Interactions(PAIRWISE)),
A (4.0*eps*pow(sig, 12)),
B (4.0*eps*pow(sig, 6)),
rcut(cutoff_radius)
{
ricut = 1.0/rcut;
r6icut = (ricut*ricut*ricut)*(ricut*ricut*ricut);
phicut = r6icut*(A*r6icut - B);
dphicut = r6icut*ricut*(6.0*B - 12.0*A*r6icut);
}
//! Returns the cutoff readius of the LJ potential.
double cutoff_radius() const { return rcut; }
//! Returns the LJ energy between two interacting atoms (6,12).
double phi(const double &r) const
{
const double r6i = 1.0/((r*r*r)*(r*r*r));
if (r < rcut) {
return (r6i*(A*r6i - B) - phicut - (r-rcut)*dphicut);
}
else {
return 0.0;
}
}
//! Returns the first derivative of the LJ energy (7,13).
double phi_r(const double &r) const
{
const double ri = 1.0/r;
const double r6i = (ri*ri*ri)*(ri*ri*ri);
if (r < rcut) {
return (r6i*ri*(6.0*B - 12.0*A*r6i) - dphicut);
}
else {
return 0.0;
}
}
//! Returns the second derivative of the LJ energy (8,14).
double phi_rr(const double &r) const
{
const double r2i = 1.0/(r*r);
const double r6i = r2i*r2i*r2i;
if (r < rcut) {
return r6i*r2i*(13.0*12.0*A*r6i - 7.0*6.0*B);
}
else {
return 0.0;
}
}
//! Returns the third derivative of the LJ bond energy (9,15).
double phi_rrr(const double &r) const
{
const double r3i = 1.0/(r*r*r);
const double r9i = r3i*r3i*r3i;
if (r < rcut) {
return r9i*(8.0*7.0*6.0*B - 14.0*13.0*12.0*A*r3i*r3i);
}
else {
return 0.0;
}
}
const double A, B; //!< phi = Ar^-12 + Br^-6
const double rcut; //!< cutoff radius
double ricut, r6icut, phicut, dphicut;
};
}
#endif

88
lib/atc/CbPotential.cpp Normal file
View File

@ -0,0 +1,88 @@
#include "CbPotential.h"
#include <cmath>
namespace ATC
{
static const double EPS = 1.0e-8;
// Approximates the derivative of phi
double CbPotential::phi_r(const double &r) const
{
const double dr = r*EPS;
return (phi(r+dr)-phi(r)) / dr;
}
// Approximates the second derivative of phi
double CbPotential::phi_rr(const double &r) const
{
const double dr = r*EPS;
return (phi_r(r+dr)-phi_r(r)) / dr;
}
// Approximates the third derivative of phi
double CbPotential::phi_rrr(const double &r) const
{
const double dr = r*EPS;
return (phi_rr(r+dr)-phi_rr(r)) / dr;
}
// Approximates the derivative of rho
double CbPotential::rho_r(const double &r) const
{
const double dr = r*EPS;
return (rho(r+dr)-rho(r)) / dr;
}
// Approximates the second derivative of rho
double CbPotential::rho_rr(const double &r) const
{
const double dr = r*EPS;
return (rho_r(r+dr)-rho_r(r)) / dr;
}
// Approximates the third derivative of rho
double CbPotential::rho_rrr(const double &r) const
{
const double dr = r*EPS;
return (rho_rr(r+dr)-rho_rr(r)) / dr;
}
// Approximates the derivative of the embedding function
double CbPotential::F_p(const double &p) const
{
const double dp = p*EPS;
return (F(p+dp)-F(p)) / dp;
}
// Approximates the second derivative of the embedding function
double CbPotential::F_pp(const double &p) const
{
const double dp = p*EPS;
return (F_p(p+dp)-F_p(p)) / dp;
}
// Approximates the third derivative of the embedding function
double CbPotential::F_ppp(const double &p) const
{
const double dp = p*EPS;
return (F_pp(p+dp)-F_pp(p)) / dp;
}
// Approximates the derivative of phi3.
double CbPotential::phi3_q (const double &q) const
{
const double dq = q*EPS;
return (phi3(q+dq)-phi3(q)) / dq;
}
// Approximates the second derivative of phi3.
double CbPotential::phi3_qq(const double &q) const
{
const double dq = q*EPS;
return (phi3_q(q+dq)-phi3_q(q)) / dq;
}
// Compute bond angle jik from the squared length of vectors ij,ik,kj.
double calculate_theta(double ij2, double ik2, double jk2)
{
return acos( 0.5*(ik2+ij2-jk2)/sqrt(ij2*ik2) );
}
// Initializes atomic interactions for up to three different terms.
Interactions::Interactions(int a, int b, int c)
{
// bitwise OR combines the terms that are listed
const int abc = a|b|c;
pairwise = (abc&PAIRWISE)>0;
embedding = (abc&EAM)>0;
three_body = (abc&THREE_BDY)>0;
angle_bending = (abc&ANGLE_BND)>0;
}
}

70
lib/atc/CbPotential.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef CBPOTENTIAL_H
#define CBPOTENTIAL_H
namespace ATC
{
//! Enumerated list of interaction types
enum Interaction{PAIRWISE=1, EAM=2, THREE_BDY=4, ANGLE_BND=8};
//! Booleans that enable types of terms the potential uses.
struct Interactions {
//! Enables up to 3 interaction types. (order independant)
Interactions(int a=0, int b=0, int c=0);
bool pairwise; //!< Pairwise interaction terms exist.
bool embedding; //!< Embedding interaction terms (EAM) exist.
bool three_body; //!< Three-body interaction terms exist.
bool angle_bending; //!< Angle bending interaction terms exist.
};
//! Compute bond angle jik from the squared length of vectors ij,ik,kj.
double calculate_theta(double ij2, double ik2, double jk2);
/**
* @class CbPotential
* @brief Base class for computing Cauchy-Born quantities for an interatomic potential material
* (assume all terms return 0)
*/
class CbPotential
{
protected:
//! CbPotential base constructor:
//! Initializes which terms are included in energy computation.
//@param potential_terms Switches for atomic interaction terms.
CbPotential(Interactions interaction_terms) : terms(interaction_terms) {}
public:
virtual ~CbPotential() {}
const Interactions terms; //!< switches for types of potential terms.
//! Returns the minimum distance that all interactions get neglected.
virtual double cutoff_radius() const=0;
//! @name Pairwise interaction term and derivatives.
//@{
virtual double phi (const double &r) const { return 0.0; }
virtual double phi_r (const double &r) const;
virtual double phi_rr (const double &r) const;
virtual double phi_rrr(const double &r) const;
//@}
//! @name Embedding terms. Electron cloud density and embedding functions
//@{
virtual double rho (const double &r) const { return 0.0; }
virtual double rho_r (const double &r) const;
virtual double rho_rr(const double &r) const;
virtual double rho_rrr(const double &r) const;
virtual double F (const double &p) const { return 0.0; }
virtual double F_p (const double &p) const;
virtual double F_pp(const double &p) const;
virtual double F_ppp(const double &p) const;
//@}
//! @name Three-body terms and derivatives
//@{
virtual double phi3 (const double &q) const {return 0.0; }
virtual double phi3_q (const double &q) const;
virtual double phi3_qq(const double &q) const;
//@}
};
}
#endif

705
lib/atc/ChargeRegulator.cpp Normal file
View File

@ -0,0 +1,705 @@
#include "ChargeRegulator.h"
#include "PoissonSolver.h"
#include "LammpsInterface.h"
#include "ATC_Coupling.h"
#include "ATC_Error.h"
#include "Function.h"
#include "PrescribedDataManager.h"
namespace ATC {
//========================================================
// Class ChargeRegulator
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ChargeRegulator::ChargeRegulator(ATC_Coupling * atc) :
AtomicRegulator(atc)
{
// do nothing
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ChargeRegulator::~ChargeRegulator()
{
map<string,ChargeRegulatorMethod *>::iterator it;
for (it = regulators_.begin(); it != regulators_.end(); it++) {
if (it->second) delete it->second;
}
}
//--------------------------------------------------------
// modify:
// parses and adjusts charge regulator state based on
// user input, in the style of LAMMPS user input
//--------------------------------------------------------
bool ChargeRegulator::modify(int narg, char **arg)
{
bool foundMatch = false;
return foundMatch;
}
//--------------------------------------------------------
// construct methods
//--------------------------------------------------------
void ChargeRegulator::construct_methods()
{
AtomicRegulator::construct_methods();
if (atc_->reset_methods()) {
// eliminate existing methods
delete_method();
// consruct new ones
map<string, ChargeRegulatorParameters>::iterator itr;
for (itr = parameters_.begin();
itr != parameters_.end(); itr++) {
string tag = itr->first;
if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag];
ChargeRegulatorParameters & p = itr->second;
LammpsInterface * lammpsInterface = LammpsInterface::instance();
p.groupBit = lammpsInterface->group_bit(tag);
if (! p.groupBit)
throw ATC_Error("ChargeRegulator::initialize group not found");
switch (p.method) {
case NONE: {
regulators_[tag] = new ChargeRegulatorMethod(this,p);
break;
}
case FEEDBACK: {
regulators_[tag] = new ChargeRegulatorMethodFeedback(this,p);
break;
}
case IMAGE_CHARGE: {
regulators_[tag] = new ChargeRegulatorMethodImageCharge(this,p);
break;
}
case EFFECTIVE_CHARGE: {
regulators_[tag] = new ChargeRegulatorMethodEffectiveCharge(this,p);
break;
}
default:
throw ATC_Error("ChargeRegulator::construct_method unknown charge regulator type");
}
}
}
}
//--------------------------------------------------------
// initialize:
//--------------------------------------------------------
void ChargeRegulator::initialize()
{
map<string, ChargeRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->initialize(); }
atc_->set_boundary_integration_type(boundaryIntegrationType_);
AtomicRegulator::reset_nlocal();
AtomicRegulator::delete_unused_data();
needReset_ = false;
}
//--------------------------------------------------------
// apply pre force
//--------------------------------------------------------
void ChargeRegulator::apply_pre_force(double dt)
{
map<string, ChargeRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->apply_pre_force(dt);}
}
//--------------------------------------------------------
// apply post force
//--------------------------------------------------------
void ChargeRegulator::apply_post_force(double dt)
{
map<string, ChargeRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->apply_post_force(dt);}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ChargeRegulator::output(OUTPUT_LIST & outputData)
{
map<string, ChargeRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->output(outputData);}
}
//========================================================
// Class ChargeRegulatorMethod
//========================================================
//--------------------------------------------------------
// Constructor
// Grab references to ATC and ChargeRegulator
//--------------------------------------------------------
ChargeRegulatorMethod::ChargeRegulatorMethod
(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & p)
: RegulatorShapeFunction(chargeRegulator),
chargeRegulator_(chargeRegulator),
lammpsInterface_(LammpsInterface::instance()),
rC_(0), rCsq_(0),
targetValue_(NULL),
targetPhi_(p.value),
surface_(p.faceset),
atomGroupBit_(p.groupBit),
boundary_(false),
depth_(p.depth),
surfaceType_(p.surfaceType),
permittivity_(p.permittivity),
initialized_(false)
{
const FE_Mesh * feMesh = atc_->fe_engine()->fe_mesh();
feMesh->faceset_to_nodeset(surface_,nodes_);
// assume flat get normal and primary coord
PAIR face = *(surface_.begin());
normal_.reset(nsd_);
feMesh->face_normal(face,0,normal_);
DENS_MAT faceCoords;
feMesh->face_coordinates(face,faceCoords);
point_.reset(nsd_);
for (int i=0; i < nsd_; i++) { point_(i) = faceCoords(i,0); }
#ifdef ATC_VERBOSE
stringstream ss; ss << "point: (" << point_(0) << "," << point_(1) << "," << point_(2) << ") normal: (" << normal_(0) << "," << normal_(1) << "," << normal_(2) << ") depth: " << depth_;
lammpsInterface_->print_msg_once(ss.str());
#endif
sum_.reset(nsd_);
}
//--------------------------------------------------------
// Initialize
//--------------------------------------------------------
// nomenclature might be a bit backwark: control --> nodes that exert the control, & influence --> atoms that feel the influence
void ChargeRegulatorMethod::initialize(void)
{
interscaleManager_ = &(atc_->interscale_manager());
poissonSolver_ =chargeRegulator_->poisson_solver();
if (! poissonSolver_) throw ATC_Error("need a poisson solver to initialize charge regulator");
// atomic vectors
// nodal information
nNodes_ = atc_->num_nodes();
// constants
rC_ = lammpsInterface_->pair_cutoff();
rCsq_ = rC_*rC_;
qV2e_ = lammpsInterface_->qv2e();
qqrd2e_ = lammpsInterface_->qqrd2e();
// note derived method set intialized to true
}
int ChargeRegulatorMethod::nlocal() { return atc_->nlocal(); }
void ChargeRegulatorMethod::set_greens_functions(void)
{
// set up Green's function per node
for (int i = 0; i < nNodes_; i++) {
set<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);
}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ChargeRegulatorMethod::output(OUTPUT_LIST & outputData)
{
//vector<double> localSum(sum_.size());
//lammpsInteface_->allsum(localSum.pointer,sum_.pointer,sum_.size());
DENS_VEC localSum(sum_.size());
lammpsInterface_->allsum(localSum.ptr(),sum_.ptr(),sum_.size());
for (int i = 0; i < sum_.size(); i++) {
string name = "charge_regulator_influence_"+to_string(i);
// atc_->fe_engine()->add_global(name,sum_[i]);
}
}
//========================================================
// Class ChargeRegulatorMethodFeedback
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ChargeRegulatorMethodFeedback::ChargeRegulatorMethodFeedback
(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & p)
: ChargeRegulatorMethod (chargeRegulator, p),
controlNodes_(nodes_),
influenceGroupBit_(p.groupBit)
{
nControlNodes_ = controlNodes_.size();
sum_.resize(1);
}
//--------------------------------------------------------
// Initialize
//--------------------------------------------------------
void ChargeRegulatorMethodFeedback::initialize(void)
{
ChargeRegulatorMethod::initialize();
if (surfaceType_ != ChargeRegulator::CONDUCTOR)
throw ATC_Error("currently charge feedback can only mimic a conductor");
set_influence();
set_influence_matrix();
initialized_ = true;
}
//--------------------------------------------------------
// find measurement atoms and nodes
//--------------------------------------------------------
void ChargeRegulatorMethodFeedback::set_influence(void)
{
// get nodes that overlap influence atoms & compact list of influence atoms
boundary_ =
atc_->nodal_influence(influenceGroupBit_,influenceNodes_,influenceAtoms_);
nInfluenceAtoms_ = influenceAtoms_.size(); // local
nInfluenceNodes_ = influenceNodes_.size(); // global
stringstream ss; ss << "control nodes: " << nControlNodes_ << " influence nodes: " << nInfluenceNodes_ << " local influence atoms: " << nInfluenceAtoms_ ;
lammpsInterface_->print_msg(ss.str());
if (nInfluenceNodes_ == 0) throw ATC_Error("no influence nodes");
const Array<int> & map = (boundary_) ? atc_->ghost_to_atom_map() : atc_->internal_to_atom_map();
for (set<int>::const_iterator itr = influenceAtoms_.begin(); itr != influenceAtoms_.end(); itr++) {
influenceAtomsIds_.insert(map(*itr));
}
}
//--------------------------------------------------------
// constuct a Green's submatrix
//--------------------------------------------------------
void ChargeRegulatorMethodFeedback::set_influence_matrix(void)
{
// construct control-influence matrix bar{G}^-1: ds{p} = G{p,m}^-1 dphi{m}
//
if (nInfluenceNodes_ < nControlNodes_) throw ATC_Error(" least square not implmented ");
if (nInfluenceNodes_ > nControlNodes_) throw ATC_Error(" solve not possible ");
DENS_MAT G(nInfluenceNodes_,nControlNodes_);
DENS_VEC G_I;
set<int>::const_iterator itr,itr2,itr3;
const Array<int> & nmap = atc_->fe_engine()->fe_mesh()->global_to_unique_map();
int i = 0;
for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) {
poissonSolver_->greens_function(*itr, G_I);
int j = 0;
for (itr2 = controlNodes_.begin(); itr2 != controlNodes_.end(); itr2++) {
int jnode = nmap(*itr2);
G(i,j++) = G_I(jnode);
}
i++;
}
invG_ = inv(G);
// construct the prolong-restrict projector N N^T for influence nodes only
InterscaleManager & interscaleManager(atc_->interscale_manager());
const SPAR_MAT & N_Ia = (boundary_) ?
(interscaleManager.per_atom_sparse_matrix("InterpolantGhost"))->quantity():
(interscaleManager.per_atom_sparse_matrix("Interpolant"))->quantity();
NT_.reset(nInfluenceAtoms_,nInfluenceNodes_);
DENS_MAT NNT(nInfluenceNodes_,nInfluenceNodes_);
int k = 0;
for (itr3 = influenceAtoms_.begin(); itr3 != influenceAtoms_.end(); itr3++) {
int katom = *itr3;
int i = 0;
for (itr = influenceNodes_.begin(); itr != influenceNodes_.end(); itr++) {
int Inode = *itr;
int j = 0;
NT_(k,i) = N_Ia(katom,Inode);
for (itr2 = influenceNodes_.begin(); itr2 != influenceNodes_.end(); itr2++) {
int Jnode = *itr2;
NNT(i,j++) += N_Ia(katom,Inode)*N_Ia(katom,Jnode);
}
i++;
}
k++;
}
// swap contributions across processors
DENS_MAT localNNT = NNT;
int count = NNT.nRows()*NNT.nCols();
lammpsInterface_->allsum(localNNT.ptr(),NNT.ptr(),count);
invNNT_ = inv(NNT);
// total influence matrix
if (nInfluenceAtoms_ > 0) { NTinvNNTinvG_ = NT_*invNNT_*invG_; }
}
//--------------------------------------------------------
// change potential/charge pre-force calculation
//--------------------------------------------------------
void ChargeRegulatorMethodFeedback::apply_pre_force(double dt)
{
sum_ = 0;
if (nInfluenceAtoms_ == 0) return; // nothing to do
apply_feedback_charges();
}
//--------------------------------------------------------
// apply feedback charges to atoms
//--------------------------------------------------------
void ChargeRegulatorMethodFeedback::apply_feedback_charges()
{
double * q = lammpsInterface_->atom_charge();
// calculate error in potential on the control nodes
const DENS_MAT & phiField = (atc_->field(ELECTRIC_POTENTIAL)).quantity();
DENS_MAT dphi(nControlNodes_,1);
int i = 0;
set<int>::const_iterator itr;
for (itr = controlNodes_.begin(); itr != controlNodes_.end(); itr++) {
dphi(i++,0) = targetPhi_ - phiField(*itr,0);
}
// construct the atomic charges consistent with the correction
DENS_MAT dq = NTinvNNTinvG_*dphi;
i = 0;
for (itr = influenceAtomsIds_.begin(); itr != influenceAtomsIds_.end(); itr++) {
sum_(0) += dq(i,0);
q[*itr] += dq(i++,0);
}
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE))->force_reset();
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE, GHOST))->force_reset();
}
//========================================================
// Class ChargeRegulatorMethodImageCharge
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ChargeRegulatorMethodImageCharge::ChargeRegulatorMethodImageCharge
(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & p)
: ChargeRegulatorMethod (chargeRegulator, p),
imageNodes_(nodes_)
{
}
//--------------------------------------------------------
// Initialize
//--------------------------------------------------------
void ChargeRegulatorMethodImageCharge::initialize(void)
{
ChargeRegulatorMethod::initialize();
if (surfaceType_ != ChargeRegulator::DIELECTRIC) throw ATC_Error("currently image charge can only mimic a dielectric");
double eps1 = permittivity_;// dielectric
double eps2 = lammpsInterface_->dielectric();// ambient
permittivityRatio_ = (eps2-eps1)/(eps2+eps1);
#ifdef ATC_VERBOSE
stringstream ss; ss << "permittivity ratio: " << permittivityRatio_;
lammpsInterface_->print_msg_once(ss.str());
#endif
set_greens_functions();
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
initialized_ = true;
}
//--------------------------------------------------------
// change potential/charge post-force calculation
//--------------------------------------------------------
void ChargeRegulatorMethodImageCharge::apply_post_force(double dt)
{
sum_ = 0;
apply_local_forces();
//correct_forces();
}
//--------------------------------------------------------
// apply local coulomb forces
// -- due to image charges
//--------------------------------------------------------
void ChargeRegulatorMethodImageCharge::apply_local_forces()
{
int inum = lammpsInterface_->neighbor_list_inum();
int * ilist = lammpsInterface_->neighbor_list_ilist();
int * numneigh = lammpsInterface_->neighbor_list_numneigh();
int ** firstneigh = lammpsInterface_->neighbor_list_firstneigh();
const int *mask = lammpsInterface_->atom_mask();
///..............................................
double ** x = lammpsInterface_->xatom();
double ** f = lammpsInterface_->fatom();
double * q = lammpsInterface_->atom_charge();
// loop over neighbor list
for (int ii = 0; ii < inum; ii++) {
int i = ilist[ii];
double qi = q[i];
if ((mask[i] & atomGroupBit_) && qi != 0.) {
double* fi = f[i];
DENS_VEC xi(x[i],nsd_);
// distance to surface
double dn = reflect(xi);
// all ions near the interface/wall
// (a) self image
if (dn < rC_) { // close enough to wall to have explicit image charges
double factor_coul = 1;
double dx = 2.*dn; // distance to image charge
double fn = factor_coul*qi*qi*permittivityRatio_/dx;
fi[0] += fn*normal_[0];
fi[1] += fn*normal_[1];
fi[2] += fn*normal_[2];
sum_ += fn*normal_;
// (b) neighbor images
int * jlist = firstneigh[i];
int jnum = numneigh[i];
for (int jj = 0; jj < jnum; jj++) {
int j = jlist[jj];
// this changes j
double factor_coul = lammpsInterface_->coulomb_factor(j);
double qj = q[j];
if (qj != 0.) { // all charged neighbors
DENS_VEC xj(x[j],nsd_);
dn = reflect(xj);
DENS_VEC dx = xi-xj;
double r2 = dx.norm_sq();
// neighbor image j' inside cutoff from i
if (r2 < rCsq_) {
double fm = factor_coul*qi*qj*permittivityRatio_/r2;
fi[0] += fm*dx(0);
fi[1] += fm*dx(1);
fi[2] += fm*dx(2);
sum_ += fm*dx;
}
}
}
} // end i < rC if
}
}
// update managed data
(interscaleManager_->fundamental_atom_quantity(LammpsInterface::ATOM_FORCE))->force_reset();
}
//--------------------------------------------------------
// correct charge densities
// - to reflect image charges
//--------------------------------------------------------
void ChargeRegulatorMethodImageCharge::correct_charge_densities()
{
}
//--------------------------------------------------------
// correct_forces
// - due to image charge density used in short-range solution
//--------------------------------------------------------
void ChargeRegulatorMethodImageCharge::correct_forces()
{
}
//========================================================
// Class ChargeRegulatorMethodEffectiveCharge
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ChargeRegulatorMethodEffectiveCharge::ChargeRegulatorMethodEffectiveCharge(
ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & p)
: ChargeRegulatorMethod (chargeRegulator, p),
chargeDensity_(p.value),
useSlab_(false)
{
}
//--------------------------------------------------------
// add_charged_surface
//--------------------------------------------------------
void ChargeRegulatorMethodEffectiveCharge::initialize( )
{
ChargeRegulatorMethod::initialize();
boundary_ = atc_->is_ghost_group(atomGroupBit_);
// set face sources to all point at unit function for use in integration
SURFACE_SOURCE faceSources;
map<PAIR, Array<XT_Function*> > & fs(faceSources[ELECTRIC_POTENTIAL]);
XT_Function * f = XT_Function_Mgr::instance()->constant_function(1.);
set< PAIR >::const_iterator fsItr;
for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) {
Array < XT_Function * > & dof = fs[*fsItr];
dof.reset(1);
dof(0) = f;
}
// computed integrals of nodal shape functions on face
FIELDS nodalFaceWeights;
Array<bool> fieldMask(NUM_FIELDS); fieldMask(ELECTRIC_POTENTIAL) = true;
(atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,nodalFaceWeights);
const DENS_MAT & w = (nodalFaceWeights[ELECTRIC_POTENTIAL].quantity());
// Get coordinates of each node in face set
for (set<int>::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) {
DENS_VEC x = atc_->fe_engine()->fe_mesh()->nodal_coordinates(*n);
// compute effective charge at each node I
// multiply charge density by integral of N_I over face
double v = w(*n,0)*chargeDensity_;
pair<DENS_VEC,double> p(x,v);
nodeXFMap_[*n] = p;
}
// set up data structure holding charged faceset information
FIELDS sources;
double k = lammpsInterface_->coulomb_constant();
string fname = "radial_power";
double xtArgs[8];
xtArgs[0] = 0; xtArgs[1] = 0; xtArgs[2] = 0;
xtArgs[3] = 1; xtArgs[4] = 1; xtArgs[5] = 1;
xtArgs[6] = k*chargeDensity_;
xtArgs[7] = -1.;
const DENS_MAT & s(sources[ELECTRIC_POTENTIAL].quantity());
NODE_TO_XF_MAP::iterator XFitr;
for (XFitr = nodeXFMap_.begin(); XFitr != nodeXFMap_.end(); XFitr++) {
// evaluate voltage at each node I
// set up X_T function for integration: k*chargeDensity_/||x_I - x_s||
// integral is approximated in two parts:
// 1) near part with all faces within r < rcrit evaluated as 2 * pi * rcrit * k sigma A/A0, A is area of this region and A0 = pi * rcrit^2, so 2 k sigma A / rcrit
// 2) far part evaluated using Gaussian quadrature on faceset
DENS_VEC x((XFitr->second).first);
xtArgs[0] = x(0); xtArgs[1] = x(1); xtArgs[2] = x(2);
f = XT_Function_Mgr::instance()->function(fname,8,xtArgs);
for (fsItr = surface_.begin(); fsItr != surface_.end(); fsItr++) {
fs[*fsItr] = f;
}
// perform integration to get quantities at nodes on facesets
// V_J' = int_S N_J k*sigma/|x_I - x_s| dS
(atc_->fe_engine())->compute_fluxes(fieldMask,0.,faceSources,sources);
// sum over all nodes in faceset to get total potential:
// V_I = sum_J VJ'
int node = XFitr->first;
nodalChargePotential_[node] = s(node,0);
double totalPotential = 0.;
for (set<int>::const_iterator n =nodes_.begin(); n != nodes_.end(); n++) {
totalPotential += s(*n,0); }
// assign an XT function per each node and
// then call the prescribed data manager and fix each node individually.
f = XT_Function_Mgr::instance()->constant_function(totalPotential);
(atc_->prescribed_data_manager())->fix_field(node,ELECTRIC_POTENTIAL,0,f);
}
initialized_ = true;
}
//--------------------------------------------------------
// add effective forces post LAMMPS force call
//--------------------------------------------------------
void ChargeRegulatorMethodEffectiveCharge::apply_post_force(double dt)
{
apply_local_forces();
}
//--------------------------------------------------------
// apply_charged_surfaces
//--------------------------------------------------------
void ChargeRegulatorMethodEffectiveCharge::apply_local_forces()
{
double * q = lammpsInterface_->atom_charge();
_atomElectricalForce_.resize(nlocal(),nsd_);
double penalty = poissonSolver_->penalty_coefficient();
if (penalty <= 0.0) throw ATC_Error("ExtrinsicModelElectrostatic::apply_charged_surfaces expecting non zero penalty");
double dx[3];
const DENS_MAT & xa((interscaleManager_->per_atom_quantity("AtomicCoarseGrainingPositions"))->quantity());
// WORKSPACE - most are static
SparseVector<double> dv(nNodes_);
vector<SparseVector<double> > derivativeVectors;
derivativeVectors.reserve(nsd_);
const SPAR_MAT_VEC & shapeFunctionDerivatives((interscaleManager_->vector_sparse_matrix("InterpolateGradient"))->quantity());
DenseVector<INDEX> nodeIndices;
DENS_VEC nodeValues;
NODE_TO_XF_MAP::const_iterator inode;
for (inode = nodeXFMap_.begin(); inode != nodeXFMap_.end(); inode++) {
int node = inode->first;
DENS_VEC xI = (inode->second).first;
double qI = (inode->second).second;
double phiI = nodalChargePotential_[node];
for (int i = 0; i < nlocal(); i++) {
int atom = (atc_->internal_to_atom_map())(i);
double qa = q[atom];
if (qa != 0) {
double dxSq = 0.;
for (int j = 0; j < nsd_; j++) {
dx[j] = xa(i,j) - xI(j);
dxSq += dx[j]*dx[j];
}
if (dxSq < rCsq_) {
// first apply pairwise coulombic interaction
if (!useSlab_) {
double coulForce = qqrd2e_*qI*qa/(dxSq*sqrtf(dxSq));
for (int j = 0; j < nsd_; j++) {
_atomElectricalForce_(i,j) += dx[j]*coulForce; }
}
// second correct for FE potential induced by BCs
// determine shape function derivatives at atomic location
// and construct sparse vectors to store derivative data
for (int j = 0; j < nsd_; j++) {
shapeFunctionDerivatives[j]->row(i,nodeValues,nodeIndices);
derivativeVectors.push_back(dv);
for (int k = 0; k < nodeIndices.size(); k++) {
derivativeVectors[j](nodeIndices(k)) = nodeValues(k); }
}
// compute greens function from charge quadrature
SparseVector<double> shortFePotential(nNodes_);
shortFePotential.add_scaled(greensFunctions_[node],penalty*phiI);
// compute electric field induced by charge
DENS_VEC efield(nsd_);
for (int j = 0; j < nsd_; j++) {
efield(j) = -.1*dot(derivativeVectors[j],shortFePotential); }
// apply correction in atomic forces
double c = qV2e_*qa;
for (int j = 0; j < nsd_; j++) {
if ((!useSlab_) || (j==nsd_)) {
_atomElectricalForce_(i,j) -= c*efield(j);
}
}
}
}
}
}
}
}; // end namespace

281
lib/atc/ChargeRegulator.h Normal file
View File

@ -0,0 +1,281 @@
#ifndef CHARGE_REGULATOR_H
#define CHARGE_REGULATOR_H
// ATC headers
#include "AtomicRegulator.h"
//class PoissonSolver;
#include "PoissonSolver.h"
// other headers
#include <map>
namespace ATC {
/**
* @class ChargeRegulator
* @brief Manager class for atom-continuum control of charge and potential
*/
class ChargeRegulatorMethod;
class ChargeRegulator : public AtomicRegulator {
public:
/** charge regulator types */
enum ChargeRegulatorType {
NONE=0,
FEEDBACK,
IMAGE_CHARGE,
EFFECTIVE_CHARGE
};
enum ChargedSurfaceType {
INSULATOR=0,
DIELECTRIC,
CONDUCTOR
};
/** parser data */
struct ChargeRegulatorParameters {
ChargeRegulatorParameters():
method(NONE),
value(0),
groupBit(0),
depth(0),
permittivity(0),
surfaceType(INSULATOR) { };
ChargeRegulatorType method;
double value;
int groupBit;
string groupTag;
double depth;
double permittivity;
ChargedSurfaceType surfaceType;
FSET faceset;
};
/** constructor */
ChargeRegulator(ATC_Coupling *atc);
/** destructor */
~ChargeRegulator();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
virtual void construct_methods();
virtual void initialize();
virtual void apply_pre_force(double dt);
virtual void apply_post_force(double dt);
virtual void output(OUTPUT_LIST & outputData);
virtual double compute_vector(int n) {return 0;} // TODO
void assign_poisson_solver(PoissonSolver * solver) { poissonSolver_ = solver;}
PoissonSolver * poisson_solver(void) { return poissonSolver_;}
protected:
/** poisson solver */
PoissonSolver * poissonSolver_;
/** registry charge regulators */
map<string,ChargeRegulatorMethod *> regulators_;
map<string,ChargeRegulatorParameters> parameters_;
private:
ChargeRegulator(); // DO NOT define this
};
/**
* @class ChargeRegulatorMethod
* @brief Base class for implementation of ChargeRegulator algorithms
*/
class ChargeRegulatorMethod : public RegulatorShapeFunction {
public:
ChargeRegulatorMethod(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & parameters);
~ChargeRegulatorMethod(){};
virtual void initialize(void);
void set_greens_functions();
virtual void apply_pre_force(double dt){};
virtual void apply_post_force(double dt){};
virtual void set_weights() {};
const DENS_VEC & total_influence() const { return sum_;}
virtual void output(OUTPUT_LIST & outputData);
protected:
int nlocal();
/** pointer to charge regulator object for data */
ChargeRegulator * chargeRegulator_;
/** interscale manager */
class InterscaleManager * interscaleManager_;
/** direct interface to lammps */
class LammpsInterface * lammpsInterface_;
/** poisson solver */
PoissonSolver * poissonSolver_;
/** (subset) of Green's functions */
vector<SparseVector<double> > greensFunctions_;
/** cutoff radius */
double rC_,rCsq_;
/** conversion constants */
double qV2e_, qqrd2e_;
/** member data */
XT_Function * targetValue_;
double targetPhi_;
// controlling
FSET surface_;
NSET nodes_;
int atomGroupBit_;
bool boundary_; // atoms on boundary
DENS_VEC point_;
DENS_VEC normal_;
double depth_;
ChargeRegulator::ChargedSurfaceType surfaceType_;
double permittivity_;
bool initialized_;
DENS_VEC sum_;
/** workspace */
mutable DENS_MAT _atomElectricalForce_;
private:
ChargeRegulatorMethod(); // DO NOT define this
};
/**
* @class ChargeRegulatorMethodFeedback
* @brief ChargeRegulator with feedback and ad hoc positions
* can be used for conductor (1 charged sheet) or insulator (2 sheets)
*/
class ChargeRegulatorMethodFeedback : public ChargeRegulatorMethod {
public:
/** constructor */
ChargeRegulatorMethodFeedback(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & parameters);
/** destructor */
~ChargeRegulatorMethodFeedback(){};
/** initialize */
virtual void initialize(void);
/** set influence nodes and atoms */
void set_influence();
void set_influence_matrix(void);
/** post first verlet step */
virtual void apply_pre_force(double dt);
void apply_feedback_charges();
protected:
int nControlNodes_;
NSET & controlNodes_;
// measurement/controlled
int influenceGroupBit_;
int nInfluenceAtoms_;
NSET influenceAtoms_, influenceAtomsIds_;
int nInfluenceNodes_;
NSET influenceNodes_;
DENS_MAT invG_;
DENS_MAT invNNT_;
DENS_MAT NT_;
DENS_MAT NTinvNNTinvG_;
private:
ChargeRegulatorMethodFeedback(); // DO NOT define this
};
/**
* @class ChargeRegulatorMethodImageCharge
* @brief ChargeRegulator with image charges
*/
class ChargeRegulatorMethodImageCharge : public ChargeRegulatorMethod {
public:
/** constructor */
ChargeRegulatorMethodImageCharge(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & parameters);
/** destructor */
~ChargeRegulatorMethodImageCharge(){};
/** initialize */
virtual void initialize(void);
/** post first verlet step */
virtual void apply_post_force(double dt);
protected:
double reflect(DENS_VEC & x) const {
double dn = (x-point_).dot(normal_);
x -= 2*dn*normal_;
return dn;
}
// internal functions
void apply_local_forces();
void correct_charge_densities();
void correct_forces();
double layerDepth_;
double permittivityRatio_;
NSET & imageNodes_;
DENS_MAT imageTransferOp_;
private:
ChargeRegulatorMethodImageCharge(); // DO NOT define this
};
/**
* @class ChargeRegulatorMethodEffectiveCharge
* @brief ChargeRegulator with effective charges at FE quadrature pts
*/
class ChargeRegulatorMethodEffectiveCharge : public ChargeRegulatorMethod {
typedef map<int,pair<DENS_VEC,double> > NODE_TO_XF_MAP;
public:
/** constructor */
ChargeRegulatorMethodEffectiveCharge(ChargeRegulator *chargeRegulator,
ChargeRegulator::ChargeRegulatorParameters & parameters);
/** destructor */
~ChargeRegulatorMethodEffectiveCharge(){};
/** initialize */
virtual void initialize(void);
/** post first verlet step */
virtual void apply_post_force(double dt);
protected:
// internal functions
void apply_local_forces();
// member data
double chargeDensity_;
map<int,double> nodalChargePotential_;
NODE_TO_XF_MAP nodeXFMap_;
bool useSlab_;
private:
ChargeRegulatorMethodEffectiveCharge(); // DO NOT define this
};
};
#endif

253
lib/atc/CloneVector.h Normal file
View File

@ -0,0 +1,253 @@
#ifndef CLONEVECTOR_H
#define CLONEVECTOR_H
#include "Vector.h"
namespace ATC_matrix {
/**
* @class CloneVector
* @brief Class for creating objects that wrap matrix data for manipulation through vector operations
*/
template<typename T>
class CloneVector : public Vector<T>
{
public:
CloneVector(); // do not implement
CloneVector(const Vector<T> &c);
CloneVector(const Matrix<T> &c, int dim, INDEX idx=0);
CloneVector(const DiagonalMatrix<T> &c, INDEX idx=0);
// overloaded virtual functions
T& operator[](INDEX i);
T operator[](INDEX i) const;
T operator()(INDEX i, INDEX j=0) const;
T& operator()(INDEX i, INDEX j=0);
INDEX nRows() const;
CloneVector<T>& operator=(const T &v);
CloneVector<T>& operator=(const CloneVector<T> &C);
CloneVector<T>& operator=(const Matrix<T> &C);
virtual bool memory_contiguous() const;
T* ptr() const;
void resize(INDEX nRows, INDEX nCols=0, bool copy=false);
void reset(INDEX nRows, INDEX nCols=0, bool zero=true);
void copy(const T * ptr, INDEX nRows, INDEX nCols=0);
private:
void _resize(INDEX nRows, INDEX nCols, bool copy, bool zero);
Vector<T> * const _baseV; // ptr to a base vector
Matrix<T> * const _baseM; // ptr to a base matrix
int _clone_type; // what to clone (see enum CLONE_TYPE)
INDEX _idx; // index of matrix dimension to clone
};
///////////////////////////////////////////////////////////////////////////////
// Template definitions ///////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// Construct from another vector
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>::CloneVector(const Vector<T> &c)
: Vector<T>(), _baseV(const_cast<Vector<T>*>(&c)), _baseM(NULL)
{}
//-----------------------------------------------------------------------------
// Construct from a matrix, the const_cast isn't pretty
/* CloneVector(const Matrix<T> &c, int dim, INDEX idx)
/ attaches to a slice of a matrix
/ Arguments: c = pointer to the matrix
/ dim = type of slice CLONE_ROW, CLONE_COL, CLONE_DIAG
/ idx = index of row or column (no effect on diag currently)
*/
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>::CloneVector(const Matrix<T> &c, int dim, INDEX idx)
: Vector<T>(), _baseV(NULL), _baseM(const_cast<Matrix<T>*>(&c))
, _clone_type(dim), _idx(idx)
{}
//-----------------------------------------------------------------------------
// Construct from a DiagonalMatrix
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>::CloneVector(const DiagonalMatrix<T> &c, INDEX idx)
: Vector<T>(), _baseV(NULL), _baseM(const_cast<DiagonalMatrix<T>*>(&c))
, _clone_type(CLONE_DIAG), _idx(0)
{}
//-----------------------------------------------------------------------------
// value (const) indexing operator
//-----------------------------------------------------------------------------
template<typename T>
T CloneVector<T>::operator()(INDEX i, INDEX j) const
{
return (*this)[i];
}
//-----------------------------------------------------------------------------
// reference index operator
//-----------------------------------------------------------------------------
template<typename T>
T& CloneVector<T>::operator()(INDEX i, INDEX j)
{
return (*this)[i];
}
//-----------------------------------------------------------------------------
// Indexes the cloned vector either from another vector or a matrix
//-----------------------------------------------------------------------------
template<typename T>
T CloneVector<T>::operator[](INDEX i) const
{
if (_baseV) return (*_baseV)(i);
if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i);
else if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx);
else if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i);
return 0;
}
//-----------------------------------------------------------------------------
// Indexes the cloned vector either from another vector or a matrix
//-----------------------------------------------------------------------------
template<typename T>
T& CloneVector<T>::operator[](INDEX i)
{
if (_baseV) return (*_baseV)(i);
if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i);
if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx);
if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i);
return (*_baseV)(i);
}
//-----------------------------------------------------------------------------
// Returns the size of the base vector or of the row/col of the base matrix
//-----------------------------------------------------------------------------
template<typename T>
INDEX CloneVector<T>::nRows() const
{
using std::min;
if (_baseV) return _baseV->size();
if (_clone_type == CLONE_ROW) return _baseM->nCols();
if (_clone_type == CLONE_COL) return _baseM->nRows();
if (_clone_type == CLONE_DIAG) return min(_baseM->nRows(), _baseM->nCols());
return 0;
}
//-----------------------------------------------------------------------------
// assigns all elements to a constant
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>& CloneVector<T>::operator=(const T &v)
{
this->set_all_elements_to(v);
return *this;
}
//-----------------------------------------------------------------------------
// assigns all elements to the corresponding elements in C
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>& CloneVector<T>::operator=(const CloneVector<T> &C)
{
GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator=");
int sz = this->size();
for (INDEX i = 0; i < sz; i++) (*this)[i] = C[i];
return *this;
}
//-----------------------------------------------------------------------------
// assigns all elements to the corresponding elements in C
//-----------------------------------------------------------------------------
template<typename T>
CloneVector<T>& CloneVector<T>::operator=(const Matrix<T> &C)
{
GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator=");
int sz = this->size();
for (INDEX i = 0; i < sz; i++) (*this)[i] = C[i];
return *this;
}
//-----------------------------------------------------------------------------
// returns true only if its guaranteed memory is contiguous
//-----------------------------------------------------------------------------
template<typename T>
bool CloneVector<T>::memory_contiguous() const
{
// drill down through clone of clones
if (_baseV) return _baseV->memory_contiguous();
// could be okay if DiagonalMatrix, but can't guarantee this
if (_clone_type == CLONE_DIAG) return false;
#ifdef ROW_STORAGE
return _clone_type == CLONE_ROW;
#else
return _clone_type == CLONE_COL;
#endif
}
//-----------------------------------------------------------------------------
// Returns a pointer to the data unless the data is a column of a matrix
//-----------------------------------------------------------------------------
template<typename T>
T* CloneVector<T>::ptr() const
{
if (_baseV) return _baseV->ptr();
#ifdef ROW_STORAGE
if (_clone_type == CLONE_ROW) return _baseM->ptr() + this->size()*_idx;
if (_clone_type == CLONE_COL) return _baseM->ptr() + this->size();
if (_clone_type == CLONE_DIAG) return _baseM->ptr();
#else
if (_clone_type == CLONE_COL) return _baseM->ptr() + this->size()*_idx;
if (_clone_type == CLONE_ROW) return _baseM->ptr() + this->size();
if (_clone_type == CLONE_DIAG) return _baseM->ptr();
#endif
return 0;
}
//-----------------------------------------------------------------------------
// general resize function, can handle parents that are matrices or vectors
//-----------------------------------------------------------------------------
template<typename T>
void CloneVector<T>::_resize(INDEX nRows, INDEX nCols, bool copy, bool zero)
{
if (_baseV)
{
if (copy) _baseV->resize(nRows, nCols, copy);
else _baseV->reset (nRows, nCols, zero);
return;
}
// parent is a matrix, need to decide what the Vector is cloning
switch (_clone_type)
{
case CLONE_ROW: // now the leading dimension is rows
nCols = nCols ? nCols : _baseM->nCols();
break;
case CLONE_COL: // now the leading dimension is columns
nCols = nCols ? nCols : _baseM->nRows();
ATC_Utility::swap(nRows, nCols);
break;
case CLONE_DIAG: // lets just hope you knew what you were doing
break;
default:
return;
}
if (zero) _baseM->reset(nRows, nCols, zero); // zero overrides copy
else _baseM->resize(nRows, nCols, copy);
}
//-----------------------------------------------------------------------------
// resizes the matrix and optionally copies what fits
//-----------------------------------------------------------------------------
template<typename T>
void CloneVector<T>::resize(INDEX nRows, INDEX nCols, bool copy)
{
_resize(nRows, nCols, copy, false);
}
//-----------------------------------------------------------------------------
// resizes the matrix and optionally zeros it out
//-----------------------------------------------------------------------------
template<typename T>
void CloneVector<T>::reset(INDEX nRows, INDEX nCols, bool zero)
{
_resize(nRows, nCols, false, zero);
}
//-----------------------------------------------------------------------------
// resizes the matrix and copies data
//-----------------------------------------------------------------------------
template<typename T>
void CloneVector<T>::copy(const T * ptr, INDEX nRows, INDEX nCols)
{
_resize(nRows, nCols, false, false);
memcpy(this->ptr(), ptr, this->size()*sizeof(T));
}
} // end namespace
#endif

View File

@ -0,0 +1,617 @@
#include "ConcentrationRegulator.h"
#include "LammpsInterface.h"
#include "ATC_Coupling.h"
#include "ATC_Error.h"
namespace ATC {
const double kMinScale_ = 10000.;
//========================================================
// Class ConcentrationRegulator
//========================================================
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ConcentrationRegulator::ConcentrationRegulator(ATC_Coupling * atc) :
AtomicRegulator(atc)
{
// do nothing
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ConcentrationRegulator::~ConcentrationRegulator()
{
if (regulators_.size()) {
map<string,ConcentrationRegulatorMethod *>::iterator it;
for (it = regulators_.begin(); it != regulators_.end(); ++it) {
delete it->second;
}
regulators_.clear();
}
if (parameters_.size()) parameters_.clear();
}
//--------------------------------------------------------
// modify:
// parses and adjusts charge regulator state based on
// user input, in the style of LAMMPS user input
//--------------------------------------------------------
bool ConcentrationRegulator::modify(int narg, char **arg)
{
bool foundMatch = false;
return foundMatch;
}
//--------------------------------------------------------
// construct_methods:
//--------------------------------------------------------
void ConcentrationRegulator::construct_methods()
{
AtomicRegulator::construct_methods();
if (atc_->reset_methods()) {
// eliminate existing methods
delete_method();
// consruct new ones
map<string, ConcentrationRegulatorParameters>::iterator itr;
for (itr = parameters_.begin();
itr != parameters_.end(); itr++) {
string tag = itr->first;
if (regulators_.find(tag) != regulators_.end()) delete regulators_[tag];
ConcentrationRegulatorParameters & p = itr->second;
switch (p.method) {
case NONE: {
regulators_[tag] = new ConcentrationRegulatorMethod(this);
break;
}
case TRANSITION: {
p.type = atc_->tag_to_type(tag);
p.groupbit = LammpsInterface::instance()->type_to_groupbit(p.type);
p.transitionType = atc_->tag_to_type(p.transitionTag);
regulators_[tag] = new ConcentrationRegulatorMethodTransition(this,p);
break;
}
default:
throw ATC_Error("ConcentrationRegulator::initialize unknown concentration regulator type");
}
}
}
}
//--------------------------------------------------------
// initialize:
//--------------------------------------------------------
void ConcentrationRegulator::initialize()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->initialize(); }
atc_->set_boundary_integration_type(boundaryIntegrationType_);
AtomicRegulator::reset_nlocal();
AtomicRegulator::delete_unused_data();
needReset_ = false;
}
//--------------------------------------------------------
// pre_exchange
//--------------------------------------------------------
void ConcentrationRegulator::pre_exchange()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->pre_exchange();}
}
//--------------------------------------------------------
// pre_force
//--------------------------------------------------------
void ConcentrationRegulator::pre_force()
{
map<string, ConcentrationRegulatorMethod *>::iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->pre_force();}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ConcentrationRegulator::output(OUTPUT_LIST & outputData) const
{
map<string, ConcentrationRegulatorMethod *>::const_iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) { itr->second->output(outputData);}
}
//--------------------------------------------------------
// compute vector
//--------------------------------------------------------
double ConcentrationRegulator::compute_vector(int n) const
{
int s = regulators_.size();
if (s == 0) return 0;
int m = n / s;
n = n % s;
int c = 0;
map<string, ConcentrationRegulatorMethod *>::const_iterator itr;
for (itr = regulators_.begin();
itr != regulators_.end(); itr++) {
if (c++ == n) { return itr->second->compute_vector(m); }
}
return 0.;
}
//--------------------------------------------------------
// size vector
//--------------------------------------------------------
int ConcentrationRegulator::size_vector(int i) const
{
int n = (regulators_.size())*5;
if (n==0) n = 20;
return n;
}
//========================================================
// Class ConcentrationRegulatorMethodTransition
//========================================================
//--------------------------------------------------------
// Constructor
// Grab references to ATC and ConcentrationRegulator
//--------------------------------------------------------
ConcentrationRegulatorMethodTransition::ConcentrationRegulatorMethodTransition
(ConcentrationRegulator *concReg,
ConcentrationRegulator::ConcentrationRegulatorParameters & p)
: ConcentrationRegulatorMethod(concReg),
concentrationRegulator_(concReg),
interscaleManager_(NULL),
lammpsInterface_(LammpsInterface::instance()),
list_(NULL),
targetConcentration_(p.value),
targetCount_(0),
elemset_(p.elemset),
p_(NULL),
randomNumberGenerator_(NULL),
q0_(0),
controlType_(p.type),
controlIndex_(0),
transitionType_(p.transitionType),
transitionInterval_(p.transitionInterval),
transitionCounter_(0),
nInTransition_(0),
transitionFactor_(0),
controlMask_(p.groupbit),
frequency_(p.frequency),
maxEnergy_(p.maxEnergy),
maxExchanges_(p.maxExchanges),
maxAttempts_(p.maxAttempts),
nexchanges_(0),
initialized_(false),
_rngUniformCounter_(0),
_rngNormalCounter_(0)
{
controlIndex_ = atc_->type_index(controlType_);
LammpsInterface * li = LammpsInterface::instance();
q0_ = li->type_to_charge(controlType_);
double kB = (li->boltz())/(li->mvv2e()); // E/T*m*v^2/E = m v^2/T
double m = li->atom_mass(controlType_);
sigma_ = sqrt(kB/m); // v / sqrt(T)
randomNumberGenerator_ = li->random_number_generator();
}
//--------------------------------------------------------
// Initialize
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::initialize(void)
{
#ifdef ATC_VERBOSE
lammpsInterface_->print_msg_once(
"\ncontrol type: "+to_string(controlType_)+
"\ntransistion type:"+to_string(transitionType_)+
"\ncontrol mask:"+to_string(controlMask_)+
"\nfrequency:"+to_string(frequency_)+
"\nmax exchanges:"+to_string(maxExchanges_)+
"\nmax attempts:"+to_string(maxAttempts_)+
"\nmax energy:"+to_string(maxEnergy_)
);
#endif
interscaleManager_ = &(atc_->interscale_manager());
PerAtomQuantity<int> * a2el = atc_->atom_to_element_map();
list_ = new AtomInElementSet(atc_,a2el,elemset_,controlType_);
nNodes_ = atc_->num_nodes();
DENS_MAT conc(nNodes_,1); conc = targetConcentration_;
DENS_VEC integral = atc_->fe_engine()->integrate(conc,elemset_);
targetCount_ = rnd(integral(0)) ;
volumes_.resize(elemset_.size());
ESET::const_iterator itr;
int i = 0;
DENS_MAT c(nNodes_,1); c = 1;
V_ = 0.;
for (itr = elemset_.begin(); itr != elemset_.end(); itr++, i++) {
ESET e; e.insert(*itr);
DENS_VEC v = atc_->fe_engine()->integrate(c,e);
volumes_(i) = v(0);
V_ += v(0);
}
volumes_ *= 1./V_;
for (int i = 1; i < volumes_.size(); i++) {
volumes_(i) += volumes_(i-1);
}
// record orginal energetic properties
int ntypes = lammpsInterface_->ntypes();
epsilon0_.reset(ntypes);
p_ = lammpsInterface_->potential();
lammpsInterface_->epsilons(controlType_,p_,epsilon0_.ptr());
#ifdef ATC_VERBOSE
string msg = "type "+to_string(controlType_)+" target count " + to_string(targetCount_);
msg += " volume " + to_string(V_);
msg += " current count " + to_string(count());
ATC::LammpsInterface::instance()->print_msg_once(msg);
msg = "WARNING: ensure neighboring happens at least every "+to_string(frequency_);
ATC::LammpsInterface::instance()->print_msg_once(msg);
#endif
}
double ConcentrationRegulatorMethodTransition::uniform() const {
_rngUniformCounter_++;
return lammpsInterface_->random_uniform(randomNumberGenerator_);
}
double ConcentrationRegulatorMethodTransition::normal() const {
_rngNormalCounter_++;
return lammpsInterface_->random_normal(randomNumberGenerator_);
}
//--------------------------------------------------------
// pre exchange
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pre_exchange(void)
{
// return if should not be called on this timestep
if ( ! lammpsInterface_->now(frequency_)) return;
nexchanges_ = excess();
int n = abs(nexchanges_);
bool success = false;
if (nexchanges_ > 0) { success = delete_atoms(n); }
else if (nexchanges_ < 0) { success = insert_atoms(n); }
else return;
if (!success) throw ATC_Error("insertions/deletions did not succeed");
if (nexchanges_ !=0) {
nInTransition_ = -nexchanges_;
lammpsInterface_->reset_ghosts(-nexchanges_);
atc_->reset_atoms();
}
transitionCounter_=0;
transition();
}
//--------------------------------------------------------
// pre force
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pre_force(void)
{
transition();
}
//--------------------------------------------------------
// accept
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::accept(double energy, double T) const
{
#ifdef ATC_VERBOSE2
if (energy < maxEnergy_) lammpsInterface_->print_msg(" energy "+to_string(energy)+" "+to_string(rngCounter_));
#endif
return (energy < maxEnergy_);
}
//--------------------------------------------------------
// energy
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::energy(int id) const
{
double e = lammpsInterface_->shortrange_energy(id,maxEnergy_);
#ifdef ATC_VERBOSE
{
int * tag = lammpsInterface_->atom_tag();
lammpsInterface_->print_msg(to_string(controlType_)+" deletion energy "+to_string(e)+" id "+to_string(tag[id])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_));
}
#endif
return e;
}
double ConcentrationRegulatorMethodTransition::energy(double * x) const
{
double e = lammpsInterface_->shortrange_energy(x,controlType_,maxEnergy_);
#ifdef ATC_VERBOSE
{
lammpsInterface_->print_msg(to_string(controlType_)+" insertion energy "+to_string(e)+" x "+to_string(x[0])+","+to_string(x[1])+","+to_string(x[2])+" "+to_string(_rngUniformCounter_)+":"+to_string(_rngNormalCounter_));
}
#endif
return e;
}
//--------------------------------------------------------
// excess
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::excess(void) const
{
int nexcess = count()-targetCount_;
nexcess = max(min(nexcess,maxExchanges_),-maxExchanges_);
return nexcess;
}
//--------------------------------------------------------
// count
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::count(void) const
{
// integrate concentration over region
const DENS_MAT & c = (atc_->field(SPECIES_CONCENTRATION)).quantity();
DENS_VEC integral = atc_->fe_engine()->integrate(c,elemset_);
return rnd(integral(controlIndex_)) ;
}
//--------------------------------------------------------
// delete atoms
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::delete_atoms(int n)
{
ID_PAIR idPair;
deletionIds_.clear();
int deletions = 0;
int attempts = 0;
while(deletions < n && attempts < maxAttempts_){
if(accept(deletion_id(idPair))) {
deletionIds_.push_back(idPair);
deletions += 1;
}
deletions = lammpsInterface_->int_allmax(deletions);
attempts++;
}
ID_LIST::iterator itr;
for (itr = deletionIds_.begin(); itr != deletionIds_.end(); itr++) {
lammpsInterface_->delete_atom(itr->second);
}
#ifdef ATC_VERBOSE
string c = to_string(controlType_);
lammpsInterface_->all_print(attempts, c+"-attempts ");
lammpsInterface_->all_print(deletions,c+" deletions ");
lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal ");
// lammpsInterface_->all_print(uniform()," RANDOM ");
#endif
return (n == deletions); // success
}
//--------------------------------------------------------
// pick id
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::deletion_id(ID_PAIR & id) const
{
if (atc_->parallel_consistency()) return deletion_id_consistent(id);
else return deletion_id_free(id);
}
double ConcentrationRegulatorMethodTransition::deletion_id_consistent(ID_PAIR & id) const
{
id.first = -1;
id.second = -1;
int ntotal = lammpsInterface_->natoms();
double r = uniform();
r *= ntotal;
const ID_LIST & list = list_->quantity();
ID_LIST::const_iterator itr;
int i=0, idx = -1;
double min = ntotal;
int * tag = lammpsInterface_->atom_tag();
for (itr = list.begin(); itr != list.end(); itr++) {
int atag = tag[itr->second];
double d = abs(atag-r);
if (d < min) {
min = d;
idx = i;
}
i++;
}
int imin = kMinScale_*min;
if(imin == lammpsInterface_->int_allmin(imin)) {
if (idx < 0) throw ATC_Error("deletion_id failed to find a suitable atom");
id = list_->item(idx);
// avoid repeats
ID_LIST & l = list_->set_quantity();
l.erase(l.begin()+idx);
return energy(id.second);
}
else {
return maxEnergy_;
}
}
double ConcentrationRegulatorMethodTransition::deletion_id_free(ID_PAIR & id) const
{
id.first = -1;
id.second = -1;
int n = list_->size();
double nrank = lammpsInterface_->int_scansum(n);
int ntotal = lammpsInterface_->int_allsum(n);
if (ntotal == 0) throw ATC_Error("control type "+to_string(controlType_)+" is depleted");
double r = uniform();
r *= ntotal;
if ( (r >= nrank-n) && (r < nrank)) { // pick processor
r = uniform();
int idx = rnd(r*(n-1));
id = list_->item(idx);
// avoid repeats
ID_LIST & l = list_->set_quantity();
l.erase(l.begin()+idx);
return energy(id.second);
}
else {
return maxEnergy_;
}
}
//--------------------------------------------------------
// insert atoms
//--------------------------------------------------------
bool ConcentrationRegulatorMethodTransition::insert_atoms(int n)
{
insertionIds_.clear();
DENS_VEC x(3); x = 0;
DENS_VEC v(3); v = 0;
const DENS_MAN & T = atc_->field(TEMPERATURE);
int additions = 0;
int attempts = 0;
while(additions < n && attempts < maxAttempts_){
if(accept(insertion_location(x))) {
DENS_VEC Tv = atc_->fe_engine()->interpolate_field(x,T);
Tv(0) = 300.;
pick_velocity(v,Tv(0)); // 3 normal
int nlocal = lammpsInterface_->insert_atom(transitionType_,controlMask_,x.ptr(),v.ptr()); // no charge
insertionIds_.push_back(pair<int,int>(-1,nlocal)); // atc id unknown
additions += 1;
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg(">>> insert x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+" v:"+to_string(v(0))+" "+to_string(v(1))+" "+to_string(v(2))+" "+to_string(rngCounter_));
#endif
}
attempts++;
//lammpsInterface_->barrier();
additions = lammpsInterface_->int_allmax(additions);
#ifdef ATC_VERBOSE
{
string c = to_string(controlType_);
lammpsInterface_->all_print(_rngUniformCounter_,c+" rng-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" rng-normal ");
// lammpsInterface_->all_print(uniform()," random ");
}
#endif
if (atc_->parallel_consistency()) { sync_random_number_generators(); }
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg("attempts: "+to_string(attempts)+" additions "+to_string(additions)+" : "+to_string(rngCounter_));
#endif
#ifdef ATC_VERBOSE
{
string c = to_string(controlType_);
lammpsInterface_->all_print(attempts, c+"+attempts ");
lammpsInterface_->all_print(additions,c+" additions ");
lammpsInterface_->all_print(_rngUniformCounter_,c+" RNG-uniform ");
lammpsInterface_->all_print(_rngNormalCounter_,c+" RNG-normal ");
// lammpsInterface_->all_print(uniform()," RANDOM ");
}
#endif
}
return (n == additions); // success
}
//--------------------------------------------------------
// sync random number generators
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::sync_random_number_generators() const
{
// normal
int n = lammpsInterface_->int_allmax(_rngNormalCounter_);
int dn = n - _rngNormalCounter_;
lammpsInterface_->advance_random_normal(randomNumberGenerator_,dn);
_rngNormalCounter_ = n;
// uniform
int u = lammpsInterface_->int_allmax(_rngUniformCounter_);
int du = u - _rngUniformCounter_;
lammpsInterface_->advance_random_uniform(randomNumberGenerator_,du);
_rngUniformCounter_ = u;
}
//--------------------------------------------------------
// pick location
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::insertion_location(DENS_VEC & x) const
{
// pick random element
int elem = pick_element(); // 1 uniform
// pick random local coordinate
DENS_VEC xi(3);
pick_coordinates(elem,xi,x); // 3 uniform
// if (! lammpsInterface_->in_box(x.ptr())) { throw ATC_Error("new atom is not in box");}
if (lammpsInterface_->in_my_processor_box(x.ptr())) {
#ifdef ATC_VERBOSE2
lammpsInterface_->print_msg(">>> insertion_location e:" +to_string(elem)+" xi:"+to_string(xi(0))+" "+to_string(xi(1))+" "+to_string(xi(2))+" x:"+to_string(x(0))+" "+to_string(x(1))+" "+to_string(x(2))+ " energy "+to_string(energy(x.ptr()))+" "+true_false(accept(energy(x.ptr())))+" "+to_string(rngUniformCounter_));
#endif
return energy(x.ptr());
}
else {
return maxEnergy_;
}
}
//--------------------------------------------------------
// pick element
//--------------------------------------------------------
int ConcentrationRegulatorMethodTransition::pick_element() const
{
double r = uniform();
ESET::iterator itr = elemset_.begin(); // global?
for (int i = 0; i < volumes_.size() ; ++i) {
if (r < volumes_(i)) return *itr;
itr++;
}
return *itr;
}
//--------------------------------------------------------
// pick coordinates
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pick_coordinates(const int elem,
DENS_VEC & xi,
DENS_VEC & x) const
{
xi.reset(3);
xi(0) = 2.*uniform()-1.;
xi(1) = 2.*uniform()-1.;
xi(2) = 2.*uniform()-1.;
atc_->fe_engine()->fe_mesh()->position(elem,xi,x);
}
//--------------------------------------------------------
// pick velocity
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::pick_velocity(DENS_VEC & v,double T) const
{
double s = sigma_*sqrt(T);
v(0) = s*normal();
v(1) = s*normal();
v(2) = s*normal();
//v = 0;
}
//--------------------------------------------------------
// transition
//--------------------------------------------------------
void ConcentrationRegulatorMethodTransition::transition()
{
transitionCounter_++;
//if (insertionIds_.size() == 0) return; //
if (transitionCounter_> transitionInterval_) {
nInTransition_ = 0;
return;
}
else if (transitionCounter_==transitionInterval_) {
nInTransition_ -= lammpsInterface_->change_type(transitionType_,controlType_);
}
else {
transitionFactor_ = insertion_factor(transitionCounter_);
if (nInTransition_ < 0) transitionFactor_ = 1-transitionFactor_;
double q = 0;
lammpsInterface_->set_charge(transitionType_,q);
DENS_VEC eps = epsilon0_;
lammpsInterface_->set_epsilons(transitionType_,p_,eps.ptr());
lammpsInterface_->pair_reinit(); // epsilon
}
}
//--------------------------------------------------------
// diagnostics
//--------------------------------------------------------
double ConcentrationRegulatorMethodTransition::compute_vector(int n) const
{
if (n==0) return count() - targetCount_;
else if (n==1) return count()/V_;
else if (n==2) return (1.-transitionFactor_)*nInTransition_;
else if (n==3) return _rngUniformCounter_;
else if (n==4) return _rngNormalCounter_;
else if (n==5) return lammpsInterface_->random_state(randomNumberGenerator_);
else return 0;
}
}; // end namespace

View File

@ -0,0 +1,212 @@
#ifndef CONCENTRATION_REGULATOR_H
#define CONCENTRATION_REGULATOR_H
// ATC headers
#include "AtomicRegulator.h"
#include "LammpsInterface.h"
namespace ATC {
/**
* @class ConcentrationRegulator
* @brief Manager class for atom-continuum control of charge and potential
*/
class ConcentrationRegulatorMethod;
class ConcentrationRegulator : public AtomicRegulator {
public:
enum ConcentrationRegulatorType {NONE=0,TRANSITION};
/** parser data */
struct ConcentrationRegulatorParameters {
ConcentrationRegulatorParameters():
method(NONE),
type(0),
groupbit(0),
transitionType(0),
value(0),
frequency(0),
transitionInterval(0),
maxEnergy(0),
maxExchanges(0),
maxAttempts(0) { };
ConcentrationRegulatorType method;
int type;
int groupbit;
int transitionType;
double value;
int frequency;
int transitionInterval;
double maxEnergy;
int maxExchanges;
int maxAttempts;
string transitionTag;
ESET elemset;
};
/** constructor */
ConcentrationRegulator(ATC_Coupling *atc);
/** destructor */
~ConcentrationRegulator();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** construct methods */
virtual void construct_methods();
/** pre time integration */
virtual void initialize();
//WIP_JAT need a nicer way to consistently handle sets of regulators, not sure how yet
// application steps
/** apply the regulator in the pre-predictor phase */
virtual void apply_pre_predictor(double dt, int timeStep){};
/** apply the regulator in the mid-predictor phase */
virtual void apply_mid_predictor(double dt, int timeStep){};
/** apply the regulator in the post-predictor phase */
virtual void apply_post_predictor(double dt, int timeStep){};
/** apply the regulator in the pre-correction phase */
virtual void apply_pre_corrector(double dt, int timeStep){};
/** apply the regulator in the post-correction phase */
virtual void apply_post_corrector(double dt, int timeStep){};
/** compute the thermal boundary flux, must be consistent with regulator */
virtual void compute_boundary_flux(FIELDS & fields){};
/** add contributions (if any) to the finite element right-hand side */
virtual void add_to_rhs(FIELDS & rhs){};
/** prior to exchanges */
virtual void pre_force();
/** prior to exchanges */
virtual void pre_exchange();
/** ATC output */
virtual void output(OUTPUT_LIST & outputData) const;
/** scalar output */
virtual double compute_vector(int n) const;
virtual int size_vector(int s) const;
bool needs_temperature() { return regulators_.size() > 0; }
protected:
/** registry charge regulators */
map<string,ConcentrationRegulatorMethod *> regulators_;
map<string,ConcentrationRegulatorParameters> parameters_;
private:
ConcentrationRegulator(); // DO NOT define this
};
/**
* @class ConcentrationRegulatorMethod
* @brief Base class for ConcentrationRegulator algorithms
*/
class ConcentrationRegulatorMethod : public RegulatorShapeFunction {
public:
ConcentrationRegulatorMethod(ConcentrationRegulator *c)
: RegulatorShapeFunction(c) {};
~ConcentrationRegulatorMethod() {};
void initialize(void) {};
virtual void pre_force() {};
virtual void pre_exchange() {};
virtual void set_weights() {};
virtual double compute_vector(int n) const { return 0;}
virtual void output(OUTPUT_LIST & outputData){};
private:
ConcentrationRegulatorMethod(); // DO NOT define this
};
/**
* @class ConcentrationRegulatorMethodTransition
* @brief GCMC + thermodynamic integration
*/
class ConcentrationRegulatorMethodTransition : public ConcentrationRegulatorMethod {
public:
/** constructor */
ConcentrationRegulatorMethodTransition(
ConcentrationRegulator *concentrationRegulator,
ConcentrationRegulator::ConcentrationRegulatorParameters & parameters);
/** destructor */
~ConcentrationRegulatorMethodTransition(){
if (randomNumberGenerator_) delete randomNumberGenerator_;
}
/** initialize */
void initialize(void);
/** prior to force evaluation */
virtual void pre_force();
/** prior to exchanges */
virtual void pre_exchange();
/** "thermo" output */
virtual double compute_vector(int n) const;
protected:
/** set transition state: epsilon and charge */
int mask(int type, int groupbit) {return 0;}
int count(void) const;
int excess(void) const;
double energy(int id) const;
double energy(double * x) const;
double insertion_location(DENS_VEC & x) const;
double deletion_id(ID_PAIR & id) const ;
double deletion_id_consistent(ID_PAIR & id) const ;
double deletion_id_free(ID_PAIR & id) const ;
double insertion_factor(int c) const // a ramp
{
if (c < transitionInterval_) return ((double) c)/transitionInterval_;
else return 1.0;
}
void transition();
bool accept(double energy, double T = 0) const;
bool delete_atoms(int n);
bool insert_atoms(int n);
int pick_element() const;
void pick_coordinates(const int elem, DENS_VEC & xi, DENS_VEC& x ) const;
void pick_velocity(DENS_VEC & v, const double T ) const;
double uniform() const;
double normal() const;
/** pointer to conc regulator object for data */
ConcentrationRegulator * concentrationRegulator_;
/** interscale manager */
class InterscaleManager * interscaleManager_;
/** interscale manager */
class LammpsInterface * lammpsInterface_;
/** member data */
class AtomInElementSet * list_;
double targetConcentration_;
double targetCount_;
ESET elemset_;
POTENTIAL p_;
RNG_POINTER randomNumberGenerator_;
DENS_VEC volumes_;
double V_;
double q0_;
int controlType_;
int controlIndex_;
int transitionType_;
int transitionInterval_;;
int transitionCounter_;
int nInTransition_;
double transitionFactor_;
DENS_VEC epsilon0_;
ID_LIST deletionIds_, insertionIds_;
int controlMask_;
int frequency_;
double maxEnergy_;
int maxExchanges_;
int maxAttempts_;
int nexchanges_;
bool initialized_;
double sigma_;
void sync_random_number_generators() const;
mutable int _rngUniformCounter_; // for parallel consistency
mutable int _rngNormalCounter_; // for parallel consistency
private:
ConcentrationRegulatorMethodTransition(); // DO NOT define this
};
};
#endif

417
lib/atc/DenseMatrix.h Normal file
View File

@ -0,0 +1,417 @@
#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(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 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(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 _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) {
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(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;
}
}
//----------------------------------------------------------------------------
// 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);
cout << "x.i: "<< x.i << "\nx.j: "<< x.j << "\nv.j: "<< x.v << std::endl << std::endl;
(*this)(x.i, x.j) = x.v;
}
return *this;
}
//* Returns the transpose of the cofactor matrix of A.
//* see http://en.wikipedia.org/wiki/Adjugate_matrix
//* symmetric flag only affects cases N>3
template<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

168
lib/atc/DenseVector.h Normal file
View File

@ -0,0 +1,168 @@
#ifndef DENSEVECTOR_H
#define DENSEVECTOR_H
#include "Vector.h"
namespace ATC_matrix {
template<typename T>
/**
* @class DenseVector
* @brief Class for storing data in a "dense" vector form
*/
class DenseVector : public Vector<T>
{
public:
explicit DenseVector(INDEX n=0, bool z=1) { _create(n,z); }
DenseVector(const DenseVector<T> &c) : _data(NULL) { _copy(c); }
DenseVector(const Vector<T> &c) : _data(NULL) { _copy(c); }
DenseVector(const T * ptr, INDEX nrows) : _data(NULL) { copy(ptr,nrows); }
virtual ~DenseVector() { _delete(); }
//* resizes the Vector, ignores nCols, optionally copys what fits
void resize(INDEX rows, INDEX cols=1, bool copy=false);
//* resizes the Vector, ignores nCols, optionally zeros it out
void reset (INDEX rows, INDEX cols=1, bool zero=true);
//* resizes the Vector and copies data, ignores nCols
void copy(const T * ptr, INDEX rows, INDEX cols=1);
// overloaded inline virtual functions
T operator[](INDEX i) const { VICK(i) return _data[i]; }
T& operator[](INDEX i) { VICK(i) return _data[i]; }
T operator()(INDEX i, INDEX j=0) const { VICK(i) return _data[i]; }
T& operator()(INDEX i, INDEX j=0) { VICK(i) return _data[i]; }
void set_all_elements_to(const T &v) {
int sz = this->size();
for (INDEX i = 0; i < sz; i++) _data[i] = v;
}
INDEX nRows() const { return _size; }
T* ptr() const { return _data; }
DenseVector<T>& operator=(const T &v);
DenseVector<T>& operator=(const Vector<T> &c);
DenseVector<T>& operator=(const DenseVector<T> &c);
void write_restart(FILE *f) const;
private:
void _delete();
void _create(INDEX n, bool zero=0);
void _copy(const Vector<T> &c);
T *_data;
INDEX _size;
};
///////////////////////////////////////////////////////////////////////////////
// Template definitions ///////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// resizes the matrix and optionally copies over what still fits, ignores cols
//-----------------------------------------------------------------------------
template <typename T>
void DenseVector<T>::resize(INDEX rows, INDEX cols, bool copy)
{
if (_size==rows) return; // if is correct size, done
if (!copy)
{
_delete();
_create(rows);
return;
}
DenseVector<T> temp(*this);
_delete();
_create(rows);
int sz = this->size();
for (INDEX i = 0; i < sz; i++)
_data[i] = i<temp.size() ? temp[i] : T(0.0);
return;
}
///////////////////////////////////////////////////////////////////////////////
//* resizes the matrix and optionally zeros it out
template <typename T>
void DenseVector<T>::reset(INDEX rows, INDEX cols, bool zero)
{
if (_size!=rows)
{
_delete();
_create(rows);
}
if (zero) this->zero();
}
///////////////////////////////////////////////////////////////////////////////
//* resizes the matrix and optionally zeros it out
template <typename T>
void DenseVector<T>::copy(const T * ptr, INDEX rows, INDEX cols)
{
resize(rows, 1, false);
memcpy(_data, ptr, this->size()*sizeof(T));
}
///////////////////////////////////////////////////////////////////////////////
//* writes the matrix data to a file
template <typename T>
void DenseVector<T>::write_restart(FILE *f) const
{
fwrite(&_size, sizeof(INDEX),1,f);
if(_size) fwrite(_data, sizeof(T), _size, f);
}
///////////////////////////////////////////////////////////////////////////////
//* clears allocated memory
template <typename T>
inline void DenseVector<T>::_delete()
{
if (_data) delete [] _data;
_size = 0;
}
///////////////////////////////////////////////////////////////////////////////
//* allocates memory for an rows by cols DenseMatrix
template <typename T>
inline void DenseVector<T>::_create(INDEX n, bool zero)
{
_size=n;
_data = _size ? new T [_size] : NULL ;
if (zero) this->zero();
}
///////////////////////////////////////////////////////////////////////////////
//* creates a deep memory copy from a general matrix
template <typename T>
inline void DenseVector<T>::_copy(const Vector<T> &c)
{
if (!_data || _size!=c.size())
{
_delete();
_create(c.size(), false);
}
else _size = c.size();
memcpy(_data, c.ptr(), _size*sizeof(T));
}
///////////////////////////////////////////////////////////////////////////////
//* assigns v to all values in the vector
template <typename T>
DenseVector<T>& DenseVector<T>::operator=(const T &v)
{
int sz = this->size();
for (INDEX i = 0; i < sz; i++) (*this)[i] = v;
return *this;
}
///////////////////////////////////////////////////////////////////////////////
//* copys c with a deep copy
template <typename T>
DenseVector<T>& DenseVector<T>::operator=(const Vector<T> &c)
{
_copy(c);
return *this;
}
///////////////////////////////////////////////////////////////////////////////
//* copys c with a deep copy
template <typename T>
DenseVector<T>& DenseVector<T>::operator=(const DenseVector<T> &c)
{
_copy(c);
return *this;
}
} // end namespace
#endif

328
lib/atc/DependencyManager.h Normal file
View File

@ -0,0 +1,328 @@
// A class for wrapping matrix operations with dependency information to speed up execution
#ifndef DEPENDENCY_MANAGER_H
#define DEPENDENCY_MANAGER_H
#include "ATC_TypeDefs.h"
#include "MatrixLibrary.h"
#include "ATC_Error.h"
#include "MPI_Wrappers.h"
using namespace MPI_Wrappers;
namespace ATC {
class InterscaleManager;
/** memory type */
enum MemoryType
{
TEMPORARY = 0,
PERSISTENT
};
/**
* @class DependencyManager
* @brief Base class for defining objects that manage the dependencies of various objects
*/
class DependencyManager {
public:
// used as a friend so it can perform a depth-first search to have safe deletions of managed dependencies
friend class InterscaleManager;
// constructor
DependencyManager() : needReset_(true), isFixed_(false), memoryType_(TEMPORARY), dfsFound_(false) {};
// destructor
virtual ~DependencyManager() {};
/** registration by other PerAtomQuantity objects */
void register_dependence(DependencyManager * dependentQuantity)
{dependentQuantities_.insert(dependentQuantity);};
/** removes dependencies from the set */
void remove_dependence(DependencyManager * dependentQuantity)
{dependentQuantities_.erase(dependentQuantity);};
/** check if a reset is required */
bool need_reset() const {return needReset_ && !isFixed_;};
/** propagate need to reset to to dependencies */
void propagate_reset()
{
if (!isFixed_) {
set<DependencyManager *>::iterator it;
for (it = dependentQuantities_.begin(); it != dependentQuantities_.end(); it++)
(*it)->force_reset();
}
};
/** actions associated with indicating this quantity requires a reset */
void set_reset()
{
needReset_ = true;
}
/** flip this object to needing a reset, and get dependencies */
void force_reset()
{
set_reset();
propagate_reset();
}
/** force quantity to be held fixed, enables dependent quantity to be used as persistent storage */
void fix_quantity() {isFixed_ = true;};
/** unfix the quantity */
void unfix_quantity()
{
if (isFixed_) {
isFixed_ = false;
if (needReset_) propagate_reset();
}
};
/** check on the memory type of the quantity */
MemoryType memory_type() const {return memoryType_;};
/** set the memory type of the quantity */
void set_memory_type(MemoryType memoryType) {memoryType_ = memoryType;};
protected:
/** list of dependent atomic quantities */
set<DependencyManager * > dependentQuantities_;
/** flag for needing a recent */
// mutable is applied because there can be internal updates because we update when needed rather than when pushed
mutable bool needReset_;
/** flag for if quantity is being held fixed */
bool isFixed_;
/** flag for if the quantity is temporary (per-run) */
MemoryType memoryType_;
/** flag for if the node has been found in depth-first search */
bool dfsFound_;
};
/**
* @class MatrixDependencyManager
* @brief Class for defining objects that manage the dependencies of matrices
*/
// Matrix class T, underlying type U
template <template <typename> class T, typename U>
class MatrixDependencyManager : public DependencyManager {
public:
MatrixDependencyManager() {};
MatrixDependencyManager(int nRows, int nCols) : quantity_(nRows,nCols) {};
virtual ~MatrixDependencyManager() {};
/** returns a non-const version for manipulations and changes, resets dependent quantities */
virtual T<U> & set_quantity() {propagate_reset(); return get_quantity();};
/** access to a constant dense matrix of the quantity, indexed by AtC atom counts */
virtual const T<U> & quantity() const {return get_quantity();};
/** number of rows in quantity */
virtual int nRows() const {return (this->quantity()).nRows();};
/** number of columns in quantity */
virtual int nCols() const {return (this->quantity()).nCols();};
/** size of the matrix */
virtual int size() const {return (this->quantity()).size();};
/** reset the quantities size */
void reset(INDEX nRows, INDEX nCols) {get_quantity().reset(nRows,nCols); propagate_reset();};
/** resize the quantities size */
void resize(INDEX nRows, INDEX nCols) {get_quantity().resize(nRows,nCols); propagate_reset();};
/** sets the quantity to a given value */
virtual void operator=(const T<U> & target) {get_quantity()=target; propagate_reset();};
/** sets the quantity to a given constant value */
virtual void operator=(U target) {get_quantity()=target; propagate_reset();};
/** adds the given data to the Lammps quantity */
virtual void operator+=(const T<U> & addition) {get_quantity()+=addition; propagate_reset();};
/** adds the scalar data to the Lammps quantity for AtC atoms */
virtual void operator+=(U addition) {get_quantity()+=addition; propagate_reset();};
/** adds the given data to the Lammps quantity */
virtual void operator-=(const T<U> & subtraction) {get_quantity()-=subtraction; propagate_reset();};
/** adds the scalar data to the Lammps quantity for AtC atoms */
virtual void operator-=(U subtraction) {get_quantity()-=subtraction; propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(const T<U> & multiplier) {get_quantity()*=multiplier; propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(U multiplier) {get_quantity()*=multiplier; propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(const T<U> & divisor) {get_quantity()/=divisor; propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(U divisor) {get_quantity()/=divisor; propagate_reset();};
// I have no idea why these won't compile (JAT, 04/07/11)
/** adds the given data to the Lammps quantity */
virtual void operator+=(const MatrixDependencyManager<T,U> & addition) {get_quantity()+=addition.quantity(); propagate_reset();};
/** adds the given data to the Lammps quantity */
virtual void operator-=(const MatrixDependencyManager<T,U> & subtraction) {get_quantity()-=subtraction.quantity(); propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(const MatrixDependencyManager<T,U> & multiplier) {get_quantity()*=multiplier.quantity(); propagate_reset();};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(const MatrixDependencyManager<T,U> & divisor) {get_quantity()/=divisor.quantity(); propagate_reset();};
/** execute the matrix print command */
virtual void const print(const string &name) {get_quantity().print(name);};
protected:
// This getter can be overridden by derived classes if they need e.g. a
// differently-constructed quantity, but would like to use the same operators.
virtual T<U> &get_quantity() const { return quantity_; }
/** matrix */
// mutable is applied because there can be internal updates because we update when needed rather than when pushed
mutable T<U> quantity_;
};
/**
* @class MatrixDependencyManager<ParSparseMatrix, T>
* @brief Class for defining objects that manage the dependencies of parallelized sparse matrices
*/
template<typename T>
class MatrixDependencyManager<ParSparseMatrix, T> :
public MatrixDependencyManager<SparseMatrix, T>
{
public:
MatrixDependencyManager(MPI_Comm comm) :
MatrixDependencyManager<SparseMatrix, T>(), quantity_(comm) {};
MatrixDependencyManager(MPI_Comm comm, int nRows, int nCols) :
MatrixDependencyManager<SparseMatrix, T>(), quantity_(comm, nRows, nCols) {};
virtual ~MatrixDependencyManager() {};
protected:
// Let the superclass's operators work on our ParSparseMatrix.
virtual ParSparseMatrix<T> &get_quantity() const { return quantity_; }
mutable ParSparseMatrix<T> quantity_;
};
/**
* @class MatrixDependencyManager<ParDiagonalMatrix, T>
* @brief Class for defining objects that manage the dependencies of parallelized diagonal matrices
*/
template<typename T>
class MatrixDependencyManager<ParDiagonalMatrix, T> :
public MatrixDependencyManager<DiagonalMatrix, T>
{
public:
MatrixDependencyManager(MPI_Comm comm) :
MatrixDependencyManager<DiagonalMatrix, T>(), quantity_(comm) {};
MatrixDependencyManager(MPI_Comm comm, int nRows, int nCols) :
MatrixDependencyManager<DiagonalMatrix, T>(), quantity_(comm, nRows, nCols) {};
virtual ~MatrixDependencyManager() {};
protected:
// Let the superclass's operators work on our ParDiagonalMatrix.
virtual ParDiagonalMatrix<T> &get_quantity() const { return quantity_; }
mutable ParDiagonalMatrix<T> quantity_;
};
/**
* @class SetDependencyManager
* @brief Class for defining objects that manage the dependencies of standard library sets
*/
template <typename T>
class SetDependencyManager : public DependencyManager {
public:
// constructor
SetDependencyManager() :
DependencyManager(), quantity_() {};
// destructor
virtual ~SetDependencyManager() {};
/** returns a non-const version for manipulations and changes, resets dependent quantities */
virtual set<T> & set_quantity() {propagate_reset(); return quantity_;};
/** access to a constant dense matrix of the quantity, indexed by AtC atom counts */
virtual const set<T> & quantity() const {return quantity_;};
/** size of the set */
virtual int size() const {return (this->quantity()).size();};
protected:
/** underlying set */
// mutable is applied because there can be internal updates because we update when needed rather than when pushed
mutable set<T> quantity_;
};
/**
* @class VectorDependencyManager
* @brief Class for defining objects that manage the dependencies of standard library vectors
*/
template <typename T>
class VectorDependencyManager : public DependencyManager {
public:
// constructor
VectorDependencyManager() :
DependencyManager(), quantity_() {};
// destructor
virtual ~VectorDependencyManager() {};
/** returns a non-const version for manipulations and changes, resets dependent quantities */
virtual vector<T> & set_quantity() {propagate_reset(); return quantity_;};
/** access to a constant dense matrix of the quantity, indexed by AtC atom counts */
virtual const vector<T> & quantity() const {return quantity_;};
/** size of the set */
virtual int size() const {return (this->quantity()).size();};
protected:
/** underlying set */
// mutable is applied because there can be internal updates because we update when needed rather than when pushed
mutable vector<T> quantity_;
};
};
#endif

495
lib/atc/DiagonalMatrix.h Normal file
View File

@ -0,0 +1,495 @@
#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);
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)
string to_string() const { return _data->to_string(); }
using Matrix<T>::matlab;
void matlab(ostream &o, const 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);
}
//-----------------------------------------------------------------------------
// 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(ostream &o, const 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
{
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;
cout << "DiagonalMatrix::inv_this(): Warning: Matrix is badly scaled.";
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
{
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;
cout << "DiagonalMatrix::inv(): Warning: Matrix is badly scaled.";
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
{
cout <<"Error in general sparse 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

View File

@ -0,0 +1,853 @@
// ATC transfer headers
#include "ElasticTimeIntegrator.h"
#include "ATC_Coupling.h"
#include "TimeFilter.h"
#include "ATC_Error.h"
#include "PerAtomQuantityLibrary.h"
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class MomentumTimeIntegrator
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
MomentumTimeIntegrator::MomentumTimeIntegrator(ATC_Coupling * atc,
TimeIntegrationType timeIntegrationType) :
TimeIntegrator(atc, timeIntegrationType)
{
// do nothing
}
//--------------------------------------------------------
// modify
// parses inputs and modifies state of the integrator
//--------------------------------------------------------
bool MomentumTimeIntegrator::modify(int narg, char **arg)
{
bool foundMatch = false;
int argIndex = 0;
// time integration scheme
/*! \page man_time_integration fix_modify AtC time_integration
\section syntax
fix_modify AtC time_integration <descriptor> \n
- descriptor (string) = time integration type \n
various time integration methods for the finite elements\n
\section description
Verlet - atomic velocity update with 2nd order Verlet \n
nodal temperature update with 2nd order Verlet \n
kinetostats based on controlling force \n
FRACTIONAL_STEP - atomic velocity update with 2nd order Verlet \n
mixed nodal momentum update, 2nd order Verlet for continuum and exact 2nd order Verlet for atomic contributions\n
kinetostats based on controlling discrete momentum changes\n
\section examples
<TT> fix_modify atc time_integration verlet </TT> \n
<TT> fix_modify atc time_integration fractional_step </TT> \n
\section description
\section related
\section default
Gear
*/
if (strcmp(arg[argIndex],"verlet")==0) {
timeIntegrationType_ = VERLET;
needReset_ = true;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"fractional_step")==0) {
timeIntegrationType_ = FRACTIONAL_STEP;
needReset_ = true;
foundMatch = true;
}
else if (strcmp(arg[argIndex],"gear")==0) {
timeIntegrationType_ = GEAR;
needReset_ = true;
foundMatch = true;
}
return foundMatch;
}
//--------------------------------------------------------
// construct_methods
// creates algorithm objects
//--------------------------------------------------------
void MomentumTimeIntegrator::construct_methods()
{
if (atc_->reset_methods()) {
if (timeIntegrationMethod_)
delete timeIntegrationMethod_;
if (timeFilterManager_->need_reset()) {
switch (timeIntegrationType_) {
case VERLET:
timeFilter_ = timeFilterManager_->construct(TimeFilterManager::IMPLICIT);
atc_->set_mass_mat_time_filter(MOMENTUM,TimeFilterManager::IMPLICIT);
break;
case FRACTIONAL_STEP:
case GEAR:
timeFilter_ = timeFilterManager_->construct(TimeFilterManager::EXPLICIT_IMPLICIT);
atc_->set_mass_mat_time_filter(MOMENTUM,TimeFilterManager::EXPLICIT_IMPLICIT);
break;
default:
throw ATC_Error("Uknown time integration type in ThermalTimeIntegrator::Initialize()");
}
}
if (timeFilterManager_->filter_dynamics()) {
switch (timeIntegrationType_) {
case VERLET: {
timeIntegrationMethod_ = new ElasticTimeIntegratorVerletFiltered(this);
break;
}
default:
throw ATC_Error("Uknown time integration type in MomentumTimeIntegrator::Initialize()");
}
}
else {
switch (timeIntegrationType_) {
case VERLET: {
timeIntegrationMethod_ = new ElasticTimeIntegratorVerlet(this);
break;
}
case FRACTIONAL_STEP: {
timeIntegrationMethod_ = new ElasticTimeIntegratorFractionalStep(this);
break;
}
case GEAR: {
timeIntegrationMethod_ = new FluidsTimeIntegratorGear(this);
break;
}
default:
throw ATC_Error("Uknown time integration type in MomentumTimeIntegrator::Initialize()");
}
}
}
}
//--------------------------------------------------------
// pack_fields
// add persistent variables to data list
//--------------------------------------------------------
void MomentumTimeIntegrator::pack_fields(RESTART_LIST & data)
{
data["NodalAtomicForceFiltered"] = & nodalAtomicForceFiltered_.set_quantity();
data["NodalAtomicMomentumFiltered"] = & nodalAtomicMomentumFiltered_.set_quantity();
TimeIntegrator::pack_fields(data);
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class MomentumIntegrationMethod
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab data from ATC
//--------------------------------------------------------
MomentumIntegrationMethod::MomentumIntegrationMethod(MomentumTimeIntegrator * momentumTimeIntegrator) :
TimeIntegrationMethod(momentumTimeIntegrator),
timeFilter_(timeIntegrator_->time_filter()),
velocity_(atc_->field(VELOCITY)),
acceleration_(atc_->field_roc(VELOCITY)),
nodalAtomicVelocityOut_(atc_->nodal_atomic_field(VELOCITY)),
velocityRhs_(atc_->field_rhs(VELOCITY)),
nodalAtomicForceOut_(atc_->nodal_atomic_field_roc(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// construct_transfers
// Grab existing managed quantities,
// create the rest
//--------------------------------------------------------
void MomentumIntegrationMethod::construct_transfers()
{
InterscaleManager & interscaleManager(atc_->interscale_manager());
nodalAtomicVelocity_ = interscaleManager.dense_matrix("NodalAtomicVelocity");
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ElasticTimeIntegratorVerlet
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ElasticTimeIntegratorVerlet::ElasticTimeIntegratorVerlet(MomentumTimeIntegrator * momentumTimeIntegrator) :
MomentumIntegrationMethod(momentumTimeIntegrator),
displacement_(atc_->field(DISPLACEMENT)),
nodalAtomicDisplacementOut_(atc_->nodal_atomic_field(DISPLACEMENT)),
nodalAtomicForceFiltered_(momentumTimeIntegrator->nodal_atomic_force_filtered()),
nodalAtomicDisplacement_(NULL),
nodalAtomicForce_(NULL)
{
// do nothing
}
//--------------------------------------------------------
// construct_transfers
// Grab existing managed quantities,
// create the rest
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::construct_transfers()
{
MomentumIntegrationMethod::construct_transfers();
InterscaleManager & interscaleManager = atc_->interscale_manager();
nodalAtomicDisplacement_ = interscaleManager.dense_matrix("NodalAtomicDisplacement");
nodalAtomicForce_ = interscaleManager.dense_matrix("NodalAtomicForce");
}
//--------------------------------------------------------
// initialize
// initialize all data
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::initialize()
{
MomentumIntegrationMethod::initialize();
// sets up time filter for cases where variables temporally filtered
// this time integrator should use an implicit filter
TimeFilterManager * timeFilterManager = (timeIntegrator_->atc())->time_filter_manager();
if (timeFilterManager->need_reset()) {
timeFilter_->initialize(nodalAtomicForce_->quantity());
}
if (!(timeFilterManager->end_equilibrate())) {
nodalAtomicForceFiltered_.reset(atc_->num_nodes(),atc_->nsd());
}
if (!(timeFilterManager->filter_dynamics())){
//post_process();
//compute_nodal_forces(velocityRhs_.set_quantity());
velocityRhs_ = nodalAtomicForce_->quantity();
}
}
//--------------------------------------------------------
// mid_initial_integrate1
// time integration at the mid-point of Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::mid_initial_integrate1(double dt)
{
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),.5*dt);
}
//--------------------------------------------------------
// post_initial_integrate1
// time integration after Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::post_initial_integrate1(double dt)
{
// for improved accuracy, but this would be inconsistent with
// the atomic integration scheme
explicit_1(displacement_.set_quantity(),velocity_.quantity(),dt);
}
//--------------------------------------------------------
// pre_final_integrate1
// first time integration computations
// before Verlet step 2
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::pre_final_integrate1(double dt)
{
// integrate filtered atomic force
timeFilter_->apply_post_step2(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_->quantity(),dt);
}
//--------------------------------------------------------
// post_final_integrate2
// second time integration computations
// after Verlet step 2
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::post_final_integrate2(double dt)
{
atc_->apply_inverse_mass_matrix(velocityRhs_.quantity(),
acceleration_.set_quantity(),
VELOCITY);
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),.5*dt);
}
//--------------------------------------------------------
// add_to_rhs
// add integrated atomic force contributions
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::add_to_rhs()
{
// Compute MD contribution to FEM equation
velocityRhs_ += nodalAtomicForce_->quantity();
}
//--------------------------------------------------------
// post_process
// post processing of variables before output
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::post_process()
{
nodalAtomicDisplacementOut_ = nodalAtomicDisplacement_->quantity();
nodalAtomicVelocityOut_ = nodalAtomicVelocity_->quantity();
}
//--------------------------------------------------------
// output
// add variables to output list
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::output(OUTPUT_LIST & outputData)
{
DENS_MAT & nodalAtomicForce(nodalAtomicForce_->set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData["NodalAtomicForce"] = &nodalAtomicForce;
}
}
//--------------------------------------------------------
// finish
// finalize state of nodal atomic quantities
//--------------------------------------------------------
void ElasticTimeIntegratorVerlet::finish()
{
post_process();
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ElasticTimeIntegratorVerletFiltered
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ElasticTimeIntegratorVerletFiltered::ElasticTimeIntegratorVerletFiltered(MomentumTimeIntegrator * momentumTimeIntegrator) :
ElasticTimeIntegratorVerlet(momentumTimeIntegrator),
nodalAtomicAcceleration_(atc_->nodal_atomic_field_roc(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// mid_initial_integrate1
// time integration at the mid-point of Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorVerletFiltered::mid_initial_integrate1(double dt)
{
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),.5*dt);
explicit_1(nodalAtomicVelocityOut_.set_quantity(),nodalAtomicAcceleration_.quantity(),.5*dt);
}
//--------------------------------------------------------
// post_initial_integrate1
// time integration after Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorVerletFiltered::post_initial_integrate1(double dt)
{
// for improved accuracy, but this would be inconsistent with
// the atomic integration scheme
explicit_1(displacement_.set_quantity(),velocity_.quantity(),dt);
explicit_1(nodalAtomicDisplacementOut_.set_quantity(),nodalAtomicVelocityOut_.quantity(),dt);
}
//--------------------------------------------------------
// post_final_integrate2
// second time integration after Verlet step 2
//--------------------------------------------------------
void ElasticTimeIntegratorVerletFiltered::post_final_integrate2(double dt)
{
DENS_MAT velocityRoc(velocityRhs_.nRows(),velocityRhs_.nCols());
atc_->apply_inverse_mass_matrix(velocityRhs_.quantity(),
acceleration_.set_quantity(),
VELOCITY);
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),.5*dt);
atc_->apply_inverse_md_mass_matrix(nodalAtomicForceFiltered_.quantity(),
nodalAtomicAcceleration_.set_quantity(),
VELOCITY);
explicit_1(nodalAtomicVelocityOut_.set_quantity(),nodalAtomicAcceleration_.quantity(),.5*dt);
}
//--------------------------------------------------------
// add_to_rhs
// add integrated atomic force contributions
//--------------------------------------------------------
void ElasticTimeIntegratorVerletFiltered::add_to_rhs()
{
// MD contributions to FE equations
velocityRhs_ += nodalAtomicForceFiltered_.set_quantity();
}
//--------------------------------------------------------
// output
// add variables to output list
//--------------------------------------------------------
void ElasticTimeIntegratorVerletFiltered::output(OUTPUT_LIST & outputData)
{
DENS_MAT & nodalAtomicForce(nodalAtomicForceFiltered_.set_quantity());
if ((atc_->lammps_interface())->rank_zero()) {
outputData["NodalAtomicForce"] = &nodalAtomicForce;
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ElasticTimeIntegratorFractionalStep
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ElasticTimeIntegratorFractionalStep::ElasticTimeIntegratorFractionalStep(MomentumTimeIntegrator * momentumTimeIntegrator) :
MomentumIntegrationMethod(momentumTimeIntegrator),
displacement_(atc_->field(DISPLACEMENT)),
nodalAtomicDisplacementOut_(atc_->nodal_atomic_field(DISPLACEMENT)),
nodalAtomicForceFiltered_(momentumTimeIntegrator->nodal_atomic_force_filtered()),
nodalAtomicMomentum_(NULL),
nodalAtomicMomentumFiltered_(momentumTimeIntegrator->nodal_atomic_momentum_filtered()),
nodalAtomicDisplacement_(NULL),
nodalAtomicMomentumOld_(atc_->num_nodes(),atc_->nsd()),
nodalAtomicVelocityOld_(atc_->num_nodes(),atc_->nsd())
{
// do nothing
}
//--------------------------------------------------------
// construct_transfers
// Grab existing managed quantities,
// create the rest
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::construct_transfers()
{
MomentumIntegrationMethod::construct_transfers();
InterscaleManager & interscaleManager = atc_->interscale_manager();
nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum");
nodalAtomicDisplacement_ = interscaleManager.dense_matrix("NodalAtomicDisplacement");
}
//--------------------------------------------------------
// initialize
// initialize all data
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::initialize()
{
MomentumIntegrationMethod::initialize();
// initial force to zero
nodalAtomicForce_.reset(atc_->num_nodes(),atc_->nsd());
// sets up time filter for cases where variables temporally filtered
// this time integrator should use Crank-Nicholson filter for 2nd order accuracy
TimeFilterManager * timeFilterManager = (timeIntegrator_->atc())->time_filter_manager();
if (timeFilterManager->need_reset()) {
// the form of this integrator implies no time filters that require history data can be used
timeFilter_->initialize();
}
// sets up time filter for post-processing the filtered power
// this time integrator should use an explicit-implicit filter
// to mirror the 2nd order Verlet integration scheme
// It requires no history information so initial value just sizes arrays
if (!(timeFilterManager->end_equilibrate())) {
// implies an initial condition of the instantaneous atomic energy
nodalAtomicMomentumFiltered_ = nodalAtomicMomentum_->quantity();
nodalAtomicForceFiltered_.reset(atc_->num_nodes(),atc_->nsd());
}
}
//--------------------------------------------------------
// pre_initial_integrate1
// time integration before Verlet step 1, used to
// provide the baseline momentum and displacement to
// quantify the change over the timestep
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::pre_initial_integrate1(double dt)
{
// initialize changes in momentum
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
// updated filtered energy using explicit-implicit scheme
timeFilter_->apply_pre_step1(nodalAtomicMomentumFiltered_.set_quantity(),
myNodalAtomicMomentum,dt);
}
//--------------------------------------------------------
// pre_initial_integrate2
// second time integration after kinetostat application
// to compute MD contributions to momentum change
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::pre_initial_integrate2(double dt)
{
// used for updating change in velocity from mass matrix change
this->compute_old_time_data();
// update filtered nodal atomic force
timeFilter_->apply_pre_step1(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_,dt);
// store current force for use later
nodalAtomicForce_ = nodalAtomicMomentum_->quantity();
nodalAtomicForce_ *= -1.;
}
//--------------------------------------------------------
// mid_initial_integrate1
// time integration between the velocity and position
// updates in Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::mid_initial_integrate1(double dt)
{
// atomic contributions to change in momentum
// compute change in restricted atomic momentum
const DENS_MAT & nodalAtomicMomentum(nodalAtomicMomentum_->quantity());
nodalAtomicForce_ += nodalAtomicMomentum;
// update FE velocity with change in velocity from MD
DENS_MAT & atomicVelocityDelta(atomicVelocityDelta_.set_quantity());
atc_->apply_inverse_mass_matrix(nodalAtomicForce_,
atomicVelocityDelta,
VELOCITY);
velocity_ += atomicVelocityDelta;
// approximation to force for output
nodalAtomicForce_ /= 0.5*dt;
timeFilter_->apply_post_step1(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_,dt);
// change to velocity from FE dynamics
atc_->apply_inverse_mass_matrix(velocityRhs_.quantity(),
acceleration_.set_quantity(),
VELOCITY);
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),0.5*dt);
// used for updating change in momentum from mass matrix change
atc_->apply_inverse_mass_matrix(nodalAtomicMomentum,
nodalAtomicVelocityOld_,
VELOCITY);
nodalAtomicMomentumOld_ = nodalAtomicMomentum;
}
//--------------------------------------------------------
// post_initial_integrate1
// time integration after Verlet step 1
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::post_initial_integrate1(double dt)
{
// get nodal momentum for second part of force update
nodalAtomicForce_ = nodalAtomicMomentum_->quantity();
nodalAtomicForce_ *= -1.;
// update nodal displacements
explicit_1(displacement_.set_quantity(),velocity_.quantity(),dt);
}
//--------------------------------------------------------
// post_final_integrate2
// second time integration computations
// after Verlet step 2
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::post_final_integrate2(double dt)
{
// atomic contributions to change in momentum
// compute change in restricted atomic momentum
nodalAtomicForce_ += nodalAtomicMomentum_->quantity();
// update FE temperature with change in temperature from MD
compute_velocity_delta(nodalAtomicForce_,dt);
velocity_ += atomicVelocityDelta_.quantity();
// approximation to power for output
nodalAtomicForce_ /= 0.5*dt;
timeFilter_->apply_post_step1(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_,dt);
// change to velocity from FE dynamics
atc_->apply_inverse_mass_matrix(velocityRhs_.quantity(),
acceleration_.set_quantity(),
VELOCITY);
explicit_1(velocity_.set_quantity(),acceleration_.quantity(),0.5*dt);
}
//--------------------------------------------------------
// post_process
// post processing of variables before output
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::post_process()
{
nodalAtomicDisplacementOut_ = nodalAtomicDisplacement_->quantity();
nodalAtomicVelocityOut_ = nodalAtomicVelocity_->quantity();
}
//--------------------------------------------------------
// output
// add variables to output list
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::output(OUTPUT_LIST & outputData)
{
if ((atc_->lammps_interface())->rank_zero()) {
outputData["NodalAtomicForce"] = & nodalAtomicForce_;
}
}
//--------------------------------------------------------
// finish
// finalize state of nodal atomic quantities
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::finish()
{
post_process();
}
//--------------------------------------------------------
// compute_old_time_data
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::compute_old_time_data()
{
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
atc_->apply_inverse_mass_matrix(myNodalAtomicMomentum,
nodalAtomicVelocityOld_,
VELOCITY);
nodalAtomicMomentumOld_ = myNodalAtomicMomentum;
}
//--------------------------------------------------------
// compute_velocity_delta
//--------------------------------------------------------
void ElasticTimeIntegratorFractionalStep::compute_velocity_delta(const DENS_MAT & atomicMomentumDelta,
double dt)
{
DENS_MAT & myAtomicVelocityDelta(atomicVelocityDelta_.set_quantity());
myAtomicVelocityDelta = nodalAtomicMomentumOld_ + atomicMomentumDelta;
atc_->apply_inverse_mass_matrix(myAtomicVelocityDelta,
VELOCITY);
myAtomicVelocityDelta += -1.*nodalAtomicVelocityOld_;
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class FluidsTimeIntegratorGear
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
// Grab data from ATC
//--------------------------------------------------------
FluidsTimeIntegratorGear::FluidsTimeIntegratorGear(MomentumTimeIntegrator * momentumTimeIntegrator) :
MomentumIntegrationMethod(momentumTimeIntegrator),
nodalAtomicForceFiltered_(momentumTimeIntegrator->nodal_atomic_force_filtered()),
nodalAtomicMomentum_(NULL),
nodalAtomicMomentumFiltered_(momentumTimeIntegrator->nodal_atomic_momentum_filtered()),
atomicVelocityDelta_(atc_->num_nodes(),atc_->nsd()),
nodalAtomicMomentumOld_(atc_->num_nodes(),atc_->nsd()),
nodalAtomicVelocityOld_(atc_->num_nodes(),atc_->nsd()),
velocity2Roc_(atc_->field_2roc(VELOCITY))
{
// do nothing
}
//--------------------------------------------------------
// construct_transfers
// Grab existing managed quantities,
// create the rest
//--------------------------------------------------------
void FluidsTimeIntegratorGear::construct_transfers()
{
MomentumIntegrationMethod::construct_transfers();
InterscaleManager & interscaleManager(atc_->interscale_manager());
nodalAtomicMomentum_ = interscaleManager.dense_matrix("NodalAtomicMomentum");
}
//--------------------------------------------------------
// initialize
// initialize all data
//--------------------------------------------------------
void FluidsTimeIntegratorGear::initialize()
{
MomentumIntegrationMethod::initialize();
// initial power to zero
nodalAtomicForce_.reset(atc_->num_nodes(),atc_->nsd());
// sets up time filter for cases where variables temporally filtered
// this time integrator should use Crank-Nicholson filter for 2nd order accuracy
TimeFilterManager * timeFilterManager = atc_->time_filter_manager();
if (timeFilterManager->need_reset()) {
// the form of this integrator implies no time filters that require history data can be used
timeFilter_->initialize();
}
// sets up time filter for post-processing the filtered power
// this time integrator should use an explicit-implicit filter
// to mirror the 2nd order Verlet integration scheme
// It requires no history information so initial value just sizes arrays
if (!timeFilterManager->end_equilibrate()) {
// implies an initial condition of the instantaneous atomic energy
// for the corresponding filtered variable, consistent with the temperature
nodalAtomicMomentumFiltered_ = nodalAtomicMomentum_->quantity();
nodalAtomicForceFiltered_.reset(atc_->num_nodes(),atc_->nsd());
}
}
//--------------------------------------------------------
// pre_initial_integrate1
//--------------------------------------------------------
void FluidsTimeIntegratorGear::pre_initial_integrate1(double dt)
{
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
// updated filtered momentum using explicit-implicit scheme
timeFilter_->apply_pre_step1(nodalAtomicMomentumFiltered_.set_quantity(),
myNodalAtomicMomentum,dt);
}
//--------------------------------------------------------
// pre_initial_integrate2
//--------------------------------------------------------
void FluidsTimeIntegratorGear::pre_initial_integrate2(double dt)
{
// used for updating change in velocity from mass matrix change
this->compute_old_time_data();
// update FE contributions
apply_gear_predictor(dt);
// update filtered nodal atomic force
// that way kinetostat and integrator can be consistent
timeFilter_->apply_pre_step1(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_,dt);
// store current momentum for use later
nodalAtomicForce_ = nodalAtomicMomentum_->quantity();
nodalAtomicForce_ *= -1.;
}
//--------------------------------------------------------
// pre_final_integrate1
//--------------------------------------------------------
void FluidsTimeIntegratorGear::pre_final_integrate1(double dt)
{
// before the new rhs is computed but after atomic velocity is updated.
// compute change in restricted atomic momentum
nodalAtomicForce_ += nodalAtomicMomentum_->quantity();
// update FE velocity with change in velocity from MD
compute_velocity_delta(nodalAtomicForce_,dt);
velocity_ += atomicVelocityDelta_.quantity();
// approximation to force for output
nodalAtomicForce_ /= dt;
timeFilter_->apply_post_step1(nodalAtomicForceFiltered_.set_quantity(),
nodalAtomicForce_,dt);
// make sure nodes are fixed
atc_->set_fixed_nodes();
}
//--------------------------------------------------------
// post_final_integrate1
//--------------------------------------------------------
void FluidsTimeIntegratorGear::post_final_integrate1(double dt)
{
// Finish updating temperature with FE contributions
atc_->apply_inverse_mass_matrix(velocityRhs_.quantity(),
_velocityResidual_,VELOCITY);
_velocityResidual_ -= acceleration_.quantity();
_velocityResidual_ *= dt;
apply_gear_corrector(_velocityResidual_,dt);
}
//--------------------------------------------------------
// post_process
//--------------------------------------------------------
void FluidsTimeIntegratorGear::post_final_integrate2(double dt)
{
// update filtered atomic energy
timeFilter_->apply_post_step1(nodalAtomicMomentumFiltered_.set_quantity(),
nodalAtomicMomentum_->quantity(),dt);
}
//--------------------------------------------------------
// output
// add variables to output list
//--------------------------------------------------------
void FluidsTimeIntegratorGear::post_process()
{
nodalAtomicForceOut_ = nodalAtomicForce_;
nodalAtomicVelocityOut_ = nodalAtomicVelocity_->quantity();
}
//--------------------------------------------------------
// output
// add variables to output list
//--------------------------------------------------------
void FluidsTimeIntegratorGear::output(OUTPUT_LIST & outputData)
{
if ((atc_->lammps_interface())->rank_zero()) {
outputData["NodalAtomicForce"] = & nodalAtomicForce_;
}
}
//--------------------------------------------------------
// finish
// finalize state of nodal atomic quantities
//--------------------------------------------------------
void FluidsTimeIntegratorGear::finish()
{
post_process();
}
//--------------------------------------------------------
// apply_gear_predictor
//--------------------------------------------------------
void FluidsTimeIntegratorGear::apply_gear_predictor(double dt)
{
gear1_3_predict(velocity_.set_quantity(),
acceleration_.set_quantity(),
velocity2Roc_.quantity(),dt);
}
//--------------------------------------------------------
// apply_gear_corrector
//--------------------------------------------------------
void FluidsTimeIntegratorGear::apply_gear_corrector(const DENS_MAT & residual, double dt)
{
gear1_3_correct(velocity_.set_quantity(),
acceleration_.set_quantity(),
velocity2Roc_.set_quantity(),
residual,dt);
}
//--------------------------------------------------------
// compute_old_time_data
//--------------------------------------------------------
void FluidsTimeIntegratorGear::compute_old_time_data()
{
const DENS_MAT & myNodalAtomicMomentum(nodalAtomicMomentum_->quantity());
atc_->apply_inverse_mass_matrix(myNodalAtomicMomentum,
nodalAtomicVelocityOld_,
VELOCITY);
nodalAtomicMomentumOld_ = myNodalAtomicMomentum;
}
//--------------------------------------------------------
// compute_velocity_delta
//--------------------------------------------------------
void FluidsTimeIntegratorGear::compute_velocity_delta(const DENS_MAT & atomicMomentumDelta,
double dt)
{
DENS_MAT & myAtomicVelocityDelta(atomicVelocityDelta_.set_quantity());
myAtomicVelocityDelta = nodalAtomicMomentumOld_ + atomicMomentumDelta;
atc_->apply_inverse_mass_matrix(myAtomicVelocityDelta,
VELOCITY);
myAtomicVelocityDelta -= nodalAtomicVelocityOld_;
}
};

View File

@ -0,0 +1,412 @@
#ifndef ELASTIC_TIME_INTEGRATOR_H
#define ELASTIC_TIME_INTEGRATOR_H
/** MomentumTimeIntegrator : a class to implement various elasticity integrators for FE quantities */
// ATC headers
#include "TimeIntegrator.h"
using namespace std;
namespace ATC {
// forward declarations
class MomentumIntegrationMethod;
/**
* @class MomentumTimeIntegrator
* @brief Base class for various time integrators for elasticity FE quantities which handles parsing and stores basic data structures
*/
class MomentumTimeIntegrator : public TimeIntegrator {
public:
// constructor
MomentumTimeIntegrator(ATC_Coupling * atc,
TimeIntegrationType timeIntegrationType);
// destructor
virtual ~MomentumTimeIntegrator(){};
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** create objects to implement requested numerical method */
virtual void construct_methods();
/** pack persistent fields */
virtual void pack_fields(RESTART_LIST & data);
// Member data access
/** access for filtered atomic force */
DENS_MAN & nodal_atomic_force_filtered(){return nodalAtomicForceFiltered_;};
/** access for filtered atomic momentum */
// note: nodalAtomicMomentum_ should always be reset as it tracks the original momentum + MD evolution
DENS_MAN & nodal_atomic_momentum_filtered(){return nodalAtomicMomentumFiltered_;};
protected:
/** filtered atomic force */
DENS_MAN nodalAtomicForceFiltered_;
/** filtered atomic momentum due initial conditions and MD updates */
DENS_MAN nodalAtomicMomentumFiltered_;
private:
// DO NOT define this
MomentumTimeIntegrator();
};
/**
* @class MomentumIntegrationMethod
* @brief Base class for various time integration methods for the momentum equation
*/
class MomentumIntegrationMethod : public TimeIntegrationMethod {
public:
// constructor
MomentumIntegrationMethod(MomentumTimeIntegrator * momentumTimeIntegrator);
// destructor
virtual ~MomentumIntegrationMethod(){};
/** create and get necessary transfer operators */
virtual void construct_transfers();
/** checks to see if first RHS computation is needed */
virtual bool has_final_predictor() {return true;};
protected:
/** time filtering object */
TimeFilter * timeFilter_;
/** finite element velocity field */
DENS_MAN & velocity_;
/** finite element acceleration field */
DENS_MAN & acceleration_;
/** atomic nodal velocity field */
DENS_MAN & nodalAtomicVelocityOut_;
/** right-hand side of velocity equation */
DENS_MAN & velocityRhs_;
/** force at nodes from atomic quantities */
DENS_MAN & nodalAtomicForceOut_;
/** transfer for computing nodal atomic velocity */
DENS_MAN * nodalAtomicVelocity_;
private:
// DO NOT define this
MomentumIntegrationMethod();
};
/**
* @class ElasticTimeIntegratorVerlet
* @brief Verlet integration for FE elastic quantities. Uses the second order Verlet integration to update the finite element velocity and displacement fields, i.e. the same integration used for the atomic velocities and positions.
*/
class ElasticTimeIntegratorVerlet : public MomentumIntegrationMethod {
public:
// constructor
ElasticTimeIntegratorVerlet(MomentumTimeIntegrator * momentumTimeIntegrator);
// destructor
virtual ~ElasticTimeIntegratorVerlet(){};
/** create and get necessary transfer operators */
virtual void construct_transfers();
/** pre time integration initialization of data */
virtual void initialize();
// time step methods, corresponding to ATC_Transfer
/** first part of mid_initial_integrate */
virtual void mid_initial_integrate1(double dt);
/** first part of post_initial_integrate */
virtual void post_initial_integrate1(double dt);
/** first part of pre_final_integrate */
virtual void pre_final_integrate1(double dt);
/** second part of post_final_integrate */
virtual void post_final_integrate2(double dt);
/** adds any contributions from time integrator to RHS */
virtual void add_to_rhs();
/** post processing step before output */
virtual void post_process();
/** add output data */
virtual void output(OUTPUT_LIST & outputData);
/** operations at end of a run */
virtual void finish();
protected:
/** finite element displacement field */
DENS_MAN & displacement_;
/** atomic nodal displacement field */
DENS_MAN & nodalAtomicDisplacementOut_;
/** filtered atomic force */
DENS_MAN & nodalAtomicForceFiltered_;
/** transfer for computing atomic displacement */
DENS_MAN * nodalAtomicDisplacement_;
/** transfer for computing nodal atomic force */
DENS_MAN * nodalAtomicForce_;
private:
// DO NOT define this
ElasticTimeIntegratorVerlet();
};
/**
* @class ElasticTimeIntegratorVerlet
* @brief Verlet integration for FE elastic quantities with time filtering
*/
class ElasticTimeIntegratorVerletFiltered : public ElasticTimeIntegratorVerlet {
public:
// constructor
ElasticTimeIntegratorVerletFiltered(MomentumTimeIntegrator * momentumTimeIntegrator);
// destructor
virtual ~ElasticTimeIntegratorVerletFiltered(){};
// time step methods, corresponding to ATC_Transfer
/** first part of mid_initial_integrate */
virtual void mid_initial_integrate1(double dt);
/** first part of post_initial_integrate */
virtual void post_initial_integrate1(double dt);
/** second part of post_final_integrate */
virtual void post_final_integrate2(double dt);
/** adds any contributions from time integrator to RHS */
virtual void add_to_rhs();
/** post processing step before output */
virtual void post_process(){};
/** add output data */
virtual void output(OUTPUT_LIST & outputData);
protected:
/** atomic nodal acceleration field */
DENS_MAN & nodalAtomicAcceleration_;
private:
// DO NOT define this
ElasticTimeIntegratorVerletFiltered();
};
/**
* @class ElasticTimeIntegratorFractionalStep
* @brief Class for using 2nd order Verlet integration to update FE contributions to momentum field
* (Uses same update for the atomic contributions to the finite
* elements as are used by the LAMMPS integration scheme
* for the atomic velocities and positions, i.e. Verlet.)
*/
class ElasticTimeIntegratorFractionalStep : public MomentumIntegrationMethod {
public:
// constructor
ElasticTimeIntegratorFractionalStep(MomentumTimeIntegrator * momentumTimeIntegrator);
// destructor
virtual ~ElasticTimeIntegratorFractionalStep() {};
/** create and get necessary transfer operators */
virtual void construct_transfers();
/** pre time integration initialization of data */
virtual void initialize();
// time step methods, corresponding to ATC_Transfer
/** first part of pre_initial_integrate */
virtual void pre_initial_integrate1(double dt);
/** second part of mid_initial_integrate */
virtual void pre_initial_integrate2(double dt);
/** first part of mid_initial_integrate */
virtual void mid_initial_integrate1(double dt);
/** first part of post_initial_integrate */
virtual void post_initial_integrate1(double dt);
/** second part of post_final_integrate */
virtual void post_final_integrate2(double dt);
/** post processing step before output */
virtual void post_process();
/** finalize state of some unfiltered variables */
virtual void finish();
/** add output data */
virtual void output(OUTPUT_LIST & outputData);
protected:
// methods
/** compute old energy and temperature for use in time integrators */
virtual void compute_old_time_data();
/** computes temperature change associated with atomic energy change */
virtual void compute_velocity_delta(const DENS_MAT & atomicMomentumDelta,
double dt);
// data
/** finite element displacement field */
DENS_MAN & displacement_;
/** atomic nodal displacement field */
DENS_MAN & nodalAtomicDisplacementOut_;
/** equivalent nodal force due to atomic momentum change */
DENS_MAT nodalAtomicForce_;
/** filtered atomic force */
DENS_MAN & nodalAtomicForceFiltered_;
/** transfer for computing atomic momentum */
DENS_MAN * nodalAtomicMomentum_;
/** filtered atomic momentum */
DENS_MAN & nodalAtomicMomentumFiltered_;
/** transfer for computing atomic displacement */
DENS_MAN * nodalAtomicDisplacement_;
/** change in FE velocity due to atomic motions */
DENS_MAN atomicVelocityDelta_;
/** restricted atomic momentum from previous time step */
DENS_MAT nodalAtomicMomentumOld_;
/** FE atomic velocity contribution from previous time step */
DENS_MAT nodalAtomicVelocityOld_;
private:
// DO NOT define this
ElasticTimeIntegratorFractionalStep();
};
/**
* @class FluidsTimeIntegratorGear
* @brief Class for using 3rd order Gear integration to update FE contributions to momentum field
* and fractional step method for atomic contributions
*/
class FluidsTimeIntegratorGear : public MomentumIntegrationMethod {
public:
// constructor
FluidsTimeIntegratorGear(MomentumTimeIntegrator * MomentumTimeIntegrator);
// destructor
virtual ~FluidsTimeIntegratorGear() {};
/** create and get necessary transfer operators */
virtual void construct_transfers();
/** pre time integration initialization of data */
virtual void initialize();
// time step methods, corresponding to ATC_Transfer
/** 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 pre_final_integrate */
virtual void pre_final_integrate1(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);
/** post processing step before output */
virtual void post_process();
/** finalize state of some unfiltered variables */
virtual void finish();
/** add output data */
virtual void output(OUTPUT_LIST & outputData);
protected:
// methods
/** applies Gear predictor */
virtual void apply_gear_predictor(double dt);
/** applies Gear corrector */
virtual void apply_gear_corrector(const DENS_MAT & R_theta,
double dt);
/** compute old energy and temperature for use in time integrators */
virtual void compute_old_time_data();
/** computes temperature change associated with atomic energy change */
virtual void compute_velocity_delta(const DENS_MAT & atomicMomentumDelta,
double dt);
// data
/** equivalent nodal force due to atomic momentum change */
DENS_MAT nodalAtomicForce_;
/** filtered atomic force */
DENS_MAN & nodalAtomicForceFiltered_;
/** transfer for computing atomic momentum */
DENS_MAN * nodalAtomicMomentum_;
/** filtered atomic momentum */
DENS_MAN & nodalAtomicMomentumFiltered_;
/** change in FE velocity due to atomic motions */
DENS_MAN atomicVelocityDelta_;
/** restricted atomic momentum from previous time step */
DENS_MAT nodalAtomicMomentumOld_;
/** FE atomic velocity contribution from previous time step */
DENS_MAT nodalAtomicVelocityOld_;
/** finite element velocity 2nd time derivative */
DENS_MAN & velocity2Roc_;
// workspace for gear integration
DENS_MAT _velocityResidual_;
private:
// DO NOT define this
FluidsTimeIntegratorGear();
};
};
#endif

View File

@ -0,0 +1,127 @@
#include "ElectronChargeDensity.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
using ATC_Utility::str2int;
namespace ATC {
ElectronChargeDensityInterpolation::ElectronChargeDensityInterpolation(
fstream &fileId, map<string,double> & parameters)
: ElectronChargeDensity(), n_()
{
if (!fileId.is_open()) throw ATC_Error("cannot open material file");
vector<string> line;
int npts = 0;
double coef = 1.;
while(fileId.good()) {
command_line(fileId, line);
if (line.size() == 0) continue;
if (line[0] == "end") return;
else if (line[0] == "scale") coef = str2dbl(line[1]);
else if (line[0] == "number_of_points") {
npts = str2int(line[1]);
n_.initialize(npts,fileId,coef);
}
}
}
ElectronChargeDensityLinear::ElectronChargeDensityLinear(
fstream &fileId, map<string,double> & parameters)
: ElectronChargeDensity()
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "coefficient") {
C_ = value;
parameters["coefficient"] = C_;
}
}
}
ElectronChargeDensityExponential::ElectronChargeDensityExponential(
fstream &fileId, map<string,double> & parameters)
: ElectronChargeDensity(),
intrinsicConcentration_(0),
intrinsicEnergy_(0),
referenceTemperature_(0)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "intrinsic_concentration") {
intrinsicConcentration_ = value;
parameters["intrinsic_concentration"] = intrinsicConcentration_;
}
else if (line[0] == "intrinsic_energy") {
intrinsicEnergy_ = value;
parameters["intrinsic_energy"] = intrinsicEnergy_;
}
else if (line[0] == "reference_temperature") {
referenceTemperature_ = value;
parameters["reference_temperature"] = referenceTemperature_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronChargeDensityFermiDirac::ElectronChargeDensityFermiDirac(
fstream &fileId, map<string,double> & parameters)
: ElectronChargeDensity(),
Ef_(0),
referenceTemperature_(0),
Ed_(0), Nd_(0), Eb_(0),
hasReferenceTemperature_(false),
donorIonization_(false)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "fermi_energy") {
Ef_ = value;
parameters["fermi_energy"] = Ef_;
}
else if (line[0] == "reference_temperature") {
hasReferenceTemperature_ = true;
referenceTemperature_ = value;
parameters["reference_temperature"] = referenceTemperature_;
}
else if (line[0] == "band_edge") {
Eb_ = value;
parameters["band_edge_potential"] = Eb_;
}
else if (line[0] == "donor_ionization_energy") {
donorIonization_ = true;
Ed_ = value;
parameters["donor_ionization_energy"] = Ed_;
}
else if (line[0] == "donor_concentration") {
donorIonization_ = true;
Nd_ = value;
parameters["donor_concentration"] = Nd_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
}

View File

@ -0,0 +1,272 @@
#ifndef ELECTRON_DENSITY_H
#define ELECTRON_DENSITY_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "ATC_TypeDefs.h"
#include "Function.h"
const double tol = 1.e-8;
namespace ATC {
/**
* @class ElectronChargeDensity
* @brief Base class for models of extrinsic electric charges
*/
class ElectronChargeDensity
{
public:
ElectronChargeDensity() {};
virtual ~ElectronChargeDensity() {};
virtual bool electron_charge_density(const FIELD_MATS &fields,
DENS_MAT &flux) const { return false; };
virtual void D_electron_charge_density(const FieldName fieldName,
const FIELD_MATS &fields,
DENS_MAT &flux) const
{ throw ATC_Error("Charge density D_electron_charge_density unimplemented function");}
virtual void band_edge_potential(const FIELD_MATS &fields,
DENS_MAT &density) const
{ throw ATC_Error("Charge density band_edge_potential unimplemented function");}
};
//-----------------------------------------------------------------------
/**
* @class ElectronChargeDensityInterpolation
* @brief Class for models of electron charge density as a tabular function of electric potential
*/
class ElectronChargeDensityInterpolation : public ElectronChargeDensity
{
public:
ElectronChargeDensityInterpolation(fstream &matfile,map<string,double> & parameters);
virtual ~ElectronChargeDensityInterpolation() {};
virtual bool electron_charge_density(const FIELD_MATS &fields,
DENS_MAT &flux) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
flux.reset(nNodes,1,false);
for (int i = 0; i < nNodes; i++) { // a mapping of a vector
flux(i,0) = n_.f(phi(i,0));
}
flux *= -1.;
return true;
};
virtual void D_electron_charge_density(const FieldName field,
const FIELD_MATS &fields,
DENS_MAT &coef) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
coef.reset(nNodes,1,false);
for (int i = 0; i < nNodes; i++) {
coef(i,0) = n_.dfdt(phi(i,0));
coef(i,0) = n_.dfdt(phi(i,0));
}
coef *= -1.;
}
private:
InterpolationFunction n_;
};
//-----------------------------------------------------------------------
/**
* @class ElectronChargeDensityLinear
* @brief Class for models of electron charge density proportional to electric potential
*/
class ElectronChargeDensityLinear : public ElectronChargeDensity
{
public:
ElectronChargeDensityLinear(fstream &matfile,map<string,double> & parameters);
virtual ~ElectronChargeDensityLinear() {};
virtual bool electron_charge_density(const FIELD_MATS &fields,
DENS_MAT &flux) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
flux = phi_field->second;
flux *= -C_;
return true;
};
virtual void D_electron_charge_density(const FieldName field,
const FIELD_MATS &fields,
DENS_MAT &coef) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
coef.reset(nNodes,1,false);
coef = -C_;
}
private:
double C_;
};
//-----------------------------------------------------------------------
/**
* @class ElectronChargeDensityExponential
* @brief Class for models of electron charge density dependent on difference between electric potential and the Fermi level n = n_i exp ( (phi-E_i) / kB T)
*/
class ElectronChargeDensityExponential : public ElectronChargeDensity
{
public:
ElectronChargeDensityExponential(fstream &matfile,map<string,double> & parameters);
virtual ~ElectronChargeDensityExponential() {};
double n(const double phi, double T) const
{
return -intrinsicConcentration_*exp((phi-intrinsicEnergy_)/(kBeV_*T));
}
double dndphi(const double phi, double T) const
{
return n(phi,T)/(kBeV_*T);
}
virtual bool electron_charge_density(const FIELD_MATS &fields,
DENS_MAT &density) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
FIELD_MATS::const_iterator T_field = fields.find(TEMPERATURE);
double T = 300;
bool hasTref = (referenceTemperature_ > 0 );
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
density.resize(nNodes,1);
if (hasTref) {
T = referenceTemperature_;
for (int i = 0; i < nNodes; i++) {
density(i,0) = n(phi(i,0),T); }
}
else {
const DENS_MAT & temp = T_field->second;
for (int i = 0; i < nNodes; i++) {
density(i,0) = n(phi(i,0),temp(i,0)); }
}
density *= -1.;
return true;
};
virtual void D_electron_charge_density(const FieldName field,
const FIELD_MATS &fields,
DENS_MAT &coef) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
FIELD_MATS::const_iterator T_field = fields.find(TEMPERATURE);
double T = 300;
bool hasTref = (referenceTemperature_ > 0 );
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
coef.resize(nNodes,1);
if (hasTref) {
T = referenceTemperature_;
for (int i = 0; i < nNodes; i++) {
coef(i,0) = dndphi(phi(i,0),T); }
}
else {
const DENS_MAT & temp = T_field->second;
for (int i = 0; i < nNodes; i++) {
coef(i,0) = dndphi(phi(i,0),temp(i,0)); }
}
coef *= -1.;
};
protected:
double intrinsicConcentration_,intrinsicEnergy_;
double referenceTemperature_;
};
//-----------------------------------------------------------------------
/**
* @class ElectronChargeDensityFermiDirac
* @brief Class for models of electron charge density based on Fermi-Dirac statistics
*/
class ElectronChargeDensityFermiDirac : public ElectronChargeDensity
{
public:
ElectronChargeDensityFermiDirac(fstream &matfile,map<string,double> & parameters);
virtual ~ElectronChargeDensityFermiDirac() {};
double fermi_dirac(const double E, const double T) const
{
double f = 1.0;
if (T > 0) f = 1.0 / ( exp((E-Ef_)/kBeV_/T)+1.0 );
else if (E > Ef_) f = 0;
return f;
};
virtual bool electron_charge_density(const FIELD_MATS &fields,
DENS_MAT &density) const
{
// psi : the inhomogeneous solution
FIELD_MATS::const_iterator psi_field = fields.find(ELECTRON_WAVEFUNCTION);
const DENS_MAT & psi = psi_field->second;
FIELD_MATS::const_iterator psis_field = fields.find(ELECTRON_WAVEFUNCTIONS);
// if (psis_field==fields.end())
//throw ATC_Error("Wavefunctions not defined");
const DENS_MAT & psis = psis_field->second;
FIELD_MATS::const_iterator E_field = fields.find(ELECTRON_WAVEFUNCTION_ENERGIES);
const DENS_MAT & Es = E_field->second;
FIELD_MATS::const_iterator T_field = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & Ts = T_field->second;
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = phi_field->second;
int nNodes = psi.nRows();
density.reset(nNodes,1);
double T = referenceTemperature_;
int count = 0;
for (int i = 0; i < nNodes; i++) {
if (!hasReferenceTemperature_) { T = Ts(i,0); }
int j = 0;
for (j = 0; j < psis.nCols(); j++) {
double E = Es(j,0); // Eigenvalue
double f = fermi_dirac(E,T);
if (f < tol) break;
else count++;
density(i,0) -= psis(i,j)*psis(i,j)*f; // < 0
}
if (donorIonization_) {
double E = -1.0* phi(i,0);// units [eV] E = - |e| phi
if ( E + Eb_ > Ef_+Ed_) density(i,0) += Nd_; // > 0
}
}
return true;
};
virtual void D_electron_charge_density(const FieldName fieldName,
const FIELD_MATS &fields,
DENS_MAT &coef) const
{
FIELD_MATS::const_iterator phi_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = phi_field->second;
int nNodes = phi.nRows();
coef.reset(nNodes,1,false);
}
virtual void band_edge_potential(const FIELD_MATS &fields,
DENS_MAT &density) const
{
FIELD_MATS::const_iterator p_field = fields.find(ELECTRIC_POTENTIAL);
const DENS_MAT & phi = p_field->second;
int nNodes = phi.nRows();
density.reset(nNodes,1,false);
density = Eb_;
};
protected:
double Ef_;
double referenceTemperature_;
double Ed_, Nd_;
double Eb_;
bool hasReferenceTemperature_, donorIonization_;
};
}
#endif

View File

@ -0,0 +1,71 @@
#include "ElectronDragPower.h"
#include "Material.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
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;
// -1/tau (m_e * n)
material_->inv_effective_mass(fields,invEffMassWorkspace_);
dragCoef = n;
dragCoef /= invEffMassWorkspace_;
dragCoef *= -electronDragInvTau_;
}
}

View File

@ -0,0 +1,73 @@
#ifndef ELECTRON_DRAG_POWER_H
#define ELECTRON_DRAG_POWER_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "Material.h"
#include "ATC_TypeDefs.h"
namespace ATC {
/**
* @class ElectronDragPower
* @brief Base class for defining the lattice drag power from electrons
*/
class ElectronDragPower
{
public:
ElectronDragPower() {};
virtual ~ElectronDragPower() {};
/** computes drag power */
virtual bool electron_drag_power(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT & flux)
{
return false;
};
virtual void electron_drag_velocity_coefficient(const FIELD_MATS &fields,
DENS_MAT & dragCoef)
{
FIELD_MATS::const_iterator t_field = fields.find(TEMPERATURE);
dragCoef.reset((t_field->second).nRows(),1); // zero out matrix, resize if necessary
};
};
//-------------------------------------------------------------------
/**
* @class ElectronDragPowerLinear
* @brief Class for electron drag that linearly depends on the difference between the electron and lattice velocities
*/
class ElectronDragPowerLinear : public ElectronDragPower
{
public:
ElectronDragPowerLinear(fstream &matfile,
map<string,double> & parameters,
Material * material_);
virtual ~ElectronDragPowerLinear() {};
virtual bool electron_drag_power(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT & flux);
virtual void electron_drag_velocity_coefficient(const FIELD_MATS &fields,
DENS_MAT & dragCoef);
protected:
double electronDragInvTau_;
Material * material_;
// used to avoid unnecessary resizing
DENS_MAT dragCoefWorkspace_;
DENS_MAT invEffMassWorkspace_;
};
}
#endif

94
lib/atc/ElectronFlux.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "ElectronFlux.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
namespace ATC {
ElectronFlux::ElectronFlux() :
maskX_(false),maskY_(false),maskZ_(false)
{}
ElectronFluxLinear::ElectronFluxLinear(
fstream &fileId, map<string,double> & parameters)
: ElectronFlux(),
electronMobility_(0),
electronDiffusivity_(0)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "mobility") {
electronMobility_ = value;
parameters["electron_mobility"] = electronMobility_;
}
else if (line[0] == "diffusivity") {
electronDiffusivity_ = value;
parameters["electron_diffusivity"] = electronDiffusivity_;
}
else if (line[0] == "mask_x") { maskX_ = true; }
else if (line[0] == "mask_y") { maskY_ = true; }
else if (line[0] == "mask_z") { maskZ_ = true; }
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronFluxThermopower::ElectronFluxThermopower(
fstream &fileId, map<string,double> & parameters)
: ElectronFlux(),
electronMobility_(0),
seebeckCoef_(0)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "mobility") {
electronMobility_ = value;
parameters["electron_mobility"] = electronMobility_;
}
else if (line[0] == "seebeck") {
seebeckCoef_ = value;
parameters["seebeck_coefficient"] = seebeckCoef_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronFluxConvection::ElectronFluxConvection(
fstream &fileId, map<string,double> & parameters)
: ElectronFlux()
{
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") return;
else if (line[0] == "mask_x") { maskX_ = true; }
else if (line[0] == "mask_y") { maskY_ = true; }
else if (line[0] == "mask_z") { maskZ_ = true; }
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
}

228
lib/atc/ElectronFlux.h Normal file
View File

@ -0,0 +1,228 @@
#ifndef ELECTRON_FLUX_H
#define ELECTRON_FLUX_H
#include <map>
#include <string>
using std::map;
using std::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(fstream &matfile, map<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
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] += -electronDiffusivity_* dn[0];
}
if (maskY_)
flux[1] = 0.;
else {
flux[1] *= -electronMobility_* dphi[1] ;
flux[1] += -electronDiffusivity_* dn[1];
}
if (maskZ_)
flux[2] = 0.;
else {
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(fstream &matfile,map<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(fstream &matfile,map<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

View File

@ -0,0 +1,73 @@
#include "ElectronHeatCapacity.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
namespace ATC {
ElectronHeatCapacityConstant::ElectronHeatCapacityConstant(
fstream &fileId, map<string,double> & parameters)
: ElectronHeatCapacity(),
electronHeatCapacity_(0)
{
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") return;
else if (line[0] == "capacity") {
electronHeatCapacity_ = str2dbl(line[1]);
parameters["electron_heat_capacity"] = electronHeatCapacity_;
}
else {
throw ATC_Error( "unrecognized material function:" + line[0]);
}
}
}
ElectronHeatCapacityLinear::ElectronHeatCapacityLinear(
fstream &fileId, map<string,double> & parameters)
: ElectronHeatCapacity(),
electronHeatCapacity_(0)
{
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") return;
else if (line[0] == "capacity") {
electronHeatCapacity_ = str2dbl(line[1]);
parameters["electron_heat_capacity"] = electronHeatCapacity_;
}
else {
throw ATC_Error( "unrecognized material function: " + line[0]);
}
}
}
ElectronHeatCapacityConstantAddDensity::ElectronHeatCapacityConstantAddDensity(fstream &fileId,
map<string,double> & parameters,
Material * material)
: ElectronHeatCapacityConstant(fileId, parameters),
material_(material)
{
// do nothing
}
ElectronHeatCapacityLinearAddDensity::ElectronHeatCapacityLinearAddDensity(fstream &fileId,
map<string,double> & parameters,
Material * material)
: ElectronHeatCapacityLinear(fileId, parameters),
material_(material)
{
// do nothing
}
}

View File

@ -0,0 +1,229 @@
#ifndef ELECTRON_HEAT_CAPACITY_H
#define ELECTRON_HEAT_CAPACITY_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "ATC_TypeDefs.h"
#include "Material.h"
namespace ATC {
/**
* @class ElectronHeatCapacity
* @brief Base class for defining the heat capcity of the electron gas
*/
class ElectronHeatCapacity
{
public:
ElectronHeatCapacity() {};
virtual ~ElectronHeatCapacity() {};
/** computes heat capacity */
virtual void electron_heat_capacity(const FIELD_MATS &fields,
DENS_MAT &capacity)=0;
/** derivative of electron heat capacity */
virtual void D_electron_heat_capacity(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC & Dcapacity)=0;
/** computes thermal energy */
virtual void electron_thermal_energy(const FIELD_MATS &fields,
DENS_MAT &energy)=0;
};
//-------------------------------------------------------------------
/**
* @class ElectronHeatCapacityConstant
* @brief Class for a constant electron heat capacity
*/
class ElectronHeatCapacityConstant : public ElectronHeatCapacity
{
public:
ElectronHeatCapacityConstant(fstream &matfile,
map<string,double> & parameters);
virtual ~ElectronHeatCapacityConstant() {};
virtual void electron_heat_capacity(const FIELD_MATS &fields,
DENS_MAT &capacity)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = etField->second;
capacity.resize(T.nRows(),T.nCols());
capacity = electronHeatCapacity_;
};
virtual void D_electron_heat_capacity(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC & Dcapacity)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
zeroWorkspace_.reset((etField->second).nRows(),(etField->second).nCols());
Dcapacity[0] = zeroWorkspace_;
Dcapacity[1] = zeroWorkspace_;
Dcapacity[2] = zeroWorkspace_;
}
virtual void electron_thermal_energy(const FIELD_MATS &fields,
DENS_MAT &energy)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = etField->second;
energy = electronHeatCapacity_ * T;
};
protected:
double electronHeatCapacity_;
DENS_MAT zeroWorkspace_;
};
//-------------------------------------------------------------------
/**
* @class ElectronHeatCapacityLinear
* @brief Class for an electron capacity that is directly proportional to the electron temperature
*/
class ElectronHeatCapacityLinear : public ElectronHeatCapacity
{
public:
ElectronHeatCapacityLinear(fstream &matfile,
map<string,double> & parameters);
virtual ~ElectronHeatCapacityLinear() {};
virtual void electron_heat_capacity(const FIELD_MATS &fields,
DENS_MAT &capacity)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = etField->second;
capacity = electronHeatCapacity_*T;
};
virtual void D_electron_heat_capacity(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &Dcapacity)
{
GRAD_FIELD_MATS::const_iterator dEtField = gradFields.find(ELECTRON_TEMPERATURE);
const DENS_MAT_VEC & dT = dEtField->second;
Dcapacity[0] = electronHeatCapacity_ * dT[0];
Dcapacity[1] = electronHeatCapacity_ * dT[1];
Dcapacity[2] = electronHeatCapacity_ * dT[2];
}
virtual void electron_thermal_energy(const FIELD_MATS &fields,
DENS_MAT &energy)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = etField->second;
energy = electronHeatCapacity_ * T;
energy *= T;
};
protected:
double electronHeatCapacity_;
};
//-------------------------------------------------------------------
/**
* @class ElectronHeatCapacityConstantAddDensity
* @brief Class for a constant electron specific heat capacity (i.e, does not include the electron density)
*/
class ElectronHeatCapacityConstantAddDensity : public ElectronHeatCapacityConstant
{
public:
ElectronHeatCapacityConstantAddDensity(fstream &matfile,
map<string,double> & parameters,
Material * material);
virtual ~ElectronHeatCapacityConstantAddDensity() {};
virtual void electron_heat_capacity(const FIELD_MATS &fields,
DENS_MAT &capacity)
{
ElectronHeatCapacityConstant::electron_heat_capacity(fields,capacity);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
capacity = capacity.mult_by_element(density);
};
virtual void D_electron_heat_capacity(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &Dcapacity)
{
ElectronHeatCapacityConstant::D_electron_heat_capacity(fields,gradFields,Dcapacity);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
Dcapacity[0] *= density;
Dcapacity[1] *= density;
Dcapacity[2] *= density;
GRAD_FIELD_MATS::const_iterator dEdField = gradFields.find(ELECTRON_DENSITY);
const DENS_MAT_VEC & Ddensity = dEdField->second;
ElectronHeatCapacityConstant::electron_heat_capacity(fields,capacityMat_);
Dcapacity[0] += Ddensity[0].mult_by_element(capacityMat_);
Dcapacity[1] += Ddensity[1].mult_by_element(capacityMat_);
Dcapacity[2] += Ddensity[2].mult_by_element(capacityMat_);
}
virtual void electron_thermal_energy(const FIELD_MATS &fields,
DENS_MAT &energy)
{
ElectronHeatCapacityConstant::electron_thermal_energy(fields,energy);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
energy *= density;
};
protected:
Material * material_;
DENS_MAT capacityMat_; // avoid resizing if possible
};
//-------------------------------------------------------------------
/**
* @class ElectronHeatCapacityLinearAddDensity
* @brief Class for a electron specific heat capacity that is proportional to the temperature (i.e., does not include density)
*/
class ElectronHeatCapacityLinearAddDensity : public ElectronHeatCapacityLinear
{
public:
ElectronHeatCapacityLinearAddDensity(fstream &matfile,
map<string,double> & parameters,
Material * material);
virtual ~ElectronHeatCapacityLinearAddDensity() {};
virtual void electron_heat_capacity(const FIELD_MATS &fields,
DENS_MAT &capacity)
{
ElectronHeatCapacityLinear::electron_heat_capacity(fields,capacity);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
capacity *= density;
};
virtual void D_electron_heat_capacity(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &Dcapacity)
{
ElectronHeatCapacityLinear::D_electron_heat_capacity(fields,gradFields,Dcapacity);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
Dcapacity[0] *= density;
Dcapacity[1] *= density;
Dcapacity[2] *= density;
GRAD_FIELD_MATS::const_iterator dEdField = gradFields.find(ELECTRON_DENSITY);
const DENS_MAT_VEC & Ddensity = dEdField->second;
ElectronHeatCapacityLinear::electron_heat_capacity(fields,capacityWorkspace_);
Dcapacity[0] += Ddensity[0].mult_by_element(capacityWorkspace_);
Dcapacity[1] += Ddensity[1].mult_by_element(capacityWorkspace_);
Dcapacity[2] += Ddensity[2].mult_by_element(capacityWorkspace_);
}
virtual void electron_thermal_energy(const FIELD_MATS &fields,
DENS_MAT &energy)
{
ElectronHeatCapacityLinear::electron_thermal_energy(fields,energy);
FIELD_MATS::const_iterator edField = fields.find(ELECTRON_DENSITY);
const DENS_MAT & density = edField->second;
energy *= density;
};
protected:
Material * material_;
DENS_MAT capacityWorkspace_; // avoid resizing if possible
};
}
#endif

View File

@ -0,0 +1,90 @@
#include "ElectronHeatFlux.h"
#include "ATC_Error.h"
#include <iostream>
#include <fstream>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
namespace ATC {
ElectronHeatFlux::ElectronHeatFlux(ElectronHeatCapacity * electronHeatCapacity)
:
electronHeatCapacity_(electronHeatCapacity)
{
// do nothing
}
ElectronHeatFluxLinear::ElectronHeatFluxLinear(fstream &fileId, map<string,double> & parameters,
ElectronHeatCapacity * electronHeatCapacity)
: ElectronHeatFlux(electronHeatCapacity),
conductivity_(0)
{
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") return;
else if (line[0] == "conductivity") {
conductivity_ = str2dbl(line[1]);
parameters["electron_thermal_conductivity"] = conductivity_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronHeatFluxPowerLaw::ElectronHeatFluxPowerLaw(fstream &fileId, map<string,double> & parameters,
ElectronHeatCapacity * electronHeatCapacity)
: ElectronHeatFlux(electronHeatCapacity),
conductivity_(0)
{
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") return;
else if (line[0] == "conductivity") {
conductivity_ = str2dbl(line[1]);
parameters["electron_thermal_conductivity"] = conductivity_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronHeatFluxThermopower::ElectronHeatFluxThermopower(
fstream &fileId, map<string,double> & parameters,
/*const*/ ElectronFlux * electronFlux,
ElectronHeatCapacity * electronHeatCapacity)
: ElectronHeatFlux(electronHeatCapacity),
conductivity_(0),
seebeckCoef_(0),
electronFlux_(electronFlux)
{
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") return;
double value = str2dbl(line[1]);
if (line[0] == "conductivity") {
conductivity_ = value;
parameters["electron_thermal_conductivity"] = conductivity_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
seebeckCoef_ = parameters["seebeck_coefficient"];
}
}
}

182
lib/atc/ElectronHeatFlux.h Normal file
View File

@ -0,0 +1,182 @@
#ifndef ELECTRON_HEAT_FLUX_H
#define ELECTRON_HEAT_FLUX_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "ATC_TypeDefs.h"
#include "ElectronFlux.h"
#include "ElectronHeatCapacity.h"
namespace ATC {
/**
* @class ElectronHeatFlux
* @brief Base class for the electron heat flux
*/
class ElectronHeatFlux
{
public:
ElectronHeatFlux(/*const*/ ElectronHeatCapacity * electronHeatCapacity = NULL);
virtual ~ElectronHeatFlux() {};
/** computes heat flux */
virtual void electron_heat_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 & Te = etField->second;
zeroWorkspace_.reset(Te.nRows(),Te.nCols());
flux[0] = zeroWorkspace_;
flux[1] = zeroWorkspace_;
flux[2] = zeroWorkspace_;
};
void electron_heat_convection(const FIELD_MATS &fields,
DENS_MAT_VEC & flux)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
FIELD_MATS::const_iterator evField = fields.find(ELECTRON_VELOCITY);
const DENS_MAT & Te = etField->second;
const DENS_MAT & v = evField->second;
electronHeatCapacity_->electron_heat_capacity(fields,cpTeWorkspace_);
cpTeWorkspace_ *= Te;
const CLON_VEC vx(v,CLONE_COL,0);
const CLON_VEC vy(v,CLONE_COL,1);
const CLON_VEC vz(v,CLONE_COL,2);
flux[0] = vx;
flux[1] = vy;
flux[2] = vz;
// scale by thermal energy
flux[0] *= cpTeWorkspace_;
flux[1] *= cpTeWorkspace_;
flux[2] *= cpTeWorkspace_;
};
protected:
ElectronHeatCapacity * electronHeatCapacity_;
DENS_MAT zeroWorkspace_;
DENS_MAT cpTeWorkspace_; // hopefully avoid resizing
};
//-----------------------------------------------------------------------
/**
* @class ElectronHeatFluxLinear
* @brief Class for an electron heat flux proportional to the temperature gradient with constant conductivity
*/
class ElectronHeatFluxLinear : public ElectronHeatFlux
{
public:
ElectronHeatFluxLinear(fstream &matfile,map<string,double> & parameters,
/*const*/ ElectronHeatCapacity * electronHeatCapacity = NULL);
virtual ~ElectronHeatFluxLinear() {};
virtual void electron_heat_flux(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &flux)
{
GRAD_FIELD_MATS::const_iterator dEtField = gradFields.find(ELECTRON_TEMPERATURE);
// flux = -ke dTe/dx
const DENS_MAT_VEC & dT = dEtField->second;
flux[0] = -conductivity_ * dT[0];
flux[1] = -conductivity_ * dT[1];
flux[2] = -conductivity_ * dT[2];
};
protected:
double conductivity_;
};
//-----------------------------------------------------------------------
/**
* @class ElectronHeatFluxPowerLaw
* @brief Class for an electron heat flux proportional to the temperature gradient but with a conductivity proportional to the ratio of the electron and phonon temperatures
*/
class ElectronHeatFluxPowerLaw : public ElectronHeatFlux
{
public:
ElectronHeatFluxPowerLaw(fstream &matfile,map<string,double> &parameters,
/*const*/ ElectronHeatCapacity * electronHeatCapacity = NULL);
virtual ~ElectronHeatFluxPowerLaw() {};
virtual void electron_heat_flux(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &flux)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
FIELD_MATS::const_iterator tField = fields.find(TEMPERATURE);
GRAD_FIELD_MATS::const_iterator dEtField = gradFields.find(ELECTRON_TEMPERATURE);
const DENS_MAT_VEC & dT = dEtField->second;
const DENS_MAT & T = tField->second;
const DENS_MAT & Te = etField->second;
// flux = -ke * ( Te / T ) dT;
flux[0] = dT[0];
flux[1] = dT[1];
flux[2] = dT[2];
electronConductivity_ = (-conductivity_* Te) / T;
flux[0] *= electronConductivity_;
flux[1] *= electronConductivity_;
flux[2] *= electronConductivity_;
};
protected:
double conductivity_;
DENS_MAT electronConductivity_; // hopefully avoid resizing
};
//-----------------------------------------------------------------------
/**
* @class ElectronHeatFluxThermopower
* @brief Class for an electron heat flux proportional to the temperature gradient but with a condu
ctivity proportional to the ratio of the electron and phonon temperatures with the thermopower from teh electric current included
*/
class ElectronHeatFluxThermopower : public ElectronHeatFlux
{
public:
ElectronHeatFluxThermopower(fstream &matfile,
map<string,double> & parameters,
/*const*/ ElectronFlux * electronFlux = NULL,
/*const*/ ElectronHeatCapacity * electronHeatCapacity = NULL);
virtual ~ElectronHeatFluxThermopower() {};
virtual void electron_heat_flux(const FIELD_MATS &fields,
const GRAD_FIELD_MATS &gradFields,
DENS_MAT_VEC &flux)
{
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
FIELD_MATS::const_iterator tField = fields.find(TEMPERATURE);
GRAD_FIELD_MATS::const_iterator dEtField = gradFields.find(ELECTRON_TEMPERATURE);
const DENS_MAT_VEC & dT = dEtField->second;
const DENS_MAT & T = tField->second;
const DENS_MAT & Te = etField->second;
// flux = -ke * ( Te / T ) dT + pi J_e;
flux[0] = dT[0];
flux[1] = dT[1];
flux[2] = dT[2];
elecCondWorkspace_ = (-conductivity_* Te) / T;
flux[0] *= elecCondWorkspace_;
flux[1] *= elecCondWorkspace_;
flux[2] *= elecCondWorkspace_;
electronFlux_->electron_flux(fields, gradFields, tmp_);
tmp_[0] *= Te;
tmp_[1] *= Te;
tmp_[2] *= Te;
flux[0] += seebeckCoef_*tmp_[0];
flux[1] += seebeckCoef_*tmp_[1];
flux[2] += seebeckCoef_*tmp_[2];
};
protected:
double conductivity_,seebeckCoef_;
ElectronFlux * electronFlux_;
DENS_MAT elecCondWorkspace_; // hopefully avoid resizing
DENS_MAT_VEC tmp_;
};
}
#endif

View File

@ -0,0 +1,115 @@
#include "Material.h"
#include "ElectronPhononExchange.h"
#include "ATC_Error.h"
#include "LammpsInterface.h"
#include <iostream>
#include <fstream>
#include <math.h>
using ATC_Utility::command_line;
using ATC_Utility::str2dbl;
using ATC_Utility::str2int;
namespace ATC {
ElectronPhononExchangeLinear::ElectronPhononExchangeLinear(
fstream &fileId, map<string,double> & parameters)
: ElectronPhononExchange(),
exchangeCoef_(0)
{
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") return;
else if (line[0] == "coefficient") {
exchangeCoef_ = str2dbl(line[1]);
parameters["electron_phonon_exchange_coefficient"] = exchangeCoef_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronPhononExchangePowerLaw::ElectronPhononExchangePowerLaw(
fstream &fileId, map<string,double> & parameters)
: ElectronPhononExchange(),
exchangeCoef_(0),
exponent_(1)
{
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") return;
else if (line[0] == "coefficient") {
exchangeCoef_ = str2dbl(line[1]);
parameters["electron_phonon_exchange_coefficient"] = exchangeCoef_;
}
else if (line[0] == "exponent") {
exponent_ = str2int(line[1]);
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
}
ElectronPhononExchangeHertel::ElectronPhononExchangeHertel(fstream &fileId,
map<string,double> & parameters,
Material * material)
: ElectronPhononExchange(),
exchangeCoef_(0),
debeyeTemperature_(1),
massEnhancement_(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;
else if (line[0] == "debeye_temperature") {
debeyeTemperature_ = str2dbl(line[1]);
parameters["debeye_temperature"] = debeyeTemperature_;
}
else if (line[0] == "mass_enhancement") {
massEnhancement_ = str2dbl(line[1]);
parameters["mass_enhancement"] = massEnhancement_;
}
else {
throw ATC_Error( "unrecognized material function "+line[0]);
}
}
// coupling coefficient, eqn. 15 of Hertel 2002
double kb = LammpsInterface::instance()->kBoltzmann();
double hbar = LammpsInterface::instance()->hbar();
double PI = 3.141592653589793238;
exchangeCoef_ = 144.*1.0369*kb/(PI*hbar);
exchangeCoef_ *= massEnhancement_/pow(debeyeTemperature_,2);
}
bool ElectronPhononExchangeHertel::electron_phonon_exchange(const FIELD_MATS &fields,
DENS_MAT &flux)
{
FIELD_MATS::const_iterator tField = fields.find(TEMPERATURE);
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = tField->second;
const DENS_MAT & Te = etField->second;
// flux = g C_e (T_e - T_p)^5 / T_e
flux = (Te - T).pow(5);
flux /= Te;
flux *= exchangeCoef_;
material_->electron_heat_capacity(fields,capacityWorkspace_);
flux*= capacityWorkspace_;
return true;
}
}

View File

@ -0,0 +1,119 @@
#ifndef ELECTRON_PHONON_EXCHANGE_H
#define ELECTRON_PHONON_EXCHANGE_H
#include <map>
#include <string>
using std::map;
using std::string;
#include "ATC_TypeDefs.h"
namespace ATC {
class Material;
/**
* @class ElectronPhononExchange
* @brief Base class for energy exchange between the electron and phonon temperatures
*/
class ElectronPhononExchange
{
public:
ElectronPhononExchange() {};
virtual ~ElectronPhononExchange() {};
/** computes heat capacity */
virtual bool electron_phonon_exchange(const FIELD_MATS &fields,
DENS_MAT &flux) { return false; }
};
//-------------------------------------------------------------------
/**
* @class ElectronPhononExchangeLinear
* @brief Class for electron-phonon energy exchange proportional to the difference between the two temperatures
*/
class ElectronPhononExchangeLinear : public ElectronPhononExchange
{
public:
ElectronPhononExchangeLinear(fstream &matfile,
map<string,double> & parameters);
virtual ~ElectronPhononExchangeLinear() {};
virtual bool electron_phonon_exchange(const FIELD_MATS &fields,
DENS_MAT &flux)
{
FIELD_MATS::const_iterator tField = fields.find(TEMPERATURE);
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = tField->second;
const DENS_MAT & Te = etField->second;
// flux = g * ( T- Te)
flux = Te - T;
flux *= exchangeCoef_;
return true;
};
protected:
double exchangeCoef_;
};
//-------------------------------------------------------------------
/**
* @class ElectronPhononExchangePowerLaw
* @brief Class for electron-phonon exchange proportional to the temperature difference raised to a constant power
*/
class ElectronPhononExchangePowerLaw : public ElectronPhononExchange
{
public:
ElectronPhononExchangePowerLaw(fstream &matfile,
map<string,double> & parameters);
virtual ~ElectronPhononExchangePowerLaw() {};
virtual bool electron_phonon_exchange(const FIELD_MATS &fields,
DENS_MAT &flux)
{
FIELD_MATS::const_iterator tField = fields.find(TEMPERATURE);
FIELD_MATS::const_iterator etField = fields.find(ELECTRON_TEMPERATURE);
const DENS_MAT & T = tField->second;
const DENS_MAT & Te = etField->second;
// flux = g c_e T_e (T_e - T_p)^5 / T_e
flux = (Te - T).pow(exponent_);
flux *= exchangeCoef_;
return true;
};
protected:
double exchangeCoef_;
int exponent_;
};
//-------------------------------------------------------------------
/**
* @class ElectronPhononExchangeHertel
* @brief Class for electron-phonon exchange based on the formulation of Hertel for Cu
*/
class ElectronPhononExchangeHertel : public ElectronPhononExchange
{
public:
ElectronPhononExchangeHertel(fstream &matfile,
map<string,double> & parameters,
Material * material);
virtual ~ElectronPhononExchangeHertel() {};
virtual bool electron_phonon_exchange(const FIELD_MATS &fields,
DENS_MAT &flux);
protected:
double exchangeCoef_;
double debeyeTemperature_;
double massEnhancement_;
Material * material_;
private:
ElectronPhononExchangeHertel() {};
DENS_MAT capacityWorkspace_;
};
}
#endif

388
lib/atc/ExtrinsicModel.cpp Normal file
View File

@ -0,0 +1,388 @@
// ATC Headers
#include "ExtrinsicModel.h"
#include "ExtrinsicModelTwoTemperature.h"
#include "ExtrinsicModelDriftDiffusion.h"
#include "ExtrinsicModelElectrostatic.h"
#include "ATC_Error.h"
#include "TimeIntegrator.h"
#include "ATC_Coupling.h"
#include "LammpsInterface.h"
#include "PrescribedDataManager.h"
#include "PhysicsModel.h"
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelManager
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ExtrinsicModelManager::ExtrinsicModelManager(ATC_Coupling * atc) :
atc_(atc)
{
// do nothing
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ExtrinsicModelManager::~ExtrinsicModelManager()
{
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
delete *(imodel);
}
//--------------------------------------------------------
// modify
//--------------------------------------------------------
bool ExtrinsicModelManager::modify(int narg, char **arg)
{
bool foundMatch = false;
// loop over models with command
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
foundMatch = (*imodel)->modify(narg,arg);
if (foundMatch) break;
}
return foundMatch;
}
//--------------------------------------------------------
// create_model
//--------------------------------------------------------
void ExtrinsicModelManager::create_model(ExtrinsicModelType modelType,
string matFileName)
{
string typeName;
bool validModel = model_to_string(modelType,typeName);
if (!validModel) {
throw ATC_Error("Could not create extrinsic model");
return;
}
ExtrinsicModel * myModel;
if (modelType==TWO_TEMPERATURE) {
stringstream ss;
ss << "creating two_temperature extrinsic model";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
myModel = new ExtrinsicModelTwoTemperature
(this,modelType,matFileName);
}
else if (modelType==DRIFT_DIFFUSION
|| modelType==DRIFT_DIFFUSION_EQUILIBRIUM
|| modelType==DRIFT_DIFFUSION_SCHRODINGER
|| modelType==DRIFT_DIFFUSION_SCHRODINGER_SLICE)
{
stringstream ss;
ss << "creating drift_diffusion extrinsic model";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
myModel = new ExtrinsicModelDriftDiffusion
(this,modelType,matFileName);
}
else if (modelType==CONVECTIVE_DRIFT_DIFFUSION
|| modelType==CONVECTIVE_DRIFT_DIFFUSION_EQUILIBRIUM
|| modelType==CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER) {
stringstream ss;
ss << "creating convective_drift_diffusion extrinsic model";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
myModel = new ExtrinsicModelDriftDiffusionConvection
(this,modelType,matFileName);
}
else if (modelType==ELECTROSTATIC || modelType==ELECTROSTATIC_EQUILIBRIUM) {
stringstream ss;
ss << "creating electrostatic extrinsic model";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
myModel = new ExtrinsicModelElectrostaticMomentum
(this,modelType,matFileName);
}
else if (modelType==FEM_EFIELD) {
stringstream ss;
ss << "creating fem_efield extrinsic model";
ATC::LammpsInterface::instance()->print_msg_once(ss.str());
myModel = new ExtrinsicModelElectrostatic
(this,modelType,matFileName);
}
extrinsicModels_.push_back(myModel);
// add new fields to fields data
map<FieldName,int> fieldSizes;
myModel->num_fields(fieldSizes);
atc_->add_fields(fieldSizes);
}
//--------------------------------------------------------
// initialize
//--------------------------------------------------------
void ExtrinsicModelManager::construct_transfers()
{
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
// initialize models
(*imodel)->construct_transfers();
}
}
//--------------------------------------------------------
// initialize
//--------------------------------------------------------
void ExtrinsicModelManager::initialize()
{
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
// initialize models
(*imodel)->initialize();
}
}
//--------------------------------------------------------
// get_model : access to a particular type of model
//--------------------------------------------------------
const ExtrinsicModel * ExtrinsicModelManager::model(const ExtrinsicModelType type) const {
vector<ExtrinsicModel *>::const_iterator imodel;
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) {
if ((*imodel)->model_type()==type) return *imodel;
}
return NULL;
}
//--------------------------------------------------------
// size_vector
//--------------------------------------------------------
int ExtrinsicModelManager::size_vector(int intrinsicSize)
{
int extrinsicSize = 0;
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
// query all models for LAMMPS display
int currentSize = intrinsicSize + extrinsicSize;
extrinsicSize += (*imodel)->size_vector(currentSize);
}
return extrinsicSize;
}
//--------------------------------------------------------
// compute_scalar
//--------------------------------------------------------
double ExtrinsicModelManager::compute_scalar(void)
{
double value = 0.;
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
value += (*imodel)->compute_scalar(); // sum
}
return value;
}
//--------------------------------------------------------
// compute_vector
//--------------------------------------------------------
double ExtrinsicModelManager::compute_vector(int n)
{
double value = 0.;
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin();
imodel!=extrinsicModels_.end(); imodel++) {
// query all models for LAMMPS display
if ((*imodel)->compute_vector(n,value))
break;
}
return value;
}
//--------------------------------------------------------
// finish
//--------------------------------------------------------
void ExtrinsicModelManager::finish()
{
// do nothing
}
//--------------------------------------------------------
// pre_init_integrate
//--------------------------------------------------------
void ExtrinsicModelManager::pre_init_integrate(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->pre_init_integrate();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->pre_init_integrate();
}
}
//--------------------------------------------------------
// mid_init_integrate
//--------------------------------------------------------
void ExtrinsicModelManager::mid_init_integrate(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->mid_init_integrate();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->mid_init_integrate();
}
}
//--------------------------------------------------------
// post_init_integrate
//--------------------------------------------------------
void ExtrinsicModelManager::post_init_integrate(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->post_init_integrate();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->post_init_integrate();
}
}
//--------------------------------------------------------
// post_force
//--------------------------------------------------------
void ExtrinsicModelManager::post_force(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->post_force();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->post_force();
}
}
//--------------------------------------------------------
// pre_final_integrate
//--------------------------------------------------------
void ExtrinsicModelManager::pre_final_integrate(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->pre_final_integrate();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->pre_final_integrate();
}
}
//--------------------------------------------------------
// post_final_integrate
//--------------------------------------------------------
void ExtrinsicModelManager::post_final_integrate(ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->post_final_integrate();
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->post_final_integrate();
}
}
//--------------------------------------------------------
// set_sources
//--------------------------------------------------------
void ExtrinsicModelManager::set_sources(FIELDS & fields, FIELDS & sources, ExtrinsicModelType modelType)
{
vector<ExtrinsicModel *>::iterator imodel;
if (modelType == NUM_MODELS) {// execute all the models
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->set_sources(fields,sources);
}
else { // execute only requested type of model
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
if ((*imodel)->model_type() == modelType)
(*imodel)->set_sources(fields,sources);
}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ExtrinsicModelManager::output(OUTPUT_LIST & outputData)
{
vector<ExtrinsicModel *>::iterator imodel;
for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++)
(*imodel)->output(outputData);
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModel
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ExtrinsicModel::ExtrinsicModel(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
string matFileName) :
atc_(modelManager->atc()),
modelManager_(modelManager),
modelType_(modelType),
physicsModel_(NULL)
{
rhsMaskIntrinsic_.reset(NUM_FIELDS,NUM_FLUX);
rhsMaskIntrinsic_ = false;
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
ExtrinsicModel::~ExtrinsicModel()
{
if (physicsModel_) delete physicsModel_;
}
//--------------------------------------------------------
// initialize
//--------------------------------------------------------
void ExtrinsicModel::initialize(void)
{
physicsModel_->initialize();
}
//--------------------------------------------------------
// get_num_fields
// - sets dict of fields
//--------------------------------------------------------
void ExtrinsicModel::num_fields(map<FieldName,int> & fieldSizes)
{
physicsModel_->num_fields(fieldSizes,atc_->fieldMask_);
}
};

315
lib/atc/ExtrinsicModel.h Normal file
View File

@ -0,0 +1,315 @@
#ifndef EXTRINSIC_MODEL
#define EXTRINSIC_MODEL
// ATC headers
#include "ATC_TypeDefs.h"
#include "MatrixLibrary.h"
using namespace std;
namespace ATC {
// forward declarations
class ATC_Coupling;
class ExtrinsicModel;
class PhysicsModel;
/** enumeration for the model types available */
enum ExtrinsicModelType {
NO_MODEL=0,
TWO_TEMPERATURE,
DRIFT_DIFFUSION,
DRIFT_DIFFUSION_EQUILIBRIUM,
DRIFT_DIFFUSION_SCHRODINGER,
DRIFT_DIFFUSION_SCHRODINGER_SLICE,
CONVECTIVE_DRIFT_DIFFUSION,
CONVECTIVE_DRIFT_DIFFUSION_EQUILIBRIUM,
CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER,
ELECTROSTATIC,
ELECTROSTATIC_EQUILIBRIUM,
FEM_EFIELD,
NUM_MODELS
};
/**
* @class ExtrinsicModelManager
* @brief Handles parsing and parameter storage extrinsic models
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelManager
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModelManager {
public:
// constructor
ExtrinsicModelManager(ATC_Coupling * atcTransfer);
// destructor
~ExtrinsicModelManager();
/** parser/modifier */
bool modify(int narg, char **arg);
/** create_model */
void create_model(ExtrinsicModelType modelType, string matFileName);
/** construct the transfers needed by the model */
void construct_transfers();
/** pre time integration */
void initialize();
/** set up LAMMPS display variables */
int size_vector(int intrinsicSize);
/** get LAMMPS display variables */
double compute_scalar(void);
double compute_vector(int n);
/** post integration run */
// is this called at end of run or simulation
void finish();
// calls during LAMMPS Velocity-Verlet integration
/** Predictor phase, executed before Verlet */
void pre_init_integrate(ExtrinsicModelType modelType = NUM_MODELS);
/** Predictor phase, executed between velocity and position Verlet */
void mid_init_integrate(ExtrinsicModelType modelType = NUM_MODELS);
/** Predictor phase, executed after Verlet */
void post_init_integrate(ExtrinsicModelType modelType = NUM_MODELS);
/** Make changes to the forces lammps calculates */
void post_force(ExtrinsicModelType modelType = NUM_MODELS);
/** Corrector phase, executed before Verlet */
void pre_final_integrate(ExtrinsicModelType modelType = NUM_MODELS);
/** Corrector phase, executed after Verlet*/
void post_final_integrate(ExtrinsicModelType modelType = NUM_MODELS);
/** get source terms for AtC equations */
void set_sources(FIELDS & fields, FIELDS & sources,
ExtrinsicModelType modelType = NUM_MODELS);
/** return output data to main AtC */
void output(OUTPUT_LIST & outputData);
/** model name enum to string */
static bool model_to_string(const ExtrinsicModelType index, string & name)
{
switch (index) {
case NO_MODEL:
name = "no_model";
break;
case TWO_TEMPERATURE:
name = "two_temperature";
break;
case DRIFT_DIFFUSION:
name = "drift_diffusion";
break;
case DRIFT_DIFFUSION_EQUILIBRIUM:
name = "drift_diffusion-equilibrium";
break;
case DRIFT_DIFFUSION_SCHRODINGER:
name = "drift_diffusion-schrodinger";
break;
case DRIFT_DIFFUSION_SCHRODINGER_SLICE:
name = "drift_diffusion-schrodinger-slice";
break;
case CONVECTIVE_DRIFT_DIFFUSION:
name = "convective_drift_diffusion";
break;
case CONVECTIVE_DRIFT_DIFFUSION_EQUILIBRIUM:
name = "convective_drift_diffusion-equilibrium";
break;
case CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER:
name = "convective_drift_diffusion-schrodinger";
break;
case ELECTROSTATIC:
name = "electrostatic";
break;
case ELECTROSTATIC_EQUILIBRIUM:
name = "electrostatic-equilibrium";
break;
case FEM_EFIELD:
name = "fem_efield";
break;
default:
return false;
break;
}
return true;
};
/** string to model enum */
static bool string_to_model(const string & name, ExtrinsicModelType & index)
{
if (name=="no_model")
index = NO_MODEL;
else if (name=="two_temperature")
index = TWO_TEMPERATURE;
else if (name=="drift_diffusion")
index = DRIFT_DIFFUSION;
else if (name=="drift_diffusion-equilibrium")
index = DRIFT_DIFFUSION_EQUILIBRIUM;
else if (name=="drift_diffusion-schrodinger")
index = DRIFT_DIFFUSION_SCHRODINGER;
else if (name=="drift_diffusion-schrodinger-slice")
index = DRIFT_DIFFUSION_SCHRODINGER_SLICE;
else if (name=="convective_drift_diffusion")
index = CONVECTIVE_DRIFT_DIFFUSION;
else if (name=="convective_drift_diffusion-equilibrium")
index = CONVECTIVE_DRIFT_DIFFUSION_EQUILIBRIUM;
else if (name=="convective_drift_diffusion-schrodinger")
index = CONVECTIVE_DRIFT_DIFFUSION_SCHRODINGER;
else if (name=="electrostatic")
index = ELECTROSTATIC;
else if (name=="electrostatic-equilibrium")
index = ELECTROSTATIC_EQUILIBRIUM;
else if (name=="fem_efield")
index = FEM_EFIELD;
else
return false;
return true;
};
/** access to ATC transfer object */
ATC_Coupling * atc() {return atc_;};
/** access to model of a specific type */
const ExtrinsicModel * model(const ExtrinsicModelType type) const;
protected:
/** associated ATC_Coupling object */
ATC_Coupling * atc_;
/** equation handler */
vector<ExtrinsicModel *> extrinsicModels_;
private:
ExtrinsicModelManager(); // DO NOT define this, only use constructor above
};
/**
* @class ExtrinsicModel
* @brief base class for functionality of all extrinsic models
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModel
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModel {
public:
// constructor
ExtrinsicModel(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
string matFileName);
// destructor
virtual ~ExtrinsicModel();
/** parser/modifier */
virtual bool modify(int narg, char **arg) {return false;};
/** construct transfers needed by the model */
virtual void construct_transfers(){};
/** pre time integration */
virtual void initialize();
/** set up LAMMPS display variables */
virtual int size_vector(int externalSize) {return 0;};
/** get LAMMPS display variables */
virtual double compute_scalar(void) { return 0.0; }
virtual bool compute_vector(int n, double & value) {return false;};
/** post integration run */
// is this called at end of run or simulation
virtual void finish(){};
/** Predictor phase, executed before Verlet */
virtual void pre_init_integrate(){};
/** Predictor phase, executed between velocity and position Verlet */
virtual void mid_init_integrate(){};
/** Predictor phase, executed after Verlet */
virtual void post_init_integrate(){};
/** changes to lammps forces */
virtual void post_force(){};
/** 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(){};
/** Set sources to AtC equation */
virtual void set_sources(FIELDS & fields, FIELDS & sources){};
/** Add model-specific output data */
virtual void output(OUTPUT_LIST & outputData){};
/** get the fields and their sizes */
void num_fields(map<FieldName,int> & fieldSizes);
/** return the type of model being used */
ExtrinsicModelType model_type() const {return modelType_;};
protected:
ExtrinsicModel(){};
/** ATC transfer object */
ATC_Coupling * atc_;
/** model manager object */
ExtrinsicModelManager * modelManager_;
/** tag for model type */
ExtrinsicModelType modelType_;
/** list of model fields in this model */
std::map<FieldName, int> fieldSizes_;
/** definition for physics used in this model */
PhysicsModel * physicsModel_;
/** rhs */
FIELDS rhs_;
/** rhs mask for coupling with MD */
Array2D<bool> rhsMaskIntrinsic_;
GRAD_FIELD_MATS fluxes_;
/** number of nodes */
int nNodes_;
/** number of spatial dimensions */
int nsd_;
};
};
#endif

View File

@ -0,0 +1,550 @@
// 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"
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)
{
// 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;
}
//--------------------------------------------------------
// 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);
}
else {
schrodingerSolver_ = new SchrodingerSolver(ELECTRON_DENSITY,
physicsModel_, atc_->feEngine_, atc_->prescribedDataMgr_, atc_,
type, true);
}
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);
}
}
//--------------------------------------------------------
// 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_);
}
// update electron temperature
if (! atc_->prescribedDataMgr_->all_fixed(ELECTRON_TEMPERATURE) )
temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_);
atc_->set_fixed_nodes();
}
}
//--------------------------------------------------------
// output
//--------------------------------------------------------
void ExtrinsicModelDriftDiffusion::output(OUTPUT_LIST & outputData)
{
ExtrinsicModelTwoTemperature::output(outputData);
// fields
outputData["dot_electron_density"] = & (atc_->dot_field(ELECTRON_DENSITY)).set_quantity();
outputData["joule_heating"] = & rhs_[ELECTRON_TEMPERATURE].set_quantity();
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);
}
//--------------------------------------------------------
// size_vector
//--------------------------------------------------------
int ExtrinsicModelDriftDiffusion::size_vector(int intrinsicSize)
{
int xSize = ExtrinsicModelTwoTemperature::size_vector(intrinsicSize);
baseSize_ = intrinsicSize + xSize;
xSize += 1;
return xSize;
}
//--------------------------------------------------------
// compute_vector
//--------------------------------------------------------
bool ExtrinsicModelDriftDiffusion::compute_vector(int n, double & value)
{
// output[1] = total electron density
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;
}
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;
}
//--------------------------------------------------------
// 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);
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);
}
}
}
//atc_->set_fixed_nodes();
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_);
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;
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_);
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;
}
};

View File

@ -0,0 +1,187 @@
#ifndef EXTRINSIC_MODEL_DRIFT_DIFFUSION
#define EXTRINSIC_MODEL_DRIFT_DIFFUSION
/** owned fields: ELECTRON_DENSITY, ... */
// ATC headers
#include "ExtrinsicModelTwoTemperature.h"
#include "SchrodingerSolver.h"
using namespace std;
namespace ATC {
// forward declarations
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
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelDriftDiffusion
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModelDriftDiffusion : public ExtrinsicModelTwoTemperature {
public:
// constructor
ExtrinsicModelDriftDiffusion(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
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();
/** 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_;
string oneDnodesetName_;
set<int> oneDnodeset_;
Array< set<int> > oneDslices_;
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,
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 */
vector<LinearSolver * > velocitySolvers_;
/** Linear solver for solving the poisson equations */
LinearSolver * cddmPoissonSolver_;
/** offset/size for LAMMPS display output */
int baseSize_;
double timerStart_, timerCurrent_;
};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,184 @@
#ifndef EXTRINSIC_MODEL_ELECTROSTATIC
#define EXTRINSIC_MODEL_ELECTROSTATIC
/** owned fields: ELECTRIC_POTENTIAL */
#define CHARGED_SURFACE
// ATC headers
#include "ExtrinsicModel.h"
using namespace std;
namespace ATC {
// forward declarations
class ATC_Coupling;
class PrescribedDataManager;
class ExtrinsicModel;
class PhysicsModel;
class PoissonSolver;
class FundamentalAtomQuantity;
class AtfShapeFunctionRestriction;
class ChargeRegulator;
/**
* @class ExtrinsicModelElectrostatic
* @brief add self-consistent electrostatic forces
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelElectrostatic
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModelElectrostatic : public ExtrinsicModel {
public:
// constructor
ExtrinsicModelElectrostatic(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
string matFileName);
// destructor
virtual ~ExtrinsicModelElectrostatic();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** construct transfers needed for model */
virtual void construct_transfers();
/** pre time integration */
virtual void initialize();
/** Predictor phase, executed before Verlet */
virtual void post_init_integrate();
/** changes lammps forces to include long-range electrostatic interactions */
virtual void post_force();
/** 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 double compute_scalar(void);
virtual bool compute_vector(int n, double & value);
PoissonSolver * poisson_solver(void) const { return poissonSolver_;}
protected:
/** poisson solver type */
SolverType poissonSolverType_;
double poissonSolverTol_;
int poissonSolverMaxIter_;
/** poisson solver */
PoissonSolver * poissonSolver_;
/** max solves per minimize */
int maxSolves_;
/** offset/size for LAMMPS display output */
int baseSize_;
/** rhs mask for Poisson solver */
Array2D<bool> rhsMask_;
/** estimate instrinsic charge density */
void add_electrostatic_forces(MATRIX & nodalPotential);
/** correct short range FE electric field */
void correct_electrostatic_forces();
#ifdef CHARGED_SURFACE
/** account for charged surfaces on charged atoms */
void apply_charged_surfaces(MATRIX & nodalPotential);
/** set charged surface data */
void add_charged_surface(const string & facesetName,
const double chargeDensity);
#endif
/** charge regulator */
ChargeRegulator * chargeRegulator_;
/** local electric potential Green's function for each node */
vector<SparseVector<double> > greensFunctions_;
#ifdef CHARGED_SURFACE
/** stores surface charge data at fixed charge surfaces */
map<string,double> surfaceCharges_;
/** data structure to store information for applying charged surfaces */
map<string,map<int,pair<DENS_VEC,double> > > chargedSurfaces_;
#endif
/** data structure storing potential induced only by charges under the nodal shape function support */
map<string,map<int,double> > nodalChargePotential_;
/** allows electric force only applied only in z direction to complement LAMMPS slab command */
bool useSlab_;
/** enables method when short range interactions are off */
bool includeShortRange_;
/** atomic forces */
FundamentalAtomQuantity * atomForces_;
/** coarse-grained charges from internal atoms */
DENS_MAN * nodalAtomicCharge_;
/** coarse-grained charges from ghost atoms */
DENS_MAN * nodalAtomicGhostCharge_;
/** workspace */
DENS_MAT _atomElectricalForce_;
double totalElectricalForce_[3];
};
/**
* @class ExtrinsicModelElectrostaticMomentum
* @brief add self-consistent electrostatic forces with elastic coupling
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelElectrostaticMomentum
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModelElectrostaticMomentum : public ExtrinsicModelElectrostatic {
public:
// constructor
ExtrinsicModelElectrostaticMomentum(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
string matFileName);
// destructor
virtual ~ExtrinsicModelElectrostaticMomentum();
/** parser/modifier */
virtual bool modify(int narg, char **arg);
/** pre time integration */
virtual void initialize();
/** Set sources to AtC equation */
virtual void set_sources(FIELDS & fields, FIELDS & sources);
/** Add model-specific output data */
virtual void output(OUTPUT_LIST & outputData);
};
};
#endif

View File

@ -0,0 +1,268 @@
// 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"
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) {
electronTimeIntegration_ = TimeIntegrator::EXPLICIT;
match = true;
}
else if (strcmp(arg[argIndx],"implicit")==0) {
electronTimeIntegration_ = TimeIntegrator::IMPLICIT;
match = true;
}
else if (strcmp(arg[argIndx],"steady")==0) {
electronTimeIntegration_ = TimeIntegrator::STEADY;
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 (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 {
temperatureIntegrator_ = new FieldExplicitEulerIntegrator(
ELECTRON_TEMPERATURE, physicsModel_, atc_->feEngine_, atc_,
rhsMask);
}
// 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()
{
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_);
}
}
//--------------------------------------------------------
// 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;
}
};

View File

@ -0,0 +1,82 @@
#ifndef EXTRINSIC_MODEL_TWO_TEMPERATURE
#define EXTRINSIC_MODEL_TWO_TEMPERATURE
/** owned fields: ELECTRON_TEMPERATURE */
// ATC headers
#include "ExtrinsicModel.h"
#include "FieldEulerIntegrator.h"
using namespace std;
namespace ATC {
// forward declarations
class ATC_Coupling;
class PrescribedDataManager;
class ExtrinsicModel;
class PhysicsModel;
/**
* @class ExtrinsicModelTwoTemperature
* @brief add electron temperature physics to phonon physics
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class ExtrinsicModelTwoTemperature
//--------------------------------------------------------
//--------------------------------------------------------
class ExtrinsicModelTwoTemperature : public ExtrinsicModel {
public:
// constructor
ExtrinsicModelTwoTemperature(ExtrinsicModelManager * modelManager,
ExtrinsicModelType modelType,
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);
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

746
lib/atc/FE_Element.cpp Normal file
View File

@ -0,0 +1,746 @@
// 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;
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 &centroid) 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[1] - y0*X[2] - y0*X[3] + y0*X[4] - x0*Y[1] + (X[2]*Y[1])*0.5 + (X[3]*Y[1])*0.5 + x0*Y[2] - (X[1]*Y[2])*0.5 - (X[4]*Y[2])*0.5 + x0*Y[3] - (X[1]*Y[3])*0.5 - (X[4]*Y[3])*0.5 - x0*Y[4] + (X[2]*Y[4])*0.5 + (X[3]*Y[4])*0.5;
c[1] = -(y0*X[1]) + y0*X[2] - y0*X[3] + y0*X[4] + x0*Y[1] - X[2]*Y[1] - x0*Y[2] + X[1]*Y[2] + x0*Y[3] - X[4]*Y[3] - x0*Y[4] + X[3]*Y[4];
c[2] = (X[2]*Y[1])*0.5 - (X[3]*Y[1])*0.5 - (X[1]*Y[2])*0.5 + (X[4]*Y[2])*0.5 + (X[1]*Y[3])*0.5 - (X[4]*Y[3])*0.5 - (X[2]*Y[4])*0.5 + (X[3]*Y[4])*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[1] + xi2[0]*X[1] - X[2] + xi2[0]*X[2] - X[3] - xi2[0]*X[3] - X[4] - xi2[0]*X[4])/(-X[1] + xi2[0]*X[1] + X[2] - xi2[0]*X[2] + X[3] + xi2[0]*X[3] - X[4] - xi2[0]*X[4]);
xi1[1] = (4*x0 - X[1] + xi2[1]*X[1] - X[2] + xi2[1]*X[2] - X[3] - xi2[1]*X[3] - X[4] - xi2[1]*X[4])/(-X[1] + xi2[1]*X[1] + X[2] - xi2[1]*X[2] + X[3] + xi2[1]*X[3] - X[4] - xi2[1]*X[4]);
// 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

356
lib/atc/FE_Element.h Normal file
View File

@ -0,0 +1,356 @@
#ifndef FE_ELEMENT_H
#define FE_ELEMENT_H
// ATC_Transfer headers
#include "MatrixLibrary.h"
#include "Array2D.h"
#include "ATC_TypeDefs.h"
using namespace std;
namespace ATC {
enum ProjectionGuessType {
COORDINATE_ALIGNED=0,
CENTROID_LINEARIZED,
TWOD_ANALYTIC};
// Forward declarations
class FE_Interpolate;
/**
* @class FE_Element
* @brief Base class for a finite element holding info for canonical element
*/
class FE_Element {
public:
///////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTOR AND DESTRUCTOR
FE_Element(const int nSD,
int numFaces,
int numNodes,
int numFaceNodes,
int numNodes1d);
virtual ~FE_Element();
///////////////////////////////////////////////////////////////////////////
//
// GETTERS
/** get number of spatial dimensions (almost always 3) */
int num_dims() { return nSD_; }
/** get number of element nodes */
int num_elt_nodes() { return numNodes_; }
/** get number of element nodes */
int num_elt_nodes_1d() { return numNodes1d_; }
/** get number of faces */
int num_faces() { return numFaces_; }
/** get number of face nodes */
int num_face_nodes() { return numFaceNodes_; }
// Getters for FE_Interpoate to have access to coordinates and connectivity
/** get canonical coordinates */
const DENS_MAT &local_coords() const { return localCoords_; }
/** get canonical coordinates in 1d */
DENS_VEC local_coords_1d() const;
/** get canonical connectivity of nodes and faces */
const Array2D<int> &local_face_conn() const { return localFaceConn_; }
/** return volume of the element */
const double vol() const { return vol_; }
/** return area of a face */
const double face_area() const { return faceArea_; }
// the following two are pass-throughs to the interpolate class, and
// can thus only be declared in the class body (or else the
// interpolate class is "incomplete" and cannot be referenced)
/** get number of integration points */
int num_ips() const;
/** get number of integration points */
int num_face_ips() const;
/** order of interpolation */
int order() const {return numNodes1d_;}
/** compute the quadrature for a given element type */
virtual void set_quadrature(FeIntQuadrature type) = 0;
/** return the set of 1d nodes that correspond to this node in 3d space */
void mapping(const int inode, vector<int> &mapping) const;
/** extract face coordinates from element coordinates */
void face_coordinates(const DENS_MAT &eltCoords,
const int faceID,
DENS_MAT &faceCoords) const;
/** set initial guess type for point in element search */
void set_projection_guess(ProjectionGuessType type)
{ projectionGuess_ = type;}
///////////////////////////////////////////////////////////////////////////
//
// GENERIC ELEMENT COMPUTATIONS
/** compute local coordinates from global */
virtual bool local_coordinates(const DENS_MAT &eltCoords,
const DENS_VEC &x,
DENS_VEC &xi) const;
/** location of local coordinates (0,0,0) */
virtual void centroid(const DENS_MAT &eltCoords,
DENS_VEC & centroid) const;
/** test if a specified element actually contains the given point */
virtual bool contains_point(const DENS_MAT &eltCoords,
const DENS_VEC &x) const;
/** check if element bounding box contains the given point */
bool range_check(const DENS_MAT &eltCoords, const DENS_VEC & x) const;
/** get the min and max coordinate of any point in an element in a
* dimension */
void bounds_in_dim(const DENS_MAT &eltCoords, const int dim,
double &min, double &max) const;
///////////////////////////////////////////////////////////////////////////
//
//PASS-THROUGHS TO INTERPOLATE CLASS
virtual void shape_function(const VECTOR & xi,
DENS_VEC &N) const;
/**
* compute shape functions at all ip's:
* indexed: N(ip,node)
* dN[nsd](ip,node)
* weights(ip)
*/
virtual void shape_function(const DENS_MAT eltCoords,
DENS_MAT &N,
vector<DENS_MAT> &dN,
DIAG_MAT &weights);
/**
* compute shape functions and derivatives at a single point,
* given the point and the element that contains it
* indexed: N(node)
*/
virtual void shape_function(const DENS_MAT eltCoords,
const VECTOR &x,
DENS_VEC &N);
/**
* compute shape functions and derivatives at a single point,
* given the point and the element that contains it
* indexed: N(node)
* dNdx(ip,nSD)
*/
virtual void shape_function(const DENS_MAT eltCoords,
const VECTOR &x,
DENS_VEC &N,
DENS_MAT &dNdx);
/**
* compute shape functions and derivatives at a single point,
* given the point and the element that contains it
* indexed:
* dNdx(ip,nSD)
*/
virtual void shape_function_derivatives(const DENS_MAT eltCoords,
const VECTOR &x,
DENS_MAT &dNdx);
/**
* compute shape functions at all face ip's:
* indexed: N(ip,node)
* n[nsd](ip,node)
* weights(ip)
*/
virtual void face_shape_function(const DENS_MAT &eltCoords,
const int faceID,
DENS_MAT &N,
DENS_MAT &n,
DIAG_MAT &weights);
/**
* compute shape functions at all face ip's:
* indexed: N(ip,node)
* dN[nsd](ip,node)
* Nn[nsd](ip,node)
* weights(ip)
*/
virtual void face_shape_function(const DENS_MAT &eltCoords,
const int faceID,
DENS_MAT &N,
vector<DENS_MAT> &dN,
vector<DENS_MAT> &Nn,
DIAG_MAT &weights);
/**
* compute normal vector from the specified face
* indexed: normal(nSD)
*/
virtual double face_normal(const DENS_MAT &eltCoords,
const int faceID,
int ip,
DENS_VEC &normal);
/**
* compute tangents to local coordinates
* indexed:
*/
virtual void tangents(const DENS_MAT &eltCoords,
const DENS_VEC &x,
vector<DENS_VEC> & tangents,
const bool normalize=false) const;
protected:
///////////////////////////////////////////////////////////////////////////
//
// HELPERS
/**
* generate the appropriate interpolation class
*/
FE_Interpolate *interpolate_factory(string interpolateType);
/** initial guess for local coordinates */
virtual void initial_local_coordinates(const DENS_MAT &eltCoords,
const DENS_VEC &x,
DENS_VEC &xiInitial) const;
///////////////////////////////////////////////////////////////////////////
//
// PROTECTED MEMBERS
// Currently used interpolation class
FE_Interpolate *feInterpolate_;
// Number of spatial dimensions
int nSD_;
// Number of faces, used for generic contains_point
int numFaces_;
// Number of element nodes
int numNodes_;
// Number of face nodes
int numFaceNodes_;
// Number of nodes in one dimension
int numNodes1d_;
// local coords of nodes: localCoords_(isd, ip)
DENS_MAT localCoords_;
// local face numbering
Array2D<int> localFaceConn_;
// volume of canonical element
double vol_;
// area of faces of canonical element
double faceArea_;
/** tolerance used in solving Newton's method for local coordinates */
double tolerance_;
ProjectionGuessType projectionGuess_;
};
/**
* @class FE_ElementHex
* @author Sean Laguna
* @brief 3D, linear 8-node hex element
*/
class FE_ElementHex : public FE_Element {
public:
FE_ElementHex(int numNodes,
int numFaceNodes,
int numNodes1d);
// Dump state info to disk for later restart (unimplemented)
void write_restart(FILE *);
~FE_ElementHex();
void set_quadrature(FeIntQuadrature type);
bool contains_point(const DENS_MAT &eltCoords,
const DENS_VEC &x) const;
};
/**
* @class FE_ElementRect
* @author Greg Wagner, amended by Sean Laguna
* @brief 3D, linear 8-node rectilinear hex element
*/
class FE_ElementRect : public FE_ElementHex {
public:
FE_ElementRect();
// Dump state info to disk for later restart (unimplemented)
void write_restart(FILE *);
~FE_ElementRect();
bool local_coordinates(const DENS_MAT &eltCoords,
const DENS_VEC &x,
DENS_VEC &xi) const;
protected:
virtual bool contains_point(const DENS_MAT &eltCoords,
const DENS_VEC &x) const;
};
/**
* @class FE_ElementTet
* @author Aaron Gable & Sean Laguna
* @brief 3D, linear 4-node tetrahedral element
*/
class FE_ElementTet : public FE_Element {
public:
FE_ElementTet(int numNodes,
int numFaceNodes,
int numNodes1d);
// Dump state info to disk for later restart (unimplemented)
void write_restart(FILE *);
~FE_ElementTet();
void set_quadrature(FeIntQuadrature type);
bool local_coordinates(const DENS_MAT &eltCoords,
const DENS_VEC &x,
DENS_VEC &xi) const;
bool contains_point(const DENS_MAT &eltCoords,
const DENS_VEC &x) const;
};
}; // namespace ATC
#endif // FE_ELEMENT_H

2668
lib/atc/FE_Engine.cpp Normal file

File diff suppressed because it is too large Load Diff

559
lib/atc/FE_Engine.h Normal file
View File

@ -0,0 +1,559 @@
#ifndef FE_ENGINE_H
#define FE_ENGINE_H
// Other headers
#include <vector>
#include <map>
// ATC headers
#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"
using namespace std;
namespace ATC {
// Forward declarations
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, string outputPrefix, 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(string fileName, RESTART_LIST *data)
{ outputManager_.write_restart_file(fileName,data); }
void read_restart_file(string fileName, RESTART_LIST *data)
{ outputManager_.read_restart_file(fileName,data); }
void delete_elements(const set<int> &elementList);
void cut_mesh(const set<PAIR> &cutFaces, const set<int> &edgeNodes);
void add_global(const string name, const double value)
{ outputManager_.add_global(name,value); }
void add_field_names(const string field, const vector<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,
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 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 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=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<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,
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<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<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 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<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 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 */
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 nodal vector of volume based sources */
void add_sources(const Array<bool> &fieldMask,
const double time,
const VOLUME_SOURCE &sourceFunctions,
FIELDS &nodalSources) const;
/** compute surface flux of a nodal field */
void field_surface_flux(const DENS_MAT &field,
const 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 string &name, const 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 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_;
/** 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(string meshFile, Array<bool> & periodicity);
/*@}*/
/** data that can be used for a subset of original mesh */
set<int> nullElements_;
/** faces upon which nodes are duplicated */
set<PAIR> cutFaces_;
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

990
lib/atc/FE_Interpolate.cpp Normal file
View File

@ -0,0 +1,990 @@
// ATC header files
#include "ATC_Error.h"
#include "FE_Element.h"
#include "FE_Interpolate.h"
#include "FE_Quadrature.h"
// Other headers
#include "math.h"
namespace ATC {
FE_Interpolate::FE_Interpolate(FE_Element *feElement)
: feElement_(feElement),
nSD_(feElement->num_dims())
{
// Nothing to do here
}
FE_Interpolate::~FE_Interpolate()
{
if (!feQuadList_.empty()) {
map<FeIntQuadrature,FE_Quadrature *>::iterator qit;
for (qit = feQuadList_.begin();
qit != feQuadList_.end(); ++qit) {
delete (qit->second);
}
}
}
void FE_Interpolate::set_quadrature(FeEltGeometry geo,
FeIntQuadrature quad)
{
if (feQuadList_.count(quad) == 0) {
feQuad_ = new FE_Quadrature(geo,quad);
feQuadList_[quad] = feQuad_;
} else {
feQuad_ = feQuadList_[quad];
}
precalculate_shape_functions();
}
void FE_Interpolate::precalculate_shape_functions()
{
int numEltNodes = feElement_->num_elt_nodes();
int numFaces = feElement_->num_faces();
int numFaceNodes = feElement_->num_face_nodes();
int numIPs = feQuad_->numIPs;
DENS_MAT &ipCoords = feQuad_->ipCoords;
int numFaceIPs = feQuad_->numFaceIPs;
vector<DENS_MAT> &ipFaceCoords = feQuad_->ipFaceCoords;
DENS_MAT &ipFace2DCoords = feQuad_->ipFace2DCoords;
// Compute elemental shape functions at ips
N_.reset(numIPs,numEltNodes);
dNdr_.assign(numIPs,DENS_MAT(nSD_,numEltNodes));
for (int ip = 0; ip < numIPs; ip++) {
CLON_VEC thisIP = column(ipCoords,ip);
CLON_VEC thisN = row(N_,ip);
DENS_MAT &thisdNdr = dNdr_[ip];
compute_N(thisIP,thisN);
compute_N_dNdr(thisIP,thisN,thisdNdr);
}
// Compute face shape functions at ip's
NFace_.assign(numFaces,DENS_MAT(numFaceIPs,numEltNodes));
dNdrFace_.resize(numFaces);
for (int f = 0; f < numFaces; f++) {
dNdrFace_[f].assign(numIPs,DENS_MAT(nSD_,numEltNodes));
}
for (int f = 0; f < numFaces; f++) {
for (int ip = 0; ip < numFaceIPs; ip++) {
CLON_VEC thisIP = column(ipFaceCoords[f],ip);
CLON_VEC thisN = row(NFace_[f],ip);
DENS_MAT &thisdNdr = dNdrFace_[f][ip];
compute_N_dNdr(thisIP,thisN,thisdNdr);
}
}
// Compute 2D face shape function derivatives
dNdrFace2D_.assign(numFaceIPs,DENS_MAT(nSD_-1,numFaceNodes));
for (int ip = 0; ip < numFaceIPs; ip++) {
CLON_VEC thisIP = column(ipFace2DCoords,ip);
DENS_MAT &thisdNdr = dNdrFace2D_[ip];
compute_dNdr(thisIP,
numFaceNodes,nSD_-1,feElement_->face_area(),
thisdNdr);
}
}
//-----------------------------------------------------------------
// shape function value at a particular point given local coordinates
//-----------------------------------------------------------------
void FE_Interpolate::shape_function(const VECTOR &xi,
DENS_VEC &N)
{
int numEltNodes = feElement_->num_elt_nodes();
N.resize(numEltNodes);
compute_N(xi,N);
}
void FE_Interpolate::shape_function(const DENS_MAT &eltCoords,
const VECTOR &xi,
DENS_VEC &N,
DENS_MAT &dNdx)
{
int numEltNodes = feElement_->num_elt_nodes();
N.resize(numEltNodes);
DENS_MAT dNdr(nSD_,numEltNodes,false);
compute_N_dNdr(xi,N,dNdr);
DENS_MAT eltCoordsT = transpose(eltCoords);
DENS_MAT dxdr, drdx;
dxdr = dNdr*eltCoordsT;
drdx = inv(dxdr);
dNdx = drdx*dNdr;
}
void FE_Interpolate::shape_function_derivatives(const DENS_MAT &eltCoords,
const VECTOR &xi,
DENS_MAT &dNdx)
{
int numEltNodes = feElement_->num_elt_nodes();
DENS_MAT dNdr(nSD_,numEltNodes,false);
DENS_VEC N(numEltNodes);
compute_N_dNdr(xi,N,dNdr);
DENS_MAT eltCoordsT = transpose(eltCoords);
DENS_MAT dxdr, drdx;
dxdr = dNdr*eltCoordsT; // tangents or Jacobian matrix
drdx = inv(dxdr);
dNdx = drdx*dNdr; // dN/dx = dN/dxi (dx/dxi)^-1
}
void FE_Interpolate::tangents(const DENS_MAT &eltCoords,
const VECTOR &xi,
DENS_MAT &dxdr) const
{
int numEltNodes = feElement_->num_elt_nodes();
DENS_MAT dNdr(nSD_,numEltNodes,false);
DENS_VEC N(numEltNodes);
compute_N_dNdr(xi,N,dNdr);
//dNdr.print("dNdr");
DENS_MAT eltCoordsT = transpose(eltCoords);
//eltCoordsT.print("elt coords");
dxdr = dNdr*eltCoordsT;
//dxdr.print("dxdr");
}
void FE_Interpolate::tangents(const DENS_MAT &eltCoords,
const VECTOR &xi,
vector<DENS_VEC> & dxdxi,
const bool normalize) const
{
DENS_MAT dxdr;
tangents(eltCoords,xi,dxdr);
//dxdr.print("dxdr-post");
dxdxi.resize(nSD_);
//for (int i = 0; i < nSD_; ++i) dxdxi[i] = CLON_VEC(dxdr,CLONE_COL,i);
for (int i = 0; i < nSD_; ++i) {
dxdxi[i].resize(nSD_);
for (int j = 0; j < nSD_; ++j) {
dxdxi[i](j) = dxdr(i,j);
}
}
//dxdxi[0].print("t1");
//dxdxi[1].print("t2");
//dxdxi[2].print("t3");
if (normalize) {
for (int j = 0; j < nSD_; ++j) {
double norm = 0;
VECTOR & t = dxdxi[j];
for (int i = 0; i < nSD_; ++i) norm += t(i)*t(i);
norm = 1./sqrt(norm);
for (int i = 0; i < nSD_; ++i) t(i) *= norm;
}
}
}
// -------------------------------------------------------------
// shape_function values at nodes
// -------------------------------------------------------------
void FE_Interpolate::shape_function(const DENS_MAT &eltCoords,
DENS_MAT &N,
vector<DENS_MAT> &dN,
DIAG_MAT &weights)
{
int numEltNodes = feElement_->num_elt_nodes();
// Transpose eltCoords
DENS_MAT eltCoordsT(transpose(eltCoords));
// Shape functions are simply the canonical element values
N = N_;
// Set sizes of matrices and vectors
if ((int)dN.size() != nSD_) dN.resize(nSD_);
for (int isd = 0; isd < nSD_; isd++)
dN[isd].resize(feQuad_->numIPs,numEltNodes);
weights.resize(feQuad_->numIPs,feQuad_->numIPs);
// Create some temporary matrices:
// Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ]
DENS_MAT dxdr, drdx, dNdx;
// Loop over integration points
for (int ip = 0; ip < feQuad_->numIPs; ip++) {
// Compute dx/dxi matrix
dxdr = dNdr_[ip]*eltCoordsT;
drdx = inv(dxdr);
// Compute dNdx and fill dN matrix
dNdx = drdx * dNdr_[ip];
for (int isd = 0; isd < nSD_; isd++)
for (int inode = 0; inode < numEltNodes; inode++)
dN[isd](ip,inode) = dNdx(isd,inode);
// Compute jacobian determinant of dxdr at this ip
double J = dxdr(0,0) * (dxdr(1,1)*dxdr(2,2) - dxdr(2,1)*dxdr(1,2))
- dxdr(0,1) * (dxdr(1,0)*dxdr(2,2) - dxdr(1,2)*dxdr(2,0))
+ dxdr(0,2) * (dxdr(1,0)*dxdr(2,1) - dxdr(1,1)*dxdr(2,0));
// Compute ip weight
weights(ip,ip) = feQuad_->ipWeights(ip)*J;
}
}
//-----------------------------------------------------------------
// shape functions on a given face
//-----------------------------------------------------------------
void FE_Interpolate::face_shape_function(const DENS_MAT &eltCoords,
const DENS_MAT &faceCoords,
const int faceID,
DENS_MAT &N,
DENS_MAT &n,
DIAG_MAT &weights)
{
int numFaceIPs = feQuad_->numFaceIPs;
// Transpose eltCoords
DENS_MAT eltCoordsT = transpose(eltCoords);
// Shape functions are simply the canonical element values
N = NFace_[faceID];
// Create some temporary matrices:
// Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ]
DENS_MAT dxdr, drdx, dNdx;
// Loop over integration points
DENS_VEC normal(nSD_);
n.resize(nSD_,numFaceIPs);
weights.resize(numFaceIPs,numFaceIPs);
for (int ip = 0; ip < numFaceIPs; ip++) {
// Compute 2d jacobian determinant of dxdr at this ip
double J = face_normal(faceCoords,ip,normal);
// Copy normal at integration point
for (int isd = 0; isd < nSD_; isd++) {
n(isd,ip) = normal(isd);
}
// Compute ip weight
weights(ip,ip) = feQuad_->ipFaceWeights(ip)*J;
}
}
void FE_Interpolate::face_shape_function(const DENS_MAT &eltCoords,
const DENS_MAT &faceCoords,
const int faceID,
DENS_MAT &N,
vector<DENS_MAT> &dN,
vector<DENS_MAT> &Nn,
DIAG_MAT &weights)
{
int numEltNodes = feElement_->num_elt_nodes();
int numFaceIPs = feQuad_->numFaceIPs;
// Transpose eltCoords
DENS_MAT eltCoordsT = transpose(eltCoords);
// Shape functions are simply the canonical element values
N = NFace_[faceID];
// Set sizes of matrices and vectors
if ((int)dN.size() != nSD_) dN.resize(nSD_);
if ((int)Nn.size() != nSD_) Nn.resize(nSD_);
for (int isd = 0; isd < nSD_; isd++) {
dN[isd].resize(numFaceIPs,numEltNodes);
Nn[isd].resize(numFaceIPs,numEltNodes);
}
weights.resize(numFaceIPs,numFaceIPs);
// Create some temporary matrices:
// Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ]
DENS_MAT dxdr, drdx, dNdx;
DENS_VEC normal(nSD_);
// Loop over integration points
for (int ip = 0; ip < numFaceIPs; ip++) {
// Compute dx/dxi matrix
dxdr = dNdrFace_[faceID][ip] * eltCoordsT;
drdx = inv(dxdr);
// Compute 2d jacobian determinant of dxdr at this ip
double J = face_normal(faceCoords,ip,normal);
// Compute dNdx and fill dN matrix
dNdx = drdx * dNdrFace_[faceID][ip];
for (int isd = 0; isd < nSD_; isd++) {
for (int inode = 0; inode < numEltNodes; inode++) {
dN[isd](ip,inode) = dNdx(isd,inode);
Nn[isd](ip,inode) = N(ip,inode)*normal(isd);
}
}
// Compute ip weight
weights(ip,ip) = feQuad_->ipFaceWeights(ip)*J;
}
}
// -------------------------------------------------------------
// face normal
// -------------------------------------------------------------
double FE_Interpolate::face_normal(const DENS_MAT &faceCoords,
int ip,
DENS_VEC &normal)
{
// Compute dx/dr for deformed element
DENS_MAT faceCoordsT = transpose(faceCoords);
DENS_MAT dxdr = dNdrFace2D_[ip]*faceCoordsT;
// Normal vector from cross product, hardcoded for 3D, sad
normal(0) = dxdr(0,1)*dxdr(1,2) - dxdr(0,2)*dxdr(1,1);
normal(1) = dxdr(0,2)*dxdr(1,0) - dxdr(0,0)*dxdr(1,2);
normal(2) = dxdr(0,0)*dxdr(1,1) - dxdr(0,1)*dxdr(1,0);
// Jacobian (3D)
double J = sqrt(normal(0)*normal(0) +
normal(1)*normal(1) +
normal(2)*normal(2));
double invJ = 1.0/J;
normal(0) *= invJ;
normal(1) *= invJ;
normal(2) *= invJ;
return J;
}
int FE_Interpolate::num_ips() const
{
return feQuad_->numIPs;
}
int FE_Interpolate::num_face_ips() const
{
return feQuad_->numFaceIPs;
}
/*********************************************************
* Class FE_InterpolateCartLagrange
*
* For computing Lagrange shape functions using Cartesian
* coordinate systems (all quads/hexes fall under this
* category, and any elements derived by degenerating
* them). Not to be used for serendipity elements, which
* should be implemented for SPEED.
*
*********************************************************/
FE_InterpolateCartLagrange::FE_InterpolateCartLagrange(
FE_Element *feElement)
: FE_Interpolate(feElement)
{
set_quadrature(HEXA,GAUSS2);
}
FE_InterpolateCartLagrange::~FE_InterpolateCartLagrange()
{
// Handled by base class
}
void FE_InterpolateCartLagrange::compute_N(const VECTOR &point,
VECTOR &N)
{
// *** see comments for compute_N_dNdr ***
const DENS_VEC &localCoords1d = feElement_->local_coords_1d();
int numEltNodes = feElement_->num_elt_nodes();
int numEltNodes1d = feElement_->num_elt_nodes_1d();
DENS_MAT lagrangeTerms(nSD_,numEltNodes1d);
DENS_MAT lagrangeDenom(nSD_,numEltNodes1d);
lagrangeTerms = 1.0;
lagrangeDenom = 1.0;
for (int iSD = 0; iSD < nSD_; ++iSD) {
for (int inode = 0; inode < numEltNodes1d; ++inode) {
for (int icont = 0; icont < numEltNodes1d; ++icont) {
if (inode != icont) {
lagrangeDenom(iSD,inode) *= (localCoords1d(inode) -
localCoords1d(icont));
lagrangeTerms(iSD,inode) *= (point(iSD)-localCoords1d(icont));
}
}
}
}
for (int iSD=0; iSD<nSD_; ++iSD) {
for (int inode=0; inode<numEltNodes1d; ++inode) {
lagrangeTerms(iSD,inode) /= lagrangeDenom(iSD,inode);
}
}
N = 1.0;
vector<int> mapping(nSD_);
for (int inode=0; inode<numEltNodes; ++inode) {
feElement_->mapping(inode,mapping);
for (int iSD=0; iSD<nSD_; ++iSD) {
N(inode) *= lagrangeTerms(iSD,mapping[iSD]);
}
}
}
// Sort of a test-ride for a generic version that can be used for
// faces too. The only thing that's not "generic" is localCoords,
// which very magically works in both cases.
void FE_InterpolateCartLagrange::compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double,
DENS_MAT &dNdr)
{
// *** see comments for compute_N_dNdr ***
const DENS_VEC &localCoords1d = feElement_->local_coords_1d();
int numEltNodes1d = feElement_->num_elt_nodes_1d();
DENS_MAT lagrangeTerms(nD,numEltNodes1d);
DENS_MAT lagrangeDenom(nD,numEltNodes1d);
DENS_MAT lagrangeDeriv(nD,numEltNodes1d);
lagrangeDenom = 1.0;
lagrangeTerms = 1.0;
lagrangeDeriv = 0.0;
DENS_VEC productRuleVec(numEltNodes1d);
productRuleVec = 1.0;
for (int iSD = 0; iSD < nD; ++iSD) {
for (int inode = 0; inode < numEltNodes1d; ++inode) {
for (int icont = 0; icont < numEltNodes1d; ++icont) {
if (inode != icont) {
lagrangeTerms(iSD,inode) *= (point(iSD)-localCoords1d(icont));
lagrangeDenom(iSD,inode) *= (localCoords1d(inode) -
localCoords1d(icont));
for (int dcont=0; dcont<numEltNodes1d; ++dcont) {
if (inode == dcont) {
productRuleVec(dcont) = 0.0;
} else if (icont == dcont) {
} else {
productRuleVec(dcont) *= (point(iSD)-localCoords1d(icont));
}
}
}
}
for (int dcont=0; dcont<numEltNodes1d; ++dcont) {
lagrangeDeriv(iSD,inode) += productRuleVec(dcont);
}
productRuleVec = 1.0;
}
}
for (int iSD=0; iSD<nD; ++iSD) {
for (int inode=0; inode<numEltNodes1d; ++inode) {
lagrangeTerms(iSD,inode) /= lagrangeDenom(iSD,inode);
lagrangeDeriv(iSD,inode) /= lagrangeDenom(iSD,inode);
}
}
dNdr = 1.0;
vector<int> mapping(nD);
for (int inode=0; inode<numNodes; ++inode) {
feElement_->mapping(inode,mapping);
for (int iSD=0; iSD<nD; ++iSD) {
for (int dSD=0; dSD<nD; ++dSD) {
if (iSD == dSD) {
dNdr(dSD,inode) *= lagrangeDeriv(iSD,mapping[iSD]);
} else {
dNdr(dSD,inode) *= lagrangeTerms(iSD,mapping[iSD]);
}
}
}
}
}
void FE_InterpolateCartLagrange::compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const
{
// Required data from element class
const DENS_VEC &localCoords1d = feElement_->local_coords_1d();
int numEltNodes = feElement_->num_elt_nodes();
int numEltNodes1d = feElement_->num_elt_nodes_1d();
// lagrangeTerms stores the numerator for the various Lagrange polynomials
// in one dimension, that will be used to produce the three dimensional
// shape functions
DENS_MAT lagrangeTerms(nSD_,numEltNodes1d);
// lagrangeDenom stores the denominator. Stored separately to reduce
// redundancy, because it will be used for the shape functions and derivs
DENS_MAT lagrangeDenom(nSD_,numEltNodes1d);
// lagrangeDeriv stores the numerator for the derivative of the Lagrange
// polynomials
DENS_MAT lagrangeDeriv(nSD_,numEltNodes1d);
// Terms/Denom are products, Deriv will be a sum, so initialize as such:
lagrangeTerms = 1.0;
lagrangeDenom = 1.0;
lagrangeDeriv = 0.0;
// the derivative requires use of the product rule; to store the prodcuts
// which make up the terms produced by the product rule, we'll use this
// vector
DENS_VEC productRuleVec(numEltNodes1d);
productRuleVec = 1.0;
for (int iSD = 0; iSD < nSD_; ++iSD) {
for (int inode = 0; inode < numEltNodes1d; ++inode) {
for (int icont = 0; icont < numEltNodes1d; ++icont) {
if (inode != icont) {
// each dimension and each 1d node per dimension has a
// contribution from all nodes besides the current node
lagrangeTerms(iSD,inode) *= (point(iSD)-localCoords1d(icont));
lagrangeDenom(iSD,inode) *= (localCoords1d(inode) -
localCoords1d(icont));
// complciated; each sum produced by the product rule has one
// "derivative", and the rest are just identical to the terms
// above
for (int dcont=0; dcont<numEltNodes1d; ++dcont) {
if (inode == dcont) {
// skip this term, derivative is 0
productRuleVec(dcont) = 0.0;
} else if (icont == dcont) {
// no numerator contribution, derivative is 1
} else {
// part of the "constant"
productRuleVec(dcont) *= (point(iSD)-localCoords1d(icont));
}
}
}
}
// sum the terms produced by the product rule and store in Deriv
for (int dcont=0; dcont<numEltNodes1d; ++dcont) {
lagrangeDeriv(iSD,inode) += productRuleVec(dcont);
}
productRuleVec = 1.0;
}
}
// divide by denom
for (int iSD=0; iSD<nSD_; ++iSD) {
for (int inode=0; inode<numEltNodes1d; ++inode) {
lagrangeTerms(iSD,inode) /= lagrangeDenom(iSD,inode);
lagrangeDeriv(iSD,inode) /= lagrangeDenom(iSD,inode);
}
}
N = 1.0;
dNdr = 1.0;
// mapping returns the 1d nodes in each dimension that sould be multiplied
// to achieve the shape functions in 3d
vector<int> mapping(nSD_);
for (int inode=0; inode<numEltNodes; ++inode) {
feElement_->mapping(inode,mapping);
for (int iSD=0; iSD<nSD_; ++iSD) {
N(inode) *= lagrangeTerms(iSD,mapping[iSD]);
for (int dSD=0; dSD<nSD_; ++dSD) {
// only use Deriv for the dimension in which we're taking the
// derivative, because the rest is essentially a "constant"
if (iSD == dSD) {
dNdr(dSD,inode) *= lagrangeDeriv(iSD,mapping[iSD]);
} else {
dNdr(dSD,inode) *= lagrangeTerms(iSD,mapping[iSD]);
}
}
}
}
}
/*********************************************************
* Class FE_InterpolateCartLin
*
* For computing linear shape functions using Cartesian
* coordinate systems (all quads/hexes fall under this
* category, and any elements derived by degenerating
* them).
*
*********************************************************/
FE_InterpolateCartLin::FE_InterpolateCartLin(
FE_Element *feElement)
: FE_Interpolate(feElement)
{
set_quadrature(HEXA,GAUSS2);
}
FE_InterpolateCartLin::~FE_InterpolateCartLin()
{
// Handled by base class
}
void FE_InterpolateCartLin::compute_N(const VECTOR &point,
VECTOR &N)
{
// *** see comments for compute_N_dNdr ***
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/(feElement_->vol());
int numEltNodes = feElement_->num_elt_nodes();
for (int inode = 0; inode < numEltNodes; ++inode) {
N(inode) = invVol;
for (int isd = 0; isd < nSD_; ++isd) {
N(inode) *= (1.0 + point(isd)*localCoords(isd,inode));
}
}
}
// Sort of a test-ride for a generic version that can be used for
// faces too. The only thing that's not "generic" is localCoords,
// which very magically works in both cases.
void FE_InterpolateCartLin::compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double vol,
DENS_MAT &dNdr)
{
// *** see comments for compute_N_dNdr ***
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/vol;
for (int inode = 0; inode < numNodes; ++inode) {
for (int idr = 0; idr < nD; ++idr) {
dNdr(idr,inode) = invVol;
}
for (int id = 0; id < nD; ++id) {
for (int idr = 0; idr < nD; ++idr) {
if (id == idr) dNdr(idr,inode) *= localCoords(id,inode);
else dNdr(idr,inode) *= 1.0 +
point(id)*localCoords(id,inode);
}
}
}
}
void FE_InterpolateCartLin::compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const
{
// Required data from element class
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/(feElement_->vol());
int numEltNodes = feElement_->num_elt_nodes();
// Fill in for each node
for (int inode = 0; inode < numEltNodes; ++inode) {
// Intiialize shape function and derivatives
N(inode) = invVol;
for (int idr = 0; idr < nSD_; ++idr) {
dNdr(idr,inode) = invVol;
}
for (int isd = 0; isd < nSD_; ++isd) {
// One term for each dimension
N(inode) *= (1.0 + point(isd)*localCoords(isd,inode));
// One term for each dimension, only deriv in deriv's dimension
for (int idr = 0; idr < nSD_; ++idr) {
if (isd == idr) dNdr(idr,inode) *= localCoords(isd,inode);
else dNdr(idr,inode) *= 1.0 +
point(isd)*localCoords(isd,inode);
}
}
}
}
/*********************************************************
* Class FE_InterpolateCartSerendipity
*
* For computing shape functions for quadratic serendipity
* elements, implemented for SPEED.
*
*********************************************************/
FE_InterpolateCartSerendipity::FE_InterpolateCartSerendipity(
FE_Element *feElement)
: FE_Interpolate(feElement)
{
set_quadrature(HEXA,GAUSS2);
}
FE_InterpolateCartSerendipity::~FE_InterpolateCartSerendipity()
{
// Handled by base class
}
void FE_InterpolateCartSerendipity::compute_N(const VECTOR &point,
VECTOR &N)
{
// *** see comments for compute_N_dNdr ***
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/(feElement_->vol());
int numEltNodes = feElement_->num_elt_nodes();
for (int inode = 0; inode < numEltNodes; ++inode) {
N(inode) = invVol;
for (int isd = 0; isd < nSD_; ++isd) {
if (((inode == 8 || inode == 10 || inode == 16 || inode == 18) &&
(isd == 0)) ||
((inode == 9 || inode == 11 || inode == 17 || inode == 19) &&
(isd == 1)) ||
((inode == 12 || inode == 13 || inode == 14 || inode == 15) &&
(isd == 2))) {
N(inode) *= (1.0 - pow(point(isd),2))*2;
} else {
N(inode) *= (1.0 + point(isd)*localCoords(isd,inode));
}
}
if (inode < 8) {
N(inode) *= (point(0)*localCoords(0,inode) +
point(1)*localCoords(1,inode) +
point(2)*localCoords(2,inode) - 2);
}
}
}
// Sort of a test-ride for a generic version that can be used for
// faces too. The only thing that's not "generic" is localCoords,
// which very magically works in both cases.
void FE_InterpolateCartSerendipity::compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double vol,
DENS_MAT &dNdr)
{
// *** see comments for compute_N_dNdr ***
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/vol;
bool serendipityNode = false;
double productRule1 = 0.0;
double productRule2 = 0.0;
if (nD != 3 && nD != 2) {
ATC_Error("Serendipity dNdr calculations are too hard-wired to do "
"what you want them to. Only 2D and 3D currently work.");
}
for (int inode = 0; inode < numNodes; ++inode) {
for (int idr = 0; idr < nD; ++idr) {
dNdr(idr,inode) = invVol;
}
for (int id = 0; id < nD; ++id) {
for (int idr = 0; idr < nD; ++idr) {
// identify nodes/dims differently if 3d or 2d case
if (nD == 3) {
serendipityNode =
(((inode == 8 || inode == 10 || inode == 16 || inode == 18) &&
(id == 0)) ||
((inode == 9 || inode == 11 || inode == 17 || inode == 19) &&
(id == 1)) ||
((inode == 12 || inode == 13 || inode == 14 || inode == 15) &&
(id == 2)));
} else if (nD == 2) {
serendipityNode =
(((inode == 4 || inode == 6) && (id == 0)) ||
((inode == 5 || inode == 7) && (id == 1)));
}
if (serendipityNode) {
if (id == idr) {
dNdr(idr,inode) *= point(id)*(-4);
} else {
dNdr(idr,inode) *= (1.0 - pow(point(id),2))*2;
}
} else {
if (id == idr) {
dNdr(idr,inode) *= localCoords(id,inode);
} else {
dNdr(idr,inode) *= (1.0 + point(id)*localCoords(id,inode));
}
}
}
}
for (int idr = 0; idr < nD; ++idr) {
if (inode < 8) {
// final corner contribution slightly different for 3d and 2d cases
if (nD == 3) {
productRule2 = (point(0)*localCoords(0,inode) +
point(1)*localCoords(1,inode) +
point(2)*localCoords(2,inode) - 2);
} else if (nD == 2) {
productRule2 = (point(0)*localCoords(0,inode) +
point(1)*localCoords(1,inode) - 1);
}
productRule1 = dNdr(idr,inode) *
(1 + point(idr)*localCoords(idr,inode));
productRule2 *= dNdr(idr,inode);
dNdr(idr,inode) = productRule1 + productRule2;
}
}
}
}
void FE_InterpolateCartSerendipity::compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const
{
// Required data from element class
const DENS_MAT &localCoords = feElement_->local_coords();
double invVol = 1.0/(feElement_->vol());
int numEltNodes = feElement_->num_elt_nodes();
// Will store terms for product rule derivative for dNdr
double productRule1;
double productRule2;
// Fill in for each node
for (int inode = 0; inode < numEltNodes; ++inode) {
// Initialize shape functions and derivatives
N(inode) = invVol;
for (int idr = 0; idr < nSD_; ++idr) {
dNdr(idr,inode) = invVol;
}
// Add components from each dimension
for (int isd = 0; isd < nSD_; ++isd) {
for (int idr = 0; idr < nSD_; ++idr) {
// Check to see if the node is NOT a corner node, and if its
// "0-coordinate" is in the same dimension as the one we're currently
// iterating over. If that's the case, we want to contribute to its
// shape functions and derivatives in a modified way:
if (((inode == 8 || inode == 10 || inode == 16 || inode == 18) &&
(isd == 0)) ||
((inode == 9 || inode == 11 || inode == 17 || inode == 19) &&
(isd == 1)) ||
((inode == 12 || inode == 13 || inode == 14 || inode == 15) &&
(isd == 2))) {
// If the 1d shape function dimension matches the derivative
// dimension...
if (isd == idr) {
// contribute to N; sloppy, but this is the easiest way to get
// N to work right without adding extra, arguably unnecessary
// loops, while also computing the shape functions
N(inode) *= (1.0 - pow(point(isd),2))*2;
// contribute to dNdr with the derivative of this shape function
// contribution
dNdr(idr,inode) *= point(isd)*(-4);
} else {
// otherwise, just use the "constant" contribution to the deriv
dNdr(idr,inode) *= (1.0 - pow(point(isd),2))*2;
}
} else {
// non-serendipity style contributions
if (isd == idr) {
N(inode) *= (1.0 + point(isd)*localCoords(isd,inode));
dNdr(idr,inode) *= localCoords(isd,inode);
} else {
dNdr(idr,inode) *= (1.0 + point(isd)*localCoords(isd,inode));
}
}
}
}
// serendipity corner nodes require more extra handling
if (inode < 8) {
N(inode) *= (point(0)*localCoords(0,inode) +
point(1)*localCoords(1,inode) +
point(2)*localCoords(2,inode) - 2);
}
for (int idr = 0; idr < nSD_; ++idr) {
if (inode < 8) {
productRule1 = dNdr(idr,inode) *
(1 + point(idr)*localCoords(idr,inode));
productRule2 = dNdr(idr,inode) * (point(0)*localCoords(0,inode) +
point(1)*localCoords(1,inode) +
point(2)*localCoords(2,inode) - 2);
dNdr(idr,inode) = productRule1 + productRule2;
}
}
}
}
/*********************************************************
* Class FE_InterpolateSimpLin
*
* For computing linear shape functions of simplices,
* which are rather different from those computed
* in Cartesian coordinates.
*
* Note: degenerating quads/hexes can yield simplices
* as well, but this class is for computing these
* shape fucntions _natively_, in their own
* triangular/tetrahedral coordinate systems.
*
*********************************************************/
FE_InterpolateSimpLin::FE_InterpolateSimpLin(
FE_Element *feElement)
: FE_Interpolate(feElement)
{
set_quadrature(TETRA,GAUSS2);
}
FE_InterpolateSimpLin::~FE_InterpolateSimpLin()
{
// Handled by base class
}
void FE_InterpolateSimpLin::compute_N(const VECTOR &point,
VECTOR &N)
{
int numEltNodes = feElement_->num_elt_nodes();
// Fill in for each node
for (int inode = 0; inode < numEltNodes; ++inode) {
if (inode == 0) {
// Fill N...the ips are serving as proxies for "dimensions"
// since we're in tetrahedral coordinates, except that
// 0th node = 3rd "dimension" (u or O_o)
// 1st node = 0th "dimension" (x or r)
// 2nd node = 1st "dimension" (y or s)
// 3rd node = 3nd "dimension" (z or t)
// and remember that u = 1 - r - s - t for tet coords
N(inode) = 1;
for (int icont = 0; icont < nSD_; ++icont) {
N(inode) -= point(icont);
}
} else {
N(inode) = point(inode-1);
}
}
}
void FE_InterpolateSimpLin::compute_dNdr(const VECTOR &,
const int numNodes,
const int nD,
const double,
DENS_MAT &dNdr)
{
// Fill in for each node
for (int inode = 0; inode < numNodes; ++inode) {
// Fill dNdr_; we want 1 if the dimension of derivative
// and variable within N correspond. That is, if N == r,
// we want the 0th dimension to contain (d/dr)r = 1. Of
// course, (d/di)r = 0 forall i != r, so we need that as
// well. This is a bit elusively complicated. Also, the 0th
// integration point contains the term u = 1 - r - s - t.
// (which map to x, y, and z). Therefore, the derivative in
// each dimension are -1.
//
// The idea is similar for 2 dimensions, which this can
// handle as well.
for (int idr = 0; idr < nD; ++idr) {
if (inode == 0) {
dNdr(idr,inode) = -1;
} else {
dNdr(idr,inode) = (inode == (idr + 1)) ? 1 : 0;
}
}
}
}
void FE_InterpolateSimpLin::compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const
{
int numEltNodes = feElement_->num_elt_nodes();
// Fill in for each node
for (int inode = 0; inode < numEltNodes; ++inode) {
// Fill N...
if (inode == 0) {
N(inode) = 1;
for (int icont = 0; icont < nSD_; ++icont) {
N(inode) -= point(icont);
}
} else {
N(inode) = point(inode-1);
}
// Fill dNdr...
for (int idr = 0; idr < nSD_; ++idr) {
if (inode == 0) {
dNdr(idr,inode) = -1;
} else {
dNdr(idr,inode) = (inode == (idr + 1)) ? 1 : 0;
}
}
}
}
} // namespace ATC

264
lib/atc/FE_Interpolate.h Normal file
View File

@ -0,0 +1,264 @@
#ifndef FE_INTERPOLATE_H
#define FE_INTERPOLATE_H
// ATC_Transfer headers
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
using namespace std;
namespace ATC {
// Forward declarations
struct FE_Quadrature;
class FE_Element;
/**
* @class FE_Interpolate
* @brief Base class for computing shape functions, nested inside FE_Element
*/
class FE_Interpolate {
public:
FE_Interpolate(FE_Element *feElement);
virtual ~FE_Interpolate();
/** compute the quadrature for a given element type */
void set_quadrature(FeEltGeometry geo, FeIntQuadrature quad);
/** store N_ and dNdr_ (and eventually dNdrFace_) the given
* quadrature */
virtual void precalculate_shape_functions();
/** compute the shape functions at the given point;
* we use CLON_VECs */
virtual void compute_N(const VECTOR &point,
VECTOR &N) = 0;
// middle args get no names because they won't be used by some
// child classes.
/** compute the shape function derivatives at the given point;
* generic, so can work for 2D or 3D case */
virtual void compute_dNdr(const VECTOR &point,
const int,
const int,
const double,
DENS_MAT &dNdr) = 0;
/** compute both shape functions and derivatives, using the
* fastest, shortcuttiest methods available for batch use */
virtual void compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const = 0;
/** compute shape functions at a single point, given the local
* coordinates; eltCoords needed for derivatives:
* indexed: N(node)
* dN[nsd](node) */
virtual void shape_function(const VECTOR &xi,
DENS_VEC &N);
virtual void shape_function(const DENS_MAT &eltCoords,
const VECTOR &xi,
DENS_VEC &N,
DENS_MAT &dNdx);
virtual void shape_function_derivatives(const DENS_MAT &eltCoords,
const VECTOR &xi,
DENS_MAT &dNdx);
/** compute shape functions at all ip's:
* indexed: N(ip,node)
* dN[nsd](ip,node)
* weights(ip) */
virtual void shape_function(const DENS_MAT &eltCoords,
DENS_MAT &N,
vector<DENS_MAT> &dN,
DIAG_MAT &weights);
/** compute shape functions at all face ip's:
* indexed: N(ip,node)
* dN[nsd](ip,node)
* n(ip,node)/Nn[nsd](ip,node)
* weights(ip) */
virtual void face_shape_function(const DENS_MAT &eltCoords,
const DENS_MAT &faceCoords,
const int faceID,
DENS_MAT &N,
DENS_MAT &n,
DIAG_MAT &weights);
virtual void face_shape_function(const DENS_MAT &eltCoords,
const DENS_MAT &faceCoords,
const int faceID,
DENS_MAT &N,
vector<DENS_MAT> &dN,
vector<DENS_MAT> &Nn,
DIAG_MAT &weights);
/** compute unit normal vector for a face:
* indexed: N(ip,node)
* dN[nsd](ip,node)
* n(ip,node)/Nn[nsd](ip,node)
* weights(ip) */
virtual double face_normal(const DENS_MAT &faceCoords,
int ip,
DENS_VEC &normal);
/** compute tangents to coordinate lines
* indexed: */
virtual void tangents(const DENS_MAT &eltCoords,
const VECTOR &localCoords,
DENS_MAT &dxdr) const;
virtual void tangents(const DENS_MAT &eltCoords,
const VECTOR &localCoords,
vector<DENS_VEC> &t,
const bool normalize = false) const;
/** get number of ips in scheme */
int num_ips() const;
/** get number of ips per face */
int num_face_ips() const;
protected:
// owner element
FE_Element *feElement_;
// Number of dimensions
int nSD_;
map<FeIntQuadrature,FE_Quadrature *> feQuadList_;
FE_Quadrature *feQuad_;
// matrix of shape functions at ip's: N_(ip, node)
DENS_MAT N_;
vector<DENS_MAT> dNdr_;
// matrix of shape functions at ip's: N_(ip, node)
vector<DENS_MAT> NFace_;
// matrix of generic face shape function derivatives
vector<vector<DENS_MAT> > dNdrFace_;
// matrix of generic face shape function derivatives
vector<DENS_MAT> dNdrFace2D_;
};
/**********************************************
* Class for linear interpolation functions with
* Cartesian coordinates
**********************************************/
class FE_InterpolateCartLagrange : public FE_Interpolate {
public:
FE_InterpolateCartLagrange(FE_Element *feElement);
virtual ~FE_InterpolateCartLagrange();
virtual void compute_N(const VECTOR &point,
VECTOR &N);
virtual void compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double,
DENS_MAT &dNdr);
virtual void compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const;
};
/**********************************************
* Class for linear elements with
* Cartesian coordinates
**********************************************/
class FE_InterpolateCartLin : public FE_Interpolate {
public:
FE_InterpolateCartLin(FE_Element *feElement);
virtual ~FE_InterpolateCartLin();
virtual void compute_N(const VECTOR &point,
VECTOR &N);
virtual void compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double vol,
DENS_MAT &dNdr);
virtual void compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const;
};
/**********************************************
* Class for quadratic serendipity elements with
* Cartesian coordinates
**********************************************/
class FE_InterpolateCartSerendipity : public FE_Interpolate {
public:
FE_InterpolateCartSerendipity(FE_Element *feElement);
virtual ~FE_InterpolateCartSerendipity();
virtual void compute_N(const VECTOR &point,
VECTOR &N);
virtual void compute_dNdr(const VECTOR &point,
const int numNodes,
const int nD,
const double vol,
DENS_MAT &dNdr);
virtual void compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const;
};
/**********************************************
* Class for linear interpolation functions with
* volumetric coordinates
**********************************************/
class FE_InterpolateSimpLin : public FE_Interpolate {
public:
// "Simp"ly overrides all standard shape function methods
FE_InterpolateSimpLin(FE_Element *feElement);
virtual ~FE_InterpolateSimpLin();
virtual void compute_N(const VECTOR &point,
VECTOR &N);
// middle args get no names because they won't be used by some
// child classes.
virtual void compute_dNdr(const VECTOR &,
const int,
const int,
const double,
DENS_MAT &dNdr);
virtual void compute_N_dNdr(const VECTOR &point,
VECTOR &N,
DENS_MAT &dNdr) const;
};
}; // namespace ATC
#endif // FE_INTERPOLATE_H

3046
lib/atc/FE_Mesh.cpp Normal file

File diff suppressed because it is too large Load Diff

707
lib/atc/FE_Mesh.h Normal file
View File

@ -0,0 +1,707 @@
#ifndef FE_MESH_H
#define FE_MESH_H
// ATC_Transfer headers
#include "Array.h"
#include "Array2D.h"
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
#include "KD_Tree.h"
// Other headers
#include <vector>
#include <deque>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <float.h>
#include "mpi.h"
using namespace std;
namespace ATC {
// Forward declarations
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(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,
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,
vector<DENS_MAT> &dN,
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
{ 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 string & name) const;
/** get node set (unique ID's) from the string name assigned to the set */
const set<int> & nodeset(const string & name) const;
/** create node set with tag "name" from nodes in given spatial range */
void create_nodeset(const string & name, const set<int> & nodeset);
void create_nodeset(const 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 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 set<int> & elementset(const string & name) const;
/** create element set with tag "name" from nodes in given spatial range */
void create_elementset(const 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 string &name,
set<int> &elemSet) const;
/** get the minimal element set from a set of nodes */
void nodeset_to_minimal_elementset(const set<int> &nodeSet,
set<int> &elemSet) const;
/** get the maximal element set from a nodeset by name */
void nodeset_to_maximal_elementset(const string &name,
set<int> &elemSet) const;
/** get the maximal element set from a set of nodes */
void nodeset_to_maximal_elementset(const set<int> &nodeSet,
set<int> &elemSet) const;
/** get complement of element set by name */
void elementset_complement(const string &name,
set<int> &elemSet) const;
void elementset_complement(const set<int> &elemSet,
set<int> &elemSetComplement) const;
/** get the node set from an element set by name */
void elementset_to_minimal_nodeset(const string &name,
set<int> &nodeSet) const;
void elementset_to_nodeset(const string &name,
set<int> &nodeSet) const;
void elementset_to_nodeset(const set<int> &elemSet,
set<int> &nodeSet) const;
/** convert faceset to nodeset in _unique_ node numbering */
void faceset_to_nodeset(const string &name,
set<int> &nodeSet) const;
void faceset_to_nodeset(const set<PAIR> &faceSet,
set<int> &nodeSet) const;
void faceset_to_nodeset_global(const string &name,
set<int> &nodeSet) const;
void faceset_to_nodeset_global(const set<PAIR> &faceSet,
set<int> &nodeSet) const;
/** get face set from the string name assigned to the set */
const set< pair<int,int> > & faceset(const string & name) const;
/** create face set with tag "name" from faces aligned with box */
void create_faceset(const 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 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 set<PAIR> & faceSet, const set<int> & nodeSet) = 0;
/** delete elements */
virtual void delete_elements(const 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 */
string element_type(void) const ;
/** output mesh subsets */
void output(string prefix) const;
/* Parallelization data members */
/** return element vector for this processor */
const vector<int> & owned_elts() const { return myElts_; }
const vector<int> & owned_and_ghost_elts() const {
return (decomposition_) ? myAndGhostElts_: myElts_; }
bool is_owned_elt(int elt) const;
protected:
/** 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<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 */
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 */
set<int> elementSetAll_;
/** length scaling used by lammps */
double xscale_, yscale_, zscale_;
/** Processor demarcations */
vector<double> procs_;
/** Dimension (x=0, y=1, or z=2) */
int partitionAxis_;
/** List of nodes for this processor */
vector<int> myNodes_;
/** List of elements for this processor */
vector<int> myElts_;
vector<int> myAndGhostElts_;
/** maps between my IDs and the total IDs */
map<int,int> elemToMyElemMap_;
/** Lists of ghost nodes/neighbor ghost nodes */
vector<int> ghostNodesL_;
vector<int> ghostNodesR_;
vector<int> shareNodesL_;
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 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);
/** 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(vector<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(vector<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 list<int> &elts,
DENS_MAT &faceAdjacencies);
/** Counts the number of nonempty vectors in a vector of vectors. **/
int numNonempty(vector<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() (vector<int> l, 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 set<PAIR> &faceSet,
const set<int> &nodeSet);
virtual void delete_elements(const 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, set<int> & nodes);
bool match_nodes(int idim, set<int> & top, 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<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 */
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 min(L_[0]/n_[0], 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

710
lib/atc/FE_Quadrature.h Normal file
View File

@ -0,0 +1,710 @@
#ifndef FE_QUADRATURE_H
#define FE_QUADRATURE_H
// ATC_Transfer headers
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
using namespace std;
namespace ATC {
/**
* @class FE_Quadrature
* @brief Stores quadrature schemes
*/
struct FE_Quadrature {
int numIPs;
int numFaceIPs;
DENS_MAT ipCoords;
vector<DENS_MAT> ipFaceCoords;
DENS_MAT ipFace2DCoords;
DENS_VEC ipWeights;
DENS_VEC ipFaceWeights;
FE_Quadrature(FeEltGeometry geo, FeIntQuadrature quad)
{
if (geo == HEXA) {
switch (quad) {
// Degenerate 1 point quadrature
case GAUSS1: {
// Set number of IPs and face IPs
numIPs = 1;
numFaceIPs = 1;
// Size matrices and vectors thereof accordingly
ipCoords.resize(3,numIPs);
ipFaceCoords.assign(6,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(2,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
// "Matrix" of integration point location
ipCoords(0,0) = 0.0;
ipCoords(1,0) = 0.0;
ipCoords(2,0) = 0.0;
// Integration point for each face
ipFaceCoords[0](0,0) = -1.0; ipFaceCoords[3](0,0) = 0.0;
ipFaceCoords[0](1,0) = 0.0; ipFaceCoords[3](1,0) = 1.0;
ipFaceCoords[0](2,0) = 0.0; ipFaceCoords[3](2,0) = 0.0;
//
ipFaceCoords[1](0,0) = 1.0; ipFaceCoords[4](0,0) = 0.0;
ipFaceCoords[1](1,0) = 0.0; ipFaceCoords[4](1,0) = 0.0;
ipFaceCoords[1](2,0) = 0.0; ipFaceCoords[4](2,0) = -1.0;
//
ipFaceCoords[2](0,0) = 0.0; ipFaceCoords[5](0,0) = 0.0;
ipFaceCoords[2](1,0) = -1.0; ipFaceCoords[5](1,0) = 0.0;
ipFaceCoords[2](2,0) = 0.0; ipFaceCoords[5](2,0) = 1.0;
// 2D integration scheme for the faces
ipFace2DCoords(0,0) = 0.0;
ipFace2DCoords(1,0) = 0.0;
// Integration point weights
ipWeights = 8.0;
// Face integration point weights
ipFaceWeights = 4.0;
break;
}
// 8 point quadratures
case NODAL:
case GAUSS2: {
// Set number of IPs and face IPs
numIPs = 8;
numFaceIPs = 4;
// Size matrices and vectors thereof accordingly
ipCoords.resize(3,numIPs);
ipFaceCoords.assign(6,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(2,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
// Dictates difference in node locations for nodal/GAUSS2
double a = 1.0/sqrt(3.0);
if (quad == NODAL) a = 1.0;
// Matrix of integration point locations & follows local
// conn
ipCoords(0,0) = -a; ipCoords(0,4) = -a;
ipCoords(1,0) = -a; ipCoords(1,4) = -a;
ipCoords(2,0) = -a; ipCoords(2,4) = a;
//
ipCoords(0,1) = a; ipCoords(0,5) = a;
ipCoords(1,1) = -a; ipCoords(1,5) = -a;
ipCoords(2,1) = -a; ipCoords(2,5) = a;
//
ipCoords(0,2) = a; ipCoords(0,6) = a;
ipCoords(1,2) = a; ipCoords(1,6) = a;
ipCoords(2,2) = -a; ipCoords(2,6) = a;
//
ipCoords(0,3) = -a; ipCoords(0,7) = -a;
ipCoords(1,3) = a; ipCoords(1,7) = a;
ipCoords(2,3) = -a; ipCoords(2,7) = a;
// Integration points by face
ipFaceCoords[0](0,0) = -1; ipFaceCoords[3](0,0) = -a;
ipFaceCoords[0](1,0) = -a; ipFaceCoords[3](1,0) = 1;
ipFaceCoords[0](2,0) = -a; ipFaceCoords[3](2,0) = -a;
//
ipFaceCoords[0](0,1) = -1; ipFaceCoords[3](0,1) = a;
ipFaceCoords[0](1,1) = a; ipFaceCoords[3](1,1) = 1;
ipFaceCoords[0](2,1) = -a; ipFaceCoords[3](2,1) = -a;
//
ipFaceCoords[0](0,2) = -1; ipFaceCoords[3](0,2) = a;
ipFaceCoords[0](1,2) = a; ipFaceCoords[3](1,2) = 1;
ipFaceCoords[0](2,2) = a; ipFaceCoords[3](2,2) = a;
//
ipFaceCoords[0](0,3) = -1; ipFaceCoords[3](0,3) = -a;
ipFaceCoords[0](1,3) = -a; ipFaceCoords[3](1,3) = 1;
ipFaceCoords[0](2,3) = a; ipFaceCoords[3](2,3) = a;
ipFaceCoords[1](0,0) = 1; ipFaceCoords[4](0,0) = -a;
ipFaceCoords[1](1,0) = -a; ipFaceCoords[4](1,0) = -a;
ipFaceCoords[1](2,0) = -a; ipFaceCoords[4](2,0) = -1;
//
ipFaceCoords[1](0,1) = 1; ipFaceCoords[4](0,1) = a;
ipFaceCoords[1](1,1) = a; ipFaceCoords[4](1,1) = -a;
ipFaceCoords[1](2,1) = -a; ipFaceCoords[4](2,1) = -1;
//
ipFaceCoords[1](0,2) = 1; ipFaceCoords[4](0,2) = a;
ipFaceCoords[1](1,2) = a; ipFaceCoords[4](1,2) = a;
ipFaceCoords[1](2,2) = a; ipFaceCoords[4](2,2) = -1;
//
ipFaceCoords[1](0,3) = 1; ipFaceCoords[4](0,3) = -a;
ipFaceCoords[1](1,3) = -a; ipFaceCoords[4](1,3) = a;
ipFaceCoords[1](2,3) = a; ipFaceCoords[4](2,3) = -1;
ipFaceCoords[2](0,0) = -a; ipFaceCoords[5](0,0) = -a;
ipFaceCoords[2](1,0) = -1; ipFaceCoords[5](1,0) = -a;
ipFaceCoords[2](2,0) = -a; ipFaceCoords[5](2,0) = 1;
//
ipFaceCoords[2](0,1) = a; ipFaceCoords[5](0,1) = a;
ipFaceCoords[2](1,1) = -1; ipFaceCoords[5](1,1) = -a;
ipFaceCoords[2](2,1) = -a; ipFaceCoords[5](2,1) = 1;
//
ipFaceCoords[2](0,2) = a; ipFaceCoords[5](0,2) = a;
ipFaceCoords[2](1,2) = -1; ipFaceCoords[5](1,2) = a;
ipFaceCoords[2](2,2) = a; ipFaceCoords[5](2,2) = 1;
//
ipFaceCoords[2](0,3) = -a; ipFaceCoords[5](0,3) = -a;
ipFaceCoords[2](1,3) = -1; ipFaceCoords[5](1,3) = a;
ipFaceCoords[2](2,3) = a; ipFaceCoords[5](2,3) = 1;
// Integration points for all faces ignoring the
// redundant dim
ipFace2DCoords(0,0) = -a; ipFace2DCoords(0,2) = a;
ipFace2DCoords(1,0) = -a; ipFace2DCoords(1,2) = a;
//
ipFace2DCoords(0,1) = a; ipFace2DCoords(0,3) = -a;
ipFace2DCoords(1,1) = -a; ipFace2DCoords(1,3) = a;
// Integration point weights
ipWeights = 1.0;
// Face integration point weights
ipFaceWeights = 1.0;
break;
}
// 6 point "face" quadrature
case FACE: {
printf("using face quad!\n");
// Set number of IPs and face IPs
numIPs = 6;
numFaceIPs = 4;
// Size matrices and vectors thereof accordingly
ipCoords.resize(3,numIPs);
ipFaceCoords.assign(6,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(2,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
// Use GAUSS2 for faces for now...
double a = 1.0/sqrt(3.0);
// Matrix of integration point locations
ipCoords(0,0) = 1.0; ipCoords(0,3) = 0.0;
ipCoords(1,0) = 0.0; ipCoords(1,3) = -1.0;
ipCoords(2,0) = 0.0; ipCoords(2,3) = 0.0;
//
ipCoords(0,1) = -1.0; ipCoords(0,4) = 0.0;
ipCoords(1,1) = 0.0; ipCoords(1,4) = 0.0;
ipCoords(2,1) = 0.0; ipCoords(2,4) = 1.0;
//
ipCoords(0,2) = 0.0; ipCoords(0,5) = 0.0;
ipCoords(1,2) = 1.0; ipCoords(1,5) = 0.0;
ipCoords(2,2) = 0.0; ipCoords(2,5) = -1.0;
// Integration points by face
ipFaceCoords[0](0,0) = -1; ipFaceCoords[3](0,0) = -a;
ipFaceCoords[0](1,0) = -a; ipFaceCoords[3](1,0) = 1;
ipFaceCoords[0](2,0) = -a; ipFaceCoords[3](2,0) = -a;
//
ipFaceCoords[0](0,1) = -1; ipFaceCoords[3](0,1) = -a;
ipFaceCoords[0](1,1) = a; ipFaceCoords[3](1,1) = 1;
ipFaceCoords[0](2,1) = -a; ipFaceCoords[3](2,1) = a;
//
ipFaceCoords[0](0,2) = -1; ipFaceCoords[3](0,2) = a;
ipFaceCoords[0](1,2) = a; ipFaceCoords[3](1,2) = 1;
ipFaceCoords[0](2,2) = a; ipFaceCoords[3](2,2) = a;
//
ipFaceCoords[0](0,3) = -1; ipFaceCoords[3](0,3) = a;
ipFaceCoords[0](1,3) = -a; ipFaceCoords[3](1,3) = 1;
ipFaceCoords[0](2,3) = a; ipFaceCoords[3](2,3) = -a;
ipFaceCoords[1](0,0) = 1; ipFaceCoords[4](0,0) = -a;
ipFaceCoords[1](1,0) = -a; ipFaceCoords[4](1,0) = -a;
ipFaceCoords[1](2,0) = -a; ipFaceCoords[4](2,0) = -1;
//
ipFaceCoords[1](0,1) = 1; ipFaceCoords[4](0,1) = a;
ipFaceCoords[1](1,1) = a; ipFaceCoords[4](1,1) = -a;
ipFaceCoords[1](2,1) = -a; ipFaceCoords[4](2,1) = -1;
//
ipFaceCoords[1](0,2) = 1; ipFaceCoords[4](0,2) = a;
ipFaceCoords[1](1,2) = a; ipFaceCoords[4](1,2) = a;
ipFaceCoords[1](2,2) = a; ipFaceCoords[4](2,2) = -1;
//
ipFaceCoords[1](0,3) = 1; ipFaceCoords[4](0,3) = -a;
ipFaceCoords[1](1,3) = -a; ipFaceCoords[4](1,3) = a;
ipFaceCoords[1](2,3) = a; ipFaceCoords[4](2,3) = -1;
ipFaceCoords[2](0,0) = -a; ipFaceCoords[5](0,0) = -a;
ipFaceCoords[2](1,0) = -1; ipFaceCoords[5](1,0) = -a;
ipFaceCoords[2](2,0) = -a; ipFaceCoords[5](2,0) = 1;
//
ipFaceCoords[2](0,1) = -a; ipFaceCoords[5](0,1) = a;
ipFaceCoords[2](1,1) = -1; ipFaceCoords[5](1,1) = -a;
ipFaceCoords[2](2,1) = a; ipFaceCoords[5](2,1) = 1;
//
ipFaceCoords[2](0,2) = a; ipFaceCoords[5](0,2) = a;
ipFaceCoords[2](1,2) = -1; ipFaceCoords[5](1,2) = a;
ipFaceCoords[2](2,2) = a; ipFaceCoords[5](2,2) = 1;
//
ipFaceCoords[2](0,3) = a; ipFaceCoords[5](0,3) = -a;
ipFaceCoords[2](1,3) = -1; ipFaceCoords[5](1,3) = a;
ipFaceCoords[2](2,3) = -a; ipFaceCoords[5](2,3) = 1;
// Integration points for all faces ignoring the
// redundant dim
ipFace2DCoords(0,0) = -a; ipFace2DCoords(0,2) = a;
ipFace2DCoords(1,0) = -a; ipFace2DCoords(1,2) = a;
//
ipFace2DCoords(0,1) = a; ipFace2DCoords(0,3) = -a;
ipFace2DCoords(1,1) = -a; ipFace2DCoords(1,3) = a;
// Integration point weights
ipWeights = 4.0/3.0;
// Face integration point weights
ipFaceWeights = 1.0;
break;
}
// 27 point quadrature
case GAUSS3: {
// Set number of IPs and face IPs
numIPs = 27;
numFaceIPs = 9;
// Size matrices and vectors thereof accordingly
ipCoords.resize(3,numIPs);
ipFaceCoords.assign(6,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(2,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
double a = sqrt(3.0/5.0);
// Matrix of integration point locations & follows local
// conn
ipCoords(0,0) = -a; ipCoords(0,16) = -a;
ipCoords(1,0) = -a; ipCoords(1,16) = -a;
ipCoords(2,0) = -a; ipCoords(2,16) = 0;
//
ipCoords(0,1) = a; ipCoords(0,17) = -a;
ipCoords(1,1) = -a; ipCoords(1,17) = a;
ipCoords(2,1) = -a; ipCoords(2,17) = 0;
//
ipCoords(0,2) = a; ipCoords(0,18) = a;
ipCoords(1,2) = a; ipCoords(1,18) = -a;
ipCoords(2,2) = -a; ipCoords(2,18) = 0;
//
ipCoords(0,3) = -a; ipCoords(0,19) = a;
ipCoords(1,3) = a; ipCoords(1,19) = a;
ipCoords(2,3) = -a; ipCoords(2,19) = 0;
ipCoords(0,4) = -a; ipCoords(0,20) = 0;
ipCoords(1,4) = -a; ipCoords(1,20) = 0;
ipCoords(2,4) = a; ipCoords(2,20) = -a;
ipCoords(0,5) = a; ipCoords(0,21) = 0;
ipCoords(1,5) = -a; ipCoords(1,21) = 0;
ipCoords(2,5) = a; ipCoords(2,21) = a;
ipCoords(0,6) = a; ipCoords(0,22) = 0;
ipCoords(1,6) = a; ipCoords(1,22) = -a;
ipCoords(2,6) = a; ipCoords(2,22) = 0;
ipCoords(0,7) = -a; ipCoords(0,23) = 0;
ipCoords(1,7) = a; ipCoords(1,23) = a;
ipCoords(2,7) = a; ipCoords(2,23) = 0;
ipCoords(0,8) = 0; ipCoords(0,24) = -a;
ipCoords(1,8) = -a; ipCoords(1,24) = 0;
ipCoords(2,8) = -a; ipCoords(2,24) = 0;
ipCoords(0,9) = 0; ipCoords(0,25) = a;
ipCoords(1,9) = -a; ipCoords(1,25) = 0;
ipCoords(2,9) = a; ipCoords(2,25) = 0;
ipCoords(0,10) = 0; ipCoords(0,26) = 0;
ipCoords(1,10) = a; ipCoords(1,26) = 0;
ipCoords(2,10) = -a; ipCoords(2,26) = 0;
ipCoords(0,11) = 0;
ipCoords(1,11) = a;
ipCoords(2,11) = a;
ipCoords(0,12) = -a;
ipCoords(1,12) = 0;
ipCoords(2,12) = -a;
ipCoords(0,13) = -a;
ipCoords(1,13) = 0;
ipCoords(2,13) = a;
ipCoords(0,14) = a;
ipCoords(1,14) = 0;
ipCoords(2,14) = -a;
ipCoords(0,15) = a;
ipCoords(1,15) = 0;
ipCoords(2,15) = a;
// Integration points by face
ipFaceCoords[0](0,0) = -1; ipFaceCoords[0](0,5) = -1;
ipFaceCoords[0](1,0) = -a; ipFaceCoords[0](1,5) = 0;
ipFaceCoords[0](2,0) = -a; ipFaceCoords[0](2,5) = a;
//
ipFaceCoords[0](0,1) = -1; ipFaceCoords[0](0,6) = -1;
ipFaceCoords[0](1,1) = a; ipFaceCoords[0](1,6) = -a;
ipFaceCoords[0](2,1) = -a; ipFaceCoords[0](2,6) = 0;
//
ipFaceCoords[0](0,2) = -1; ipFaceCoords[0](0,7) = -1;
ipFaceCoords[0](1,2) = a; ipFaceCoords[0](1,7) = a;
ipFaceCoords[0](2,2) = a; ipFaceCoords[0](2,7) = 0;
//
ipFaceCoords[0](0,3) = -1; ipFaceCoords[0](0,8) = -1;
ipFaceCoords[0](1,3) = -a; ipFaceCoords[0](1,8) = 0;
ipFaceCoords[0](2,3) = a; ipFaceCoords[0](2,8) = 0;
//
ipFaceCoords[0](0,4) = -1;
ipFaceCoords[0](1,4) = 0;
ipFaceCoords[0](2,4) = -a;
ipFaceCoords[1](0,0) = 1; ipFaceCoords[1](0,5) = 1;
ipFaceCoords[1](1,0) = -a; ipFaceCoords[1](1,5) = 0;
ipFaceCoords[1](2,0) = -a; ipFaceCoords[1](2,5) = a;
//
ipFaceCoords[1](0,1) = 1; ipFaceCoords[1](0,6) = 1;
ipFaceCoords[1](1,1) = a; ipFaceCoords[1](1,6) = -a;
ipFaceCoords[1](2,1) = -a; ipFaceCoords[1](2,6) = 0;
//
ipFaceCoords[1](0,2) = 1; ipFaceCoords[1](0,7) = 1;
ipFaceCoords[1](1,2) = a; ipFaceCoords[1](1,7) = a;
ipFaceCoords[1](2,2) = a; ipFaceCoords[1](2,7) = 0;
//
ipFaceCoords[1](0,3) = 1; ipFaceCoords[1](0,8) = 1;
ipFaceCoords[1](1,3) = -a; ipFaceCoords[1](1,8) = 0;
ipFaceCoords[1](2,3) = a; ipFaceCoords[1](2,8) = 0;
//
ipFaceCoords[1](0,4) = 1;
ipFaceCoords[1](1,4) = 0;
ipFaceCoords[1](2,4) = -a;
ipFaceCoords[2](0,0) = -a; ipFaceCoords[2](0,5) = 0;
ipFaceCoords[2](1,0) = -1; ipFaceCoords[2](1,5) = -1;
ipFaceCoords[2](2,0) = -a; ipFaceCoords[2](2,5) = a;
//
ipFaceCoords[2](0,1) = -a; ipFaceCoords[2](0,6) = -a;
ipFaceCoords[2](1,1) = -1; ipFaceCoords[2](1,6) = -1;
ipFaceCoords[2](2,1) = a; ipFaceCoords[2](2,6) = 0;
//
ipFaceCoords[2](0,2) = a; ipFaceCoords[2](0,7) = a;
ipFaceCoords[2](1,2) = -1; ipFaceCoords[2](1,7) = -1;
ipFaceCoords[2](2,2) = a; ipFaceCoords[2](2,7) = 0;
//
ipFaceCoords[2](0,3) = a; ipFaceCoords[2](0,8) = 0;
ipFaceCoords[2](1,3) = -1; ipFaceCoords[2](1,8) = -1;
ipFaceCoords[2](2,3) = -a; ipFaceCoords[2](2,8) = 0;
//
ipFaceCoords[2](0,4) = 0;
ipFaceCoords[2](1,4) = -1;
ipFaceCoords[2](2,4) = -a;
ipFaceCoords[3](0,0) = -a; ipFaceCoords[3](0,5) = 0;
ipFaceCoords[3](1,0) = 1; ipFaceCoords[3](1,5) = 1;
ipFaceCoords[3](2,0) = -a; ipFaceCoords[3](2,5) = a;
//
ipFaceCoords[3](0,1) = -a; ipFaceCoords[3](0,6) = -a;
ipFaceCoords[3](1,1) = 1; ipFaceCoords[3](1,6) = 1;
ipFaceCoords[3](2,1) = a; ipFaceCoords[3](2,6) = 0;
//
ipFaceCoords[3](0,2) = a; ipFaceCoords[3](0,7) = a;
ipFaceCoords[3](1,2) = 1; ipFaceCoords[3](1,7) = 1;
ipFaceCoords[3](2,2) = a; ipFaceCoords[3](2,7) = 0;
//
ipFaceCoords[3](0,3) = a; ipFaceCoords[3](0,8) = 0;
ipFaceCoords[3](1,3) = 1; ipFaceCoords[3](1,8) = 1;
ipFaceCoords[3](2,3) = -a; ipFaceCoords[3](2,8) = 0;
//
ipFaceCoords[3](0,4) = 0;
ipFaceCoords[3](1,4) = 1;
ipFaceCoords[3](2,4) = -a;
ipFaceCoords[4](0,0) = -a; ipFaceCoords[4](0,5) = 0;
ipFaceCoords[4](1,0) = -a; ipFaceCoords[4](1,5) = a;
ipFaceCoords[4](2,0) = -1; ipFaceCoords[4](2,5) = -1;
//
ipFaceCoords[4](0,1) = a; ipFaceCoords[4](0,6) = -a;
ipFaceCoords[4](1,1) = -a; ipFaceCoords[4](1,6) = 0;
ipFaceCoords[4](2,1) = -1; ipFaceCoords[4](2,6) = -1;
//
ipFaceCoords[4](0,2) = a; ipFaceCoords[4](0,7) = a;
ipFaceCoords[4](1,2) = a; ipFaceCoords[4](1,7) = 0;
ipFaceCoords[4](2,2) = -1; ipFaceCoords[4](2,7) = -1;
//
ipFaceCoords[4](0,3) = -a; ipFaceCoords[4](0,8) = 0;
ipFaceCoords[4](1,3) = a; ipFaceCoords[4](1,8) = 0;
ipFaceCoords[4](2,3) = -1; ipFaceCoords[4](2,8) = -1;
//
ipFaceCoords[4](0,4) = 0;
ipFaceCoords[4](1,4) = -a;
ipFaceCoords[4](2,4) = -1;
ipFaceCoords[5](0,0) = -a; ipFaceCoords[5](0,5) = 0;
ipFaceCoords[5](1,0) = -a; ipFaceCoords[5](1,5) = a;
ipFaceCoords[5](2,0) = 1; ipFaceCoords[5](2,5) = 1;
//
ipFaceCoords[5](0,1) = a; ipFaceCoords[5](0,6) = -a;
ipFaceCoords[5](1,1) = -a; ipFaceCoords[5](1,6) = 0;
ipFaceCoords[5](2,1) = 1; ipFaceCoords[5](2,6) = 1;
//
ipFaceCoords[5](0,2) = a; ipFaceCoords[5](0,7) = a;
ipFaceCoords[5](1,2) = a; ipFaceCoords[5](1,7) = 0;
ipFaceCoords[5](2,2) = 1; ipFaceCoords[5](2,7) = 1;
//
ipFaceCoords[5](0,3) = -a; ipFaceCoords[5](0,8) = 0;
ipFaceCoords[5](1,3) = a; ipFaceCoords[5](1,8) = 0;
ipFaceCoords[5](2,3) = 1; ipFaceCoords[5](2,8) = 1;
//
ipFaceCoords[5](0,4) = 0;
ipFaceCoords[5](1,4) = -a;
ipFaceCoords[5](2,4) = 1;
// Integration points for all faces ignoring the
// redundant dim
ipFace2DCoords(0,0) = -a; ipFace2DCoords(0,5) = 0;
ipFace2DCoords(1,0) = -a; ipFace2DCoords(1,5) = a;
//
ipFace2DCoords(0,1) = a; ipFace2DCoords(0,6) = -a;
ipFace2DCoords(1,1) = -a; ipFace2DCoords(1,6) = 0;
//
ipFace2DCoords(0,2) = a; ipFace2DCoords(0,7) = a;
ipFace2DCoords(1,2) = a; ipFace2DCoords(1,7) = 0;
//
ipFace2DCoords(0,3) = -a; ipFace2DCoords(0,8) = 0;
ipFace2DCoords(1,3) = a; ipFace2DCoords(1,8) = 0;
//
ipFace2DCoords(0,4) = 0;
ipFace2DCoords(1,4) = -a;
// Integration point weights
for (int i=0; i<numIPs; ++i) {
if (i < 8) ipWeights[i] = 125.0/729.0;
else if (i < 20) ipWeights[i] = 200.0/729.0;
else if (i < 26) ipWeights[i] = 320.0/729.0;
else ipWeights[i] = 512.0/729.0;
}
// Face integration point weights
for (int i=0; i<numFaceIPs; ++i) {
if (i < 4) ipFaceWeights[i] = 25.0/81.0;
else if (i < 8) ipFaceWeights[i] = 40.0/81.0;
else ipFaceWeights[i] = 64.0/81.0;
}
break;
}
// Error
default: {
throw ATC_Error("Unrecognized quadrature type "
"for element type HEXA.");
}
}
} else if (geo == TETRA) {
switch (quad) {
// 4 point quadratures
case NODAL:
case GAUSS2: {
// Set number of IPs and face IPs
numIPs = 4;
numFaceIPs = 3;
// Size matrices and vectors thereof accordingly
ipCoords.resize(4,numIPs);
ipFaceCoords.assign(4,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(3,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
double v1, v2, a1, a2;
v1 = 0.585410196624969;
v2 = 0.138196601125011;
a1 = 2.0/3.0;
a2 = 1.0/6.0;
if (quad == NODAL) {
// Nodal quadrature
v1 = 1;
v2 = 0;
a1 = 1;
a2 = 0;
}
// Integration point coordinates
ipCoords.resize(4, numIPs);
ipCoords(0,0) = v2; ipCoords(0,2) = v2;
ipCoords(1,0) = v2; ipCoords(1,2) = v1;
ipCoords(2,0) = v2; ipCoords(2,2) = v2;
ipCoords(3,0) = v1; ipCoords(3,2) = v2;
//
ipCoords(0,1) = v1; ipCoords(0,3) = v2;
ipCoords(1,1) = v2; ipCoords(1,3) = v2;
ipCoords(2,1) = v2; ipCoords(2,3) = v1;
ipCoords(3,1) = v2; ipCoords(3,3) = v2;
// Integration points by face
// ...face 0 ...face 2
ipFaceCoords[0](0,0) = a1; ipFaceCoords[2](0,0) = a2;
ipFaceCoords[0](1,0) = a2; ipFaceCoords[2](1,0) = 0;
ipFaceCoords[0](2,0) = a2; ipFaceCoords[2](2,0) = a2;
//
ipFaceCoords[0](0,1) = a2; ipFaceCoords[2](0,1) = a1;
ipFaceCoords[0](1,1) = a1; ipFaceCoords[2](1,1) = 0;
ipFaceCoords[0](2,1) = a2; ipFaceCoords[2](2,1) = a2;
//
ipFaceCoords[0](0,2) = a2; ipFaceCoords[2](0,2) = a2;
ipFaceCoords[0](1,2) = a2; ipFaceCoords[2](1,2) = 0;
ipFaceCoords[0](2,2) = a1; ipFaceCoords[2](2,2) = a1;
// ...face 1 ...face 3
ipFaceCoords[1](0,0) = 0; ipFaceCoords[3](0,0) = a2;
ipFaceCoords[1](1,0) = a1; ipFaceCoords[3](1,0) = a2;
ipFaceCoords[1](2,0) = a2; ipFaceCoords[3](2,0) = 0;
//
ipFaceCoords[1](0,1) = 0; ipFaceCoords[3](0,1) = a2;
ipFaceCoords[1](1,1) = a2; ipFaceCoords[3](1,1) = a1;
ipFaceCoords[1](2,1) = a2; ipFaceCoords[3](2,1) = 0;
//
ipFaceCoords[1](0,2) = 0; ipFaceCoords[3](0,2) = a1;
ipFaceCoords[1](1,2) = a2; ipFaceCoords[3](1,2) = a2;
ipFaceCoords[1](2,2) = a1; ipFaceCoords[3](2,2) = 0;
// 2D integration points for faces
ipFace2DCoords(0,0) = a2; ipFace2DCoords(0,2) = a2;
ipFace2DCoords(1,0) = a2; ipFace2DCoords(1,2) = a1;
ipFace2DCoords(2,0) = a1; ipFace2DCoords(2,2) = a2;
//
ipFace2DCoords(0,1) = a1;
ipFace2DCoords(1,1) = a2;
ipFace2DCoords(2,1) = a2;
// Integration point weights
ipWeights = (1.0/6.0)/numIPs;
// Integration point face weights
ipFaceWeights = (1.0/2.0)/numFaceIPs;
break;
}
case GAUSS3: {
// Set number of IPs and face IPs
numIPs = 5;
numFaceIPs = 4;
// Size matrices and vectors thereof accordingly
ipCoords.resize(4,numIPs);
ipFaceCoords.assign(4,DENS_MAT(3,numFaceIPs));
ipFace2DCoords.resize(3,numFaceIPs);
ipWeights.reset(numIPs);
ipFaceWeights.reset(numFaceIPs);
double v1, v2, v3, a1, a2, a3;
/* These weights for calculating Gaussian Quadrature
* points are taken from the paper "Integration Points
* for Triangles and Tetrahedra Obtained from the
* Gaussian Quadrature Points for a Line" by K. S. Sunder
* and R. A. Cookson, Computers and Structures, Vol 21,
* No. 5, 1985. */
v1 = 1.0/4.0;
v2 = 1.0/2.0;
v3 = 1.0/6.0;
a1 = 1.0/3.0;
a2 = 3.0/5.0;
a3 = 1.0/5.0;
// Integration point coordinates
ipCoords.resize(4, numIPs);
ipCoords(0,0) = v1;
ipCoords(1,0) = v1;
ipCoords(2,0) = v1;
ipCoords(3,0) = v1;
//
ipCoords(0,1) = v3; ipCoords(0,3) = v3;
ipCoords(1,1) = v3; ipCoords(1,3) = v2;
ipCoords(2,1) = v3; ipCoords(2,3) = v3;
ipCoords(3,1) = v2; ipCoords(3,3) = v3;
//
ipCoords(0,2) = v2; ipCoords(0,4) = v3;
ipCoords(1,2) = v3; ipCoords(1,4) = v3;
ipCoords(2,2) = v3; ipCoords(2,4) = v2;
ipCoords(3,2) = v3; ipCoords(3,4) = v3;
// Integration points by face
// ...face 0 ...face 2
ipFaceCoords[0](0,0) = a1; ipFaceCoords[2](0,0) = a1;
ipFaceCoords[0](1,0) = a1; ipFaceCoords[2](1,0) = 0;
ipFaceCoords[0](2,0) = a1; ipFaceCoords[2](2,0) = a1;
//
ipFaceCoords[0](0,1) = a2; ipFaceCoords[2](0,1) = a3;
ipFaceCoords[0](1,1) = a3; ipFaceCoords[2](1,1) = 0;
ipFaceCoords[0](2,1) = a3; ipFaceCoords[2](2,1) = a3;
//
ipFaceCoords[0](0,2) = a3; ipFaceCoords[2](0,2) = a2;
ipFaceCoords[0](1,2) = a2; ipFaceCoords[2](1,2) = 0;
ipFaceCoords[0](2,2) = a3; ipFaceCoords[2](2,2) = a3;
//
ipFaceCoords[0](0,3) = a3; ipFaceCoords[2](0,3) = a3;
ipFaceCoords[0](1,3) = a3; ipFaceCoords[2](1,3) = 0;
ipFaceCoords[0](2,3) = a2; ipFaceCoords[2](2,3) = a2;
// ...face 1 ...face 3
ipFaceCoords[1](0,0) = 0; ipFaceCoords[3](0,0) = a1;
ipFaceCoords[1](1,0) = a1; ipFaceCoords[3](1,0) = a1;
ipFaceCoords[1](2,0) = a1; ipFaceCoords[3](2,0) = 0;
//
ipFaceCoords[1](0,1) = 0; ipFaceCoords[3](0,1) = a3;
ipFaceCoords[1](1,1) = a2; ipFaceCoords[3](1,1) = a3;
ipFaceCoords[1](2,1) = a3; ipFaceCoords[3](2,1) = 0;
//
ipFaceCoords[1](0,2) = 0; ipFaceCoords[3](0,2) = a3;
ipFaceCoords[1](1,2) = a3; ipFaceCoords[3](1,2) = a2;
ipFaceCoords[1](2,2) = a3; ipFaceCoords[3](2,2) = 0;
//
ipFaceCoords[1](0,3) = 0; ipFaceCoords[3](0,3) = a2;
ipFaceCoords[1](1,3) = a3; ipFaceCoords[3](1,3) = a3;
ipFaceCoords[1](2,3) = a2; ipFaceCoords[3](2,3) = 0;
// 2D integration points for faces
//
ipFace2DCoords(0,0) = a1; ipFace2DCoords(0,2) = a2;
ipFace2DCoords(1,0) = a1; ipFace2DCoords(1,2) = a3;
ipFace2DCoords(2,0) = a1; ipFace2DCoords(2,2) = a3;
//
ipFace2DCoords(0,1) = a3; ipFace2DCoords(0,3) = a3;
ipFace2DCoords(1,1) = a3; ipFace2DCoords(1,3) = a2;
ipFace2DCoords(2,1) = a2; ipFace2DCoords(2,3) = a3;
for (int i=0; i<numIPs; ++i) {
if (i < 1) ipWeights[i] = -4.0/5.0;
else ipWeights[i] = 9.0/20.0;
}
// Face integration point weights
for (int i=0; i<numFaceIPs; ++i) {
if (i < 1) ipFaceWeights[i] = -9.0/16.0;
else ipFaceWeights[i] = 25.0/48.0;
}
break;
}
// Error
default: {
throw ATC_Error("Unrecognized quadrature type "
"for element type TETRA.");
}
}
}
}
};
}; // namespace ATC
#endif // FE_QUADRATURE_H

View File

@ -0,0 +1,102 @@
#include "FieldEulerIntegrator.h"
#include "ATC_Coupling.h"
#include "FE_Engine.h"
#include "PhysicsModel.h"
#include "GMRES.h"
#include "CG.h"
#include "ImplicitSolveOperator.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_,
fields, fieldName_, rhsMask_, physicsModel_,
time, dt, alpha_);
DiagonalMatrix<double> preconditioner = solver.preconditioner(fields);
DENS_VEC myRhs = solver.rhs();
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);
if (convergence != 0) {
throw ATC_Error(field_to_string(fieldName_) + " evolution did not converge");
}
fields[fieldName_] += dT;
rhs[fieldName_] = myRhs;
}
} // namespace ATC

View File

@ -0,0 +1,138 @@
#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() {};
/** 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_;
};
} // namespace ATC
#endif

504
lib/atc/FieldManager.cpp Normal file
View File

@ -0,0 +1,504 @@
#include "FieldManager.h"
#include "ATC_Method.h"
#include "LammpsInterface.h"
#include "PerAtomQuantity.h"
#include "TransferOperator.h"
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, FieldName massMat, 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,massMat);
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, const DENS_MAT * reference, FieldName massMat, 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,massMat);
}
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, FieldName massMat, 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,massMat);
}
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,MASS_DENSITY,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,MASS_DENSITY,atc_->accumulant_inverse_volumes());
}
//-----------------------------------------------------------------------------
//* SPECIES_CONCENTRATION
//-----------------------------------------------------------------------------
DENS_MAN * FieldManager::species_concentration(string name)
{
DENS_MAN * c = NULL;
#ifdef ATC_VERBOSE
atc_->print_tracked();
#endif
PAQ * atomSpecies = atomic_species_vector();
if (atc_->kernel_on_the_fly()) {
if (atc_->kernel_based()) {
c = new OnTheFlyKernelAccumulationNormalized(atc_, atomSpecies,
atc_->kernel_function(),
atc_->atom_coarsegraining_positions(),
atc_->accumulant_inverse_volumes());
} else {
c = new OnTheFlyMeshAccumulationNormalized(atc_, atomSpecies,
atc_->atom_coarsegraining_positions(),
atc_->accumulant_inverse_volumes());
}
} else {
c = new AtfProjection(atc_, atomSpecies,
atc_->accumulant(),
atc_->accumulant_inverse_volumes());
}
if (name == "default") { name = field_to_string(SPECIES_CONCENTRATION); }
interscaleManager_.add_dense_matrix(c,name);
return c;
}
//-----------------------------------------------------------------------------
//* 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,MASS_DENSITY,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,MASS_DENSITY,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,MASS_DENSITY,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)
{
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);
}
return projected_atom_quantity(REFERENCE_POTENTIAL_ENERGY,"NodalAtomicReferencePotential",atomic,MASS_DENSITY,atc_->accumulant_inverse_volumes());
}
//-----------------------------------------------------------------------------
//* POTENTIAL_ENERGY
//-----------------------------------------------------------------------------
DENS_MAN * FieldManager::potential_energy(string name)
{
PerAtomQuantity<double> * atomic = interscaleManager_.per_atom_quantity("AtomicPotentialEnergy");
const DENS_MAT * reference = atc_->nodal_ref_potential_energy();
return referenced_projected_atom_quantity(POTENTIAL_ENERGY,name,atomic,reference,MASS_DENSITY,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,TEMPERATURE,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,MASS_DENSITY,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,MASS_DENSITY,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,MASS_DENSITY,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,MASS_DENSITY,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,MASS_DENSITY,atc_->accumulant_inverse_volumes());
}
//=============================================================================
//* 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;
}
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;
}
}

124
lib/atc/FieldManager.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef FIELD_MANAGER_H
#define FIELD_MANAGER_H
#include "ATC_TypeDefs.h"
#include "ATC_Error.h"
#include "PerAtomQuantity.h"
namespace ATC {
class ATC_Method;
enum CanonicalName {ATOMIC_TWICE_FLUCTUATING_KINETIC_ENERGY,
ATOMIC_TWICE_KINETIC_ENERGY,
ATOMIC_FLUCTUATING_VELOCITY,
ATOMIC_CHARGE_VELOCITY,
ATOMIC_SPECIES_VELOCITY,
PROLONGED_VELOCITY};
typedef PerAtomQuantity<double> PAQ;
/**
* @class FieldManager
* @brief Manager for constructing fields from atomic data
*/
class FieldManager{
public:
FieldManager(ATC_Method * atc);
virtual ~FieldManager(void){};
DENS_MAN * nodal_atomic_field(FieldName fieldName,
string name = "default") {
switch (fieldName) {
case CHARGE_DENSITY: return charge_density(name);
case MASS_DENSITY: return mass_density(name);
case SPECIES_CONCENTRATION: return species_concentration(name);
case NUMBER_DENSITY: return number_density(name);
case MOMENTUM: return momentum(name);
case VELOCITY: return velocity(name);
case PROJECTED_VELOCITY: return projected_velocity(name);
case DISPLACEMENT: return displacement(name);
case REFERENCE_POTENTIAL_ENERGY: return reference_potential_energy(name);
case POTENTIAL_ENERGY: return potential_energy(name);
case THERMAL_ENERGY: return thermal_energy(name);
case KINETIC_ENERGY: return kinetic_energy(name);
case TEMPERATURE: return temperature(name);
case KINETIC_TEMPERATURE: return kinetic_temperature(name);
case CHARGE_FLUX: return charge_flux(name);
case SPECIES_FLUX: return species_flux(name);
default: throw ATC_Error("FieldManager:: unknown field"); return NULL;
}
}
CanonicalName string_to_canonical_name(string name){
if (name == "AtomicTwiceFluctuatingKineticEnergy")
return ATOMIC_TWICE_FLUCTUATING_KINETIC_ENERGY;
else if (name == "AtomicTwiceKineticEnergy")
return ATOMIC_TWICE_KINETIC_ENERGY;
else if (name == "AtomicTwiceKineticEnergy")
return ATOMIC_TWICE_KINETIC_ENERGY;
else if (name == "AtomicFluctuatingVelocity")
return ATOMIC_FLUCTUATING_VELOCITY;
else if (name == "AtomicChargeVelocity") // ionic current
return ATOMIC_CHARGE_VELOCITY;
else if (name == "AtomicSpeciesVelocity") // per species momentum
return ATOMIC_SPECIES_VELOCITY;
else if (name == field_to_prolongation_name(VELOCITY))
return PROLONGED_VELOCITY;
else
throw ATC_Error("unknown canonical name "+name);
}
PAQ * per_atom_quantity(string name) {
switch (string_to_canonical_name(name)) {
case ATOMIC_TWICE_FLUCTUATING_KINETIC_ENERGY:
return atomic_twice_fluctuating_kinetic_energy();
case ATOMIC_TWICE_KINETIC_ENERGY:
return atomic_twice_kinetic_energy();
case ATOMIC_FLUCTUATING_VELOCITY:
return atomic_fluctuating_velocity();
case ATOMIC_CHARGE_VELOCITY:
return atomic_charge_velocity();
case ATOMIC_SPECIES_VELOCITY:
return atomic_species_velocity();
case PROLONGED_VELOCITY:
return prolonged_field(VELOCITY);
default:
throw ATC_Error("FieldManager:: unknown PAQ"); return NULL;
}
}
DENS_MAN * restricted_atom_quantity(FieldName field, string name = "default", PAQ * atomi = NULL);
protected:
ATC_Method * atc_;
InterscaleManager & interscaleManager_;
// nodal atomic fields
DENS_MAN * charge_density(string name);
DENS_MAN * mass_density(string name);
DENS_MAN * species_concentration(string name);
DENS_MAN * number_density(string name);
DENS_MAN * momentum(string name);
DENS_MAN * velocity(string name);
DENS_MAN * projected_velocity(string name);
DENS_MAN * displacement(string name);
DENS_MAN * reference_potential_energy(string name);
DENS_MAN * potential_energy(string name);
DENS_MAN * thermal_energy(string name);
DENS_MAN * kinetic_energy(string name);
DENS_MAN * temperature(string name);
DENS_MAN * kinetic_temperature(string name);
DENS_MAN * charge_flux(string name);
DENS_MAN * species_flux(string name);
// non intrinsic per atom quantities (intrinsic are handled elsewhere)
PAQ * atomic_twice_kinetic_energy();
PAQ * atomic_twice_fluctuating_kinetic_energy();
PAQ * atomic_fluctuating_velocity();
PAQ * atomic_charge_velocity();
PAQ * atomic_species_velocity();
PAQ * atomic_species_vector();
// internal functions
DENS_MAN * projected_atom_quantity(FieldName field,string name, PAQ * atomic, FieldName massMat, DIAG_MAN * normalization = NULL);
DENS_MAN * scaled_projected_atom_quantity(FieldName field,string name, PAQ * atomic, double scale, FieldName massMat, DIAG_MAN * normalization = NULL);
DENS_MAN * referenced_projected_atom_quantity(FieldName field, string name, PAQ * atomic, const DENS_MAT * reference, FieldName massMat, DIAG_MAN * normalization = NULL);
DENS_MAN * inferred_atom_quantity(FieldName field, string name, PAQ * atomic, FieldName massMat){return NULL;};
PAQ * prolonged_field(FieldName field);
private:
FieldManager(void);
};
}
#endif

543
lib/atc/Function.cpp Normal file
View File

@ -0,0 +1,543 @@
#include "Function.h"
#include "ATC_Error.h"
#include "LammpsInterface.h"
#include <sstream>
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";
}
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;
}
}

432
lib/atc/Function.h Normal file
View File

@ -0,0 +1,432 @@
#ifndef XT_FUNCTION_H
#define XT_FUNCTION_H
#include <math.h>
#include <string>
#include <set>
#include <cstdlib>
#include <iostream>
#include "Array.h"
#include "ATC_TypeDefs.h"
using namespace std;
using ATC_matrix::Array;
namespace ATC {
/**
* @class Function
* @brief Base class for functions of fields, space and time
*/
class Function {
public:
Function(int nargs, char** args);
virtual ~Function(void) {};
/** name */
const string & tag() { return tag_;}
/** depdendencies */
virtual inline ARG_NAMES args(void) {ARG_NAMES names; return names;};
/** function value */
virtual inline double f(ARGS& args) {return 0.0;};
virtual inline void f(ARGS& args, DENS_MAT vals) {};
/** (1st) derivative of function wrt to a field */
virtual inline double dfd(FieldName field, ARGS& args ) {return 0.0;};
virtual inline void dfd(FieldName field, ARGS& args, DENS_MAT vals ) {};
// addl: d2fd2(field1, field2, args), linearization(), grad_args
protected:
/** tag : name of function */
string tag_;
};
/**
* @class Function_Mgr
* @brief Base class that constructs and returns UXT_Function objects based on requests
*/
class Function_Mgr {
public:
/** Static instance of this class */
static Function_Mgr * instance();
Function* function(char ** arg, int nargs);
Function* copy_function(Function* other);
protected:
Function_Mgr() {};
~Function_Mgr();
private:
static Function_Mgr * myInstance_;
/** set to store all generated objects for later deletion */
set<Function * > pointerSet_;
};
/**
* @class LinearFieldFunction
* @brief Class for functions returning values linear a given field
*/
class LinearFieldFunction : public Function {
public:
LinearFieldFunction(int nargs, char** args);
virtual ~LinearFieldFunction(void) {};
inline double f(double* u, double* x, double t) {return c1_*u[0]-c0_;}
inline double dfd(FieldName field, ARGS& args) {return c1_;}
private :
double c0_,c1_;
};
/**
* @class UXT_Function
* @brief Base class for functions of fields, space and time
*/
class UXT_Function {
public:
UXT_Function(int nargs, double* args);
virtual ~UXT_Function(void) {};
const string & tag() { return tag_;}
/** function value */
virtual inline double f(double * u, double* x, double t) {return 0.0;};
/** derivative of function wrt to field */
virtual inline double dfdu(double * u, double* x, double t) {return 0.0;};
protected:
/** tag : name of function */
string tag_;
};
/**
* @class UXT_Function_Mgr
* @brief Base class that constructs and returns UXT_Function objects based on requests
*/
class UXT_Function_Mgr {
public:
/** Static instance of this class */
static UXT_Function_Mgr * instance();
UXT_Function* function(string & type, int nargs, double * arg);
UXT_Function* function(char ** arg, int nargs);
UXT_Function* linear_function(double c0, double c1);
UXT_Function* copy_UXT_function(UXT_Function* other);
protected:
UXT_Function_Mgr() {};
~UXT_Function_Mgr();
private:
static UXT_Function_Mgr * myInstance_;
/** set to store all generated objects for later deletion */
set<UXT_Function * > pointerSet_;
};
/**
* @class ScalarLinearFunction
* @brief Class for functions returning values linear in space
*/
class ScalarLinearFunction : public UXT_Function {
public:
ScalarLinearFunction(int nargs, double* args);
virtual ~ScalarLinearFunction(void) {};
//inline double f(double* u, double* x, double t) {return c1_*(u[0]-c0_);}
inline double f(double* u, double* x, double t) {return c1_*u[0]+c0_;}
inline double dfdu(double* u, double* x, double t) {return c1_;}
private :
double c0_,c1_;
};
/**
* @class XT_Function
* @brief Base class for functions based on space and time variables
*/
class XT_Function {
public:
XT_Function(int nargs, double* args);
virtual ~XT_Function(void) {};
const string & tag() { return tag_;}
/** function value */
virtual inline double f(double* x, double t) {return 0.0;};
/** time derivative of function */
virtual inline double dfdt(double* x, double t) {return 0.0;};
/** 2nd time derivative of function */
virtual inline double ddfdt(double* x, double t) {return 0.0;};
/** 3rd time derivative of function */
virtual inline double dddfdt(double* x, double t) {return 0.0;};
protected:
/** mask : masks x,y,z dependence, x0 : origin */
double mask[3], x0[3];
/** tag : name of function */
string tag_;
};
/**
* @class XT_Function_Mgr
* @brief Base class that constructs and returns XT_Function objects based on requests
*/
class XT_Function_Mgr {
public:
/** Static instance of this class */
static XT_Function_Mgr * instance();
XT_Function* function(string & type, int nargs, double * arg);
XT_Function* function(char ** arg, int nargs);
XT_Function* constant_function(double c);
XT_Function* copy_XT_function(XT_Function* other);
protected:
XT_Function_Mgr() {};
~XT_Function_Mgr();
private:
static XT_Function_Mgr * myInstance_;
/** set to store all generated objects for later deletion */
set<XT_Function * > pointerSet_;
};
//------------------------------------------------------------------------
// derived classes
//------------------------------------------------------------------------
/**
* @class ConstantFunction
* @brief Class for functions returning constant values
*/
class ConstantFunction : public XT_Function {
public:
ConstantFunction(int nargs, double* args);
ConstantFunction(double arg);
virtual ~ConstantFunction(void) {};
inline double f(double* x, double t)
{return C0;};
private :
double C0;
};
/**
* @class LinearFunction
* @brief Class for functions returning values linear in space
*/
class LinearFunction : public XT_Function {
public:
LinearFunction(int nargs, double* args);
virtual ~LinearFunction(void) {};
double f(double* x, double t)
{return mask[0]*(x[0]-x0[0])+mask[1]*(x[1]-x0[1])+mask[2]*(x[2]-x0[2]) + C0;};
private :
double C0;
};
/**
* @class PiecewiseLinearFunction
* @brief Class for functions returning values piecewise linear in space
* along given direction
*/
class PiecewiseLinearFunction : public XT_Function {
public:
PiecewiseLinearFunction(int nargs, double* args);
virtual ~PiecewiseLinearFunction(void) {};
double f(double* x, double t) ;
private :
Array<double> xi;
Array<double> fi;
};
/**
* @class LinearTemporalRamp
* @brief Class for functions returning values linear in space and time
*/
class LinearTemporalRamp : public XT_Function {
public:
LinearTemporalRamp(int nargs, double* args);
~LinearTemporalRamp(void) {};
double f(double* x, double t);
double dfdt(double* x, double t);
protected :
double mask_slope[3];
double C0_initial, C0_slope;
};
/**
* @class QuadraticFunction
* @brief Class for functions returning values quadratic in space
*/
class QuadraticFunction : public XT_Function {
public:
QuadraticFunction(int nargs, double* args);
virtual ~QuadraticFunction(void) {};
inline double f(double* x, double t)
{return
C2[0]*(x[0]-x0[0])*(x[0]-x0[0])+
C2[1]*(x[1]-x0[1])*(x[1]-x0[1])+
C2[2]*(x[2]-x0[2])*(x[2]-x0[2])+
2.0*C2[3]*(x[0]-x0[0])*(x[1]-x0[1]) +
2.0*C2[4]*(x[0]-x0[0])*(x[2]-x0[2]) +
2.0*C2[5]*(x[1]-x0[1])*(x[2]-x0[2]) +
mask[0]*(x[0]-x0[0])+mask[1]*(x[1]-x0[1])+mask[2]*(x[2]-x0[2]) + C0;};
private :
double C0, C2[6]; // C2 1:xx 2:yy 3:zz 4:xy|yx 5:xz|zx 6:yz|zy
};
/**
* @class SineFunction
* @brief Class for functions returning values sinusoidally varying in space and time
*/
class SineFunction : public XT_Function {
public:
SineFunction(int nargs, double* args);
virtual ~SineFunction(void){};
inline double f(double* x, double t)
{return C*sin( mask[0]*(x[0]-x0[0])
+mask[1]*(x[1]-x0[1])
+mask[2]*(x[2]-x0[2]) - w*t) + C0;};
private :
double C, C0, w;
};
/**
* @class GaussianFunction
* @brief Class for functions returning values according to a Gaussian distribution in space
*/
class GaussianFunction : public XT_Function {
public:
GaussianFunction(int nargs, double* args);
virtual ~GaussianFunction(void){};
// 1/(2 pi \sigma)^(n/2) exp(-1/2 x.x/\sigma^2 ) for n = dimension
inline double f(double* x, double t)
{return C*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) + C0;};
protected:
double tau, C, C0;
};
/**
* @class GaussianTemporalRamp
* @brief Class for functions returning values according to a Gaussian distribution in space and linearly in time
*/
class GaussianTemporalRamp : public GaussianFunction {
public:
GaussianTemporalRamp(int nargs, double* args);
virtual ~GaussianTemporalRamp(void){};
double f(double* x, double t);
double dfdt(double* x, double t);
protected:
double tau_initial, tau_slope;
double C_initial, C_slope;
double C0_initial, C0_slope;
};
/**
* @class TemporalRamp
* @brief Class for functions returning values constant in space and varying linearly in time
*/
class TemporalRamp : public XT_Function {
public:
TemporalRamp(int nargs, double* args);
virtual ~TemporalRamp(void) {};
inline double f(double* x, double t)
{return f_initial + slope*t;};
inline double dfdt(double* x, double t)
{return slope;};
private :
double f_initial, slope;
};
/**
* @class RadialPower
* @brief Class for functions returning values based on distance from a fix point raised to a specified power
*/
class RadialPower : public XT_Function {
public:
RadialPower(int nargs, double* args);
virtual ~RadialPower(void) {};
inline double f(double* x, double t)
{
double dx = x[0]-x0[0]; double dy = x[1]-x0[1]; double dz = x[2]-x0[2];
double r = mask[0]*dx*dx+mask[1]*dy*dy+mask[2]*dz*dz; r = sqrt(r);
return C0*pow(r,n);
};
private :
double C0, n;
};
/**
* @class InterpolationFunction
* @brief Base class for interpolation functions
*/
class InterpolationFunction {
public:
InterpolationFunction(void) : npts_(0) {};
virtual ~InterpolationFunction(void) {};
/** populate data */
void initialize(int npts,fstream &fileId, double coef = 1.);
/** function value */
double f(const double t) const;
/** derivative of function */
double dfdt(const double t) const;
protected:
double coordinate(double x,
double & f0, double & fp0, double & f1, double & fp1, double & inv_dx) const;
int npts_;
Array<double> xs_;
Array<double> fs_;
Array<double> fps_;
};
}
#endif

View File

@ -0,0 +1,150 @@
#include "FundamentalAtomicQuantity.h"
namespace ATC {
//--------------------------------------------------------
//--------------------------------------------------------
// Class FundamentalAtomQuantity
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
FundamentalAtomQuantity::FundamentalAtomQuantity(ATC_Method * atc,
LammpsInterface::FundamentalAtomQuantity atomQuantity,
AtomType atomType) :
ShallowAtomQuantity<double>(atc,0,atomType),
atomQuantity_(atomQuantity),
unitsConversion_(lammpsInterface_->atom_quantity_conversion(atomQuantity_))
{
nCols_ = lammpsInterface_->atom_quantity_ndof(atomQuantity_);
}
//--------------------------------------------------------
// set_lammps_to_quantity
//--------------------------------------------------------
void FundamentalAtomQuantity::set_lammps_to_quantity() const
{
if (unitsConversion_==1.) { // is there a way to avoid equal testing a double?
PerAtomQuantity<double>::set_lammps_to_quantity();
}
else { // perform unit conversion
if (quantity_.nRows()>0) {
// full matrix copy
if (atomType_ == ALL || atomType_ == PROC_GHOST) {
if (nCols_==1) { // scalar
double * lammpsQuantity = lammps_scalar();
for (int i = 0; i < atc_.nlocal_total(); i++)
lammpsQuantity[i] = quantity_(i,0)/unitsConversion_;
}
else{ // vector
double ** lammpsQuantity = lammps_vector();
for (int i = 0; i < atc_.nlocal_total(); i++)
for (int j = 0; j < nCols_; j++)
lammpsQuantity[i][j] = quantity_(i,j)/unitsConversion_;
}
}
// mapped copy
else {
int atomIndex;
if (nCols_==1) { // scalar
double * lammpsQuantity = lammps_scalar();
for (int i = 0; i < quantity_.nRows(); i++) {
atomIndex = quantityToLammps_(i);
lammpsQuantity[atomIndex] = quantity_(i,0)/unitsConversion_;
}
}
else{ // vector
double ** lammpsQuantity = lammps_vector();
for (int i = 0; i < quantity_.nRows(); i++) {
atomIndex = quantityToLammps_(i);
for (int j = 0; j < nCols_; j++) {
lammpsQuantity[atomIndex][j] = quantity_(i,j)/unitsConversion_;
}
}
}
}
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class AtomMass
// Access-only operations when mass is
// defined per type.
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
AtomMass::AtomMass(ATC_Method * atc,AtomType atomType) :
FundamentalAtomQuantity(atc,LammpsInterface::ATOM_MASS,atomType)
{
// do nothing
}
//--------------------------------------------------------
// set_quantity_to_lammps
//--------------------------------------------------------
void AtomMass::set_quantity_to_lammps() const
{
const int * type = lammpsInterface_->atom_type();
const double * mass = lammpsInterface_->atom_mass();
if (atomType_ == ALL || atomType_ == PROC_GHOST) {
for (int i = 0; i < quantity_.nRows(); i++)
quantity_(i,0) = mass[type[i]];
}
else {
int atomIndex;
for (int i = 0; i < quantity_.nRows(); i++) {
atomIndex = quantityToLammps_(i);
quantity_(i,0) = mass[type[atomIndex]];
}
}
}
//--------------------------------------------------------
//--------------------------------------------------------
// Class ComputedAtomQuantity
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
ComputedAtomQuantity::ComputedAtomQuantity(ATC_Method * atc,
const string & tag,
double unitsConversion,
AtomType atomType) :
ShallowAtomQuantity<double>(atc,0,atomType),
computePointer_(NULL),
computeTag_(tag),
unitsConversion_(unitsConversion)
{
// register compute with lammps interface and provide pointer for syncing
computePointer_ = lammpsInterface_->compute_pointer(computeTag_.c_str());
nCols_ = lammpsInterface_->compute_ncols_peratom(computePointer_);
}
//--------------------------------------------------------
// force_reset
//--------------------------------------------------------
void ComputedAtomQuantity::force_reset()
{
// only reset if the compute needs it this timestep
if (lammpsInterface_->compute_matchstep(computePointer_,lammpsInterface_->ntimestep())) {
if (!isFixed_) {
lammpsInterface_->reset_invoked_flag(computePointer_);
}
ShallowAtomQuantity<double>::force_reset();
}
}
}

View File

@ -0,0 +1,255 @@
// A class for defining an alternate atomic mass, needed for templating
#ifndef FUNDAMENTAL_ATOM_QUANTITY_H
#define FUNDAMENTAL_ATOM_QUANTITY_H
// ATC_Method headers
#include "PerAtomQuantity.h"
using namespace std;
namespace ATC {
// forward declarations
class ATC_Method;
//--------------------------------------------------------
//--------------------------------------------------------
// Class FundamentalAtomQuantity
// A base class for defining objects that manage
// quantities defined at atoms based on lammps data
//--------------------------------------------------------
//--------------------------------------------------------
class FundamentalAtomQuantity : public ShallowAtomQuantity<double> {
public:
// constructor
FundamentalAtomQuantity(ATC_Method * atc,
LammpsInterface::FundamentalAtomQuantity atomQuantity,
AtomType atomType=INTERNAL);
// destructor
virtual ~FundamentalAtomQuantity() {};
/** specialized reset to account for quantities which lammps can change */
virtual void lammps_force_reset() {this->force_reset();};
protected:
/** enumerates the type of atom quantity being considered */
LammpsInterface::FundamentalAtomQuantity atomQuantity_;
/** converts from Lammps units to ATC units */
double unitsConversion_;
/** sets lammps data based on the quantity */
virtual void set_lammps_to_quantity() const;
/** sets the quantity based on a lammps pointer */
virtual void set_quantity_to_lammps() const
{ShallowAtomQuantity<double>::set_quantity_to_lammps(); if (unitsConversion_!=1.) quantity_ *= unitsConversion_;};
/** gets appropriate pointer for lammps data */
virtual double * lammps_scalar() const
{return lammpsInterface_->atom_scalar(atomQuantity_);};
/** gets appropriate pointer for lammps data */
virtual double ** lammps_vector() const
{return lammpsInterface_->atom_vector(atomQuantity_);};
private:
// do not define
FundamentalAtomQuantity();
};
//--------------------------------------------------------
//--------------------------------------------------------
// Class AtomMass
//--------------------------------------------------------
//--------------------------------------------------------
class AtomMass : public FundamentalAtomQuantity {
public:
// constructor
AtomMass(ATC_Method * atc,AtomType atomType = INTERNAL);
// destructor
virtual ~AtomMass() {};
/** sets the quantity to a given value */
virtual void operator=(const DENS_MAT & target)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** sets the quantity to a given constant value */
virtual void operator=(const double & target)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** adds the given data to the Lammps quantity */
virtual void operator+=(const DENS_MAT & addition)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** adds the scalar data to the Lammps quantity for AtC atoms */
virtual void operator+=(double addition)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** subtracts the given data from the Lammps quantity */
virtual void operator-=(const DENS_MAT & subtraction)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** subtracts the scalar data from the Lammps quantity for AtC atoms */
virtual void operator-=(double subtracts)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(const DENS_MAT & multiplier)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(double multiplier)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(const DENS_MAT & divisor)
{throw ATC_Error("Cannot modify type-based atom mass");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(double divisor)
{throw ATC_Error("Cannot modify type-based atom mass");};
protected:
/** sets lammps data based on the quantity */
virtual void set_lammps_to_quantity(){};
/** sets the quantity based on a lammps pointer */
virtual void set_quantity_to_lammps() const;
/** gets appropriate pointer for lammps data */
virtual double * lammps_scalar() const
{return NULL;};
/** gets appropriate pointer for lammps data */
virtual double ** lammps_vector() const
{return NULL;};
private:
// do not define
AtomMass();
};
//--------------------------------------------------------
//--------------------------------------------------------
// Class ComputedAtomQuantity
// A base class for defining objects that manage
// quantities defined at atoms by Lammps computes
// The compute associated with the tag must already
// be initialized.
//--------------------------------------------------------
//--------------------------------------------------------
class ComputedAtomQuantity : public ShallowAtomQuantity<double> {
public:
// constructor
ComputedAtomQuantity(ATC_Method * atc,
const string & tag,
double unitsConversion = 1.,
AtomType atomType=INTERNAL);
// destructor
virtual ~ComputedAtomQuantity() {};
/** resets compute, must be this way to accomodate atom sorting between runs */
virtual void post_exchange() {this->needReset_ = true;};
/** specialized reset to account for forcing lammps to perform the compute */
virtual void force_reset();
/** specialized reset to account for quantities which lammps can change */
virtual void lammps_force_reset() {this->force_reset();};
// remove operations that change the lammps data
/** returns a non-const version for manipulations and changes, resets dependent quantities */
virtual DENS_MAT & set_quantity()
{throw ATC_Error("ComputedAtomQuantity::set_quantity - Cannot modify computed per atom quantities"); return quantity_;};
/** sets the quantity to a given constant value */
virtual void operator=(const DENS_MAT & target)
{throw ATC_Error("ComputedAtomQuantity::operator= - Cannot modify computed per atom quantities");};
/** adds the given data to the Lammps quantity */
virtual void operator+=(const DENS_MAT & addition)
{throw ATC_Error("ComputedAtomQuantity::operator+= - Cannot modify computed per atom quantities");};
/** adds the scalar data to the Lammps quantity for AtC atoms */
virtual void operator+=(double addition)
{throw ATC_Error("ComputedAtomQuantity::operator+= - Cannot modify computed per atom quantities");};
/** subtracts the given data from the Lammps quantity */
virtual void operator-=(const DENS_MAT & subtraction)
{throw ATC_Error("ComputedAtomQuantity::operator-= - Cannot modify computed per atom quantities");};
/** subtracts the scalar data from the Lammps quantity for AtC atoms */
virtual void operator-=(double subtraction)
{throw ATC_Error("ComputedAtomQuantity::operator-= - Cannot modify computed per atom quantities");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(const DENS_MAT & multiplier)
{throw ATC_Error("ComputedAtomQuantity::operator*= - Cannot modify computed per atom quantities");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator*=(double multiplier)
{throw ATC_Error("ComputedAtomQuantity::operator*= - Cannot modify computed per atom quantities");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(const DENS_MAT & divisor)
{throw ATC_Error("ComputedAtomQuantity::operator/= - Cannot modify computed per atom quantities");};
/** multiples the Lammps quantity by the given data, input is indexed in AtC atom counts */
virtual void operator/=(double divisor)
{throw ATC_Error("ComputedAtomQuantity::operator/= - Cannot modify computed per atom quantities");};
protected:
/** pointer to Lammps compute, meant as rapid indexing only (do not use!) */
COMPUTE_POINTER computePointer_;
/** tag for Lammps compute */
string computeTag_;
/** units conversion from LAMMPS to ATC units */
double unitsConversion_;
/** sets the quantity based on a lammps pointer */
virtual void set_quantity_to_lammps() const
{ShallowAtomQuantity<double>::set_quantity_to_lammps(); if (unitsConversion_!=1.) quantity_ *= unitsConversion_;};
/** gets appropriate data for lammps pointer */
virtual double * lammps_scalar() const {return lammpsInterface_->compute_vector_peratom(computePointer_);};
/** gets appropriate data for lammps pointer */
virtual double ** lammps_vector() const {return lammpsInterface_->compute_array_peratom(computePointer_);};
// not needed if no MPI
/** sets lammps data based on the quantity */
virtual void set_lammps_to_quantity()
{throw ATC_Error("ComputedAtomQuantity::set_lammps_to_quantity - Cannot modify a compute's LAMMPS data");};
private:
// do not define
ComputedAtomQuantity();
};
}
#endif

155
lib/atc/GMRES.h Normal file
View File

@ -0,0 +1,155 @@
//*****************************************************************
// Iterative template routine -- GMRES
//
// GMRES solves the unsymmetric linear system Ax = b using the
// Generalized Minimum Residual method
//
// GMRES follows the algorithm described on p. 20 of the
// SIAM Templates book.
//
// The return value indicates convergence within max_iter (input)
// iterations (0), or no convergence within max_iter iterations (1).
//
// Upon successful return, output arguments have the following values:
//
// x -- approximate solution to Ax = b
// max_iter -- the number of iterations performed before the
// tolerance was reached
// tol -- the residual after the final iteration
//
//*****************************************************************
#include <math.h>
template<class Real>
void ApplyPlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn);
template<class Real>
void GeneratePlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn);
template < class Matrix, class Vector >
void
Update(Vector &x, int k, Matrix &h, Vector &s, Vector v[])
{
Vector y(s);
// Backsolve:
for (int i = k; i >= 0; i--) {
y(i) /= h(i,i);
for (int j = i - 1; j >= 0; j--)
y(j) -= h(j,i) * y(i);
}
for (int j = 0; j <= k; j++)
x += v[j] * y(j);
}
template < class Real >
Real
abs(Real x)
{
return (x > 0 ? x : -x);
}
template < class Operator, class Vector, class Preconditioner,
class Matrix, class Real >
int
GMRES(const Operator &A, Vector &x, const Vector &b,
const Preconditioner &M, Matrix &H, int &m, int &max_iter,
Real &tol)
{
Real resid;
int i, j = 1, k;
Vector s(m+1), cs(m+1), sn(m+1), w;
Vector p = inv(M)*b;
Real normb = p.norm();
Vector r = inv(M) * (b - A * x);
Real beta = r.norm();
if (normb == 0.0)
normb = 1;
if ((resid = r.norm() / normb) <= tol) {
tol = resid;
max_iter = 0;
return 0;
}
Vector *v = new Vector[m+1];
while (j <= max_iter) {
v[0] = r * (1.0 / beta); // ??? r / beta
s = 0.0;
s(0) = beta;
for (i = 0; i < m && j <= max_iter; i++, j++) {
w = inv(M) * (A * v[i]);
for (k = 0; k <= i; k++) {
H(k, i) = w.dot(v[k]);
w -= H(k, i) * v[k];
}
H(i+1, i) = w.norm();
v[i+1] = w * (1.0 / H(i+1, i)); // ??? w / H(i+1, i)
for (k = 0; k < i; k++)
ApplyPlaneRotation(H(k,i), H(k+1,i), cs(k), sn(k));
GeneratePlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
if ((resid = abs(s(i+1)) / normb) < tol) {
Update(x, i, H, s, v);
tol = resid;
max_iter = j;
delete [] v;
return 0;
}
}
Update(x, m - 1, H, s, v);
r = inv(M) * (b - A * x);
beta = r.norm();
if ((resid = beta / normb) < tol) {
tol = resid;
max_iter = j;
delete [] v;
return 0;
}
}
tol = resid;
delete [] v;
return 1;
}
template<class Real>
void GeneratePlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn)
{
if (dy == 0.0) {
cs = 1.0;
sn = 0.0;
} else if (abs(dy) > abs(dx)) {
Real temp = dx / dy;
sn = 1.0 / sqrt( 1.0 + temp*temp );
cs = temp * sn;
} else {
Real temp = dy / dx;
cs = 1.0 / sqrt( 1.0 + temp*temp );
sn = temp * cs;
}
}
template<class Real>
void ApplyPlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn)
{
Real temp = cs * dx + sn * dy;
dy = -sn * dx + cs * dy;
dx = temp;
}

View File

@ -0,0 +1,160 @@
// 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"
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),
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);
}
// --------------------------------------------------------------------
// operator *(Vector)
// --------------------------------------------------------------------
DENS_VEC
FieldImplicitSolveOperator::operator * (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:
//
// dT/dt = R(T)
//
// A generalized discretization can be written:
//
// 1/dt * (T^n+1 - T^n) = alpha * R(T^n+1) + (1-alpha) * R(T^n)
//
// Taylor expanding the R(T^n+1) term and rearranging gives the
// equation to be solved for dT at each timestep:
//
// [1 - dt * alpha * dR/dT] * dT = dt * R(T^n)
//
// The operator defined in this method computes the left-hand side,
// given a vector dT. It uses a finite difference, matrix-free
// approximation of dR/dT * dT, giving:
//
// [1 - dt * alpha * dR/dT] * dT = dt * R(T^n)
// ~= dT - dt*alpha/epsilon * ( R(T^n + epsilon*dT) - R(T^n) )
//
// Compute epsilon
double epsilon = (x.norm() > 0.0) ? epsilon0_ * TnVect_.norm()/x.norm() : epsilon0_;
// Compute incremented vector = T + epsilon*dT
fieldsNp1_[fieldName_] = TnVect_ + epsilon * x;
// Evaluate R(b)
atc_->compute_rhs_vector(rhsMask_, fieldsNp1_, RnpMap_,
FULL_DOMAIN, physicsModel_);
DENS_MAT & Rnp = RnpMap_[fieldName_].set_quantity();
atc_->prescribed_data_manager()
->set_fixed_dfield(time_, fieldName_, Rnp);
atc_->apply_inverse_mass_matrix(Rnp,fieldName_);
RnpVect_ = column(Rnp,0);
// Compute full left hand side and return it
DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (RnpVect_ - RnVect_);
return Ax;
}
// --------------------------------------------------------------------
// rhs
// --------------------------------------------------------------------
DENS_VEC
FieldImplicitSolveOperator::rhs()
{
// Return dt * R(T^n)
return dt_ * RnVect_;
}
// --------------------------------------------------------------------
// preconditioner
// --------------------------------------------------------------------
DIAG_MAT
FieldImplicitSolveOperator::preconditioner(FIELDS & fields)
{
// Just create and return identity matrix
int nNodes = atc_->num_nodes();
DENS_VEC ones(nNodes);
ones = 1.0;
DIAG_MAT identity(ones);
return identity;
}
} // namespace ATC

View File

@ -0,0 +1,129 @@
#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);
/** Destructor */
virtual ~ImplicitSolveOperator() {};
/** pure virtual operator to compute Ax, for equation Ax=b */
virtual DENS_VEC operator * (DENS_VEC x) const = 0;
/** pure virtual method to return the rhs vector b */
virtual DENS_VEC rhs() = 0;
/** pure virtual method to return preconditioner */
virtual DiagonalMatrix<double> preconditioner(FIELDS & fields) = 0;
protected:
/** Pointer to atc */
ATC_Coupling * atc_;
/** Pointer to FE_Engine */
/*const*/ FE_Engine * feEngine_;
/** Pointer to PhysicsModel */
const PhysicsModel * physicsModel_;
};
/**
* @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 Array2D< bool > & rhsMask,
const PhysicsModel * physicsModel,
double simTime,
double dt,
double alpha);
/** 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);
protected:
// field name of ODE to solve
FieldName fieldName_;
// Reference to current fields (passed in constructor)
FIELDS & fields_;
// 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_;
Array2D<bool> rhsMask_;
Array<FieldName> massMask_;
// 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

View File

@ -0,0 +1,653 @@
// ATC transfer headers
#include "InterscaleOperators.h"
#include "PerAtomQuantity.h"
#include "TransferOperator.h"
#include "MoleculeSet.h"
#include "ATC_Method.h"
//#include <typeinfo>
namespace ATC{
//--------------------------------------------------------
//--------------------------------------------------------
// Class InterscaleManager
//--------------------------------------------------------
//--------------------------------------------------------
//--------------------------------------------------------
// Constructor
//--------------------------------------------------------
InterscaleManager::InterscaleManager(ATC_Method * atc) :
atc_(atc),
initialized_(false),
prefix_(":fix_atc:")
{
fundamentalAtomQuantities_.resize(NUM_ATOM_TYPES);
for (unsigned int i = 0; i < NUM_ATOM_TYPES; i++) {
fundamentalAtomQuantities_[i].resize(LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES);
for (unsigned int j = 0; j < LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES; j++)
fundamentalAtomQuantities_[i][j] = NULL;
}
}
//----------------------------------------------------
// Set_lammps_data_prefix
//--------------------------------------------------------
void InterscaleManager::set_lammps_data_prefix()
{
prefix_ = (atc_->lammps_interface())->fix_id() + prefix_;
}
//--------------------------------------------------------
// Destructor
//--------------------------------------------------------
InterscaleManager::~InterscaleManager()
{
clear();
}
//--------------------------------------------------------
// clear
//--------------------------------------------------------
void InterscaleManager::clear()
{
// set all memory types to temporary
for (unsigned int i = 0; i < NUM_ATOM_TYPES; i++) {
for (unsigned int j = 0; j < LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES; j++) {
if (fundamentalAtomQuantities_[i][j]) {
fundamentalAtomQuantities_[i][j]->set_memory_type(TEMPORARY);
}
}
}
set_memory_temporary(perAtomQuantities_);
set_memory_temporary(perAtomIntQuantities_);
set_memory_temporary(perAtomDiagonalMatrices_);
set_memory_temporary(perAtomSparseMatrices_);
set_memory_temporary(pairMaps_);
set_memory_temporary(denseMatrices_);
set_memory_temporary(denseMatricesInt_);
set_memory_temporary(denseMatricesBool_);
set_memory_temporary(sparseMatrices_);
set_memory_temporary(diagonalMatrices_);
set_memory_temporary(vectorSparMat_);
set_memory_temporary(setInt_);
set_memory_temporary(smallMoleculeSets_);
// clean up maps and vectors
clear_temporary_data();
}
//--------------------------------------------------------
// clear
//--------------------------------------------------------
void InterscaleManager::clear_temporary_data()
{
deletionList_.clear();
int listSize = fundamentalAtomQuantities_.size()+perAtomQuantities_.size()+perAtomIntQuantities_.size()+perAtomDiagonalMatrices_.size()+perAtomSparseMatrices_.size();
listSize += pairMaps_.size()+denseMatrices_.size()+denseMatricesInt_.size()+denseMatricesBool_.size()+sparseMatrices_.size()+diagonalMatrices_.size()+vectorSparMat_.size()+setInt_.size()+smallMoleculeSets_.size();
deletionList_.reserve(listSize);
create_deletion_list();
for (unsigned int i = 0; i < deletionList_.size(); i++) {
if (deletionList_[i]) {
delete deletionList_[i];
}
}
}
//--------------------------------------------------------
// create_deletion_list
//--------------------------------------------------------
void InterscaleManager::create_deletion_list()
{
// set all quantities to unfound
dfs_prepare_loop(perAtomQuantities_);
dfs_prepare_loop(perAtomIntQuantities_);
dfs_prepare_loop(perAtomDiagonalMatrices_);
dfs_prepare_loop(perAtomSparseMatrices_);
dfs_prepare_loop(perAtomQuantities_);
dfs_prepare_loop(pairMaps_);
dfs_prepare_loop(denseMatrices_);
dfs_prepare_loop(denseMatricesInt_);
dfs_prepare_loop(denseMatricesBool_);
dfs_prepare_loop(sparseMatrices_);
dfs_prepare_loop(diagonalMatrices_);
dfs_prepare_loop(vectorSparMat_);
dfs_prepare_loop(setInt_);
dfs_prepare_loop(smallMoleculeSets_);
// perform dfs, special case for fundamental atom quantities
int index = 0;
for (unsigned int i = 0; i < NUM_ATOM_TYPES; i++) {
for (unsigned int j = 0; j < LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES; j++) {
if (fundamentalAtomQuantities_[i][j]) {
index = dfs_visit(fundamentalAtomQuantities_[i][j],index);
if ((fundamentalAtomQuantities_[i][j])->memory_type()==TEMPORARY) {
fundamentalAtomQuantities_[i][j] = NULL;
}
}
}
}
// dfs for everything else
dfs_visit_loop(perAtomQuantities_,index);
dfs_visit_loop(perAtomIntQuantities_,index);
dfs_visit_loop(perAtomDiagonalMatrices_,index);
dfs_visit_loop(perAtomSparseMatrices_,index);
dfs_visit_loop(pairMaps_,index);
dfs_visit_loop(denseMatrices_,index);
dfs_visit_loop(denseMatricesInt_,index);
dfs_visit_loop(denseMatricesBool_,index);
dfs_visit_loop(sparseMatrices_,index);
dfs_visit_loop(diagonalMatrices_,index);
dfs_visit_loop(vectorSparMat_,index);
dfs_visit_loop(setInt_,index);
dfs_visit_loop(smallMoleculeSets_,index);
}
//--------------------------------------------------------
// dfs_visit
//--------------------------------------------------------
int InterscaleManager::dfs_visit(DependencyManager * quantity, const int index)
{
int myIndex = index;
set<DependencyManager * >::iterator it;
bool isTemporary = (quantity->memory_type()==TEMPORARY);
for (it = (quantity->dependentQuantities_).begin(); it != (quantity->dependentQuantities_).end(); it++) {
// make sure that if quantity isn't persistent, none of it's depedencies are
if ((*it)->memory_type()==PERSISTENT && isTemporary) {
throw ATC_Error("InterscaleManager::dfs_visit - a persistent quantity has a temporary dependency");
}
if (!((*it)->dfsFound_)) {
myIndex = dfs_visit(*it,myIndex);
}
}
quantity->dfsFound_ = true;
if (isTemporary)
deletionList_.push_back(quantity);
return ++myIndex;
}
//--------------------------------------------------------
// initialize
//--------------------------------------------------------
void InterscaleManager::initialize()
{
// force all existing objects to reset
for (unsigned int i = 0; i < NUM_ATOM_TYPES; i++) {
for (unsigned int j = 0; j < LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES; j++) {
if (fundamentalAtomQuantities_[i][j]) {
fundamentalAtomQuantities_[i][j]->force_reset();
}
}
}
force_reset_loop(perAtomQuantities_);
force_reset_loop(perAtomIntQuantities_);
force_reset_loop(perAtomDiagonalMatrices_);
force_reset_loop(perAtomSparseMatrices_);
force_reset_loop(perAtomQuantities_);
force_reset_loop(pairMaps_);
force_reset_loop(denseMatrices_);
force_reset_loop(denseMatricesInt_);
force_reset_loop(denseMatricesBool_);
force_reset_loop(sparseMatrices_);
force_reset_loop(diagonalMatrices_);
force_reset_loop(vectorSparMat_);
force_reset_loop(setInt_);
force_reset_loop(smallMoleculeSets_);
}
// access methods for atomic quantities
//--------------------------------------------------------
// fundamental_atom_quantity
//--------------------------------------------------------
FundamentalAtomQuantity * InterscaleManager::fundamental_atom_quantity(LammpsInterface::FundamentalAtomQuantity id,
AtomType atomType)
{
if (!fundamentalAtomQuantities_[atomType][id]) { // create a new one if it doesn't exist
if (id == LammpsInterface::ATOM_MASS) {
double * mass = LammpsInterface::instance()->atom_mass();
if (mass)
fundamentalAtomQuantities_[atomType][id] = new AtomMass(atc_,atomType);
else
fundamentalAtomQuantities_[atomType][id] = new FundamentalAtomQuantity(atc_,id,atomType);
}
else
fundamentalAtomQuantities_[atomType][id] = new FundamentalAtomQuantity(atc_,id,atomType);
fundamentalAtomQuantities_[atomType][id]->set_memory_type(PERSISTENT);
}
return fundamentalAtomQuantities_[atomType][id];
}
//--------------------------------------------------------
// per_atom_quantity
//--------------------------------------------------------
PerAtomQuantity<double> * InterscaleManager::per_atom_quantity(const string & tag)
{
return return_quantity(perAtomQuantities_,tag);
}
//--------------------------------------------------------
// per_atom_int_quantity
//--------------------------------------------------------
PerAtomQuantity<int> * InterscaleManager::per_atom_int_quantity(const string & tag)
{
return return_quantity(perAtomIntQuantities_,tag);
}
//--------------------------------------------------------
// per_atom_diagonal_matrix
//--------------------------------------------------------
PerAtomDiagonalMatrix<double> * InterscaleManager::per_atom_diagonal_matrix(const string & tag)
{
return return_quantity(perAtomDiagonalMatrices_,tag);
}
//--------------------------------------------------------
// per_atom_sparse_matrix
//--------------------------------------------------------
PerAtomSparseMatrix<double> * InterscaleManager::per_atom_sparse_matrix(const string & tag)
{
return return_quantity(perAtomSparseMatrices_,tag);
}
//--------------------------------------------------------
// pair_map
//--------------------------------------------------------
PairMap * InterscaleManager::pair_map(const string & tag)
{
return return_quantity(pairMaps_,tag);
}
//--------------------------------------------------------
// add_per_atom_quantity
//--------------------------------------------------------
void InterscaleManager::add_per_atom_quantity(PerAtomQuantity<double> * atomQuantity,
const string & tag)
{
add_comm_quantity(perAtomQuantities_,commList_,atomQuantity,tag);
}
//--------------------------------------------------------
// add_per_atom_int_quantity
//--------------------------------------------------------
void InterscaleManager::add_per_atom_int_quantity(PerAtomQuantity<int> * atomQuantity,
const string & tag)
{
add_comm_quantity(perAtomIntQuantities_,commIntList_,atomQuantity,tag);
}
//--------------------------------------------------------
// add_per_atom_diagonal_matrix
//--------------------------------------------------------
void InterscaleManager::add_per_atom_diagonal_matrix(PerAtomDiagonalMatrix<double> * atomQuantity,
const string & tag)
{
add_comm_quantity(perAtomDiagonalMatrices_,commDmList_,atomQuantity,tag);
}
//--------------------------------------------------------
// add_per_atom_sparse_matrix
//--------------------------------------------------------
void InterscaleManager::add_per_atom_sparse_matrix(PerAtomSparseMatrix<double> * atomQuantity,
const string & tag)
{
add_comm_quantity(perAtomSparseMatrices_,commSmList_,atomQuantity,tag);
}
//--------------------------------------------------------
// add_pair_map
//--------------------------------------------------------
void InterscaleManager::add_pair_map(PairMap * pairMap,
const string & tag)
{
add_quantity(pairMaps_,pairMap,tag);
}
//--------------------------------------------------------
// dense_matrix
//--------------------------------------------------------
DENS_MAN * InterscaleManager::dense_matrix(const string & tag)
{
return return_quantity(denseMatrices_,tag);
}
//--------------------------------------------------------
// add_dense_matrix
//--------------------------------------------------------
void InterscaleManager::add_dense_matrix(DENS_MAN * denseMatrix,
const string & tag)
{
add_quantity(denseMatrices_,denseMatrix,tag);
}
//--------------------------------------------------------
// dense_matrix_int
//--------------------------------------------------------
MatrixDependencyManager<DenseMatrix, int> * InterscaleManager::dense_matrix_int(const string & tag)
{
return return_quantity(denseMatricesInt_,tag);
}
//--------------------------------------------------------
// add_dense_matrix_int
//--------------------------------------------------------
void InterscaleManager::add_dense_matrix_int(MatrixDependencyManager<DenseMatrix, int> * denseMatrix,
const string & tag)
{
add_quantity(denseMatricesInt_,denseMatrix,tag);
}
//--------------------------------------------------------
// dense_matrix_bool
//--------------------------------------------------------
MatrixDependencyManager<DenseMatrix, bool> * InterscaleManager::dense_matrix_bool(const string & tag)
{
return return_quantity(denseMatricesBool_,tag);
}
//--------------------------------------------------------
// add_dense_matrix_bool
//--------------------------------------------------------
void InterscaleManager::add_dense_matrix_bool(MatrixDependencyManager<DenseMatrix, bool> * denseMatrix,
const string & tag)
{
add_quantity(denseMatricesBool_,denseMatrix,tag);
}
//--------------------------------------------------------
// sparse_matrix
//--------------------------------------------------------
SPAR_MAN * InterscaleManager::sparse_matrix(const string & tag)
{
return return_quantity(sparseMatrices_,tag);
}
//--------------------------------------------------------
// add_sparse_matrix
//--------------------------------------------------------
void InterscaleManager::add_sparse_matrix(SPAR_MAN * sparseMatrix,
const string & tag)
{
add_quantity(sparseMatrices_,sparseMatrix,tag);
}
//--------------------------------------------------------
// diagonal_matrix
//--------------------------------------------------------
DIAG_MAN * InterscaleManager::diagonal_matrix(const string & tag)
{
return return_quantity(diagonalMatrices_,tag);
}
//--------------------------------------------------------
// add_sparse_matrix
//--------------------------------------------------------
void InterscaleManager::add_diagonal_matrix(DIAG_MAN * diagonalMatrix,
const string & tag)
{
add_quantity(diagonalMatrices_,diagonalMatrix,tag);
}
//--------------------------------------------------------
// vector_spar_mat
//--------------------------------------------------------
VectorDependencyManager<SPAR_MAT * > * InterscaleManager::vector_sparse_matrix(const string & tag)
{
return return_quantity(vectorSparMat_,tag);
}
//--------------------------------------------------------
// add_vector_spar_mat
//--------------------------------------------------------
void InterscaleManager::add_vector_sparse_matrix(VectorDependencyManager<SPAR_MAT * > * vectorSparMat,
const string & tag)
{
add_quantity(vectorSparMat_,vectorSparMat,tag);
}
//--------------------------------------------------------
// set_int
//--------------------------------------------------------
SetDependencyManager<int> * InterscaleManager::set_int(const string & tag)
{
return return_quantity(setInt_,tag);
}
//--------------------------------------------------------
// add_set_int
//--------------------------------------------------------
void InterscaleManager::add_set_int(SetDependencyManager<int> * setInt,
const string & tag)
{
add_quantity(setInt_,setInt,tag);
}
//--------------------------------------------------------
// molecule_set
//--------------------------------------------------------
SmallMoleculeSet * InterscaleManager::small_molecule_set(const string & tag)
{
return return_quantity(smallMoleculeSets_,tag);
}
//--------------------------------------------------------
// add_molecule_set
//--------------------------------------------------------
void InterscaleManager::add_small_molecule_set(SmallMoleculeSet * moleculeSet,
const string & tag)
{
add_quantity(smallMoleculeSets_,moleculeSet,tag);
moleculeSet->initialize();
}
//--------------------------------------------------------
// find_tag
//--------------------------------------------------------
DependencyManager * InterscaleManager::find(const string & tag)
{
// REFACTOR add check for duplicate entries
DependencyManager * quantity = NULL;
quantity = find_in_list(perAtomQuantities_,tag);
if (quantity) return quantity;
quantity = find_in_list(perAtomIntQuantities_,tag);
if (quantity) return quantity;
quantity = find_in_list(perAtomDiagonalMatrices_,tag);
if (quantity) return quantity;
quantity = find_in_list(perAtomSparseMatrices_,tag);
if (quantity) return quantity;
quantity = find_in_list(pairMaps_,tag);
if (quantity) return quantity;
quantity = find_in_list(denseMatrices_,tag);
if (quantity) return quantity;
quantity = find_in_list(denseMatricesInt_,tag);
if (quantity) return quantity;
quantity = find_in_list(denseMatricesBool_,tag);
if (quantity) return quantity;
quantity = find_in_list(sparseMatrices_,tag);
if (quantity) return quantity;
quantity = find_in_list(diagonalMatrices_,tag);
if (quantity) return quantity;
quantity = find_in_list(vectorSparMat_,tag);
if (quantity) return quantity;
quantity = find_in_list(setInt_,tag);
if (quantity) return quantity;
quantity = find_in_list(smallMoleculeSets_,tag);
if (quantity) return quantity;
return NULL;
}
//--------------------------------------------------------
// remove
//--------------------------------------------------------
void InterscaleManager::remove(const string & tag)
{
DependencyManager * toBeDeleted = find(tag);
if (toBeDeleted) {
toBeDeleted->set_memory_type(TEMPORARY);
}
}
//--------------------------------------------------------
// reset_nlocal
//--------------------------------------------------------
void InterscaleManager::reset_nlocal()
{
reset_nlocal_loop(perAtomSparseMatrices_); // this goes in between diagonal matrices and molecule sets above
}
//--------------------------------------------------------
// computes_force_reset
//--------------------------------------------------------
void InterscaleManager::lammps_force_reset()
{
for (unsigned int i = 0; i < NUM_ATOM_TYPES; i++) {
for (unsigned int j = 0; j < LammpsInterface::NUM_FUNDAMENTAL_ATOM_QUANTITIES; j++) {
if (fundamentalAtomQuantities_[i][j]) {
fundamentalAtomQuantities_[i][j]->lammps_force_reset();
}
}
}
lammps_reset_loop(perAtomQuantities_);
lammps_reset_loop(perAtomIntQuantities_);
lammps_reset_loop(perAtomDiagonalMatrices_);
lammps_reset_loop(perAtomSparseMatrices_);
}
//--------------------------------------------------------
// size_comm_quantities
//--------------------------------------------------------
void InterscaleManager::size_comm_quantities()
{
size_comm_loop(commList_);
size_comm_loop(commIntList_);
size_comm_loop(commDmList_);
size_comm_loop(commSmList_);
}
//--------------------------------------------------------
// prepare_exchange
//--------------------------------------------------------
void InterscaleManager::prepare_exchange()
{
prepare_exchange_loop(perAtomIntQuantities_);
prepare_exchange_loop(perAtomQuantities_);
prepare_exchange_loop(perAtomDiagonalMatrices_);
prepare_exchange_loop(perAtomSparseMatrices_);
}
//--------------------------------------------------------
// post_exchange
//--------------------------------------------------------
void InterscaleManager::post_exchange()
{
post_exchange_loop(perAtomIntQuantities_);
post_exchange_loop(perAtomQuantities_);
post_exchange_loop(perAtomDiagonalMatrices_);
post_exchange_loop(perAtomSparseMatrices_);
post_exchange_loop(smallMoleculeSets_);
}
//--------------------------------------------------------
// memory_usage
//--------------------------------------------------------
int InterscaleManager::memory_usage() const
{
int usage = 0;
memory_usage_loop(perAtomQuantities_,usage);
memory_usage_loop(perAtomIntQuantities_,usage);
memory_usage_loop(perAtomDiagonalMatrices_,usage);
memory_usage_loop(perAtomSparseMatrices_,usage);
return usage;
}
//--------------------------------------------------------
// pack_exchange
//--------------------------------------------------------
int InterscaleManager::pack_exchange(int i, double *buffer)
{
int index = 0;
pack_exchange_loop(perAtomQuantities_,index,i,&buffer[index]);
pack_exchange_loop(perAtomIntQuantities_,index,i,&buffer[index]);
pack_exchange_loop(perAtomDiagonalMatrices_,index,i,&buffer[index]);
pack_exchange_loop(perAtomSparseMatrices_,index,i,&buffer[index]);
return index;
}
//--------------------------------------------------------
// unpack_exchange
//--------------------------------------------------------
int InterscaleManager::unpack_exchange(int i, double *buffer)
{
int index = 0;
unpack_exchange_loop(perAtomQuantities_,index,i,&buffer[index]);
unpack_exchange_loop(perAtomIntQuantities_,index,i,&buffer[index]);
unpack_exchange_loop(perAtomDiagonalMatrices_,index,i,&buffer[index]);
unpack_exchange_loop(perAtomSparseMatrices_,index,i,&buffer[index]);
return index;
}
//--------------------------------------------------------
// pack_comm
//--------------------------------------------------------
int InterscaleManager::pack_comm(int index, double *buf,
int pbc_flag, int *pbc)
{
int size = 0;
//pack_comm_loop(commList_,size,index,buf,pbc_flag,pbc);
pack_comm_loop(commIntList_,size,index,buf,pbc_flag,pbc);
//pack_comm_loop(commDmList_,size,index,buf,pbc_flag,pbc);
//pack_comm_loop(commSmList_,size,index,buf,pbc_flag,pbc);
return size;
}
//--------------------------------------------------------
// unpack_comm
//--------------------------------------------------------
int InterscaleManager::unpack_comm(int index, double *buf)
{
int size = 0;
//unpack_comm_loop(commList_,size,index,buf);
unpack_comm_loop(commIntList_,size,index,buf);
//unpack_comm_loop(commDmList_,size,index,buf);
//unpack_comm_loop(commSmList_,size,index,buf);
return size;
}
//--------------------------------------------------------
// grow_array
//--------------------------------------------------------
void InterscaleManager::grow_arrays(int nmax)
{
grow_arrays_loop(perAtomQuantities_,nmax);
grow_arrays_loop(perAtomIntQuantities_,nmax);
grow_arrays_loop(perAtomDiagonalMatrices_,nmax);
grow_arrays_loop(perAtomSparseMatrices_,nmax);
}
//--------------------------------------------------------
// copy_array
//--------------------------------------------------------
void InterscaleManager::copy_arrays(int i, int j)
{
copy_arrays_loop(perAtomQuantities_,i,j);
copy_arrays_loop(perAtomIntQuantities_,i,j);
copy_arrays_loop(perAtomDiagonalMatrices_,i,j);
copy_arrays_loop(perAtomSparseMatrices_,i,j);
}
};

View File

@ -0,0 +1,458 @@
// An interscale operator class for sharing definitions of atomic quantities, e.g., temperature
// between different parts of the code
#ifndef INTERSCALE_MANAGER_H
#define INTERSCALE_MANAGER_H
// ATC_Transfer headers
#include "MatrixLibrary.h"
#include "ATC_TypeDefs.h"
#include "ATC_Error.h"
#include "LammpsInterface.h"
#include "PerAtomQuantity.h"
#include "PerPairQuantity.h"
#include "FundamentalAtomicQuantity.h"
#include <vector>
#include <map>
using namespace std;
namespace ATC {
// forward declarations
class ATC_Method;
class SmallMoleculeSet;
/**
* @class InterscaleManager
* @brief Handles definitions for atomistic quantities
*/
//--------------------------------------------------------
//--------------------------------------------------------
// Class InterscaleManager
//--------------------------------------------------------
//--------------------------------------------------------
class InterscaleManager {
public:
// constructor
InterscaleManager(ATC_Method * atc);
// destructor
~InterscaleManager();
/** delete all allocated data */
void clear();
/** delete non-persistent data */
void clear_temporary_data();
/** set lammps data prefix */
void set_lammps_data_prefix();
/** parser/modifier */
bool modify(int narg, char **arg);
/** pre time integration */
void initialize();
// access to per atom quantity objects
/** access to fundamental atomic quantities */
FundamentalAtomQuantity * fundamental_atom_quantity(LammpsInterface::FundamentalAtomQuantity id,
AtomType atomType = INTERNAL);
/** access to double per atom quantities */
PerAtomQuantity<double> * per_atom_quantity(const string & tag);
/** access to integer per atom quantities */
PerAtomQuantity<int> * per_atom_int_quantity(const string & tag);
/** access to double per atom diagonal matrices */
PerAtomDiagonalMatrix<double> * per_atom_diagonal_matrix(const string & tag);
/** access to double per atom sparse matrices */
PerAtomSparseMatrix<double> * per_atom_sparse_matrix(const string & tag);
/** access to pair maps */
PairMap * pair_map(const string & tag);
// addition of new atom quantities, note provider must allocate but the manager will clean-up
/** addition of a double atomic quantity */
void add_per_atom_quantity(PerAtomQuantity<double> * atomQuantity,
const string & tag);
/** addition of an integer atomic quantity */
void add_per_atom_int_quantity(PerAtomQuantity<int> * atomQuantity,
const string & tag);
/** addition of a double atomic diagonal matrix */
void add_per_atom_diagonal_matrix(PerAtomDiagonalMatrix<double> * atomQuantity,
const string & tag);
/** addition of a double atomic sparse matrix */
void add_per_atom_sparse_matrix(PerAtomSparseMatrix<double> * atomQuantity,
const string & tag);
/** addition of an pair map */
void add_pair_map(PairMap * pairMap, const string & tag);
/** access to dense matrices */
DENS_MAN * dense_matrix(const string & tag);
/** addition of dense matrices */
void add_dense_matrix(DENS_MAN * denseMatrix,
const string & tag);
/** access integer dense matrices */
MatrixDependencyManager<DenseMatrix, int> * dense_matrix_int(const string & tag);
/** addition of integer dense matrices */
void add_dense_matrix_int(MatrixDependencyManager<DenseMatrix, int> * denseMatrix,
const string & tag);
/** access boolean dense matrices */
MatrixDependencyManager<DenseMatrix, bool> * dense_matrix_bool(const string & tag);
/** addition of boolean dense matrices */
void add_dense_matrix_bool(MatrixDependencyManager<DenseMatrix, bool> * denseMatrix,
const string & tag);
/** access to sparse matrices */
SPAR_MAN * sparse_matrix(const string & tag);
/** addition of a sparse matrix */
void add_sparse_matrix(SPAR_MAN * sparseMatrix,
const string & tag);
/** access to diagonal matrices */
DIAG_MAN * diagonal_matrix(const string & tag);
/** addition of a diagonal matrix */
void add_diagonal_matrix(DIAG_MAN * diagonalMatrix,
const string & tag);
/** access to vectors of sparse matrices */
VectorDependencyManager<SPAR_MAT * > * vector_sparse_matrix(const string & tag);
/** addition of a vector of sparse matrices */
void add_vector_sparse_matrix(VectorDependencyManager<SPAR_MAT * > * sparseMatrix,
const string & tag);
/** access to sets of ints */
SetDependencyManager<int> * set_int(const string & tag);
/** addition of a set of ints */
void add_set_int(SetDependencyManager<int> * sparseMatrix,
const string & tag);
/** access to molecule sets */
SmallMoleculeSet * small_molecule_set(const string & tag);
/** addition of a transfer operator */
void add_small_molecule_set(SmallMoleculeSet * moleculeSet,
const string & tag);
/** addition of exchange list object */
void add_to_exchange_list(const string & tag);
/** searches through all lists to see if a tag is registered */
DependencyManager * find(const string & tag);
/** schedules a quantity for deletion, if it exists */
void remove(const string & tag);
/** size communicated quantities initially */
void size_comm_quantities();
/** resets nlocal count of managed atomic quantities which do not perform parallel exchange */
void reset_nlocal();
/** resets all lammps data, as needed, to account for times when lammps can change quantities */
void lammps_force_reset();
/** syncs lammps data to managed objects for parallel communication */
void prepare_exchange();
/** syncs managed objects to lammps data after parallel communication */
void post_exchange();
/** returns how much lammps memory is used in this function */
int memory_usage() const;
/** packs up data for parallel transfer, called from pack_exchange */
int pack_exchange(int i, double *buffer);
/** unpacks data after parallel transfer, called from unpack_exchange */
int unpack_exchange(int i, double *buffer);
/** packs up data for parallel transfer to ghost atoms on other processors */
int pack_comm(int index, double *buf,
int pbc_flag, int *pbc);
/** unpacks data after parallel transfer to ghost atoms on other processors */
int unpack_comm(int index, double *buf);
/** changes size of temperary lammps storage data if transfer is being used */
void grow_arrays(int nmax);
/** rearrange memory of temporary lammps storage data, called from copy_array */
void copy_arrays(int i, int j);
protected:
/** pointer to access ATC methods */
ATC_Method * atc_;
/** flag for if first initialization has happened */
bool initialized_;
/** containers for fundamental atom quantities, set on request */
vector<vector<FundamentalAtomQuantity* > > fundamentalAtomQuantities_;
/** container for per-atom quantities using dense matrices of doubles */
map<string, PerAtomQuantity<double> * > perAtomQuantities_;
/** container for integer atom quantities, set by AtC classes */
map<string, PerAtomQuantity<int> * > perAtomIntQuantities_;
/** container for per-atom quantities using diagonal matrices of doubles */
map<string, PerAtomDiagonalMatrix<double> * > perAtomDiagonalMatrices_;
/** container for per-atom quantities using sparse matrices of doubles */
map<string, PerAtomSparseMatrix<double> * > perAtomSparseMatrices_;
/** container for pair maps */
map<string, PairMap * > pairMaps_;
/** container for dense matrices */
map<string, DENS_MAN * > denseMatrices_;
/** container for dense matrices for integer quantities */
map<string, MatrixDependencyManager<DenseMatrix, int> * > denseMatricesInt_;
/** container for dense matrces for boolean quantities */
map<string, MatrixDependencyManager<DenseMatrix, bool> * > denseMatricesBool_;
/** container for sparse matrices */
map<string, SPAR_MAN * > sparseMatrices_;
/** container for diagonal matrices */
map<string, DIAG_MAN * > diagonalMatrices_;
/** container for vectors of vectors of sparse matrices */
map<string, VectorDependencyManager<SPAR_MAT * > * > vectorSparMat_;
/** container for sets of integer quantities */
map<string, SetDependencyManager<int> * > setInt_;
/** container for molecule sets */
map<string, SmallMoleculeSet * > smallMoleculeSets_;
/** container for atomic quantities which must be transfered when atoms cross processors */
set<PerAtomQuantity<double> *> exchangeList_;
/** container for atomic quantities which must be transfered to ghost atoms on other processors */
vector<PerAtomQuantity<double> *> commList_;
/** container for integer atomic quantities which must be transfered to ghost atoms on other processors */
vector<PerAtomQuantity<int> *> commIntList_;
/** container for atomic diagonal matrices which must be transfered to ghost atoms on other processors */
vector<PerAtomDiagonalMatrix<double> *> commDmList_;
/** container for atomic sparse matrices which must be transfered to ghost atoms on other processors */
vector<PerAtomSparseMatrix<double> *> commSmList_;
/** prefix for labeling associated lammps arrays */
string prefix_;
/** order of deletion list of managed quantities */
vector<DependencyManager * > deletionList_;
/** creates a reverse sorted depth-first search list for deleting managed quantities */
void create_deletion_list();
/** executes a depth-first search visit on a managed quantity */
int dfs_visit(DependencyManager * quantity, const int index);
/** helper function to access a data entry in a list */
template <typename data>
data * return_quantity(map<string,data * > & list, const string & tag)
{
typename map<string,data * >::iterator it = list.find(tag);
if (it==list.end()) return NULL;
return it->second;
};
/** helper function to add a data entry to a list */
template <typename data>
void add_quantity(map<string,data * > & list, data * quantity, const string & tag)
{
typename map<string,data * >::iterator it = list.find(tag);
if (it!=list.end())
throw ATC_Error("Tried to add another Quantity with tag "+tag+" in InterscaleManager::add_quantity");
typename std::template pair<string,data * > myPair(tag,quantity);
list.insert(myPair);
};
/** helper function to add a data entry to a list when it requires neighbor communication*/
template <typename data>
void add_comm_quantity(map<string,data * > & list, vector<data * > & commList, data * quantity, const string & tag)
{
add_quantity(list,quantity,tag);
// allocate data for parallel communication
quantity->grow_lammps_array(LammpsInterface::instance()->nmax(),prefix_+tag);
if (quantity->atom_type() == PROC_GHOST) {
commList.push_back(quantity);
}
};
/** helper function to fina a data entry in a list */
template <typename data>
data * find_in_list(map<string,data * > & list, const string & tag)
{
typename map<string,data * >::iterator it = list.find(tag);
if (it!=list.end()) return it->second;
return NULL;
};
/** helper function to force the reset of all data in a list */
template <typename data>
void force_reset_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->force_reset();
};
/** helper function to set the memory type to temporary of a list */
template <typename data>
void set_memory_temporary(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->set_memory_type(TEMPORARY);
};
/** helper function to perform intialization for dfs of a list */
template <typename data>
void dfs_prepare_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it) {
(it->second)->dfsFound_ = false;
}
};
/** helper function to start the dfs visit for list */
template <typename data>
void dfs_visit_loop(map<string,data * > & list,
int & index)
{
typename map<string,data* >::iterator it = list.begin();
while (it != list.end()) {
if (!((it->second)->dfsFound_)) index = dfs_visit(it->second,index);
if ((it->second)->memory_type()==TEMPORARY) list.erase(it++);
else ++it;
}
};
// PAQ helper functions
/** helper function to adjust local atom count for all data in a list before exchange, only valid with quantities that do that are aware of atom counts */
template <typename data>
void reset_nlocal_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it) {
(it->second)->reset_nlocal();
}
};
/** helper function to indicate lammps data is stale for all data in a list before exchange, only valid with PAQs */
template <typename data>
void lammps_reset_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->lammps_force_reset();
};
/** helper function to size all data in a list, only valid with comm lists */
template <typename data>
void size_comm_loop(vector<data * > & list)
{
for (typename vector<data* >::iterator it = list.begin(); it != list.end(); ++it)
(*it)->quantity();
};
/** helper function to pack all data in a list before exchange, only valid with quantities that do work before parallel communication */
template <typename data>
void prepare_exchange_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->prepare_exchange();
};
/** helper function to extract all data in a list after exchange, only valid with quantities that do work after parallel communication */
template <typename data>
void post_exchange_loop(map<string,data * > & list)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->post_exchange();
};
/** helper function to determine memory usage of all data in a list, only valid with PAQs */
template <typename data>
void memory_usage_loop(const map<string,data * > & list, int & usage) const
{
for (typename map<string,data* >::const_iterator it = list.begin(); it != list.end(); ++it)
usage += (it->second)->memory_usage();
};
/** helper function to pack arrays of all data before exchange in a list, only valid with PAQs */
template <typename data>
void pack_exchange_loop(map<string,data * > & list, int & index, int i, double *buffer)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
index += (it->second)->pack_exchange(i,&buffer[index]);
};
/** helper function to unpack arrays of all data after exchange in a list, only valid with PAQs */
template <typename data>
void unpack_exchange_loop(map<string,data * > & list, int & index, int i, double *buffer)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
index += (it->second)->unpack_exchange(i,&buffer[index]);
};
/** helper function to pack arrays of all data in a list, only valid with comm lists */
template <typename data>
void pack_comm_loop(vector<data * > & list, int & size, int index, double *buf,
int pbc_flag, int *pbc)
{
for (typename vector<data* >::iterator it = list.begin(); it != list.end(); ++it)
size += (*it)->pack_comm(index,&buf[size],pbc_flag,pbc);
};
/** helper function to unpack arrays of all data in a list, only valid with comm lists */
template <typename data>
void unpack_comm_loop(vector<data * > & list, int & size, int index, double *buf)
{
for (typename vector<data* >::iterator it = list.begin(); it != list.end(); ++it)
size += (*it)->unpack_comm(index,&buf[size]);
};
/** helper function to grow arrays of all data in a list, only valid with PAQs */
template <typename data>
void grow_arrays_loop(map<string,data * > & list, int nmax)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->grow_lammps_array(nmax,prefix_+it->first);
};
/** helper function to copy arrays of all data in a list, only valid with PAQs */
template <typename data>
void copy_arrays_loop(map<string,data * > & list, int i, int j)
{
for (typename map<string,data* >::iterator it = list.begin(); it != list.end(); ++it)
(it->second)->copy_lammps_array(i,j);
};
private:
InterscaleManager();
};
}
#endif

162
lib/atc/KD_Tree.cpp Normal file
View File

@ -0,0 +1,162 @@
#include "KD_Tree.h"
#include <assert.h>
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 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;
} 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;
} 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;
}

89
lib/atc/KD_Tree.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef KD_TREE_H
#define KD_TREE_H
#include "Array2D.h"
#include "MatrixDef.h"
#include "MatrixLibrary.h"
#include <math.h>
using namespace ATC_matrix;
class Node {
public:
Node() {
// Zero argument constructor just for default initialization.
}
Node(int node, double x, double y, double z)
: index_(node)
{
coords_[0] = x;
coords_[1] = y;
coords_[2] = z;
}
int index_;
double coords_[3];
double distanceSquared(Node query) {
return pow(coords_[0] - query.coords_[0], 2)
+ pow(coords_[1] - query.coords_[1], 2)
+ pow(coords_[2] - query.coords_[2], 2);
}
double distanceInDimension(Node query, int dimension) {
return pow(coords_[dimension] - query.coords_[dimension], 2);
}
bool lessThanInDimension(Node query, int dimension) {
if (dimension == 0) return Node::compareX(*this, query);
else if (dimension == 1) return Node::compareY(*this, query);
else if (dimension == 2) return Node::compareZ(*this, query);
else return false;
}
bool operator==(const Node &rhs) {
return ((*this).coords_[0] == rhs.coords_[0] &&
(*this).coords_[1] == rhs.coords_[1] &&
(*this).coords_[2] == rhs.coords_[2]);
}
static bool compareX(Node one, Node two) { return one.coords_[0] < two.coords_[0]; }
static bool compareY(Node one, Node two) { return one.coords_[1] < two.coords_[1]; }
static bool compareZ(Node one, Node two) { return one.coords_[2] < two.coords_[2]; }
};
typedef std::pair<int,vector<Node> > Elem;
class KD_Tree {
public:
static KD_Tree* create_KD_tree(const int nNodesPerElement, const int nNodes,
const DENS_MAT *points, const int nElems,
const Array2D<int> &conn);
KD_Tree(vector<Node> *points, vector<Elem> *elems,
int dimension=0);
~KD_Tree();
vector<int> find_nearest(DENS_VEC query) {
// Create a fake Node and find the nearest Node in the tree to it.
return find_nearest_elements(Node(-1, query(0), query(1), query(2)));
}
vector<int> find_nearest_elements(Node query, int dimension=0);
vector<vector<int> > getElemIDs(int depth);
private:
Node value_;
vector<Node> *sortedPts_;
vector<Elem> *candElems_;
KD_Tree *leftChild_;
KD_Tree *rightChild_;
};
#endif

726
lib/atc/KernelFunction.cpp Normal file
View File

@ -0,0 +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));
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);
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);
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_);
}
};

256
lib/atc/KernelFunction.h Normal file
View File

@ -0,0 +1,256 @@
/** KernelFunction: Hardy smoothing */
#ifndef KERNEL_FUNCTION_H
#define KERNEL_FUNCTION_H
#include <set>
#include "LammpsInterface.h"
#include "MatrixLibrary.h"
namespace ATC {
/**
* @class KernelFunctionMgr
* @brief Base class for managing kernels
*/
class KernelFunctionMgr {
public:
/** Static instance of this class */
static KernelFunctionMgr * instance();
class KernelFunction* function(char** arg, int nargs);
protected:
KernelFunctionMgr() {};
~KernelFunctionMgr();
private:
static KernelFunctionMgr * myInstance_;
set<KernelFunction*> pointerSet_;
};
/**
* @class KernelFunction
* @brief Base class for kernels for atom-continuum transfer
*/
class KernelFunction {
public:
// constructor
KernelFunction(int nparameters, double* parameters);
// destructor
virtual ~KernelFunction() {};
// function value
virtual double value(DENS_VEC& x_atom) const =0 ;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const =0 ;
// bond function value via quadrature
virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) const;
// localization-volume intercepts for bond calculation
virtual void bond_intercepts(DENS_VEC& xa,
DENS_VEC& xb, double &lam1, double &lam2) const;
virtual bool node_contributes(DENS_VEC node) const;
virtual bool in_support(DENS_VEC node) const;
double inv_vol(void) const { return invVol_; }
virtual double dimensionality_factor(void) const { return 1.; }
protected:
double invVol_; // normalization factor
double Rc_, invRc_; // cutoff radius
int nsd_ ; // number of dimensions
/** pointer to lammps interface class */
LammpsInterface * lammpsInterface_;
/** periodicity flags and lengths */
int periodicity[3];
double box_bounds[2][3];
double box_length[3];
};
/**
* @class KernelFunctionStep
* @brief Class for defining kernel function of a step with spherical support
*/
class KernelFunctionStep : public KernelFunction {
public:
// constructor
KernelFunctionStep(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionStep() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
// bond function value
virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) const
{ return lam2-lam1; }
};
/**
* @class KernelFunctionCell
* @brief Class for defining kernel function of a step with rectangular support
* suitable for a rectangular grid
*/
class KernelFunctionCell : public KernelFunction {
public:
// constructor
KernelFunctionCell(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionCell() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
// bond function value
virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) const
{return lam2 -lam1;}
// bond intercept values : origin is the node position
void bond_intercepts(DENS_VEC& xa, DENS_VEC& xb,
double &lam1, double &lam2) const;
bool node_contributes(DENS_VEC node) const;
bool in_support(DENS_VEC dx) const;
protected:
double hx, hy, hz;
DENS_VEC cellBounds_;
};
/**
* @class KernelFunctionCubicSphere
* @brief Class for defining kernel function of a cubic with spherical support
*/
class KernelFunctionCubicSphere : public KernelFunction {
public:
// constructor
KernelFunctionCubicSphere(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionCubicSphere() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
};
/**
* @class KernelFunctionQuarticSphere
* @brief Class for defining kernel function of a quartic with spherical support
*/
class KernelFunctionQuarticSphere : public KernelFunction {
public:
// constructor
KernelFunctionQuarticSphere(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionQuarticSphere() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
};
/**
* @class KernelFunctionCubicCyl
* @brief Class for defining kernel function of a cubic with cylindrical support
*/
class KernelFunctionCubicCyl : public KernelFunction {
public:
// constructor
KernelFunctionCubicCyl(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionCubicCyl() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
virtual double dimensionality_factor(void) const { return 0.5; }
};
/**
* @class KernelFunctionQuarticCyl
* @brief Class for defining kernel function of a quartic with cylindrical support
*/
class KernelFunctionQuarticCyl : public KernelFunction {
public:
// constructor
KernelFunctionQuarticCyl(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionQuarticCyl() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
virtual double dimensionality_factor(void) const { return 0.5; }
};
/**
* @class KernelFunctionCubicBar
* @brief Class for defining kernel function of a cubic with 1-dimensional (bar) support
*/
class KernelFunctionCubicBar : public KernelFunction {
public:
// constructor
KernelFunctionCubicBar(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionCubicBar() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
virtual double dimensionality_factor(void) const { return 0.25; }
};
/**
* @class KernelFunctionCubicBar
* @brief Class for defining kernel function of a cubic with 1-dimensional (bar) support
*/
class KernelFunctionLinearBar : public KernelFunction {
public:
// constructor
KernelFunctionLinearBar(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionLinearBar() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
virtual double dimensionality_factor(void) const { return 0.25; }
};
/**
* @class KernelFunctionQuarticBar
* @brief Class for defining kernel function of a quartic with 1-dimensional (bar) support
*/
class KernelFunctionQuarticBar : public KernelFunction {
public:
// constructor
KernelFunctionQuarticBar(int nparameters, double* parameters);
// destructor
virtual ~KernelFunctionQuarticBar() {};
// function value
virtual double value(DENS_VEC& x_atom) const;
// function derivative
virtual void derivative(const DENS_VEC& x_atom, DENS_VEC& deriv) const;
virtual double dimensionality_factor(void) const { return 0.25; }
};
};
#endif

View File

@ -0,0 +1,442 @@
#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")
{
// 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;
int argIndex = 0;
if (strcmp(arg[argIndex],"thermal")==0) {
foundMatch = thermostat_.modify(narg,arg);
}
if (strcmp(arg[argIndex],"momentum")==0) {
foundMatch = kinetostat_.modify(narg,arg);
}
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 KinetoThermostat::reset_lambda_contribution(const DENS_MAT & target,
const FieldName field)
{
//TEMP_JAT
if (field==VELOCITY) {
kinetostat_.reset_lambda_contribution(target);
}
else if (field == TEMPERATURE) {
thermostat_.reset_lambda_contribution(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);
// }
// 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");
// }
// }
// AtomicRegulator::reset_method();
// }
// else {
// set_all_data_to_used();
// }
// #endif
//TEMP_JAT
kinetostat_.construct_methods();
thermostat_.construct_methods();
}
//TEMP_JAT all functions which have explicit thermo and kinetostats need to be removed
//--------------------------------------------------------
// reset_nlocal:
// resizes lambda force if necessary
//--------------------------------------------------------
void KinetoThermostat::reset_nlocal()
{
kinetostat_.reset_nlocal();
thermostat_.reset_nlocal();
}
//--------------------------------------------------------
// reset_atom_materials:
// resets the localized atom to material map
//--------------------------------------------------------
void KinetoThermostat::reset_atom_materials(const Array<int> & elementToMaterialMap,
const MatrixDependencyManager<DenseMatrix, int> * atomElement)
{
kinetostat_.reset_atom_materials(elementToMaterialMap,
atomElement);
thermostat_.reset_atom_materials(elementToMaterialMap,
atomElement);
}
//--------------------------------------------------------
// construct_transfers:
// pass through to appropriate transfer constuctors
//--------------------------------------------------------
void KinetoThermostat::construct_transfers()
{
kinetostat_.construct_transfers();
thermostat_.construct_transfers();
}
//--------------------------------------------------------
// initialize:
// sets up methods before a run
//--------------------------------------------------------
void KinetoThermostat::initialize()
{
kinetostat_.initialize();
thermostat_.initialize();
needReset_ = false;
}
//--------------------------------------------------------
// output:
// pass through to appropriate output methods
//--------------------------------------------------------
void KinetoThermostat::output(OUTPUT_LIST & outputData) const
{
kinetostat_.output(outputData);
thermostat_.output(outputData);
}
//--------------------------------------------------------
// finish:
// pass through to appropriate end-of-run methods
//--------------------------------------------------------
void KinetoThermostat::finish()
{
kinetostat_.finish();
thermostat_.finish();
}
//--------------------------------------------------
// pack_fields
// bundle all allocated field matrices into a list
// for output needs
//--------------------------------------------------
void KinetoThermostat::pack_fields(RESTART_LIST & data)
{
kinetostat_.pack_fields(data);
thermostat_.pack_fields(data);
}
//--------------------------------------------------------
// compute_boundary_flux:
// computes the boundary flux to be consistent with
// the controller
//--------------------------------------------------------
void KinetoThermostat::compute_boundary_flux(FIELDS & fields)
{
kinetostat_.compute_boundary_flux(fields);
thermostat_.compute_boundary_flux(fields);
}
//--------------------------------------------------------
// add_to_rhs:
// adds any controller contributions to the FE rhs
//--------------------------------------------------------
void KinetoThermostat::add_to_rhs(FIELDS & rhs)
{
thermostat_.add_to_rhs(rhs);
kinetostat_.add_to_rhs(rhs);
}
//--------------------------------------------------------
// apply_pre_predictor:
// applies the controller in the pre-predictor
// phase of the time integrator
//--------------------------------------------------------
void KinetoThermostat::apply_pre_predictor(double dt, int timeStep)
{
thermostat_.apply_pre_predictor(dt,timeStep);
kinetostat_.apply_pre_predictor(dt,timeStep);
}
//--------------------------------------------------------
// apply_mid_predictor:
// applies the controller in the mid-predictor
// phase of the time integrator
//--------------------------------------------------------
void KinetoThermostat::apply_mid_predictor(double dt, int timeStep)
{
thermostat_.apply_mid_predictor(dt,timeStep);
kinetostat_.apply_mid_predictor(dt,timeStep);
}
//--------------------------------------------------------
// apply_post_predictor:
// applies the controller in the post-predictor
// phase of the time integrator
//--------------------------------------------------------
void KinetoThermostat::apply_post_predictor(double dt, int timeStep)
{
thermostat_.apply_post_predictor(dt,timeStep);
kinetostat_.apply_post_predictor(dt,timeStep);
}
//--------------------------------------------------------
// apply_pre_corrector:
// applies the controller in the pre-corrector phase
// of the time integrator
//--------------------------------------------------------
void KinetoThermostat::apply_pre_corrector(double dt, int timeStep)
{
thermostat_.apply_pre_corrector(dt,timeStep);
kinetostat_.apply_pre_corrector(dt,timeStep);
}
//--------------------------------------------------------
// apply_post_corrector:
// applies the controller in the post-corrector phase
// of the time integrator
//--------------------------------------------------------
void KinetoThermostat::apply_post_corrector(double dt, int timeStep)
{
thermostat_.apply_post_corrector(dt,timeStep);
kinetostat_.apply_post_corrector(dt,timeStep);
}
//--------------------------------------------------------
// pre_exchange
//--------------------------------------------------------
void KinetoThermostat::pre_exchange()
{
thermostat_.pre_exchange();
kinetostat_.pre_exchange();
}
//--------------------------------------------------------
// pre_force
//--------------------------------------------------------
AtomicRegulator::RegulatorCouplingType KinetoThermostat::coupling_mode(const FieldName field) const
{
//TEMP_JAT
if (field==VELOCITY) {
return kinetostat_.coupling_mode();
}
else if (field == TEMPERATURE) {
return thermostat_.coupling_mode();
}
else {
throw ATC_Error("KinetoThermostat::coupling_mode - invalid field given");
}
}
};

689
lib/atc/KinetoThermostat.h Normal file
View File

@ -0,0 +1,689 @@
#ifndef KINETOTHERMOSTAT_H
#define KINETOTHERMOSTAT_H
// ATC headers
#include "AtomicRegulator.h"
#include "PerAtomQuantityLibrary.h"
//TEMP_JAT - transitional headers until we have a new method
#include "Kinetostat.h"
#include "Thermostat.h"
// other headers
#include <map>
#include <set>
namespace ATC {
/* #ifdef WIP_JAT */
/* static const int myLambdaMaxIterations = 50; */
/* #endif */
// 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 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();
/** 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();};
protected:
//TEMP_JAT - individual controllers for now
Kinetostat kinetostat_;
Thermostat thermostat_;
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 ThermostatShapeFunction : public RegulatorShapeFunction { */
/* public: */
/* ThermostatShapeFunction(Thermostat * thermostat, */
/* const 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: */
/* ThermostatRescale(Thermostat * 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); */
/* #ifdef WIP_JAT */
/* /\** compute boundary flux, requires thermostat input since it is part of the coupling scheme *\/ */
/* virtual void compute_boundary_flux(FIELDS & fields) */
/* {boundaryFlux_[TEMPERATURE] = 0.;}; */
/* #endif */
/* /\** get data for output *\/ */
/* virtual void output(OUTPUT_LIST & outputData); */
/* protected: */
/* /\** apply solution to atomic quantities *\/ */
/* void apply_to_atoms(PerAtomQuantity<double> * atomVelocities); */
/* /\** correct the RHS for complex temperature definitions *\/ */
/* virtual void correct_rhs(DENS_MAT & rhs) {}; // base class does no correction, assuming kinetic definition */
/* /\** FE temperature field *\/ */
/* DENS_MAN & nodalTemperature_; */
/* /\** construction for prolongation of lambda to atoms *\/ */
/* AtomicVelocityRescaleFactor * atomVelocityRescalings_; */
/* /\** workspace variables *\/ */
/* DENS_MAT _rhs_; */
/* private: */
/* // DO NOT define this */
/* ThermostatRescale(); */
/* }; */
/* /\** */
/* * @class ThermostatRescaleMixedKePe */
/* * @brief Enforces constraint on atomic kinetic energy based on FE temperature */
/* * when the temperature is a mix of the KE and PE */
/* *\/ */
/* class ThermostatRescaleMixedKePe : public ThermostatRescale { */
/* public: */
/* ThermostatRescaleMixedKePe(Thermostat * thermostat); */
/* virtual ~ThermostatRescaleMixedKePe() {}; */
/* /\** instantiate all needed data *\/ */
/* virtual void construct_transfers(); */
/* /\** pre-run initialization of method data *\/ */
/* virtual void initialize(); */
/* protected: */
/* /\** correct the RHS for inclusion of the PE *\/ */
/* virtual void correct_rhs(DENS_MAT & rhs); */
/* /\** nodal fluctuating potential energy *\/ */
/* DENS_MAN * nodalAtomicFluctuatingPotentialEnergy_; */
/* /\** fraction of temperature from KE *\/ */
/* double keMultiplier_; */
/* /\** fraction of temperature from PE *\/ */
/* double peMultiplier_; */
/* private: */
/* // DO NOT define this */
/* ThermostatRescaleMixedKePe(); */
/* }; */
/* /\** */
/* * @class ThermostatGlcFs */
/* * @brief Class for thermostat algorithms based on Gaussian least constraints (GLC) for fractional step (FS) algorithsm */
/* *\/ */
/* class ThermostatGlcFs : public ThermostatShapeFunction { */
/* public: */
/* ThermostatGlcFs(Thermostat * thermostat, */
/* const 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); */
/* /\** 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 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 */
/* /\** 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_; */
/* /\** 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 */
/* * @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 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 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 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 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

2242
lib/atc/Kinetostat.cpp Normal file

File diff suppressed because it is too large Load Diff

809
lib/atc/Kinetostat.h Normal file
View File

@ -0,0 +1,809 @@
#ifndef KINETOSTAT_H
#define KINETOSTAT_H
// ATC headers
#include "AtomicRegulator.h"
#include "PerAtomQuantityLibrary.h"
// other headers
#include <map>
#include <set>
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 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,
const 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_;
/** 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);
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 */
set<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);
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);
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);
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);
/** 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);
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);
virtual ~StressFlux();
/** instantiate all needed data */
virtual void construct_transfers();
/** applies kinetostat to atoms in the mid-predictor phase */
virtual void apply_mid_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);
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);
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,
const 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 post-corrector phase */
virtual void apply_post_corrector(double dt);
/** get data for output */
virtual void output(OUTPUT_LIST & outputData);
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_;
/** right-hand side data for thermostat equation */
DENS_MAT rhs_;
/** mapping from all to regulated nodes */
DENS_MAT rhsMap_;
// 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,
const 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,
const 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,
const 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 post-corrector phase */
virtual void apply_post_corrector(double dt);
/** 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_;
private:
// DO NOT define this
KinetostatFixed();
};
}
#endif

1511
lib/atc/LammpsInterface.cpp Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More