Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F90446709
FE_Engine.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Nov 1, 18:19
Size
63 KB
Mime Type
text/x-c
Expires
Sun, Nov 3, 18:19 (2 d)
Engine
blob
Format
Raw Data
Handle
22056640
Attached To
rLAMMPS lammps
FE_Engine.cpp
View Options
#include "FE_Engine.h"
#include "ATC_Transfer.h"
#include "FE_Element.h"
#include "XT_Function.h"
#include "LammpsInterface.h"
#include "PhysicsModel.h"
#include <stdio.h>
#include <map>
using namespace std;
namespace ATC{
//-----------------------------------------------------------------
FE_Engine::FE_Engine(ATC_Transfer * atcTransfer)
: atcTransfer_(atcTransfer),
feMesh_(NULL),
initialized_(false),
amendedMeshData_(false),
outputManager_()
{
}
FE_Engine::~FE_Engine()
{
if (feMesh_) delete feMesh_;
if (connectivity_ && amendedMeshData_) delete connectivity_;
if (nodeMap_ && amendedMeshData_) delete nodeMap_;
if (coordinates_ && amendedMeshData_) delete coordinates_;
}
//-----------------------------------------------------------------
void FE_Engine::initialize()
{
if (! feMesh_)
throw ATC_Error(0,"FE_Engine has no mesh");
if (! initialized_ ) {
// mesh data
nodeMap_ = &(feMesh_->global_to_unique_map());
connectivity_ = &(feMesh_->connectivity());
coordinates_ = &(feMesh_->nodal_coordinates());
// set up work spaces
nNodesPerElement_ = feMesh_->get_nNodesPerElement();
nIPsPerElement_ = feMesh_->get_nIPsPerElement();
nIPsPerFace_ = feMesh_->get_nIPsPerFace();
nSD_ = feMesh_->get_nSpatialDimensions();
nElems_ = feMesh_->get_nElements();
// remove specified elements
if (nullElements_.size() > 0) delete_elements(nullElements_);
initialized_ = true;
}
}
//-----------------------------------------------------------------
bool FE_Engine::modify(int narg, char **arg)
{
bool match = false;
/*! \page man_fem_mesh fix_modify AtC fem create mesh
\section syntax
fix_modify AtC fem create mesh <nx> <ny> <nz> <region-id>
<f|p> <f|p> <f|p>
- nx ny nz = number of elements in x, y, z
- region-id = id of region that is to be meshed
- f p p = perioidicity flags for x, y, z
\section examples
<TT> fix_modify AtC fem create mesh 10 1 1 feRegion p p p </TT>
\section description
Creates a uniform mesh in a rectangular region
\section restrictions
creates only uniform rectangular grids in a rectangular region
\section related
\section default
none
*/
if (strcmp(arg[0],"fem")==0) {
// create mesh
if (strcmp(arg[1],"create")==0) {
if (strcmp(arg[2],"mesh")==0) {
int nx = atoi(arg[3]);
int ny = atoi(arg[4]);
int nz = atoi(arg[5]);
int xperiodic = 0;
if (strcmp(arg[7],"p")==0) xperiodic = 1;
int yperiodic = 0;
if (strcmp(arg[8],"p")==0) yperiodic = 1;
int zperiodic = 0;
if (strcmp(arg[9],"p")==0) zperiodic = 1;
if (feMesh_) throw ATC_Error(0,"FE Engine already has a mesh");
create_mesh(nx, ny, nz, arg[6], xperiodic, yperiodic, zperiodic );
// construct prescribed data manager NOTE move to ATC_Transfer?
atcTransfer_->initialize_mesh_data();
match = true;
}
}
}
/*! \page man_mesh_delete_elements fix_modify AtC mesh delete_elements
\section syntax
fix_modify AtC mesh delete_elements <element_set>
- <element_set> = name of an element set
\section examples
<TT> fix_modify AtC delete_elements gap </TT>
\section description
Deletes a group of elements from the mesh.
\section restrictions
\section related
\section default
none
*/
else if ( (strcmp(arg[0],"mesh")==0)
&& (strcmp(arg[1],"delete_elements")==0) ) {
string esetName = arg[2];
set<int> elemSet = feMesh_->get_elementset(esetName);
nullElements_.insert(elemSet.begin(), elemSet.end());
match = true;
}
// FE_Mesh
else {
match = feMesh_->modify(narg,arg);
}
return match;
}
//-----------------------------------------------------------------
void FE_Engine::finish()
{
// Nothing to do
}
//-----------------------------------------------------------------
// write geometry
//-----------------------------------------------------------------
void FE_Engine::initialize_output(int rank,
string outputPrefix, OutputType otype)
{
outputManager_.initialize(outputPrefix, otype);
if (! feMesh_) throw ATC_Error(0,"output needs mesh");
if (! initialized_) initialize();
if (rank == 0) outputManager_.write_geometry(*coordinates_, connectivity_);
}
//-----------------------------------------------------------------
// write geometry
//-----------------------------------------------------------------
void FE_Engine::write_geometry(void)
{
outputManager_.write_geometry(*coordinates_, connectivity_);
}
// -------------------------------------------------------------
// write data
// -------------------------------------------------------------
void FE_Engine::write_data(double time, FIELDS &soln, OUTPUT_LIST *data)
{
outputManager_.write_data(time, &soln, data, nodeMap_->get_data());
}
// -------------------------------------------------------------
// write data
// -------------------------------------------------------------
void FE_Engine::write_data(double time, OUTPUT_LIST *data)
{
outputManager_.write_data(time, data, nodeMap_->get_data());
}
// -------------------------------------------------------------
// amend mesh for deleted elements
// -------------------------------------------------------------
void FE_Engine::delete_elements(const set<int> & elementList)
{
int nsd = feMesh_->get_nSpatialDimensions();
set<int> elementsNew;
feMesh_->elementset_complement(elementList,elementsNew);
int nElementsNew = elementsNew.size();
set<int> newToOld;
map<int,int> oldToNewMap;
feMesh_->elementset_to_nodeset(elementsNew,newToOld);
int nNodesNew = newToOld.size();
set<int>::const_iterator itr;
// coordinates & node map (from nodes to data)
const DENS_MAT &coordinates = feMesh_->nodal_coordinates();
const Array<int> & node_map = feMesh_->global_to_unique_map();
if (coordinates_ && amendedMeshData_) delete coordinates_;
coordinates_ = new DENS_MAT(nsd,nNodesNew);
DENS_MAT & coor = * (const_cast<DENS_MAT *> ( coordinates_));
if (nodeMap_ && amendedMeshData_) delete nodeMap_;
nodeMap_ = new Array<int> (nNodesNew);
Array<int> & nmap = * (const_cast<Array<int> *> ( nodeMap_));
int k = 0, i = 0;
for (itr = newToOld.begin(); itr != newToOld.end(); itr++) {
int node = *itr;
oldToNewMap[node]=i++;
nmap(k) = node_map(node);
for(int j = 0; j < nsd; j++) {
coor(j,k) = coordinates(j,node);
}
k++;
}
// connectivity
const Array2D<int> & connectivity = feMesh_->connectivity();
int nNodesPerElement = connectivity.nRows();
int nElements = connectivity.nCols();
if (connectivity_ && amendedMeshData_) delete connectivity_;
connectivity_ = new Array2D<int>(nNodesPerElement,nElementsNew);
Array2D<int> & conn = * (const_cast<Array2D<int> *> ( connectivity_));
k = 0;
for (itr = elementsNew.begin(); itr != elementsNew.end(); itr++) {
int ielem = *itr;
for(int j = 0; j < nNodesPerElement; j++) {
int old_node = connectivity(j,ielem);
map<int,int>::iterator map_itr = oldToNewMap.find(old_node);
if (map_itr == oldToNewMap.end()) {
cout << "map failure " << old_node << "\n";
}
int node = map_itr->second;
conn(j,k) = node;
}
k++;
}
amendedMeshData_ = true;
}
// -------------------------------------------------------------
// compute dimensionless stiffness matrix using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_stiffness_matrix(SPAR_MAT &matrix) const
{
//int nfields = field_mask.get_length();
int nNodes = feMesh_->get_nNodesUnique();
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
DENS_MAT N(nIPsPerElement,nNodesPerElement);
vector<DENS_MAT> dN(nsd);
dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement));
DiagonalMatrix<double> weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// assemble consistent mass (nnodes X nnodes)
matrix.reset(nNodes,nNodes);
DENS_MAT elementMassMatrix(nNodesPerElement,nNodesPerElement);
for (int ielem = 0; ielem < nelems; ++ielem)
{
// evaluate shape functions
feMesh_->shape_function(ielem, N, dN, weights);
// perform quadrature
elementMassMatrix = dN[0].transMat(weights*dN[0]);
for (int i = 1; i < nsd; ++i) {
elementMassMatrix += dN[i].transMat(weights*dN[i]);
}
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
for (int i = 0; i < nNodesPerElement; ++i)
{
int inode = conn(i);
for (int j = 0; j < nNodesPerElement; ++j)
{
int jnode = conn(j);
matrix.add(inode, jnode, elementMassMatrix(i,j));
}
}
}
}
// -------------------------------------------------------------
// compute dimensionless consistent mass using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_mass_matrix(SPAR_MAT &mass_matrix) const
{
int nNodes = feMesh_->get_nNodesUnique();
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
DENS_MAT N(nIPsPerElement,nNodesPerElement);
DiagonalMatrix<double> weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// assemble consistent mass (nnodes X nnodes)
mass_matrix.reset(nNodes,nNodes);
DENS_MAT elementMassMatrix(nNodesPerElement,nNodesPerElement);
for (int ielem = 0; ielem < nelems; ++ielem)
{
// evaluate shape functions
feMesh_->shape_function(ielem, N, weights);
// perform quadrature
elementMassMatrix = N.transMat(weights*N);
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
for (int i = 0; i < nNodesPerElement; ++i)
{
int inode = conn(i);
for (int j = 0; j < nNodesPerElement; ++j)
{
int jnode = conn(j);
mass_matrix.add(inode, jnode, elementMassMatrix(i,j));
}
}
}
}
// -------------------------------------------------------------
// compute consistent mass using given quadrature
// -------------------------------------------------------------
void FE_Engine::compute_mass_matrix(const DIAG_MAT &weights,
const SPAR_MAT &N,
SPAR_MAT &mass_matrix) const
{
int nn = N.nCols();
int nips = N.nRows();
DENS_MAT tmp_mass_matrix_local(nn,nn), tmp_mass_matrix(nn,nn);
if (nips>0) { tmp_mass_matrix_local = N.transMat(weights*N); }
// share information between processors
int count = nn*nn;
LammpsInterface::instance()->allsum(
tmp_mass_matrix_local.get_ptr(),
tmp_mass_matrix.get_ptr(), count);
// create sparse from dense
mass_matrix.reset(tmp_mass_matrix);
}
// -------------------------------------------------------------
// compute dimensionless lumped mass using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_lumped_mass_matrix(DiagonalMatrix<double> & M) const
{
// initialize
int nNodes = feMesh_->get_nNodesUnique();
M.reset(nNodes,nNodes);
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
DENS_MAT N(nNodesPerElement,nIPsPerElement);
DIAG_MAT weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// assemble lumped diagonal mass (nnodes X nnodes)
int inode = -1;
DENS_VEC weightVec(nIPsPerElement);
DENS_VEC Nvec(nNodesPerElement);
for (int ielem = 0; ielem < nelems; ++ielem)
{
// evaluate shape functions
feMesh_->shape_function(ielem, N, weights);
int nips = weights.nRows(); // different than nIPsPerElement?
weightVec = 1.;
weightVec = weights*weightVec;
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
Nvec = N*weightVec;
for (int i = 0; i < nNodesPerElement; ++i)
{
inode = conn(i);
M(inode,inode) += Nvec(i);
}
}
}
// -------------------------------------------------------------
// compute physical lumped mass using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_lumped_mass_matrix(
const Array<FieldName>& field_mask,
const FIELDS & fields,
const PhysicsModel * physicsModel,
const Array<int> &elementMaterials,
map<FieldName, DIAG_MAT>& M, // mass matrix
const Array<bool> *element_mask) const
{
int nfields = field_mask.get_length();
// initialize
int nNodes = feMesh_->get_nNodesUnique();
for (int j = 0; j < nfields; ++j) {
M[field_mask(j)].reset(nNodes,nNodes);
}
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
DENS_MAT N(nNodesPerElement,nIPsPerElement);
DIAG_MAT weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
FIELDS fieldsAtIPs, localElementFields;
DENS_MAT Nmat;
// assemble lumped diagonal mass (nnodes X nnodes)
for (int ielem = 0; ielem < nelems; ++ielem)
{
// if element is masked, skip it
if (element_mask && !(*element_mask)(ielem)) continue;
// material id
int imat = elementMaterials(ielem);
// evaluate shape functions
feMesh_->shape_function(ielem, N, weights);
int nips = weights.nRows();
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
// interpolate fields at IPs
FIELDS::const_iterator field;
for (field = fields.begin(); field != fields.end(); field++)
{
// field values at all nodes
const DENS_MAT &vI = field->second;
// field values at element nodes
DENS_MAT &vIe = localElementFields[field->first];
// field values at integration points -> to be computed
DENS_MAT &vP = fieldsAtIPs[field->first];
int numFieldDOF = vI.nCols();
vIe.reset(nNodesPerElement, numFieldDOF);
// gather local field
for (int i = 0; i < nNodesPerElement; i++) {
for (int j = 0; j < numFieldDOF; j++) {
vIe(i,j) = vI(conn(i),j);
}
}
// interpolate field at integration points
vP = N*vIe;
}
// compute energy/momentum/mass densities
FIELDS capacities;
physicsModel->M_integrand(field_mask, fieldsAtIPs, capacities, imat);
// integrate & assemble
for (int j = 0; j < nfields; ++j) {
FieldName thisFieldName = field_mask(j);
Nmat = N.transMat(weights*capacities[thisFieldName]);
for (int i = 0; i < nNodesPerElement; ++i) {
int inode = conn(i);
M[thisFieldName](inode,inode) += Nmat(i,0);// assume all dof same
}
}
}
}
// -------------------------------------------------------------
// compute physical lumped mass using given quadrature
// -------------------------------------------------------------
void FE_Engine::compute_lumped_mass_matrix(
const Array<FieldName> &field_mask,
const FIELDS &fields,
const PhysicsModel * physicsModel,
const Array<set<int> > & pointMaterialGroups,
const DIAG_MAT &weights, // NOTE use these as a mask
const SPAR_MAT &N,
map<FieldName, DIAG_MAT> &M) const // mass matrices
{
int nips = weights.nCols();
int nsd = feMesh_->get_nSpatialDimensions();
int nfields = field_mask.get_length();
int nNodes = feMesh_->get_nNodesUnique();
// initialize
FieldName thisFieldName;
map<FieldName, DIAG_MAT> M_local;
for (int j = 0; j < nfields; ++j) {
thisFieldName = field_mask(j);
M_local[thisFieldName].reset(nNodes,nNodes);
M[thisFieldName].reset(nNodes,nNodes);
}
if (nips>0) {
// compute fields at given ips
FIELDS fieldsAtIPs;
for (int j = 0; j < nfields; ++j) {
thisFieldName = field_mask(j);
const FIELD & field = (fields.find(thisFieldName))->second;
int numFieldDOF = field.nCols();
fieldsAtIPs[thisFieldName].reset(nips,numFieldDOF);
fieldsAtIPs[thisFieldName] = N*field;
}
// treat single material point sets specially
int nMatls = pointMaterialGroups.size();
int atomMatls = 0;
for (int imat = 0; imat < nMatls; imat++) {
const set<int> & indices = pointMaterialGroups(imat);
if ( indices.size() > 0) atomMatls++;
}
bool singleMaterial = ( atomMatls == 1 );
// setup data structures
FIELDS capacities;
// evaluate physics model
if (singleMaterial) {
physicsModel->M_integrand(field_mask, fieldsAtIPs, capacities);
}
else {
throw ATC_Error(0,"unimplemented function in FE_Engine::compute_mass_matrix");
}
// integrate & assemble
for (int j = 0; j < nfields; ++j) {
FieldName thisFieldName = field_mask(j);
M_local[thisFieldName].reset( // assume all columns same
column(N.transMat(weights*capacities[thisFieldName]),0) );
}
}
// Share information between processors
for (int j = 0; j < nfields; ++j) {
FieldName thisFieldName = field_mask(j);
int count = M_local[thisFieldName].size();
LammpsInterface::instance()->allsum(
M_local[thisFieldName].get_ptr(),
M[thisFieldName].get_ptr(), count);
}
}
//-----------------------------------------------------------------
// compute assembled average gradient evaluated at the nodes
//-----------------------------------------------------------------
void FE_Engine::compute_gradient_matrix(
GRAD_SHPFCN & grad_matrix) const
{
int nNodesUnique = feMesh_->get_nNodesUnique();
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
DENS_MAT N(nIPsPerElement,nNodesPerElement);
vector< DENS_MAT > dN(nsd);
vector< DENS_MAT > tmp_grad_matrix(nsd);
for (int i = 0; i < nsd; i++) {
dN[i].reset(nNodesPerElement,nNodesPerElement);
tmp_grad_matrix[i].reset(nNodesUnique,nNodesUnique);
}
DiagonalMatrix<double> weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// element wise assembly
Array<int> count(nNodesUnique); count = 0;
feMesh_->set_quadrature(FE_Element::NODAL_QUADRATURE);
for (int ielem = 0; ielem < nelems; ++ielem) {
// evaluate shape functions
feMesh_->shape_function(ielem, N, dN, weights);
int nips = weights.nRows();
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
for (int j = 0; j < nIPsPerElement; ++j) {
int jnode = conn(j); // NOTE assumes ip order is node order
count(jnode) += 1;
for (int i = 0; i < nNodesPerElement; ++i) {
int inode = conn(i);
for (int k = 0; k < nsd; ++k) {
tmp_grad_matrix[k](jnode,inode) += dN[k](j,i);
}
}
}
}
feMesh_->set_quadrature(FE_Element::GAUSSIAN_QUADRATURE); // reset to default
for (int inode = 0; inode < nNodesUnique; ++inode) {
//cout << inode << " count " << count(inode) << "\n";
for (int jnode = 0; jnode < nNodesUnique; ++jnode) {
for (int k = 0; k < nsd; ++k) {
tmp_grad_matrix[k](jnode,inode) /= count(jnode);
}
}
}
// compact dense matrices
grad_matrix.resize(nsd);
for (int k = 0; k < nsd; ++k) {
grad_matrix[k].reset(tmp_grad_matrix[k]);
}
}
// -------------------------------------------------------------
// compute energy per node using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_energy(const Array<FieldName> &mask,
const FIELDS &fields,
const PhysicsModel * physicsModel,
const Array<int> & elementMaterials,
FIELDS &energy,
const Array<bool> *element_mask) const
{
//NOTE move to initialization and make workspace
const int nNodesPerElement = feMesh_->get_nNodesPerElement();
const int nIPsPerElement = feMesh_->get_nIPsPerElement();
const int nsd = feMesh_->get_nSpatialDimensions();
const int nelems = feMesh_->get_nElements();
DENS_MAT Nmat;
//NOTE move to initialization and make workspace
DENS_MAT N(nIPsPerElement,nNodesPerElement);
vector<DENS_MAT> dN(nsd);
dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement));
FIELDS fieldsAtIPs, localElementFields;
GRAD_FIELDS grad_fieldsAtIPs;
FIELDS::const_iterator field;
FieldName thisFieldName;
int numFieldDOF;
for (int n = 0; n < mask.get_length(); n++) {
FieldName thisFieldName = mask(n);
const FIELD & field = (fields.find(thisFieldName))->second;
energy[thisFieldName].reset(field.nRows(), field.nCols());
}
DIAG_MAT weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// element wise assembly
int inode = -1;
for (int ielem=0; ielem < nelems; ielem++)
{
// if element is masked, skip it
if (element_mask && !(*element_mask)(ielem)) continue;
// material id
int imat = elementMaterials(ielem);
// evaluate shape functions
feMesh_->shape_function(ielem, N, dN, weights);
int nips = weights.nRows();
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
// compute fields and gradients of fields ips of this element
for (int n = 0; n < mask.get_length(); n++) {
FieldName thisFieldName = mask(n);
FIELDS::const_iterator field = (fields.find(thisFieldName));
// field values at all nodes
const DENS_MAT &vI = field->second;
// field values at element nodes
DENS_MAT &vIe = localElementFields[field->first];
// field values at integration points -> to be computed
DENS_MAT &vP = fieldsAtIPs[field->first];
// gradients of field at integration points -> to be computed
vector<DENS_MAT> &dvP = grad_fieldsAtIPs[field->first];
numFieldDOF = vI.nCols();
vIe.reset(nNodesPerElement, numFieldDOF);
// gather local field
for (int i = 0; i < nNodesPerElement; i++)
for (int j = 0; j < numFieldDOF; j++)
vIe(i,j) = vI(conn(i),j);
// interpolate field at integration points
vP = N*vIe;
dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF));
for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe;
}
FIELDS energies;
physicsModel->E_integrand(mask, fieldsAtIPs, grad_fieldsAtIPs, energies, imat);
// assemble
for (int n = 0; n < mask.get_length(); n++) {
FieldName thisFieldName = mask(n);
FIELDS::const_iterator field = (fields.find(thisFieldName));
numFieldDOF = (field->second).nCols();
Nmat = N.transMat(weights*energies[thisFieldName]);
for (int i = 0; i < nNodesPerElement; ++i) {
inode = conn(i);
for (int k = 0; k < numFieldDOF; ++k) {
energy[thisFieldName](inode,k) += Nmat(i,k);
}
}
}
} // element loop
} // function call
// -------------------------------------------------------------
// compute rhs using native quadrature
// -------------------------------------------------------------
void FE_Engine::compute_rhs_vector(const Array2D<bool> &rhs_mask,
const FIELDS &fields,
const PhysicsModel * physicsModel,
const Array<int> & elementMaterials,
FIELDS &rhs,
const Array<bool> *element_mask) const
{
//NOTE move to initialization and make workspace
const int nNodesPerElement = feMesh_->get_nNodesPerElement();
const int nIPsPerElement = feMesh_->get_nIPsPerElement();
const int nsd = feMesh_->get_nSpatialDimensions();
const int nelems = feMesh_->get_nElements();
DENS_MAT Nmat;
//NOTE move to initialization and make workspace
DENS_MAT N(nIPsPerElement,nNodesPerElement);
vector<DENS_MAT> dN(nsd);
dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement));
FIELDS fieldsAtIPs, localElementFields;
GRAD_FIELDS grad_fieldsAtIPs;
FIELDS::const_iterator field;
FieldName thisFieldName;
int numFieldDOF;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) {
int nrows = (field->second).nRows();
int ncols = (field->second).nCols();
rhs[thisFieldName].reset(nrows,ncols);
}
}
DIAG_MAT weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// element wise assembly
int inode = -1;
for (int ielem=0; ielem < nelems; ielem++)
{
// if element is masked, skip it
if (element_mask && !(*element_mask)(ielem)) continue;
// material id
int imat = elementMaterials(ielem);
// evaluate shape functions
feMesh_->shape_function(ielem, N, dN, weights);
int nips = weights.nRows();
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
// compute fields and gradients of fields ips of this element
for (field = fields.begin(); field != fields.end(); field++)
{
// field values at all nodes
const DENS_MAT &vI = field->second;
// field values at element nodes
DENS_MAT &vIe = localElementFields[field->first];
// field values at integration points -> to be computed
DENS_MAT &vP = fieldsAtIPs[field->first];
// gradients of field at integration points -> to be computed
vector<DENS_MAT> &dvP = grad_fieldsAtIPs[field->first];
numFieldDOF = vI.nCols();
vIe.reset(nNodesPerElement, numFieldDOF);
// gather local field
for (int i = 0; i < nNodesPerElement; i++)
for (int j = 0; j < numFieldDOF; j++)
vIe(i,j) = vI(conn(i),j);
// interpolate field at integration points
vP = N*vIe;
dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF));
for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe;
}
// NOTE move scaling of N by weights up here
// evaluate physics model
// N_fluxes is a set of fields
if(physicsModel->has_N_integrand()) {
FIELDS N_fluxes;
physicsModel->N_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, N_fluxes, imat);
// assemble
for (field = fields.begin(); field != fields.end(); field++)
{
thisFieldName = field->first;
// NOTE why is this line here?
// SOURCE should refer only to adding extrinsic coupling sources
if (!rhs_mask(thisFieldName,SOURCE)) continue;
numFieldDOF = (field->second).nCols();
Nmat = N.transMat(weights*N_fluxes[thisFieldName]);
for (int i = 0; i < nNodesPerElement; ++i) {
inode = conn(i);
for (int k = 0; k < numFieldDOF; ++k) {
rhs[thisFieldName](inode,k) += Nmat(i,k);
}
}
}
}
// evaluate Physics model
// B_fluxes is a set of field gradients
if (physicsModel->has_B_integrand()) {
GRAD_FIELDS B_fluxes;
physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes, imat);
// assemble
for (field = fields.begin(); field != fields.end(); field++)
{
thisFieldName = field->first;
numFieldDOF = (field->second).nCols();
if (!rhs_mask(thisFieldName,FLUX)) continue;
for (int j = 0; j < nsd; j++)
{
Nmat = dN[j].transMat(weights*B_fluxes[thisFieldName][j]);
for (int i = 0; i < nNodesPerElement; ++i)
{
inode = conn(i);
for (int k = 0; k < numFieldDOF; ++k)
{
rhs[thisFieldName](inode,k) += Nmat(i,k);
} // scatter k
} // scatter i
} // loop over nsd
} //loop on fields
} // if B_integrand
} // element loop
} // function call
// -------------------------------------------------------------
// compute rhs using given quadrature
// -------------------------------------------------------------
void FE_Engine::compute_rhs_vector(const Array2D<bool> &rhs_mask,
const FIELDS &fields,
const PhysicsModel *physicsModel,
const Array<set<int> >&pointMaterialGroups,
const DIAG_MAT &weights,
const SPAR_MAT &N,
const GRAD_SHPFCN &dN,
FIELDS &rhs) const
{
int nips = weights.nCols();
int nNodesUnique = feMesh_->get_nNodesUnique();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
FieldName thisFieldName;
FIELDS::const_iterator field;
FIELDS rhs_local;
for (field = rhs.begin(); field != rhs.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) {
int nrows = (field->second).nRows();
int ncols = (field->second).nCols();
rhs [thisFieldName].reset(nrows,ncols);
rhs_local[thisFieldName].reset(nrows,ncols);
}
}
if (nips>0) {
// compute fields and gradients of fields at given ips
GRAD_FIELDS grad_fieldsAtIPs;
FIELDS fieldsAtIPs;
int numFieldDOF;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
const FIELD & field = (fields.find(thisFieldName))->second;
numFieldDOF = field.nCols();
grad_fieldsAtIPs[thisFieldName].assign(nsd,DENS_MAT(nips,numFieldDOF));
fieldsAtIPs[thisFieldName].reset(nips,numFieldDOF);
fieldsAtIPs[thisFieldName] = N*field;
for (int j = 0; j < nsd; ++j) {
grad_fieldsAtIPs[thisFieldName][j] = dN[j]*field;
}
}
// treat single material point sets specially
int nMatls = pointMaterialGroups.size();
int atomMatls = 0;
for (int imat = 0; imat < nMatls; imat++) {
const set<int> & indices = pointMaterialGroups(imat);
if ( indices.size() > 0) atomMatls++;
}
bool singleMaterial = ( atomMatls == 1 );
// setup data structures
FIELDS N_fluxes;
GRAD_FIELDS B_fluxes;
if(physicsModel->has_B_integrand()) {
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int index = (int) thisFieldName;
if ( rhs_mask(index,FLUX) ) {
B_fluxes[thisFieldName].reserve(nsd);
}
}
}
// evaluate physics model
if (singleMaterial)
{
if(physicsModel->has_N_integrand()) {
physicsModel->N_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs,
N_fluxes);
}
if(physicsModel->has_B_integrand()) {
physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs,
B_fluxes);
}
}
else
{
// NOTE this copy in fields/copy out fluxes is extremely slow
// NOTE alternates:
// 1) permanent workspace with per-row mapped clones per matl
// from caller/atc
// 2) per point calls to N/B_integrand
// 3) collect internalToAtom into materials and use mem-cont clones
// what about dof for matrices and data storage: clone _matrix_
// for each material group:
// set up storage
FIELDS groupN_fluxes, fieldsGroup;
GRAD_FIELDS groupB_fluxes, grad_fieldsGroup;
if(physicsModel->has_B_integrand()) {
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int index = (int) thisFieldName;
int ndof = (field->second).nCols();
grad_fieldsGroup[thisFieldName].assign(nsd,DENS_MAT(nips,ndof));
N_fluxes[thisFieldName].reset(nips,ndof);
B_fluxes[thisFieldName].assign(nsd,DENS_MAT(nips,ndof));
if ( rhs_mask(index,FLUX) ) {
groupB_fluxes[thisFieldName].reserve(nsd);
}
}
}
// copy fields
for ( int imat = 0; imat < pointMaterialGroups.get_length(); imat++)
{
const set<int> & indices = pointMaterialGroups(imat);
int npts = indices.size();
int i = 0;
for (set<int>::const_iterator iter=indices.begin();
iter != indices.end(); iter++, i++ ) {
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int ndof = (field->second).nCols();
fieldsGroup[thisFieldName].reset(npts,ndof);
for (int j = 0; j < nsd; ++j) {
(grad_fieldsGroup[thisFieldName][j]).reset(npts,ndof);
}
for (int dof = 0; dof < ndof; ++dof) {
fieldsGroup[thisFieldName](i,dof)
= fieldsAtIPs[thisFieldName](*iter,dof);
for (int j = 0; j < nsd; ++j) {
grad_fieldsGroup[thisFieldName][j](i,dof)
= grad_fieldsAtIPs[thisFieldName][j](*iter,dof);
}
}
}
}
// calculate forces, & assemble
if(physicsModel->has_N_integrand()) {
physicsModel->N_integrand(rhs_mask, fieldsGroup, grad_fieldsGroup,
groupN_fluxes, imat);
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int index = (int) thisFieldName;
int ndof = (field->second).nCols();
if ( rhs_mask(index,SOURCE) ) {
int i = 0;
for (set<int>::const_iterator iter=indices.begin();
iter != indices.end(); iter++, i++ ) {
for (int dof = 0; dof < ndof; ++dof) {
N_fluxes[thisFieldName](*iter,dof)
= groupN_fluxes[thisFieldName](i,dof);
}
}
}
}
}
// calculate forces, & assemble
if(physicsModel->has_B_integrand()) {
physicsModel->B_integrand(rhs_mask, fieldsGroup, grad_fieldsGroup,
groupB_fluxes, imat);
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int index = (int) thisFieldName;
int ndof = (field->second).nCols();
if ( rhs_mask(index,FLUX) ) {
int i = 0;
for (set<int>::const_iterator iter=indices.begin();
iter != indices.end(); iter++, i++ ) {
for (int dof = 0; dof < ndof; ++dof) {
for (int j = 0; j < nsd; ++j) {
B_fluxes[thisFieldName][j](*iter,dof)
= groupB_fluxes[thisFieldName][j](i,dof);
}
}
}
}
}
}
}
}
// assemble
for (field = fields.begin(); field != fields.end(); field++)
{
thisFieldName = field->first;
int index = (int) thisFieldName;
if(physicsModel->has_N_integrand()) {
if ( rhs_mask(index,SOURCE) ) {
rhs_local[thisFieldName]
+= N.transMat(weights*N_fluxes[thisFieldName]);
}
}
if(physicsModel->has_B_integrand()) {
if ( rhs_mask(index,FLUX) ) {
for (int j = 0; j < nsd; ++j) {
rhs_local[thisFieldName]
+= dN[j].transMat(weights*B_fluxes[thisFieldName][j]);
}
}
}
}
}
// Share information between processors
for (field = rhs.begin(); field != rhs.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) {
int count = rhs_local[thisFieldName].size();
//rhs_local[thisFieldName].print("RHS LCL");
LammpsInterface::instance()->allsum(
rhs_local[thisFieldName].get_ptr(),
rhs[thisFieldName].get_ptr(), count);
}
}
}
// -------------------------------------------------------------
// compute flux for post processing
// -------------------------------------------------------------
void FE_Engine::compute_flux(const Array2D<bool> &rhs_mask,
const FIELDS &fields,
const PhysicsModel * physicsModel,
const Array<int> & elementMaterials,
GRAD_FIELDS &flux,
const Array<bool> *element_mask) const
{
//NOTE move to initialization and make workspace
const int nNodesPerElement = feMesh_->get_nNodesPerElement();
const int nIPsPerElement = feMesh_->get_nIPsPerElement();
const int nsd = feMesh_->get_nSpatialDimensions();
const int nelems = feMesh_->get_nElements();
DENS_MAT Nmat;
//NOTE move to initialization and make workspace
DENS_MAT N(nIPsPerElement,nNodesPerElement);
vector<DENS_MAT> dN(nsd);
dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement));
FIELDS fieldsAtIPs, localElementFields;
GRAD_FIELDS grad_fieldsAtIPs;
FIELDS::const_iterator field;
FieldName thisFieldName;
int numFieldDOF;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX)) {
int nrows = (field->second).nRows();
int ncols = (field->second).nCols();
flux[thisFieldName].assign(nsd, DENS_MAT(nrows,ncols));
}
}
DIAG_MAT weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// element wise assembly
int inode = -1;
for (int ielem=0; ielem < nelems; ielem++)
{
// if element is masked, skip it
if (element_mask && !(*element_mask)(ielem)) continue;
// material id
int imat = elementMaterials(ielem);
// evaluate shape functions
feMesh_->shape_function(ielem, N, dN, weights);
int nips = weights.nRows();
// get connectivity
feMesh_->element_connectivity_unique(ielem, conn);
// compute fields and gradients of fields ips of this element
for (field = fields.begin(); field != fields.end(); field++)
{
// field values at all nodes
const DENS_MAT &vI = field->second;
// field values at element nodes
DENS_MAT &vIe = localElementFields[field->first];
// field values at integration points -> to be computed
DENS_MAT &vP = fieldsAtIPs[field->first];
// gradients of field at integration points -> to be computed
vector<DENS_MAT> &dvP = grad_fieldsAtIPs[field->first];
numFieldDOF = vI.nCols();
vIe.reset(nNodesPerElement, numFieldDOF);
// gather local field
for (int i = 0; i < nNodesPerElement; i++)
for (int j = 0; j < numFieldDOF; j++)
vIe(i,j) = vI(conn(i),j);
// interpolate field at integration points
vP = N*vIe;
dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF));
for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe;
}
// NOTE move scaling of N by weights up here
// evaluate Physics model
// B_fluxes is a set of field gradients
if (physicsModel->has_B_integrand()) {
GRAD_FIELDS B_fluxes;
physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes, imat);
// assemble
for (field = fields.begin(); field != fields.end(); field++)
{
thisFieldName = field->first;
numFieldDOF = (field->second).nCols();
if (!rhs_mask(thisFieldName,FLUX)) continue;
for (int j = 0; j < nsd; j++)
{
Nmat = N.transMat(weights*B_fluxes[thisFieldName][j]);
for (int i = 0; i < nNodesPerElement; ++i)
{
inode = conn(i);
for (int k = 0; k < numFieldDOF; ++k)
{
flux[thisFieldName][j](inode,k) += Nmat(i,k);
} // scatter k
} // scatter i
} // loop over nsd
} //loop on fields
} // if B_integrand
} // element loop
} // function call
//-----------------------------------------------------------------
// boundary flux using native quadrature
//-----------------------------------------------------------------
void FE_Engine::compute_boundary_flux(
const Array2D<bool> & rhs_mask,
const FIELDS & fields,
const PhysicsModel * physicsModel,
const Array<int> & elementMaterials,
const set< pair <int,int> > & faceSet,
FIELDS & rhs) const
{
//NOTE move to initialization and make workspace
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerFace = feMesh_->get_nIPsPerFace();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
DENS_MAT Nmat(nNodesPerElement,3);
FieldName thisFieldName;
FIELDS::const_iterator field;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX)) {
int nrows = (field->second).nRows();
int ncols = (field->second).nCols();
rhs [thisFieldName].reset(nrows,ncols);
}
}
//NOTE move to initialization and make workspace
// sizing working arrays
DENS_MAT N(nIPsPerFace,nNodesPerElement);
vector< DENS_MAT > dN(nsd), nN(nsd);
for (int i = 0; i < nsd; i++) {
dN[i].reset(nIPsPerFace,nNodesPerElement);
nN[i].reset(nIPsPerFace,nNodesPerElement);
}
GRAD_FIELDS grad_fieldsAtIPs;
FIELDS fieldsAtIPs;
FIELDS localElementFields;
int numFieldDOF;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
numFieldDOF = field->second.nCols();
grad_fieldsAtIPs[thisFieldName].reserve(nsd);
for (int i = 0; i < nsd; ++i) {
grad_fieldsAtIPs[thisFieldName].push_back(DENS_MAT(nIPsPerFace,numFieldDOF));
}
fieldsAtIPs[thisFieldName].reset(nIPsPerFace,numFieldDOF);
localElementFields[thisFieldName].reset(nNodesPerElement,numFieldDOF);
}
DiagonalMatrix<double> weights(nIPsPerFace,nIPsPerFace);
Array<int> conn(nNodesPerElement);
// element wise assembly
int inode = -1;
set< pair <int,int> >::iterator iter;
for (iter = faceSet.begin(); iter != faceSet.end(); iter++) {
// evaluate shape functions at ips
feMesh_->face_shape_function(*iter, N, dN, nN, weights);
int nips = weights.nRows();
// get connectivity
int ielem = iter->first;
int imat = elementMaterials(ielem);
feMesh_->element_connectivity_unique(ielem, conn);
// interpolate fields and gradients of fields ips of this element
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
const DenseMatrix<double> * thisField
= (const DENS_MAT *) &(field->second);
numFieldDOF = thisField->nCols();
//NOTE an hopefully unnecessary copy (should alias)
for (int i = 0; i < nNodesPerElement; i++) {
for (int j = 0; j < numFieldDOF; j++) {
localElementFields[thisFieldName](i,j) = (*thisField)(conn(i),j);
}
}
// ips X dof = ips X nodes * nodes X dof
fieldsAtIPs[thisFieldName] = N*localElementFields[thisFieldName];
for (int j = 0; j < nsd; ++j) {
grad_fieldsAtIPs[thisFieldName][j] = dN[j]*localElementFields[thisFieldName];
}
}
// Evaluate- physics model
// do nothing for N_integrand
// nN : precomputed and held by ATC_Transfer
if(physicsModel->has_B_integrand()) {
map<FieldName, vector< DENS_MAT > > B_fluxes;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
if ( rhs_mask(thisFieldName,FLUX) ) {
B_fluxes[thisFieldName].reserve(nsd);
}
}
physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes,imat);
// assemble
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
int index = (int) thisFieldName;
const DenseMatrix<double> * thisField
= (const DENS_MAT *) &(field->second);
if ( rhs_mask(index,FLUX) ) {
numFieldDOF = thisField->nCols();
Nmat.reset(nNodesPerElement,numFieldDOF);
for (int j = 0; j < nsd; j++) {
// nodes X dof = nodes X ips * ips X dof ???
Nmat = nN[j].transMat(weights*B_fluxes[thisFieldName][j]);
for (int i = 0; i < nNodesPerElement; ++i) {
inode = conn(i);
for (int k = 0; k < numFieldDOF; ++k) {
rhs[thisFieldName](inode,k) += Nmat(i,k);
}
}
}
}
}
}
}
}
// -------------------------------------------------------------
// compute boundary flux using given quadrature and interpolation
// -------------------------------------------------------------
void FE_Engine::compute_boundary_flux(
const Array2D<bool> & rhs_mask,
const FIELDS & fields,
const PhysicsModel * physicsModel,
const Array< int > & elementMaterials,
const Array< set<int> > & pointMaterialGroups,
const DIAG_MAT & weights,
const SPAR_MAT & N,
const GRAD_SHPFCN & dN,
const DIAG_MAT & flux_mask,
FIELDS & flux) const
{
int nNodesUnique = feMesh_->get_nNodesUnique();
FieldName thisFieldName;
map<FieldName, DENS_MAT > rhs;
map<FieldName, DENS_MAT > rhsSubset;
map<FieldName, DENS_MAT > rhsSubset_local;
FIELDS::const_iterator field;
for (field = fields.begin(); field != fields.end(); field++) {
thisFieldName = field->first;
if (rhs_mask(thisFieldName,FLUX)) {
int nrows = (field->second).nRows();
int ncols = (field->second).nCols();
rhs [thisFieldName].reset(nrows,ncols);
rhsSubset[thisFieldName].reset(nrows,ncols);
}
}
// R_I = - int_Omega Delta N_I .q dV
compute_rhs_vector(rhs_mask, fields, physicsModel, elementMaterials, rhs);
// R_I^md = - int_Omega^md Delta N_I .q dV
compute_rhs_vector(rhs_mask, fields, physicsModel, pointMaterialGroups,
weights, N, dN, rhsSubset);
// flux_I = 1/Delta V_I V_I^md R_I + R_I^md
// where : Delta V_I = int_Omega N_I dV
for (int n = 0; n < NUM_FIELDS; ++n) {
FieldName thisFieldName = (FieldName) n;
if ( rhs_mask(thisFieldName,FLUX) ) {
flux[thisFieldName]
= rhsSubset[thisFieldName] - flux_mask*rhs[thisFieldName];
}
}
}
//-----------------------------------------------------------------
// prescribed boundary flux using native quadrature
// integrate \int_delV N_I s(x,t).n dA
//-----------------------------------------------------------------
void FE_Engine::add_fluxes(const Array<bool> &fieldMask,
const double time,
const SURFACE_SOURCE & sourceFunctions,
FIELDS &nodalSources) const
{
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nNodesPerFace = feMesh_->get_nNodesPerFace();
int nIPsPerFace = feMesh_->get_nIPsPerFace();
int nsd = feMesh_->get_nSpatialDimensions();
FieldName thisField;
int nFieldDOF = 1;
XT_Function * f = NULL;
//NOTE move arrays to initialization and make workspace
// sizing working arrays
vector<DENS_MAT> dN, nN;
dN.assign(nsd, DENS_MAT(nIPsPerFace, nNodesPerElement));
nN.assign(nsd, DENS_MAT(nIPsPerFace, nNodesPerElement));
DENS_MAT N(nIPsPerFace,nNodesPerElement);
DENS_MAT xCoords(nsd,nNodesPerElement);
DENS_MAT xAtIPs(nsd,nIPsPerFace);
DENS_MAT Nmat(nNodesPerElement,nsd);
DIAG_MAT weights(nIPsPerFace,nIPsPerFace);
Array<int> conn(nNodesPerElement);
DENS_MAT faceSource(nIPsPerFace,nFieldDOF);
// element wise assembly
SURFACE_SOURCE::const_iterator src_iter;
for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++)
{
FieldName thisFieldName = src_iter->first;
if (!fieldMask((int)thisFieldName)) continue;
typedef map<PAIR,Array<XT_Function*> > FSET;
const FSET *fset = (const FSET *)&(src_iter->second);
FSET::const_iterator fset_iter;
for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++)
{
const PAIR &face = fset_iter->first;
const int elem = face.first;
const Array <XT_Function*> &fs = fset_iter->second;
feMesh_->element_connectivity_unique(elem, conn);
// evaluate location at ips
feMesh_->face_shape_function(face, N, dN, nN, weights);
feMesh_->element_coordinates(elem, xCoords);
MultAB(xCoords,N,xAtIPs,0,1); //xAtIPs = xCoords*(N.transpose());
// interpolate prescribed flux at ips of this element
// NOTE this seems totally redundant
FSET::const_iterator face_iter = fset->find(face);
if (face_iter == fset->end())
{
cout << "face not found" << std::endl;
// NOTE: do something there
}
// NOTE why can't I know the size earlier??
// NOTE only normal flux here
nFieldDOF = (face_iter->second).size();
faceSource.reset(nIPsPerFace,nFieldDOF);
for (int ip = 0; ip < nIPsPerFace; ++ip)
{
for (int idof = 0; idof<nFieldDOF; ++idof)
{
f = fs(idof);
if (!f) continue;
faceSource(ip,idof) = f->f(column(xAtIPs,ip).get_ptr(),time);
}
}
// assemble
Nmat = N.transMat(weights*faceSource);
for (int i = 0; i < nNodesPerElement; ++i)
{
int inode = conn(i);
for (int idof = 0; idof < nFieldDOF; ++idof)
{
nodalSources[thisFieldName](inode,idof) += Nmat(i,idof);
} // end assemble nFieldDOF
} // end assemble nNodesPerElement
} // end fset loop
} // field loop
}
//-----------------------------------------------------------------
// prescribed volume flux using native quadrature
// integrate \int_V N_I s(x,t) dV
//-----------------------------------------------------------------
void FE_Engine::add_sources(const Array<bool> &fieldMask,
const double time,
const VOLUME_SOURCE &sourceFunctions,
FIELDS &nodalSources) const
{
int nNodes = feMesh_->get_nNodes();
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerElement = feMesh_->get_nIPsPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
int nFieldDOF = 1;
XT_Function * f = NULL;
//NOTE move arrays to initialization and make workspace
// sizing working arrays
DENS_MAT elemSource(nIPsPerElement,nFieldDOF);
DENS_MAT N(nIPsPerElement,nNodesPerElement);
DENS_MAT xCoords(nsd,nNodesPerElement);
DENS_MAT xAtIPs(nsd,nIPsPerElement);
DENS_MAT Nwg(nNodesPerElement,1);
DENS_MAT Nmat(nNodesPerElement,nsd);
DiagonalMatrix<double> weights(nIPsPerElement,nIPsPerElement);
Array<int> conn(nNodesPerElement);
// element wise assembly
for (int ielem = 0; ielem < nelems; ++ielem) {
feMesh_->element_connectivity_unique(ielem, conn);
// evaluate location at ips
feMesh_->shape_function(ielem, N, weights);
feMesh_->element_coordinates(ielem, xCoords);
xAtIPs =xCoords*(N.transpose());
VOLUME_SOURCE ::const_iterator src_iter;
for (src_iter = sourceFunctions.begin();
src_iter != sourceFunctions.end(); src_iter++) {
FieldName thisField = src_iter->first;
int index = (int) thisField;
if ( fieldMask(index) ) {
const Array2D<XT_Function *> * thisSource
= (const Array2D<XT_Function *> *) &(src_iter->second);
nFieldDOF = thisSource->nCols();
elemSource.reset(nIPsPerElement,nFieldDOF);
// interpolate prescribed flux at ips of this element
for (int ip = 0; ip < nIPsPerElement; ++ip) {
for (int idof = 0; idof < nFieldDOF; ++idof) {
f = (*thisSource)(ielem,idof);
if (f) {
elemSource(ip,idof) = f->f(column(xAtIPs,ip).get_ptr(),time);
}
}
}
// assemble
Nmat.reset(nNodesPerElement,nFieldDOF);
Nmat = N.transMat(weights*elemSource);
for (int i = 0; i < nNodesPerElement; ++i) {
int inode = conn(i);
for (int idof = 0; idof < nFieldDOF; ++idof) {
nodalSources[thisField](inode,idof) += Nmat(i,idof);
}
}
}
}
}
}
//-----------------------------------------------------------------
// boundary integral of a nodal field
//-----------------------------------------------------------------
void FE_Engine::field_surface_flux(
const DENS_MAT & field,
const set< PAIR > & faceSet,
DENS_MAT & values,
const bool contour,
const int axis) const
{
int nNodesPerElement = feMesh_->get_nNodesPerElement();
int nIPsPerFace = feMesh_->get_nIPsPerFace();
int nsd = feMesh_->get_nSpatialDimensions();
int nelems = feMesh_->get_nElements();
int numFieldDOF = field.nCols();
double a[3] = {0,0,0};
a[axis] = 1;
// sizing working arrays
DENS_MAT N(nIPsPerFace,nNodesPerElement);
DENS_MAT n(nsd,nIPsPerFace);
DENS_MAT fieldsAtIPs(nIPsPerFace,numFieldDOF);
DENS_MAT localElementFields(nNodesPerElement,numFieldDOF);
DiagonalMatrix<double> weights(nIPsPerFace,nIPsPerFace);
Array<int> conn(nNodesPerElement);
DENS_MAT Nmat(nsd,numFieldDOF);
DENS_MAT integrals(numFieldDOF,nsd);
// element wise assembly
set< pair <int,int> >::iterator iter;
for (iter = faceSet.begin(); iter != faceSet.end(); iter++) {
// evaluate shape functions at ips
feMesh_->face_shape_function(*iter, N, n, weights);
// cross n with axis to get tangent
if (contour) {
double t[3];
for (int i = 0; i < nIPsPerFace; i++) {
t[0] = a[1]*n(2,i) - a[2]*n(1,i);
t[1] = a[2]*n(0,i) - a[0]*n(2,i);
t[2] = a[0]*n(1,i) - a[1]*n(0,i);
n(0,i) = t[0];
n(1,i) = t[1];
n(2,i) = t[2];
}
}
int nips = weights.nRows();
// get connectivity
int ielem = iter->first;
feMesh_->element_connectivity_unique(ielem, conn);
// interpolate fields and gradients of fields ips of this element
for (int i = 0; i < nNodesPerElement; i++) {
for (int j = 0; j < numFieldDOF; j++) {
localElementFields(i,j) = field(conn(i),j);
}
}
// ips X dof = ips X nodes * nodes X dof
fieldsAtIPs = N*localElementFields;
// assemble : integral(k,j) = sum_ip n(j,ip) wg(ip,ip) field(ip,k)
Nmat = n*weights*fieldsAtIPs;
for (int j = 0; j < nsd; j++) {
for (int k = 0; k < numFieldDOF; ++k) {
integrals(k,j) += Nmat(j,k);
}
}
}
// (S.n)_1 = S_1j n_j = S_11 n_1 + S_12 n_2 + S_13 n_3
// (S.n)_2 = S_2j n_j = S_21 n_1 + S_22 n_2 + S_23 n_3
// (S.n)_3 = S_3j n_j = S_31 n_1 + S_32 n_2 + S_33 n_3
if (numFieldDOF == 9) { // tensor
values.reset(nsd,1);
values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2);
values(1,0) = integrals(3,0)+integrals(4,1)+integrals(5,2);
values(2,0) = integrals(6,0)+integrals(7,1)+integrals(8,2);
}
else if (numFieldDOF == 6) { // sym tensor
values.reset(nsd,1);
values(0,0) = integrals(0,0)+integrals(3,1)+integrals(4,2);
values(1,0) = integrals(3,0)+integrals(1,1)+integrals(5,2);
values(2,0) = integrals(4,1)+integrals(5,1)+integrals(2,2);
}
// (v.n) = v_j n_j
else if (numFieldDOF == 3) { // vector
values.reset(1,1);
values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2);
}
// s n = s n_j e_j
else if (numFieldDOF == 1) { // scalar
values.reset(nsd,1);
values(0,0) = integrals(0,0);
values(1,0) = integrals(0,1);
values(2,0) = integrals(0,2);
}
else {
string msg = "FE_Engine::field_surface_flux unsupported field width: ";
msg += tostring(numFieldDOF);
throw ATC_Error(0,msg);
}
}
//-----------------------------------------------------------------
// evaluate shape functions at given points
//-----------------------------------------------------------------
void FE_Engine::evaluate_shape_functions(
const MATRIX & pt_coords,
SPAR_MAT & N,
Array<int> & pointToEltMap) const
{
// Get shape function and derivatives at atomic locations
int npe = feMesh_->get_nNodesPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nnodes = feMesh_->get_nNodesUnique();
int npts = pt_coords.nCols();
pointToEltMap.reset(npts);
// loop over point (atom) coordinates
DENS_VEC x(nsd);
Array<int> node_index(npe);
DENS_VEC shp(npe);
N.reset(npts,nnodes);
for (int i = 0; i < npts; ++i) {
for (int k = 0; k < nsd; ++k) {
x(k) = pt_coords(k,i);
}
int eltID;
feMesh_->shape_functions(x,shp,eltID,node_index);
for (int j = 0; j < npe; ++j) {
int jnode = node_index(j);
N.add(i,jnode,shp(j));
}
pointToEltMap(i) = eltID;
}
N.compress();
}
//-----------------------------------------------------------------
// evaluate shape functions and their spatial derivatives at given points
//-----------------------------------------------------------------
void FE_Engine::evaluate_shape_functions(
const MATRIX & pt_coords,
SPAR_MAT & N,
GRAD_SHPFCN & dN,
Array<int> & pointToEltMap) const
{
// Get shape function and derivatives at atomic locations
int npe = feMesh_->get_nNodesPerElement();
int nsd = feMesh_->get_nSpatialDimensions();
int nnodes = feMesh_->get_nNodesUnique();
int npts = pt_coords.nCols();
pointToEltMap.reset(npts);
// loop over point (atom) coordinates
DENS_VEC x(nsd);
Array<int> node_index(npe);
DENS_VEC shp(npe);
DENS_MAT dshp(nsd,npe);
for (int k = 0; k < nsd; ++k) {
dN[k].reset(npts,nnodes);
}
N.reset(npts,nnodes);
for (int i = 0; i < npts; ++i) {
for (int k = 0; k < nsd; ++k) {
x(k) = pt_coords(k,i);
}
int eltID;
feMesh_->shape_functions(x,shp,dshp,eltID,node_index);
for (int j = 0; j < npe; ++j) {
int jnode = node_index(j);
N.add(i,jnode,shp(j));
for (int k = 0; k < nsd; ++k) {
dN[k].add(i,jnode,dshp(k,j));
}
}
pointToEltMap(i) = eltID;
}
N.compress();
for (int k = 0; k < nsd; ++k) {
dN[k].compress();
}
}
//-----------------------------------------------------------------
// evaluate shape functions at given points
//-----------------------------------------------------------------
void FE_Engine::evaluate_shape_functions(const VECTOR & x,
Array<int>& node_index,
DENS_VEC& shp,
DENS_MAT& dshp,
int & eltID) const
{
// Get shape function and derivatives at a specific point
int nsd = feMesh_->get_nSpatialDimensions();
int npe = feMesh_->get_nNodesPerElement();
dshp.reset(nsd,npe);
feMesh_->shape_functions(x,shp,dshp,eltID,node_index);
}
//-----------------------------------------------------------------
// create a uniform rectangular mesh on a rectangular region
//-----------------------------------------------------------------
void FE_Engine::create_mesh(int nx, int ny, int nz, char * regionName,
int xperiodic, int yperiodic, int zperiodic)
{
double xmin, xmax, ymin, ymax, zmin, zmax;
double xscale, yscale, zscale;
double boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi;
// check to see if region exists and is it a box, and if so get the bounds
bool isBox;
isBox = atcTransfer_->get_region_bounds(regionName,
xmin, xmax,
ymin, ymax,
zmin, zmax,
xscale,
yscale,
zscale);
if (!isBox) throw ATC_Error(0,"Region for FE mesh is not a box");
feMesh_ = new FE_Uniform3DMesh(nx,ny,nz,
xmin,xmax,
ymin,ymax,
zmin,zmax,
xscale,
yscale,
zscale,
xperiodic,
yperiodic,
zperiodic);
if (LammpsInterface::instance()->comm_rank()==0) {
printf(" ATC:: created FEM Mesh with %i Global Nodes, %i Unique Nodes, and %i Elements\n",
feMesh_->get_nNodes(),feMesh_->get_nNodesUnique(),feMesh_->get_nElements()); }
}
};
Event Timeline
Log In to Comment