Files
IFEM/src/SIM/SIMbase.C
akva feb5548931 added: ability to specify which patch to dump the basis for
will be useful when storing the geometry within the HDF5 file

git-svn-id: http://svn.sintef.no/trondheim/IFEM/trunk@1018 e10b68d5-8a6e-419e-a041-bce267b0401d
2015-07-09 09:43:31 +02:00

1571 lines
41 KiB
C

// $Id$
//==============================================================================
//!
//! \file SIMbase.C
//!
//! \date Dec 08 2009
//!
//! \author Knut Morten Okstad / SINTEF
//!
//! \brief Base class for NURBS-based FEM simulators.
//!
//==============================================================================
#include "SIMbase.h"
#include "ASMbase.h"
#ifdef PARALLEL_PETSC
#include "SAMpatchPara.h"
#include "petscksp.h"
#else
#include "SAMpatch.h"
#endif
#include "IntegrandBase.h"
#include "AlgEqSystem.h"
#include "LinSolParams.h"
#include "GlbNorm.h"
#include "ElmNorm.h"
#include "AnaSol.h"
#include "Tensor.h"
#include "Vec3Oper.h"
#include "EigSolver.h"
#include "Profiler.h"
#include "ElementBlock.h"
#include "VTF.h"
#include "Utilities.h"
#include <iomanip>
#include <ctype.h>
#include <stdio.h>
SIMbase::Discretization SIMbase::discretization = SIMbase::Spline;
bool SIMbase::ignoreDirichlet = false;
int SIMbase::num_threads_SLU = 1;
SIMbase::SIMbase ()
{
myProblem = 0;
mySol = 0;
myVtf = 0;
myEqSys = 0;
mySam = 0;
mySolParams = 0;
nGlPatches = 0;
MPCLess::compareSlaveDofOnly = true; // to avoid multiple slave definitions
#ifdef PARALLEL_PETSC
// In parallel simulations, we need to retain all DOFs in the equation system.
// The fixed DOFs (if any) will receive a homogeneous constraint instead.
ASMbase::fixHomogeneousDirichlet = false;
#endif
}
SIMbase::~SIMbase ()
{
#ifdef SP_DEBUG
std::cout <<"\nEntering SIMbase destructor"<< std::endl;
#endif
if (myProblem) delete myProblem;
if (mySol) delete mySol;
if (myVtf) delete myVtf;
if (myEqSys) delete myEqSys;
if (mySam) delete mySam;
if (mySolParams) delete mySolParams;
for (FEModelVec::iterator i1 = myModel.begin(); i1 != myModel.end(); i1++)
delete *i1;
for (SclFuncMap::iterator i2 = myScalars.begin(); i2 != myScalars.end(); i2++)
delete i2->second;
for (VecFuncMap::iterator i3 = myVectors.begin(); i3 != myVectors.end(); i3++)
delete i3->second;
for (TracFuncMap::iterator i4 = myTracs.begin(); i4 != myTracs.end(); i4++)
delete i4->second;
#ifdef SP_DEBUG
std::cout <<"Leaving SIMbase destructor"<< std::endl;
#endif
}
bool SIMbase::parse (char* keyWord, std::istream& is)
{
if (!strncasecmp(keyWord,"LINEARSOLVER",12))
this->readLinSolParams(is,atoi(keyWord+12));
else if (!strncasecmp(keyWord,"PARTITIONING",12))
{
int nproc = atoi(keyWord+12);
if (myPid == 0)
std::cout <<"\nNumber of partitions: "<< nproc << std::endl;
char* cline = 0;
nGlPatches = 0;
for (int i = 0; i < nproc && (cline = utl::readLine(is)); i++)
{
int proc = atoi(strtok(cline," "));
int first = atoi(strtok(NULL," "));
int last = atoi(strtok(NULL," "));
if (last > nGlPatches) nGlPatches = last;
if (proc == myPid && last >= first)
{
myPatches.reserve(last-first+1);
for (int j = first; j <= last; j++)
myPatches.push_back(j);
}
}
if (myPatches.empty())
{
std::cerr <<" *** SIMbase::parse: No partitioning input for processor: "
<< myPid << std::endl;
return false;
}
}
else if (!strncasecmp(keyWord,"RESULTPOINTS",12))
{
int nres = atoi(keyWord+12);
if (myPid == 0 && nres > 0)
std::cout <<"\nNumber of result points: "<< nres;
char* cline = 0;
myPoints.resize(nres);
for (int i = 0; i < nres && (cline = utl::readLine(is)); i++)
{
myPoints[i].patch = atoi(strtok(cline," "));
if (myPid == 0)
std::cout <<"\n\tPoint "<< i+1 <<": P"<< myPoints[i].patch <<" xi =";
for (int j = 0; j < 3 && (cline = strtok(NULL," ")); j++)
{
myPoints[i].par[j] = atof(cline);
if (myPid == 0)
std::cout <<' '<< myPoints[i].par[j];
}
}
if (myPid == 0 && nres > 0)
std::cout << std::endl;
}
else if (isalpha(keyWord[0]))
std::cerr <<" *** SIMbase::parse: Unknown keyword: "<< keyWord << std::endl;
return true;
}
void SIMbase::readLinSolParams (std::istream& is, int npar)
{
if (!mySolParams)
mySolParams = new LinSolParams();
mySolParams->read(is,npar);
}
bool SIMbase::createFEMmodel ()
{
for (size_t i = 0; i < myModel.size(); i++)
if (!myModel[i]->generateFEMTopology())
return false;
if (nGlPatches == 0 && nProc == 1)
nGlPatches = myModel.size();
return true;
}
int SIMbase::getLocalPatchIndex (int patchNo) const
{
if (patchNo < 1 || (patchNo > nGlPatches && nGlPatches > 0))
{
std::cerr <<" *** SIMbase::getLocalPatchIndex: Patch number "<< patchNo
<<" out of range [1,"<< nGlPatches <<"]"<< std::endl;
return -1;
}
else if (myPatches.empty() || nProc == 1)
return patchNo;
for (size_t i = 0; i < myPatches.size(); i++)
if (myPatches[i] == patchNo)
return 1+i;
return 0;
}
bool SIMbase::preprocess (const std::vector<int>& ignoredPatches, bool fixDup)
{
if (!this->createFEMmodel()) return false;
// Erase all patches that should be ignored in the analysis
std::vector<int>::const_iterator it;
for (it = ignoredPatches.begin(); it != ignoredPatches.end(); it++)
if (*it > 0 && (size_t)*it <= myModel.size())
myModel[*it-1]->clear();
// If material properties are specified for at least one patch, assign the
// property code 999999 to all patches with no material property code yet
std::vector<int> pchWthMat;
for (PropertyVec::const_iterator p = myProps.begin(); p != myProps.end(); p++)
if (p->pcode == Property::MATERIAL && !myModel[p->patch-1]->empty())
pchWthMat.push_back(p->patch-1);
if (!pchWthMat.empty())
for (size_t i = 0; i < myModel.size(); i++)
if (!myModel[i]->empty())
if (std::find(pchWthMat.begin(),pchWthMat.end(),i) == pchWthMat.end())
myProps.push_back(Property(Property::MATERIAL,999999,i+1,
myModel[i]->getNoSpaceDim()));
// Renumber the nodes to account for overlapping nodes and erased patches
#ifdef PARALLEL_PETSC
std::vector<int> l2gn;
int nnod = ASMbase::renumberNodes(myModel,&l2gn);
#else
int nnod = ASMbase::renumberNodes(myModel);
#endif
if (fixDup)
{
// Check for duplicated nodes (missing topology)
int nDupl = 0;
std::map<Vec3,int> globalNodes;
for (size_t i = 0; i < myModel.size(); i++)
{
std::map<int,int> old2new;
std::cout <<" * Checking Patch "<< i+1 << std::endl;
for (size_t node = 1; node <= myModel[i]->getNoNodes(); node++)
{
int globNum = myModel[i]->getNodeID(node);
Vec3 X(myModel[i]->getCoord(node));
std::map<Vec3,int>::const_iterator xit = globalNodes.find(X);
if (xit == globalNodes.end())
globalNodes.insert(std::make_pair(X,globNum));
else if (xit->second != globNum)
{
std::cout <<" ** Merging duplicated nodes "<< xit->second <<" and "
<< globNum <<" at X="<< X << std::endl;
if (myModel[i]->mergeNodes(node,xit->second))
old2new[globNum] = xit->second;
}
}
if (!old2new.empty())
{
myModel[i]->renumberNodes(old2new,true);
nDupl += old2new.size();
}
}
if (nDupl > 0)
{
std::cout <<" * "<< nDupl <<" duplicated nodes merged."<< std::endl;
// Renumber the nodes to account for the merged nodes
nnod = ASMbase::renumberNodes(myModel);
}
}
// Process the specified Dirichlet boundary conditions
std::cout <<"\nResolving Dirichlet boundary conditions"<< std::endl;
bool ok = true;
for (PropertyVec::const_iterator p = myProps.begin(); p != myProps.end(); p++)
switch (p->pcode) {
case Property::DIRICHLET:
if (this->addConstraint(p->patch,p->lindx,p->ldim,p->pindx))
std::cout << std::endl;
else
ok = false;
break;
case Property::DIRICHLET_INHOM:
if (this->addConstraint(p->patch,p->lindx,p->ldim,p->pindx%1000,p->pindx))
std::cout << std::endl;
else
ok = false;
break;
default:
break;
}
// Set initial values for the inhomogeneous diriclet conditions, if any
this->initDirichlet();
// Resolve possibly chained multi-point constraints
ASMbase::resolveMPCchains(myModel);
// Preprocess the result points
for (ResPointVec::iterator p = myPoints.begin(); p != myPoints.end();)
{
int pid = this->getLocalPatchIndex(p->patch);
if (pid < 1 || myModel[pid-1]->empty())
p = myPoints.erase(p);
else if ((p->inod = myModel[pid-1]->evalPoint(p->par,p->par,p->X)) < 0)
p = myPoints.erase(p);
else
{
p->npar = myModel[pid-1]->getNoParamDim();
int ipt = 1 + (int)(p-myPoints.begin());
if (ipt == 1) std::cout <<'\n';
std::cout <<"Result point #"<< ipt <<": patch #"<< p->patch;
switch (p->npar) {
case 1: std::cout <<" u="; break;
case 2: std::cout <<" (u,v)=("; break;
case 3: std::cout <<" (u,v,w)=("; break;
}
std::cout << p->par[0];
for (unsigned char c = 1; c < p->npar; c++)
std::cout <<','<< p->par[c];
if (p->npar > 1) std::cout <<')';
if (p->inod > 0) std::cout <<", node #"<< p->inod;
std::cout <<", X = "<< p->X << std::endl;
p++;
}
}
// Initialize data structures for the algebraic system
#ifdef PARALLEL_PETSC
mySam = new SAMpatchPara(l2gn);
#else
mySam = new SAMpatch();
#endif
return mySam->init(myModel,nnod) && ok;
}
bool SIMbase::setPropertyType (int code, Property::Type ptype, int pindex)
{
if (code < 0)
{
std::cerr <<" ** SIMbase::setPropertyType: Negative property code "
<< code <<" (ignored)"<< std::endl;
return false;
}
for (size_t j = 0; j < myProps.size(); j++)
if (myProps[j].pindx == (size_t)code &&
myProps[j].pcode == Property::UNDEFINED)
{
myProps[j].pcode = ptype;
if (pindex >= 0) myProps[j].pindx = pindex;
}
return true;
}
bool SIMbase::initSystem (SystemMatrix::Type mType, size_t nMats, size_t nVec)
{
if (!mySam) return false;
#if SP_DEBUG > 1
mySam->print(std::cout);
std::string heading("\n\nNodal coordinates for Patch 1");
size_t i, j = heading.size()-1;
for (i = 0; i < myModel.size() && i < 9; i++, heading[j]++)
myModel[i]->printNodes(std::cout,heading.c_str());
#endif
myEqSys = new AlgEqSystem(*mySam);
myEqSys->init(mType,mySolParams,nMats,nVec,num_threads_SLU);
myEqSys->initAssembly();
return true;
}
bool SIMbase::setAssociatedRHS (size_t iMat, size_t iVec)
{
if (!myEqSys) return false;
return myEqSys->setAssociatedVector(iMat,iVec);
}
bool SIMbase::setMode (int mode)
{
if (!myProblem) return false;
myProblem->setMode((SIM::SolutionMode)mode);
return true;
}
void SIMbase::printProblem (std::ostream& os) const
{
if (myProblem)
{
std::cout <<"\nProblem definition:"<< std::endl;
myProblem->print(os);
}
#if SP_DEBUG > 1
std::cout <<"\nProperty mapping:";
for (PropertyVec::const_iterator i = myProps.begin(); i != myProps.end(); i++)
std::cout <<"\n"<< i->pcode <<" "<< i->pindx <<" "<< i->patch
<< (int)i->lindx <<" "<< (int)i->ldim;
std::cout << std::endl;
#endif
}
size_t SIMbase::getNoFields (int basis) const
{
return myModel.empty() ? 0 : myModel.front()->getNoFields(basis);
}
size_t SIMbase::getNoSpaceDim () const
{
return myModel.empty() ? 0 : myModel.front()->getNoSpaceDim();
}
size_t SIMbase::getNoDOFs () const
{
return mySam ? mySam->getNoDOFs() : 0;
}
size_t SIMbase::getNoNodes (bool unique) const
{
size_t nnod = 0;
if (unique && mySam)
nnod = mySam->getNoNodes();
else for (size_t i = 0; i < myModel.size(); i++)
nnod += myModel[i]->getNoNodes();
return nnod;
}
size_t SIMbase::getNoSolutions () const
{
return myProblem ? myProblem->getNoSolutions() : 0;
}
bool SIMbase::initDirichlet (double time)
{
Vector dummy;
return this->updateDirichlet(time,&dummy);
}
bool SIMbase::updateDirichlet (double time, const Vector* prevSol)
{
if (prevSol)
for (size_t i = 0; i < myModel.size(); i++)
if (!myModel[i]->updateDirichlet(myScalars,time))
return false;
if (mySam)
return mySam->updateConstraintEqs(myModel,prevSol);
else
return true;
}
bool SIMbase::assembleSystem (const TimeDomain& time, const Vectors& prevSol,
bool newLHSmatrix)
{
PROFILE1("Element assembly");
if (!myProblem) return false;
myProblem->initIntegration(time);
myEqSys->init(newLHSmatrix);
bool ok = true;
// Loop over the different material regions, integrating interior
// coefficient matrix terms for the patch associated with each material
size_t i, j = 0, lp = 0;
for (i = 0; i < myProps.size() && ok; i++)
if (myProps[i].pcode == Property::MATERIAL)
if ((j = myProps[i].patch) < 1 || j > myModel.size())
{
std::cerr <<" *** SIMbase::assembleSystem: Patch index "<< j
<<" out of range [1,"<< myModel.size() <<"]"<< std::endl;
ok = false;
}
else if (this->initMaterial(myProps[i].pindx))
{
if (msgLevel > 1)
std::cout <<"\nAssembling interior matrix terms for P"<< j
<< std::endl;
this->extractPatchSolution(myModel[j-1],prevSol);
ok = myModel[j-1]->integrate(*myProblem,*myEqSys,time);
lp = j;
}
else
ok = false;
if (j == 0 && ok)
// All patches are referring to the same material, and we assume it has
// been initialized during input processing (thus no initMaterial call here)
for (i = 0; i < myModel.size() && ok; i++)
{
if (msgLevel > 1)
std::cout <<"\nAssembling interior matrix terms for P"<< i+1
<< std::endl;
this->extractPatchSolution(myModel[i],prevSol);
ok = myModel[i]->integrate(*myProblem,*myEqSys,time);
lp = i+1;
}
// Assemble right-hand-side contributions from the Neumann boundary conditions
if (myEqSys->getVector())
for (i = 0; i < myProps.size() && ok; i++)
if (myProps[i].pcode == Property::NEUMANN)
if ((j = myProps[i].patch) < 1 || j > myModel.size())
{
std::cerr <<" *** SIMbase::assembleSystem: Patch index "<< j
<<" out of range [1,"<< myModel.size() <<"]"<< std::endl;
ok = false;
}
else if (myProps[i].ldim+1 == myModel[j-1]->getNoSpaceDim())
if (this->initNeumann(myProps[i].pindx))
{
int bIndex = myProps[i].lindx;
if (msgLevel > 1)
std::cout <<"\nAssembling Neumann matrix terms for boundary "
<< bIndex <<" on P"<< j << std::endl;
if (j != lp) this->extractPatchSolution(myModel[j-1],prevSol);
ok = myModel[j-1]->integrate(*myProblem,bIndex,*myEqSys,time);
lp = j;
}
else
ok = false;
else if (myProps[i].ldim+2 == myModel[j-1]->getNoSpaceDim())
if (this->initNeumann(myProps[i].pindx))
{
int bIndex = myProps[i].lindx;
if (msgLevel > 1)
std::cout <<"\nAssembling Neumann matrix terms for edge "
<< bIndex <<" on P"<< j << std::endl;
if (j != lp) this->extractPatchSolution(myModel[j-1],prevSol);
ok = myModel[j-1]->integrateEdge(*myProblem,bIndex,*myEqSys,time);
lp = j;
}
else
ok = false;
return ok && this->finalizeAssembly(newLHSmatrix);
}
bool SIMbase::finalizeAssembly (bool newLHSmatrix)
{
// Communication of matrix and vector assembly (for PETSc only)
SystemMatrix* A = myEqSys->getMatrix();
if (A && newLHSmatrix)
{
if (!A->beginAssembly()) return false;
if (!A->endAssembly()) return false;
#if SP_DEBUG > 3
std::cout <<"\nSystem coefficient matrix:"<< *A;
#endif
}
SystemVector* b = myEqSys->getVector();
if (b)
{
if (!b->beginAssembly()) return false;
if (!b->endAssembly()) return false;
#if SP_DEBUG > 2
std::cout <<"\nSystem right-hand-side vector:"<< *b;
#endif
}
return true;
}
bool SIMbase::extractLoadVec (Vector& loadVec) const
{
SystemVector* b = myEqSys->getVector();
if (!b) return false;
// Expand load vector from equation ordering to DOF-ordering
return mySam->expandSolution(*b,loadVec);
}
void SIMbase::extractPatchSolution (const ASMbase* patch, const Vectors& sol)
{
for (size_t i = 0; i < sol.size() && i < myProblem->getNoSolutions(); i++)
if (!sol[i].empty())
patch->extractNodeVec(sol[i],myProblem->getSolution(i));
}
bool SIMbase::solveSystem (Vector& solution, int printSol,
const char* compName, bool newLHS)
{
SystemMatrix* A = myEqSys->getMatrix();
SystemVector* b = myEqSys->getVector();
if (!A) std::cerr <<" *** SIMbase::solveSystem: No LHS matrix"<< std::endl;
if (!b) std::cerr <<" *** SIMbase::solveSystem: No RHS vector"<< std::endl;
if (!A || !b) return false;
// Solve the linear system of equations
if (msgLevel > 1)
std::cout <<"\nSolving the equation system ..."<< std::endl;
{
PROFILE1("Equation solving");
if (!A->solve(*b,newLHS)) return false;
}
// Expand solution vector from equation ordering to DOF-ordering
if (!mySam->expandSolution(*b,solution)) return false;
if (printSol < 1) return true;
// Compute and print solution norms
const size_t nf = this->getNoFields(1);
size_t iMax[nf];
double dMax[nf];
double dNorm = this->solutionNorms(solution,dMax,iMax,nf);
if (myPid == 0)
{
std::cout <<"\n >>> Solution summary <<<\n"
<<"\nL2-norm : "<< dNorm;
if (nf == 1)
std::cout <<"\nMax "<< compName <<" : "<< dMax[0] <<" node "<< iMax[0];
else
{
char D = 'X';
for (size_t d = 0; d < nf; d++, D++)
std::cout <<"\nMax "<< D <<'-'<< compName <<" : "
<< dMax[d] <<" node "<< iMax[d];
}
std::cout << std::endl;
}
// Print entire solution vector if it is small enough
if (mySam->getNoEquations() < printSol)
{
std::cout <<"\nSolution vector:";
for (int inod = 1; inod <= mySam->getNoNodes(); inod++)
{
std::cout <<"\nNode "<< inod <<":";
std::pair<int,int> dofs = mySam->getNodeDOFs(inod);
for (int d = dofs.first-1; d < dofs.second; d++)
std::cout <<" "<< solution[d];
}
std::cout << std::endl;
}
#if SP_DEBUG > 2
else
std::cout <<"\nSolution vector:"<< *b;
#endif
return true;
}
void SIMbase::iterationNorms (const Vector& u, const Vector& r,
double& eNorm, double& rNorm, double& dNorm) const
{
eNorm = mySam->dot(r,u,'A');
rNorm = mySam->norm2(r,'D');
dNorm = mySam->norm2(u,'D');
}
double SIMbase::solutionNorms (const Vector& x, double* inf,
size_t* ind, size_t nf) const
{
if (nf == 0) nf = this->getNoSpaceDim();
for (size_t d = 0; d < nf; d++)
{
ind[d] = d+1;
inf[d] = mySam->normInf(x,ind[d]);
}
return mySam->normL2(x);
}
bool SIMbase::solutionNorms (const TimeDomain& time, const Vectors& psol,
Matrix& eNorm, Vector& gNorm)
{
NormBase* norm = myProblem->getNormIntegrand(mySol);
if (!norm)
{
#ifdef SP_DEBUG
std::cerr <<" *** SIMbase::solutionNorms: No integrand."<< std::endl;
#endif
return false;
}
PROFILE1("Norm integration");
myProblem->initIntegration(time);
size_t nNorms = norm->getNoFields();
const Vector& primsol = psol.front();
size_t i, nel = mySam->getNoElms();
eNorm.resize(nNorms,nel,true);
gNorm.resize(nNorms,true);
// Initialize norm integral classes
GlbNorm globalNorm(gNorm,GlbNorm::SQRT);
LintegralVec elementNorms;
elementNorms.reserve(nel);
for (i = 0; i < nel; i++)
elementNorms.push_back(new ElmNorm(eNorm.ptr(i)));
// Loop over the different material regions, integrating solution norm terms
// for the patch domain associated with each material
bool ok = true;
size_t j = 0, lp = 0;
for (i = 0; i < myProps.size() && ok; i++)
if (myProps[i].pcode == Property::MATERIAL)
if ((j = myProps[i].patch) < 1 || j > myModel.size())
ok = false;
else if (this->initMaterial(myProps[i].pindx))
{
myModel[j-1]->extractNodeVec(primsol,myProblem->getSolution());
ok = myModel[j-1]->integrate(*norm,globalNorm,time,elementNorms);
lp = j;
}
else
ok = false;
if (j == 0 && ok)
// All patches are referring to the same material, and we assume it has
// been initialized during input processing (thus no initMaterial call here)
for (i = 0; i < myModel.size() && ok; i++)
{
myModel[i]->extractNodeVec(primsol,myProblem->getSolution());
ok = myModel[i]->integrate(*norm,globalNorm,time,elementNorms);
lp = j;
}
// Integrate norm contributions due to Neumann boundary conditions, if any.
// Note: Currently, only the global norms are considered here.
// The corresponding element-level norms are not stored. This is mainly
// because the current design only allows one loop over the elements
// in the element-norm calculations. Consider rivising this later.
if (norm->hasBoundaryTerms())
for (i = 0; i < myProps.size() && ok; i++)
if (myProps[i].pcode == Property::NEUMANN)
if ((j = myProps[i].patch) < 1 || j > myModel.size())
ok = false;
else if (myProps[i].ldim+1 == myModel[j-1]->getNoSpaceDim())
if (this->initNeumann(myProps[i].pindx))
{
if (j != lp)
myModel[j-1]->extractNodeVec(primsol,myProblem->getSolution());
ok = myModel[j-1]->integrate(*norm,myProps[i].lindx,
globalNorm,time);
lp = j;
}
else
ok = false;
else if (myProps[i].ldim+2 == myModel[j-1]->getNoSpaceDim())
if (this->initNeumann(myProps[i].pindx))
{
if (j != lp)
myModel[j-1]->extractNodeVec(primsol,myProblem->getSolution());
ok = myModel[j-1]->integrateEdge(*norm,myProps[i].lindx,
globalNorm,time);
lp = j;
}
else
ok = false;
// Add norm contributions due to inhomogeneous Dirichlet boundary conditions
// (if any), that is, the path integral of the total solution vector
// times the reaction forces at the prescribed DOFs
const Vector* reactionForces = myEqSys->getReactions();
if (reactionForces && norm->indExt() > 0)
if (psol.size() > 1)
{
static double extEnergy = 0.0;
static Vector prevForces(reactionForces->size());
extEnergy += mySam->normReact(psol[0]-psol[1],*reactionForces+prevForces);
gNorm(norm->indExt()) += extEnergy;
prevForces = *reactionForces;
}
else
gNorm(norm->indExt()) += mySam->normReact(psol.front(),*reactionForces);
#ifdef PARALLEL_PETSC
if (nProc > 1)
{
double* tmp = new double[nNorms];
MPI_Allreduce(gNorm.ptr(),tmp,nNorms,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD);
memcpy(gNorm.ptr(),tmp,nNorms*sizeof(double));
delete[] tmp;
}
#endif
delete norm;
for (i = 0; i < nel; i++)
delete elementNorms[i];
return ok;
}
/*!
The content of the output array \a RF is as follows:
\f[
RF[0] = \sum_{i=n}^{\rm nnod} \sum_{i=1}^{\rm nsd} f_n^i u_n^i
\quad\quad\mbox{(energy)}\f]
\f[
RF[i] = \sum_{n=1}^{\rm nnod} f_n^i \quad\forall\quad i=1,\ldots,{\rm nsd}
\f]
*/
bool SIMbase::getCurrentReactions (RealArray& RF, const Vector& psol) const
{
const Vector* reactionForces = myEqSys->getReactions();
if (!reactionForces) return false;
RF.resize(1+this->getNoSpaceDim());
RF.front() = 2.0*mySam->normReact(psol,*reactionForces);
for (size_t dir = 1; dir < RF.size(); dir++)
RF[dir] = mySam->getReaction(dir,*reactionForces);
return true;
}
bool SIMbase::systemModes (std::vector<Mode>& solution,
int nev, int ncv, int iop, double shift,
size_t iA, size_t iB)
{
if (nev < 1 || ncv <= nev) return false;
PROFILE1("Eigenvalue analysis");
Vector eigVal;
Matrix eigVec;
if (nev > mySam->getNoEquations()) nev = mySam->getNoEquations();
if (ncv > mySam->getNoEquations()) ncv = mySam->getNoEquations();
// Solve the eigenvalue problem
std::cout <<"\nSolving the eigenvalue problem ..."<< std::endl;
SystemMatrix* A = myEqSys->getMatrix(iA);
SystemMatrix* B = myEqSys->getMatrix(iB);
bool ok = eig::solve(A,B,eigVal,eigVec,nev,ncv,iop,shift);
// To interface SLEPC another interface is used
//bool ok = eig::solve(A,B,eigVal,eigVec,nev);
// Expand eigenvectors to DOF-ordering and print out eigenvalues
bool freq = iop == 3 || iop == 4 || iop == 6;
std::cout <<"\n >>> Computed Eigenvalues <<<\n Mode\t"
<< (freq ? "Frequency [Hz]" : "Eigenvalue");
solution.resize(nev);
for (int i = 1; i <= nev && ok; i++)
{
solution[i-1].eigNo = i;
if (!mySam->expandVector(eigVec.getColumn(i),solution[i-1].eigVec))
ok = false;
else if (!freq)
solution[i-1].eigVal = eigVal(i);
else if (eigVal(i) < 0.0)
solution[i-1].eigVal = -sqrt(-eigVal(i))*0.5/M_PI;
else
solution[i-1].eigVal = sqrt( eigVal(i))*0.5/M_PI;
std::cout <<"\n "<< i <<"\t\t"<< solution[i-1].eigVal;
}
std::cout << std::endl;
return ok;
}
bool SIMbase::writeGlv (const char* inpFile, const int* nViz, int format)
{
if (myVtf) return false;
// Open a new VTF-file
char* vtfName = new char[strlen(inpFile)+10];
strtok(strcpy(vtfName,inpFile),".");
if (nProc > 1)
sprintf(vtfName+strlen(vtfName),"_p%04d.vtf",myPid);
else
strcat(vtfName,".vtf");
std::cout <<"\nWriting VTF-file "<< vtfName << std::endl;
myVtf = new VTF(vtfName,format);
delete[] vtfName;
// Convert and write model geometry
char pname[16];
for (size_t i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (msgLevel > 0)
std::cout <<"Writing geometry for patch "<< i+1 << std::endl;
size_t nd = myModel[i]->getNoParamDim();
ElementBlock* lvb = new ElementBlock(nd == 3 ? 8 : (nd == 2 ? 4 : 2));
if (!myModel[i]->tesselate(*lvb,nViz))
return false;
sprintf(pname,"Patch %ld",i+1);
if (!myVtf->writeGrid(lvb,pname))
return false;
}
return true;
}
bool SIMbase::writeGlvBC (const int* nViz, int& nBlock) const
{
if (!myVtf) return false;
Matrix field;
size_t i, j;
int node, geomID = 0;
std::vector<int> dID[3];
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
geomID++;
size_t nbc = myModel[i]->getNoFields(1);
Matrix bc(nbc,myModel[i]->getNoNodes());
RealArray flag(3,0.0);
ASMbase::BCVec::const_iterator bit;
for (bit = myModel[i]->begin_BC(); bit != myModel[i]->end_BC(); bit++)
if ((node = myModel[i]->getNodeIndex(bit->node)))
{
if (!bit->CX && nbc > 0) bc(1,node) = flag[0] = 1.0;
if (!bit->CY && nbc > 1) bc(2,node) = flag[1] = 1.0;
if (!bit->CZ && nbc > 2) bc(3,node) = flag[2] = 1.0;
}
if (flag[0]+flag[1]+flag[2] == 0.0)
continue; // nothing on this patch
if (msgLevel > 1)
std::cout <<"Writing boundary conditions for patch "<< i+1 << std::endl;
if (!myModel[i]->evalSolution(field,bc,nViz))
return false;
// The BC fields should either be 0.0 or 1.0
if (nViz[0] > 2 || nViz[1] > 2 || nViz[2] > 2)
for (j = 1; j <= 3; j++)
if (flag[j-1] == 1.0)
for (size_t n = 1; n <= field.cols(); n++)
if (field(j,n) < 0.9999) field(j,n) = 0.0;
for (j = 0; j < 3; j++)
if (flag[j] == 1.0)
if (myVtf->writeNres(field.getRow(1+j),++nBlock,geomID))
dID[j].push_back(nBlock);
else
return false;
}
const char* label[3] = {
"fix_X", "fix_Y", "fix_Z"
};
for (j = 0; j < 3; j++)
if (!dID[j].empty())
if (!myVtf->writeSblk(dID[j],label[j],1+j))
return false;
return true;
}
bool SIMbase::writeGlvT (int iStep, int& nBlock) const
{
if (myProblem->hasTractionValues())
{
if (msgLevel > 1)
std::cout <<"Writing boundary tractions"<< std::endl;
return myProblem->writeGlvT(myVtf,iStep,nBlock);
}
return true;
}
bool SIMbase::writeGlvV (const Vector& vec, const char* fieldName,
const int* nViz, int iStep, int& nBlock) const
{
if (vec.empty())
return true;
else if (!myVtf)
return false;
Matrix field;
Vector lovec;
int geomID = 0;
std::vector<int> vID;
for (size_t i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (msgLevel > 1)
std::cout <<"Writing vector field for patch "<< i+1 << std::endl;
myModel[i]->extractNodeVec(vec,lovec);
if (!myModel[i]->evalSolution(field,lovec,nViz))
return false;
if (!myVtf->writeVres(field,++nBlock,++geomID,this->getNoSpaceDim()))
return false;
else
vID.push_back(nBlock);
}
int idBlock = 2;
return myVtf->writeVblk(vID,fieldName,idBlock,iStep);
}
bool SIMbase::writeGlvS (const Vector& psol, const int* nViz,
int iStep, int& nBlock, double time,
char psolOnly, const char* pvecName,
int idBlock, int psolComps)
{
if (psol.empty())
return true;
else if (!myVtf)
return false;
if (!psolOnly && myProblem)
myProblem->initResultPoints(time);
Matrix field;
Vector lovec;
size_t i, j;
int geomID = 0;
const size_t pMAX = 6;
const size_t sMAX = 21;
std::vector<int> vID[2], dID[pMAX], sID[sMAX];
bool scalarEq = this->getNoFields() == 1;
size_t nVcomp = scalarEq ? 1 : this->getNoSpaceDim();
bool haveAsol = false;
if (mySol)
if (scalarEq)
haveAsol = mySol->hasScalarSol() > 1;
else
haveAsol = mySol->hasVectorSol() > 1;
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (msgLevel > 1)
std::cout <<"Writing FE solution for patch "<< i+1 << std::endl;
// 1. Evaluate primary solution variables
Vector& locvec = myProblem ? myProblem->getSolution() : lovec;
myModel[i]->extractNodeVec(psol,locvec,psolComps);
if (!myModel[i]->evalSolution(field,locvec,nViz))
return false;
if (psolOnly > 1)
geomID++;
else if (!myVtf->writeVres(field,++nBlock,++geomID,nVcomp))
return false;
else
vID[0].push_back(nBlock);
for (j = 0; j < field.rows() && j < pMAX; j++)
if (!myVtf->writeNres(field.getRow(1+j),++nBlock,geomID))
return false;
else
dID[j].push_back(nBlock);
if (psolOnly || !myProblem) continue; // skip secondary solution
// 2. Direct evaluation of secondary solution variables
LocalSystem::patch = i;
if (!myModel[i]->evalSolution(field,*myProblem,nViz))
return false;
if (scalarEq)
if (!myVtf->writeVres(field,++nBlock,geomID))
return false;
else
vID[1].push_back(nBlock);
size_t k = 0;
for (j = 1; j <= field.rows() && k < sMAX; j++)
if (!myVtf->writeNres(field.getRow(j),++nBlock,geomID))
return false;
else
sID[k++].push_back(nBlock);
if (discretization == Spline)
{
// 3. Projection of secondary solution variables (splines only)
if (!myModel[i]->evalSolution(field,*myProblem,nViz,true))
return false;
for (j = 1; j <= field.rows() && k < sMAX; j++)
if (!myVtf->writeNres(field.getRow(j),++nBlock,geomID))
return false;
else
sID[k++].push_back(nBlock);
}
if (haveAsol)
{
// 4. Evaluate analytical solution variables
if (msgLevel > 1)
std::cout <<"Writing exact solution for patch "<< i+1 << std::endl;
const ElementBlock* grid = myVtf->getBlock(geomID);
std::vector<Vec3>::const_iterator cit = grid->begin_XYZ();
Vector solPt; field.fill(0.0);
bool ok = true;
for (j = 1; cit != grid->end_XYZ() && ok; j++, cit++)
{
if (scalarEq)
ok = myProblem->evalSol(solPt,*mySol->getScalarSecSol(),*cit);
else if (mySol->hasVectorSol() == 3)
ok = myProblem->evalSol(solPt,*mySol->getStressSol(),*cit);
else
ok = myProblem->evalSol(solPt,*mySol->getVectorSecSol(),*cit);
if (ok)
field.fillColumn(j,solPt);
}
for (j = 1; j <= field.rows() && k < sMAX && ok; j++)
if (!myVtf->writeNres(field.getRow(j),++nBlock,geomID))
return false;
else
sID[k++].push_back(nBlock);
}
}
// Write result block identifications
bool ok = true;
if (!vID[0].empty())
if (pvecName)
ok = myVtf->writeVblk(vID[0],pvecName,idBlock,iStep);
else
ok = myVtf->writeDblk(vID[0],"Solution",idBlock,iStep);
if (ok && !vID[1].empty())
ok = myVtf->writeVblk(vID[1],"Flux",idBlock+1,iStep);
std::string pname;
if (!myProblem)
{
pname = pvecName ? pvecName : "Solution";
if (psolOnly < 2) pname += "_w";
}
for (j = 0; j < pMAX && !dID[j].empty() && ok; j++)
{
if (myProblem)
pname = myProblem->getField1Name(j);
else if (psolOnly < 2)
(*pname.rbegin()) ++;
ok = myVtf->writeSblk(dID[j],pname.c_str(),++idBlock,iStep);
}
if (psolOnly || !myProblem || !ok) return ok;
size_t nf = myProblem->getNoFields(2);
for (i = j = 0; i < nf && !sID[j].empty(); i++, j++)
if (!myVtf->writeSblk(sID[j],myProblem->getField2Name(i,haveAsol?"FE":0),
++idBlock,iStep)) return false;
if (discretization == Spline)
for (i = 0; i < nf && !sID[j].empty(); i++, j++)
if (!myVtf->writeSblk(sID[j],myProblem->getField2Name(i,"Projected"),
++idBlock,iStep)) return false;
if (haveAsol)
for (i = 0; i < nf && !sID[j].empty(); i++, j++)
if (!myVtf->writeSblk(sID[j],myProblem->getField2Name(i,"Exact"),
++idBlock,iStep)) return false;
return true;
}
bool SIMbase::writeGlvStep (int iStep, double value, int itype)
{
if (itype == 0)
return myVtf->writeState(iStep,"Time %g",value,itype);
else
return myVtf->writeState(iStep,"Step %g",value,itype);
}
bool SIMbase::writeGlvM (const Mode& mode, bool freq,
const int* nViz, int& nBlock)
{
if (mode.eigVec.empty())
return true;
else if (!myVtf)
return false;
if (msgLevel > 1)
std::cout <<"Writing eigenvector for Mode "<< mode.eigNo << std::endl;
Vector displ;
Matrix field;
int geomID = 0;
std::vector<int> vID;
for (size_t i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (myModel.size() > 1 && msgLevel > 1) std::cout <<"."<< std::flush;
geomID++;
myModel[i]->extractNodeVec(mode.eigVec,displ);
if (!myModel[i]->evalSolution(field,displ,nViz))
return false;
if (!myVtf->writeVres(field,++nBlock,geomID))
return false;
else
vID.push_back(nBlock);
}
if (myModel.size() > 1 && msgLevel > 1) std::cout << std::endl;
int idBlock = 10;
if (!myVtf->writeDblk(vID,"Mode Shape",idBlock,mode.eigNo))
return false;
return myVtf->writeState(mode.eigNo, freq ? "Frequency %g" : "Eigenvalue %g",
mode.eigVal, 1);
}
bool SIMbase::writeGlvN (const Matrix& norms, int iStep, int& nBlock)
{
if (norms.empty())
return true;
else if (!myVtf)
return false;
Matrix field;
int geomID = 0;
std::vector<int> sID[3];
size_t i, j, k;
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (msgLevel > 1)
std::cout <<"Writing element norms for patch "<< i+1 << std::endl;
geomID++;
myModel[i]->extractElmRes(norms,field);
for (j = k = 0; j < field.rows() && j < 4; j++)
if (field.rows()%2 || j != 1) // Skip the external norms (always zero)
if (!myVtf->writeEres(field.getRow(1+j),++nBlock,geomID))
return false;
else
sID[k++].push_back(nBlock);
}
const char* label[3] = {
"a(u^h,u^h)^0.5",
"a(u,u)^0.5",
"a(e,e)^0.5, e=u-u^h"
};
int idBlock = 100;
for (j = 0; j < 3; j++)
if (!sID[j].empty())
if (!myVtf->writeSblk(sID[j],label[j],++idBlock,iStep))
return false;
return true;
}
void SIMbase::closeGlv ()
{
if (myVtf) delete myVtf;
myVtf = 0;
}
void SIMbase::dumpPrimSol (const Vector& psol, std::ostream& os,
bool withID) const
{
if (psol.empty()) return;
size_t i, j, ip;
unsigned char k, n;
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
Vector patchSol;
myModel[i]->extractNodeVec(psol,patchSol);
if (withID)
{
if (myModel.size() > 1)
os <<"\n# Patch: "<< i+1;
os <<"\n# inod/gnod\tNodal Coordinates\tSolution\n";
}
for (ip = 0, j = 1; j <= myModel[i]->getNoNodes(); j++)
{
if ((n = myModel[i]->getNodalDOFs(j)) == 0)
continue;
else if (withID)
os << j <<' '<< myModel[i]->getNodeID(j)
<<"\t\t"<< myModel[i]->getCoord(j) <<"\t\t";
os << patchSol[ip++];
for (k = 1; k < n; k++)
os <<' '<< patchSol[ip++];
os <<'\n';
}
}
os.flush();
}
bool SIMbase::dumpGeometry (std::ostream& os) const
{
for (size_t i = 0; i < myModel.size(); i++)
if (!myModel[i]->empty())
if (!myModel[i]->write(os))
return false;
return true;
}
bool SIMbase::dumpBasis (std::ostream& os, int basis, size_t patch) const
{
size_t start = patch?patch-1:0;
size_t end = patch?start+1:myModel.size();
for (size_t i = start; i < end; i++)
if (!myModel[i]->empty())
if (!myModel[i]->write(os,basis))
return false;
return true;
}
bool SIMbase::dumpSolution (const Vector& psol, std::ostream& os) const
{
if (psol.empty())
return true;
Matrix field;
size_t i, j, k;
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
if (myModel.size() > 1)
os <<"\n# Patch: "<< i+1;
// Extract and write primary solution
size_t nf = myModel[i]->getNoFields(1);
Vector& patchSol = myProblem->getSolution();
myModel[i]->extractNodeVec(psol,patchSol);
for (k = 1; k <= nf; k++)
{
os << myProblem->getField1Name(k,"# FE");
for (j = 1; j <= myModel[i]->getNoNodes(); j++)
{
std::pair<int,int> dofs = mySam->getNodeDOFs(j);
int idof = dofs.first+k-1;
if (idof <= dofs.second)
os <<"\n"<< patchSol[idof-1];
}
os << std::endl;
}
// Evaluate secondary solution variables
LocalSystem::patch = i;
if (!myModel[i]->evalSolution(field,*myProblem))
return false;
// Write the secondary solution
for (j = 1; j <= field.rows(); j++)
{
os << myProblem->getField2Name(j-1,"# FE");
for (k = 1; k <= field.cols(); k++)
os <<"\n"<< field(j,k);
os << std::endl;
}
}
return true;
}
bool SIMbase::dumpResults (const Vector& psol, double time, std::ostream& os,
std::streamsize outputPrecision) const
{
if (psol.empty() || myPoints.empty())
return true;
myProblem->initResultPoints(time);
size_t i, j, k;
Matrix sol1, sol2;
Vector reactionFS;
const Vector* reactionForces = myEqSys->getReactions();
for (i = 0; i < myModel.size(); i++)
{
if (myModel[i]->empty()) continue; // skip empty patches
ResPointVec::const_iterator p;
std::vector<int> points;
RealArray params[3];
// Find all evaluation points within this patch, if any
for (j = 0, p = myPoints.begin(); p != myPoints.end(); j++, p++)
if (this->getLocalPatchIndex(p->patch) == (int)(i+1))
if (discretization == Spline)
{
points.push_back(p->inod > 0 ? p->inod : -(j+1));
for (k = 0; k < myModel[i]->getNoParamDim(); k++)
params[k].push_back(p->par[k]);
}
else if (p->inod > 0)
points.push_back(p->inod);
if (points.empty()) continue; // no points in this patch
myModel[i]->extractNodeVec(psol,myProblem->getSolution());
if (discretization == Spline)
{
// Evaluate the primary solution variables
if (!myModel[i]->evalSolution(sol1,myProblem->getSolution(),params,false))
return false;
// Evaluate the secondary solution variables
LocalSystem::patch = i;
if (!myModel[i]->evalSolution(sol2,*myProblem,params,false))
return false;
}
else
// Extract nodal primary solution variables
if (!myModel[i]->getSolution(sol1,myProblem->getSolution(),points))
return false;
// Formatted output, use scientific notation with fixed field width
std::streamsize flWidth = 8 + outputPrecision;
std::streamsize oldPrec = os.precision(outputPrecision);
std::ios::fmtflags oldF = os.flags(std::ios::scientific | std::ios::right);
for (j = 0; j < points.size(); j++)
{
if (points[j] > 0)
{
points[j] = myModel[i]->getNodeID(points[j]);
os <<" Node #"<< points[j] <<":\tsol1 =";
}
else
os <<" Point #"<< -points[j] <<":\tsol1 =";
for (k = 1; k <= sol1.rows(); k++)
os << std::setw(flWidth) << sol1(k,j+1);
if (discretization == Spline)
{
os <<"\n\t\tsol2 =";
for (k = 1; k <= sol2.rows(); k++)
os << std::setw(flWidth) << sol2(k,j+1);
}
if (reactionForces && points[j] > 0)
// Print nodal reaction forces for nodes with prescribed DOFs
if (mySam->getNodalReactions(points[j],*reactionForces,reactionFS))
{
os <<"\n\t\treac =";
for (k = 0; k < reactionFS.size(); k++)
os << std::setw(flWidth) << reactionFS[k];
}
os << std::endl;
}
os.precision(oldPrec);
os.flags(oldF);
}
return true;
}
bool SIMbase::project (Vector& psol)
{
Matrix values;
Vector ssol;
ssol.resize(psol.size()*myProblem->getNoFields());
for (size_t i = 0; i < myModel.size(); i++)
{
myModel[i]->extractNodeVec(psol,myProblem->getSolution());
if (!myModel[i]->evalSolution(values,*myProblem))
return false;
if (!myModel[i]->injectNodeVec(values,ssol,values.rows()))
return false;
}
psol = ssol;
return true;
}
size_t SIMbase::extractPatchSolution (const Vector& sol, int pindx)
{
if (pindx < 0 || (size_t)pindx >= myModel.size() || sol.empty())
return 0;
myModel[pindx]->extractNodeVec(sol,myProblem->getSolution());
return myModel[pindx]->getNoFields(1)*myModel[pindx]->getNoNodes(1);
}
bool SIMbase::injectPatchSolution (Vector& sol, const Vector& locSol,
int pindx, unsigned char nndof)
{
if (pindx < 0 || (size_t)pindx >= myModel.size())
return false;
return myModel[pindx]->injectNodeVec(locSol,sol,nndof);
}
bool SIMbase::evalSecondarySolution (Matrix& field, int pindx) const
{
if (pindx < 0 || (size_t)pindx >= myModel.size())
return false;
return myModel[pindx]->evalSolution(field,*myProblem);
}