lammps/lib/atc/ATC_Transfer.cpp

2012 lines
74 KiB
C++

// ATC_Transfer headers
#include "ATC_Transfer.h"
#include "ATC_Error.h"
#include "FE_Engine.h"
#include "LammpsInterface.h"
#include "Quadrature.h"
#include "VoigtOperations.h"
#include "TransferLibrary.h"
#include "Stress.h"
#include "KernelFunction.h"
#include "PerPairQuantity.h"
#include "FieldManager.h"
#define ESHELBY_VIRIAL
#include "LinearSolver.h"
// Other Headers
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <fstream>
#include <sstream>
#include <exception>
// PLAN:
//* energies
//* filters - make filterFields class
//* output directly
//* enum, tagged, computes, mat(field to field) functions
//* grads & rates
//* on-the-fly
// * remove derived classes
using namespace std;
using namespace ATC_Utility;
using namespace voigt3;
namespace ATC {
const int numFields_ = 17;
FieldName indices_[numFields_] = {
CHARGE_DENSITY,
MASS_DENSITY,
SPECIES_CONCENTRATION,
NUMBER_DENSITY,
MOMENTUM,
VELOCITY,
PROJECTED_VELOCITY,
DISPLACEMENT,
POTENTIAL_ENERGY,
KINETIC_ENERGY,
KINETIC_TEMPERATURE,
TEMPERATURE,
CHARGE_FLUX,
SPECIES_FLUX,
THERMAL_ENERGY,
ENERGY,
INTERNAL_ENERGY
};
//KINETIC_STRESS;
//ELECTRIC_POTENTIAL};
ATC_Transfer::ATC_Transfer(string groupName,
double ** & perAtomArray,
LAMMPS_NS::Fix * thisFix,
string matParamFile)
: ATC_Method(groupName,perAtomArray,thisFix),
xPointer_(NULL),
outputStepZero_(true),
neighborReset_(false),
pairMap_(NULL),
bondMatrix_(NULL),
pairVirial_(NULL),
pairHeatFlux_(NULL),
nComputes_(0),
hasPairs_(true),
hasBonds_(false),
resetKernelFunction_(false),
dxaExactMode_(true),
cauchyBornStress_(NULL)
{
nTypes_ = lammpsInterface_->ntypes();
peScale_=1.;
keScale_= lammpsInterface_->mvv2e();
// if surrogate model of md (no physics model created)
if (matParamFile != "none") {
fstream fileId(matParamFile.c_str(), std::ios::in);
if (!fileId.is_open()) throw ATC_Error("cannot open material file");
CbData cb;
LammpsInterface *lmp = LammpsInterface::instance();
lmp->lattice(cb.cell_vectors, cb.basis_vectors);
cb.inv_atom_volume = 1.0 / lmp->volume_per_atom();
cb.e2mvv = 1.0 / lmp->mvv2e();
cb.atom_mass = lmp->atom_mass(1);
cb.boltzmann = lmp->boltz();
cb.hbar = lmp->hbar();
cauchyBornStress_ = new StressCauchyBorn(fileId, cb);
}
// Defaults
set_time();
outputFlags_.reset(NUM_TOTAL_FIELDS);
outputFlags_ = false;
fieldFlags_.reset(NUM_TOTAL_FIELDS);
fieldFlags_ = false;
gradFlags_.reset(NUM_TOTAL_FIELDS);
gradFlags_ = false;
rateFlags_.reset(NUM_TOTAL_FIELDS);
rateFlags_ = false;
outputFields_.resize(NUM_TOTAL_FIELDS);
for (int i = 0; i < NUM_TOTAL_FIELDS; i++) { outputFields_[i] = NULL; }
// Hardy requires ref positions for processor ghosts for bond list
//needXrefProcessorGhosts_ = true;
}
//-------------------------------------------------------------------
ATC_Transfer::~ATC_Transfer()
{
interscaleManager_.clear();
if (cauchyBornStress_) delete cauchyBornStress_;
}
//-------------------------------------------------------------------
// called before the beginning of a "run"
void ATC_Transfer::initialize()
{
if (kernelOnTheFly_ && !readRefPE_ && !setRefPEvalue_) {
if (setRefPE_) {
stringstream ss;
ss << "WARNING: Reference PE requested from atoms, but not yet implemented for on-the-fly, ignoring";
lammpsInterface_->print_msg_once(ss.str());
setRefPE_ = false;
}
}
ATC_Method::initialize();
if (!initialized_) {
if (cauchyBornStress_) cauchyBornStress_->initialize();
}
if (!initialized_ || ATC::LammpsInterface::instance()->atoms_sorted() || resetKernelFunction_) {
// initialize kernel function matrix N_Ia
if (! kernelOnTheFly_) {
try{
if (!moleculeIds_.empty()) compute_kernel_matrix_molecule(); //KKM add
}
catch(bad_alloc&) {
ATC::LammpsInterface::instance()->print_msg("kernel will be computed on-the-fly");
kernelOnTheFly_ = true;
}
}
resetKernelFunction_ = false;
}
// clears need for reset
ghostManager_.initialize();
// initialize bond matrix B_Iab
if ((! bondOnTheFly_)
&& ( ( fieldFlags_(STRESS)
|| fieldFlags_(ESHELBY_STRESS)
|| fieldFlags_(HEAT_FLUX) ) ) ) {
try {
compute_bond_matrix();
}
catch(bad_alloc&) {
ATC::LammpsInterface::instance()->print_msg("stress/heat_flux will be computed on-the-fly");
bondOnTheFly_ = true;
}
}
// set sample frequency to output if sample has not be specified
if (sampleFrequency_ == 0) sampleFrequency_ = outputFrequency_;
// output for step 0
if (!initialized_) {
if (outputFrequency_ > 0) {
// initialize filtered data
compute_fields();
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if(fieldFlags_(index)) {
string name = field_to_string((FieldName) index);
filteredData_[name] = hardyData_[name];
timeFilters_(index)->initialize(filteredData_[name].quantity());
}
if (rateFlags_(index)) {
string name = field_to_string((FieldName) index);
string rate_field = name + "_rate";
filteredData_[rate_field] = hardyData_[rate_field];
}
if (gradFlags_(index)) {
string name = field_to_string((FieldName) index);
string grad_field = name + "_gradient";
filteredData_[grad_field] = hardyData_[grad_field];
}
}
int index = NUM_TOTAL_FIELDS;
map <string,int>::const_iterator iter;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
filteredData_[tag] = hardyData_[tag];
timeFilters_(index)->initialize(filteredData_[tag].quantity());
#ifdef ESHELBY_VIRIAL
if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) {
filteredData_["eshelby_virial"] = hardyData_["eshelby_virial"];
}
#endif
index++;
}
output();
}
}
initialized_ = true;
lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+sampleFrequency_);
//remap_ghost_ref_positions();
update_peratom_output();
}
//-------------------------------------------------------------------
void ATC_Transfer::set_continuum_data()
{
ATC_Method::set_continuum_data();
if (!initialized_) {
nNodesGlobal_ = feEngine_->fe_mesh()->num_nodes();
}
}
//-------------------------------------------------------------------
void ATC_Transfer::construct_time_integration_data()
{
if (!initialized_) {
// size arrays for requested/required fields
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (fieldFlags_(index)) {
int size = FieldSizes[index];
if (atomToElementMapType_ == EULERIAN) {
if (index == STRESS) size=6;
if (index == CAUCHY_BORN_STRESS) size=6;
}
if (size == 0) {
if (index == SPECIES_CONCENTRATION) size=typeList_.size()+groupList_.size();
}
string name = field_to_string((FieldName) index);
hardyData_ [name].reset(nNodes_,size);
filteredData_[name].reset(nNodes_,size);
}
}
// size arrays for projected compute fields
map <string,int>::const_iterator iter;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
COMPUTE_POINTER cmpt = lammpsInterface_->compute_pointer(tag);
int ncols = lammpsInterface_->compute_ncols_peratom(cmpt);
hardyData_ [tag].reset(nNodes_,ncols);
filteredData_[tag].reset(nNodes_,ncols);
#ifdef ESHELBY_VIRIAL
if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) {
string esh = "eshelby_virial";
int size = FieldSizes[ESHELBY_STRESS];
hardyData_ [esh].reset(nNodes_,size);
filteredData_[esh].reset(nNodes_,size);
}
#endif
}
}
}
//--------------------------------------------------------
// set_computational_geometry
// constructs needed transfer operators which define
// hybrid atom/FE computational geometry
//--------------------------------------------------------
void ATC_Transfer::set_computational_geometry()
{
ATC_Method::set_computational_geometry();
}
//-------------------------------------------------------------------
// construct_interpolant
// constructs: interpolatn, accumulant, weights, and spatial derivatives
//--------------------------------------------------------
void ATC_Transfer::construct_interpolant()
{
// interpolant
if (!(kernelOnTheFly_)) {
// finite element shape functions for interpolants
PerAtomShapeFunction * atomShapeFunctions = new PerAtomShapeFunction(this);
interscaleManager_.add_per_atom_sparse_matrix(atomShapeFunctions,"Interpolant");
shpFcn_ = atomShapeFunctions;
}
// accummulant and weights
this->create_atom_volume();
// accumulants
if (kernelFunction_) {
// kernel-based accumulants
if (kernelOnTheFly_) {
ConstantQuantity<double> * atomCount = new ConstantQuantity<double>(this,1.);
interscaleManager_.add_per_atom_quantity(atomCount,"AtomCount");
OnTheFlyKernelAccumulation * myWeights
= new OnTheFlyKernelAccumulation(this,
atomCount, kernelFunction_, atomCoarseGrainingPositions_);
interscaleManager_.add_dense_matrix(myWeights,
"KernelInverseWeights");
accumulantWeights_ = new OnTheFlyKernelWeights(myWeights);
}
else {
PerAtomKernelFunction * atomKernelFunctions = new PerAtomKernelFunction(this);
interscaleManager_.add_per_atom_sparse_matrix(atomKernelFunctions,
"Accumulant");
accumulant_ = atomKernelFunctions;
accumulantWeights_ = new AccumulantWeights(accumulant_);
}
accumulantInverseVolumes_ = new KernelInverseVolumes(this,kernelFunction_);
interscaleManager_.add_diagonal_matrix(accumulantInverseVolumes_,
"AccumulantInverseVolumes");
interscaleManager_.add_diagonal_matrix(accumulantWeights_,
"AccumulantWeights");
}
else {
// mesh-based accumulants
if (kernelOnTheFly_) {
ConstantQuantity<double> * atomCount = new ConstantQuantity<double>(this,1.);
interscaleManager_.add_per_atom_quantity(atomCount,"AtomCount");
OnTheFlyMeshAccumulation * myWeights
= new OnTheFlyMeshAccumulation(this,
atomCount, atomCoarseGrainingPositions_);
interscaleManager_.add_dense_matrix(myWeights,
"KernelInverseWeights");
accumulantWeights_ = new OnTheFlyKernelWeights(myWeights);
} else {
accumulant_ = shpFcn_;
accumulantWeights_ = new AccumulantWeights(accumulant_);
interscaleManager_.add_diagonal_matrix(accumulantWeights_,
"AccumulantWeights");
}
}
// gradient matrix
if (gradFlags_.has_member(true)) {
NativeShapeFunctionGradient * gradientMatrix = new NativeShapeFunctionGradient(this);
interscaleManager_.add_vector_sparse_matrix(gradientMatrix,"GradientMatrix");
gradientMatrix_ = gradientMatrix;
}
}
//-------------------------------------------------------------------
void ATC_Transfer::construct_molecule_transfers()
{
// molecule centroid, molecule charge, dipole moment and quadrupole moment calculations KKM add
if (!moleculeIds_.empty()) {
map<string,pair<MolSize,int> >::const_iterator molecule;
InterscaleManager & interscaleManager = this->interscale_manager(); // KKM add, may be we do not need this as interscaleManager_ already exists.
PerAtomQuantity<double> * atomProcGhostCoarseGrainingPositions_ = interscaleManager.per_atom_quantity("AtomicProcGhostCoarseGrainingPositions");
FundamentalAtomQuantity * mass = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_MASS,PROC_GHOST);
molecule = moleculeIds_.begin();
int groupbit = (molecule->second).second;
smallMoleculeSet_ = new SmallMoleculeSet(this,groupbit);
smallMoleculeSet_->initialize(); // KKM add, why should we?
interscaleManager_.add_small_molecule_set(smallMoleculeSet_,"MoleculeSet");
moleculeCentroid_ = new SmallMoleculeCentroid(this,mass,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_);
interscaleManager_.add_dense_matrix(moleculeCentroid_,"MoleculeCentroid");
AtomToSmallMoleculeTransfer<double> * moleculeMass =
new AtomToSmallMoleculeTransfer<double>(this,mass,smallMoleculeSet_);
interscaleManager_.add_dense_matrix(moleculeMass,"MoleculeMass");
FundamentalAtomQuantity * atomicCharge = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE,PROC_GHOST);
AtomToSmallMoleculeTransfer<double> * moleculeCharge =
new AtomToSmallMoleculeTransfer<double>(this,atomicCharge,smallMoleculeSet_);
interscaleManager_.add_dense_matrix(moleculeCharge,"MoleculeCharge");
dipoleMoment_ = new SmallMoleculeDipoleMoment(this,atomicCharge,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_,moleculeCentroid_);
interscaleManager_.add_dense_matrix(dipoleMoment_,"DipoleMoment");
quadrupoleMoment_ = new SmallMoleculeQuadrupoleMoment(this,atomicCharge,smallMoleculeSet_,atomProcGhostCoarseGrainingPositions_,moleculeCentroid_);
interscaleManager_.add_dense_matrix(quadrupoleMoment_,"QuadrupoleMoment");
}
}
//----------------------------------------------------------------------
// constructs quantities
void ATC_Transfer::construct_transfers()
{
// set pointer to positions
// REFACTOR use method's handling of xref/xpointer
set_xPointer();
ATC_Method::construct_transfers();
// reference potential energy
if (setRefPE_) {
if (!setRefPEvalue_ && !readRefPE_) {
FieldManager fmgr(this);
nodalRefPotentialEnergy_ = fmgr.nodal_atomic_field(REFERENCE_POTENTIAL_ENERGY);
}
else {
nodalRefPotentialEnergy_ = new DENS_MAN(nNodes_,1);
nodalRefPotentialEnergy_->set_memory_type(PERSISTENT);
interscaleManager_.add_dense_matrix(nodalRefPotentialEnergy_,
field_to_string(REFERENCE_POTENTIAL_ENERGY));
}
}
// for hardy-based fluxes
bool needsBondMatrix = (! bondOnTheFly_ ) &&
(fieldFlags_(STRESS)
|| fieldFlags_(ESHELBY_STRESS)
|| fieldFlags_(HEAT_FLUX));
if (needsBondMatrix) {
if (hasPairs_ && hasBonds_) {
pairMap_ = new PairMapBoth(lammpsInterface_,groupbit_);
}
else if (hasBonds_) {
pairMap_ = new PairMapBond(lammpsInterface_,groupbit_);
}
else if (hasPairs_) {
pairMap_ = new PairMapNeighbor(lammpsInterface_,groupbit_);
}
}
if (pairMap_) interscaleManager_.add_pair_map(pairMap_,"PairMap");
if ( fieldFlags_(STRESS) || fieldFlags_(ESHELBY_STRESS) || fieldFlags_(HEAT_FLUX) ) {
const FE_Mesh * fe_mesh = feEngine_->fe_mesh();
if (!kernelBased_) {
bondMatrix_ = new BondMatrixPartitionOfUnity(lammpsInterface_,*pairMap_,xPointer_,fe_mesh,accumulantInverseVolumes_);
}
else {
bondMatrix_ = new BondMatrixKernel(lammpsInterface_,*pairMap_,xPointer_,fe_mesh,kernelFunction_);
}
}
if (bondMatrix_) interscaleManager_.add_sparse_matrix(bondMatrix_,"BondMatrix");
if ( fieldFlags_(STRESS) || fieldFlags_(ESHELBY_STRESS) ) {
if (atomToElementMapType_ == LAGRANGIAN) {
pairVirial_ = new PairVirialLagrangian(lammpsInterface_,*pairMap_,xref_);
}
else if (atomToElementMapType_ == EULERIAN) {
pairVirial_ = new PairVirialEulerian(lammpsInterface_,*pairMap_);
}
else {
throw ATC_Error("no atom to element map specified");
}
}
if (pairVirial_) interscaleManager_.add_dense_matrix(pairVirial_,"PairVirial");
if ( fieldFlags_(HEAT_FLUX) ) {
if (atomToElementMapType_ == LAGRANGIAN) {
pairHeatFlux_ = new PairPotentialHeatFluxLagrangian(lammpsInterface_,*pairMap_,xref_);
}
else if (atomToElementMapType_ == EULERIAN) {
pairHeatFlux_ = new PairPotentialHeatFluxEulerian(lammpsInterface_,*pairMap_);
}
else {
throw ATC_Error("no atom to element map specified");
}
}
if (pairHeatFlux_) interscaleManager_.add_dense_matrix(pairHeatFlux_,"PairHeatFlux");
FieldManager fmgr(this);
// for(int index=0; index < NUM_TOTAL_FIELDS; ++index)
for(int i=0; i < numFields_; ++i) {
FieldName index = indices_[i];
if (fieldFlags_(index)) {
outputFields_[index] = fmgr.nodal_atomic_field(index);
}
}
// WIP REJ - move to fmgr
if (fieldFlags_(ELECTRIC_POTENTIAL)) {
PerAtomQuantity<double> * atomCharge = interscaleManager_.fundamental_atom_quantity(LammpsInterface::ATOM_CHARGE);
restrictedCharge_ = fmgr.restricted_atom_quantity(CHARGE_DENSITY,"default",atomCharge);
}
// computes
map <string,int>::const_iterator iter;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
ComputedAtomQuantity * c = new ComputedAtomQuantity(this, tag);
interscaleManager_.add_per_atom_quantity(c,tag);
int projection = iter->second;
DIAG_MAN * w = NULL;
if (projection == VOLUME_NORMALIZATION )
{ w = accumulantInverseVolumes_; }
else if (projection == NUMBER_NORMALIZATION )
{ w = accumulantWeights_; }
if (kernelFunction_ && kernelOnTheFly_) {
OnTheFlyKernelAccumulationNormalized * C = new OnTheFlyKernelAccumulationNormalized(this, c, kernelFunction_, atomCoarseGrainingPositions_, w);
interscaleManager_.add_dense_matrix(C,tag);
outputFieldsTagged_[tag] = C;
}
else {
AtfProjection * C = new AtfProjection(this, c, accumulant_, w);
interscaleManager_.add_dense_matrix(C,tag);
outputFieldsTagged_[tag] = C;
}
}
}
//-------------------------------------------------------------------
// sets initial values of filtered quantities
void ATC_Transfer::construct_methods()
{
ATC_Method::construct_methods();
if ((!initialized_) || timeFilterManager_.need_reset()) {
timeFilters_.reset(NUM_TOTAL_FIELDS+nComputes_);
sampleCounter_ = 0;
// for filtered fields
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (fieldFlags_(index)) {
string name = field_to_string((FieldName) index);
filteredData_[name] = 0.0;
timeFilters_(index) = timeFilterManager_.construct();
}
}
// for filtered projected computes
// lists/accessing of fields ( & computes)
map <string,int>::const_iterator iter;
int index = NUM_TOTAL_FIELDS;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
filteredData_[tag] = 0.0;
timeFilters_(index) = timeFilterManager_.construct();
index++;
}
}
}
//-------------------------------------------------------------------
// called after the end of a "run"
void ATC_Transfer::finish()
{
// base class
ATC_Method::finish();
}
//-------------------------------------------------------------------
// this is the parser
bool ATC_Transfer::modify(int narg, char **arg)
{
bool match = false;
int argIdx = 0;
// check to see if it is a transfer class command
/*! \page man_hardy_fields fix_modify AtC fields
\section syntax
fix_modify AtC fields <all | none> \n
fix_modify AtC fields <add | delete> <list_of_fields> \n
- all | none (keyword) = output all or no fields \n
- add | delete (keyword) = add or delete the listed output fields \n
- fields (keyword) = \n
density : mass per unit volume \n
displacement : displacement vector \n
momentum : momentum per unit volume \n
velocity : defined by momentum divided by density \n
projected_velocity : simple kernel estimation of atomic velocities \n
temperature : temperature derived from the relative atomic kinetic energy (as done by ) \n
kinetic_temperature : temperature derived from the full kinetic energy \n
number_density : simple kernel estimation of number of atoms per unit volume \n
stress :
Cauchy stress tensor for eulerian analysis (atom_element_map), or
1st Piola-Kirchhoff stress tensor for lagrangian analysis \n
transformed_stress :
1st Piola-Kirchhoff stress tensor for eulerian analysis (atom_element_map), or
Cauchy stress tensor for lagrangian analysis \n
heat_flux : spatial heat flux vector for eulerian,
or referential heat flux vector for lagrangian \n
potential_energy : potential energy per unit volume \n
kinetic_energy : kinetic energy per unit volume \n
thermal_energy : thermal energy (kinetic energy - continuum kinetic energy) per unit volume \n
internal_energy : total internal energy (potential + thermal) per unit volume \n
energy : total energy (potential + kinetic) per unit volume \n
number_density : number of atoms per unit volume \n
eshelby_stress: configurational stress (energy-momentum) tensor defined by Eshelby
[References: Philos. Trans. Royal Soc. London A, Math. Phys. Sci., Vol. 244,
No. 877 (1951) pp. 87-112; J. Elasticity, Vol. 5, Nos. 3-4 (1975) pp. 321-335] \n
vacancy_concentration: volume fraction of vacancy content \n
type_concentration: volume fraction of a specific atom type \n
\section examples
<TT> fix_modify AtC fields add velocity temperature </TT>
\section description
Allows modification of the fields calculated and output by the
transfer class. The commands are cumulative, e.g.\n
<TT> fix_modify AtC fields none </TT> \n
followed by \n
<TT> fix_modify AtC fields add velocity temperature </TT> \n
will only output the velocity and temperature fields.
\section restrictions
Must be used with the hardy/field type of AtC fix, see \ref man_fix_atc.
Currently, the stress and heat flux formulas are only correct for
central force potentials, e.g. Lennard-Jones and EAM
but not Stillinger-Weber.
\section related
See \ref man_hardy_gradients , \ref man_hardy_rates and \ref man_hardy_computes
\section default
By default, no fields are output
*/
if (strcmp(arg[argIdx],"fields")==0) {
argIdx++;
if (strcmp(arg[argIdx],"all")==0) {
outputFlags_ = true;
match = true;
}
else if (strcmp(arg[argIdx],"none")==0) {
outputFlags_ = false;
match = true;
}
else if (strcmp(arg[argIdx],"add")==0) {
argIdx++;
for (int i = argIdx; i < narg; ++i) {
FieldName field_name = string_to_field(arg[i]);
outputFlags_(field_name) = true;
}
match = true;
}
else if (strcmp(arg[argIdx],"delete")==0) {
argIdx++;
for (int i = argIdx; i < narg; ++i) {
FieldName field_name = string_to_field(arg[i]);
outputFlags_(field_name) = false;
}
match = true;
}
check_field_dependencies();
if (fieldFlags_(DISPLACEMENT)) { trackDisplacement_ = true; }
}
/*! \page man_hardy_gradients fix_modify AtC gradients
\section syntax
fix_modify AtC gradients <add | delete> <list_of_fields> \n
- add | delete (keyword) = add or delete the calculation of gradients for the listed output fields \n
- fields (keyword) = \n
gradients can be calculated for all fields listed in \ref man_hardy_fields
\section examples
<TT> fix_modify AtC gradients add temperature velocity stress </TT> \n
<TT> fix_modify AtC gradients delete velocity </TT> \n
\section description
Requests calculation and output of gradients of the fields from the
transfer class. These gradients will be with regard to spatial or material
coordinate for eulerian or lagrangian analysis, respectively, as specified by
atom_element_map (see \ref man_atom_element_map )
\section restrictions
Must be used with the hardy/field type of AtC fix
( see \ref man_fix_atc )
\section related
\section default
No gradients are calculated by default
*/
else if (strcmp(arg[argIdx],"gradients")==0) {
argIdx++;
if (strcmp(arg[argIdx],"add")==0) {
argIdx++;
FieldName field_name;
for (int i = argIdx; i < narg; ++i) {
field_name = string_to_field(arg[i]);
gradFlags_(field_name) = true;
}
match = true;
}
else if (strcmp(arg[argIdx],"delete")==0) {
argIdx++;
FieldName field_name;
for (int i = argIdx; i < narg; ++i) {
field_name = string_to_field(arg[i]);
gradFlags_(field_name) = false;
}
match = true;
}
}
/*! \page man_hardy_rates fix_modify AtC rates
\section syntax
fix_modify AtC rates <add | delete> <list_of_fields> \n
- add | delete (keyword) = add or delete the calculation of rates (time derivatives) for the listed output fields \n
- fields (keyword) = \n
rates can be calculated for all fields listed in \ref man_hardy_fields
\section examples
<TT> fix_modify AtC rates add temperature velocity stress </TT> \n
<TT> fix_modify AtC rates delete stress </TT> \n
\section description
Requests calculation and output of rates (time derivatives) of the fields from the
transfer class. For eulerian analysis (see \ref man_atom_element_map ), these rates
are the partial time derivatives of the nodal fields, not the full (material) time
derivatives. \n
\section restrictions
Must be used with the hardy/field type of AtC fix
( see \ref man_fix_atc )
\section related
\section default
No rates are calculated by default
*/
else if (strcmp(arg[argIdx],"rates")==0) {
argIdx++;
if (strcmp(arg[argIdx],"add")==0) {
argIdx++;
FieldName field_name;
for (int i = argIdx; i < narg; ++i) {
field_name = string_to_field(arg[i]);
rateFlags_(field_name) = true;
}
match = true;
}
else if (strcmp(arg[argIdx],"delete")==0) {
argIdx++;
FieldName field_name;
for (int i = argIdx; i < narg; ++i) {
field_name = string_to_field(arg[i]);
rateFlags_(field_name) = false;
}
match = true;
}
}
/*! \page man_pair_interactions fix_modify AtC pair_interactions/bond_interactions
\section syntax
fix_modify AtC pair_interactions <on|off> \n
fix_modify AtC bond_interactions <on|off> \n
\section examples
<TT> fix_modify AtC bond_interactions on </TT> \n
\section description
include bonds and/or pairs in the stress and heat flux computations
\section restrictions
\section related
\section default
pair interactions: on, bond interactions: off
*/
if (strcmp(arg[argIdx],"pair_interactions")==0) { // default true
argIdx++;
if (strcmp(arg[argIdx],"on")==0) { hasPairs_ = true; }
else { hasPairs_ = false;}
match = true;
}
if (strcmp(arg[argIdx],"bond_interactions")==0) { // default false
argIdx++;
if (strcmp(arg[argIdx],"on")==0) { hasBonds_ = true; }
else { hasBonds_ = false;}
match = true;
}
/*! \page man_hardy_computes fix_modify AtC computes
\section syntax
fix_modify AtC computes <add | delete> [per-atom compute id] <volume | number> \n
- add | delete (keyword) = add or delete the calculation of an equivalent continuum field
for the specified per-atom compute as volume or number density quantity \n
- per-atom compute id = name/id for per-atom compute,
fields can be calculated for all per-atom computes available from LAMMPS \n
- volume | number (keyword) = field created is a per-unit-volume quantity
or a per-atom quantity as weighted by kernel functions \n
\section examples
<TT> compute virial all stress/atom </TT> \n
<TT> fix_modify AtC computes add virial volume </TT> \n
<TT> fix_modify AtC computes delete virial </TT> \n
\n
<TT> compute centrosymmetry all centro/atom </TT> \n
<TT> fix_modify AtC computes add centrosymmetry number </TT> \n
\section description
Calculates continuum fields corresponding to specified per-atom computes created by LAMMPS \n
\section restrictions
Must be used with the hardy/field type of AtC fix ( see \ref man_fix_atc ) \n
Per-atom compute must be specified before corresponding continuum field can be requested \n
\section related
See manual page for compute
\section default
No defaults exist for this command
*/
else if (strcmp(arg[argIdx],"computes")==0) {
argIdx++;
if (strcmp(arg[argIdx],"add")==0) {
argIdx++;
string tag(arg[argIdx++]);
int normalization = NO_NORMALIZATION;
if (narg > argIdx) {
if (strcmp(arg[argIdx],"volume")==0) {
normalization = VOLUME_NORMALIZATION;
}
else if (strcmp(arg[argIdx],"number")==0) {
normalization = NUMBER_NORMALIZATION;
}
else if (strcmp(arg[argIdx],"mass")==0) {
normalization = MASS_NORMALIZATION;
throw ATC_Error("mass normalized not implemented");
}
}
computes_[tag] = normalization;
nComputes_++;
match = true;
}
else if (strcmp(arg[argIdx],"delete")==0) {
argIdx++;
string tag(arg[argIdx]);
if (computes_.find(tag) != computes_.end()) {
computes_.erase(tag);
nComputes_--;
}
else {
throw ATC_Error(tag+" compute is not in list");
}
match = true;
}
}
/*! \page man_sample_frequency fix_modify AtC sample_frequency
\section syntax
fix_modify AtC sample_frequency [freq]
- freq (int) : frequency to sample field in number of steps
\section examples
<TT> fix_modify AtC sample_frequency 10
\section description
Specifies a frequency at which fields are computed for the case
where time filters are being applied.
\section restrictions
Must be used with the hardy/field AtC fix ( see \ref man_fix_atc )
and is only relevant when time filters are being used.
\section related
\section default
none
*/
else if (strcmp(arg[argIdx],"sample_frequency")==0) {
argIdx++;
int value = outputFrequency_; // default to output frequency
if (narg > 1) {
if (atoi(arg[argIdx]) > 0) value = atoi(arg[argIdx]);
}
sampleFrequency_ = value;
match = true;
} // end "sample_frequency"
// no match, call base class parser
if (!match) {
match = ATC_Method::modify(narg, arg);
}
return match;
}
//-------------------------------------------------------------------
// called at the beginning of a timestep
void ATC_Transfer::pre_init_integrate()
{
ATC_Method::pre_init_integrate();
}
//-------------------------------------------------------------------
// called at the beginning of second half timestep
// REFACTOR move this to post_neighbor
void ATC_Transfer::pre_final_integrate()
{
// update time
update_time(); // time uses step if dt = 0
if ( neighborReset_ && sample_now() ) {
if (! kernelOnTheFly_ ) {
if (!moleculeIds_.empty()) compute_kernel_matrix_molecule(); //KKM add
}
neighborReset_ = false;
}
}
//-------------------------------------------------------------------
// called at the end of second half timestep
void ATC_Transfer::post_final_integrate()
{
// compute spatially smoothed quantities
double dt = lammpsInterface_->dt();
if ( sample_now() ) {
bool needsBond = (! bondOnTheFly_ ) &&
(fieldFlags_(STRESS)
|| fieldFlags_(ESHELBY_STRESS)
|| fieldFlags_(HEAT_FLUX));
if ( needsBond ) {
if (pairMap_->need_reset()) {
// ATC::LammpsInterface::instance()->print_msg("Recomputing bond matrix due to atomReset_ value");
compute_bond_matrix();
}
}
time_filter_pre (dt);
compute_fields();
time_filter_post(dt);
lammpsInterface_->computes_addstep(lammpsInterface_->ntimestep()+sampleFrequency_);
}
// output
if ( output_now() && !outputStepZero_ ) output();
outputStepZero_ = false;
//ATC_Method::post_final_integrate();
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_bond_matrix(void)
{
bondMatrix_->reset();
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_fields(void)
{
// keep per-atom computes fresh. JAZ and REJ not sure why;
// need to confer with JAT. (JAZ, 4/5/12)
interscaleManager_.lammps_force_reset();
// (1) direct quantities
for(int i=0; i < numFields_; ++i) {
FieldName index = indices_[i];
if (fieldFlags_(index)) {
DENS_MAT & data(hardyData_[field_to_string(index)].set_quantity());
data = (outputFields_[index])->quantity();
}
}
if (fieldFlags_(STRESS))
compute_stress(hardyData_["stress"].set_quantity());
if (fieldFlags_(HEAT_FLUX))
compute_heatflux(hardyData_["heat_flux"].set_quantity());
// molecule data
if (fieldFlags_(DIPOLE_MOMENT))
compute_dipole_moment(hardyData_["dipole_moment"].set_quantity());
if (fieldFlags_(QUADRUPOLE_MOMENT))
compute_quadrupole_moment(hardyData_["quadrupole_moment"].set_quantity());
if (fieldFlags_(DISLOCATION_DENSITY))
compute_dislocation_density(hardyData_["dislocation_density"].set_quantity());
// (2) derived quantities
// compute: gradients
if (gradFlags_.has_member(true)) {
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (gradFlags_(index)) {
string field= field_to_string((FieldName) index);
string grad_field = field + "_gradient";
if (hardyData_.find(field) == hardyData_.end() ) {
throw ATC_Error("field " + field + " needs to be defined for gradient");
}
gradient_compute(hardyData_[field].quantity(), hardyData_[grad_field].set_quantity());
}
}
}
// compute: eshelby stress
if (fieldFlags_(ESHELBY_STRESS)) {
{
compute_eshelby_stress(hardyData_["eshelby_stress"].set_quantity(),
hardyData_["internal_energy"].quantity(),
hardyData_["stress"].quantity(),
hardyData_["displacement_gradient"].quantity());
}
}
if (fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)) {
DENS_MAT & H = hardyData_["displacement_gradient"].set_quantity();
DENS_MAT E(H.nRows(),1);
ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature");
const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity());
//DENS_MAT & T = hardyData_["temperature"];
//cauchy_born_entropic_energy(H,E,T); E += hardyData_["internal_energy"];
cauchy_born_energy(H, E, temp);
compute_eshelby_stress(hardyData_["cauchy_born_eshelby_stress"].set_quantity(),
E,hardyData_["stress"].quantity(),
hardyData_["displacement_gradient"].quantity());
}
// compute: cauchy born stress
if (fieldFlags_(CAUCHY_BORN_STRESS)) {
ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature");
const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity());
cauchy_born_stress(hardyData_["displacement_gradient"].quantity(),
hardyData_["cauchy_born_stress"].set_quantity(), temp);
}
// compute: cauchy born energy
if (fieldFlags_(CAUCHY_BORN_ENERGY)) {
ATOMIC_DATA::const_iterator tfield = hardyData_.find("temperature");
const DENS_MAT *temp = tfield==hardyData_.end() ? NULL : &((tfield->second).quantity());
cauchy_born_energy(hardyData_["displacement_gradient"].quantity(),
hardyData_["cauchy_born_energy"].set_quantity(), temp);
}
// 1st PK transformed to cauchy (lag) or cauchy transformed to 1st PK (eul)
if (fieldFlags_(TRANSFORMED_STRESS)) {
compute_transformed_stress(hardyData_["transformed_stress"].set_quantity(),
hardyData_["stress"].quantity(),
hardyData_["displacement_gradient"].quantity());
}
if (fieldFlags_(VACANCY_CONCENTRATION)) {
compute_vacancy_concentration(hardyData_["vacancy_concentration"].set_quantity(),
hardyData_["displacement_gradient"].quantity(),
hardyData_["number_density"].quantity());
}
if (fieldFlags_(ELECTRIC_POTENTIAL)) {
compute_electric_potential(
hardyData_[field_to_string(ELECTRIC_POTENTIAL)].set_quantity());
}
// compute: rotation and/or stretch from deformation gradient
if (fieldFlags_(ROTATION) || fieldFlags_(STRETCH)) {
compute_polar_decomposition(hardyData_["rotation"].set_quantity(),
hardyData_["stretch"].set_quantity(),
hardyData_["displacement_gradient"].quantity());
}
// compute: rotation and/or stretch from deformation gradient
if (fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) {
compute_elastic_deformation_gradient2(hardyData_["elastic_deformation_gradient"].set_quantity(),
hardyData_["stress"].quantity(),
hardyData_["displacement_gradient"].quantity());
}
// (3) computes
lammpsInterface_->computes_clearstep();
map <string,int>::const_iterator iter;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
COMPUTE_POINTER cmpt = lammpsInterface_->compute_pointer(tag);
int projection = iter->second;
int ncols = lammpsInterface_->compute_ncols_peratom(cmpt);;
DENS_MAT atomicData(nLocal_,ncols);
if (ncols == 1) {
double * atomData = lammpsInterface_->compute_vector_peratom(cmpt);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
atomicData(i,0) = atomData[atomIdx];
}
}
else {
double ** atomData = lammpsInterface_->compute_array_peratom(cmpt);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
for (int k = 0; k < ncols; k++) {
atomicData(i,k) = atomData[atomIdx][k];
}
}
}
// REFACTOR -- make dep manage
if (projection == NO_NORMALIZATION) {
project(atomicData,hardyData_[tag].set_quantity());
}
else if (projection == VOLUME_NORMALIZATION) {
project_volume_normalized(atomicData,hardyData_[tag].set_quantity());
}
else if (projection == NUMBER_NORMALIZATION) {
project_count_normalized(atomicData,hardyData_[tag].set_quantity());
}
else if (projection == MASS_NORMALIZATION) {
throw ATC_Error("unimplemented normalization");
}
else {
throw ATC_Error("unimplemented normalization");
}
#ifdef ESHELBY_VIRIAL
if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) {
if (atomToElementMapType_ == LAGRANGIAN) {
DENS_MAT tmp = hardyData_[tag].quantity();
DENS_MAT & myData(hardyData_[tag].set_quantity());
myData.reset(nNodes_,FieldSizes[STRESS]);
DENS_MAT F(3,3),FT(3,3),FTINV(3,3),CAUCHY(3,3),PK1(3,3);
const DENS_MAT& H(hardyData_["displacement_gradient"].quantity());
for (int k = 0; k < nNodes_; k++ ) {
vector_to_symm_matrix(k,tmp,CAUCHY);
vector_to_matrix(k,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
FT = F.transpose();
FTINV = inv(FT);
// volumes are already reference volumes.
PK1 = CAUCHY*FTINV;
matrix_to_vector(k,PK1,myData);
}
}
compute_eshelby_stress(hardyData_["eshelby_virial"].set_quantity(),
hardyData_["internal_energy"].quantity(),hardyData_[tag].quantity(),
hardyData_["displacement_gradient"].quantity());
}
#endif
}
}// end of compute_fields routine
//-------------------------------------------------------------------
void ATC_Transfer::time_filter_pre(double dt)
{
sampleCounter_++;
string name;
double delta_t = dt*sampleFrequency_;
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (fieldFlags_(index)) {
name = field_to_string((FieldName) index);
timeFilters_(index)->apply_pre_step1(filteredData_[name].set_quantity(),
hardyData_[name].quantity(), delta_t);
}
}
map <string,int>::const_iterator iter;
int index = NUM_TOTAL_FIELDS;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
timeFilters_(index)->apply_pre_step1(filteredData_[tag].set_quantity(),
hardyData_[tag].quantity(), delta_t);
index++;
}
}
//-------------------------------------------------------------------
void ATC_Transfer::time_filter_post(double dt)
{
sampleCounter_++;
string name;
double delta_t = dt*sampleFrequency_;
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (fieldFlags_(index)) {
name = field_to_string((FieldName) index);
timeFilters_(index)->apply_post_step2(filteredData_[name].set_quantity(),
hardyData_[name].quantity(), delta_t);
}
}
if (rateFlags_.has_member(true)) {
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (rateFlags_(index)) {
string field= field_to_string((FieldName) index);
string rate_field = field + "_rate";
timeFilters_(index)->rate(hardyData_[rate_field].set_quantity(),
filteredData_[field].quantity(),
hardyData_[field].quantity(), delta_t);
}
}
}
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (rateFlags_(index)) {
name = field_to_string((FieldName) index);
string rate_field = name + "_rate";
filteredData_[rate_field] = hardyData_[rate_field];
}
if (gradFlags_(index)) {
name = field_to_string((FieldName) index);
string grad_field = name + "_gradient";
filteredData_[grad_field] = hardyData_[grad_field];
}
}
// lists/accessing of fields ( & computes)
map <string,int>::const_iterator iter;
int index = NUM_TOTAL_FIELDS;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
timeFilters_(index)->apply_post_step2(filteredData_[tag].set_quantity(),
hardyData_[tag].quantity(), delta_t);
#ifdef ESHELBY_VIRIAL
if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) {
filteredData_["eshelby_virial"] = hardyData_["eshelby_virial"];
}
#endif
index++;
}
}
//-------------------------------------------------------------------
void ATC_Transfer::output()
{
feEngine_->departition_mesh();
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (outputFlags_(index)) {
FieldName fName = (FieldName) index;
string name= field_to_string(fName);
fields_[fName] = filteredData_[name];
}
}
ATC_Method::output();
if (lammpsInterface_->comm_rank() == 0) {
// data
OUTPUT_LIST output_data;
#ifdef REFERENCE_PE_OUTPUT
output_data["reference_potential_energy"] = nodalRefPotentialEnergy_->quantity();
#endif
for(int index=0; index < NUM_TOTAL_FIELDS; ++index) {
if (outputFlags_(index)) {
string name= field_to_string((FieldName) index);
output_data[name] = & ( filteredData_[name].set_quantity());
}
if (rateFlags_(index)) {
string name= field_to_string((FieldName) index);
string rate_name = name + "_rate";
output_data[rate_name] = & ( filteredData_[rate_name].set_quantity());
}
if (gradFlags_(index)) {
string name= field_to_string((FieldName) index);
string grad_name = name + "_gradient";
output_data[grad_name] = & ( filteredData_[grad_name].set_quantity());
}
}
// lists/accessing of fields ( & computes)
map <string,int>::const_iterator iter;
for (iter = computes_.begin(); iter != computes_.end(); iter++) {
string tag = iter->first;
output_data[tag] = & ( filteredData_[tag].set_quantity());
#ifdef ESHELBY_VIRIAL
if (tag == "virial" && fieldFlags_(ESHELBY_STRESS)) {
output_data["eshelby_virial"] = & ( filteredData_["eshelby_virial"].set_quantity() );
}
#endif
}
DENS_MAT nodalInverseVolumes = CLON_VEC(accumulantInverseVolumes_->quantity());
output_data["NodalInverseVolumes"] = &nodalInverseVolumes;
// output
feEngine_->write_data(output_index(), & output_data);
}
feEngine_->partition_mesh();
}
/////// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/////// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------
// computes nodeData = N*atomData
void ATC_Transfer::project(const DENS_MAT & atomData,
DENS_MAT & nodeData)
{
if (! kernelOnTheFly_ ) {
nodeData.reset(nNodes_,atomData.nCols(),true);
DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols());
if (nLocal_>0) workNodeArray = (accumulant_->quantity()).transMat(atomData);
int count = nodeData.nRows()*nodeData.nCols();
lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count);
}
else {
compute_projection(atomData,nodeData);
}
}
//-------------------------------------------------------------------
// computes nodeData = N*molData specially for molecules
void ATC_Transfer::project_molecule(const DENS_MAT & molData,
DENS_MAT & nodeData)
{
if (! kernelOnTheFly_ ) {
nodeData.reset(nNodes_,molData.nCols(),true);
DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols());
if (nLocal_>0) workNodeArray = (accumulantMol_->quantity()).transMat(molData);
int count = nodeData.nRows()*nodeData.nCols();
lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count);
}
else {
compute_projection(molData,nodeData);
}
}
//-------------------------------------------------------------------
// computes nodeData = gradient of N*molData specially for molecules
void ATC_Transfer::project_molecule_gradient(const DENS_MAT & molData,
DENS_MAT & nodeData)
{
if (! kernelOnTheFly_ ) {
nodeData.reset(nNodes_,molData.nCols(),true);
DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols());
if (nLocal_>0) workNodeArray = (accumulantMolGrad_->quantity()).transMat(molData);
int count = nodeData.nRows()*nodeData.nCols();
lammpsInterface_->allsum(workNodeArray.ptr(),nodeData.ptr(),count);
}
else {
compute_projection(molData,nodeData);
}
}
//-------------------------------------------------------------------
// count normalized
void ATC_Transfer::project_count_normalized(const DENS_MAT & atomData,
DENS_MAT & nodeData)
{
DENS_MAT tmp;
project(atomData,tmp);
nodeData = (accumulantWeights_->quantity())*tmp;
}
//-------------------------------------------------------------------
// volume normalized
void ATC_Transfer::project_volume_normalized(const DENS_MAT & atomData,
DENS_MAT & nodeData)
{
DENS_MAT tmp;
project(atomData,tmp);
nodeData = (accumulantInverseVolumes_->quantity())*tmp;
}
//-------------------------------------------------------------------
// volume normalized molecule
void ATC_Transfer::project_volume_normalized_molecule(const DENS_MAT & molData,
DENS_MAT & nodeData)
{
DENS_MAT tmp;
project_molecule(molData,tmp);
nodeData = (accumulantInverseVolumes_->quantity())*tmp;
}
//-------------------------------------------------------------------
// volume normalized molecule_gradient
void ATC_Transfer::project_volume_normalized_molecule_gradient(const DENS_MAT & molData,
DENS_MAT & nodeData)
{
DENS_MAT tmp;
project_molecule_gradient(molData,tmp);
nodeData = (accumulantInverseVolumes_->quantity())*tmp;
}
//-------------------------------------------------------------------
void ATC_Transfer::gradient_compute(const DENS_MAT & inNodeData,
DENS_MAT & outNodeData)
{
int nrows = inNodeData.nRows();
int ncols = inNodeData.nCols();
outNodeData.reset(nrows,ncols*nsd_);
int index = 0;
for (int n = 0; n < ncols; n++) { //output v1,1 v1,2 v1,3 ...
for (int m = 0; m < nsd_; m++) {
CLON_VEC inData(inNodeData,CLONE_COL,n);
CLON_VEC outData(outNodeData,CLONE_COL,index);
outData = (*((gradientMatrix_->quantity())[m]))*inData;
++index;
}
}
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_force_matrix()
{
atomicForceMatrix_ = pairVirial_->quantity();
}
//-------------------------------------------------------------------
// computes "virial" part of heat flux
// This is correct ONLY for pair potentials.
void ATC_Transfer::compute_heat_matrix()
{
atomicHeatMatrix_ = pairHeatFlux_->quantity();
}
//-------------------------------------------------------------------
// set xPointer_ to xref or xatom depending on Lagrangian/Eulerian analysis
void ATC_Transfer::set_xPointer()
{
xPointer_ = xref_;
if (atomToElementMapType_ == EULERIAN) {
xPointer_ = lammpsInterface_->xatom();
}
}
//-------------------------------------------------------------------
// SOON TO BE OBSOLETE
// check consistency of fieldFlags_
void ATC_Transfer::check_field_dependencies()
{
fieldFlags_ = outputFlags_;
if (fieldFlags_(TRANSFORMED_STRESS)) {
fieldFlags_(STRESS) = true;
fieldFlags_(DISPLACEMENT) = true;
}
if (fieldFlags_(ESHELBY_STRESS)) {
fieldFlags_(STRESS) = true;
fieldFlags_(INTERNAL_ENERGY) = true;
fieldFlags_(DISPLACEMENT) = true;
}
if (fieldFlags_(CAUCHY_BORN_STRESS)
|| fieldFlags_(CAUCHY_BORN_ENERGY)
|| fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)
|| fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) {
if (! (cauchyBornStress_) ) {
throw ATC_Error("can't compute cauchy-born stress w/o cauchy born model");
}
}
if (fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)) {
fieldFlags_(STRESS) = true;
}
if (fieldFlags_(CAUCHY_BORN_STRESS)
|| fieldFlags_(CAUCHY_BORN_ENERGY)) {
fieldFlags_(TEMPERATURE) = true;
fieldFlags_(DISPLACEMENT) = true;
}
if (fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)) {
fieldFlags_(TEMPERATURE) = true;
fieldFlags_(DISPLACEMENT) = true;
fieldFlags_(STRESS) = true;
}
if (fieldFlags_(VACANCY_CONCENTRATION)) {
fieldFlags_(DISPLACEMENT) = true;
fieldFlags_(NUMBER_DENSITY) = true;
}
if (fieldFlags_(INTERNAL_ENERGY)) {
fieldFlags_(POTENTIAL_ENERGY) = true;
fieldFlags_(THERMAL_ENERGY) = true;
}
if (fieldFlags_(ENERGY)) {
fieldFlags_(POTENTIAL_ENERGY) = true;
fieldFlags_(KINETIC_ENERGY) = true;
}
if (fieldFlags_(TEMPERATURE) || fieldFlags_(HEAT_FLUX) ||
fieldFlags_(KINETIC_ENERGY) || fieldFlags_(THERMAL_ENERGY) ||
fieldFlags_(ENERGY) || fieldFlags_(INTERNAL_ENERGY) ||
fieldFlags_(KINETIC_ENERGY) || (fieldFlags_(STRESS) &&
atomToElementMapType_ == EULERIAN) ) {
fieldFlags_(VELOCITY) = true;
fieldFlags_(MASS_DENSITY) = true;
}
if (fieldFlags_(VELOCITY)) {
fieldFlags_(MASS_DENSITY) = true;
fieldFlags_(MOMENTUM) = true;
}
if (fieldFlags_(DISPLACEMENT)) {
fieldFlags_(MASS_DENSITY) = true;
}
if (fieldFlags_(TEMPERATURE) ) {
fieldFlags_(NUMBER_DENSITY) = true;
}
if (fieldFlags_(ROTATION) ||
fieldFlags_(STRETCH)) {
fieldFlags_(DISPLACEMENT) = true;
}
if (fieldFlags_(ESHELBY_STRESS)
|| fieldFlags_(CAUCHY_BORN_STRESS)
|| fieldFlags_(CAUCHY_BORN_ENERGY)
|| fieldFlags_(CAUCHY_BORN_ESHELBY_STRESS)
|| fieldFlags_(CAUCHY_BORN_ELASTIC_DEFORMATION_GRADIENT)
|| fieldFlags_(VACANCY_CONCENTRATION)
|| fieldFlags_(ROTATION)
|| fieldFlags_(STRETCH) ) {
gradFlags_(DISPLACEMENT) = true;
}
// check whether single_enable==0 for stress/heat flux calculation
if (fieldFlags_(STRESS) || fieldFlags_(HEAT_FLUX)) {
if (lammpsInterface_->single_enable()==0) {
throw ATC_Error("Calculation of stress field not possible with selected pair type.");
}
}
}
//============== THIN WRAPPERS ====================================
// OBSOLETE
// HARDY COMPUTES
// ***************UNCONVERTED**************************
//-------------------------------------------------------------------
// MOLECULE
//-------------------------------------------------------------------
void ATC_Transfer::compute_dipole_moment(DENS_MAT & dipole_moment)
{
const DENS_MAT & molecularVector(dipoleMoment_->quantity());
project_volume_normalized_molecule(molecularVector,dipole_moment); // KKM add
//
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_quadrupole_moment(DENS_MAT & quadrupole_moment)
{
const DENS_MAT & molecularVector(quadrupoleMoment_->quantity());
project_volume_normalized_molecule_gradient(molecularVector,quadrupole_moment); // KKM add
//
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_stress(DENS_MAT & stress)
{
// table of bond functions already calculated in initialize function
// get conversion factor for nktV to p units
double nktv2p = lammpsInterface_->nktv2p();
// calculate kinetic energy tensor part of stress for Eulerian analysis
if (atomToElementMapType_ == EULERIAN && nLocal_>0) {
compute_kinetic_stress(stress);
}
else {
// zero stress table for Lagrangian analysis or if nLocal_ = 0
stress.zero();
}
// add-in potential part of stress tensor
int nrows = stress.nRows();
int ncols = stress.nCols();
DENS_MAT local_potential_hardy_stress(nrows,ncols);
if (nLocal_>0) {
if (bondOnTheFly_) {
compute_potential_stress(local_potential_hardy_stress);
}
else {
// compute table of force & position dyad
compute_force_matrix();
// calculate force part of stress tensor
local_potential_hardy_stress = atomicBondMatrix_*atomicForceMatrix_;
local_potential_hardy_stress *= 0.5;
}
}
// global summation of potential part of stress tensor
DENS_MAT potential_hardy_stress(nrows,ncols);
int count = nrows*ncols;
lammpsInterface_->allsum(local_potential_hardy_stress.ptr(),
potential_hardy_stress.ptr(), count);
stress += potential_hardy_stress;
stress = nktv2p*stress;
}
//-------------------------------------------------------------------
// kinetic energy portion of stress
void ATC_Transfer::compute_kinetic_stress(DENS_MAT& stress)
{
const DENS_MAT& density = hardyData_["mass_density"].quantity();
const DENS_MAT& velocity = hardyData_["velocity"].quantity();
int * type = lammpsInterface_->atom_type();
double * mass = lammpsInterface_->atom_mass();
double * rmass = lammpsInterface_->atom_rmass();
double ** vatom = lammpsInterface_->vatom();
double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy]
atomicTensor_.reset(nLocal_,6);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx];
ma *= mvv2e; // convert mass to appropriate units
double* v = vatom[atomIdx];
atomicTensor_(i,0) -= ma*v[0]*v[0];
atomicTensor_(i,1) -= ma*v[1]*v[1];
atomicTensor_(i,2) -= ma*v[2]*v[2];
atomicTensor_(i,3) -= ma*v[0]*v[1];
atomicTensor_(i,4) -= ma*v[0]*v[2];
atomicTensor_(i,5) -= ma*v[1]*v[2];
}
project_volume_normalized(atomicTensor_, stress);
for (int i = 0; i < nNodes_; i++) {
double rho_i = mvv2e*density(i,0);
stress(i,0) += rho_i*velocity(i,0)*velocity(i,0);
stress(i,1) += rho_i*velocity(i,1)*velocity(i,1);
stress(i,2) += rho_i*velocity(i,2)*velocity(i,2);
stress(i,3) += rho_i*velocity(i,0)*velocity(i,1);
stress(i,4) += rho_i*velocity(i,0)*velocity(i,2);
stress(i,5) += rho_i*velocity(i,1)*velocity(i,2);
}
}
//-------------------------------------------------------------------
void ATC_Transfer::compute_heatflux(DENS_MAT & flux)
{
// calculate kinetic part of heat flux
if (atomToElementMapType_ == EULERIAN && nLocal_>0) {
compute_kinetic_heatflux(flux);
}
else {
flux.zero(); // zero stress table for Lagrangian analysis
}
// add potential part of heat flux vector
int nrows = flux.nRows();
int ncols = flux.nCols();
DENS_MAT local_hardy_heat(nrows,ncols);
if (nLocal_>0) {
if (bondOnTheFly_) {
compute_potential_heatflux(local_hardy_heat);
}
else {
// calculate force/potential-derivative part of heat flux
compute_heat_matrix();
local_hardy_heat = atomicBondMatrix_*atomicHeatMatrix_;
}
}
// global summation of potential part of heat flux vector
DENS_MAT hardy_heat(nrows,ncols);
int count = nrows*ncols;
lammpsInterface_->allsum(local_hardy_heat.ptr(),
hardy_heat.ptr(), count);
flux += hardy_heat;
}
//-------------------------------------------------------------------
// compute kinetic part of heat flux
void ATC_Transfer::compute_kinetic_heatflux(DENS_MAT& flux)
{
const DENS_MAT& velocity = hardyData_["velocity"].quantity();
const DENS_MAT& energy = hardyData_["mass_density"].quantity();
const DENS_MAT& stress = hardyData_["stress"].quantity();
int * type = lammpsInterface_->atom_type();
double * mass = lammpsInterface_->atom_mass();
double * rmass = lammpsInterface_->atom_rmass();
double ** vatom = lammpsInterface_->vatom();
double mvv2e = lammpsInterface_->mvv2e();
double * atomPE = lammpsInterface_->compute_pe_peratom();
double atomKE, atomEnergy;
atomicVector_.reset(nLocal_,3);
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
double ma = mass ? mass[type[atomIdx]]: rmass[atomIdx];
ma *= mvv2e; // convert mass to appropriate units
double* v = vatom[atomIdx];
atomKE = 0.0;
for (int k = 0; k < nsd_; k++) { atomKE += v[k]*v[k]; }
atomKE *= 0.5*ma;
atomEnergy = atomKE + atomPE[atomIdx];
for (int j = 0; j < nsd_; j++) {
atomicVector_(i,j) += atomEnergy*v[j];
}
}
project_volume_normalized(atomicVector_,flux);
// - e^0_I v_I + \sigma^T_I v_I
for (int i = 0; i < nNodes_; i++) {
double e_i = energy(i,0);
flux(i,0) += (e_i + stress(i,0))*velocity(i,0)
+ stress(i,3)*velocity(i,1)+ stress(i,4)*velocity(i,2);
flux(i,1) += (e_i + stress(i,1))*velocity(i,1)
+ stress(i,3)*velocity(i,0)+ stress(i,5)*velocity(i,2);
flux(i,2) += (e_i + stress(i,2))*velocity(i,2)
+ stress(i,4)*velocity(i,0)+ stress(i,5)*velocity(i,1);
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_electric_potential(DENS_MAT & phi)
{
// Poisson solve with insulating bcs
const DENS_MAT & rho = (restrictedCharge_->quantity());
SPAR_MAT K;
feEngine_->stiffness_matrix(K);
double permittivity = lammpsInterface_->dielectric();
permittivity *= LammpsInterface::instance()->epsilon0();
K *= permittivity;
BC_SET bcs;
bcs.insert(pair<int,int>(0,0));
LinearSolver solver(K,bcs);
CLON_VEC x = column(phi,0);
CLON_VEC b = column(rho,0);
solver.solve(x,b);
//x.print("x:phi");
//b.print("b:rho");
//LinearSolver solver(K,AUTO_SOLVE,true);
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_vacancy_concentration(DENS_MAT & Cv,
const DENS_MAT & H, const DENS_MAT & /* rhoN */)
{
int * type = lammpsInterface_->atom_type();
DENS_MAT new_rho(nNodes_,1);
DENS_MAT atomCnt(nLocal_,1);
double atomic_weight_sum = 0.0;
double site_weight_sum = 0.0;
int number_atoms = 0;
const DIAG_MAT & myAtomicWeights(atomVolume_->quantity());
for (int i = 0; i < nLocal_; i++) {
int atomIdx = internalToAtom_(i);
if (type[atomIdx] != 13) {
atomCnt(i,0) = myAtomicWeights(i,i);
atomic_weight_sum += myAtomicWeights(i,i);
number_atoms++;
}
site_weight_sum += myAtomicWeights(i,i);
}
project_volume_normalized(atomCnt, new_rho);
DENS_MAT F(3,3);
for (int i = 0; i < nNodes_; i++) {
if (atomToElementMapType_ == EULERIAN) {
// for Eulerian analysis: F = (1-H)^{-1}
DENS_MAT G(3,3);
vector_to_matrix(i,H,G);
G *= -1.;
G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0;
F = inv(G);
}
else if (atomToElementMapType_ == LAGRANGIAN) {
// for Lagrangian analysis: F = (1+H)
vector_to_matrix(i,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
}
double J = det(F);
double volume_per_atom = lammpsInterface_->volume_per_atom();
J *= volume_per_atom;
Cv(i,0) = 1.0 - J*new_rho(i,0);
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_eshelby_stress(DENS_MAT & M,
const DENS_MAT & E, const DENS_MAT & S, const DENS_MAT & H)
{
// eshelby stress:M, energy:E, stress:S, displacement gradient: H
// eshelby stress = W I - F^T.P = W I - C.S [energy]
// symmetric if isotropic S = a_0 I + a_1 C + a_2 C^2
M.reset(nNodes_,FieldSizes[ESHELBY_STRESS]);
double nktv2p = lammpsInterface_->nktv2p();
DENS_MAT P(3,3),F(3,3),FT(3,3),FTP(3,3),ESH(3,3);
for (int i = 0; i < nNodes_; i++) {
double W = E(i,0);
ESH.identity();
ESH *= W;
// copy to local
if (atomToElementMapType_ == LAGRANGIAN) {
// Stress notation convention:: 0:11 1:12 2:13 3:21 4:22 5:23 6:31 7:32 8:33
vector_to_matrix(i,S,P);
vector_to_matrix(i,H,F);
#ifndef H_BASED
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
#endif
FT = F.transpose();
}
else if (atomToElementMapType_ == EULERIAN) {
vector_to_symm_matrix(i,S,P);
vector_to_matrix(i,H,F);
FT = F.transpose();
}
FTP = (1.0/nktv2p)*FT*P;
ESH -= FTP;
if (atomToElementMapType_ == EULERIAN) {
// For Eulerian analysis, M = F^T*(w-H^T.CauchyStress)
DENS_MAT Q(3,3);
Q.identity();
// Q stores (1-H)
Q -= FT.transpose();
DENS_MAT F(3,3);
F = inv(Q);
FT = F.transpose();
ESH = FT*ESH;
}
// copy to global
matrix_to_vector(i,ESH,M);
}
}
//---------------------------------------------------------------------------
// Computes the Cauchy Born stress tensor, T given displacement gradient, H
// and optional temperature argument (passed by pointer), TEMP
//---------------------------------------------------------------------------
void ATC_Transfer::cauchy_born_stress(const DENS_MAT &H, DENS_MAT &T, const DENS_MAT *temp)
{
FIELD_MATS uField; // uField should contain temperature.
DENS_MAT_VEC tField;
GRAD_FIELD_MATS hField;
DENS_MAT_VEC &h = hField[DISPLACEMENT];
h.assign(nsd_, DENS_MAT(nNodes_,nsd_));
tField.assign(nsd_, DENS_MAT(nNodes_,nsd_));
// each row is the CB stress at a node stored in voigt form
T.reset(nNodes_,FieldSizes[CAUCHY_BORN_STRESS]);
const double nktv2p = lammpsInterface_->nktv2p();
const double fact = -lammpsInterface_->mvv2e()*nktv2p;
// reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient
vector_to_dens_mat_vec(H,h);
// if temperature is provided, then set it
if (temp) uField[TEMPERATURE] = *temp;
// Computes the stress at each node.
cauchyBornStress_->stress(uField, hField, tField);
// reshapes the stress, T to a (#node,6) DenseMatrix.
DENS_MAT S(nNodes_,6);
symm_dens_mat_vec_to_vector(tField,S);
S *= fact;
// tField/S holds the 2nd P-K stress tensor. Transform to
// Cauchy for EULERIAN analysis, transform to 1st P-K
// for LAGRANGIAN analysis.
DENS_MAT PK2(3,3),G(3,3),F(3,3),FT(3,3),STRESS(3,3);
for (int i = 0; i < nNodes_; i++) {
vector_to_symm_matrix(i,S,PK2);
if (atomToElementMapType_ == EULERIAN) {
// for Eulerian analysis: F = (1-H)^{-1}
vector_to_matrix(i,H,G);
G *= -1.;
G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0;
F = inv(G);
FT = transpose(F);
double J = det(F);
STRESS = F*PK2*FT;
STRESS *= 1/J;
symm_matrix_to_vector(i,STRESS,T);
}
else{
// for Lagrangian analysis: F = 1 + H
vector_to_matrix(i,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
STRESS = F*PK2;
matrix_to_vector(i,STRESS,T);
}
}
}
//---------------------------------------------------------------------------
// Computes the Cauchy Born energy density, E given displacement gradient, H
// and optional temperature argument (passed by pointer), TEMP
//---------------------------------------------------------------------------
void ATC_Transfer::cauchy_born_energy(const DENS_MAT &H, DENS_MAT &E, const DENS_MAT *temp)
{
FIELD_MATS uField; // uField should contain temperature.
GRAD_FIELD_MATS hField;
DENS_MAT_VEC &h = hField[DISPLACEMENT];
h.assign(nsd_, DENS_MAT(nNodes_,nsd_));
// reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient
vector_to_dens_mat_vec(H,h);
// if temperature is provided, then set it
if (temp) uField[TEMPERATURE] = *temp;
// Computes the free/potential energy at each node.
cauchyBornStress_->elastic_energy(uField, hField, E);
// convert back to energy units for ( ATC coupling uses MLT units)
double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy]
E *= mvv2e;
// for Eulerian analysis, convert energy density to per-unit deformed volume
if (atomToElementMapType_ == EULERIAN) {
DENS_MAT G(3,3),F(3,3);
for (int i = 0; i < nNodes_; i++) {
// for Eulerian analysis: F = (1-H)^{-1}
vector_to_matrix(i,H,G);
G *= -1.;
G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0;
F = inv(G);
double J = det(F);
E(i,0) *= 1/J;
}
}
// subtract zero point energy
if (nodalRefPotentialEnergy_)
E -= nodalRefPotentialEnergy_->quantity();
}
//---------------------------------------------------------------------------
// Computes the M/LH entropic energy density
//---------------------------------------------------------------------------
void ATC_Transfer::cauchy_born_entropic_energy(const DENS_MAT &H, DENS_MAT &E, const DENS_MAT &T)
{
FIELD_MATS uField; // uField should contain temperature.
uField[TEMPERATURE] = T;
GRAD_FIELD_MATS hField;
DENS_MAT_VEC &h = hField[DISPLACEMENT];
h.assign(nsd_, DENS_MAT(nNodes_,nsd_));
// reshape H (#nodes,9) into h [3](#nodes,3) displacement gradient
vector_to_dens_mat_vec(H,h);
// Computes the free/potential energy at each node.
cauchyBornStress_->entropic_energy(uField, hField, E);
// convert back to energy units for ( ATC coupling uses MLT units)
double mvv2e = lammpsInterface_->mvv2e(); // [MV^2]-->[Energy]
E *= mvv2e;
// for Eulerian analysis, convert energy density to per-unit deformed volume
if (atomToElementMapType_ == EULERIAN) {
DENS_MAT G(3,3),F(3,3);
for (int i = 0; i < nNodes_; i++) {
// for Eulerian analysis: F = (1-H)^{-1}
vector_to_matrix(i,H,G);
G *= -1.;
G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0;
F = inv(G);
double J = det(F);
E(i,0) *= 1/J;
}
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_transformed_stress(DENS_MAT & stress,
const DENS_MAT & T, const DENS_MAT & H)
{
stress.reset(nNodes_,FieldSizes[TRANSFORMED_STRESS]);
DENS_MAT S(3,3),FT(3,3),P(3,3);
for (int i = 0; i < nNodes_; i++) {
if (atomToElementMapType_ == EULERIAN) {
vector_to_symm_matrix(i,T,P);
// for Eulerian analysis: F^T = (1-H)^{-T}
DENS_MAT G(3,3);
vector_to_matrix(i,H,G);
G *= -1.;
G(0,0) += 1.0; G(1,1) += 1.0; G(2,2) += 1.0;
FT = inv(G.transpose());
}
else{
vector_to_matrix(i,T,P);
// for Lagrangian analysis: F^T = (1+H)^T
DENS_MAT F(3,3);
vector_to_matrix(i,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
FT = F.transpose();
}
//
double J = det(FT);
FT *= 1/J;
if (atomToElementMapType_ == EULERIAN) {
FT = inv(FT);
}
S = P*FT;
matrix_to_vector(i,S,stress);
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_polar_decomposition(DENS_MAT & rotation,
DENS_MAT & stretch, const DENS_MAT & H)
{
DENS_MAT F(3,3),R(3,3),U(3,3);
for (int i = 0; i < nNodes_; i++) {
vector_to_matrix(i,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
if (atomToElementMapType_ == EULERIAN) {
polar_decomposition(F,R,U,false); } // F = V R
else {
polar_decomposition(F,R,U); } // F = R U
// copy to local
if ( fieldFlags_(ROTATION) ) {
matrix_to_vector(i,R,rotation);
}
if ( fieldFlags_(STRETCH) ) {
matrix_to_vector(i,U,stretch);
}
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_elastic_deformation_gradient(DENS_MAT & Fe,
const DENS_MAT & P, const DENS_MAT & H)
{
// calculate Fe for every node
const double nktv2p = lammpsInterface_->nktv2p();
const double fact = 1.0/ ( lammpsInterface_->mvv2e()*nktv2p );
for (int i = 0; i < nNodes_; i++) {
DENS_VEC Pv = global_vector_to_vector(i,P);
Pv *= fact;
CBElasticTangentOperator tangent(cauchyBornStress_, Pv);
NonLinearSolver solver(&tangent);
DENS_VEC Fv = global_vector_to_vector(i,H); // pass in initial guess
add_identity_voigt_unsymmetric(Fv);
solver.solve(Fv);
vector_to_global_vector(i,Fv,Fe);
}
}
//--------------------------------------------------------------------
void ATC_Transfer::compute_elastic_deformation_gradient2(DENS_MAT & Fe,
const DENS_MAT & P, const DENS_MAT & H)
{
// calculate Fe for every node
const double nktv2p = lammpsInterface_->nktv2p();
const double fact = 1.0/ ( lammpsInterface_->mvv2e()*nktv2p );
DENS_MAT F(3,3),R(3,3),U(3,3),PP(3,3),S(3,3);
for (int i = 0; i < nNodes_; i++) {
// get F = RU
vector_to_matrix(i,H,F);
F(0,0) += 1.0; F(1,1) += 1.0; F(2,2) += 1.0;
if (atomToElementMapType_ == EULERIAN) {
polar_decomposition(F,R,U,false); } // F = V R
else {
polar_decomposition(F,R,U); } // F = R U
// get S
vector_to_matrix(i,P,PP);
//S = PP*transpose(F);
S = inv(F)*PP;
S += S.transpose(); S *= 0.5; // symmetrize
DENS_VEC Sv = to_voigt(S);
Sv *= fact;
// solve min_U || S - S_CB(U) ||
CB2ndElasticTangentOperator tangent(cauchyBornStress_, Sv);
NonLinearSolver solver(&tangent);
//DENS_VEC Uv = to_voigt_unsymmetric(U); // pass in initial guess
DENS_VEC Uv = to_voigt(U); // pass in initial guess
//DENS_VEC Uv(6); Uv(0)=1;Uv(1)=1;Uv(2)=1;Uv(3)=0;Uv(4)=0;Uv(5)=0;
solver.solve(Uv);
DENS_MAT Ue = from_voigt(Uv);
DENS_MAT FFe = R*Ue;
matrix_to_vector(i,FFe,Fe);
}
}
// =========== Analytical solutions ==========================
} // end namespace ATC